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