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 */