4 * vsnprintf(), from which the rest of the printf()
16 FL_ZERO = 0x01, /* Zero modifier */
17 FL_MINUS = 0x02, /* Minus modifier */
18 FL_PLUS = 0x04, /* Plus modifier */
19 FL_TICK = 0x08, /* ' modifier */
20 FL_SPACE = 0x10, /* Space modifier */
21 FL_HASH = 0x20, /* # modifier */
22 FL_SIGNED = 0x40, /* Number is signed */
23 FL_UPPER = 0x80 /* Upper case digits */
26 /* These may have to be adjusted on certain implementations */
35 #define MIN_RANK rank_char
36 #define MAX_RANK rank_longlong
38 #define INTMAX_RANK rank_longlong
39 #define SIZE_T_RANK rank_long
40 #define PTRDIFF_T_RANK rank_long
42 #define EMIT(x) ({ if (o<n){*q++ = (x);} o++; })
45 format_int(char *q, size_t n, uintmax_t val, enum flags flags,
46 int base, int width, int prec)
50 static const char lcdigits[] = "0123456789abcdef";
51 static const char ucdigits[] = "0123456789ABCDEF";
55 int ndigits = 0, nchars;
58 /* Select type of digits */
59 digits = (flags & FL_UPPER) ? ucdigits : lcdigits;
61 /* If signed, separate out the minus */
62 if ( flags & FL_SIGNED && (intmax_t)val < 0 ) {
64 val = (uintmax_t)(-(intmax_t)val);
67 /* Count the number of digits needed. This returns zero for 0. */
74 /* Adjust ndigits for size of output */
76 if ( flags & FL_HASH && base == 8 ) {
77 if ( prec < ndigits+1 )
81 if ( ndigits < prec ) {
82 ndigits = prec; /* Mandatory number padding */
83 } else if ( val == 0 ) {
84 ndigits = 1; /* Zero still requires space */
87 /* For ', figure out what the skip should be */
88 if ( flags & FL_TICK ) {
89 tickskip = (base == 16) ? 4 : 3;
91 tickskip = ndigits; /* No tick marks */
94 /* Tick marks aren't digits, but generated by the number converter */
95 ndigits += (ndigits-1)/tickskip;
97 /* Now compute the number of nondigits */
100 if ( minus || (flags & (FL_PLUS|FL_SPACE)) )
101 nchars++; /* Need space for sign */
102 if ( (flags & FL_HASH) && base == 16 ) {
103 nchars += 2; /* Add 0x for hex */
106 /* Emit early space padding */
107 if ( !(flags & (FL_MINUS|FL_ZERO)) && width > nchars ) {
108 while ( width > nchars ) {
117 else if ( flags & FL_PLUS )
119 else if ( flags & FL_SPACE )
122 if ( (flags & FL_HASH) && base == 16 ) {
124 EMIT((flags & FL_UPPER) ? 'X' : 'x');
127 /* Emit zero padding */
128 if ( (flags & (FL_MINUS|FL_ZERO)) == FL_ZERO && width > ndigits ) {
129 while ( width > nchars ) {
135 /* Generate the number. This is done from right to left. */
136 q += ndigits; /* Advance the pointer to end of number */
138 qq = q; oo = o; /* Temporary values */
141 while ( ndigits > 0 ) {
143 qq--; oo--; ndigits--;
144 if ( oo < n ) *qq = '_';
147 qq--; oo--; ndigits--;
148 if ( oo < n ) *qq = digits[val%base];
152 /* Emit late space padding */
153 while ( (flags & FL_MINUS) && width > nchars ) {
162 int vsnprintf(char *buffer, size_t n, const char *format, va_list ap)
164 const char *p = format;
167 size_t o = 0; /* Number of characters output */
169 int rank = rank_int; /* Default rank */
174 enum flags flags = 0;
176 st_normal, /* Ground state */
177 st_flags, /* Special flags */
178 st_width, /* Field width */
179 st_prec, /* Field precision */
180 st_modifiers /* Length or conversion modifiers */
182 const char *sarg; /* %s string argument */
183 char carg; /* %c char argument */
184 int slen; /* String length */
186 while ( (ch = *p++) ) {
191 flags = 0; rank = rank_int; width = 0; prec = -1;
219 p--; /* Process this character again */
225 if ( ch >= '0' && ch <= '9' ) {
226 width = width*10+(ch-'0');
227 } else if ( ch == '*' ) {
228 width = va_arg(ap, int);
233 } else if ( ch == '.' ) {
234 prec = 0; /* Precision given */
237 state = st_modifiers;
238 p--; /* Process this character again */
243 if ( ch >= '0' && ch <= '9' ) {
244 prec = prec*10+(ch-'0');
245 } else if ( ch == '*' ) {
246 prec = va_arg(ap, int);
250 state = st_modifiers;
251 p--; /* Process this character again */
257 /* Length modifiers - nonterminal sequences */
259 rank--; /* Shorter rank */
262 rank++; /* Longer rank */
271 rank = PTRDIFF_T_RANK;
278 /* Output modifiers - terminal sequences */
279 state = st_normal; /* Next state will be normal */
280 if ( rank < MIN_RANK ) /* Canonicalize rank */
282 else if ( rank > MAX_RANK )
286 case 'P': /* Upper case pointer */
289 case 'p': /* Pointer */
291 prec = (CHAR_BIT*sizeof(void *)+3)/4;
293 val = (uintmax_t)(uintptr_t)va_arg(ap, void *);
296 case 'd': /* Signed decimal output */
302 /* Yes, all these casts are needed... */
303 val = (uintmax_t)(intmax_t)(signed char)va_arg(ap, signed int);
306 val = (uintmax_t)(intmax_t)(signed short)va_arg(ap, signed int);
309 val = (uintmax_t)(intmax_t)va_arg(ap, signed int);
312 val = (uintmax_t)(intmax_t)va_arg(ap, signed long);
315 val = (uintmax_t)(intmax_t)va_arg(ap, signed long long);
319 case 'o': /* Octal */
322 case 'u': /* Unsigned decimal */
325 case 'X': /* Upper case hexadecimal */
328 case 'x': /* Hexadecimal */
335 val = (uintmax_t)(unsigned char)va_arg(ap, unsigned int);
338 val = (uintmax_t)(unsigned short)va_arg(ap, unsigned int);
341 val = (uintmax_t)va_arg(ap, unsigned int);
344 val = (uintmax_t)va_arg(ap, unsigned long);
347 val = (uintmax_t)va_arg(ap, unsigned long long);
353 sz = format_int(q, (o<n) ? n-o : 0, val, flags, base, width, prec);
357 case 'c': /* Character */
358 carg = (char)va_arg(ap, int);
362 case 's': /* String */
363 sarg = va_arg(ap, const char *);
364 sarg = sarg ? sarg : "(null)";
373 if ( prec != -1 && slen > prec )
376 if ( width > slen && !(flags & FL_MINUS) ) {
377 char pad = (flags & FL_ZERO) ? '0' : ' ';
378 while ( width > slen ) {
383 for ( i = slen ; i ; i-- ) {
387 if ( width > slen && (flags & FL_MINUS) ) {
388 while ( width > slen ) {
396 case 'n': /* Output the number of characters written */
400 *va_arg(ap, signed char *) = o;
403 *va_arg(ap, signed short *) = o;
406 *va_arg(ap, signed int *) = o;
409 *va_arg(ap, signed long *) = o;
412 *va_arg(ap, signed long long *) = o;
418 default: /* Anything else, including % */
426 /* Null-terminate the string */
428 *q = '\0'; /* No overflow */
430 buffer[n-1] = '\0'; /* Overflow - terminate at end of buffer */