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