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