[net.sources.mac] Standalone printf for LightSpeedC

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