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