148255Sbostic /*-
2*63049Sbostic * Copyright (c) 1980, 1993
3*63049Sbostic * The Regents of the University of California. All rights reserved.
448255Sbostic *
548255Sbostic * %sccs.include.proprietary.c%
621708Sdist */
721708Sdist
821708Sdist #ifndef lint
9*63049Sbostic static char sccsid[] = "@(#)printf.c 8.1 (Berkeley) 06/09/93";
1048255Sbostic #endif /* not lint */
1148255Sbostic
1248255Sbostic #ifndef lint
1330596Sconrad /* The pwb version this is based on */
1430596Sconrad static char *printf_id = "@(#) printf.c:2.2 6/5/79";
1548255Sbostic #endif /* not lint */
1621708Sdist
1750677Storek #if __STDC__
1850677Storek #include <stdarg.h>
1950677Storek #else
2030596Sconrad #include <varargs.h>
2150677Storek #endif
2230596Sconrad
23466Smark /*
24466Smark * This version of printf is compatible with the Version 7 C
25466Smark * printf. The differences are only minor except that this
26466Smark * printf assumes it is to print through putchar. Version 7
27466Smark * printf is more general (and is much larger) and includes
28466Smark * provisions for floating point.
29466Smark */
30466Smark
31466Smark #define MAXOCT 11 /* Maximum octal digits in a long */
32466Smark #define MAXINT 32767 /* largest normal length positive integer */
33466Smark #define BIG 1000000000 /* largest power of 10 less than an unsigned long */
34466Smark #define MAXDIGS 10 /* number of digits in BIG */
35466Smark
36466Smark static int width, sign, fill;
37466Smark
38466Smark char *_p_dconv();
39466Smark
4030596Sconrad /* VARARGS */
4150677Storek #if __STDC__
ex_printf(const char * fmt0,...)4250677Storek ex_printf(const char *fmt0, ...)
4350677Storek #else
4450677Storek ex_printf(fmt0, va_alist)
4550677Storek char *fmt0;
46466Smark va_dcl
4750677Storek #endif
48466Smark {
4950677Storek register char *fmt;
50466Smark va_list ap;
51466Smark char fcode;
52466Smark int prec;
53466Smark int length,mask1,nbits,n;
54466Smark long int mask2, num;
55466Smark register char *bptr;
56466Smark char *ptr;
57466Smark char buf[134];
58466Smark
5950677Storek #if __STDC__
6050677Storek va_start(ap, fmt0);
6150677Storek #else
62466Smark va_start(ap);
6350677Storek #endif
6450677Storek fmt = (char *)fmt0;
65466Smark for (;;) {
66466Smark /* process format string first */
67466Smark while ((fcode = *fmt++)!='%') {
68466Smark /* ordinary (non-%) character */
69466Smark if (fcode=='\0')
70466Smark return;
7130596Sconrad ex_putchar(fcode);
72466Smark }
73466Smark /* length modifier: -1 for h, 1 for l, 0 for none */
74466Smark length = 0;
75466Smark /* check for a leading - sign */
76466Smark sign = 0;
77466Smark if (*fmt == '-') {
78466Smark sign++;
79466Smark fmt++;
80466Smark }
81466Smark /* a '0' may follow the - sign */
82466Smark /* this is the requested fill character */
83466Smark fill = 1;
84466Smark if (*fmt == '0') {
85466Smark fill--;
86466Smark fmt++;
87466Smark }
88466Smark
89466Smark /* Now comes a digit string which may be a '*' */
90466Smark if (*fmt == '*') {
91466Smark width = va_arg(ap, int);
92466Smark if (width < 0) {
93466Smark width = -width;
94466Smark sign = !sign;
95466Smark }
96466Smark fmt++;
97466Smark }
98466Smark else {
99466Smark width = 0;
100466Smark while (*fmt>='0' && *fmt<='9')
101466Smark width = width * 10 + (*fmt++ - '0');
102466Smark }
103466Smark
104466Smark /* maybe a decimal point followed by more digits (or '*') */
105466Smark if (*fmt=='.') {
106466Smark if (*++fmt == '*') {
107466Smark prec = va_arg(ap, int);
108466Smark fmt++;
109466Smark }
110466Smark else {
111466Smark prec = 0;
112466Smark while (*fmt>='0' && *fmt<='9')
113466Smark prec = prec * 10 + (*fmt++ - '0');
114466Smark }
115466Smark }
116466Smark else
117466Smark prec = -1;
118466Smark
119466Smark /*
120466Smark * At this point, "sign" is nonzero if there was
121466Smark * a sign, "fill" is 0 if there was a leading
122466Smark * zero and 1 otherwise, "width" and "prec"
123466Smark * contain numbers corresponding to the digit
124466Smark * strings before and after the decimal point,
125466Smark * respectively, and "fmt" addresses the next
126466Smark * character after the whole mess. If there was
127466Smark * no decimal point, "prec" will be -1.
128466Smark */
129466Smark switch (*fmt) {
130466Smark case 'L':
131466Smark case 'l':
132466Smark length = 2;
133466Smark /* no break!! */
134466Smark case 'h':
135466Smark case 'H':
136466Smark length--;
137466Smark fmt++;
138466Smark break;
139466Smark }
140466Smark
141466Smark /*
142466Smark * At exit from the following switch, we will
143466Smark * emit the characters starting at "bptr" and
144466Smark * ending at "ptr"-1, unless fcode is '\0'.
145466Smark */
146466Smark switch (fcode = *fmt++) {
147466Smark /* process characters and strings first */
148466Smark case 'c':
149466Smark buf[0] = va_arg(ap, int);
150466Smark ptr = bptr = &buf[0];
151466Smark if (buf[0] != '\0')
152466Smark ptr++;
153466Smark break;
154466Smark case 's':
155466Smark bptr = va_arg(ap,char *);
156466Smark if (bptr==0)
157466Smark bptr = "(null pointer)";
158466Smark if (prec < 0)
159466Smark prec = MAXINT;
160466Smark for (n=0; *bptr++ && n < prec; n++) ;
161466Smark ptr = --bptr;
162466Smark bptr -= n;
163466Smark break;
164466Smark case 'O':
165466Smark length = 1;
166466Smark fcode = 'o';
167466Smark /* no break */
168466Smark case 'o':
169466Smark case 'X':
170466Smark case 'x':
171466Smark if (length > 0)
172466Smark num = va_arg(ap,long);
173466Smark else
174466Smark num = (unsigned)va_arg(ap,int);
175466Smark if (fcode=='o') {
176466Smark mask1 = 0x7;
177466Smark mask2 = 0x1fffffffL;
178466Smark nbits = 3;
179466Smark }
180466Smark else {
181466Smark mask1 = 0xf;
182466Smark mask2 = 0x0fffffffL;
183466Smark nbits = 4;
184466Smark }
185466Smark n = (num!=0);
186466Smark bptr = buf + MAXOCT + 3;
187466Smark /* shift and mask for speed */
188466Smark do
189466Smark if (((int) num & mask1) < 10)
190466Smark *--bptr = ((int) num & mask1) + 060;
191466Smark else
192466Smark *--bptr = ((int) num & mask1) + 0127;
193466Smark while (num = (num >> nbits) & mask2);
194466Smark
195466Smark if (fcode=='o') {
196466Smark if (n)
197466Smark *--bptr = '0';
198466Smark }
199466Smark else
200466Smark if (!sign && fill <= 0) {
20130596Sconrad ex_putchar('0');
20230596Sconrad ex_putchar(fcode);
203466Smark width -= 2;
204466Smark }
205466Smark else {
206466Smark *--bptr = fcode;
207466Smark *--bptr = '0';
208466Smark }
209466Smark ptr = buf + MAXOCT + 3;
210466Smark break;
211466Smark case 'D':
212466Smark case 'U':
213466Smark case 'I':
214466Smark length = 1;
215466Smark fcode = fcode + 'a' - 'A';
216466Smark /* no break */
217466Smark case 'd':
218466Smark case 'i':
219466Smark case 'u':
220466Smark if (length > 0)
221466Smark num = va_arg(ap,long);
222466Smark else {
223466Smark n = va_arg(ap,int);
224466Smark if (fcode=='u')
225466Smark num = (unsigned) n;
226466Smark else
227466Smark num = (long) n;
228466Smark }
229466Smark if (n = (fcode != 'u' && num < 0))
230466Smark num = -num;
231466Smark /* now convert to digits */
232466Smark bptr = _p_dconv(num, buf);
233466Smark if (n)
234466Smark *--bptr = '-';
235466Smark if (fill == 0)
236466Smark fill = -1;
237466Smark ptr = buf + MAXDIGS + 1;
238466Smark break;
239466Smark default:
240466Smark /* not a control character,
241466Smark * print it.
242466Smark */
243466Smark ptr = bptr = &fcode;
244466Smark ptr++;
245466Smark break;
246466Smark }
247466Smark if (fcode != '\0')
248466Smark _p_emit(bptr,ptr);
249466Smark }
250466Smark va_end(ap);
251466Smark }
252466Smark
253466Smark /* _p_dconv converts the unsigned long integer "value" to
254466Smark * printable decimal and places it in "buffer", right-justified.
255466Smark * The value returned is the address of the first non-zero character,
256466Smark * or the address of the last character if all are zero.
257466Smark * The result is NOT null terminated, and is MAXDIGS characters long,
258466Smark * starting at buffer[1] (to allow for insertion of a sign).
259466Smark *
260466Smark * This program assumes it is running on 2's complement machine
261466Smark * with reasonable overflow treatment.
262466Smark */
263466Smark char *
_p_dconv(value,buffer)264466Smark _p_dconv(value, buffer)
265466Smark long value;
266466Smark char *buffer;
267466Smark {
268466Smark register char *bp;
269466Smark register int svalue;
270466Smark int n;
271466Smark long lval;
272466Smark
273466Smark bp = buffer;
274466Smark
275466Smark /* zero is a special case */
276466Smark if (value == 0) {
277466Smark bp += MAXDIGS;
278466Smark *bp = '0';
279466Smark return(bp);
280466Smark }
281466Smark
282466Smark /* develop the leading digit of the value in "n" */
283466Smark n = 0;
284466Smark while (value < 0) {
285466Smark value -= BIG; /* will eventually underflow */
286466Smark n++;
287466Smark }
288466Smark while ((lval = value - BIG) >= 0) {
289466Smark value = lval;
290466Smark n++;
291466Smark }
292466Smark
293466Smark /* stash it in buffer[1] to allow for a sign */
294466Smark bp[1] = n + '0';
295466Smark /*
296466Smark * Now develop the rest of the digits. Since speed counts here,
297466Smark * we do it in two loops. The first gets "value" down until it
298466Smark * is no larger than MAXINT. The second one uses integer divides
299466Smark * rather than long divides to speed it up.
300466Smark */
301466Smark bp += MAXDIGS + 1;
302466Smark while (value > MAXINT) {
303466Smark *--bp = (int)(value % 10) + '0';
304466Smark value /= 10;
305466Smark }
306466Smark
307466Smark /* cannot lose precision */
308466Smark svalue = value;
309466Smark while (svalue > 0) {
310466Smark *--bp = (svalue % 10) + '0';
311466Smark svalue /= 10;
312466Smark }
313466Smark
314466Smark /* fill in intermediate zeroes if needed */
315466Smark if (buffer[1] != '0') {
316466Smark while (bp > buffer + 2)
317466Smark *--bp = '0';
318466Smark --bp;
319466Smark }
320466Smark return(bp);
321466Smark }
322466Smark
323466Smark /*
324466Smark * This program sends string "s" to putchar. The character after
325466Smark * the end of "s" is given by "send". This allows the size of the
326466Smark * field to be computed; it is stored in "alen". "width" contains the
327466Smark * user specified length. If width<alen, the width will be taken to
328466Smark * be alen. "sign" is zero if the string is to be right-justified
329466Smark * in the field, nonzero if it is to be left-justified. "fill" is
330466Smark * 0 if the string is to be padded with '0', positive if it is to be
331466Smark * padded with ' ', and negative if an initial '-' should appear before
332466Smark * any padding in right-justification (to avoid printing "-3" as
333466Smark * "000-3" where "-0003" was intended).
334466Smark */
_p_emit(s,send)335466Smark _p_emit(s, send)
336466Smark register char *s;
337466Smark char *send;
338466Smark {
339466Smark char cfill;
340466Smark register int alen;
341466Smark int npad;
342466Smark
343466Smark alen = send - s;
344466Smark if (alen > width)
345466Smark width = alen;
346466Smark cfill = fill>0? ' ': '0';
347466Smark
348466Smark /* we may want to print a leading '-' before anything */
349466Smark if (*s == '-' && fill < 0) {
35030596Sconrad ex_putchar(*s++);
351466Smark alen--;
352466Smark width--;
353466Smark }
354466Smark npad = width - alen;
355466Smark
356466Smark /* emit any leading pad characters */
357466Smark if (!sign)
358466Smark while (--npad >= 0)
35930596Sconrad ex_putchar(cfill);
360466Smark
361466Smark /* emit the string itself */
362466Smark while (--alen >= 0)
36330596Sconrad ex_putchar(*s++);
364466Smark
365466Smark /* emit trailing pad characters */
366466Smark if (sign)
367466Smark while (--npad >= 0)
36830596Sconrad ex_putchar(cfill);
369466Smark }
370