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