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