chiark / gitweb /
@@@ fltfmt fettling
[mLib] / buf / lbuf.c
1 /* -*-c-*-
2  *
3  * Block-to-line buffering
4  *
5  * (c) 1999 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of the mLib utilities library.
11  *
12  * mLib is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU Library General Public License as
14  * published by the Free Software Foundation; either version 2 of the
15  * License, or (at your option) any later version.
16  *
17  * mLib is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with mLib; if not, write to the Free
24  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25  * MA 02111-1307, USA.
26  */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include <assert.h>
31 #include <string.h>
32
33 #include "alloc.h"
34 #include "arena.h"
35 #include "lbuf.h"
36
37 /*----- Main code ---------------------------------------------------------*/
38
39 /* --- @lbuf_flush@ --- *
40  *
41  * Arguments:   @lbuf *b@ = pointer to buffer block
42  *              @char *p@ = pointer to where to start searching
43  *              @size_t len@ = length of new material added
44  *
45  * Returns:     ---
46  *
47  * Use:         Flushes any complete lines in a line buffer.  New material
48  *              is assumed to have been added starting at @p@.  If @p@ is
49  *              null, then the scan starts at the beginning of the buffer,
50  *              and the size of data already in the buffer is used in place
51  *              of @len@.
52  *
53  *              It is assumed that the buffer is initially enabled.  You
54  *              shouldn't be contributing data to a disabled buffer anyway.
55  *              However, the buffer handler may at some point disable itself,
56  *              and @lbuf_flush@ can cope with this eventuality.  Any pending
57  *              data is left at the start of the buffer and can be flushed
58  *              out by calling @lbuf_flush(b, 0, 0)@ if the buffer is ever
59  *              re-enabled.
60  */
61
62 void lbuf_flush(lbuf *b, char *p, size_t len)
63 {
64   char *l;                              /* Limit of data in buffer */
65   char *q;                              /* Roving pointer through string */
66   char *base;                           /* Base address of current line */
67   int cr;                               /* Carriage return state */
68
69   if (b->f & LBUF_CLOSE) {
70     b->func(0, 0, b->p);
71     return;
72   }
73
74   /* --- Initialize variables as necessary --- */
75
76   if (!p) {
77     p = b->buf;
78     cr = 0;
79     len = b->len;
80   } else
81     cr = b->f & LBUF_CR;
82
83   l = p + len;
84
85   /* --- Clear @base@ if I'm discarding an overlong line --- */
86
87   if (b->len == b->sz)
88     base = 0;
89   else
90     base = b->buf;
91
92   /* --- Now I march through the string --- */
93
94   for (q = p; q < l; q++) {
95
96     /* --- Quickly discard uninteresting characters --- */
97
98     switch (b->delim) {
99       case LBUF_CRLF:
100       case LBUF_STRICTCRLF:
101         if (*q != '\r' && *q != '\n') {
102           cr = 0;
103           continue;
104         }
105         if (*q == '\r') {
106           cr = 1;
107           continue;
108         }
109         if (!cr && b->delim == LBUF_STRICTCRLF)
110           continue;
111         break;
112       default:
113         if (*q != b->delim)
114           continue;
115     }
116
117     /* --- I have a positive ID on a delimiter --- *
118      *
119      * If I'm interested in this string, report it to my owner.
120      */
121
122     if (base) {
123       len = q - base;
124       if (cr)
125         len--;                          /* Exercise: why is this safe? */
126       base[len] = 0;
127       b->func(base, len, b->p);
128       if (!(b->f & LBUF_ENABLE)) {
129         base = q + 1;
130         break;
131       }
132     }
133     base = q + 1;
134     cr = 0;
135   }
136
137   /* --- Sift through the aftermath --- */
138
139   if (base) {
140     len = l - base;
141     if (len == b->sz) {
142       b->buf[len - 1] = 0;
143       b->func(base, len - 1, b->p);
144     } else if (base != b->buf)
145       memmove(b->buf, base, len);
146     b->len = len;
147     if (cr)
148       b->f |= LBUF_CR;
149     else
150       b->f &= ~LBUF_CR;
151   }
152 }
153
154 /* --- @lbuf_close@ --- *
155  *
156  * Arguments:   @lbuf *b@ = pointer to buffer block
157  *
158  * Returns:     ---
159  *
160  * Use:         Empties the buffer of any data currently lurking in it, and
161  *              informs the client that this has happened.  It's assumed that
162  *              the buffer is enabled: you shouldn't be reading close events
163  *              on disabled buffers.  The buffer, if allocated, is freed.
164  */
165
166 void lbuf_close(lbuf *b)
167 {
168   if (b->len && b->len != b->sz) {
169     b->buf[b->len] = 0;
170     b->func(b->buf, b->len, b->p);
171   }
172   if (b->buf) {
173     x_free(b->a, b->buf);
174     b->buf = 0;
175   }
176   b->f |= LBUF_CLOSE;
177   if (b->f & LBUF_ENABLE)
178     b->func(0, 0, b->p);
179 }
180
181 /* --- @lbuf_free@ --- *
182  *
183  * Arguments:   @lbuf *b@ = pointer to buffer block
184  *              @char **p@ = output pointer to free space
185  *
186  * Returns:     Free buffer size.
187  *
188  * Use:         Returns the free portion of a line buffer.  Data can then be
189  *              written to this portion, and split out into lines by calling
190  *              @lbuf_flush@.  A buffer is allocated if none currently
191  *              exists.
192  */
193
194 size_t lbuf_free(lbuf *b, char **p)
195 {
196   /* --- There's a special case to consider --- *
197    *
198    * If a line from the file wouldn't fit in the buffer, I truncate it and
199    * return what would fit.  The rest of the line ought to be discarded.
200    * This condition is signalled by @len = b->sz@, and means that the entire
201    * buffer is OK to be trashed.  In other cases, @len@ is the amount of
202    * space currently occupied in the buffer.  This special case is the reason
203    * this routine exists.
204    */
205
206   if (b->len != 0 && b->len != b->sz) {
207     *p = b->buf + b->len;
208     return (b->sz - b->len);
209   } else {
210     if (!b->buf)
211       b->buf = x_alloc(b->a, b->sz);
212     *p = b->buf;
213     return (b->sz);
214   }
215 }
216
217 /* --- @lbuf_snarf@ --- *
218  *
219  * Arguments:   @lbuf *b@ = pointer to buffer block
220  *              @const void *p@ = pointer to input data buffer
221  *              @size_t sz@ = size of data in input buffer
222  *
223  * Returns:     ---
224  *
225  * Use:         Snarfs the data from the input buffer and spits it out as
226  *              lines.  This interface ignores the complexities of dealing
227  *              with disablement: you should be using @lbuf_free@ to
228  *              contribute data if you want to cope with that.
229  */
230
231 void lbuf_snarf(lbuf *b, const void *p, size_t sz)
232 {
233   const char *pp = p;
234   while (sz && (b->f & LBUF_ENABLE)) {
235     size_t bsz;
236     char *bp;
237
238     bsz = lbuf_free(b, &bp);
239     if (bsz > sz)
240       bsz = sz;
241     memcpy(bp, pp, bsz);
242     lbuf_flush(b, bp, bsz);
243     pp += bsz;
244     sz -= bsz;
245   }
246 }
247
248 /* --- @lbuf_setsize@ --- *
249  *
250  * Arguments:   @lbuf *b@ = pointer to buffer block
251  *              @size_t sz@ = requested maximum line size
252  *
253  * Returns:     ---
254  *
255  * Use:         Modifies the size of the buffer associated with the block.
256  *              It is an error to resize a buffer while it contains data.
257  */
258
259 void lbuf_setsize(lbuf *b, size_t sz)
260 {
261   if (b->buf)
262   assert(((void)"Buffer in use in lbuf_setsize",
263          b->len == 0 || b->len == b->sz));
264   if (b->buf)
265     x_free(b->a, b->buf);
266   b->sz = sz;
267   b->buf = 0;
268 }
269
270 /* --- @lbuf_init@ --- *
271  *
272  * Arguments:   @lbuf *b@ = pointer to buffer block
273  *              @lbuf_func *func@ = handler function
274  *              @void *p@ = argument pointer for @func@
275  *
276  * Returns:     ---
277  *
278  * Use:         Initializes a line buffer block.  Any recognized lines are
279  *              passed to @func@ for processing.  No buffer is initially
280  *              allocated; this is done when the buffer is actually required
281  *              for the first time.
282  */
283
284 void lbuf_init(lbuf *b, lbuf_func *func, void *p)
285 {
286   b->func = func;
287   b->p = p;
288   b->len = 0;
289   b->f = LBUF_ENABLE;
290   b->delim = LBUF_CRLF;
291   b->buf = 0;
292   b->a = arena_global;
293   lbuf_setsize(b, 256);
294 }
295
296 /* --- @lbuf_destroy@ --- *
297  *
298  * Arguments:   @lbuf *b@ = pointer to buffer block
299  *
300  * Returns:     ---
301  *
302  * Use:         Deallocates a line buffer and frees any resources it owned.
303  */
304
305 void lbuf_destroy(lbuf *b)
306 {
307   if (b->buf) {
308     x_free(b->a, b->buf);
309     b->buf = 0;
310   }
311 }
312
313 /*----- That's all, folks -------------------------------------------------*/