[comp.os.minix] new doprintf.c

bunscho@cs.vu.nl (Bunschoten Jacob Pieter) (12/04/87)

/* This version of doprintf.c can handle %% and optional arguments.
   %% is recognized and printf("%%") gives a single %
   printf("%*c",12,'a'); gives 11 spaces and then 'a'

	modification made by Jacob P. Bunschoten

   %-*.*	works now too	(JPB)
*/
#include "../include/stdio.h"

#define MAXDIGITS 12
#define PRIVATE   static
#define IS_DIGIT(X) ('0' <= (X) && (X) <= '9')

/* This is the same as varargs , on BSD systems */

#define GET_ARG(arglist,mode) ((mode *)(arglist += sizeof(mode)))[-1]

_doprintf(fp, format, args)
FILE *fp;
register char *format;
int args;
{
	register char *vl;
	int  r,
	    w1, w2,
	    sign;
	long l;
	char c;
	char *s;
	char padchar;
	char a[MAXDIGITS];

	vl = (char *) args;

	while ( *format != '\0') {
		if ( *format != '%' ) {
			putc( *format++, fp );
			continue;
		}

		format++;	/* % seen */
		if (*format == '%') {	/* %% seen */
			putc(*format++, fp);
			continue;
		}

		w1 = 0;
		w2 = 0;
		sign = 1;
		padchar = ' ';

		if ( *format == '-' ) {
			sign = -1;
			format++;
		}

		if ( *format == '0') {
			padchar = '0';
			format++;
		}

		if ( IS_DIGIT(*format) || *format == '*') {
			if (*format == '*') {
				w1 = (int) GET_ARG(vl, int);
				w1 *= sign;
				format++;
			} else {
				while ( IS_DIGIT( *format )) {
					w1 *= 10 ;
					w1 += sign * ( *format - '0' );
					format++;
				}
			}
		}

		if ( *format == '.'){
			format++;
			if ( IS_DIGIT( *format) || *format == '*') {
				if (*format == '*') {
					w2 = (int) GET_ARG(vl, int);
					format++;
				} else {
					while ( IS_DIGIT( *format )) {
						w2 *= 10;
						w2 += *format - '0';
						format++;
					}
				}
			}
		}

		switch (*format) {
		case 'd':
			l = (long) GET_ARG(vl, int);
			r = 10;
			break;
		case 'u':
			l = (long) GET_ARG(vl, int);
			l = l & 0xFFFF;
			r = 10;
			break;
		case 'o':
			l = (long) GET_ARG(vl, int);
			if (l < 0) l = l & 0xFFFF;
			r = 8;
			break;
		case 'x':
			l = (long) GET_ARG(vl, int);
			if (l < 0) l = l & 0xFFFF;
			r = 16;
			break;
		case 'D':
			l = (long) GET_ARG(vl, long);
			r = 10;
			break;
		case 'O':
			l = (long) GET_ARG(vl, long);
			r = 8;
			break;
		case 'X':
			l = (long) GET_ARG(vl, long);
			r = 16;
			break;
		case 'c':
			c = (char) GET_ARG(vl, int); 
			/* char's are casted back to int's */
			a[0] = c;	/* set c into a string */
			a[1] = '\0';
			_printit(a,w1,w2,padchar,strlen(a),fp);			
			format++;
			continue;
		case 's':
			s = GET_ARG(vl, char *);
			_printit(s,w1,w2,padchar,strlen(s),fp);
			format++;
			continue;
		default:
			putc('%',fp);	/* why */
			putc(*format++,fp);
			continue;
		}

		_bintoascii (l, r, a);
		_printit(a,w1,w2,padchar,strlen(a),fp);
		format++;
	}
}



PRIVATE _bintoascii (num, radix, a)
long    num;
int     radix;
char    *a;
{
	char b[MAXDIGITS];
	int hit, negative;
	register int n, i;

	negative = 0;
	if (num == 0) {
		a[0] = '0';
		a[1] = '\0';
		return;
	}
	if (radix == 10) {
		if (num < 0) {num = -num; negative++;}
	} 

	for (n = 0; n < MAXDIGITS; n++)
		b[n] = 0;

	n = 0;

	do {
		if (radix == 10) {
			b[n] = num % 10;
			num = (num - b[n]) / 10;
		}
		if (radix == 8) {
			b[n] = num & 0x7;
			num = (num >> 3) & 0x1FFFFFFF;
		}
		if (radix == 16) {
			b[n] = num & 0xF;
			num = (num >> 4) & 0x0FFFFFFF;
		}
		n++;
	} 
	while (num != 0);

	/* Convert to ASCII. */
	hit = 0;
	for (i = n - 1; i >= 0; i--) {
		if (b[i] == 0 && hit == 0) {
			b[i] = ' ';
		}
		else {
			if (b[i] < 10)
				b[i] += '0';
			else
				b[i] += 'A' - 10;
			hit++;
		}
	}
	if (negative)
		b[n++] = '-';
	
	for(i = n - 1 ; i >= 0 ; i-- )
		*a++ = b[i];
	
	*a = '\0';
	
}


PRIVATE _printit(str, w1, w2, padchar, length, file)
char *str;
int w1, w2;
char padchar;
int length;
FILE *file;
{
	int len2 = length;
	int temp;
	
	if ( w2 > 0  && length > w2) 
		len2 = w2;

	temp = len2;

	if ( w1 > 0 )
		while ( w1-- > len2 ) 
			putc(padchar,file);
	
	while ( *str && ( len2-- != 0 ))
		putc(*str++, file);

	if ( w1 < 0 )
		if ( padchar == '0' ){
			putc('.',file);
			w1++;
		}
		while ( w1++ < -temp )
			putc(padchar,file);
}