1 /* -----------------------------------------------------------------------------
2 * fio.c
3 *
4 * This file implements a number of standard I/O operations included
5 * formatted output, readline, and splitting.
6 *
7 * Author(s) : David Beazley (beazley@cs.uchicago.edu)
8 *
9 * Copyright (C) 1999-2000. The University of Chicago
10 * See the file LICENSE for information on usage and redistribution.
11 * ----------------------------------------------------------------------------- */
12
13 static char cvsroot[] = "$Header: /cvs/projects/SWILL/Source/Objects/fio.c,v 1.2 2002/02/25 03:55:09 beazley Exp $";
14
15 #include "dohint.h"
16
17 #define OBUFLEN 512
18
19 static DOH *encodings = 0; /* Encoding hash */
20
21 /* -----------------------------------------------------------------------------
22 * Writen()
23 *
24 * Write's N characters of output and retries until all characters are
25 * written. This is useful should a write operation encounter a spurious signal.
26 * ----------------------------------------------------------------------------- */
27
Writen(DOH * out,void * buffer,int len)28 static int Writen(DOH *out, void *buffer, int len) {
29 int nw = len, ret;
30 char *cb = (char *) buffer;
31 while (nw) {
32 ret = Write(out,cb,nw);
33 if (ret < 0) return -1;
34 nw = nw - ret;
35 cb += ret;
36 }
37 return len;
38 }
39
40 /* -----------------------------------------------------------------------------
41 * DohEncoding()
42 *
43 * Registers a new printf encoding method. An encoder function should accept
44 * two file-like objects and operate as a filter.
45 * ----------------------------------------------------------------------------- */
46
47 void
DohEncoding(char * name,DOH * (* fn)(DOH * s))48 DohEncoding(char *name, DOH *(*fn)(DOH *s)) {
49 if (!encodings) encodings = NewHash();
50 Setattr(encodings,(void *) name, NewVoid((void *)fn,0));
51 }
52
53 /* internal function for processing an encoding */
encode(char * name,DOH * s)54 static DOH *encode(char *name, DOH *s) {
55 DOH *handle, *ns;
56 DOH *(*fn)(DOH *);
57 long pos;
58 if (!encodings || !(handle = Getattr(encodings,name))) {
59 return Copy(s);
60 }
61 pos = Tell(s);
62 Seek(s,0,SEEK_SET);
63 fn = (DOH *(*)(DOH *)) Data(handle);
64 ns = (*fn)(s);
65 Seek(s,pos,SEEK_SET);
66 return ns;
67 }
68
69 /* -----------------------------------------------------------------------------
70 * DohvPrintf()
71 *
72 * DOH implementation of printf. Output can be directed to any file-like object
73 * including bare FILE * objects. The same formatting codes as printf are
74 * recognized with two extensions:
75 *
76 * %s - Prints a "char *" or the string representation of any
77 * DOH object. This will implicitly result in a call to
78 * Str(obj).
79 *
80 * %(encoder)* - Filters the output through an encoding function registered
81 * with DohEncoder().
82 *
83 * Note: This function is not particularly memory efficient with large strings.
84 * It's better to use Dump() or some other method instead.
85 * ----------------------------------------------------------------------------- */
86
87 int
DohvPrintf(DOH * so,const char * format,va_list ap)88 DohvPrintf(DOH *so, const char *format, va_list ap)
89 {
90 static char *fmt_codes = "dioxXucsSfeEgGpn";
91 int state = 0;
92 const char *p = format;
93 char newformat[256];
94 char obuffer[OBUFLEN];
95 char *fmt = 0;
96 char temp[64];
97 int widthval = 0;
98 int precval = 0;
99 int maxwidth;
100 char *w = 0;
101 int ivalue;
102 double dvalue;
103 void *pvalue;
104 char *stemp;
105 int nbytes = 0;
106 char encoder[128], *ec = 0;
107
108 memset (newformat, 0, sizeof (char *));
109
110 while (*p) {
111 switch(state) {
112 case 0: /* Ordinary text */
113 if (*p != '%') {
114 Putc(*p,so);
115 nbytes++;
116 } else{
117 fmt = newformat;
118 widthval = 0;
119 precval = 0;
120 *(fmt++) = *p;
121 encoder[0] = 0;
122 state = 10;
123 }
124 break;
125 case 10: /* Look for a width and precision */
126 if (isdigit(*p) && (*p != '0')) {
127 w = temp;
128 *(w++) = *p;
129 *(fmt++) = *p;
130 state = 20;
131 } else if (strchr(fmt_codes,*p)) {
132 /* Got one of the formatting codes */
133 p--;
134 state = 100;
135 } else if (*p == '*') {
136 /* Width field is specified in the format list */
137 widthval = va_arg(ap,int);
138 sprintf(temp,"%d",widthval);
139 for (w = temp; *w; w++) {
140 *(fmt++) = *w;
141 }
142 state = 30;
143 } else if (*p == '%') {
144 Putc(*p,so);
145 fmt = newformat;
146 nbytes++;
147 state = 0;
148 } else if (*p == '(') {
149 ec = encoder;
150 state = 60;
151 } else {
152 *(fmt++) = *p;
153 }
154 break;
155
156 case 20: /* Hmmm. At the start of a width field */
157 if (isdigit(*p)) {
158 *(w++) = *p;
159 *(fmt++) = *p;
160 } else if (strchr(fmt_codes,*p)) {
161 /* Got one of the formatting codes */
162 /* Figure out width */
163 *w = 0;
164 widthval = atoi(temp);
165 p--;
166 state = 100;
167 } else if (*p == '.') {
168 *w = 0;
169 widthval = atoi(temp);
170 w = temp;
171 *(fmt++) = *p;
172 state = 40;
173 } else {
174 /* ??? */
175 *w = 0;
176 widthval = atoi(temp);
177 state = 50;
178 }
179 break;
180
181 case 30: /* Parsed a width from an argument. Look for a . */
182 if (*p == '.') {
183 w = temp;
184 *(fmt++) = *p;
185 state = 40;
186 } else if (strchr(fmt_codes,*p)) {
187 /* Got one of the formatting codes */
188 /* Figure out width */
189 p--;
190 state = 100;
191 } else {
192 /* hmmm. Something else. */
193 state = 50;
194 }
195 break;
196
197 case 40:
198 /* Start of precision expected */
199 if (isdigit(*p) && (*p != '0')) {
200 *(fmt++) = *p;
201 *(w++) = *p;
202 state = 41;
203 } else if (*p == '*') {
204 /* Precision field is specified in the format list */
205 precval = va_arg(ap,int);
206 sprintf(temp,"%d",precval);
207 for (w = temp; *w; w++) {
208 *(fmt++) = *w;
209 }
210 state = 50;
211 } else if (strchr(fmt_codes,*p)) {
212 p--;
213 state = 100;
214 } else {
215 *(fmt++) = *p;
216 state = 50;
217 }
218 break;
219 case 41:
220 if (isdigit(*p)) {
221 *(fmt++) = *p;
222 *(w++) = *p;
223 } else if (strchr(fmt_codes,*p)) {
224 /* Got one of the formatting codes */
225 /* Figure out width */
226 *w = 0;
227 precval = atoi(temp);
228 p--;
229 state = 100;
230 } else {
231 *w = 0;
232 precval = atoi(temp);
233 *(fmt++) = *p;
234 state = 50;
235 }
236 break;
237 /* Hang out, wait for format specifier */
238 case 50:
239 if (strchr(fmt_codes,*p)) {
240 p--;
241 state = 100;
242 } else {
243 *(fmt++) = *p;
244 }
245 break;
246
247 /* Got an encoding header */
248 case 60:
249 if (*p == ')') {
250 *ec = 0;
251 state = 10;
252 } else {
253 *ec = *p;
254 ec++;
255 }
256 break;
257 case 100:
258 /* Got a formatting code */
259 if (widthval < precval) maxwidth = precval;
260 else maxwidth = widthval;
261 if ((*p == 's') || (*p == 'S')) { /* Null-Terminated string */
262 DOH *doh;
263 DOH *Sval;
264 DOH *enc = 0;
265 doh = va_arg(ap, DOH *);
266 if (DohCheck(doh)) {
267 /* Is a DOH object. */
268 if (DohIsString(doh)) {
269 Sval = doh;
270 } else {
271 Sval = Str(doh);
272 }
273 if (strlen(encoder)) {
274 enc = encode(encoder,Sval);
275 maxwidth = maxwidth+strlen(newformat)+Len(enc);
276 } else {
277 maxwidth = maxwidth+strlen(newformat)+Len(Sval);
278 }
279 *(fmt++) = 's';
280 *fmt = 0;
281 if ((maxwidth + 1) < OBUFLEN) {
282 stemp = obuffer;
283 } else {
284 stemp = (char *) DohMalloc(maxwidth+1);
285 }
286 if (enc) {
287 nbytes+=sprintf(stemp,newformat,Data(enc));
288 } else {
289 nbytes+=sprintf(stemp,newformat,Data(Sval));
290 }
291 if (Writen(so,stemp,strlen(stemp)) < 0) return -1;
292 if ((DOH *) Sval != doh) {
293 Delete(Sval);
294 }
295 if (enc) Delete(enc);
296 if (*p == 'S') {
297 Delete(doh);
298 }
299 if (stemp != obuffer) {
300 DohFree(stemp);
301 }
302 } else {
303 if (!doh) doh = "";
304
305 if (strlen(encoder)) {
306 DOH *s = NewString(doh);
307 Seek(s,0, SEEK_SET);
308 enc = encode(encoder,s);
309 Delete(s);
310 doh = Char(enc);
311 } else {
312 enc = 0;
313 }
314 maxwidth = maxwidth+strlen(newformat)+strlen((char *) doh);
315 *(fmt++) = 's';
316 *fmt = 0;
317 if ((maxwidth+1) < OBUFLEN) {
318 stemp = obuffer;
319 } else {
320 stemp = (char *) DohMalloc(maxwidth + 1);
321 }
322 nbytes+=sprintf(stemp,newformat,doh);
323 if (Writen(so,stemp,strlen(stemp)) < 0) return -1;
324 if (stemp != obuffer) {
325 DohFree(stemp);
326 }
327 if (enc) Delete(enc);
328 }
329 } else {
330 *(fmt++) = *p;
331 *fmt = 0;
332 maxwidth = maxwidth+strlen(newformat)+64;
333
334 /* Only allocate a buffer if it is too big to fit. Shouldn't have to do
335 this very often */
336
337 if (maxwidth < OBUFLEN)
338 stemp = obuffer;
339 else
340 stemp = (char *) DohMalloc(maxwidth+1);
341 switch(*p) {
342 case 'd':
343 case 'i':
344 case 'o':
345 case 'u':
346 case 'x':
347 case 'X':
348 case 'c':
349 ivalue = va_arg(ap,int);
350 nbytes+=sprintf(stemp,newformat,ivalue);
351 break;
352 case 'f':
353 case 'g':
354 case 'e':
355 case 'E':
356 case 'G':
357 dvalue = va_arg(ap,double);
358 nbytes+=sprintf(stemp,newformat,dvalue);
359 break;
360 case 'p':
361 pvalue = va_arg(ap,void *);
362 nbytes+=sprintf(stemp,newformat,pvalue);
363 break;
364 default:
365 break;
366 }
367 if (Writen(so,stemp,strlen(stemp)) < 0) return -1;
368 if (stemp != obuffer) DohFree(stemp);
369 }
370 state = 0;
371 break;
372 }
373 p++;
374 }
375 if (state) {
376 int r;
377 *fmt = 0;
378 r = Writen(so,fmt,strlen(fmt));
379 if (r < 0) return -1;
380 nbytes += r;
381 }
382 return nbytes;
383 }
384
385 /* -----------------------------------------------------------------------------
386 * DohPrintf()
387 *
388 * Variable length argument entry point to Printf
389 * ----------------------------------------------------------------------------- */
390
391 int
DohPrintf(DOH * obj,const char * format,...)392 DohPrintf(DOH *obj, const char *format, ...) {
393 va_list ap;
394 int ret;
395 va_start(ap,format);
396 ret = DohvPrintf(obj,format,ap);
397 va_end(ap);
398 return ret;
399 }
400
401 /* -----------------------------------------------------------------------------
402 * DohPrintv()
403 *
404 * Print a null-terminated variable length list of DOH objects
405 * ----------------------------------------------------------------------------- */
406
DohPrintv(DOHFile * f,...)407 int DohPrintv(DOHFile *f, ...) {
408 va_list ap;
409 int ret = 0;
410 DOH *obj;
411 va_start(ap,f);
412 while(1) {
413 obj = va_arg(ap,void *);
414 if (!obj) break;
415 if (DohCheck(obj)) {
416 ret += DohDump(obj,f);
417 } else {
418 ret += DohWrite(f,obj,strlen((char *) obj));
419 }
420 }
421 va_end(ap);
422 return ret;
423 }
424
425 /* -----------------------------------------------------------------------------
426 * DohCopyto()
427 *
428 * Copies all of the input from an input stream to an output stream. Returns the
429 * number of bytes copied.
430 * ----------------------------------------------------------------------------- */
431
432 int
DohCopyto(DOH * in,DOH * out)433 DohCopyto(DOH *in, DOH *out) {
434 int nbytes = 0, ret;
435 int nwrite = 0, wret;
436 char *cw;
437 char buffer[16384];
438
439 if ((!in) || (!out)) return 0;
440 while (1) {
441 ret = Read(in,buffer,16384);
442 if (ret > 0) {
443 nwrite = ret;
444 cw = buffer;
445 while (nwrite) {
446 wret = Write(out,cw,nwrite);
447 if (wret < 0) return -1;
448 nwrite = nwrite - wret;
449 cw += wret;
450 }
451 nbytes += ret;
452 } else {
453 return nbytes;
454 }
455 }
456 }
457
458
459 /* -----------------------------------------------------------------------------
460 * DohSplit()
461 *
462 * Split an input stream into a list of strings delimeted by characters in a
463 * string. Optionally accepts a maximum number of splits to perform.
464 * ----------------------------------------------------------------------------- */
465
466 DOH *
DohSplit(DOH * in,const char * chs,int nsplits)467 DohSplit(DOH *in, const char *chs, int nsplits) {
468 DOH *list;
469 DOH *str;
470 int c;
471
472 list = NewList();
473
474 if (DohIsString(in)) {
475 Seek(in,0,SEEK_SET);
476 }
477
478 while (1) {
479 str = NewString("");
480 do {
481 c = Getc(in);
482 } while ((c != EOF) && (c == *chs));
483 if (c != EOF) {
484 Putc(c,str);
485 while (1) {
486 c = Getc(in);
487 if ((c == EOF) || ((c == *chs) && (nsplits != 0))) break;
488 Putc(c,str);
489 }
490 nsplits--;
491 }
492 Append(list,str);
493 Delete(str);
494 if (c == EOF) break;
495 }
496 return list;
497 }
498
499 /* -----------------------------------------------------------------------------
500 * DohReadline()
501 *
502 * Read a single input line and return it as a string.
503 * ----------------------------------------------------------------------------- */
504
505 DOH *
DohReadline(DOH * in)506 DohReadline(DOH *in) {
507 char c;
508 int n = 0;
509 DOH *s = NewString("");
510 while (1) {
511 if (Read(in,&c,1) < 0) {
512 if (n == 0) {
513 Delete(s);
514 return 0;
515 }
516 return s;
517 }
518 if (c == '\n') return s;
519 if (c == '\r') continue;
520 Putc(c,s);
521 n++;
522 }
523 }
524
525
526