/* This file is in the public domain. */ #include #include #include #include "fnprintf.h" static unsigned int cvt_num(char *buf, unsigned long int n, const char *dv) { char *bp; unsigned int base; unsigned int i; unsigned int j; bp = buf; base = strlen(dv); do { *bp++ = dv[n%base]; n /= base; } while (n > 0); n = bp - buf; i = 0; j = n - 1; while (i < j) { char t; t = buf[i]; buf[i] = buf[j]; buf[j] = t; i ++; j --; } return(n); } int fnprintf(void (*fn)(char), const char *fmt, ...) { va_list ap; const char *fp; unsigned int flags; #define F_ALT 0x00000001 /* "alternate form" */ #define F_ZFILL 0x00000002 /* pad with zeroes rather than spaces */ #define F_LEFT 0x00000004 /* justify left rather than right */ #define F_PLUS 0x00000008 /* explicit sign always */ #define F_SPACE 0x00000010 /* space for positive number */ #define F_WIDTH 0x00001000 /* explicit width given */ #define F_DOT 0x00002000 /* width/precision separator seen */ #define F_PREC 0x00004000 /* explicit precision given */ #define F_SHORT 0x00008000 /* short-data flag given */ #define F_LONG 0x00010000 /* long-data flag given */ #define F_NUM 0x02000000 /* doing numeric conversion */ unsigned int width; unsigned int prec; unsigned int *dvar; char conv; unsigned int charcount; char tmp[64]; const char *os; const char *altstr; int altlen; int nsb; int nz; int noc; int nsa; int i; charcount = 0; fp = fmt; va_start(ap,fmt); while (*fp) { if (*fp != '%') { if (fn) (*fn)(*fp); fp ++; charcount ++; continue; } flags = 0; width = 0; prec = 0; dvar = &width; conv = 0; while (*++fp && !conv) { switch (*fp) { case '#': flags |= F_ALT; break; case '-': flags |= F_LEFT; break; case '+': flags |= F_PLUS; break; case ' ': flags |= F_SPACE; break; case '0': if ((flags & (F_ZFILL|F_WIDTH|F_DOT|F_PREC)) == 0) { flags |= F_ZFILL; break; } /* fall through */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': *dvar = (*dvar * 10) + (*fp - '0'); flags |= (flags & F_DOT) ? F_PREC : F_WIDTH; break; case '*': *dvar = va_arg(ap,int); flags |= (flags & F_DOT) ? F_PREC : F_WIDTH; break; case '.': flags |= F_DOT; dvar = ≺ break; case 'h': flags |= F_SHORT; break; case 'l': flags |= F_LONG; break; default: conv = *fp; break; } } if (conv) { os = &tmp[0]; altstr = ""; altlen = 0; switch (conv) { case 'd': case 'i': { int signedp; const char *digs; long int v; unsigned long int uv; signedp = 1; digs = "0123456789"; if (0) { case 'u': signedp = 0; digs = "0123456789"; altstr = ""; altlen = 0; } if (0) { case 'o': signedp = 0; digs = "01234567"; altstr = "0"; altlen = 1; } if (0) { case 'x': signedp = 0; digs = "0123456789abcdef"; altstr = "0x"; altlen = 2; } if (0) { case 'X': signedp = 0; digs = "0123456789ABCDEF"; altstr = "0X"; altlen = 2; } if (signedp) { v = (flags & F_LONG) ? va_arg(ap,long int) : va_arg(ap,int); i = (v < 0); uv = i ? -(unsigned long int)v : v; } else { uv = (flags & F_LONG) ? va_arg(ap,unsigned long int) : va_arg(ap,unsigned int); i = 0; } if ((uv == 0) || !(flags & F_ALT)) { altstr = ""; altlen = 0; } if (0) { case 'p': signedp = 0; digs = "0123456789abcdef"; altstr = "0x"; altlen = 2; uv = (unsigned long int) va_arg(ap,void *); i = 0; } if (i) { noc = cvt_num(&tmp[0],uv,digs); altstr = "-"; altlen = 1; } else { if (signedp) { if (flags & F_PLUS) altstr = "+"; else if (flags & F_SPACE) altstr = " "; else altstr = 0; altlen = altstr ? 1 : 0; } noc = cvt_num(&tmp[0],uv,digs); } flags |= F_NUM; } break; case 'c': tmp[0] = va_arg(ap,int); noc = 1; break; case 's': os = va_arg(ap,char *); if (os == 0) os = "(nil pointer)"; noc = strlen(os); if ((flags & F_PREC) && (noc > prec)) noc = prec; break; case 'n': *va_arg(ap,int *) = charcount; noc = 0; flags &= ~F_WIDTH; break; case 'f': case 'e': case 'g': os = "(unimplemented)"; noc = 15; break; case '%': os = "%"; noc = 1; break; } } if (! (flags & F_NUM)) flags &= ~(F_ZFILL|F_PREC); nsb = 0; nsa = 0; nz = 0; if ((flags & F_PREC) && (noc < prec)) nz = prec - noc; if ((flags & F_WIDTH) && (noc+nz+altlen < width)) { nsb = width - (noc+nz+altlen); if (flags & F_LEFT) { nsa = nsb; nsb = 0; } if (flags & F_ZFILL) { nz += nsb; nsb = 0; } } charcount += nsb + altlen + nz + noc + nsa; #define FOO(var,c) do { if (var > 100) { if (fn) { (*fn)(171); (*fn)(c); (*fn)(187); } var = 100; } } while (0) FOO(nsb,'b'); FOO(altlen,'l'); FOO(nz,'z'); FOO(noc,'o'); FOO(nsa,'a'); if (fn) { for (;nsb>0;nsb--) (*fn)(' '); for (i=0;i0;nz--) (*fn)('0'); for (i=0;i0;nsa--) (*fn)(' '); } } return(charcount); #undef F_ALT #undef F_ZFILL #undef F_LEFT #undef F_PLUS #undef F_SPACE #undef F_WIDTH #undef F_DOT #undef F_PREC #undef F_SHORT #undef F_LONG #undef F_NUM }