#include #include #include #include /* * $Id: doprint.c,v 1.7 1998/11/10 22:35:28 mhw Exp $ */ enum { FL_PLUS = 1<<0, FL_MINUS = 1<<1, FL_HASH = 1<<2, FL_LONG = 1<<3, FL_SHORT = 1<<4, FL_UNSIGNED = 1<<5, FL_LONGLONG = 1<<6 }; enum { F_DIG = 1, F_STAR, F_PER, F_FLG, F_DOT, F_C, F_S, #if defined(PRINT_RUNES) F_CR, F_SR, #endif F_ERR, F_BI #if !defined(PRINT_RUNES) , F_CR = 0, F_SR = 0 #endif }; static char fmttab[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* !"#$%&'*/ 0, 0, 0, F_FLG, 0, F_PER, 0, 0, /*()*+,-./ */ 0, 0, F_STAR, F_FLG, 0, F_FLG, F_DOT, 0, /*01234567*/ F_DIG, F_DIG, F_DIG, F_DIG, F_DIG, F_DIG, F_DIG, F_DIG, /*89:;<=>?*/ F_DIG, F_DIG, 0, 0, 0, 0, 0, 0, /*@ABCDEFG*/ 0, 0, 0, F_CR, 0, 0, 0, 0, /*HIJKLMNO*/ 0, 0, 0, 0, 0, 0, 0, 0, /*PQRSTUVW*/ 0, 0, 0, F_SR, 0, 0, 0, 0, /*XYZ[\]^_*/ F_BI+0, 0, 0, 0, 0, 0, 0, 0, /*`abcdefg*/ 0, 0, 0, F_C, F_BI+0, 0, 0, 0, /*hijklmno*/ F_FLG, 0, 0, 0, F_FLG, 0, 0, F_BI+0, /*pqrstuvw*/ 0, 0, F_ERR, F_S, 0, F_FLG, 0, 0, /*xyz{|}~ */ F_BI+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static int (*fmtfns[16])(void *, Fconv *) = { numbconv, }; int fmtinstall(int c, int (*f)(void *, Fconv *)) { int i; if (c < 0 || c >= 256) return -1; for (i = 0; i < 16; ++i) if (fmtfns[i] == 0 || fmtfns[i] == f) break; if (i == 16) return -1; fmtfns[i] = f; fmttab[c] = F_BI+i; return 0; } char * doprint(char *s, char *es, char *format, void *argp) { va_list ap = argp; int c; int percent = 0; int dot = 0; Fconv f; while (s < es && (c = *format++) != 0) { if (!percent && c != '%') { *s++ = c; continue; } switch (fmttab[c]) { case 0: /* non-special */ *s++ = c; percent = 0; break; case F_DIG: /* 0-9 */ if (dot == 0) f.f1 = f.f1*10+(c-'0'); else if (dot == 1) f.f2 = f.f2*10+(c-'0'); else { *s++ = c; percent = 0; } break; case F_STAR: /* * */ if (dot == 0) f.f1 = abs(va_arg(ap, int)); else f.f2 = abs(va_arg(ap, int)); break; case F_PER: /* % */ if (percent) { *s++ = '%'; percent = 0; break; } percent = 1; dot = 0; f.out = s; f.eout = es; f.f1 = 0; f.f2 = -1; f.f3 = 0; break; case F_FLG: /* flags */ switch (c) { case '+': f.f3 |= FL_PLUS; break; case '-': f.f3 |= FL_MINUS; break; case '#': f.f3 |= FL_HASH; break; case 'h': f.f3 |= FL_SHORT; break; case 'u': f.f3 |= FL_UNSIGNED; break; case 'l': if ((f.f3 & (FL_LONG|FL_LONGLONG)) == FL_LONG) f.f3 |= FL_LONGLONG; else f.f3 |= FL_LONG; break; } break; case F_DOT: /* . */ ++dot; f.f2 = 0; break; case F_C: /* c */ { char arg[2]; arg[0] = va_arg(ap, int); arg[1] = 0; strconv(arg, &f); s = f.out; percent = 0; } break; case F_S: /* s */ { char *arg = va_arg(ap, char *); strconv(arg, &f); s = f.out; percent = 0; } break; #if defined(PRINT_RUNES) case F_CR: /* C */ { Rune r = va_arg(ap, int); char arg[UTFmax+1]; int l = runetochar(arg, &r); arg[l] = 0; strconv(arg, &f); s = f.out; percent = 0; } break; case F_SR: /* S */ { Rune *arg = va_arg(ap, Rune *); Strconv(arg, &f); s = f.out; percent = 0; } break; #endif case F_ERR: /* r */ strconv(strerror(errno), &f); s = f.out; percent = 0; break; default: { int fmt, r; f.chr = c; fmt = fmttab[c]; assert(fmt >= F_BI); assert(fmtfns[fmt-F_BI] != 0); r = fmtfns[fmt-F_BI](ap, &f); if (r < 0) f.f3 |= ~r; else { ap += r; s = f.out; percent = 0; } } break; } } return s; } void strconv(char *s, Fconv *fp) { int l, i; int pad = (fp->f1 != 0); if (pad || fp->f2 > -1) { char *s2 = s; for (l = 0; l < fp->f2 && *s2 != 0; ++l) ++s2; if (pad && (fp->f3 & FL_MINUS) == 0) for (i = fp->f1-l; fp->out < fp->eout && i > 0; --i) *fp->out++ = ' '; for (i = 0; i < l && fp->out < fp->eout; ++i) *fp->out++ = *s++; if (pad && (fp->f3 & FL_MINUS) != 0) for (i = fp->f1-l; fp->out < fp->eout && i > 0; --i) *fp->out++ = ' '; } else { while (fp->out < fp->eout && *s != 0) *fp->out++ = *s++; } } #if defined(PRINT_RUNES) void Strconv(Rune *s, Fconv *fp) { char temp[UTFmax]; int l, i, j; int pad = (fp->f1 != 0); if (pad || fp->f2 > -1) { Rune *s2 = s; for (l = 0; l < fp->f2 && *s2 != 0; ++l) ++s2; if (pad && (fp->f3 & FL_MINUS) == 0) for (i = fp->f1-l; fp->out < fp->eout && i > 0; --i) *fp->out++ = ' '; for (i = 0; i < l && fp->out < fp->eout-UTFmax+1; ++i) fp->out += runetochar(fp->out, s++); while (i < l && fp->out+(i = runetochar(temp, s)) <= fp->eout) for (j = 0; j < i; ++j) *fp->out++ = temp[j]; if (pad && (fp->f3 & FL_MINUS) != 0) for (i = fp->f1-l; fp->out < fp->eout && i > 0; --i) *fp->out++ = ' '; } else { while (fp->out <= fp->eout-UTFmax && *s != 0) fp->out += runetochar(fp->out, s++); while (fp->out+(i = runetochar(temp, s)) <= fp->eout && *s != 0) for (j = 0; j < i; ++j) *fp->out++ = temp[j]; } } #endif int numbconv(void *o, Fconv *fp) { static char digits[16] = "0123456789abcdef"; char buf[80]; /* arbitrary limit. enough digits, but no limit on f2 */ char *s = buf+sizeof(buf)-1; char sign = 0; va_list ap = o; int uc = 0; unsigned long u; if (fp->f3 & FL_UNSIGNED) { if (fp->f3 & FL_LONG) u = va_arg(ap, unsigned long); else u = va_arg(ap, unsigned int); } else { long sl; if (fp->f3 & FL_LONG) sl = va_arg(ap, long); else sl = va_arg(ap, int); if (sl < 0) { sign = '-'; u = -sl; } else { u = sl; } } *s = 0; switch (fp->chr) { case 'd': do { *--s = digits[u%10]; u /= 10; } while (u != 0); while (s > buf && buf+sizeof(buf)-s < fp->f2) *--s = '0'; if (sign) *--s = sign; break; case 'o': do { *--s = digits[u%8]; u /= 8; } while (u != 0); while (s > buf && buf+sizeof(buf)-s < fp->f2) *--s = '0'; if ((fp->f3 & FL_HASH) != 0 && *s != '0') *--s = '0'; if (sign) *--s = sign; break; case 'X': uc = 1; /* FALLTHROUGH */ case 'x': do { char digit = digits[u%16]; if (uc && digit >= 'a') digit -= 'a'-'A'; *--s = digit; u /= 16; } while (u != 0); while (s > buf && buf+sizeof(buf)-s < fp->f2) *--s = '0'; if (s > buf+2 && (fp->f3 & FL_HASH) != 0) { *--s = uc? 'X' : 'x'; *--s = '0'; } if (sign) *--s = sign; break; } strconv(s, fp); return ap-(va_list)o; }