core: do aligned transfers in bcopy32
[syslinux.git] / com32 / lib / vsnprintf.c
blob80280059378e60527c6e57b5290b9fdf971aa680
1 /*
2 * vsnprintf.c
4 * vsnprintf(), from which the rest of the printf()
5 * family is built
6 */
8 #include <stdarg.h>
9 #include <stddef.h>
10 #include <inttypes.h>
11 #include <string.h>
12 #include <limits.h>
13 #include <stdio.h>
15 enum flags {
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 */
27 enum ranks {
28 rank_char = -2,
29 rank_short = -1,
30 rank_int = 0,
31 rank_long = 1,
32 rank_longlong = 2
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++; })
44 static size_t
45 format_int(char *q, size_t n, uintmax_t val, enum flags flags,
46 int base, int width, int prec)
48 char *qq;
49 size_t o = 0, oo;
50 static const char lcdigits[] = "0123456789abcdef";
51 static const char ucdigits[] = "0123456789ABCDEF";
52 const char *digits;
53 uintmax_t tmpval;
54 int minus = 0;
55 int ndigits = 0, nchars;
56 int tickskip, b4tick;
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 ) {
63 minus = 1;
64 val = (uintmax_t)(-(intmax_t)val);
67 /* Count the number of digits needed. This returns zero for 0. */
68 tmpval = val;
69 while ( tmpval ) {
70 tmpval /= base;
71 ndigits++;
74 /* Adjust ndigits for size of output */
76 if ( flags & FL_HASH && base == 8 ) {
77 if ( prec < ndigits+1 )
78 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;
90 } else {
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 */
98 nchars = ndigits;
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 ) {
109 EMIT(' ');
110 width--;
114 /* Emit nondigits */
115 if ( minus )
116 EMIT('-');
117 else if ( flags & FL_PLUS )
118 EMIT('+');
119 else if ( flags & FL_SPACE )
120 EMIT(' ');
122 if ( (flags & FL_HASH) && base == 16 ) {
123 EMIT('0');
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 ) {
130 EMIT('0');
131 width--;
135 /* Generate the number. This is done from right to left. */
136 q += ndigits; /* Advance the pointer to end of number */
137 o += ndigits;
138 qq = q; oo = o; /* Temporary values */
140 b4tick = tickskip;
141 while ( ndigits > 0 ) {
142 if ( !b4tick-- ) {
143 qq--; oo--; ndigits--;
144 if ( oo < n ) *qq = '_';
145 b4tick = tickskip-1;
147 qq--; oo--; ndigits--;
148 if ( oo < n ) *qq = digits[val%base];
149 val /= base;
152 /* Emit late space padding */
153 while ( (flags & FL_MINUS) && width > nchars ) {
154 EMIT(' ');
155 width--;
158 return o;
162 int vsnprintf(char *buffer, size_t n, const char *format, va_list ap)
164 const char *p = format;
165 char ch;
166 char *q = buffer;
167 size_t o = 0; /* Number of characters output */
168 uintmax_t val = 0;
169 int rank = rank_int; /* Default rank */
170 int width = 0;
171 int prec = -1;
172 int base;
173 size_t sz;
174 enum flags flags = 0;
175 enum {
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 */
181 } state = st_normal;
182 const char *sarg; /* %s string argument */
183 char carg; /* %c char argument */
184 int slen; /* String length */
186 while ( (ch = *p++) ) {
187 switch ( state ) {
188 case st_normal:
189 if ( ch == '%' ) {
190 state = st_flags;
191 flags = 0; rank = rank_int; width = 0; prec = -1;
192 } else {
193 EMIT(ch);
195 break;
197 case st_flags:
198 switch ( ch ) {
199 case '-':
200 flags |= FL_MINUS;
201 break;
202 case '+':
203 flags |= FL_PLUS;
204 break;
205 case '\'':
206 flags |= FL_TICK;
207 break;
208 case ' ':
209 flags |= FL_SPACE;
210 break;
211 case '#':
212 flags |= FL_HASH;
213 break;
214 case '0':
215 flags |= FL_ZERO;
216 break;
217 default:
218 state = st_width;
219 p--; /* Process this character again */
220 break;
222 break;
224 case st_width:
225 if ( ch >= '0' && ch <= '9' ) {
226 width = width*10+(ch-'0');
227 } else if ( ch == '*' ) {
228 width = va_arg(ap, int);
229 if ( width < 0 ) {
230 width = -width;
231 flags |= FL_MINUS;
233 } else if ( ch == '.' ) {
234 prec = 0; /* Precision given */
235 state = st_prec;
236 } else {
237 state = st_modifiers;
238 p--; /* Process this character again */
240 break;
242 case st_prec:
243 if ( ch >= '0' && ch <= '9' ) {
244 prec = prec*10+(ch-'0');
245 } else if ( ch == '*' ) {
246 prec = va_arg(ap, int);
247 if ( prec < 0 )
248 prec = -1;
249 } else {
250 state = st_modifiers;
251 p--; /* Process this character again */
253 break;
255 case st_modifiers:
256 switch ( ch ) {
257 /* Length modifiers - nonterminal sequences */
258 case 'h':
259 rank--; /* Shorter rank */
260 break;
261 case 'l':
262 rank++; /* Longer rank */
263 break;
264 case 'j':
265 rank = INTMAX_RANK;
266 break;
267 case 'z':
268 rank = SIZE_T_RANK;
269 break;
270 case 't':
271 rank = PTRDIFF_T_RANK;
272 break;
273 case 'L':
274 case 'q':
275 rank += 2;
276 break;
277 default:
278 /* Output modifiers - terminal sequences */
279 state = st_normal; /* Next state will be normal */
280 if ( rank < MIN_RANK ) /* Canonicalize rank */
281 rank = MIN_RANK;
282 else if ( rank > MAX_RANK )
283 rank = MAX_RANK;
285 switch ( ch ) {
286 case 'P': /* Upper case pointer */
287 flags |= FL_UPPER;
288 /* fall through */
289 case 'p': /* Pointer */
290 base = 16;
291 prec = (CHAR_BIT*sizeof(void *)+3)/4;
292 flags |= FL_HASH;
293 val = (uintmax_t)(uintptr_t)va_arg(ap, void *);
294 goto is_integer;
296 case 'd': /* Signed decimal output */
297 case 'i':
298 base = 10;
299 flags |= FL_SIGNED;
300 switch (rank) {
301 case rank_char:
302 /* Yes, all these casts are needed... */
303 val = (uintmax_t)(intmax_t)(signed char)va_arg(ap, signed int);
304 break;
305 case rank_short:
306 val = (uintmax_t)(intmax_t)(signed short)va_arg(ap, signed int);
307 break;
308 case rank_int:
309 val = (uintmax_t)(intmax_t)va_arg(ap, signed int);
310 break;
311 case rank_long:
312 val = (uintmax_t)(intmax_t)va_arg(ap, signed long);
313 break;
314 case rank_longlong:
315 val = (uintmax_t)(intmax_t)va_arg(ap, signed long long);
316 break;
318 goto is_integer;
319 case 'o': /* Octal */
320 base = 8;
321 goto is_unsigned;
322 case 'u': /* Unsigned decimal */
323 base = 10;
324 goto is_unsigned;
325 case 'X': /* Upper case hexadecimal */
326 flags |= FL_UPPER;
327 /* fall through */
328 case 'x': /* Hexadecimal */
329 base = 16;
330 goto is_unsigned;
332 is_unsigned:
333 switch (rank) {
334 case rank_char:
335 val = (uintmax_t)(unsigned char)va_arg(ap, unsigned int);
336 break;
337 case rank_short:
338 val = (uintmax_t)(unsigned short)va_arg(ap, unsigned int);
339 break;
340 case rank_int:
341 val = (uintmax_t)va_arg(ap, unsigned int);
342 break;
343 case rank_long:
344 val = (uintmax_t)va_arg(ap, unsigned long);
345 break;
346 case rank_longlong:
347 val = (uintmax_t)va_arg(ap, unsigned long long);
348 break;
350 /* fall through */
352 is_integer:
353 sz = format_int(q, (o<n) ? n-o : 0, val, flags, base, width, prec);
354 q += sz; o += sz;
355 break;
357 case 'c': /* Character */
358 carg = (char)va_arg(ap, int);
359 sarg = &carg;
360 slen = 1;
361 goto is_string;
362 case 's': /* String */
363 sarg = va_arg(ap, const char *);
364 sarg = sarg ? sarg : "(null)";
365 slen = strlen(sarg);
366 goto is_string;
368 is_string:
370 char sch;
371 int i;
373 if ( prec != -1 && slen > prec )
374 slen = prec;
376 if ( width > slen && !(flags & FL_MINUS) ) {
377 char pad = (flags & FL_ZERO) ? '0' : ' ';
378 while ( width > slen ) {
379 EMIT(pad);
380 width--;
383 for ( i = slen ; i ; i-- ) {
384 sch = *sarg++;
385 EMIT(sch);
387 if ( width > slen && (flags & FL_MINUS) ) {
388 while ( width > slen ) {
389 EMIT(' ');
390 width--;
394 break;
396 case 'n': /* Output the number of characters written */
398 switch (rank) {
399 case rank_char:
400 *va_arg(ap, signed char *) = o;
401 break;
402 case rank_short:
403 *va_arg(ap, signed short *) = o;
404 break;
405 case rank_int:
406 *va_arg(ap, signed int *) = o;
407 break;
408 case rank_long:
409 *va_arg(ap, signed long *) = o;
410 break;
411 case rank_longlong:
412 *va_arg(ap, signed long long *) = o;
413 break;
416 break;
418 default: /* Anything else, including % */
419 EMIT(ch);
420 break;
426 /* Null-terminate the string */
427 if ( o<n )
428 *q = '\0'; /* No overflow */
429 else if ( n>0 )
430 buffer[n-1] = '\0'; /* Overflow - terminate at end of buffer */
432 return o;