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