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