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