/*===========================================================================* * * * sfllbuf.c - * * * * Copyright (c) 1991-2003 iMatix Corporation * * * * ------------------ GPL Licensed Source Code ------------------ * * iMatix makes this software available under the GNU General * * Public License (GPL) license for open source projects. For * * details of the GPL license please see www.gnu.org or read the * * file license.gpl provided in this package. * * * * 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 2 of * * the License, 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 in the file 'license.gpl'; if * * not, write to the Free Software Foundation, Inc., 59 Temple * * Place - Suite 330, Boston, MA 02111-1307, USA. * * * * You can also license this software under iMatix's General Terms * * of Business (GTB) for commercial projects. If you have not * * explicitly licensed this software under the iMatix GTB you may * * only use it under the terms of the GNU General Public License. * * * * For more information, send an email to info@imatix.com. * * -------------------------------------------------------------- * *===========================================================================*/ #include "prelude.h" /* Universal header file */ #include "sfllist.h" /* Linked-list functions */ #include "sflmem.h" /* Memory handling functions */ #include "sflstr.h" /* String handling functions */ #include "sfllbuf.h" /* Prototypes for functions */ static char *start_next_line (LINEBUF *buffer, const char *lineptr); static char *get_line (LINEBUF *buffer, DESCR *descr, const char *line); #define buffer_inc(p) if (++(p) == buffer-> top) p = buffer-> data #define buffer_dec(p) if (p == buffer-> data) p = buffer-> top - 1; else (p)-- /* ---------------------------------------------------------------------[<]- Function: linebuf_create Synopsis: Creates a new line buffer with the specified size. The size must be at least LINE_MAX + 1 characters long. Returns the address of the newly-created buffer, or NULL if there was insufficient memory. The fresh line buffer is set to empty (tail == head). ---------------------------------------------------------------------[>]-*/ LINEBUF * linebuf_create (size_t maxsize) { LINEBUF *buffer; ASSERT (maxsize > LINE_MAX); buffer = mem_alloc (sizeof (LINEBUF)); if (!buffer) return (NULL); buffer-> data = mem_alloc (maxsize); if (!buffer-> data) { free (buffer); return (NULL); } buffer-> head = buffer-> data; buffer-> tail = buffer-> data; buffer-> top = buffer-> data + maxsize; buffer-> size = maxsize; return (buffer); } /* ---------------------------------------------------------------------[<]- Function: linebuf_destroy Synopsis: Destroys a line buffer and frees its memory. ---------------------------------------------------------------------[>]-*/ void linebuf_destroy (LINEBUF *buffer) { ASSERT (buffer); mem_free (buffer-> data); mem_free (buffer); } /* ---------------------------------------------------------------------[<]- Function: linebuf_reset Synopsis: Resets a line buffer; i.e. empties it of all data. This is done simply by setting the tail and head to the start of the buffer. ---------------------------------------------------------------------[>]-*/ void linebuf_reset (LINEBUF *buffer) { ASSERT (buffer); buffer-> head = buffer-> data; buffer-> tail = buffer-> data; } /* ---------------------------------------------------------------------[<]- Function: linebuf_append Synopsis: Appends a line to the line buffer. If the buffer was full, the oldest line is lost. Updates the buffer head and tail as needed. ---------------------------------------------------------------------[>]-*/ void linebuf_append (LINEBUF *buffer, const char *line) { int length, /* Size of line to insert */ room_left, /* Space left between head and top */ tail_old, /* Offset of tail into buffer */ head_old, /* Offset of head before insert */ head_new; /* Offset of head after insert */ char *linedata; /* Address of data to store */ ASSERT (buffer); ASSERT (line); linedata = (char *) line; length = strlen (line) + 1; /* Include trailing null */ room_left = (int) (buffer-> top - buffer-> head); /* We need to make space for the new line; we calculate the new head * and if the tail falls between the old and new head, it must be moved * up to the next line start. We compare 'ints' not 'char *' because * they can be negative. */ tail_old = (int) (buffer-> tail - buffer-> data); head_old = (int) (buffer-> head - buffer-> data); if (head_old > tail_old) /* Shift head_old down to get it */ head_old -= buffer-> size; /* somewhere before tail_old */ head_new = head_old + length; /* And calculate head_new */ /* If the line is too large for the remaining space, copy what we can */ if (length > room_left) { memcpy (buffer-> head, linedata, room_left); linedata += room_left; length -= room_left; buffer-> head = buffer-> data; /* Bump head to start of buffer */ } /* Copy rest of line to buffer */ memcpy (buffer-> head, linedata, length); buffer-> head += length; /* Bump head past string */ if (buffer-> head == buffer-> top) /* and maybe wrap-around */ buffer-> head = buffer-> data; ASSERT (buffer-> head <= buffer-> top); if (head_old < tail_old /* If tail falls between head_old */ && tail_old <= head_new) /* and/on head_new, bump it up */ buffer-> tail = start_next_line (buffer, buffer-> head); } /* ------------------------------------------------------------------------- * start_next_line -- local * * Returns the address of the character following the first null byte after * lineptr. Wraps-around the end-start of the buffer if it has to. */ static char * start_next_line (LINEBUF *buffer, const char *lineptr) { char *next; next = (char *) memchr (lineptr, 0, (size_t) (buffer-> top - (char *) lineptr)); if (next == NULL) next = (char *) memchr (buffer-> data, 0, buffer-> size); ASSERT (next != NULL); /* If not there, abandon ship! */ buffer_inc (next); return (next); } /* ---------------------------------------------------------------------[<]- Function: linebuf_first Synopsis: Fetches the oldest line in the buffer. Returns a pointer that may be used in calls to linebuf_next(). Returns NULL if the buffer is empty. The line is stored in the supplied descriptor, and is truncated if the descriptor is too small. ---------------------------------------------------------------------[>]-*/ char * linebuf_first (LINEBUF *buffer, DESCR *descr) { ASSERT (buffer); ASSERT (descr); return (linebuf_next (buffer, descr, buffer-> tail)); } /* ------------------------------------------------------------------------- * get_line -- local * * Picks-up the line at the specified address and stores it in the descr * data, truncating to the descr size if necessary. Returns address of * next line in the buffer. */ static char * get_line (LINEBUF *buffer, DESCR *descr, const char *line) { char *dest = (char *) descr-> data; size_t size = 0; while (*line && (size < descr-> size - 1)) { *dest++ = *line; buffer_inc (line); } *dest = '\0'; /* Terminate with a null */ buffer_inc (line); /* Bump past null */ return ((char *) line); } /* ---------------------------------------------------------------------[<]- Function: linebuf_next Synopsis: Fetches the next line in the buffer, using the pointer that was returned by linebuf_first(). Returns NULL if there are no more lines in the buffer, or a pointer for further calls. The line is stored in the supplied descriptor, and is truncated if the descriptor is too small. ---------------------------------------------------------------------[>]-*/ char * linebuf_next (LINEBUF *buffer, DESCR *descr, const char *curline) { ASSERT (buffer); ASSERT (descr); ASSERT (curline); if (curline == buffer-> head) return (NULL); /* We're at the end */ else return (get_line (buffer, descr, curline)); } /* ---------------------------------------------------------------------[<]- Function: linebuf_last Synopsis: Fetches the newest line in the buffer. Returns a pointer that may be used in calls to linebuf_next(). Returns NULL if the buffer is empty. The line is stored in the supplied descriptor, and is truncated if the descriptor is too small. ---------------------------------------------------------------------[>]-*/ char * linebuf_last (LINEBUF *buffer, DESCR *descr) { ASSERT (buffer); ASSERT (descr); return (linebuf_prev (buffer, descr, buffer-> head)); } /* ---------------------------------------------------------------------[<]- Function: linebuf_prev Synopsis: Fetches the previous line in the buffer, using the pointer that was returned by linebuf_last(). Returns NULL if there are no more lines in the buffer, or a pointer for further calls. The line is stored in the supplied descriptor, and is truncated if the descriptor is too small. ---------------------------------------------------------------------[>]-*/ char * linebuf_prev (LINEBUF *buffer, DESCR *descr, const char *curline) { ASSERT (buffer); ASSERT (descr); ASSERT (curline); if (curline == buffer-> tail) return (NULL); /* We're at the start */ else { /* We're pointing to the byte after the line's null byte */ buffer_dec (curline); /* Bump down to null */ ASSERT (*curline == '\0'); do { buffer_dec (curline); /* And now look for previous null */ if (*curline == '\0') { buffer_inc (curline); /* Bump up to start of string */ break; } } until (curline == buffer-> tail); get_line (buffer, descr, curline); return ((char *) curline); } }