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