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 #include <geekos/vm_cons.h>
82 #define EMIT(x) do { if (q == NULL) {(q)->Emit((q), (x));} } while (0)
85 * DHH - As a hack, we buffer this many digits when generating
86 * a number. Because this code originally was an implementation
87 * of vnsprintf(), it generated some digits backwards in a buffer.
88 * Obviously we can't do this when emitting output directly
89 * to the console or a file. So, we buffer the generated digits
90 * and then emit them in order.
92 * This value should be adequate to emit values up to 2^32-1 in
93 * bases 2 or greater, including tick marks.
95 #define NDIGITS_MAX 43
98 format_int(struct Output_Sink *q, uintmax_t val, enum flags flags,
99 int base, int width, int prec)
103 static const char lcdigits[] = "0123456789abcdef";
104 static const char ucdigits[] = "0123456789ABCDEF";
108 int ndigits = 0, nchars;
109 int tickskip, b4tick;
110 char digit_buffer[NDIGITS_MAX]; /* DHH */
111 size_t ndigits_save; /* DHH */
113 /* Select type of digits */
114 digits = (flags & FL_UPPER) ? ucdigits : lcdigits;
116 /* If signed, separate out the minus */
117 if ( flags & FL_SIGNED && (intmax_t)val < 0 ) {
119 val = (uintmax_t)(-(intmax_t)val);
122 /* Count the number of digits needed. This returns zero for 0. */
129 /* Adjust ndigits for size of output */
131 if ( flags & FL_HASH && base == 8 ) {
132 if ( prec < ndigits+1 )
136 if ( ndigits < prec ) {
137 ndigits = prec; /* Mandatory number padding */
138 } else if ( val == 0 ) {
139 ndigits = 1; /* Zero still requires space */
142 /* For ', figure out what the skip should be */
143 if ( flags & FL_TICK ) {
144 tickskip = (base == 16) ? 4 : 3;
146 tickskip = ndigits; /* No tick marks */
149 /* Tick marks aren't digits, but generated by the number converter */
150 ndigits += (ndigits-1)/tickskip;
152 /* Now compute the number of nondigits */
155 if ( minus || (flags & (FL_PLUS|FL_SPACE)) )
156 nchars++; /* Need space for sign */
157 if ( (flags & FL_HASH) && base == 16 ) {
158 nchars += 2; /* Add 0x for hex */
161 /* Emit early space padding */
162 if ( !(flags & (FL_MINUS|FL_ZERO)) && width > nchars ) {
163 while ( width > nchars ) {
172 else if ( flags & FL_PLUS )
174 else if ( flags & FL_SPACE )
177 if ( (flags & FL_HASH) && base == 16 ) {
179 EMIT((flags & FL_UPPER) ? 'X' : 'x');
182 /* Emit zero padding */
183 if ( (flags & (FL_MINUS|FL_ZERO)) == FL_ZERO && width > ndigits ) {
184 while ( width > nchars ) {
190 /* Generate the number. This is done from right to left. */
191 ASSERT(ndigits <= NDIGITS_MAX); /* DHH */
192 ndigits_save = ndigits;
193 qq = digit_buffer + ndigits;
196 /* Emit digits to temp buffer */
198 while ( ndigits > 0 ) {
200 qq--; oo--; ndigits--;
204 qq--; oo--; ndigits--;
205 *qq = digits[val%base];
209 /* Copy digits to Output_Sink */
210 for (oo = 0; oo < ndigits_save; ++oo)
211 EMIT(digit_buffer[oo]);
213 /* Emit late space padding */
214 while ( (flags & FL_MINUS) && width > nchars ) {
223 * DHH - This function was originally vsnprintf().
224 * I renamed it to Format_Output() and changed it to take
225 * a struct Output_Sink instead of a buffer. That way, it can
226 * be used for any kind of formatted output (string, console,
229 int Format_Output(struct Output_Sink *q, const char *format, va_list ap)
231 const char *p = format;
233 size_t o = 0; /* Number of characters output */
235 int rank = rank_int; /* Default rank */
240 enum flags flags = 0;
242 st_normal, /* Ground state */
243 st_flags, /* Special flags */
244 st_width, /* Field width */
245 st_prec, /* Field precision */
246 st_modifiers /* Length or conversion modifiers */
248 const char *sarg; /* %s string argument */
249 char carg; /* %c char argument */
250 int slen; /* String length */
252 while ( (ch = *p++) ) {
257 flags = 0; rank = rank_int; width = 0; prec = -1;
285 p--; /* Process this character again */
291 if ( ch >= '0' && ch <= '9' ) {
292 width = width*10+(ch-'0');
293 } else if ( ch == '*' ) {
294 width = va_arg(ap, int);
299 } else if ( ch == '.' ) {
300 prec = 0; /* Precision given */
303 state = st_modifiers;
304 p--; /* Process this character again */
309 if ( ch >= '0' && ch <= '9' ) {
310 prec = prec*10+(ch-'0');
311 } else if ( ch == '*' ) {
312 prec = va_arg(ap, int);
316 state = st_modifiers;
317 p--; /* Process this character again */
323 /* Length modifiers - nonterminal sequences */
325 rank--; /* Shorter rank */
328 rank++; /* Longer rank */
337 rank = PTRDIFF_T_RANK;
344 /* Output modifiers - terminal sequences */
345 state = st_normal; /* Next state will be normal */
346 if ( rank < MIN_RANK ) /* Canonicalize rank */
348 else if ( rank > MAX_RANK )
352 case 'P': /* Upper case pointer */
355 case 'p': /* Pointer */
357 prec = (CHAR_BIT*sizeof(void *)+3)/4;
359 val = (uintmax_t)(uintptr_t)va_arg(ap, void *);
362 case 'd': /* Signed decimal output */
368 /* Yes, all these casts are needed... */
369 val = (uintmax_t)(intmax_t)(signed char)va_arg(ap, signed int);
372 val = (uintmax_t)(intmax_t)(signed short)va_arg(ap, signed int);
375 val = (uintmax_t)(intmax_t)va_arg(ap, signed int);
378 val = (uintmax_t)(intmax_t)va_arg(ap, signed long);
382 val = (uintmax_t)(intmax_t)va_arg(ap, signed long long);
387 case 'o': /* Octal */
390 case 'u': /* Unsigned decimal */
393 case 'X': /* Upper case hexadecimal */
396 case 'x': /* Hexadecimal */
403 val = (uintmax_t)(unsigned char)va_arg(ap, unsigned int);
406 val = (uintmax_t)(unsigned short)va_arg(ap, unsigned int);
409 val = (uintmax_t)va_arg(ap, unsigned int);
412 val = (uintmax_t)va_arg(ap, unsigned long);
416 val = (uintmax_t)va_arg(ap, unsigned long long);
423 sz = format_int(q, val, flags, base, width, prec);
427 case 'c': /* Character */
428 carg = (char)va_arg(ap, int);
432 case 's': /* String */
433 sarg = va_arg(ap, const char *);
434 sarg = sarg ? sarg : "(null)";
443 if ( prec != -1 && slen > prec )
446 if ( width > slen && !(flags & FL_MINUS) ) {
447 char pad = (flags & FL_ZERO) ? '0' : ' ';
448 while ( width > slen ) {
453 for ( i = slen ; i ; i-- ) {
457 if ( width > slen && (flags & FL_MINUS) ) {
458 while ( width > slen ) {
466 case 'n': /* Output the number of characters written */
470 *va_arg(ap, signed char *) = o;
473 *va_arg(ap, signed short *) = o;
476 *va_arg(ap, signed int *) = o;
479 *va_arg(ap, signed long *) = o;
483 *va_arg(ap, signed long long *) = o;
490 default: /* Anything else, including % */
498 /* Null-terminate the string */
501 *q = '\0'; /* No overflow */
503 buffer[n-1] = '\0'; /* Overflow - terminate at end of buffer */