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 */