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