Added boot process information to help someone find out what really happens.
[bootos.git] / stage2 / printf.c
blob77bf0c57d8d193385404317e2ff77be6fb33d06b
1 /*
2 * Copyright (c) 1995 Patrick Powell.
4 * This code is based on code written by Patrick Powell <papowell@astart.com>.
5 * It may be used for any purpose as long as this notice remains intact on all
6 * source code distributions.
7 */
9 /*
10 * Copyright (c) 2008 Holger Weiss.
12 * This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
13 * My changes to the code may freely be used, modified and/or redistributed for
14 * any purpose. It would be nice if additions and fixes to this file (including
15 * trivial code cleanups) would be sent back in order to let me include them in
16 * the version available at <http://www.jhweiss.de/software/snprintf.html>.
17 * However, this is not a requirement for using or redistributing (possibly
18 * modified) versions of this file, nor is leaving this notice intact mandatory.
22 * History
24 * 2009-03-05 Hector Martin <hector@marcansoft.com>
26 * Hacked up and removed a lot of stuff including floating-point support,
27 * a bunch of ifs and defines, locales, and tests
29 * 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1:
31 * Fixed the detection of infinite floating point values on IRIX (and
32 * possibly other systems) and applied another few minor cleanups.
34 * 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0:
36 * Added a lot of new features, fixed many bugs, and incorporated various
37 * improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery
38 * <rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller
39 * <djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH
40 * projects. The additions include: support the "e", "E", "g", "G", and
41 * "F" conversion specifiers (and use conversion style "f" or "F" for the
42 * still unsupported "a" and "A" specifiers); support the "hh", "ll", "j",
43 * "t", and "z" length modifiers; support the "#" flag and the (non-C99)
44 * "'" flag; use localeconv(3) (if available) to get both the current
45 * locale's decimal point character and the separator between groups of
46 * digits; fix the handling of various corner cases of field width and
47 * precision specifications; fix various floating point conversion bugs;
48 * handle infinite and NaN floating point values; don't attempt to write to
49 * the output buffer (which may be NULL) if a size of zero was specified;
50 * check for integer overflow of the field width, precision, and return
51 * values and during the floating point conversion; use the OUTCHAR() macro
52 * instead of a function for better performance; provide asprintf(3) and
53 * vasprintf(3) functions; add new test cases. The replacement functions
54 * have been renamed to use an "rpl_" prefix, the function calls in the
55 * main project (and in this file) must be redefined accordingly for each
56 * replacement function which is needed (by using Autoconf or other means).
57 * Various other minor improvements have been applied and the coding style
58 * was cleaned up for consistency.
60 * 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13:
62 * C99 compliant snprintf(3) and vsnprintf(3) functions return the number
63 * of characters that would have been written to a sufficiently sized
64 * buffer (excluding the '\0'). The original code simply returned the
65 * length of the resulting output string, so that's been fixed.
67 * 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8:
69 * The original code assumed that both snprintf(3) and vsnprintf(3) were
70 * missing. Some systems only have snprintf(3) but not vsnprintf(3), so
71 * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
73 * 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i:
75 * The PGP code was using unsigned hexadecimal formats. Unfortunately,
76 * unsigned formats simply didn't work.
78 * 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1:
80 * Ok, added some minimal floating point support, which means this probably
81 * requires libm on most operating systems. Don't yet support the exponent
82 * (e,E) and sigfig (g,G). Also, fmtint() was pretty badly broken, it just
83 * wasn't being exercised in ways which showed it, so that's been fixed.
84 * Also, formatted the code to Mutt conventions, and removed dead code left
85 * over from the original. Also, there is now a builtin-test, run with:
86 * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf
88 * 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43:
90 * This was ugly. It is still ugly. I opted out of floating point
91 * numbers, but the formatter understands just about everything from the
92 * normal C string format, at least as far as I can tell from the Solaris
93 * 2.5 printf(3S) man page.
96 #include "types.h"
98 #include <limits.h>
99 #include <stdarg.h>
101 #define VA_START(ap, last) va_start(ap, last)
102 #define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */
104 #if HAVE_INTTYPES_H
105 #include <inttypes.h> /* For intmax_t (if not defined in <stdint.h>). */
106 #endif /* HAVE_INTTYPES_H */
108 #if HAVE_STDDEF_H
109 #include <stddef.h> /* For ptrdiff_t. */
110 #endif /* HAVE_STDDEF_H */
112 #if HAVE_STDINT_H
113 #include <stdint.h> /* For intmax_t. */
114 #endif /* HAVE_STDINT_H */
116 /* Support for unsigned long long int. We may also need ULLONG_MAX. */
117 #ifndef ULONG_MAX /* We may need ULONG_MAX as a fallback. */
118 #ifdef UINT_MAX
119 #define ULONG_MAX UINT_MAX
120 #else
121 #define ULONG_MAX INT_MAX
122 #endif /* defined(UINT_MAX) */
123 #endif /* !defined(ULONG_MAX) */
124 #ifdef ULLONG
125 #undef ULLONG
126 #endif /* defined(ULLONG) */
127 #if HAVE_UNSIGNED_LONG_LONG_INT
128 #define ULLONG unsigned long long int
129 #ifndef ULLONG_MAX
130 #define ULLONG_MAX ULONG_MAX
131 #endif /* !defined(ULLONG_MAX) */
132 #else
133 #define ULLONG unsigned long int
134 #ifdef ULLONG_MAX
135 #undef ULLONG_MAX
136 #endif /* defined(ULLONG_MAX) */
137 #define ULLONG_MAX ULONG_MAX
138 #endif /* HAVE_LONG_LONG_INT */
140 /* Support for uintmax_t. We also need UINTMAX_MAX. */
141 #ifdef UINTMAX_T
142 #undef UINTMAX_T
143 #endif /* defined(UINTMAX_T) */
144 #if HAVE_UINTMAX_T || defined(uintmax_t)
145 #define UINTMAX_T uintmax_t
146 #ifndef UINTMAX_MAX
147 #define UINTMAX_MAX ULLONG_MAX
148 #endif /* !defined(UINTMAX_MAX) */
149 #else
150 #define UINTMAX_T ULLONG
151 #ifdef UINTMAX_MAX
152 #undef UINTMAX_MAX
153 #endif /* defined(UINTMAX_MAX) */
154 #define UINTMAX_MAX ULLONG_MAX
155 #endif /* HAVE_UINTMAX_T || defined(uintmax_t) */
157 /* Support for long long int. */
158 #ifndef LLONG
159 #if HAVE_LONG_LONG_INT
160 #define LLONG long long int
161 #else
162 #define LLONG long int
163 #endif /* HAVE_LONG_LONG_INT */
164 #endif /* !defined(LLONG) */
166 /* Support for intmax_t. */
167 #ifndef INTMAX_T
168 #if HAVE_INTMAX_T || defined(intmax_t)
169 #define INTMAX_T intmax_t
170 #else
171 #define INTMAX_T LLONG
172 #endif /* HAVE_INTMAX_T || defined(intmax_t) */
173 #endif /* !defined(INTMAX_T) */
175 /* Support for uintptr_t. */
176 #ifndef UINTPTR_T
177 #if HAVE_UINTPTR_T || defined(uintptr_t)
178 #define UINTPTR_T uintptr_t
179 #else
180 #define UINTPTR_T unsigned long int
181 #endif /* HAVE_UINTPTR_T || defined(uintptr_t) */
182 #endif /* !defined(UINTPTR_T) */
184 /* Support for ptrdiff_t. */
185 #ifndef PTRDIFF_T
186 #if HAVE_PTRDIFF_T || defined(ptrdiff_t)
187 #define PTRDIFF_T ptrdiff_t
188 #else
189 #define PTRDIFF_T long int
190 #endif /* HAVE_PTRDIFF_T || defined(ptrdiff_t) */
191 #endif /* !defined(PTRDIFF_T) */
194 * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99:
195 * 7.19.6.1, 7). However, we'll simply use PTRDIFF_T and convert it to an
196 * unsigned type if necessary. This should work just fine in practice.
198 #ifndef UPTRDIFF_T
199 #define UPTRDIFF_T PTRDIFF_T
200 #endif /* !defined(UPTRDIFF_T) */
203 * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7).
204 * However, we'll simply use size_t and convert it to a signed type if
205 * necessary. This should work just fine in practice.
207 #ifndef SSIZE_T
208 #define SSIZE_T size_t
209 #endif /* !defined(SSIZE_T) */
213 * Buffer size to hold the octal string representation of UINT128_MAX without
214 * nul-termination ("3777777777777777777777777777777777777777777").
216 #ifdef MAX_CONVERT_LENGTH
217 #undef MAX_CONVERT_LENGTH
218 #endif /* defined(MAX_CONVERT_LENGTH) */
219 #define MAX_CONVERT_LENGTH 43
221 /* Format read states. */
222 #define PRINT_S_DEFAULT 0
223 #define PRINT_S_FLAGS 1
224 #define PRINT_S_WIDTH 2
225 #define PRINT_S_DOT 3
226 #define PRINT_S_PRECISION 4
227 #define PRINT_S_MOD 5
228 #define PRINT_S_CONV 6
230 /* Format flags. */
231 #define PRINT_F_MINUS (1 << 0)
232 #define PRINT_F_PLUS (1 << 1)
233 #define PRINT_F_SPACE (1 << 2)
234 #define PRINT_F_NUM (1 << 3)
235 #define PRINT_F_ZERO (1 << 4)
236 #define PRINT_F_QUOTE (1 << 5)
237 #define PRINT_F_UP (1 << 6)
238 #define PRINT_F_UNSIGNED (1 << 7)
239 #define PRINT_F_TYPE_G (1 << 8)
240 #define PRINT_F_TYPE_E (1 << 9)
242 /* Conversion flags. */
243 #define PRINT_C_CHAR 1
244 #define PRINT_C_SHORT 2
245 #define PRINT_C_LONG 3
246 #define PRINT_C_LLONG 4
247 //#define PRINT_C_LDOUBLE 5
248 #define PRINT_C_SIZE 6
249 #define PRINT_C_PTRDIFF 7
250 #define PRINT_C_INTMAX 8
252 #ifndef MAX
253 #define MAX(x, y) ((x >= y) ? x : y)
254 #endif /* !defined(MAX) */
255 #ifndef CHARTOINT
256 #define CHARTOINT(ch) (ch - '0')
257 #endif /* !defined(CHARTOINT) */
258 #ifndef ISDIGIT
259 #define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9')
260 #endif /* !defined(ISDIGIT) */
262 #define OUTCHAR(str, len, size, ch) \
263 do { \
264 if (len + 1 < size) \
265 str[len] = ch; \
266 (len)++; \
267 } while (/* CONSTCOND */ 0)
269 static void fmtstr(char *, size_t *, size_t, const char *, int, int, int);
270 static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int);
271 static void printsep(char *, size_t *, size_t);
272 static int getnumsep(int);
273 static int convert(UINTMAX_T, char *, size_t, int, int);
275 int vsnprintf(char *str, size_t size, const char *format, va_list args)
277 INTMAX_T value;
278 unsigned char cvalue;
279 const char *strvalue;
280 INTMAX_T *intmaxptr;
281 PTRDIFF_T *ptrdiffptr;
282 SSIZE_T *sizeptr;
283 LLONG *llongptr;
284 long int *longptr;
285 int *intptr;
286 short int *shortptr;
287 signed char *charptr;
288 size_t len = 0;
289 int overflow = 0;
290 int base = 0;
291 int cflags = 0;
292 int flags = 0;
293 int width = 0;
294 int precision = -1;
295 int state = PRINT_S_DEFAULT;
296 char ch = *format++;
299 * C99 says: "If `n' is zero, nothing is written, and `s' may be a null
300 * pointer." (7.19.6.5, 2) We're forgiving and allow a NULL pointer
301 * even if a size larger than zero was specified. At least NetBSD's
302 * snprintf(3) does the same, as well as other versions of this file.
303 * (Though some of these versions will write to a non-NULL buffer even
304 * if a size of zero was specified, which violates the standard.)
306 if (str == NULL && size != 0)
307 size = 0;
309 while (ch != '\0')
310 switch (state) {
311 case PRINT_S_DEFAULT:
312 if (ch == '%')
313 state = PRINT_S_FLAGS;
314 else
315 OUTCHAR(str, len, size, ch);
316 ch = *format++;
317 break;
318 case PRINT_S_FLAGS:
319 switch (ch) {
320 case '-':
321 flags |= PRINT_F_MINUS;
322 ch = *format++;
323 break;
324 case '+':
325 flags |= PRINT_F_PLUS;
326 ch = *format++;
327 break;
328 case ' ':
329 flags |= PRINT_F_SPACE;
330 ch = *format++;
331 break;
332 case '#':
333 flags |= PRINT_F_NUM;
334 ch = *format++;
335 break;
336 case '0':
337 flags |= PRINT_F_ZERO;
338 ch = *format++;
339 break;
340 case '\'': /* SUSv2 flag (not in C99). */
341 flags |= PRINT_F_QUOTE;
342 ch = *format++;
343 break;
344 default:
345 state = PRINT_S_WIDTH;
346 break;
348 break;
349 case PRINT_S_WIDTH:
350 if (ISDIGIT(ch)) {
351 ch = CHARTOINT(ch);
352 if (width > (INT_MAX - ch) / 10) {
353 overflow = 1;
354 goto out;
356 width = 10 * width + ch;
357 ch = *format++;
358 } else if (ch == '*') {
360 * C99 says: "A negative field width argument is
361 * taken as a `-' flag followed by a positive
362 * field width." (7.19.6.1, 5)
364 if ((width = va_arg(args, int)) < 0) {
365 flags |= PRINT_F_MINUS;
366 width = -width;
368 ch = *format++;
369 state = PRINT_S_DOT;
370 } else
371 state = PRINT_S_DOT;
372 break;
373 case PRINT_S_DOT:
374 if (ch == '.') {
375 state = PRINT_S_PRECISION;
376 ch = *format++;
377 } else
378 state = PRINT_S_MOD;
379 break;
380 case PRINT_S_PRECISION:
381 if (precision == -1)
382 precision = 0;
383 if (ISDIGIT(ch)) {
384 ch = CHARTOINT(ch);
385 if (precision > (INT_MAX - ch) / 10) {
386 overflow = 1;
387 goto out;
389 precision = 10 * precision + ch;
390 ch = *format++;
391 } else if (ch == '*') {
393 * C99 says: "A negative precision argument is
394 * taken as if the precision were omitted."
395 * (7.19.6.1, 5)
397 if ((precision = va_arg(args, int)) < 0)
398 precision = -1;
399 ch = *format++;
400 state = PRINT_S_MOD;
401 } else
402 state = PRINT_S_MOD;
403 break;
404 case PRINT_S_MOD:
405 switch (ch) {
406 case 'h':
407 ch = *format++;
408 if (ch == 'h') { /* It's a char. */
409 ch = *format++;
410 cflags = PRINT_C_CHAR;
411 } else
412 cflags = PRINT_C_SHORT;
413 break;
414 case 'l':
415 ch = *format++;
416 if (ch == 'l') { /* It's a long long. */
417 ch = *format++;
418 cflags = PRINT_C_LLONG;
419 } else
420 cflags = PRINT_C_LONG;
421 break;
422 case 'j':
423 cflags = PRINT_C_INTMAX;
424 ch = *format++;
425 break;
426 case 't':
427 cflags = PRINT_C_PTRDIFF;
428 ch = *format++;
429 break;
430 case 'z':
431 cflags = PRINT_C_SIZE;
432 ch = *format++;
433 break;
435 state = PRINT_S_CONV;
436 break;
437 case PRINT_S_CONV:
438 switch (ch) {
439 case 'd':
440 /* FALLTHROUGH */
441 case 'i':
442 switch (cflags) {
443 case PRINT_C_CHAR:
444 value = (signed char)va_arg(args, int);
445 break;
446 case PRINT_C_SHORT:
447 value = (short int)va_arg(args, int);
448 break;
449 case PRINT_C_LONG:
450 value = va_arg(args, long int);
451 break;
452 case PRINT_C_LLONG:
453 value = va_arg(args, LLONG);
454 break;
455 case PRINT_C_SIZE:
456 value = va_arg(args, SSIZE_T);
457 break;
458 case PRINT_C_INTMAX:
459 value = va_arg(args, INTMAX_T);
460 break;
461 case PRINT_C_PTRDIFF:
462 value = va_arg(args, PTRDIFF_T);
463 break;
464 default:
465 value = va_arg(args, int);
466 break;
468 fmtint(str, &len, size, value, 10, width,
469 precision, flags);
470 break;
471 case 'X':
472 flags |= PRINT_F_UP;
473 /* FALLTHROUGH */
474 case 'x':
475 base = 16;
476 /* FALLTHROUGH */
477 case 'o':
478 if (base == 0)
479 base = 8;
480 /* FALLTHROUGH */
481 case 'u':
482 if (base == 0)
483 base = 10;
484 flags |= PRINT_F_UNSIGNED;
485 switch (cflags) {
486 case PRINT_C_CHAR:
487 value = (unsigned char)va_arg(args,
488 unsigned int);
489 break;
490 case PRINT_C_SHORT:
491 value = (unsigned short int)va_arg(args,
492 unsigned int);
493 break;
494 case PRINT_C_LONG:
495 value = va_arg(args, unsigned long int);
496 break;
497 case PRINT_C_LLONG:
498 value = va_arg(args, ULLONG);
499 break;
500 case PRINT_C_SIZE:
501 value = va_arg(args, size_t);
502 break;
503 case PRINT_C_INTMAX:
504 value = va_arg(args, UINTMAX_T);
505 break;
506 case PRINT_C_PTRDIFF:
507 value = va_arg(args, UPTRDIFF_T);
508 break;
509 default:
510 value = va_arg(args, unsigned int);
511 break;
513 fmtint(str, &len, size, value, base, width,
514 precision, flags);
515 break;
516 case 'c':
517 cvalue = va_arg(args, int);
518 OUTCHAR(str, len, size, cvalue);
519 break;
520 case 's':
521 strvalue = va_arg(args, char *);
522 fmtstr(str, &len, size, strvalue, width,
523 precision, flags);
524 break;
525 case 'p':
527 * C99 says: "The value of the pointer is
528 * converted to a sequence of printing
529 * characters, in an implementation-defined
530 * manner." (C99: 7.19.6.1, 8)
532 if ((strvalue = va_arg(args, void *)) == NULL)
534 * We use the glibc format. BSD prints
535 * "0x0", SysV "0".
537 fmtstr(str, &len, size, "(nil)", width,
538 -1, flags);
539 else {
541 * We use the BSD/glibc format. SysV
542 * omits the "0x" prefix (which we emit
543 * using the PRINT_F_NUM flag).
545 flags |= PRINT_F_NUM;
546 flags |= PRINT_F_UNSIGNED;
547 fmtint(str, &len, size,
548 (UINTPTR_T)strvalue, 16, width,
549 precision, flags);
551 break;
552 case 'n':
553 switch (cflags) {
554 case PRINT_C_CHAR:
555 charptr = va_arg(args, signed char *);
556 *charptr = len;
557 break;
558 case PRINT_C_SHORT:
559 shortptr = va_arg(args, short int *);
560 *shortptr = len;
561 break;
562 case PRINT_C_LONG:
563 longptr = va_arg(args, long int *);
564 *longptr = len;
565 break;
566 case PRINT_C_LLONG:
567 llongptr = va_arg(args, LLONG *);
568 *llongptr = len;
569 break;
570 case PRINT_C_SIZE:
572 * C99 says that with the "z" length
573 * modifier, "a following `n' conversion
574 * specifier applies to a pointer to a
575 * signed integer type corresponding to
576 * size_t argument." (7.19.6.1, 7)
578 sizeptr = va_arg(args, SSIZE_T *);
579 *sizeptr = len;
580 break;
581 case PRINT_C_INTMAX:
582 intmaxptr = va_arg(args, INTMAX_T *);
583 *intmaxptr = len;
584 break;
585 case PRINT_C_PTRDIFF:
586 ptrdiffptr = va_arg(args, PTRDIFF_T *);
587 *ptrdiffptr = len;
588 break;
589 default:
590 intptr = va_arg(args, int *);
591 *intptr = len;
592 break;
594 break;
595 case '%': /* Print a "%" character verbatim. */
596 OUTCHAR(str, len, size, ch);
597 break;
598 default: /* Skip other characters. */
599 break;
601 ch = *format++;
602 state = PRINT_S_DEFAULT;
603 base = cflags = flags = width = 0;
604 precision = -1;
605 break;
607 out:
608 if (len < size)
609 str[len] = '\0';
610 else if (size > 0)
611 str[size - 1] = '\0';
613 if (overflow || len >= INT_MAX) {
614 return -1;
616 return (int)len;
619 static void
620 fmtstr(char *str, size_t *len, size_t size, const char *value, int width,
621 int precision, int flags)
623 int padlen, strln; /* Amount to pad. */
624 int noprecision = (precision == -1);
626 if (value == NULL) /* We're forgiving. */
627 value = "(null)";
629 /* If a precision was specified, don't read the string past it. */
630 for (strln = 0; value[strln] != '\0' &&
631 (noprecision || strln < precision); strln++)
632 continue;
634 if ((padlen = width - strln) < 0)
635 padlen = 0;
636 if (flags & PRINT_F_MINUS) /* Left justify. */
637 padlen = -padlen;
639 while (padlen > 0) { /* Leading spaces. */
640 OUTCHAR(str, *len, size, ' ');
641 padlen--;
643 while (*value != '\0' && (noprecision || precision-- > 0)) {
644 OUTCHAR(str, *len, size, *value);
645 value++;
647 while (padlen < 0) { /* Trailing spaces. */
648 OUTCHAR(str, *len, size, ' ');
649 padlen++;
653 static void
654 fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width,
655 int precision, int flags)
657 UINTMAX_T uvalue;
658 char iconvert[MAX_CONVERT_LENGTH];
659 char sign = 0;
660 char hexprefix = 0;
661 int spadlen = 0; /* Amount to space pad. */
662 int zpadlen = 0; /* Amount to zero pad. */
663 int pos;
664 int separators = (flags & PRINT_F_QUOTE);
665 int noprecision = (precision == -1);
667 if (flags & PRINT_F_UNSIGNED)
668 uvalue = value;
669 else {
670 uvalue = (value >= 0) ? value : -value;
671 if (value < 0)
672 sign = '-';
673 else if (flags & PRINT_F_PLUS) /* Do a sign. */
674 sign = '+';
675 else if (flags & PRINT_F_SPACE)
676 sign = ' ';
679 pos = convert(uvalue, iconvert, sizeof(iconvert), base,
680 flags & PRINT_F_UP);
682 if (flags & PRINT_F_NUM && uvalue != 0) {
684 * C99 says: "The result is converted to an `alternative form'.
685 * For `o' conversion, it increases the precision, if and only
686 * if necessary, to force the first digit of the result to be a
687 * zero (if the value and precision are both 0, a single 0 is
688 * printed). For `x' (or `X') conversion, a nonzero result has
689 * `0x' (or `0X') prefixed to it." (7.19.6.1, 6)
691 switch (base) {
692 case 8:
693 if (precision <= pos)
694 precision = pos + 1;
695 break;
696 case 16:
697 hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x';
698 break;
702 if (separators) /* Get the number of group separators we'll print. */
703 separators = getnumsep(pos);
705 zpadlen = precision - pos - separators;
706 spadlen = width /* Minimum field width. */
707 - separators /* Number of separators. */
708 - MAX(precision, pos) /* Number of integer digits. */
709 - ((sign != 0) ? 1 : 0) /* Will we print a sign? */
710 - ((hexprefix != 0) ? 2 : 0); /* Will we print a prefix? */
712 if (zpadlen < 0)
713 zpadlen = 0;
714 if (spadlen < 0)
715 spadlen = 0;
718 * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
719 * ignored. For `d', `i', `o', `u', `x', and `X' conversions, if a
720 * precision is specified, the `0' flag is ignored." (7.19.6.1, 6)
722 if (flags & PRINT_F_MINUS) /* Left justify. */
723 spadlen = -spadlen;
724 else if (flags & PRINT_F_ZERO && noprecision) {
725 zpadlen += spadlen;
726 spadlen = 0;
728 while (spadlen > 0) { /* Leading spaces. */
729 OUTCHAR(str, *len, size, ' ');
730 spadlen--;
732 if (sign != 0) /* Sign. */
733 OUTCHAR(str, *len, size, sign);
734 if (hexprefix != 0) { /* A "0x" or "0X" prefix. */
735 OUTCHAR(str, *len, size, '0');
736 OUTCHAR(str, *len, size, hexprefix);
738 while (zpadlen > 0) { /* Leading zeros. */
739 OUTCHAR(str, *len, size, '0');
740 zpadlen--;
742 while (pos > 0) { /* The actual digits. */
743 pos--;
744 OUTCHAR(str, *len, size, iconvert[pos]);
745 if (separators > 0 && pos > 0 && pos % 3 == 0)
746 printsep(str, len, size);
748 while (spadlen < 0) { /* Trailing spaces. */
749 OUTCHAR(str, *len, size, ' ');
750 spadlen++;
755 static void
756 printsep(char *str, size_t *len, size_t size)
758 OUTCHAR(str, *len, size, ',');
761 static int
762 getnumsep(int digits)
764 int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3;
765 return separators;
768 static int
769 convert(UINTMAX_T value, char *buf, size_t size, int base, int caps)
771 const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef";
772 size_t pos = 0;
774 /* We return an unterminated buffer with the digits in reverse order. */
775 do {
776 buf[pos++] = digits[value % base];
777 value /= base;
778 } while (value != 0 && pos < size);
780 return (int)pos;
783 int vsprintf(char *buf, const char *fmt, va_list args)
785 return vsnprintf(buf, INT_MAX, fmt, args);
788 int sprintf(char *buffer, const char *fmt, ...)
790 va_list args;
791 int i;
793 va_start(args, fmt);
794 i = vsprintf(buffer, fmt,args);
795 va_end(args);
796 return i;