/* prf.c -- printf & sprintf routines for RAM-Monitor and VIOS
 * copyright (c) 1984  American Information Systems Corporation
 *  Daniel Steinberg
 *  April, 1984
 *
 */

#include "monitor.h"
#include "vctype.h"

#define Putchar(c) if (buf EQ NULL) \
			{if ((char) (c) NE '\0')  OUTCHR(c);} \
			else *buf++ = (char) (c);
 

/*VARARGS1*/
printf(fmt, x1)
    char *fmt;
    unsigned x1;
{
    prf(NULL, fmt, &x1);	/* formatted print to default output port */
}

/*VARARGS1*/
sprintf(buf, fmt, x1)
    char *buf;
    char *fmt;
    unsigned x1;
{
    prf(buf, fmt, &x1);		/* formatted print to supplied buffer */
}

prf (buf, fmt, args)
    char *buf;
    register char *fmt;
    unsigned *args;

/* prf (buf, fmt, args) -- Formatted print to buffer or default port
 *	in:	buf		Buffer address, or NULL if direct output
 *	in:	fmt		Format string
 *	in:	args		Address of arguments
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Similar to stdio printf/sprintf, but stripped down to handle:
 *		%c		character
 *		%[#]s		string (#, if specified, pads at the right,
 *					behaving like stdio  %-#s)
 *		%[#]x or %[#]h	hexadecimal constant
 *		%[#]o		octal constant
 *		%[#]u		unsigned decimal constant
 *		%[#]d		signed decimal constant
 *		%[#]b		unsigned binary constant (not in stdio)
 *
 *	[IN THE FUTURE:]
 *		%e or %f or %g	super-simplified floating point
 *		%[#][l]?	long numbers
 */
{
    register char *s;
    register int c;
    int limit;
    long num;
    register int base;

loop:
    while((c = *fmt++) != '%')
	{
	Putchar(c);		/* put char in output stream */
	if (c EQ '\0')
	    return;		/* asciz terminator */
	} /*while*/

    limit = 0;			/* initialize default char count */
    while ((c = *fmt++), isdigit(c))	/* as long as digits follow % */
	{
	limit *= 10;		/* digit counts are always decimal */
	limit += (c-'0');	/* add in new digit value */
	}

    if (limit EQ 0)  limit = -1;	/* convert zero to no limit */

    switch (c)
	{
    case 'c':
	Putchar(*args);		/* convert int-ized arg to a character */
	goto nextarg;		/* skip digit output */

    case 's':
	s = (char*) *args;	/* arg is a ptr to a string */

	while (limit NE 0)		/* limit the string length */
	    {
	    if ((c = *s++) EQ '\0')	/* if end of string */
		break;			/* break out */
	    Putchar(c);			/* otherwise, print the next char */
	    limit--;			/* count a character out */
	    } /*while*/
	
	while (limit-- GT 0)		/* pad out to limit with spaces */
	    Putchar(' ');
	
	goto nextarg;		/* skip digit output */

    case 'd':			/* signed decimal number */
	if ( (num = (long) (*((int*)(args)))) LT 0 )
	    {
	    Putchar('-');	/* if negative, output a minus sign */
	    num *= -1;		/* and change to positive */
	    }
	base = 10;
	goto outnumber;		/* output num directly */

    case 'u':			/* unsigned decimal number */
	base = 10;
	break;

    case 'b':			/* unsigned binary number */
	base = 2;
	break;

    case 'o':			/* unsigned octal number */
	base = 8;
	break;

    default:
    case 'x':
    case 'h':			/* unsigned hexadecimal number */
	base = 16;
	break;
	} /*switch*/

    num = (unsigned long) *args;	/* get number to print */

outnumber:
    prn(&buf, (long) num, base, limit);	/* print it out */

nextarg:
    args++;			/* update argument pointer */
    goto loop;
}

prn (str, num, base, limit)
    char **str;
    unsigned long num;
    int base;
    int limit;

/* prn (str, num, base, limit) -- Print an unsigned number in any base
 *	in:	str		ptr to output buf ptr, or NULL for direct output
 *	in:	num		(long) unsigned integer
 *	in:	base		numeric base for number conversion (2-16)
 *	in:	limit		if >0, number of characters to output
 *	return:	(NONE)
 *	thrown:	(NONE)
 *
 *	Converts 'num' to an ascii numeric string of numeric base 'base'
 *	and sends it to 'buf' or the default output port.  If 'limit' is
 *	greater than 0, the output is padded on the left with zeroes.
 *	If 'limit' is smaller than the number of significant digits,
 *	only the least significant digits will be output.
 *
 *	CAUTION: All input values must be reasonable as no error detection
 *			is performed!!
 */
{
    register char *buf;
    register long a;
    register char c;

    /* If there are more than one significant digit and the limit
     * has not been reached, or if there are more zeroes to fill,
     * recall, recursively, with the next most significant value */

    if ( ((a = num/base) AND (limit NE 1)) OR (limit GT 1) ) {
	prn(str, a, base, --limit);	/* one less char total in limit */
    }

    buf = *str;		/* set buffer ptr for Putchar macro */

    c = (char) ("0123456789ABCDEF"[(int)(num%base)]);

    Putchar(c);
    *str = buf;		/* restore buffer ptr, if any */
}
