diff options
Diffstat (limited to 'Source/DOH/fio.c')
| -rw-r--r-- | Source/DOH/fio.c | 589 |
1 files changed, 589 insertions, 0 deletions
diff --git a/Source/DOH/fio.c b/Source/DOH/fio.c new file mode 100644 index 0000000..7f3eb8b --- /dev/null +++ b/Source/DOH/fio.c @@ -0,0 +1,589 @@ +/* ----------------------------------------------------------------------------- + * fio.c + * + * This file implements a number of standard I/O operations included + * formatted output, readline, and splitting. + * + * Author(s) : David Beazley (beazley@cs.uchicago.edu) + * + * Copyright (C) 1999-2000. The University of Chicago + * See the file LICENSE for information on usage and redistribution. + * ----------------------------------------------------------------------------- */ + +char cvsroot_fio_c[] = "$Id: fio.c 9607 2006-12-05 22:11:40Z beazley $"; + +#include "dohint.h" + +#define OBUFLEN 512 + +static DOH *encodings = 0; /* Encoding hash */ + +/* ----------------------------------------------------------------------------- + * Writen() + * + * Write's N characters of output and retries until all characters are + * written. This is useful should a write operation encounter a spurious signal. + * ----------------------------------------------------------------------------- */ + +static int Writen(DOH *out, void *buffer, int len) { + int nw = len, ret; + char *cb = (char *) buffer; + while (nw) { + ret = Write(out, cb, nw); + if (ret < 0) + return -1; + nw = nw - ret; + cb += ret; + } + return len; +} + +/* ----------------------------------------------------------------------------- + * DohEncoding() + * + * Registers a new printf encoding method. An encoder function should accept + * two file-like objects and operate as a filter. + * ----------------------------------------------------------------------------- */ + +void DohEncoding(char *name, DOH *(*fn) (DOH *s)) { + if (!encodings) + encodings = NewHash(); + Setattr(encodings, (void *) name, NewVoid((void *) fn, 0)); +} + +/* internal function for processing an encoding */ +static DOH *encode(char *name, DOH *s) { + DOH *handle, *ns; + DOH *(*fn) (DOH *); + long pos; + char *cfmt = strchr(name, ':'); + DOH *tmp = 0; + if (cfmt) { + tmp = NewString(cfmt + 1); + Append(tmp, s); + Setfile(tmp, Getfile((DOH *) s)); + Setline(tmp, Getline((DOH *) s)); + *cfmt = '\0'; + } + if (!encodings || !(handle = Getattr(encodings, name))) { + return Copy(s); + } + if (tmp) + s = tmp; + pos = Tell(s); + Seek(s, 0, SEEK_SET); + fn = (DOH *(*)(DOH *)) Data(handle); + ns = (*fn) (s); + Seek(s, pos, SEEK_SET); + if (tmp) + Delete(tmp); + return ns; +} + +/* ----------------------------------------------------------------------------- + * DohvPrintf() + * + * DOH implementation of printf. Output can be directed to any file-like object + * including bare FILE * objects. The same formatting codes as printf are + * recognized with two extensions: + * + * %s - Prints a "char *" or the string representation of any + * DOH object. This will implicitly result in a call to + * Str(obj). + * + * %(encoder)* - Filters the output through an encoding function registered + * with DohEncoder(). + * + * Note: This function is not particularly memory efficient with large strings. + * It's better to use Dump() or some other method instead. + * ----------------------------------------------------------------------------- */ + +int DohvPrintf(DOH *so, const char *format, va_list ap) { + static char *fmt_codes = "dioxXucsSfeEgGpn"; + int state = 0; + const char *p = format; + char newformat[256]; + char obuffer[OBUFLEN]; + char *fmt = 0; + char temp[64]; + int widthval = 0; + int precval = 0; + int maxwidth; + char *w = 0; + int ivalue; + double dvalue; + void *pvalue; + char *stemp; + int nbytes = 0; + char encoder[128], *ec = 0; + int plevel = 0; + + memset(newformat, 0, sizeof(newformat)); + + while (*p) { + switch (state) { + case 0: /* Ordinary text */ + if (*p != '%') { + Putc(*p, so); + nbytes++; + } else { + fmt = newformat; + widthval = 0; + precval = 0; + *(fmt++) = *p; + encoder[0] = 0; + state = 10; + } + break; + case 10: /* Look for a width and precision */ + if (isdigit((int) *p) && (*p != '0')) { + w = temp; + *(w++) = *p; + *(fmt++) = *p; + state = 20; + } else if (strchr(fmt_codes, *p)) { + /* Got one of the formatting codes */ + p--; + state = 100; + } else if (*p == '*') { + /* Width field is specified in the format list */ + widthval = va_arg(ap, int); + sprintf(temp, "%d", widthval); + for (w = temp; *w; w++) { + *(fmt++) = *w; + } + state = 30; + } else if (*p == '%') { + Putc(*p, so); + fmt = newformat; + nbytes++; + state = 0; + } else if (*p == '(') { + ++plevel; + ec = encoder; + state = 60; + } else { + *(fmt++) = *p; + } + break; + + case 20: /* Hmmm. At the start of a width field */ + if (isdigit((int) *p)) { + *(w++) = *p; + *(fmt++) = *p; + } else if (strchr(fmt_codes, *p)) { + /* Got one of the formatting codes */ + /* Figure out width */ + *w = 0; + widthval = atoi(temp); + p--; + state = 100; + } else if (*p == '.') { + *w = 0; + widthval = atoi(temp); + w = temp; + *(fmt++) = *p; + state = 40; + } else { + /* ??? */ + *w = 0; + widthval = atoi(temp); + state = 50; + } + break; + + case 30: /* Parsed a width from an argument. Look for a . */ + if (*p == '.') { + w = temp; + *(fmt++) = *p; + state = 40; + } else if (strchr(fmt_codes, *p)) { + /* Got one of the formatting codes */ + /* Figure out width */ + p--; + state = 100; + } else { + /* hmmm. Something else. */ + state = 50; + } + break; + + case 40: + /* Start of precision expected */ + if (isdigit((int) *p) && (*p != '0')) { + *(fmt++) = *p; + *(w++) = *p; + state = 41; + } else if (*p == '*') { + /* Precision field is specified in the format list */ + precval = va_arg(ap, int); + sprintf(temp, "%d", precval); + for (w = temp; *w; w++) { + *(fmt++) = *w; + } + state = 50; + } else if (strchr(fmt_codes, *p)) { + p--; + state = 100; + } else { + *(fmt++) = *p; + state = 50; + } + break; + case 41: + if (isdigit((int) *p)) { + *(fmt++) = *p; + *(w++) = *p; + } else if (strchr(fmt_codes, *p)) { + /* Got one of the formatting codes */ + /* Figure out width */ + *w = 0; + precval = atoi(temp); + p--; + state = 100; + } else { + *w = 0; + precval = atoi(temp); + *(fmt++) = *p; + state = 50; + } + break; + /* Hang out, wait for format specifier */ + case 50: + if (strchr(fmt_codes, *p)) { + p--; + state = 100; + } else { + *(fmt++) = *p; + } + break; + + /* Got an encoding header */ + case 60: + if (*p == '(') { + ++plevel; + *ec = *p; + ec++; + } else if (*p == ')') { + --plevel; + if (plevel <= 0) { + *ec = 0; + state = 10; + } else { + *ec = *p; + ec++; + } + } else { + *ec = *p; + ec++; + } + break; + case 100: + /* Got a formatting code */ + if (widthval < precval) + maxwidth = precval; + else + maxwidth = widthval; + if ((*p == 's') || (*p == 'S')) { /* Null-Terminated string */ + DOH *doh; + DOH *Sval; + DOH *enc = 0; + doh = va_arg(ap, DOH *); + if (DohCheck(doh)) { + /* Is a DOH object. */ + if (DohIsString(doh)) { + Sval = doh; + } else { + Sval = Str(doh); + } + if (strlen(encoder)) { + enc = encode(encoder, Sval); + maxwidth = maxwidth + strlen(newformat) + Len(enc); + } else { + maxwidth = maxwidth + strlen(newformat) + Len(Sval); + } + *(fmt++) = 's'; + *fmt = 0; + if ((maxwidth + 1) < OBUFLEN) { + stemp = obuffer; + } else { + stemp = (char *) DohMalloc(maxwidth + 1); + } + if (enc) { + nbytes += sprintf(stemp, newformat, Data(enc)); + } else { + nbytes += sprintf(stemp, newformat, Data(Sval)); + } + if (Writen(so, stemp, strlen(stemp)) < 0) + return -1; + if ((DOH *) Sval != doh) { + Delete(Sval); + } + if (enc) + Delete(enc); + if (*p == 'S') { + Delete(doh); + } + if (stemp != obuffer) { + DohFree(stemp); + } + } else { + if (!doh) + doh = (char *) ""; + + if (strlen(encoder)) { + DOH *s = NewString(doh); + Seek(s, 0, SEEK_SET); + enc = encode(encoder, s); + Delete(s); + doh = Char(enc); + } else { + enc = 0; + } + maxwidth = maxwidth + strlen(newformat) + strlen((char *) doh); + *(fmt++) = 's'; + *fmt = 0; + if ((maxwidth + 1) < OBUFLEN) { + stemp = obuffer; + } else { + stemp = (char *) DohMalloc(maxwidth + 1); + } + nbytes += sprintf(stemp, newformat, doh); + if (Writen(so, stemp, strlen(stemp)) < 0) + return -1; + if (stemp != obuffer) { + DohFree(stemp); + } + if (enc) + Delete(enc); + } + } else { + *(fmt++) = *p; + *fmt = 0; + maxwidth = maxwidth + strlen(newformat) + 64; + + /* Only allocate a buffer if it is too big to fit. Shouldn't have to do + this very often */ + + if (maxwidth < OBUFLEN) + stemp = obuffer; + else + stemp = (char *) DohMalloc(maxwidth + 1); + switch (*p) { + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + case 'c': + ivalue = va_arg(ap, int); + nbytes += sprintf(stemp, newformat, ivalue); + break; + case 'f': + case 'g': + case 'e': + case 'E': + case 'G': + dvalue = va_arg(ap, double); + nbytes += sprintf(stemp, newformat, dvalue); + break; + case 'p': + pvalue = va_arg(ap, void *); + nbytes += sprintf(stemp, newformat, pvalue); + break; + default: + break; + } + if (Writen(so, stemp, strlen(stemp)) < 0) + return -1; + if (stemp != obuffer) + DohFree(stemp); + } + state = 0; + break; + } + p++; + } + if (state) { + int r; + *fmt = 0; + r = Writen(so, fmt, strlen(fmt)); + if (r < 0) + return -1; + nbytes += r; + } + return nbytes; +} + +/* ----------------------------------------------------------------------------- + * DohPrintf() + * + * Variable length argument entry point to Printf + * ----------------------------------------------------------------------------- */ + +int DohPrintf(DOH *obj, const char *format, ...) { + va_list ap; + int ret; + va_start(ap, format); + ret = DohvPrintf(obj, format, ap); + va_end(ap); + return ret; +} + +/* ----------------------------------------------------------------------------- + * DohPrintv() + * + * Print a null-terminated variable length list of DOH objects + * ----------------------------------------------------------------------------- */ + +int DohPrintv(DOHFile * f, ...) { + va_list ap; + int ret = 0; + DOH *obj; + va_start(ap, f); + while (1) { + obj = va_arg(ap, void *); + if ((!obj) || (obj == DohNone)) + break; + if (DohCheck(obj)) { + ret += DohDump(obj, f); + } else { + ret += DohWrite(f, obj, strlen((char *) obj)); + } + } + va_end(ap); + return ret; +} + +/* ----------------------------------------------------------------------------- + * DohCopyto() + * + * Copies all of the input from an input stream to an output stream. Returns the + * number of bytes copied. + * ----------------------------------------------------------------------------- */ + +int DohCopyto(DOH *in, DOH *out) { + int nbytes = 0, ret; + int nwrite = 0, wret; + char *cw; + char buffer[16384]; + + if ((!in) || (!out)) + return 0; + while (1) { + ret = Read(in, buffer, 16384); + if (ret > 0) { + nwrite = ret; + cw = buffer; + while (nwrite) { + wret = Write(out, cw, nwrite); + if (wret < 0) + return -1; + nwrite = nwrite - wret; + cw += wret; + } + nbytes += ret; + } else { + return nbytes; + } + } +} + + +/* ----------------------------------------------------------------------------- + * DohSplit() + * + * Split an input stream into a list of strings delimited by the specified + * character. Optionally accepts a maximum number of splits to perform. + * ----------------------------------------------------------------------------- */ + +DOH *DohSplit(DOH *in, char ch, int nsplits) { + DOH *list; + DOH *str; + int c; + + list = NewList(); + + if (DohIsString(in)) { + Seek(in, 0, SEEK_SET); + } + + while (1) { + str = NewStringEmpty(); + do { + c = Getc(in); + } while ((c != EOF) && (c == ch)); + if (c != EOF) { + Putc(c, str); + while (1) { + c = Getc(in); + if ((c == EOF) || ((c == ch) && (nsplits != 0))) + break; + Putc(c, str); + } + nsplits--; + } + Append(list, str); + Delete(str); + if (c == EOF) + break; + } + return list; +} + +/* ----------------------------------------------------------------------------- + * DohSplitLines() + * + * Split an input stream into a list of strings delimited by newline characters. + * ----------------------------------------------------------------------------- */ + +DOH *DohSplitLines(DOH *in) { + DOH *list; + DOH *str; + int c = 0; + + list = NewList(); + + if (DohIsString(in)) { + Seek(in, 0, SEEK_SET); + } + + while (c != EOF) { + str = NewStringEmpty(); + while ((c = Getc(in)) != '\n' && c != EOF) { + Putc(c, str); + } + Append(list, str); + Delete(str); + } + return list; +} + + +/* ----------------------------------------------------------------------------- + * DohReadline() + * + * Read a single input line and return it as a string. + * ----------------------------------------------------------------------------- */ + +DOH *DohReadline(DOH *in) { + char c; + int n = 0; + DOH *s = NewStringEmpty(); + while (1) { + if (Read(in, &c, 1) < 0) { + if (n == 0) { + Delete(s); + return 0; + } + return s; + } + if (c == '\n') + return s; + if (c == '\r') + continue; + Putc(c, s); + n++; + } +} |
