xref: /csrg-svn/usr.bin/ex/printf.c (revision 466)
1*466Smark /* char printf_id[] = "@(#) printf.c:2.2 6/5/79";*/
2*466Smark #include "varargs.h"
3*466Smark /*
4*466Smark  * This version of printf is compatible with the Version 7 C
5*466Smark  * printf. The differences are only minor except that this
6*466Smark  * printf assumes it is to print through putchar. Version 7
7*466Smark  * printf is more general (and is much larger) and includes
8*466Smark  * provisions for floating point.
9*466Smark  */
10*466Smark 
11*466Smark 
12*466Smark #define MAXOCT	11	/* Maximum octal digits in a long */
13*466Smark #define MAXINT	32767	/* largest normal length positive integer */
14*466Smark #define BIG	1000000000  /* largest power of 10 less than an unsigned long */
15*466Smark #define MAXDIGS	10	/* number of digits in BIG */
16*466Smark 
17*466Smark static int width, sign, fill;
18*466Smark 
19*466Smark char *_p_dconv();
20*466Smark 
21*466Smark printf(va_alist)
22*466Smark 	va_dcl
23*466Smark {
24*466Smark 	va_list ap;
25*466Smark 	register char *fmt;
26*466Smark 	char fcode;
27*466Smark 	int prec;
28*466Smark 	int length,mask1,nbits,n;
29*466Smark 	long int mask2, num;
30*466Smark 	register char *bptr;
31*466Smark 	char *ptr;
32*466Smark 	char buf[134];
33*466Smark 
34*466Smark 	va_start(ap);
35*466Smark 	fmt = va_arg(ap,char *);
36*466Smark 	for (;;) {
37*466Smark 		/* process format string first */
38*466Smark 		while ((fcode = *fmt++)!='%') {
39*466Smark 			/* ordinary (non-%) character */
40*466Smark 			if (fcode=='\0')
41*466Smark 				return;
42*466Smark 			putchar(fcode);
43*466Smark 		}
44*466Smark 		/* length modifier: -1 for h, 1 for l, 0 for none */
45*466Smark 		length = 0;
46*466Smark 		/* check for a leading - sign */
47*466Smark 		sign = 0;
48*466Smark 		if (*fmt == '-') {
49*466Smark 			sign++;
50*466Smark 			fmt++;
51*466Smark 		}
52*466Smark 		/* a '0' may follow the - sign */
53*466Smark 		/* this is the requested fill character */
54*466Smark 		fill = 1;
55*466Smark 		if (*fmt == '0') {
56*466Smark 			fill--;
57*466Smark 			fmt++;
58*466Smark 		}
59*466Smark 
60*466Smark 		/* Now comes a digit string which may be a '*' */
61*466Smark 		if (*fmt == '*') {
62*466Smark 			width = va_arg(ap, int);
63*466Smark 			if (width < 0) {
64*466Smark 				width = -width;
65*466Smark 				sign = !sign;
66*466Smark 			}
67*466Smark 			fmt++;
68*466Smark 		}
69*466Smark 		else {
70*466Smark 			width = 0;
71*466Smark 			while (*fmt>='0' && *fmt<='9')
72*466Smark 				width = width * 10 + (*fmt++ - '0');
73*466Smark 		}
74*466Smark 
75*466Smark 		/* maybe a decimal point followed by more digits (or '*') */
76*466Smark 		if (*fmt=='.') {
77*466Smark 			if (*++fmt == '*') {
78*466Smark 				prec = va_arg(ap, int);
79*466Smark 				fmt++;
80*466Smark 			}
81*466Smark 			else {
82*466Smark 				prec = 0;
83*466Smark 				while (*fmt>='0' && *fmt<='9')
84*466Smark 					prec = prec * 10 + (*fmt++ - '0');
85*466Smark 			}
86*466Smark 		}
87*466Smark 		else
88*466Smark 			prec = -1;
89*466Smark 
90*466Smark 		/*
91*466Smark 		 * At this point, "sign" is nonzero if there was
92*466Smark 		 * a sign, "fill" is 0 if there was a leading
93*466Smark 		 * zero and 1 otherwise, "width" and "prec"
94*466Smark 		 * contain numbers corresponding to the digit
95*466Smark 		 * strings before and after the decimal point,
96*466Smark 		 * respectively, and "fmt" addresses the next
97*466Smark 		 * character after the whole mess. If there was
98*466Smark 		 * no decimal point, "prec" will be -1.
99*466Smark 		 */
100*466Smark 		switch (*fmt) {
101*466Smark 			case 'L':
102*466Smark 			case 'l':
103*466Smark 				length = 2;
104*466Smark 				/* no break!! */
105*466Smark 			case 'h':
106*466Smark 			case 'H':
107*466Smark 				length--;
108*466Smark 				fmt++;
109*466Smark 				break;
110*466Smark 		}
111*466Smark 
112*466Smark 		/*
113*466Smark 		 * At exit from the following switch, we will
114*466Smark 		 * emit the characters starting at "bptr" and
115*466Smark 		 * ending at "ptr"-1, unless fcode is '\0'.
116*466Smark 		 */
117*466Smark 		switch (fcode = *fmt++) {
118*466Smark 			/* process characters and strings first */
119*466Smark 			case 'c':
120*466Smark 				buf[0] = va_arg(ap, int);
121*466Smark 				ptr = bptr = &buf[0];
122*466Smark 				if (buf[0] != '\0')
123*466Smark 					ptr++;
124*466Smark 				break;
125*466Smark 			case 's':
126*466Smark 				bptr = va_arg(ap,char *);
127*466Smark 				if (bptr==0)
128*466Smark 					bptr = "(null pointer)";
129*466Smark 				if (prec < 0)
130*466Smark 					prec = MAXINT;
131*466Smark 				for (n=0; *bptr++ && n < prec; n++) ;
132*466Smark 				ptr = --bptr;
133*466Smark 				bptr -= n;
134*466Smark 				break;
135*466Smark 			case 'O':
136*466Smark 				length = 1;
137*466Smark 				fcode = 'o';
138*466Smark 				/* no break */
139*466Smark 			case 'o':
140*466Smark 			case 'X':
141*466Smark 			case 'x':
142*466Smark 				if (length > 0)
143*466Smark 					num = va_arg(ap,long);
144*466Smark 				else
145*466Smark 					num = (unsigned)va_arg(ap,int);
146*466Smark 				if (fcode=='o') {
147*466Smark 					mask1 = 0x7;
148*466Smark 					mask2 = 0x1fffffffL;
149*466Smark 					nbits = 3;
150*466Smark 				}
151*466Smark 				else {
152*466Smark 					mask1 = 0xf;
153*466Smark 					mask2 = 0x0fffffffL;
154*466Smark 					nbits = 4;
155*466Smark 				}
156*466Smark 				n = (num!=0);
157*466Smark 				bptr = buf + MAXOCT + 3;
158*466Smark 				/* shift and mask for speed */
159*466Smark 				do
160*466Smark 				    if (((int) num & mask1) < 10)
161*466Smark 					*--bptr = ((int) num & mask1) + 060;
162*466Smark 				    else
163*466Smark 					*--bptr = ((int) num & mask1) + 0127;
164*466Smark 				while (num = (num >> nbits) & mask2);
165*466Smark 
166*466Smark 				if (fcode=='o') {
167*466Smark 					if (n)
168*466Smark 						*--bptr = '0';
169*466Smark 				}
170*466Smark 				else
171*466Smark 					if (!sign && fill <= 0) {
172*466Smark 						putchar('0');
173*466Smark 						putchar(fcode);
174*466Smark 						width -= 2;
175*466Smark 					}
176*466Smark 					else {
177*466Smark 						*--bptr = fcode;
178*466Smark 						*--bptr = '0';
179*466Smark 					}
180*466Smark 				ptr = buf + MAXOCT + 3;
181*466Smark 				break;
182*466Smark 			case 'D':
183*466Smark 			case 'U':
184*466Smark 			case 'I':
185*466Smark 				length = 1;
186*466Smark 				fcode = fcode + 'a' - 'A';
187*466Smark 				/* no break */
188*466Smark 			case 'd':
189*466Smark 			case 'i':
190*466Smark 			case 'u':
191*466Smark 				if (length > 0)
192*466Smark 					num = va_arg(ap,long);
193*466Smark 				else {
194*466Smark 					n = va_arg(ap,int);
195*466Smark 					if (fcode=='u')
196*466Smark 						num = (unsigned) n;
197*466Smark 					else
198*466Smark 						num = (long) n;
199*466Smark 				}
200*466Smark 				if (n = (fcode != 'u' && num < 0))
201*466Smark 					num = -num;
202*466Smark 				/* now convert to digits */
203*466Smark 				bptr = _p_dconv(num, buf);
204*466Smark 				if (n)
205*466Smark 					*--bptr = '-';
206*466Smark 				if (fill == 0)
207*466Smark 					fill = -1;
208*466Smark 				ptr = buf + MAXDIGS + 1;
209*466Smark 				break;
210*466Smark 			default:
211*466Smark 				/* not a control character,
212*466Smark 				 * print it.
213*466Smark 				 */
214*466Smark 				ptr = bptr = &fcode;
215*466Smark 				ptr++;
216*466Smark 				break;
217*466Smark 			}
218*466Smark 			if (fcode != '\0')
219*466Smark 				_p_emit(bptr,ptr);
220*466Smark 	}
221*466Smark 	va_end(ap);
222*466Smark }
223*466Smark 
224*466Smark /* _p_dconv converts the unsigned long integer "value" to
225*466Smark  * printable decimal and places it in "buffer", right-justified.
226*466Smark  * The value returned is the address of the first non-zero character,
227*466Smark  * or the address of the last character if all are zero.
228*466Smark  * The result is NOT null terminated, and is MAXDIGS characters long,
229*466Smark  * starting at buffer[1] (to allow for insertion of a sign).
230*466Smark  *
231*466Smark  * This program assumes it is running on 2's complement machine
232*466Smark  * with reasonable overflow treatment.
233*466Smark  */
234*466Smark char *
235*466Smark _p_dconv(value, buffer)
236*466Smark 	long value;
237*466Smark 	char *buffer;
238*466Smark {
239*466Smark 	register char *bp;
240*466Smark 	register int svalue;
241*466Smark 	int n;
242*466Smark 	long lval;
243*466Smark 
244*466Smark 	bp = buffer;
245*466Smark 
246*466Smark 	/* zero is a special case */
247*466Smark 	if (value == 0) {
248*466Smark 		bp += MAXDIGS;
249*466Smark 		*bp = '0';
250*466Smark 		return(bp);
251*466Smark 	}
252*466Smark 
253*466Smark 	/* develop the leading digit of the value in "n" */
254*466Smark 	n = 0;
255*466Smark 	while (value < 0) {
256*466Smark 		value -= BIG;	/* will eventually underflow */
257*466Smark 		n++;
258*466Smark 	}
259*466Smark 	while ((lval = value - BIG) >= 0) {
260*466Smark 		value = lval;
261*466Smark 		n++;
262*466Smark 	}
263*466Smark 
264*466Smark 	/* stash it in buffer[1] to allow for a sign */
265*466Smark 	bp[1] = n + '0';
266*466Smark 	/*
267*466Smark 	 * Now develop the rest of the digits. Since speed counts here,
268*466Smark 	 * we do it in two loops. The first gets "value" down until it
269*466Smark 	 * is no larger than MAXINT. The second one uses integer divides
270*466Smark 	 * rather than long divides to speed it up.
271*466Smark 	 */
272*466Smark 	bp += MAXDIGS + 1;
273*466Smark 	while (value > MAXINT) {
274*466Smark 		*--bp = (int)(value % 10) + '0';
275*466Smark 		value /= 10;
276*466Smark 	}
277*466Smark 
278*466Smark 	/* cannot lose precision */
279*466Smark 	svalue = value;
280*466Smark 	while (svalue > 0) {
281*466Smark 		*--bp = (svalue % 10) + '0';
282*466Smark 		svalue /= 10;
283*466Smark 	}
284*466Smark 
285*466Smark 	/* fill in intermediate zeroes if needed */
286*466Smark 	if (buffer[1] != '0') {
287*466Smark 		while (bp > buffer + 2)
288*466Smark 			*--bp = '0';
289*466Smark 		--bp;
290*466Smark 	}
291*466Smark 	return(bp);
292*466Smark }
293*466Smark 
294*466Smark /*
295*466Smark  * This program sends string "s" to putchar. The character after
296*466Smark  * the end of "s" is given by "send". This allows the size of the
297*466Smark  * field to be computed; it is stored in "alen". "width" contains the
298*466Smark  * user specified length. If width<alen, the width will be taken to
299*466Smark  * be alen. "sign" is zero if the string is to be right-justified
300*466Smark  * in the field, nonzero if it is to be left-justified. "fill" is
301*466Smark  * 0 if the string is to be padded with '0', positive if it is to be
302*466Smark  * padded with ' ', and negative if an initial '-' should appear before
303*466Smark  * any padding in right-justification (to avoid printing "-3" as
304*466Smark  * "000-3" where "-0003" was intended).
305*466Smark  */
306*466Smark _p_emit(s, send)
307*466Smark 	register char *s;
308*466Smark 	char *send;
309*466Smark {
310*466Smark 	char cfill;
311*466Smark 	register int alen;
312*466Smark 	int npad;
313*466Smark 
314*466Smark 	alen = send - s;
315*466Smark 	if (alen > width)
316*466Smark 		width = alen;
317*466Smark 	cfill = fill>0? ' ': '0';
318*466Smark 
319*466Smark 	/* we may want to print a leading '-' before anything */
320*466Smark 	if (*s == '-' && fill < 0) {
321*466Smark 		putchar(*s++);
322*466Smark 		alen--;
323*466Smark 		width--;
324*466Smark 	}
325*466Smark 	npad = width - alen;
326*466Smark 
327*466Smark 	/* emit any leading pad characters */
328*466Smark 	if (!sign)
329*466Smark 		while (--npad >= 0)
330*466Smark 			putchar(cfill);
331*466Smark 
332*466Smark 	/* emit the string itself */
333*466Smark 	while (--alen >= 0)
334*466Smark 		putchar(*s++);
335*466Smark 
336*466Smark 	/* emit trailing pad characters */
337*466Smark 	if (sign)
338*466Smark 		while (--npad >= 0)
339*466Smark 			putchar(cfill);
340*466Smark }
341