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