/************************************************************************ * IRC - Internet Relay Chat, common/dbuf.c * Copyright (C) 1990 Markku Savela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* -- Jto -- 20 Jun 1990 * extern void free() fixed as suggested by * gruner@informatik.tu-muenchen.de */ /* -- Jto -- 10 May 1990 * Changed memcpy into bcopy and removed the declaration of memset * because it was unnecessary. * Added the #includes for "struct.h" and "sys.h" to get bcopy/memcpy * work */ /* ** For documentation of the *global* functions implemented here, ** see the header file (dbuf.h). ** */ #ifndef lint static char sccsid[] = "@(#)dbuf.c 2.17 1/30/94 (C) 1990 Markku Savela"; #endif #include #include "struct.h" #include "common.h" #include "sys.h" #if !defined(VALLOC) && !defined(valloc) #define valloc malloc #endif int dbufalloc = 0, dbufblocks = 0; static dbufbuf *freelist = NULL; /* This is a dangerous define because a broken compiler will set DBUFSIZ ** to 4, which will work but will be very inefficient. However, there ** are other places where the code breaks badly if this is screwed ** up, so... -- Wumpus */ #define DBUFSIZ sizeof(((dbufbuf *)0)->data) /* ** dbuf_alloc - allocates a dbufbuf structure either from freelist or ** creates a new one. */ static dbufbuf *dbuf_alloc() { #if defined(VALLOC) && !defined(DEBUGMODE) Reg1 dbufbuf *dbptr, *db2ptr; Reg2 int num; #else Reg1 dbufbuf *dbptr; #endif dbufalloc++; if ((dbptr = freelist)) { freelist = freelist->next; return dbptr; } if (dbufalloc * DBUFSIZ > BUFFERPOOL) { dbufalloc--; return NULL; } #if defined(VALLOC) && !defined(DEBUGMODE) # if defined(SOL20) || defined(_SC_PAGESIZE) num = sysconf(_SC_PAGESIZE)/sizeof(dbufbuf); # else num = getpagesize()/sizeof(dbufbuf); # endif if (num < 0) num = 1; dbufblocks += num; dbptr = (dbufbuf *)valloc(num*sizeof(dbufbuf)); if (!dbptr) return (dbufbuf *)NULL; num--; for (db2ptr = dbptr; num; num--) { db2ptr = (dbufbuf *)((char *)db2ptr + sizeof(dbufbuf)); db2ptr->next = freelist; freelist = db2ptr; } return dbptr; #else dbufblocks++; return (dbufbuf *)MyMalloc(sizeof(dbufbuf)); #endif } /* ** dbuf_free - return a dbufbuf structure to the freelist */ static void dbuf_free(ptr) Reg1 dbufbuf *ptr; { dbufalloc--; ptr->next = freelist; freelist = ptr; } /* ** This is called when malloc fails. Scrap the whole content ** of dynamic buffer and return -1. (malloc errors are FATAL, ** there is no reason to continue this buffer...). After this ** the "dbuf" has consistent EMPTY status... ;) */ static int dbuf_malloc_error(dyn) dbuf *dyn; { dbufbuf *p; dyn->length = 0; dyn->offset = 0; while ((p = dyn->head) != NULL) { dyn->head = p->next; dbuf_free(p); } dyn->tail = dyn->head; return -1; } int dbuf_put(dyn, buf, length) dbuf *dyn; char *buf; int length; { Reg1 dbufbuf **h, *d; Reg2 int off; Reg3 int chunk; off = (dyn->offset + dyn->length) % DBUFSIZ; /* ** Locate the last non-empty buffer. If the last buffer is ** full, the loop will terminate with 'd==NULL'. This loop ** assumes that the 'dyn->length' field is correctly ** maintained, as it should--no other check really needed. */ if (!dyn->length) h = &(dyn->head); else { if (off) h = &(dyn->tail); else h = &(dyn->tail->next); } /* ** Append users data to buffer, allocating buffers as needed */ chunk = DBUFSIZ - off; dyn->length += length; for ( ;length > 0; h = &(d->next)) { if ((d = *h) == NULL) { if ((d = (dbufbuf *)dbuf_alloc()) == NULL) return dbuf_malloc_error(dyn); dyn->tail = d; *h = d; d->next = NULL; } if (chunk > length) chunk = length; bcopy(buf, d->data + off, chunk); length -= chunk; buf += chunk; off = 0; chunk = DBUFSIZ; } return 1; } char *dbuf_map(dyn,length) dbuf *dyn; int *length; { if (dyn->head == NULL) { dyn->tail = NULL; *length = 0; return NULL; } *length = DBUFSIZ - dyn->offset; if (*length > dyn->length) *length = dyn->length; return (dyn->head->data + dyn->offset); } int dbuf_delete(dyn,length) dbuf *dyn; int length; { dbufbuf *d; int chunk; if (length > dyn->length) length = dyn->length; chunk = DBUFSIZ - dyn->offset; while (length > 0) { if (chunk > length) chunk = length; length -= chunk; dyn->offset += chunk; dyn->length -= chunk; if (dyn->offset == DBUFSIZ || dyn->length == 0) { d = dyn->head; dyn->head = d->next; dyn->offset = 0; dbuf_free(d); } chunk = DBUFSIZ; } if (dyn->head == (dbufbuf *)NULL) { dyn->length = 0; dyn->tail = 0; } return 0; } int dbuf_get(dyn, buf, length) dbuf *dyn; char *buf; int length; { int moved = 0; int chunk; char *b; while (length > 0 && (b = dbuf_map(dyn, &chunk)) != NULL) { if (chunk > length) chunk = length; bcopy(b, buf, (int)chunk); (void)dbuf_delete(dyn, chunk); buf += chunk; length -= chunk; moved += chunk; } return moved; } /* ** dbuf_getmsg ** ** Check the buffers to see if there is a string which is terminted with ** either a \r or \n prsent. If so, copy as much as possible (determined by ** length) into buf and return the amount copied - else return 0. */ int dbuf_getmsg(dyn, buf, length) dbuf *dyn; char *buf; register int length; { dbufbuf *d; register char *s; register int dlen; register int i; int copy; getmsg_init: d = dyn->head; dlen = dyn->length; i = DBUFSIZ - dyn->offset; if (i <= 0) return -1; copy = 0; if (d && dlen) s = dyn->offset + d->data; else return 0; if (i > dlen) i = dlen; while (length > 0 && dlen > 0) { dlen--; if (*s == '\n' || *s == '\r') { copy = dyn->length - dlen; /* ** Shortcut this case here to save time elsewhere. ** -avalon */ if (copy == 1) { (void)dbuf_delete(dyn, 1); goto getmsg_init; } break; } length--; if (!--i) { if ((d = d->next)) { s = d->data; i = MIN(DBUFSIZ, dlen); } } else s++; } if (copy <= 0) return 0; /* ** copy as much of the message as wanted into parse buffer */ i = dbuf_get(dyn, buf, MIN(copy, length)); /* ** and delete the rest of it! */ if (copy - i > 0) (void)dbuf_delete(dyn, copy - i); if (i >= 0) *(buf+i) = '\0'; /* mark end of messsage */ return i; }