2 * This code was originally part of klibc-0.103.
3 * It was adapted by David Hovemeyer <daveho@cs.umd.edu>
4 * for use in GeekOS (http://geekos.sourceforge.net).
6 * For information/source for klibc, visit
7 * http://www.kernel.org/pub/linux/libs/klibc/
8 * http://www.zytor.com/mailman/listinfo/klibc/
9 * http://www.zytor.com/cvsweb.cgi/klibc/
11 * Modifications are marked with "DHH".
12 * Summary of modifications:
14 * 1. Use struct Output_Sink to emit formatted output, rather than a
15 * character buffer, to allow output polymorphism.
17 * 2. Buffer generated numbers so that all output is generated in order.
19 * 3. Don't use long long types: unsigned long is largest
20 * supported type. Arithmetic on 64 bit types requires runtime support
23 * See the file LICENSE-klibc for license information.
29 * vsnprintf(), from which the rest of the printf()
35 #include <geekos/string.h>
37 #include <geekos/fmtout.h> /* DHH: for struct Output_Sink */
40 * DHH: Hack, long long arithmetic requires runtime support.
41 * Use unsigned long as greatest unsigned integer supported.
43 typedef long intmax_t;
44 typedef unsigned long uintmax_t;
45 typedef unsigned long uintptr_t;
49 do { if (!(exp)) while(1); } while (0)
52 FL_ZERO = 0x01, /* Zero modifier */
53 FL_MINUS = 0x02, /* Minus modifier */
54 FL_PLUS = 0x04, /* Plus modifier */
55 FL_TICK = 0x08, /* ' modifier */
56 FL_SPACE = 0x10, /* Space modifier */
57 FL_HASH = 0x20, /* # modifier */
58 FL_SIGNED = 0x40, /* Number is signed */
59 FL_UPPER = 0x80 /* Upper case digits */
62 /* These may have to be adjusted on certain implementations */
73 #define MIN_RANK rank_char
74 #define MAX_RANK rank_long
76 #define INTMAX_RANK rank_long
77 #define SIZE_T_RANK rank_long
78 #define PTRDIFF_T_RANK rank_long
81 #define EMIT(x) do { (q)->Emit((q), (x)); } while (0)
84 * DHH - As a hack, we buffer this many digits when generating
85 * a number. Because this code originally was an implementation
86 * of vnsprintf(), it generated some digits backwards in a buffer.
87 * Obviously we can't do this when emitting output directly
88 * to the console or a file. So, we buffer the generated digits
89 * and then emit them in order.
91 * This value should be adequate to emit values up to 2^32-1 in
92 * bases 2 or greater, including tick marks.
94 #define NDIGITS_MAX 43
97 format_int(struct Output_Sink *q, uintmax_t val, enum flags flags,
98 int base, int width, int prec)
102 static const char lcdigits[] = "0123456789abcdef";
103 static const char ucdigits[] = "0123456789ABCDEF";
107 int ndigits = 0, nchars;
108 int tickskip, b4tick;
109 char digit_buffer[NDIGITS_MAX]; /* DHH */
110 size_t ndigits_save; /* DHH */
112 /* Select type of digits */
113 digits = (flags & FL_UPPER) ? ucdigits : lcdigits;
115 /* If signed, separate out the minus */
116 if ( flags & FL_SIGNED && (intmax_t)val < 0 ) {
118 val = (uintmax_t)(-(intmax_t)val);
121 /* Count the number of digits needed. This returns zero for 0. */
128 /* Adjust ndigits for size of output */
130 if ( flags & FL_HASH && base == 8 ) {
131 if ( prec < ndigits+1 )
135 if ( ndigits < prec ) {
136 ndigits = prec; /* Mandatory number padding */
137 } else if ( val == 0 ) {
138 ndigits = 1; /* Zero still requires space */
141 /* For ', figure out what the skip should be */
142 if ( flags & FL_TICK ) {
143 tickskip = (base == 16) ? 4 : 3;
145 tickskip = ndigits; /* No tick marks */
148 /* Tick marks aren't digits, but generated by the number converter */
149 ndigits += (ndigits-1)/tickskip;
151 /* Now compute the number of nondigits */
154 if ( minus || (flags & (FL_PLUS|FL_SPACE)) )
155 nchars++; /* Need space for sign */
156 if ( (flags & FL_HASH) && base == 16 ) {
157 nchars += 2; /* Add 0x for hex */
160 /* Emit early space padding */
161 if ( !(flags & (FL_MINUS|FL_ZERO)) && width > nchars ) {
162 while ( width > nchars ) {
171 else if ( flags & FL_PLUS )
173 else if ( flags & FL_SPACE )
176 if ( (flags & FL_HASH) && base == 16 ) {
178 EMIT((flags & FL_UPPER) ? 'X' : 'x');
181 /* Emit zero padding */
182 if ( (flags & (FL_MINUS|FL_ZERO)) == FL_ZERO && width > ndigits ) {
183 while ( width > nchars ) {
189 /* Generate the number. This is done from right to left. */
190 ASSERT(ndigits <= NDIGITS_MAX); /* DHH */
191 ndigits_save = ndigits;
192 qq = digit_buffer + ndigits;
195 /* Emit digits to temp buffer */
197 while ( ndigits > 0 ) {
199 qq--; oo--; ndigits--;
203 qq--; oo--; ndigits--;
204 *qq = digits[val%base];
208 /* Copy digits to Output_Sink */
209 for (oo = 0; oo < ndigits_save; ++oo)
210 EMIT(digit_buffer[oo]);
212 /* Emit late space padding */
213 while ( (flags & FL_MINUS) && width > nchars ) {
222 * DHH - This function was originally vsnprintf().
223 * I renamed it to Format_Output() and changed it to take
224 * a struct Output_Sink instead of a buffer. That way, it can
225 * be used for any kind of formatted output (string, console,
228 int Format_Output(struct Output_Sink *q, const char *format, va_list ap)
230 const char *p = format;
232 size_t o = 0; /* Number of characters output */
234 int rank = rank_int; /* Default rank */
239 enum flags flags = 0;
241 st_normal, /* Ground state */
242 st_flags, /* Special flags */
243 st_width, /* Field width */
244 st_prec, /* Field precision */
245 st_modifiers /* Length or conversion modifiers */
247 const char *sarg; /* %s string argument */
248 char carg; /* %c char argument */
249 int slen; /* String length */
251 while ( (ch = *p++) ) {
256 flags = 0; rank = rank_int; width = 0; prec = -1;
284 p--; /* Process this character again */
290 if ( ch >= '0' && ch <= '9' ) {
291 width = width*10+(ch-'0');
292 } else if ( ch == '*' ) {
293 width = va_arg(ap, int);
298 } else if ( ch == '.' ) {
299 prec = 0; /* Precision given */
302 state = st_modifiers;
303 p--; /* Process this character again */
308 if ( ch >= '0' && ch <= '9' ) {
309 prec = prec*10+(ch-'0');
310 } else if ( ch == '*' ) {
311 prec = va_arg(ap, int);
315 state = st_modifiers;
316 p--; /* Process this character again */
322 /* Length modifiers - nonterminal sequences */
324 rank--; /* Shorter rank */
327 rank++; /* Longer rank */
336 rank = PTRDIFF_T_RANK;
343 /* Output modifiers - terminal sequences */
344 state = st_normal; /* Next state will be normal */
345 if ( rank < MIN_RANK ) /* Canonicalize rank */
347 else if ( rank > MAX_RANK )
351 case 'P': /* Upper case pointer */
354 case 'p': /* Pointer */
356 prec = (CHAR_BIT*sizeof(void *)+3)/4;
358 val = (uintmax_t)(uintptr_t)va_arg(ap, void *);
361 case 'd': /* Signed decimal output */
367 /* Yes, all these casts are needed... */
368 val = (uintmax_t)(intmax_t)(signed char)va_arg(ap, signed int);
371 val = (uintmax_t)(intmax_t)(signed short)va_arg(ap, signed int);
374 val = (uintmax_t)(intmax_t)va_arg(ap, signed int);
377 val = (uintmax_t)(intmax_t)va_arg(ap, signed long);
381 val = (uintmax_t)(intmax_t)va_arg(ap, signed long long);
386 case 'o': /* Octal */
389 case 'u': /* Unsigned decimal */
392 case 'X': /* Upper case hexadecimal */
395 case 'x': /* Hexadecimal */
402 val = (uintmax_t)(unsigned char)va_arg(ap, unsigned int);
405 val = (uintmax_t)(unsigned short)va_arg(ap, unsigned int);
408 val = (uintmax_t)va_arg(ap, unsigned int);
411 val = (uintmax_t)va_arg(ap, unsigned long);
415 val = (uintmax_t)va_arg(ap, unsigned long long);
422 sz = format_int(q, val, flags, base, width, prec);
426 case 'c': /* Character */
427 carg = (char)va_arg(ap, int);
431 case 's': /* String */
432 sarg = va_arg(ap, const char *);
433 sarg = sarg ? sarg : "(null)";
442 if ( prec != -1 && slen > prec )
445 if ( width > slen && !(flags & FL_MINUS) ) {
446 char pad = (flags & FL_ZERO) ? '0' : ' ';
447 while ( width > slen ) {
452 for ( i = slen ; i ; i-- ) {
456 if ( width > slen && (flags & FL_MINUS) ) {
457 while ( width > slen ) {
465 case 'n': /* Output the number of characters written */
469 *va_arg(ap, signed char *) = o;
472 *va_arg(ap, signed short *) = o;
475 *va_arg(ap, signed int *) = o;
478 *va_arg(ap, signed long *) = o;
482 *va_arg(ap, signed long long *) = o;
489 default: /* Anything else, including % */
497 /* Null-terminate the string */
500 *q = '\0'; /* No overflow */
502 buffer[n-1] = '\0'; /* Overflow - terminate at end of buffer */