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