chiark / gitweb /
wip message tidying
[innduct.git] / __oop-read-copy.c
1 /* THIS IS A COPY OF THE FILE WHICH SHOULD BE IN LIBOOP
2 ** When liboop gets the two tiny bugfixes which are here,
3 ** this file can and should be eliminated   -iwj 29.4.2010
4 */
5
6 /* read.c, liboop, copyright 2000 Ian jackson
7    
8    This is free software; you can redistribute it and/or modify it under the
9    terms of the GNU Lesser General Public License, version 2.1 or later.
10    See the file COPYING for details. */
11
12 #include "oop.h"
13 #include "oop-read.h"
14
15 #include <string.h>
16 #include <errno.h>
17 #include <assert.h>
18 #include <limits.h>
19
20 #undef MIN /* for systems that define it */
21 #define MIN(a,b) ((a)<(b) ? (a) : (b))
22
23 static void *on_time(oop_source*, struct timeval, void*);
24 static void *on_readable(oop_source*, oop_readable*, void*);
25 static void *on_process(oop_source*, oop_read*, int try_read);
26
27 static int set_time_ifbuf(oop_source *oop, oop_read *rd);
28 static void cancel_time(oop_source *oop, oop_read *rd);
29
30 const oop_rd_style OOP_RD_STYLE_GETLINE[]= {{
31   OOP_RD_DELIM_STRIP,'\n', OOP_RD_NUL_FORBID, OOP_RD_SHORTREC_EOF,
32 }};
33 const oop_rd_style OOP_RD_STYLE_BLOCK[]= {{
34   OOP_RD_DELIM_NONE, 0,    OOP_RD_NUL_PERMIT, OOP_RD_SHORTREC_EOF,
35 }};
36 const oop_rd_style OOP_RD_STYLE_IMMED[]= {{
37   OOP_RD_DELIM_NONE, 0,    OOP_RD_NUL_PERMIT, OOP_RD_SHORTREC_SOONEST,
38 }};
39
40 struct oop_read {
41   /* set at creation time: */
42   oop_source *oop;
43   oop_readable *ra;
44   char *userbuf;
45   /* persistent state */
46   oop_rd_bufctl_op readahead; /* _ENABLE or _DISABLE */
47   char *allocbuf;
48   size_t alloc, used, discard;
49   size_t neednotcheck; /* data we've already searched for delimiter */
50   int displacedchar; /* >=0, first unused */
51   /* arguments to oop_rd_read */
52   oop_rd_style style;
53   size_t maxrecsz;
54   oop_rd_call *call_ok, *call_err;
55   void *data_ok, *data_err;
56 };
57
58 /* Buffer is structured like this if displacedchar>=0 and delim found:
59  *
60  *              done stuff,    displaced readahead - read     unused
61  *              we've called  delimiter| but not yet          buffer
62  *              back for              || returned             space
63  *              ddddddddddddddddddddddDOaaaaaaaaaaaaaaaaaaa____________
64  *              <------- discard ----->
65  *              <----------------------- used ------------>
66  *              <------------------------------------- alloc --------->
67  *
68  * If displacedchar>=0 then the the first character of readahead has
69  * been displaced by a nul byte and is stored in displacedchar.  If
70  * _DELIM_STRIP and the delimiter is found then the nul overwrites the
71  * delimiter.
72  *
73  *               Buffer when full   {this,max}                  may need
74  * DELIM found?  <-recval->      recdata  buffer required       readahead
75  *  NONE  n/a    ddddddddddOaaa_ recsz    recdata+1 == recsz+1  maxrecsz
76  *  KEEP  Yes    dddddddddDOaaa_ recsz    recdata+1 == recsz+1  maxrecsz
77  *  KEEP  No     ddddddddddOaaa_ recsz    recdata+1 == recsz+1  maxrecsz
78  *  STRIP Yes    dddddddddd0aaaa recsz+1  recdata   == recsz+1  maxrecsz+1
79  *  STRIP No     ddddddddddOaaaa recsz    recdata+1 == recsz+1  maxrecsz+1
80  *
81  * Key:  d = data to be returned
82  *       D = delimiter, being returned
83  *       a = readahead, not to be returned
84  *       O = readahead character displaced by a nul
85  *       0 = delimiter replaced by a nul
86  *       _ = unused
87  */
88
89 static const char *const eventstrings_nl[]= {
90   "INTERNAL ERROR (_nl _OK) please report",
91   "End of file",
92   "Missing newline at end of file",
93   "Line too long",
94   "Nul byte",
95   "Nul byte, in line which is also too long",
96   "INTERNAL ERROR (_nl _SYSTEM) please report"
97 };
98
99 static const char *const eventstrings_other[]= {
100   "Record read successfully",
101   "End of file",
102   "Incomplete record at end of file",
103   "Record too long",
104   "Nul byte",
105   "Nul byte in record which is also too long",
106   "System error"
107 };
108
109 oop_read *oop_rd_new(oop_source *oop, oop_readable *ra, char *buf, size_t bufsz) {
110   oop_read *rd= 0;
111
112   assert(buf ? bufsz>=2 : !bufsz);
113
114   rd= oop_malloc(sizeof(*rd));  if (!rd) goto x_fail;
115   rd->oop= oop;
116   rd->ra= ra;
117   rd->userbuf= buf;
118   rd->readahead= OOP_RD_BUFCTL_ENABLE;
119   rd->allocbuf= 0;
120   rd->used= 0;
121   rd->alloc= buf ? bufsz : 0;
122   rd->discard= 0;
123   rd->neednotcheck= 0;
124   rd->displacedchar= -1;
125   rd->style= *OOP_RD_STYLE_IMMED;
126
127   return rd;
128
129 x_fail:
130   oop_free(rd);
131   return 0;
132 }
133
134 static int set_time_ifbuf(oop_source *oop, oop_read *rd) {
135   if (rd->used > rd->discard)
136     return oop->on_time(oop,OOP_TIME_NOW,on_time,rd), 0; /* fixme */
137   return 0;
138 }
139 static void cancel_time(oop_source *oop, oop_read *rd) {
140   oop->cancel_time(oop,OOP_TIME_NOW,on_time,rd);
141 }
142 static int set_read(oop_source *oop, oop_read *rd) {
143   return rd->ra->on_readable(rd->ra,on_readable,rd), 0; /* fixme */
144 }
145 static void cancel_read(oop_source *oop, oop_read *rd) {
146   rd->ra->on_cancel(rd->ra);
147 }
148
149 int oop_rd_read(oop_read *rd, const oop_rd_style *style, size_t maxrecsz,
150                 oop_rd_call *ifok, void *data_ok,
151                 oop_rd_call *iferr, void *data_err) {
152   oop_source *oop= rd->oop;
153   int er;
154
155   cancel_time(oop,rd);
156   cancel_read(oop,rd);
157
158   if (style->delim_mode == OOP_RD_DELIM_NONE ||
159       rd->style.delim_mode == OOP_RD_DELIM_NONE ||
160       style->delim != rd->style.delim)
161     rd->neednotcheck= 0;
162
163   rd->style= *style;
164   rd->maxrecsz= maxrecsz;
165   rd->call_ok= ifok; rd->data_ok= data_ok;
166   rd->call_err= iferr; rd->data_err= data_err;
167
168   er= set_read(oop,rd);        if (er) return er;
169   er= set_time_ifbuf(oop,rd);  if (er) return er;
170   return 0;
171 }
172
173 void oop_rd_delete(oop_read *rd) {
174   rd->ra->on_cancel(rd->ra);
175   oop_free(rd->allocbuf);
176   oop_free(rd);
177 }
178
179 void oop_rd_cancel(oop_read *rd) {
180   cancel_time(rd->oop,rd);
181   cancel_read(rd->oop,rd);
182 }
183
184 const char *oop_rd_errmsg(oop_read *rd, oop_rd_event event, int errnoval,
185                           const oop_rd_style *style) {
186   if (event == OOP_RD_SYSTEM)
187     return strerror(errnoval);
188   else if (style && style->delim_mode != OOP_RD_DELIM_NONE
189            && style->delim == '\n')
190     return eventstrings_nl[event];
191   else
192     return eventstrings_other[event];
193 }
194
195 static void *on_readable(oop_source *oop, oop_readable *ra, void *rd_void) {
196   oop_read *rd= rd_void;
197
198   assert(oop == rd->oop);
199   assert(ra == rd->ra);
200   return on_process(oop,rd,1);
201 }
202
203 static void *on_time(oop_source *oop, struct timeval when, void *rd_void) {
204   oop_read *rd= rd_void;
205
206   assert(oop == rd->oop);
207   return on_process(oop,rd,0);
208 }
209
210 static size_t calc_dataspace(oop_read *rd) {
211   if (rd->style.delim_mode == OOP_RD_DELIM_STRIP) {
212     return rd->alloc;
213   } else {
214     return rd->alloc ? rd->alloc-1 : 0;
215   }
216 }
217
218 static void *on_process(oop_source *oop, oop_read *rd, int try_read) {
219   oop_rd_event event;
220   int evkind; /* 0=none, -1=error, 1=something */
221   int errnoval, nread, cancelnow;
222   oop_rd_call *call;
223   char *buf, *delimp;
224   const char *errmsg;
225   size_t maxrecsz; /* like in arg to oop_rd_read, but 0 -> large val */
226   size_t maxbufreqd; /* maximum buffer we might possibly want to alloc */
227   size_t readahead; /* max amount of data we might want to readahead */
228   size_t want; /* amount we want to allocate or data we want to read */
229   size_t dataspace; /* amount of buffer we can usefully fill with data */
230   size_t thisrecsz; /* length of the record we've found */
231   size_t thisrecdata; /* length of data representing the record */
232   void *call_data;
233
234   cancel_time(oop,rd);
235
236   if (rd->userbuf) {
237     buf= rd->userbuf;
238   } else {
239     buf= rd->allocbuf;
240   }
241   
242   if (rd->discard) {
243     rd->used -= rd->discard;
244     if (rd->neednotcheck > rd->discard) {
245       rd->neednotcheck -= rd->discard;
246     } else {
247       rd->neednotcheck= 0;
248     }
249     memmove(buf, buf + rd->discard, rd->used);
250     rd->discard= 0;
251   }
252   if (rd->displacedchar >= 0) {
253     assert(rd->used > 0);
254     buf[0]= rd->displacedchar;
255     rd->displacedchar= -1;
256   }
257
258   maxrecsz= rd->maxrecsz ? rd->maxrecsz : INT_MAX / 5 /* allows +20 and *4 */;
259   maxbufreqd= maxrecsz+1;
260
261   if (rd->userbuf && maxbufreqd > rd->alloc) {
262     maxrecsz -= (maxbufreqd - rd->alloc);
263     maxbufreqd= rd->alloc;
264   }
265
266   if (rd->style.delim_mode == OOP_RD_DELIM_STRIP) {
267     readahead= maxrecsz+1;
268   } else {
269     readahead= maxrecsz;
270   }
271
272   for (;;) {
273     evkind= 0;
274     event= -1;
275     thisrecdata= thisrecsz= 0;
276     errnoval= 0;
277
278     assert(rd->used <= rd->alloc);
279     dataspace= calc_dataspace(rd);
280     
281     if (/* delimiter already in buffer, within max record data ? */
282         rd->style.delim_mode != OOP_RD_DELIM_NONE &&
283         (delimp= memchr(buf + rd->neednotcheck, rd->style.delim,
284                         MIN(rd->used, readahead) - rd->neednotcheck))) {
285       
286       thisrecsz= (delimp - buf);
287       thisrecdata= thisrecsz+1;
288       if (rd->style.delim_mode == OOP_RD_DELIM_KEEP)
289         thisrecsz= thisrecdata;
290       event= OOP_RD_OK;
291       evkind= +1;
292
293     } else if (rd->used >= readahead) {
294       
295       thisrecsz= thisrecdata= maxrecsz;
296       evkind= +1;
297
298       if (rd->style.delim_mode == OOP_RD_DELIM_NONE) {
299         event= OOP_RD_OK;
300       } else {
301         event= OOP_RD_LONG;
302         if (rd->style.shortrec_mode < OOP_RD_SHORTREC_LONG) {
303           evkind= -1;
304           thisrecsz= thisrecdata= 0;
305         }
306       }
307
308     } else if (/* want to return ASAP, and we have something ? */
309                rd->style.shortrec_mode == OOP_RD_SHORTREC_SOONEST &&
310                rd->used > 0 && rd->alloc >= 2) {
311       
312       thisrecdata= rd->used;
313       if (thisrecdata == rd->alloc) thisrecdata--;
314       thisrecsz= thisrecdata;
315       event= OOP_RD_OK;
316       evkind= +1;
317
318     }
319
320     want= 0;
321     if (evkind && thisrecdata && thisrecsz >= rd->alloc) {
322       /* Need to make space for the trailing nul */
323       want= rd->alloc+1;
324     } else if (!evkind && !rd->userbuf &&
325                rd->used >= dataspace && rd->alloc < maxbufreqd) {
326       /* Need to make space to read more data */
327       want= rd->alloc + 20;
328       want <<= 2;
329       want= MIN(want, maxbufreqd);
330     }
331
332     if (want) {
333       assert(!rd->userbuf);
334       assert(want <= maxbufreqd);
335
336       buf= oop_realloc(rd->allocbuf,want);
337       if (!buf) {
338         event= OOP_RD_SYSTEM;
339         evkind= -1;
340         errnoval= ENOMEM;
341         thisrecsz= thisrecdata= 0;
342         break;
343       }
344       rd->allocbuf= buf;
345       rd->alloc= want;
346     }
347
348     if (evkind) break; /* OK, process it then */
349
350     if (!try_read) return OOP_CONTINUE; /* But we weren't told it was ready. */
351
352     dataspace= calc_dataspace(rd);
353     want= MIN(dataspace, readahead);
354     assert(rd->used < want);
355
356     errno= 0;
357     nread= rd->ra->try_read(rd->ra, buf+rd->used, want-rd->used);
358     if (errno == EAGAIN) return OOP_CONTINUE;
359
360     if (nread > 0) {
361       rd->neednotcheck= rd->used;
362       rd->used += nread;
363       continue;
364     }
365
366     if (nread < 0) { /* read error */
367
368       event= OOP_RD_SYSTEM;
369       evkind= -1;
370       errnoval= errno;
371       thisrecsz= thisrecdata= rd->used;
372       break;
373
374     } else {
375
376       if (rd->used) {
377         event= OOP_RD_PARTREC;
378         evkind= (rd->style.shortrec_mode == OOP_RD_SHORTREC_FORBID) ? -1 : +1;
379         thisrecsz= thisrecdata= rd->used;
380       } else {
381         event= OOP_RD_EOF;
382         evkind= +1;
383       }
384       break;
385
386     }
387   }
388
389   /* OK, we have an event of some kind */
390
391   /* Nul byte handling */
392   if (thisrecsz > 0 && rd->style.nul_mode != OOP_RD_NUL_PERMIT) {
393     size_t checked;
394     char *nul, *notnul;
395     
396     for (checked=0;
397          (nul= memchr(buf+checked,0,thisrecsz-checked));
398          ) {
399       if (rd->style.nul_mode == OOP_RD_NUL_FORBID) {
400         event= OOP_RD_NUL;
401         evkind= -1;
402         thisrecdata= thisrecsz= 0;
403         break;
404       }
405       assert(rd->style.nul_mode == OOP_RD_NUL_DISCARD);
406       for (notnul= nul+1;
407            notnul < buf+thisrecsz && notnul == '\0';
408            notnul++);
409       thisrecsz-= (notnul-nul);
410       checked= nul-buf;
411       memmove(nul,notnul,thisrecsz-checked);
412     }
413   }
414
415   /* Checks that all is well */
416
417   assert(evkind);
418   assert(thisrecsz <= thisrecdata);
419   assert(!rd->maxrecsz || thisrecsz <= rd->maxrecsz);
420   assert(thisrecdata <= rd->used);
421
422   rd->discard= thisrecdata;
423
424   cancelnow= (evkind < 0) || (event == OOP_RD_EOF);
425
426   if (!cancelnow) {
427     errnoval= set_time_ifbuf(oop,rd);
428     if (errnoval) {
429       event= OOP_RD_SYSTEM;
430       evkind= -1;
431       cancelnow= 1;
432       thisrecsz= thisrecdata= 0;
433       rd->discard= 0;
434     }
435   }
436
437   if (evkind < 0) {
438     call= rd->call_err;
439     call_data= rd->data_err;
440     errmsg= oop_rd_errmsg(rd,event,errnoval,&rd->style);
441   } else {
442     call= rd->call_ok;
443     call_data= rd->data_ok;
444     errmsg= 0;
445   }
446
447   if (thisrecdata) {
448     /* We have to fill in a nul byte. */
449     assert(thisrecsz < rd->alloc);
450     if (thisrecsz == thisrecdata && thisrecsz < rd->used)
451       rd->displacedchar= (unsigned char)buf[thisrecdata];
452     buf[thisrecsz]= 0;
453   }
454
455   if (cancelnow)
456     oop_rd_cancel(rd);
457
458   return
459     call(oop,rd, event,errmsg,errnoval,
460          (thisrecdata ? buf : 0), thisrecsz, call_data);
461 }
462
463 oop_read *oop_rd_new_fd(oop_source *oop, int fd, char *buf, size_t bufsz) {
464   oop_readable *ra;
465   oop_read *rd;
466
467   ra= oop_readable_fd(oop,fd);
468   if (!ra) return 0;
469
470   rd= oop_rd_new(oop,ra,buf,bufsz);
471   if (!rd) { ra->delete_tidy(ra); return 0; }
472
473   return rd;
474 }
475
476 int oop_rd_delete_tidy(oop_read *rd) {
477   oop_readable *ra= rd->ra;
478   oop_rd_delete(rd);
479   return ra->delete_tidy(ra);
480 }
481
482 void oop_rd_delete_kill(oop_read *rd) {
483   oop_readable *ra= rd->ra;
484   oop_rd_delete(rd);
485   ra->delete_kill(ra);
486 }