1 /* -----------------------------------------------------------------------------
4 * This file implements a number of standard I/O operations included
5 * formatted output, readline, and splitting.
7 * Author(s) : David Beazley (beazley@cs.uchicago.edu)
9 * Copyright (C) 1999-2000. The University of Chicago
10 * See the file LICENSE for information on usage and redistribution.
11 * ----------------------------------------------------------------------------- */
13 char cvsroot_fio_c[] = "$Id: fio.c 9607 2006-12-05 22:11:40Z beazley $";
19 static DOH *encodings = 0; /* Encoding hash */
21 /* -----------------------------------------------------------------------------
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 * ----------------------------------------------------------------------------- */
28 static int Writen(DOH *out, void *buffer, int len) {
30 char *cb = (char *) buffer;
32 ret = Write(out, cb, nw);
41 /* -----------------------------------------------------------------------------
44 * Registers a new printf encoding method. An encoder function should accept
45 * two file-like objects and operate as a filter.
46 * ----------------------------------------------------------------------------- */
48 void DohEncoding(char *name, DOH *(*fn) (DOH *s)) {
50 encodings = NewHash();
51 Setattr(encodings, (void *) name, NewVoid((void *) fn, 0));
54 /* internal function for processing an encoding */
55 static DOH *encode(char *name, DOH *s) {
59 char *cfmt = strchr(name, ':');
62 tmp = NewString(cfmt + 1);
64 Setfile(tmp, Getfile((DOH *) s));
65 Setline(tmp, Getline((DOH *) s));
68 if (!encodings || !(handle = Getattr(encodings, name))) {
75 fn = (DOH *(*)(DOH *)) Data(handle);
77 Seek(s, pos, SEEK_SET);
83 /* -----------------------------------------------------------------------------
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:
90 * %s - Prints a "char *" or the string representation of any
91 * DOH object. This will implicitly result in a call to
94 * %(encoder)* - Filters the output through an encoding function registered
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 * ----------------------------------------------------------------------------- */
101 int DohvPrintf(DOH *so, const char *format, va_list ap) {
102 static char *fmt_codes = "dioxXucsSfeEgGpn";
104 const char *p = format;
106 char obuffer[OBUFLEN];
118 char encoder[128], *ec = 0;
121 memset(newformat, 0, sizeof(newformat));
125 case 0: /* Ordinary text */
138 case 10: /* Look for a width and precision */
139 if (isdigit((int) *p) && (*p != '0')) {
144 } else if (strchr(fmt_codes, *p)) {
145 /* Got one of the formatting codes */
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++) {
156 } else if (*p == '%') {
161 } else if (*p == '(') {
170 case 20: /* Hmmm. At the start of a width field */
171 if (isdigit((int) *p)) {
174 } else if (strchr(fmt_codes, *p)) {
175 /* Got one of the formatting codes */
176 /* Figure out width */
178 widthval = atoi(temp);
181 } else if (*p == '.') {
183 widthval = atoi(temp);
190 widthval = atoi(temp);
195 case 30: /* Parsed a width from an argument. Look for a . */
200 } else if (strchr(fmt_codes, *p)) {
201 /* Got one of the formatting codes */
202 /* Figure out width */
206 /* hmmm. Something else. */
212 /* Start of precision expected */
213 if (isdigit((int) *p) && (*p != '0')) {
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++) {
225 } else if (strchr(fmt_codes, *p)) {
234 if (isdigit((int) *p)) {
237 } else if (strchr(fmt_codes, *p)) {
238 /* Got one of the formatting codes */
239 /* Figure out width */
241 precval = atoi(temp);
246 precval = atoi(temp);
251 /* Hang out, wait for format specifier */
253 if (strchr(fmt_codes, *p)) {
261 /* Got an encoding header */
267 } else if (*p == ')') {
282 /* Got a formatting code */
283 if (widthval < precval)
287 if ((*p == 's') || (*p == 'S')) { /* Null-Terminated string */
291 doh = va_arg(ap, DOH *);
293 /* Is a DOH object. */
294 if (DohIsString(doh)) {
299 if (strlen(encoder)) {
300 enc = encode(encoder, Sval);
301 maxwidth = maxwidth + strlen(newformat) + Len(enc);
303 maxwidth = maxwidth + strlen(newformat) + Len(Sval);
307 if ((maxwidth + 1) < OBUFLEN) {
310 stemp = (char *) DohMalloc(maxwidth + 1);
313 nbytes += sprintf(stemp, newformat, Data(enc));
315 nbytes += sprintf(stemp, newformat, Data(Sval));
317 if (Writen(so, stemp, strlen(stemp)) < 0)
319 if ((DOH *) Sval != doh) {
327 if (stemp != obuffer) {
334 if (strlen(encoder)) {
335 DOH *s = NewString(doh);
336 Seek(s, 0, SEEK_SET);
337 enc = encode(encoder, s);
343 maxwidth = maxwidth + strlen(newformat) + strlen((char *) doh);
346 if ((maxwidth + 1) < OBUFLEN) {
349 stemp = (char *) DohMalloc(maxwidth + 1);
351 nbytes += sprintf(stemp, newformat, doh);
352 if (Writen(so, stemp, strlen(stemp)) < 0)
354 if (stemp != obuffer) {
363 maxwidth = maxwidth + strlen(newformat) + 64;
365 /* Only allocate a buffer if it is too big to fit. Shouldn't have to do
368 if (maxwidth < OBUFLEN)
371 stemp = (char *) DohMalloc(maxwidth + 1);
380 ivalue = va_arg(ap, int);
381 nbytes += sprintf(stemp, newformat, ivalue);
388 dvalue = va_arg(ap, double);
389 nbytes += sprintf(stemp, newformat, dvalue);
392 pvalue = va_arg(ap, void *);
393 nbytes += sprintf(stemp, newformat, pvalue);
398 if (Writen(so, stemp, strlen(stemp)) < 0)
400 if (stemp != obuffer)
411 r = Writen(so, fmt, strlen(fmt));
419 /* -----------------------------------------------------------------------------
422 * Variable length argument entry point to Printf
423 * ----------------------------------------------------------------------------- */
425 int DohPrintf(DOH *obj, const char *format, ...) {
428 va_start(ap, format);
429 ret = DohvPrintf(obj, format, ap);
434 /* -----------------------------------------------------------------------------
437 * Print a null-terminated variable length list of DOH objects
438 * ----------------------------------------------------------------------------- */
440 int DohPrintv(DOHFile * f, ...) {
446 obj = va_arg(ap, void *);
447 if ((!obj) || (obj == DohNone))
450 ret += DohDump(obj, f);
452 ret += DohWrite(f, obj, strlen((char *) obj));
459 /* -----------------------------------------------------------------------------
462 * Copies all of the input from an input stream to an output stream. Returns the
463 * number of bytes copied.
464 * ----------------------------------------------------------------------------- */
466 int DohCopyto(DOH *in, DOH *out) {
468 int nwrite = 0, wret;
475 ret = Read(in, buffer, 16384);
480 wret = Write(out, cw, nwrite);
483 nwrite = nwrite - wret;
494 /* -----------------------------------------------------------------------------
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 * ----------------------------------------------------------------------------- */
501 DOH *DohSplit(DOH *in, char ch, int nsplits) {
508 if (DohIsString(in)) {
509 Seek(in, 0, SEEK_SET);
513 str = NewStringEmpty();
516 } while ((c != EOF) && (c == ch));
521 if ((c == EOF) || ((c == ch) && (nsplits != 0)))
535 /* -----------------------------------------------------------------------------
538 * Split an input stream into a list of strings delimited by newline characters.
539 * ----------------------------------------------------------------------------- */
541 DOH *DohSplitLines(DOH *in) {
548 if (DohIsString(in)) {
549 Seek(in, 0, SEEK_SET);
553 str = NewStringEmpty();
554 while ((c = Getc(in)) != '\n' && c != EOF) {
564 /* -----------------------------------------------------------------------------
567 * Read a single input line and return it as a string.
568 * ----------------------------------------------------------------------------- */
570 DOH *DohReadline(DOH *in) {
573 DOH *s = NewStringEmpty();
575 if (Read(in, &c, 1) < 0) {