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