shebanow@ernie.Berkeley.EDU (Mike Shebanow) (09/09/86)
(beware the line eater) This is a standalone version of sprintf() and vsprintf(), intended for use with Fritz Anderson's wprintf library. It provides all of the common features of UNIX printfs, with one exception: floating point numbers are not supported. The advantage of these routines over the ones provided with LightSpeedC is, quite simply, object size. This sprintf occupies about 2400 bytes of code/ disk space, compared to the 16K+ required by LightSpeed's library. Taken together with the wprintf library, the overhead required for debug messages is a mere 5K. Although the UNIX version of this routine has been used extensively, the LightSpeedC version has not been. Please report any problems to me, and I will do my best to fix them. Anyone interested in adding Mac floating point support? Please send me a copy of the hacked up code. Andrew Shebanow shebanow@ernie.Berkeley.EDU /------------------------- CUT HERE ----------------------------------/ /* * printf.c 9/8/86 * * new versions of the printf routine which use the new version of * doprnt. (Conforms to "The C Reference Manual" by Harbison and Steele!) * * Written by Michael Shebanow * Converted to LightSpeedC(TM) by Andrew G. Shebanow * * (c) Copyright 1985 Michael C. Shebanow. All Rights Reserved. * * Permission is hereby granted to use this code within your * program for debugging purposes ONLY. This code may not be * sold. */ /* since we do not include stdio, we need this definition */ #define NULL 0 /* local buffer size */ #define MAXB 64 /* * flags used in conversion state */ #define LEFT 0001 /* left justify */ #define USEPLUS 0002 /* use a plus sign for pos numbers */ #define USESP 0004 /* use space for plus numbers */ #define USEZERO 0010 /* pad with zero */ #define USEVAR 0020 /* variant format */ #define LONG 0040 /* is a long number */ #define NOSIGN 0100 /* not a signed number */ #define UPPER 0200 /* upper case hex numbers */ /* local variables */ static short state; /* state of conversion */ static short fw; /* field width */ static short prec; /* precision */ static short strprec; /* string precision */ static char sign; /* sign character */ static char pad; /* pad char */ static char buffer[MAXB]; /* local buffer */ static char reverse[MAXB]; /* for putting numbers into */ /* * number conversion routines */ static char *decimal(ptr, num) register char *ptr; register long num; { register unsigned long n; register short i, j; short signed = 0; if (state & NOSIGN) n = (unsigned long) num; else { signed = num < 0; n = (unsigned long) (signed ? -num : num); } for (i = 0; n; i++) { reverse[i] = n % 10 + '0'; n /= 10; } while (i < prec) reverse[i++] = '0'; if (!(state & LEFT)) { j = fw - i; if (signed || sign) j--; if (pad == ' ') { if (signed) reverse[i++] = '-'; else if (sign && !(state & NOSIGN)) reverse[i++] = sign; } while (j-- > 0) reverse[i++] = pad; if (pad == '0') { if (signed) reverse[i++] = '-'; else if (sign && !(state & NOSIGN)) reverse[i++] = sign; } } else if (signed) reverse[i++] = '-'; else if (sign && !(state & NOSIGN)) reverse[i++] = sign; j = i; while (i--) *ptr++ = reverse[i]; if (state & LEFT) { while (j < fw) { *ptr++ = ' '; j++; } } return(ptr); } static long _bcdtod(bcd) register unsigned long bcd; { register unsigned long val; val = bcd & 15; bcd >>= 4; val += (bcd & 15) * 10; bcd >>= 4; val += (bcd & 15) * 100; bcd >>= 4; val += (bcd & 15) * 1000; return(val); } static char *binary(ptr, n, power) register char *ptr; register unsigned long n; register short power; { register short i; register long rn; register long j = (1 << power) - 1; unsigned long original = n; for (i = 0; n; i++) { rn = n & j; rn += (rn > 9) ? (state & UPPER ? 'A' - 10 : 'a' - 10) : '0'; reverse[i] = rn; n >>= power; } while (i < prec) reverse[i++] = '0'; if (!(state & LEFT) && pad == '0') { j = fw - (state & USEVAR ? 1 + (power == 3 ? 0 : 1) : 0); while (i < j) reverse[i++] = '0'; } if (state & USEVAR) { if (power == 1) reverse[i++] = (state & UPPER) ? 'B' : 'b'; else if (power == 4) reverse[i++] = (state & UPPER) ? 'X' : 'x'; if (power != 3 || original != 0) reverse[i++] = '0'; } if (!(state & LEFT)) { while (i < fw) reverse[i++] = ' '; } j = i; while (i--) *ptr++ = reverse[i]; while (j++ < fw) *ptr++ = ' '; return(ptr); } static char *fmtchar(ptr, ch) register char *ptr; char ch; { register int i = 1; if (!(state & LEFT)) { for ( ;i < fw; i++) *ptr++ = pad; } *ptr++ = ch; for ( ;i < fw; i++) *ptr++ = ' '; return((char *) ptr); } static char *fmtstr(ptr, str) register char *ptr, *str; { register char *src; register short i = 0; register short j; for (src = str; *src; src++) i++; if (i > strprec) i = strprec; j = i; if (!(state & LEFT)) { while (j++ < fw) *ptr++ = pad; } for (i = 0; i < strprec && *str; i++) *ptr++ = *str++; while (j++ < fw) *ptr++ = ' '; return(ptr); } /* * _doprnt - this routine implements printing into a string * buffer. This has the disadvantage that the max string size is * limited by the size of the buffer. The general purpose nature * of the routine though does has advantages, in that it may be * used anywhere. */ static long _doprnt(buf, fmt, argp) register char *buf; register char *fmt; register int *argp; { register long ch; register char theCh; register char *str; char *start = buf; long *largp; while (ch = *fmt++) { if (ch != '%' || (ch = *fmt++) == '%') *buf++ = ch; else { state = 0; fw = 0; prec = 1; strprec = 30000; pad = ' '; sign = 0; for ( ; ch; ch = *fmt++) { switch (ch) { case '-': state |= LEFT; continue; case '0': state |= USEZERO; pad = '0'; break; case ' ': if (state & USEPLUS) continue; state |= USESP; sign = ' '; continue; case '+': state |= USEPLUS; state &= ~USESP; sign = '+'; continue; case '#': state |= USEVAR; continue; } break; } if (ch >= '0' && ch <= '9') { fw = 0; do { fw *= 10; fw += ch - '0'; ch = *fmt++; } while (ch >= '0' && ch <= '9'); } else if (ch == '*') { fw = *argp++; ch = *fmt++; } if (ch == '.') { prec = 0; while ((ch = *fmt++) >= '0' && ch <= '9') { prec *= 10; prec += ch - '0'; } if (ch == '*') { prec = *argp++; ch = *fmt++; } if (prec < 0) { prec = 1; strprec = 30000; } else strprec = prec; } if (ch == 'l') { state |= LONG; ch = *fmt++; } switch (ch) { case 0: fmt--; break; case 'P': state |= UPPER; /* fall through... */ case 'p': state |= NOSIGN; if ((state & LONG)) { largp = (long *) argp; ch = *largp++; argp = (short *) largp; } else ch = *argp++; ch = _bcdtod(ch); buf = decimal(buf, ch); break; case 'u': state |= NOSIGN; case 'd': if ((state & LONG)) { largp = (long *) argp; ch = *largp++; argp = (short *) largp; } else ch = *argp++; buf = decimal(buf, ch); break; case 'o': state |= NOSIGN; if ((state & LONG)) { largp = (long *) argp; ch = *largp++; argp = (short *) largp; } else ch = *argp++; buf = binary(buf, ch, 3); break; case 'B': state |= UPPER; case 'b': state |= NOSIGN; if ((state & LONG)) { largp = (long *) argp; ch = *largp++; argp = (short *) largp; } else ch = *argp++; buf = binary(buf, ch, 1); break; case 'X': state |= UPPER; case 'x': state |= NOSIGN; if ((state & LONG)) { largp = (long *) argp; ch = *largp++; argp = (short *) largp; } else ch = *argp++; buf = binary(buf, ch, 4); break; case 'c': theCh = (*argp & 0x00ff); argp++; buf = fmtchar(buf, theCh); break; case 's': str = *((char **) argp); argp++; buf = fmtstr(buf, str); break; case '%': buf = fmtchar(buf, '%'); break; default: *buf++ = ch; break; } } } *buf = '\0'; return((long) (buf - start)); } /* * PUBLIC ROUTINES: */ /* good old sprintf */ long sprintf(buf, fmt, args) char *buf, *fmt; int args; { return(_doprnt(buf, fmt, &args)); } /* this is the routine used by wprintf() */ long vsprintf(buf, fmt, args) char *buf, *fmt; int *args; { return(_doprnt(buf, fmt, args)); } /* the end */