]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Mach Operating System | |
3 | * Copyright (c) 1991,1990,1989 Carnegie Mellon University | |
4 | * All Rights Reserved. | |
5 | * | |
6 | * Permission to use, copy, modify and distribute this software and its | |
7 | * documentation is hereby granted, provided that both the copyright | |
8 | * notice and this permission notice appear in all copies of the | |
9 | * software, derivative works or modified versions, and any portions | |
10 | * thereof, and that both notices appear in supporting documentation. | |
11 | * | |
12 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" | |
13 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR | |
14 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. | |
15 | * | |
16 | * Carnegie Mellon requests users of this software to return to | |
17 | * | |
18 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU | |
19 | * School of Computer Science | |
20 | * Carnegie Mellon University | |
21 | * Pittsburgh PA 15213-3890 | |
22 | * | |
23 | * any improvements or extensions that they make and grant Carnegie Mellon | |
24 | * the rights to redistribute these changes. | |
25 | */ | |
26 | ||
27 | #include <stdarg.h> | |
28 | #include <minilib.h> | |
29 | #include "doprnt.h" | |
30 | ||
31 | /* How irritating -- this boolean crap. */ | |
32 | #define FALSE 0 | |
33 | #define TRUE 1 | |
34 | typedef int boolean_t; | |
35 | ||
36 | /* | |
37 | * Common code for printf et al. | |
38 | * | |
39 | * The calling routine typically takes a variable number of arguments, | |
40 | * and passes the address of the first one. This implementation | |
41 | * assumes a straightforward, stack implementation, aligned to the | |
42 | * machine's wordsize. Increasing addresses are assumed to point to | |
43 | * successive arguments (left-to-right), as is the case for a machine | |
44 | * with a downward-growing stack with arguments pushed right-to-left. | |
45 | * | |
46 | * To write, for example, fprintf() using this routine, the code | |
47 | * | |
48 | * fprintf(fd, format, args) | |
49 | * FILE *fd; | |
50 | * char *format; | |
51 | * { | |
52 | * _doprnt(format, &args, fd); | |
53 | * } | |
54 | * | |
55 | * would suffice. (This example does not handle the fprintf's "return | |
56 | * value" correctly, but who looks at the return value of fprintf | |
57 | * anyway?) | |
58 | * | |
59 | * This version implements the following printf features: | |
60 | * | |
61 | * %d decimal conversion | |
62 | * %u unsigned conversion | |
63 | * %x hexadecimal conversion | |
64 | * %X hexadecimal conversion with capital letters | |
65 | * %o octal conversion | |
66 | * %c character | |
67 | * %s string | |
68 | * %m.n field width, precision | |
69 | * %-m.n left adjustment | |
70 | * %0m.n zero-padding | |
71 | * %*.* width and precision taken from arguments | |
72 | * | |
73 | * This version does not implement %f, %e, or %g. It accepts, but | |
74 | * ignores, an `l' as in %ld, %lo, %lx, and %lu, and therefore will not | |
75 | * work correctly on machines for which sizeof(long) != sizeof(int). | |
76 | * It does not even parse %D, %O, or %U; you should be using %ld, %o and | |
77 | * %lu if you mean long conversion. | |
78 | * | |
79 | * As mentioned, this version does not return any reasonable value. | |
80 | * | |
81 | * Permission is granted to use, modify, or propagate this code as | |
82 | * long as this notice is incorporated. | |
83 | * | |
84 | * Steve Summit 3/25/87 | |
85 | */ | |
86 | ||
87 | /* | |
88 | * Added formats for decoding device registers: | |
89 | * | |
90 | * printf("reg = %b", regval, "<base><arg>*") | |
91 | * | |
92 | * where <base> is the output base expressed as a control character: | |
93 | * i.e. '\10' gives octal, '\20' gives hex. Each <arg> is a sequence of | |
94 | * characters, the first of which gives the bit number to be inspected | |
95 | * (origin 1), and the rest (up to a control character (<= 32)) give the | |
96 | * name of the register. Thus | |
97 | * printf("reg = %b\n", 3, "\10\2BITTWO\1BITONE") | |
98 | * would produce | |
99 | * reg = 3<BITTWO,BITONE> | |
100 | * | |
101 | * If the second character in <arg> is also a control character, it | |
102 | * indicates the last bit of a bit field. In this case, printf will extract | |
103 | * bits <1> to <2> and print it. Characters following the second control | |
104 | * character are printed before the bit field. | |
105 | * printf("reg = %b\n", 0xb, "\10\4\3FIELD1=\2BITTWO\1BITONE") | |
106 | * would produce | |
107 | * reg = b<FIELD1=2,BITONE> | |
108 | */ | |
109 | /* | |
110 | * Added for general use: | |
111 | * # prefix for alternate format: | |
112 | * 0x (0X) for hex | |
113 | * leading 0 for octal | |
114 | * + print '+' if positive | |
115 | * blank print ' ' if positive | |
116 | * | |
117 | * z signed hexadecimal | |
118 | * r signed, 'radix' | |
119 | * n unsigned, 'radix' | |
120 | * | |
121 | * D,U,O,Z same as corresponding lower-case versions | |
122 | * (compatibility) | |
123 | */ | |
124 | /* | |
125 | * Added ANSI %p for pointers. Output looks like 0x%08x. | |
126 | */ | |
127 | /* | |
128 | * | |
129 | * Added special for L4-use: %t format | |
130 | * | |
131 | * prints L4-threadids. standard format is "task.thread". Field-width | |
132 | * may be specified with width-modifier. Padding begins with threadid, | |
133 | * up to 2 chars, task-part follows. | |
134 | * | |
135 | * modifiers: | |
136 | * # surrounds output with square brackets [] | |
137 | * l prints the high and low part of a threadid | |
138 | * fixed length for the dwords: 8 chars | |
139 | * 0 as usual, padding after optional '[' | |
140 | * - as usual | |
141 | * | |
142 | * Jork Loeser 9/20/99 | |
143 | */ | |
144 | ||
145 | #define isdigit(d) ((d) >= '0' && (d) <= '9') | |
146 | #define Ctod(c) ((c) - '0') | |
147 | ||
148 | #define MAXBUF (sizeof(long int) * 8) /* enough for binary */ | |
149 | ||
150 | static char digs[] = "0123456789abcdef"; | |
151 | ||
152 | static void | |
153 | printnum(u, base, putc, putc_arg) | |
154 | register unsigned long u; /* number to print */ | |
155 | register int base; | |
156 | void (*putc)(); | |
157 | char *putc_arg; | |
158 | { | |
159 | char buf[MAXBUF]; /* build number here */ | |
160 | register char * p = &buf[MAXBUF-1]; | |
161 | ||
162 | do { | |
163 | *p-- = digs[u % base]; | |
164 | u /= base; | |
165 | } while (u != 0); | |
166 | ||
167 | while (++p != &buf[MAXBUF]) | |
168 | (*putc)(putc_arg, *p); | |
169 | } | |
170 | ||
171 | boolean_t _doprnt_truncates = FALSE; | |
172 | ||
173 | void _doprnt(fmt, args, radix, putc, putc_arg) | |
174 | register const char *fmt; | |
175 | va_list args; | |
176 | int radix; /* default radix - for '%r' */ | |
177 | void (*putc)(); /* character output */ | |
178 | char *putc_arg; /* argument for putc */ | |
179 | { | |
180 | int length; | |
181 | int prec; | |
182 | boolean_t ladjust; | |
183 | char padc; | |
184 | long n; | |
185 | unsigned long u; | |
186 | int plus_sign; | |
187 | int sign_char; | |
188 | boolean_t altfmt, truncate; | |
189 | int base; | |
190 | char c; | |
191 | int longopt; | |
192 | ||
193 | while (*fmt != '\0') { | |
194 | if (*fmt != '%') { | |
195 | (*putc)(putc_arg, *fmt++); | |
196 | continue; | |
197 | } | |
198 | ||
199 | fmt++; | |
200 | ||
201 | length = 0; | |
202 | prec = -1; | |
203 | ladjust = FALSE; | |
204 | padc = ' '; | |
205 | plus_sign = 0; | |
206 | sign_char = 0; | |
207 | altfmt = FALSE; | |
208 | longopt = 0; | |
209 | ||
210 | while (TRUE) { | |
211 | if (*fmt == '#') { | |
212 | altfmt = TRUE; | |
213 | fmt++; | |
214 | } | |
215 | else if (*fmt == '-') { | |
216 | ladjust = TRUE; | |
217 | fmt++; | |
218 | } | |
219 | else if (*fmt == '+') { | |
220 | plus_sign = '+'; | |
221 | fmt++; | |
222 | } | |
223 | else if (*fmt == ' ') { | |
224 | if (plus_sign == 0) | |
225 | plus_sign = ' '; | |
226 | fmt++; | |
227 | } | |
228 | else | |
229 | break; | |
230 | } | |
231 | ||
232 | if (*fmt == '0') { | |
233 | padc = '0'; | |
234 | fmt++; | |
235 | } | |
236 | ||
237 | if (isdigit(*fmt)) { | |
238 | while(isdigit(*fmt)) | |
239 | length = 10 * length + Ctod(*fmt++); | |
240 | } | |
241 | else if (*fmt == '*') { | |
242 | length = va_arg(args, int); | |
243 | fmt++; | |
244 | if (length < 0) { | |
245 | ladjust = !ladjust; | |
246 | length = -length; | |
247 | } | |
248 | } | |
249 | ||
250 | if (*fmt == '.') { | |
251 | fmt++; | |
252 | if (isdigit(*fmt)) { | |
253 | prec = 0; | |
254 | while(isdigit(*fmt)) | |
255 | prec = 10 * prec + Ctod(*fmt++); | |
256 | } | |
257 | else if (*fmt == '*') { | |
258 | prec = va_arg(args, int); | |
259 | fmt++; | |
260 | } | |
261 | } | |
262 | ||
263 | while (*fmt == 'l'){ | |
264 | longopt++; | |
265 | fmt++; | |
266 | } | |
267 | ||
268 | truncate = FALSE; | |
269 | ||
270 | switch(*fmt) { | |
271 | case 'b': | |
272 | case 'B': | |
273 | { | |
274 | register char *p; | |
275 | boolean_t any; | |
276 | register int i; | |
277 | ||
278 | u = va_arg(args, unsigned long); | |
279 | p = va_arg(args, char *); | |
280 | base = *p++; | |
281 | printnum(u, base, putc, putc_arg); | |
282 | ||
283 | if (u == 0) | |
284 | break; | |
285 | ||
286 | any = FALSE; | |
287 | while ((i = *p++) != 0) { | |
288 | /* NOTE: The '32' here is because ascii space */ | |
289 | if (*p <= 32) { | |
290 | /* | |
291 | * Bit field | |
292 | */ | |
293 | register int j; | |
294 | if (any) | |
295 | (*putc)(putc_arg, ','); | |
296 | else { | |
297 | (*putc)(putc_arg, '<'); | |
298 | any = TRUE; | |
299 | } | |
300 | j = *p++; | |
301 | for (; (c = *p) > 32; p++) | |
302 | (*putc)(putc_arg, c); | |
303 | printnum((unsigned)( (u>>(j-1)) & ((2<<(i-j))-1)), | |
304 | base, putc, putc_arg); | |
305 | } | |
306 | else if (u & (1<<(i-1))) { | |
307 | if (any) | |
308 | (*putc)(putc_arg, ','); | |
309 | else { | |
310 | (*putc)(putc_arg, '<'); | |
311 | any = TRUE; | |
312 | } | |
313 | for (; (c = *p) > 32; p++) | |
314 | (*putc)(putc_arg, c); | |
315 | } | |
316 | else { | |
317 | for (; *p > 32; p++) | |
318 | continue; | |
319 | } | |
320 | } | |
321 | if (any) | |
322 | (*putc)(putc_arg, '>'); | |
323 | break; | |
324 | } | |
325 | ||
326 | case 'c': | |
327 | c = va_arg(args, int); | |
328 | (*putc)(putc_arg, c); | |
329 | break; | |
330 | ||
331 | case 's': | |
332 | { | |
333 | register char *p; | |
334 | register char *p2; | |
335 | ||
336 | if (prec == -1) | |
337 | prec = 0x7fffffff; /* MAXINT */ | |
338 | ||
339 | p = va_arg(args, char *); | |
340 | ||
341 | if (p == (char *)0) | |
342 | p = ""; | |
343 | ||
344 | if (length > 0 && !ladjust) { | |
345 | n = 0; | |
346 | p2 = p; | |
347 | ||
348 | for (; *p != '\0' && n < prec; p++) | |
349 | n++; | |
350 | ||
351 | p = p2; | |
352 | ||
353 | while (n < length) { | |
354 | (*putc)(putc_arg, ' '); | |
355 | n++; | |
356 | } | |
357 | } | |
358 | ||
359 | n = 0; | |
360 | ||
361 | while (*p != '\0') { | |
362 | if (++n > prec) | |
363 | break; | |
364 | ||
365 | (*putc)(putc_arg, *p++); | |
366 | } | |
367 | ||
368 | if (n < length && ladjust) { | |
369 | while (n < length) { | |
370 | (*putc)(putc_arg, ' '); | |
371 | n++; | |
372 | } | |
373 | } | |
374 | ||
375 | break; | |
376 | } | |
377 | ||
378 | ||
379 | case 'o': | |
380 | truncate = _doprnt_truncates; | |
381 | case 'O': | |
382 | base = 8; | |
383 | goto print_unsigned; | |
384 | ||
385 | case 'd': | |
386 | truncate = _doprnt_truncates; | |
387 | case 'D': | |
388 | base = 10; | |
389 | goto print_signed; | |
390 | ||
391 | case 'u': | |
392 | truncate = _doprnt_truncates; | |
393 | case 'U': | |
394 | base = 10; | |
395 | goto print_unsigned; | |
396 | ||
397 | case 'p': | |
398 | padc = '0'; | |
399 | length = 8; | |
400 | /* | |
401 | * We do this instead of just setting altfmt to TRUE | |
402 | * because we want 0 to have a 0x in front, and we want | |
403 | * eight digits after the 0x -- not just 6. | |
404 | */ | |
405 | (*putc)(putc_arg, '0'); | |
406 | (*putc)(putc_arg, 'x'); | |
407 | case 'x': | |
408 | truncate = _doprnt_truncates; | |
409 | case 'X': | |
410 | base = 16; | |
411 | goto print_unsigned; | |
412 | ||
413 | case 'z': | |
414 | truncate = _doprnt_truncates; | |
415 | case 'Z': | |
416 | base = 16; | |
417 | goto print_signed; | |
418 | ||
419 | case 'r': | |
420 | truncate = _doprnt_truncates; | |
421 | case 'R': | |
422 | base = radix; | |
423 | goto print_signed; | |
424 | ||
425 | case 'n': | |
426 | truncate = _doprnt_truncates; | |
427 | case 'N': | |
428 | base = radix; | |
429 | goto print_unsigned; | |
430 | ||
431 | print_signed: | |
432 | if (longopt>1) | |
433 | n = va_arg(args, long long); | |
434 | else | |
435 | n = va_arg(args, long); | |
436 | if (n >= 0) { | |
437 | u = n; | |
438 | sign_char = plus_sign; | |
439 | } | |
440 | else { | |
441 | u = -n; | |
442 | sign_char = '-'; | |
443 | } | |
444 | goto print_num; | |
445 | ||
446 | print_unsigned: | |
447 | if (longopt>1) | |
448 | u = va_arg(args, unsigned long long); | |
449 | else | |
450 | u = va_arg(args, unsigned long); | |
451 | goto print_num; | |
452 | ||
453 | print_num: | |
454 | { | |
455 | char buf[MAXBUF]; /* build number here */ | |
456 | register char * p = &buf[MAXBUF-1]; | |
457 | static char digits[] = "0123456789abcdef"; | |
458 | char *prefix = 0; | |
459 | ||
460 | if (truncate) u = (long)((int)(u)); | |
461 | ||
462 | if (u != 0 && altfmt) { | |
463 | if (base == 8) | |
464 | prefix = "0"; | |
465 | else if (base == 16) | |
466 | prefix = "0x"; | |
467 | } | |
468 | ||
469 | do { | |
470 | *p-- = digits[u % base]; | |
471 | u /= base; | |
472 | } while (u != 0); | |
473 | ||
474 | length -= (&buf[MAXBUF-1] - p); | |
475 | if (sign_char) | |
476 | length--; | |
477 | if (prefix) | |
478 | length -= strlen(prefix); | |
479 | ||
480 | if (padc == ' ' && !ladjust) { | |
481 | /* blank padding goes before prefix */ | |
482 | while (--length >= 0) | |
483 | (*putc)(putc_arg, ' '); | |
484 | } | |
485 | if (sign_char) | |
486 | (*putc)(putc_arg, sign_char); | |
487 | if (prefix) | |
488 | while (*prefix) | |
489 | (*putc)(putc_arg, *prefix++); | |
490 | if (padc == '0') { | |
491 | /* zero padding goes after sign and prefix */ | |
492 | while (--length >= 0) | |
493 | (*putc)(putc_arg, '0'); | |
494 | } | |
495 | while (++p != &buf[MAXBUF]) | |
496 | (*putc)(putc_arg, *p); | |
497 | ||
498 | if (ladjust) { | |
499 | while (--length >= 0) | |
500 | (*putc)(putc_arg, ' '); | |
501 | } | |
502 | break; | |
503 | } | |
504 | ||
505 | case '\0': | |
506 | fmt--; | |
507 | break; | |
508 | ||
509 | default: | |
510 | (*putc)(putc_arg, *fmt); | |
511 | } | |
512 | fmt++; | |
513 | } | |
514 | } |