1 /* read.c, liboop, copyright 2000 Ian jackson
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. */
15 #undef MIN /* for systems that define it */
16 #define MIN(a,b) ((a)<(b) ? (a) : (b))
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);
22 static int set_time_ifbuf(oop_source *oop, oop_read *rd);
23 static void cancel_time(oop_source *oop, oop_read *rd);
25 const oop_rd_style OOP_RD_STYLE_GETLINE[]= {{
26 OOP_RD_DELIM_STRIP,'\n', OOP_RD_NUL_FORBID, OOP_RD_SHORTREC_EOF,
28 const oop_rd_style OOP_RD_STYLE_BLOCK[]= {{
29 OOP_RD_DELIM_NONE, 0, OOP_RD_NUL_PERMIT, OOP_RD_SHORTREC_EOF,
31 const oop_rd_style OOP_RD_STYLE_IMMED[]= {{
32 OOP_RD_DELIM_NONE, 0, OOP_RD_NUL_PERMIT, OOP_RD_SHORTREC_SOONEST,
36 /* set at creation time: */
40 /* persistent state */
41 oop_rd_bufctl_op readahead; /* _ENABLE or _DISABLE */
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 */
49 oop_rd_call *call_ok, *call_err;
50 void *data_ok, *data_err;
53 /* Buffer is structured like this if displacedchar>=0 and delim found:
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 --------->
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
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
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
84 static const char *const eventstrings_nl[]= {
85 "INTERNAL ERROR (_nl _OK) please report",
87 "Missing newline at end of file",
90 "Nul byte, in line which is also too long",
91 "INTERNAL ERROR (_nl _SYSTEM) please report"
94 static const char *const eventstrings_other[]= {
95 "Record read successfully",
97 "Incomplete record at end of file",
100 "Nul byte in record which is also too long",
104 oop_read *oop_rd_new(oop_source *oop, oop_readable *ra, char *buf, size_t bufsz) {
107 assert(buf ? bufsz>=2 : !bufsz);
109 rd= oop_malloc(sizeof(*rd)); if (!rd) goto x_fail;
113 rd->readahead= OOP_RD_BUFCTL_ENABLE;
116 rd->alloc= buf ? bufsz : 0;
119 rd->displacedchar= -1;
120 rd->style= *OOP_RD_STYLE_IMMED;
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 */
134 static void cancel_time(oop_source *oop, oop_read *rd) {
135 oop->cancel_time(oop,OOP_TIME_NOW,on_time,rd);
137 static int set_read(oop_source *oop, oop_read *rd) {
138 return rd->ra->on_readable(rd->ra,on_readable,rd), 0; /* fixme */
140 static void cancel_read(oop_source *oop, oop_read *rd) {
141 rd->ra->on_cancel(rd->ra);
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;
153 if (style->delim_mode == OOP_RD_DELIM_NONE ||
154 rd->style.delim_mode == OOP_RD_DELIM_NONE ||
155 style->delim != rd->style.delim)
159 rd->maxrecsz= maxrecsz;
160 rd->call_ok= ifok; rd->data_ok= data_ok;
161 rd->call_err= iferr; rd->data_err= data_err;
163 er= set_read(oop,rd); if (er) return er;
164 er= set_time_ifbuf(oop,rd); if (er) return er;
168 void oop_rd_delete(oop_read *rd) {
169 rd->ra->on_cancel(rd->ra);
170 oop_free(rd->allocbuf);
174 void oop_rd_cancel(oop_read *rd) {
175 cancel_time(rd->oop,rd);
176 cancel_read(rd->oop,rd);
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];
187 return eventstrings_other[event];
190 static void *on_readable(oop_source *oop, oop_readable *ra, void *rd_void) {
191 oop_read *rd= rd_void;
193 assert(oop == rd->oop);
194 assert(ra == rd->ra);
195 return on_process(oop,rd,1);
198 static void *on_time(oop_source *oop, struct timeval when, void *rd_void) {
199 oop_read *rd= rd_void;
201 assert(oop == rd->oop);
202 return on_process(oop,rd,0);
205 static size_t calc_dataspace(oop_read *rd) {
206 if (rd->style.delim_mode == OOP_RD_DELIM_STRIP) {
209 return rd->alloc ? rd->alloc-1 : 0;
213 static void *on_process(oop_source *oop, oop_read *rd, int try_read) {
215 int evkind; /* 0=none, -1=error, 1=something */
216 int errnoval, nread, cancelnow;
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 */
238 rd->used -= rd->discard;
239 if (rd->neednotcheck > rd->discard) {
240 rd->neednotcheck -= rd->discard;
244 memmove(buf, buf + rd->discard, rd->used);
247 if (rd->displacedchar >= 0) {
248 assert(rd->used > 0);
249 buf[0]= rd->displacedchar;
250 rd->displacedchar= -1;
253 maxrecsz= rd->maxrecsz ? rd->maxrecsz : INT_MAX / 5 /* allows +20 and *4 */;
254 maxbufreqd= maxrecsz+1;
256 if (rd->userbuf && maxbufreqd > rd->alloc) {
257 maxrecsz -= (maxbufreqd - rd->alloc);
258 maxbufreqd= rd->alloc;
261 if (rd->style.delim_mode == OOP_RD_DELIM_STRIP) {
262 readahead= maxrecsz+1;
270 thisrecdata= thisrecsz= 0;
273 assert(rd->used <= rd->alloc);
274 dataspace= calc_dataspace(rd);
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))) {
281 thisrecsz= (delimp - buf);
282 thisrecdata= thisrecsz+1;
283 if (rd->style.delim_mode == OOP_RD_DELIM_KEEP)
284 thisrecsz= thisrecdata;
288 } else if (rd->used >= readahead) {
290 thisrecsz= thisrecdata= maxrecsz;
293 if (rd->style.delim_mode == OOP_RD_DELIM_NONE) {
297 if (rd->style.shortrec_mode < OOP_RD_SHORTREC_LONG) {
299 thisrecsz= thisrecdata= 0;
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) {
307 thisrecdata= rd->used;
308 if (thisrecdata == rd->alloc) thisrecdata--;
309 thisrecsz= thisrecdata;
316 if (evkind && thisrecdata && thisrecsz >= rd->alloc) {
317 /* Need to make space for the trailing nul */
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;
324 want= MIN(want, maxbufreqd);
328 assert(!rd->userbuf);
329 assert(want <= maxbufreqd);
331 buf= oop_realloc(rd->allocbuf,want);
333 event= OOP_RD_SYSTEM;
336 thisrecsz= thisrecdata= 0;
343 if (evkind) break; /* OK, process it then */
345 if (!try_read) return OOP_CONTINUE; /* But we weren't told it was ready. */
347 dataspace= calc_dataspace(rd);
348 want= MIN(dataspace, readahead);
349 assert(rd->used < want);
352 nread= rd->ra->try_read(rd->ra, buf+rd->used, want-rd->used);
353 if (errno == EAGAIN) return OOP_CONTINUE;
356 rd->neednotcheck= rd->used;
361 if (nread < 0) { /* read error */
363 event= OOP_RD_SYSTEM;
366 thisrecsz= thisrecdata= rd->used;
372 event= OOP_RD_PARTREC;
373 evkind= (rd->style.shortrec_mode == OOP_RD_SHORTREC_FORBID) ? -1 : +1;
374 thisrecsz= thisrecdata= rd->used;
384 /* OK, we have an event of some kind */
386 /* Nul byte handling */
387 if (thisrecsz > 0 && rd->style.nul_mode != OOP_RD_NUL_PERMIT) {
392 (nul= memchr(buf+checked,0,thisrecsz-checked));
394 if (rd->style.nul_mode == OOP_RD_NUL_FORBID) {
397 thisrecdata= thisrecsz= 0;
400 assert(rd->style.nul_mode == OOP_RD_NUL_DISCARD);
402 notnul < buf+thisrecsz && notnul == '\0';
404 thisrecsz-= (notnul-nul);
406 memmove(nul,notnul,thisrecsz-checked);
410 /* Checks that all is well */
413 assert(thisrecsz <= thisrecdata);
414 assert(!rd->maxrecsz || thisrecsz <= rd->maxrecsz);
415 assert(thisrecdata <= rd->used);
417 rd->discard= thisrecdata;
419 cancelnow= (evkind < 0) || (event == OOP_RD_EOF);
422 errnoval= set_time_ifbuf(oop,rd);
424 event= OOP_RD_SYSTEM;
427 thisrecsz= thisrecdata= 0;
434 call_data= rd->data_err;
435 errmsg= oop_rd_errmsg(rd,event,errnoval,&rd->style);
438 call_data= rd->data_ok;
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];
454 call(oop,rd, event,errmsg,errnoval,
455 (thisrecdata ? buf : 0), thisrecsz, call_data);
458 oop_read *oop_rd_new_fd(oop_source *oop, int fd, char *buf, size_t bufsz) {
462 ra= oop_readable_fd(oop,fd);
465 rd= oop_rd_new(oop,ra,buf,bufsz);
466 if (!rd) { ra->delete_tidy(ra); return 0; }
471 int oop_rd_delete_tidy(oop_read *rd) {
472 oop_readable *ra= rd->ra;
474 return ra->delete_tidy(ra);
477 void oop_rd_delete_kill(oop_read *rd) {
478 oop_readable *ra= rd->ra;