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