summaryrefslogtreecommitdiff
path: root/firmware/common/vuprintf.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/common/vuprintf.c')
-rw-r--r--firmware/common/vuprintf.c974
1 files changed, 974 insertions, 0 deletions
diff --git a/firmware/common/vuprintf.c b/firmware/common/vuprintf.c
new file mode 100644
index 0000000000..c32b690cf8
--- /dev/null
+++ b/firmware/common/vuprintf.c
@@ -0,0 +1,974 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Gary Czvitkovicz
11 * Copyright (C) 2017 by Michael A. Sevakis
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#include <sys/types.h>
23#include <limits.h>
24#include <string.h>
25#include "system.h"
26#include "vuprintf.h"
27
28#if 0
29/* turn everything on */
30#define FMT_LENMOD (0xffffffff)
31#define FMT_RADIX (0xffffffff)
32#endif
33
34/* these are the defaults if no other preference is given */
35#ifndef FMT_LENMOD
36#define FMT_LENMOD (FMT_LENMOD_l | \
37 FMT_LENMOD_z)
38#endif /* FMT_LENMOD */
39
40#ifndef FMT_RADIX
41#define FMT_RADIX (FMT_RADIX_c | \
42 FMT_RADIX_d | \
43 FMT_RADIX_p | \
44 FMT_RADIX_s | \
45 FMT_RADIX_u | \
46 FMT_RADIX_x)
47#endif /* FMT_RADIX */
48
49/** Length modifier and radix flags **/
50
51/* compulsory length modifiers: NONE
52 * however a compatible 'l' or 'll' must be defined if another requires it */
53#define FMT_LENMOD_h 0x001 /* signed/unsigned short (%h<radix>) */
54#define FMT_LENMOD_hh 0x002 /* signed/unsigned char (%hh<radix>) */
55#define FMT_LENMOD_j 0x004 /* intmax_t/uintmax_t (%j<radix>) */
56#define FMT_LENMOD_l 0x008 /* signed/unsigned long (%l<radix>) */
57#define FMT_LENMOD_ll 0x010 /* signed/unsigned long long (%ll<radix>) */
58#define FMT_LENMOD_t 0x020 /* signed/unsigned ptrdiff_t (%t<radix>) */
59#define FMT_LENMOD_z 0x040 /* size_t/ssize_t (%z<radix>) */
60#define FMT_LENMOD_L 0x080 /* long double (instead of double) */
61
62/* compulsory radixes: c, d, i, u, s */
63#define FMT_RADIX_c 0x001 /* single character (%c) */
64#define FMT_RADIX_d 0x002 /* signed integer type, decimal (%d %i) */
65#define FMT_RADIX_n 0x004 /* bytes output so far (%n) */
66#define FMT_RADIX_o 0x008 /* unsigned integer type, octal (%o) */
67#define FMT_RADIX_p 0x010 /* pointer (%p %P) */
68#define FMT_RADIX_s 0x020 /* string (%s) */
69#define FMT_RADIX_u 0x040 /* unsigned integer type, decimal (%u) */
70#define FMT_RADIX_x 0x080 /* unsigned integer type, hex (%x %X) */
71#define FMT_RADIX_a 0x100 /* hex floating point "[-]0xh.hhhhp±d" */
72#define FMT_RADIX_e 0x200 /* floating point with exponent "[-]d.ddde±dd" */
73#define FMT_RADIX_f 0x400 /* floating point "[-]ddd.ddd" */
74#define FMT_RADIX_g 0x800 /* floating point exponent or decimal depending
75 upon value and precision */
76
77/* avoid defining redundant functions if two or more types can use the same
78 * something not getting a macro means it gets assigned its own value and
79 * formatter */
80
81/* l */
82#if LONG_MIN == INT_MIN && LONG_MAX == INT_MAX
83#define val_ld val_d
84#define format_ld format_d
85#define branch_fmt_ld branch_fmt_d
86#elif !(FMT_LENMOD & FMT_LENMOD_l) /* unique */
87#define val_ld
88#endif /* LONG_ */
89
90#if ULONG_MAX == UINT_MAX
91#define val_lu val_u
92#define format_lu format_u
93#define branch_fmt_lu branch_fmt_u
94#elif !(FMT_LENMOD & FMT_LENMOD_l) /* unique */
95#define val_lu
96#endif /* ULONG_ */
97
98/* ll */
99#if LLONG_MIN == INT_MIN && LLONG_MAX == INT_MAX
100#define val_lld val_d
101#define format_lld format_d
102#define branch_fmt_lld branch_fmt_d
103#elif LLONG_MIN == LONG_MIN && LLONG_MAX == LONG_MAX
104#define val_lld val_ld
105#define format_lld format_ld
106#define branch_fmt_lld branch_fmt_ld
107#elif !(FMT_LENMOD & FMT_LENMOD_ll) /* unique */
108#define val_lld
109#endif /* LLONG_ */
110
111#if ULLONG_MAX == UINT_MAX
112#define val_llu val_u
113#define format_llu format_u
114#define branch_fmt_llu branch_fmt_u
115#elif ULLONG_MAX == ULONG_MAX
116#define val_llu val_lu
117#define format_llu format_lu
118#define branch_fmt_llu branch_fmt_lu
119#elif !(FMT_LENMOD & FMT_LENMOD_ll) /* unique */
120#define val_llu
121#endif /* ULLONG_ */
122
123/* char/short parameter type promotions */
124#define SCHAR_INT_ARG int
125#define UCHAR_INT_ARG int
126#define SSHRT_INT_ARG int
127#if USHRT_MAX == UINT_MAX
128#define USHRT_INT_ARG unsigned int
129#else
130#define USHRT_INT_ARG int
131#endif
132
133/* some macros to have conditional work inside macros */
134#if (FMT_LENMOD & FMT_LENMOD_l)
135#define IF_FMT_LENMOD_l(...) __VA_ARGS__
136#else
137#define IF_FMT_LENMOD_l(...)
138#endif
139
140#if (FMT_LENMOD & FMT_LENMOD_ll)
141#define IF_FMT_LENMOD_ll(...) __VA_ARGS__
142#else
143#define IF_FMT_LENMOD_ll(...)
144#endif
145
146#if (FMT_RADIX & FMT_RADIX_o)
147#define IF_FMT_RADIX_o(...) __VA_ARGS__
148#else
149#define IF_FMT_RADIX_o(...)
150#endif
151
152#if (FMT_RADIX & FMT_RADIX_x)
153#define IF_FMT_RADIX_x(...) __VA_ARGS__
154#else
155#define IF_FMT_RADIX_x(...)
156#endif
157
158/* synthesize multicharacter constant */
159#define LENMOD2(cv, ch) \
160 (((cv) << CHAR_BIT) | (ch))
161
162#define LENMOD_NONE 0
163
164#if (FMT_LENMOD & FMT_LENMOD_h)
165#define LENMOD_h 'h'
166#endif
167#if (FMT_LENMOD & FMT_LENMOD_hh)
168#define LENMOD_hh LENMOD2('h', 'h') /* 'hh' */
169#endif
170#if (FMT_LENMOD & FMT_LENMOD_j)
171#define LENMOD_j 'j'
172#endif
173#if (FMT_LENMOD & FMT_LENMOD_l)
174#define LENMOD_l 'l'
175#endif
176#if (FMT_LENMOD & FMT_LENMOD_ll)
177#undef FMT_MAX_L
178#define LENMOD_ll LENMOD2('l', 'l') /* 'll' */
179#endif
180#if (FMT_LENMOD & FMT_LENMOD_t)
181#define LENMOD_t 't'
182#endif
183#if (FMT_LENMOD & FMT_LENMOD_z)
184#define LENMOD_z 'z'
185#endif
186
187/* select type-compatible length modifier
188 * (a bit hacky; it should be range-based) */
189#define LENMOD_INTCOMPAT_SEL(type, signd) \
190 ({ int __lenmod; \
191 size_t __size = sizeof (type); \
192 if (__size == ((signd) ? sizeof (int) : \
193 sizeof (unsigned int))) { \
194 __lenmod = LENMOD_NONE; \
195 } \
196 else if (__size == ((signd) ? sizeof (long) : \
197 sizeof (unsigned long))) { \
198 IF_FMT_LENMOD_l(__lenmod = LENMOD_l;) \
199 } \
200 else if (__size == ((signd) ? sizeof (long long) : \
201 sizeof (unsigned long long))) { \
202 IF_FMT_LENMOD_ll(__lenmod = LENMOD_ll;) \
203 } \
204 __lenmod; })
205
206/* call formatting function for the compatible integer type */
207#define LENMOD_INTCOMPAT_CALL(inteqv, val, fmt_buf, x, signd) \
208 ({ const char *__buf; \
209 switch (inteqv) { \
210 case LENMOD_NONE: \
211 __buf = (signd) ? \
212 format_d((val), (fmt_buf), (x)) : \
213 format_u((val), (fmt_buf), (x)); \
214 break; \
215 IF_FMT_LENMOD_l( \
216 case LENMOD_l: \
217 __buf = (signd) ? \
218 format_ld((val), (fmt_buf), (x)) : \
219 format_lu((val), (fmt_buf), (x)); \
220 break; \
221 ) \
222 IF_FMT_LENMOD_ll( \
223 case LENMOD_ll: \
224 __buf = (signd) ? \
225 format_lld((val), (fmt_buf), (x)) : \
226 format_llu((val), (fmt_buf), (x)); \
227 break; \
228 ) \
229 } \
230 __buf; \
231 })
232
233/* execute formatting branch for the compatible integer type */
234#define LENMOD_INTCOMPAT_BRANCH(inteqv, val, signd) \
235 ({ switch (inteqv) { \
236 case LENMOD_NONE: \
237 if (signd) { \
238 val_d = (val); \
239 goto branch_fmt_d; \
240 } \
241 else { \
242 val_u = (val); \
243 goto branch_fmt_u; \
244 } \
245 IF_FMT_LENMOD_l( \
246 case LENMOD_l: \
247 if (signd) { \
248 val_ld = (val); \
249 goto branch_fmt_ld; \
250 } \
251 else { \
252 val_lu = (val); \
253 goto branch_fmt_lu; \
254 } \
255 ) \
256 IF_FMT_LENMOD_ll( \
257 case LENMOD_ll: \
258 if (signd) { \
259 val_lld = (val); \
260 goto branch_fmt_lld; \
261 } \
262 else { \
263 val_llu = (val); \
264 goto branch_fmt_llu; \
265 } \
266 ) \
267 } \
268 })
269
270#define CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, type) \
271 do { \
272 if (val) { \
273 unsigned type v; \
274 \
275 if (val < 0) { \
276 v = (typeof (v))-(val + 1) + 1; \
277 signchar = '-'; \
278 } \
279 else { \
280 v = val; \
281 } \
282 \
283 do { \
284 *--p = (v % 10) + '0'; \
285 v /= 10; \
286 } while (v); \
287 } \
288 \
289 if (signchar) { \
290 p[-1] = signchar; \
291 fmt_buf->length = 1; \
292 break; \
293 } \
294 \
295 fmt_buf->length = 0; \
296 } while (0)
297
298#define CONVERT_RADIX_8(val, fmt_buf, p) \
299 do { \
300 if (val) { \
301 typeof (val) v = val; \
302 \
303 do { \
304 *--p = (v % 010) + '0'; \
305 v /= 010; \
306 } while (v); \
307 } \
308 \
309 if (fmt_buf->length) { \
310 *--p = '0'; \
311 fmt_buf->length = 0; \
312 } \
313 } while (0)
314
315#define CONVERT_RADIX_10(val, fmt_buf, p) \
316 do { \
317 if (val) { \
318 typeof (val) v = val; \
319 \
320 do { \
321 *--p = (v % 10) + '0'; \
322 v /= 10; \
323 } while (v); \
324 } \
325 \
326 fmt_buf->length = 0; \
327 } while (0)
328
329#define CONVERT_RADIX_16(val, fmt_buf, p, x) \
330 do { \
331 if (val) { \
332 const int h = x - 'X' - 0xA \
333 + 'A' - '0'; \
334 typeof (val) v = val; \
335 \
336 do { \
337 unsigned int d = v % 0x10; \
338 if (d >= 0xA) { \
339 d += h; \
340 } \
341 *--p = d + '0'; \
342 v /= 0x10; \
343 } while (v); \
344 \
345 if (fmt_buf->length) { \
346 p[-1] = x; \
347 p[-2] = '0'; \
348 fmt_buf->length = 2; \
349 break; \
350 } \
351 } \
352 \
353 fmt_buf->length = 0; \
354 } while (0)
355
356#define CONVERT_RADIX_NOSIGN(val, fmt_buf, p, x) \
357 switch (x) \
358 { \
359 IF_FMT_RADIX_o( case 'o': \
360 CONVERT_RADIX_8(val, fmt_buf, p); \
361 break; ) \
362 case 'u': \
363 CONVERT_RADIX_10(val, fmt_buf, p); \
364 break; \
365 IF_FMT_RADIX_x( default: \
366 CONVERT_RADIX_16(val, fmt_buf, p, x); \
367 break; ) \
368 }
369
370struct fmt_buf {
371 const char *fmt_start; /* second character of formatter after '%' */
372 size_t length; /* length of formatted text (non-numeric)
373 or prefix (numeric) */
374 char buf[24]; /* work buffer */
375 char bufend[1]; /* buffer end marker and guard '0' */
376};
377
378/* %d %i */
379static inline const char * format_d(int val,
380 struct fmt_buf *fmt_buf,
381 int signchar)
382{
383 char *p = fmt_buf->bufend;
384 CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, int);
385 return p;
386}
387
388/* %o %u %x %X */
389static inline const char * format_u(unsigned int val,
390 struct fmt_buf *fmt_buf,
391 int radixchar)
392{
393 char *p = fmt_buf->bufend;
394 CONVERT_RADIX_NOSIGN(val, fmt_buf, p, radixchar);
395 return p;
396}
397
398#if (FMT_LENMOD & FMT_LENMOD_l)
399#ifndef format_ld
400/* %ld %li */
401static inline const char * format_ld(long val,
402 struct fmt_buf *fmt_buf,
403 int signchar)
404{
405 char *p = fmt_buf->bufend;
406 CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, long);
407 return p;
408}
409#endif /* format_ld */
410
411#ifndef format_lu
412/* %lo %lu %lx %lX */
413static inline const char * format_lu(unsigned long val,
414 struct fmt_buf *fmt_buf,
415 int radixchar)
416{
417 char *p = fmt_buf->bufend;
418 CONVERT_RADIX_NOSIGN(val, fmt_buf, p, radixchar);
419 return p;
420}
421#endif /* format_lu */
422#endif /* FMT_LENMOD_l */
423
424#if (FMT_LENMOD & FMT_LENMOD_ll)
425#ifndef format_lld
426/* %lld %lli */
427static inline const char * format_lld(long long val,
428 struct fmt_buf *fmt_buf,
429 int signchar)
430{
431 char *p = fmt_buf->bufend;
432 CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, long long);
433 return p;
434}
435#endif /* format_lld */
436
437#ifndef format_llu
438/* %llo %llu %llx %llX */
439static inline const char * format_llu(unsigned long long val,
440 struct fmt_buf *fmt_buf,
441 int radixchar)
442{
443 char *p = fmt_buf->bufend;
444 CONVERT_RADIX_NOSIGN(val, fmt_buf, p, radixchar);
445 return p;
446}
447#endif /* format_llu */
448#endif /* FMT_LENMOD_ll */
449
450/* %c */
451static inline const char * format_c(int c,
452 struct fmt_buf *fmt_buf,
453 int lenmod)
454{
455 if (lenmod != LENMOD_NONE) {
456 return NULL; /* wchar_t support for now */
457 }
458
459 char *p = fmt_buf->bufend;
460 fmt_buf->length = 1;
461 *--p = (unsigned char)c;
462 return p;
463}
464
465/* %s */
466static inline const char * format_s(const void *str,
467 struct fmt_buf *fmt_buf,
468 int precision,
469 int lenmod)
470{
471 if (lenmod != LENMOD_NONE) {
472 return NULL; /* wchar_t support for now */
473 }
474
475 /* string length may be specified by precision instead of \0-
476 terminated; however, don't go past a \0 if one is there */
477 const char *s = str;
478 size_t len = precision >= 0 ? precision : -1;
479
480 const char *nil = memchr(s, '\0', len);
481 if (nil) {
482 len = nil - s;
483 }
484
485 fmt_buf->length = len;
486 return s;
487}
488
489#if (FMT_RADIX & FMT_RADIX_n)
490/* %n */
491static inline bool format_n(void *np,
492 int count,
493 int lenmod)
494{
495 if (lenmod != LENMOD_NONE) {
496 return false; /* int only for now */
497 }
498
499 *(int *)np = count;
500 return true;
501}
502#endif /* FMT_RADIX_n */
503
504#if (FMT_RADIX & FMT_RADIX_p)
505/* %p %P */
506static inline const char * format_p(const void *p,
507 struct fmt_buf *fmt_buf,
508 int radixchar,
509 bool *numericp)
510{
511 if (p) {
512 /* format as %#x or %#X */
513 *numericp = true;
514 radixchar -= 'P' - 'X';
515 fmt_buf->length = 2;
516 return LENMOD_INTCOMPAT_CALL(LENMOD_INTCOMPAT_SEL(uintptr_t, false),
517 (uintptr_t)p, fmt_buf, radixchar, false);
518 }
519 else {
520 /* format as %s */
521 fmt_buf->length = 5;
522 return "(nil)";
523 }
524}
525#endif /* FMT_RADIX_p */
526
527/* parse fixed width or precision field */
528static const char * parse_number_spec(const char *fmt,
529 int ch,
530 int *out)
531{
532 int i = ch - '0';
533
534 while (1) {
535 ch = *fmt - '0';
536
537 if (ch < 0 || ch > 9 || i > INT_MAX / 10 ||
538 (i == INT_MAX / 10 && ch > INT_MAX % 10)) {
539 break;
540 }
541
542 i = i * 10 + ch;
543 fmt++;
544 }
545
546 *out = i;
547 return fmt;
548}
549
550int vuprintf(vuprintf_push_cb push, /* call 'push()' for each output letter */
551 void *userp,
552 const char *fmt,
553 va_list ap)
554{
555 #define PUSHCHAR(ch) \
556 do { \
557 int __ch = (ch); \
558 int __rc = push(userp, __ch); \
559 count += __rc >= 0; \
560 if (__rc <= 0) { \
561 goto done; \
562 } \
563 } while (0)
564
565 int count = 0;
566 int ch;
567
568 /* macrofied identifiers share a variable with another */
569 unsigned int val_d;
570 unsigned int val_u;
571 #ifndef val_ld
572 unsigned long val_ld;
573 #endif
574 #ifndef val_lu
575 unsigned long val_lu;
576 #endif
577 #ifndef val_lld
578 unsigned long long val_lld;
579 #endif
580 #ifndef val_llu
581 unsigned long long val_llu;
582 #endif
583
584 struct fmt_buf fmt_buf;
585 fmt_buf.bufend[0] = '0';
586
587 while (1) {
588 while (1) {
589 if ((ch = *fmt++) == '\0') {
590 goto done;
591 }
592
593 if (ch == '%' && (ch = *fmt++) != '%') {
594 break;
595 }
596
597 PUSHCHAR(ch);
598 }
599
600 /* set to defaults */
601 fmt_buf.fmt_start = fmt;
602
603 int signchar = 0;
604 unsigned int width = 0;
605 int lenmod = LENMOD_NONE;
606 size_t length = 0;
607 size_t pfxlen = 0;
608 bool numeric = false;
609 int alignchar = '0' + 1;
610 int precision = -1;
611 const char *buf = NULL;
612
613 /*** flags ***/
614 while (1) {
615 switch (ch)
616 {
617 case ' ': /* <space> before non-negative value (signed conversion) */
618 case '+': /* '+' before non-negative value (signed conversion) */
619 /* '+' overrides ' ' */
620 if (ch > signchar) {
621 signchar = ch;
622 }
623 break;
624 case '-': /* left-justify in field */
625 case '0': /* zero-pad to fill field */
626 /* '-' overrides '0' */
627 if (ch < alignchar) {
628 alignchar = ch;
629 }
630 break;
631 case '#': /* number prefix (nonzero %o:'0' %x/%X:'0x') */
632 /* indicate; formatter updates with actual length */
633 pfxlen = 1;
634 break;
635 #if 0
636 case '\'': /* digit grouping (non-monetary) */
637 break;
638 #endif
639 default:
640 goto flags_done;
641 }
642
643 ch = *fmt++;
644 }
645 flags_done:
646
647 /*** width ***/
648 if (ch == '*') {
649 /* variable width */
650 int w = va_arg(ap, int);
651 if (w < 0) {
652 /* negative width is width with implied '-' */
653 width = (unsigned int)-(w + 1) + 1;
654 alignchar = '-';
655 }
656 else {
657 width = w;
658 }
659
660 ch = *fmt++;
661 }
662 else if (ch >= '1' && ch <= '9') {
663 /* fixed width */
664 fmt = parse_number_spec(fmt, ch, &width);
665 ch = *fmt++;
666 }
667
668 /*** precision ***/
669 if (ch == '.') {
670 ch = *fmt++;
671
672 if (ch == '*') {
673 /* variable precision; negative precision is ignored */
674 precision = va_arg (ap, int);
675 ch = *fmt++;
676 }
677 else if (ch >= '0' && ch <= '9') {
678 /* fixed precision */
679 fmt = parse_number_spec(fmt, ch, &precision);
680 ch = *fmt++;
681 }
682 }
683
684 /*** length modifier ***/
685 #if FMT_LENMOD
686 switch (ch)
687 {
688 #if (FMT_LENMOD & (FMT_LENMOD_h | FMT_LENMOD_hh))
689 case 'h':
690 #endif
691 #if (FMT_LENMOD & FMT_LENMOD_j)
692 case 'j':
693 #endif
694 #if (FMT_LENMOD & (FMT_LENMOD_l | FMT_LENMOD_ll))
695 case 'l':
696 #endif
697 #if (FMT_LENMOD & FMT_LENMOD_t)
698 case 't':
699 #endif
700 #if (FMT_LENMOD & FMT_LENMOD_z)
701 case 'z':
702 #endif
703 lenmod = ch;
704 ch = *fmt++;
705 #if (FMT_LENMOD & (FMT_LENMOD_hh | FMT_LENMOD_ll))
706 /* doesn't matter if jj, tt or zz happen; they will be rejected
707 by the radix handler */
708 if (ch == lenmod) {
709 lenmod = LENMOD2(lenmod, ch);
710 ch = *fmt++;
711 }
712 #endif
713 }
714 #endif /* FMT_LENMOD */
715
716 /*** radix ***/
717 switch (ch)
718 {
719 /** non-numeric **/
720 case 'c':
721 buf = format_c(va_arg(ap, int), &fmt_buf, lenmod);
722 break;
723 #if (FMT_RADIX & FMT_RADIX_n)
724 case 'n':
725 if (format_n(va_arg(ap, void *), count, lenmod)) {
726 continue; /* no output */
727 }
728 break;
729 #endif
730 case 's':
731 buf = format_s(va_arg(ap, const void *), &fmt_buf,
732 precision, lenmod);
733 break;
734
735 /** non-integer **/
736 #if (FMT_RADIX & FMT_RADIX_p)
737 case 'p':
738 case 'P':
739 buf = format_p(va_arg(ap, void *), &fmt_buf, ch,
740 &numeric);
741 break;
742 #endif
743
744 /** signed integer **/
745 case 'd':
746 case 'i':
747 fmt_buf.length = pfxlen;
748
749 switch (lenmod)
750 {
751 case LENMOD_NONE:
752 val_d = va_arg(ap, signed int);
753 goto branch_fmt_d;
754 #if (FMT_LENMOD & FMT_LENMOD_h)
755 case LENMOD_h:
756 val_d = (signed short)va_arg(ap, SSHRT_INT_ARG);
757 goto branch_fmt_d;
758 #endif
759 #if (FMT_LENMOD & FMT_LENMOD_hh)
760 case LENMOD_hh:
761 val_d = (signed char)va_arg(ap, SCHAR_INT_ARG);
762 goto branch_fmt_d;
763 #endif
764 #if (FMT_LENMOD & FMT_LENMOD_j)
765 case LENMOD_j:
766 LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(intmax_t, true),
767 va_arg(ap, intmax_t), true);
768 #endif
769 #if (FMT_LENMOD & FMT_LENMOD_l)
770 case LENMOD_l:
771 val_ld = va_arg(ap, signed long);
772 goto branch_fmt_ld;
773 #endif
774 #if (FMT_LENMOD & FMT_LENMOD_ll)
775 case LENMOD_ll:
776 val_lld = va_arg(ap, signed long long);
777 goto branch_fmt_lld;
778 #endif
779 #if (FMT_LENMOD & FMT_LENMOD_t)
780 case LENMOD_t:
781 LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(ptrdiff_t, true),
782 va_arg(ap, ptrdiff_t), true);
783 #endif
784 #if (FMT_LENMOD & FMT_LENMOD_z)
785 case LENMOD_z:
786 LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(ssize_t, true),
787 va_arg(ap, ssize_t), true);
788 #endif
789 }
790
791 /* macrofied labels share a formatter with another */
792 if (0) {
793 branch_fmt_d:
794 buf = format_d(val_d, &fmt_buf, signchar);
795 } else if (0) {
796 #ifndef val_ld
797 branch_fmt_ld:
798 buf = format_ld(val_ld, &fmt_buf, signchar);
799 #endif
800 } else if (0) {
801 #ifndef val_lld
802 branch_fmt_lld:
803 buf = format_lld(val_lld, &fmt_buf, signchar);
804 #endif
805 }
806
807 numeric = true;
808 break;
809
810 /** unsigned integer **/
811 #if (FMT_RADIX & FMT_RADIX_o)
812 case 'o':
813 #endif
814 case 'u':
815 #if (FMT_RADIX & FMT_RADIX_x)
816 case 'x':
817 case 'X':
818 #endif
819 fmt_buf.length = pfxlen;
820
821 switch (lenmod)
822 {
823 case LENMOD_NONE:
824 val_u = va_arg(ap, unsigned int);
825 goto branch_fmt_u;
826 #if (FMT_LENMOD & FMT_LENMOD_h)
827 case LENMOD_h:
828 val_u = (unsigned short)va_arg(ap, USHRT_INT_ARG);
829 goto branch_fmt_u;
830 #endif
831 #if (FMT_LENMOD & FMT_LENMOD_hh)
832 case LENMOD_hh:
833 val_u = (unsigned char)va_arg(ap, UCHAR_INT_ARG);
834 goto branch_fmt_u;
835 #endif
836 #if (FMT_LENMOD & FMT_LENMOD_j)
837 case LENMOD_j:
838 LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(uintmax_t, false),
839 va_arg(ap, uintmax_t), false);
840 #endif
841 #if (FMT_LENMOD & FMT_LENMOD_l)
842 case LENMOD_l:
843 val_lu = va_arg(ap, unsigned long);
844 goto branch_fmt_lu;
845 #endif
846 #if (FMT_LENMOD & FMT_LENMOD_ll)
847 case LENMOD_ll:
848 val_llu = va_arg(ap, unsigned long long);
849 goto branch_fmt_llu;
850 #endif
851 #if (FMT_LENMOD & (FMT_LENMOD_t | FMT_LENMOD_z))
852 /* format "uptrdiff_t" as size_t (unless it becomes standard) */
853 #if (FMT_LENMOD & FMT_LENMOD_t)
854 case LENMOD_t:
855 #endif
856 #if (FMT_LENMOD & FMT_LENMOD_z)
857 case LENMOD_z:
858 #endif
859 LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(size_t, false),
860 va_arg(ap, size_t), false);
861 #endif
862 }
863
864 /* macrofied labels share a formatter with another */
865 if (0) {
866 branch_fmt_u:
867 buf = format_u(val_u, &fmt_buf, ch);
868 } else if (0) {
869 #ifndef val_lu
870 branch_fmt_lu:
871 buf = format_lu(val_lu, &fmt_buf, ch);
872 #endif
873 } else if (0) {
874 #ifndef val_llu
875 branch_fmt_llu:
876 buf = format_llu(val_llu, &fmt_buf, ch);
877 #endif
878 }
879
880 numeric = true;
881 break;
882 }
883
884 if (buf) {
885 /** padding **/
886 if (numeric) {
887 /* numeric formats into fmt_buf.buf */
888 pfxlen = fmt_buf.length;
889 length = fmt_buf.bufend - buf;
890
891 size_t size = pfxlen + length;
892
893 if (precision >= 0) {
894 /* explicit precision */
895 precision -= (int)length;
896
897 if (precision > 0) {
898 size += precision;
899 }
900
901 width -= MIN(width, size);
902 }
903 else {
904 /* default precision */
905 if (!length) {
906 length = 1;
907 size++;
908 }
909
910 width -= MIN(width, size);
911
912 if (alignchar == '0') {
913 /* width zero-fill */
914 precision = width;
915 width = 0;
916 }
917 }
918 }
919 else {
920 /* non-numeric: supress prefix and precision; keep length and
921 width */
922 pfxlen = 0;
923 precision = 0;
924 length = fmt_buf.length;
925 width -= MIN(width, length);
926 }
927 }
928 else {
929 /* format not accepted; print it literally */
930 buf = fmt_buf.fmt_start - 2;
931 length = fmt - buf;
932 width = 0;
933 pfxlen = 0;
934 precision = 0;
935 }
936
937 /** push all the stuff **/
938
939 if (alignchar != '-') {
940 /* left padding */
941 while (width > 0) {
942 PUSHCHAR(' ');
943 width--;
944 }
945 }
946
947 /* prefix */
948 while (pfxlen > 0) {
949 PUSHCHAR(buf[-pfxlen]);
950 pfxlen--;
951 }
952
953 /* 0-padding */
954 while (precision > 0) {
955 PUSHCHAR('0');
956 precision--;
957 }
958
959 /* field */
960 while (length > 0) {
961 PUSHCHAR(*buf++);
962 length--;
963 }
964
965 /* right padding */
966 while (width > 0) {
967 PUSHCHAR(' ');
968 width--;
969 }
970 }
971
972done:
973 return count;
974}