/* print.c -- formatted printing routines (Paul Haahr, 12/91) */ #include "rc.h" #include <setjmp.h> #define PRINT_ALLOCSIZE ((size_t)64) #define SPRINT_BUFSIZ ((size_t)1024) #define MAXCONV 256 /* * conversion functions * true return -> flag changes only, not a conversion */ #define Flag(name, flag) \ static bool name(Format *format, int c) { \ format->flags |= flag; \ return TRUE; \ } Flag(uconv, FMT_unsigned) Flag(hconv, FMT_short) Flag(lconv, FMT_long) Flag(altconv, FMT_altform) Flag(leftconv, FMT_leftside) Flag(dotconv, FMT_f2set) static bool digitconv(Format *format, int c) { if (format->flags & FMT_f2set) format->f2 = 10 * format->f2 + c - '0'; else { format->flags |= FMT_f1set; format->f1 = 10 * format->f1 + c - '0'; } return TRUE; } static bool zeroconv(Format *format, int c) { if (format->flags & (FMT_f1set | FMT_f2set)) return digitconv(format, '0'); format->flags |= FMT_zeropad; return TRUE; } static void pad(Format *format, size_t len, int c) { while (len-- != 0) fmtputc(format, c); } static bool sconv(Format *format, int c) { char *s = va_arg(format->args, char *); if ((format->flags & FMT_f1set) == 0) fmtcat(format, s); else { size_t len = strlen(s), width = format->f1 - len; if (format->flags & FMT_leftside) { fmtappend(format, s, len); pad(format, width, ' '); } else { pad(format, width, ' '); fmtappend(format, s, len); } } return FALSE; } static char *utoa(unsigned long u, char *t, unsigned int radix, const char *digit) { if (u >= radix) { t = utoa(u / radix, t, radix, digit); u %= radix; } *t++ = digit[u]; return t; } static void intconv(Format *format, unsigned int radix, int upper, const char *altform) { static const char * const table[] = { "0123456789abcdefghijklmnopqrstuvwxyz", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", }; char padchar; size_t len, pre, zeroes, padding, width; long n, flags; unsigned long u; char number[64], prefix[20]; if (radix > 36) return; flags = format->flags; if (flags & FMT_long) n = va_arg(format->args, long); else if (flags & FMT_short) n = va_arg(format->args, short); else n = va_arg(format->args, int); pre = 0; if ((flags & FMT_unsigned) || n >= 0) u = n; else { prefix[pre++] = '-'; u = -n; } if (flags & FMT_altform) while (*altform != '\0') prefix[pre++] = *altform++; len = utoa(u, number, radix, table[upper]) - number; if ((flags & FMT_f2set) && (size_t) format->f2 > len) zeroes = format->f2 - len; else zeroes = 0; width = pre + zeroes + len; if ((flags & FMT_f1set) && (size_t) format->f1 > width) { padding = format->f1 - width; } else padding = 0; padchar = ' '; if (padding > 0 && flags & FMT_zeropad) { padchar = '0'; if ((flags & FMT_leftside) == 0) { zeroes += padding; padding = 0; } } if ((flags & FMT_leftside) == 0) pad(format, padding, padchar); fmtappend(format, prefix, pre); pad(format, zeroes, '0'); fmtappend(format, number, len); if (flags & FMT_leftside) pad(format, padding, padchar); } static bool cconv(Format *format, int c) { fmtputc(format, va_arg(format->args, int)); return FALSE; } static bool dconv(Format *format, int c) { intconv(format, 10, 0, ""); return FALSE; } static bool oconv(Format *format, int c) { intconv(format, 8, 0, "0"); return FALSE; } static bool xconv(Format *format, int c) { intconv(format, 16, 0, "0x"); return FALSE; } static bool pctconv(Format *format, int c) { fmtputc(format, '%'); return FALSE; } static bool badconv(Format *format, int c) { panic("bad conversion character in printfmt"); /* NOTREACHED */ return FALSE; /* hush up gcc -Wall */ } /* * conversion table management */ static Conv fmttab[MAXCONV]; static void inittab(void) { int i; for (i = 0; i < MAXCONV; i++) fmttab[i] = badconv; fmttab['s'] = sconv; fmttab['c'] = cconv; fmttab['d'] = dconv; fmttab['o'] = oconv; fmttab['x'] = xconv; fmttab['%'] = pctconv; fmttab['u'] = uconv; fmttab['h'] = hconv; fmttab['l'] = lconv; fmttab['#'] = altconv; fmttab['-'] = leftconv; fmttab['.'] = dotconv; fmttab['0'] = zeroconv; for (i = '1'; i <= '9'; i++) fmttab[i] = digitconv; } extern bool (*fmtinstall(int c, bool (*f)(Format *, int)))(Format *, int) { /*Conv fmtinstall(int c, Conv f) {*/ Conv oldf; if (fmttab[0] == NULL) inittab(); c &= MAXCONV - 1; oldf = fmttab[c]; if (f != NULL) fmttab[c] = f; return oldf; } /* * functions for inserting strings in the format buffer */ extern void fmtappend(Format *format, const char *s, size_t len) { while (format->buf + len > format->bufend) { size_t split = format->bufend - format->buf; memcpy(format->buf, s, split); format->buf += split; s += split; len -= split; (*format->grow)(format, len); } memcpy(format->buf, s, len); format->buf += len; } extern void fmtcat(Format *format, const char *s) { fmtappend(format, s, strlen(s)); } /* * printfmt -- the driver routine */ extern int printfmt(Format *format, const char *fmt) { unsigned const char *s = (unsigned const char *) fmt; if (fmttab[0] == NULL) inittab(); for (;;) { int c = *s++; switch (c) { case '%': format->flags = format->f1 = format->f2 = 0; do c = *s++; while ((*fmttab[c])(format, c)); break; case '\0': return format->buf - format->bufbegin + format->flushed; default: fmtputc(format, c); break; } } } /* * the public entry points */ extern int fmtprint(Format *format, const char *fmt,...) { int n = -format->flushed; va_list saveargs = format->args; va_start(format->args, fmt); n += printfmt(format, fmt); va_end(format->args); format->args = saveargs; return n + format->flushed; } static void fprint_flush(Format *format, size_t more) { size_t n = format->buf - format->bufbegin; char *buf = format->bufbegin; format->flushed += n; format->buf = format->bufbegin; writeall(format->u.n, buf, n); } extern int fprint(int fd, const char *fmt,...) { char buf[1024]; Format format; format.buf = buf; format.bufbegin = buf; format.bufend = buf + sizeof buf; format.grow = fprint_flush; format.flushed = 0; format.u.n = fd; va_start(format.args, fmt); printfmt(&format, fmt); va_end(format.args); fprint_flush(&format, (size_t) 0); return format.flushed; } static void memprint_grow(Format *format, size_t more) { char *buf; size_t len = format->bufend - format->bufbegin + 1; len = (len >= more) ? len * 2 : ((len + more) + PRINT_ALLOCSIZE) &~ (PRINT_ALLOCSIZE - 1); if (format->u.n) buf = erealloc(format->bufbegin, len); else { size_t used = format->buf - format->bufbegin; buf = nalloc(len); memcpy(buf, format->bufbegin, used); } format->buf = buf + (format->buf - format->bufbegin); format->bufbegin = buf; format->bufend = buf + len - 1; } static char *memprint(Format *format, const char *fmt, char *buf, size_t len) { format->buf = buf; format->bufbegin = buf; format->bufend = buf + len - 1; format->grow = memprint_grow; format->flushed = 0; printfmt(format, fmt); *format->buf = '\0'; return format->bufbegin; } extern char *mprint(const char *fmt,...) { Format format; char *result; format.u.n = 1; va_start(format.args, fmt); result = memprint(&format, fmt, ealloc(PRINT_ALLOCSIZE), PRINT_ALLOCSIZE); va_end(format.args); return result; } extern char *nprint(const char *fmt,...) { Format format; char *result; format.u.n = 0; va_start(format.args, fmt); result = memprint(&format, fmt, nalloc(PRINT_ALLOCSIZE), PRINT_ALLOCSIZE); va_end(format.args); return result; }