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
6 /* read.c, liboop, copyright 2000 Ian jackson
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. */
20 #undef MIN /* for systems that define it */
21 #define MIN(a,b) ((a)<(b) ? (a) : (b))
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);
27 static int set_time_ifbuf(oop_source *oop, oop_read *rd);
28 static void cancel_time(oop_source *oop, oop_read *rd);
30 const oop_rd_style OOP_RD_STYLE_GETLINE[]= {{
31 OOP_RD_DELIM_STRIP,'\n', OOP_RD_NUL_FORBID, OOP_RD_SHORTREC_EOF,
33 const oop_rd_style OOP_RD_STYLE_BLOCK[]= {{
34 OOP_RD_DELIM_NONE, 0, OOP_RD_NUL_PERMIT, OOP_RD_SHORTREC_EOF,
36 const oop_rd_style OOP_RD_STYLE_IMMED[]= {{
37 OOP_RD_DELIM_NONE, 0, OOP_RD_NUL_PERMIT, OOP_RD_SHORTREC_SOONEST,
41 /* set at creation time: */
45 /* persistent state */
46 oop_rd_bufctl_op readahead; /* _ENABLE or _DISABLE */
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 */
54 oop_rd_call *call_ok, *call_err;
55 void *data_ok, *data_err;
58 /* Buffer is structured like this if displacedchar>=0 and delim found:
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 --------->
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
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
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
89 static const char *const eventstrings_nl[]= {
90 "INTERNAL ERROR (_nl _OK) please report",
92 "Missing newline at end of file",
95 "Nul byte, in line which is also too long",
96 "INTERNAL ERROR (_nl _SYSTEM) please report"
99 static const char *const eventstrings_other[]= {
100 "Record read successfully",
102 "Incomplete record at end of file",
105 "Nul byte in record which is also too long",
109 oop_read *oop_rd_new(oop_source *oop, oop_readable *ra, char *buf, size_t bufsz) {
112 assert(buf ? bufsz>=2 : !bufsz);
114 rd= oop_malloc(sizeof(*rd)); if (!rd) goto x_fail;
118 rd->readahead= OOP_RD_BUFCTL_ENABLE;
121 rd->alloc= buf ? bufsz : 0;
124 rd->displacedchar= -1;
125 rd->style= *OOP_RD_STYLE_IMMED;
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 */
139 static void cancel_time(oop_source *oop, oop_read *rd) {
140 oop->cancel_time(oop,OOP_TIME_NOW,on_time,rd);
142 static int set_read(oop_source *oop, oop_read *rd) {
143 return rd->ra->on_readable(rd->ra,on_readable,rd), 0; /* fixme */
145 static void cancel_read(oop_source *oop, oop_read *rd) {
146 rd->ra->on_cancel(rd->ra);
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;
158 if (style->delim_mode == OOP_RD_DELIM_NONE ||
159 rd->style.delim_mode == OOP_RD_DELIM_NONE ||
160 style->delim != rd->style.delim)
164 rd->maxrecsz= maxrecsz;
165 rd->call_ok= ifok; rd->data_ok= data_ok;
166 rd->call_err= iferr; rd->data_err= data_err;
168 er= set_read(oop,rd); if (er) return er;
169 er= set_time_ifbuf(oop,rd); if (er) return er;
173 void oop_rd_delete(oop_read *rd) {
174 rd->ra->on_cancel(rd->ra);
175 oop_free(rd->allocbuf);
179 void oop_rd_cancel(oop_read *rd) {
180 cancel_time(rd->oop,rd);
181 cancel_read(rd->oop,rd);
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];
192 return eventstrings_other[event];
195 static void *on_readable(oop_source *oop, oop_readable *ra, void *rd_void) {
196 oop_read *rd= rd_void;
198 assert(oop == rd->oop);
199 assert(ra == rd->ra);
200 return on_process(oop,rd,1);
203 static void *on_time(oop_source *oop, struct timeval when, void *rd_void) {
204 oop_read *rd= rd_void;
206 assert(oop == rd->oop);
207 return on_process(oop,rd,0);
210 static size_t calc_dataspace(oop_read *rd) {
211 if (rd->style.delim_mode == OOP_RD_DELIM_STRIP) {
214 return rd->alloc ? rd->alloc-1 : 0;
218 static void *on_process(oop_source *oop, oop_read *rd, int try_read) {
220 int evkind; /* 0=none, -1=error, 1=something */
221 int errnoval, nread, cancelnow;
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 */
243 rd->used -= rd->discard;
244 if (rd->neednotcheck > rd->discard) {
245 rd->neednotcheck -= rd->discard;
249 memmove(buf, buf + rd->discard, rd->used);
252 if (rd->displacedchar >= 0) {
253 assert(rd->used > 0);
254 buf[0]= rd->displacedchar;
255 rd->displacedchar= -1;
258 maxrecsz= rd->maxrecsz ? rd->maxrecsz : INT_MAX / 5 /* allows +20 and *4 */;
259 maxbufreqd= maxrecsz+1;
261 if (rd->userbuf && maxbufreqd > rd->alloc) {
262 maxrecsz -= (maxbufreqd - rd->alloc);
263 maxbufreqd= rd->alloc;
266 if (rd->style.delim_mode == OOP_RD_DELIM_STRIP) {
267 readahead= maxrecsz+1;
275 thisrecdata= thisrecsz= 0;
278 assert(rd->used <= rd->alloc);
279 dataspace= calc_dataspace(rd);
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))) {
286 thisrecsz= (delimp - buf);
287 thisrecdata= thisrecsz+1;
288 if (rd->style.delim_mode == OOP_RD_DELIM_KEEP)
289 thisrecsz= thisrecdata;
293 } else if (rd->used >= readahead) {
295 thisrecsz= thisrecdata= maxrecsz;
298 if (rd->style.delim_mode == OOP_RD_DELIM_NONE) {
302 if (rd->style.shortrec_mode < OOP_RD_SHORTREC_LONG) {
304 thisrecsz= thisrecdata= 0;
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) {
312 thisrecdata= rd->used;
313 if (thisrecdata == rd->alloc) thisrecdata--;
314 thisrecsz= thisrecdata;
321 if (evkind && thisrecdata && thisrecsz >= rd->alloc) {
322 /* Need to make space for the trailing nul */
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;
329 want= MIN(want, maxbufreqd);
333 assert(!rd->userbuf);
334 assert(want <= maxbufreqd);
336 buf= oop_realloc(rd->allocbuf,want);
338 event= OOP_RD_SYSTEM;
341 thisrecsz= thisrecdata= 0;
348 if (evkind) break; /* OK, process it then */
350 if (!try_read) return OOP_CONTINUE; /* But we weren't told it was ready. */
352 dataspace= calc_dataspace(rd);
353 want= MIN(dataspace, readahead);
354 assert(rd->used < want);
357 nread= rd->ra->try_read(rd->ra, buf+rd->used, want-rd->used);
358 if (errno == EAGAIN) return OOP_CONTINUE;
361 rd->neednotcheck= rd->used;
366 if (nread < 0) { /* read error */
368 event= OOP_RD_SYSTEM;
371 thisrecsz= thisrecdata= rd->used;
377 event= OOP_RD_PARTREC;
378 evkind= (rd->style.shortrec_mode == OOP_RD_SHORTREC_FORBID) ? -1 : +1;
379 thisrecsz= thisrecdata= rd->used;
389 /* OK, we have an event of some kind */
391 /* Nul byte handling */
392 if (thisrecsz > 0 && rd->style.nul_mode != OOP_RD_NUL_PERMIT) {
397 (nul= memchr(buf+checked,0,thisrecsz-checked));
399 if (rd->style.nul_mode == OOP_RD_NUL_FORBID) {
402 thisrecdata= thisrecsz= 0;
405 assert(rd->style.nul_mode == OOP_RD_NUL_DISCARD);
407 notnul < buf+thisrecsz && notnul == '\0';
409 thisrecsz-= (notnul-nul);
411 memmove(nul,notnul,thisrecsz-checked);
415 /* Checks that all is well */
418 assert(thisrecsz <= thisrecdata);
419 assert(!rd->maxrecsz || thisrecsz <= rd->maxrecsz);
420 assert(thisrecdata <= rd->used);
422 rd->discard= thisrecdata;
424 cancelnow= (evkind < 0) || (event == OOP_RD_EOF);
427 errnoval= set_time_ifbuf(oop,rd);
429 event= OOP_RD_SYSTEM;
432 thisrecsz= thisrecdata= 0;
439 call_data= rd->data_err;
440 errmsg= oop_rd_errmsg(rd,event,errnoval,&rd->style);
443 call_data= rd->data_ok;
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];
459 call(oop,rd, event,errmsg,errnoval,
460 (thisrecdata ? buf : 0), thisrecsz, call_data);
463 oop_read *oop_rd_new_fd(oop_source *oop, int fd, char *buf, size_t bufsz) {
467 ra= oop_readable_fd(oop,fd);
470 rd= oop_rd_new(oop,ra,buf,bufsz);
471 if (!rd) { ra->delete_tidy(ra); return 0; }
476 int oop_rd_delete_tidy(oop_read *rd) {
477 oop_readable *ra= rd->ra;
479 return ra->delete_tidy(ra);
482 void oop_rd_delete_kill(oop_read *rd) {
483 oop_readable *ra= rd->ra;