A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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 <stddef.h>
26#include <stdlib.h>
27#include "system.h"
28#include "vuprintf.h"
29#include "ap_int.h"
30
31#ifndef BOOTLOADER
32
33/* Turn everything on if we have enough RAM. */
34#if MEMORYSIZE >= 8
35# define FMT_LENMOD (0xffffffff)
36# define FMT_RADIX (0xffffffff)
37#endif
38
39#endif
40
41/* these are the defaults if no other preference is given */
42#ifndef FMT_LENMOD
43#define FMT_LENMOD (FMT_LENMOD_l | \
44 FMT_LENMOD_z)
45#endif /* FMT_LENMOD */
46
47#ifndef FMT_RADIX
48#define FMT_RADIX (FMT_RADIX_c | \
49 FMT_RADIX_d | \
50 FMT_RADIX_p | \
51 FMT_RADIX_s | \
52 FMT_RADIX_u | \
53 FMT_RADIX_x)
54#endif /* FMT_RADIX */
55
56/** Length modifier and radix flags **/
57
58/* compulsory length modifiers: NONE
59 * however a compatible 'l' or 'll' must be defined if another requires it */
60#define FMT_LENMOD_h 0x001 /* signed/unsigned short (%h<radix>) */
61#define FMT_LENMOD_hh 0x002 /* signed/unsigned char (%hh<radix>) */
62#define FMT_LENMOD_j 0x004 /* intmax_t/uintmax_t (%j<radix>) */
63#define FMT_LENMOD_l 0x008 /* signed/unsigned long (%l<radix>) */
64#define FMT_LENMOD_ll 0x010 /* signed/unsigned long long (%ll<radix>) */
65#define FMT_LENMOD_t 0x020 /* signed/unsigned ptrdiff_t (%t<radix>) */
66#define FMT_LENMOD_z 0x040 /* size_t/ssize_t (%z<radix>) */
67#if 0
68#define FMT_LENMOD_L 0x080 /* long double (instead of double) */
69#else
70#define FMT_LENMOD_L 0x000
71#endif
72
73/* compulsory radixes: c, d, i, u, s */
74#define FMT_RADIX_c 0x001 /* single character (%c) */
75#define FMT_RADIX_d 0x002 /* signed integer type, decimal (%d %i) */
76#define FMT_RADIX_n 0x004 /* bytes output so far (%n) */
77#define FMT_RADIX_o 0x008 /* unsigned integer type, octal (%o) */
78#define FMT_RADIX_p 0x010 /* pointer (%p %P) */
79#define FMT_RADIX_s 0x020 /* string (%s) */
80#define FMT_RADIX_u 0x040 /* unsigned integer type, decimal (%u) */
81#define FMT_RADIX_x 0x080 /* unsigned integer type, hex (%x %X) */
82#define FMT_RADIX_a 0x100 /* hex floating point "[-]0xh.hhhhp±d" */
83#define FMT_RADIX_e 0x200 /* floating point with exponent "[-]d.ddde±dd" */
84#define FMT_RADIX_f 0x400 /* floating point "[-]ddd.ddd" */
85#define FMT_RADIX_g 0x800 /* floating point exponent or decimal depending
86 upon value and precision */
87
88/* TODO: 'a' 'A' */
89#define FMT_RADIX_floats (FMT_RADIX_e|FMT_RADIX_f|FMT_RADIX_g)
90
91#if (FMT_RADIX & FMT_RADIX_floats)
92/* Assumes IEEE 754 double-precision, native-endian; replace to parse and init
93 for some other format */
94#define parse_double parse_ieee754_double
95#define init_double_chunks init_ieee754_double_chunks
96#define format_double_int10 format_ap_int10
97#define format_double_frac10 format_ap_frac10
98#endif
99
100/* avoid defining redundant functions if two or more types can use the same
101 * something not getting a macro means it gets assigned its own value and
102 * formatter */
103
104/* l */
105#if LONG_MIN == INT_MIN && LONG_MAX == INT_MAX
106#define val_ld val_d
107#define format_ld format_d
108#define branch_fmt_ld branch_fmt_d
109#elif !(FMT_LENMOD & FMT_LENMOD_l) /* unique */
110#define val_ld
111#endif /* LONG_ */
112
113#if ULONG_MAX == UINT_MAX
114#define val_lu val_u
115#define format_lu format_u
116#define branch_fmt_lu branch_fmt_u
117#elif !(FMT_LENMOD & FMT_LENMOD_l) /* unique */
118#define val_lu
119#endif /* ULONG_ */
120
121/* ll */
122#if LLONG_MIN == INT_MIN && LLONG_MAX == INT_MAX
123#define val_lld val_d
124#define format_lld format_d
125#define branch_fmt_lld branch_fmt_d
126#elif LLONG_MIN == LONG_MIN && LLONG_MAX == LONG_MAX
127#define val_lld val_ld
128#define format_lld format_ld
129#define branch_fmt_lld branch_fmt_ld
130#elif !(FMT_LENMOD & FMT_LENMOD_ll) /* unique */
131#define val_lld
132#endif /* LLONG_ */
133
134#if ULLONG_MAX == UINT_MAX
135#define val_llu val_u
136#define format_llu format_u
137#define branch_fmt_llu branch_fmt_u
138#elif ULLONG_MAX == ULONG_MAX
139#define val_llu val_lu
140#define format_llu format_lu
141#define branch_fmt_llu branch_fmt_lu
142#elif !(FMT_LENMOD & FMT_LENMOD_ll) /* unique */
143#define val_llu
144#endif /* ULLONG_ */
145
146/* char/short parameter type promotions */
147#define SCHAR_INT_ARG int
148#define UCHAR_INT_ARG int
149#define SSHRT_INT_ARG int
150#if USHRT_MAX == UINT_MAX
151#define USHRT_INT_ARG unsigned int
152#else
153#define USHRT_INT_ARG int
154#endif
155
156/* some macros to have conditional work inside macros */
157#if (FMT_LENMOD & FMT_LENMOD_l)
158#define IF_FMT_LENMOD_l(...) __VA_ARGS__
159#else
160#define IF_FMT_LENMOD_l(...)
161#endif
162
163#if (FMT_LENMOD & FMT_LENMOD_ll)
164#define IF_FMT_LENMOD_ll(...) __VA_ARGS__
165#else
166#define IF_FMT_LENMOD_ll(...)
167#endif
168
169#if (FMT_RADIX & FMT_RADIX_o)
170#define IF_FMT_RADIX_o(...) __VA_ARGS__
171#else
172#define IF_FMT_RADIX_o(...)
173#endif
174
175#if (FMT_RADIX & FMT_RADIX_x)
176#define IF_FMT_RADIX_x(...) __VA_ARGS__
177#else
178#define IF_FMT_RADIX_x(...)
179#endif
180
181/* synthesize multicharacter constant */
182#define LENMOD2(cv, ch) \
183 (((cv) << CHAR_BIT) | (ch))
184
185#define LENMOD_NONE 0
186
187#if (FMT_LENMOD & FMT_LENMOD_h)
188#define LENMOD_h 'h'
189#endif
190#if (FMT_LENMOD & FMT_LENMOD_hh)
191#define LENMOD_hh LENMOD2('h', 'h') /* 'hh' */
192#endif
193#if (FMT_LENMOD & FMT_LENMOD_j)
194#define LENMOD_j 'j'
195#endif
196#if (FMT_LENMOD & FMT_LENMOD_l)
197#define LENMOD_l 'l'
198#endif
199#if (FMT_LENMOD & FMT_LENMOD_ll)
200#undef FMT_MAX_L
201#define LENMOD_ll LENMOD2('l', 'l') /* 'll' */
202#endif
203#if (FMT_LENMOD & FMT_LENMOD_t)
204#define LENMOD_t 't'
205#endif
206#if (FMT_LENMOD & FMT_LENMOD_z)
207#define LENMOD_z 'z'
208#endif
209
210/* select type-compatible length modifier
211 * (a bit hacky; it should be range-based) */
212#define LENMOD_INTCOMPAT_SEL(type, signd) \
213 ({ int __lenmod; \
214 size_t __size = sizeof (type); \
215 if (__size == ((signd) ? sizeof (int) : \
216 sizeof (unsigned int))) { \
217 __lenmod = LENMOD_NONE; \
218 } \
219 else if (__size == ((signd) ? sizeof (long) : \
220 sizeof (unsigned long))) { \
221 IF_FMT_LENMOD_l(__lenmod = LENMOD_l;) \
222 } \
223 else if (__size == ((signd) ? sizeof (long long) : \
224 sizeof (unsigned long long))) { \
225 IF_FMT_LENMOD_ll(__lenmod = LENMOD_ll;) \
226 } \
227 __lenmod; })
228
229/* call formatting function for the compatible integer type */
230#define LENMOD_INTCOMPAT_CALL(inteqv, val, fmt_buf, x, signd) \
231 ({ const char *__buf; \
232 switch (inteqv) { \
233 case LENMOD_NONE: \
234 __buf = (signd) ? \
235 format_d((val), (fmt_buf), (x)) : \
236 format_u((val), (fmt_buf), (x)); \
237 break; \
238 IF_FMT_LENMOD_l( \
239 case LENMOD_l: \
240 __buf = (signd) ? \
241 format_ld((val), (fmt_buf), (x)) : \
242 format_lu((val), (fmt_buf), (x)); \
243 break; \
244 ) \
245 IF_FMT_LENMOD_ll( \
246 case LENMOD_ll: \
247 __buf = (signd) ? \
248 format_lld((val), (fmt_buf), (x)) : \
249 format_llu((val), (fmt_buf), (x)); \
250 break; \
251 ) \
252 } \
253 __buf; \
254 })
255
256/* execute formatting branch for the compatible integer type */
257#define LENMOD_INTCOMPAT_BRANCH(inteqv, val, signd) \
258 ({ switch (inteqv) { \
259 case LENMOD_NONE: \
260 if (signd) { \
261 val_d = (val); \
262 goto branch_fmt_d; \
263 } \
264 else { \
265 val_u = (val); \
266 goto branch_fmt_u; \
267 } \
268 IF_FMT_LENMOD_l( \
269 case LENMOD_l: \
270 if (signd) { \
271 val_ld = (val); \
272 goto branch_fmt_ld; \
273 } \
274 else { \
275 val_lu = (val); \
276 goto branch_fmt_lu; \
277 } \
278 ) \
279 IF_FMT_LENMOD_ll( \
280 case LENMOD_ll: \
281 if (signd) { \
282 val_lld = (val); \
283 goto branch_fmt_lld; \
284 } \
285 else { \
286 val_llu = (val); \
287 goto branch_fmt_llu; \
288 } \
289 ) \
290 } \
291 })
292
293#define CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, type) \
294 do { \
295 if (val) { \
296 unsigned type v; \
297 \
298 if (val < 0) { \
299 v = (typeof (v))-(val + 1) + 1; \
300 signchar = '-'; \
301 } \
302 else { \
303 v = val; \
304 } \
305 \
306 do { \
307 *--p = (v % 10) + '0'; \
308 v /= 10; \
309 } while (v); \
310 } \
311 \
312 if (signchar) { \
313 p[-1] = signchar; \
314 fmt_buf->length = 1; \
315 break; \
316 } \
317 \
318 fmt_buf->length = 0; \
319 } while (0)
320
321#define CONVERT_RADIX_8(val, fmt_buf, p) \
322 do { \
323 if (val) { \
324 typeof (val) v = val; \
325 \
326 do { \
327 *--p = (v % 010) + '0'; \
328 v /= 010; \
329 } while (v); \
330 } \
331 \
332 if (fmt_buf->length) { \
333 *--p = '0'; \
334 fmt_buf->length = 0; \
335 } \
336 } while (0)
337
338#define CONVERT_RADIX_10(val, fmt_buf, p) \
339 do { \
340 if (val) { \
341 typeof (val) v = val; \
342 \
343 do { \
344 *--p = (v % 10) + '0'; \
345 v /= 10; \
346 } while (v); \
347 } \
348 \
349 fmt_buf->length = 0; \
350 } while (0)
351
352#define CONVERT_RADIX_16(val, fmt_buf, p, x) \
353 do { \
354 if (val) { \
355 const int h = x - 'X' - 0xA \
356 + 'A' - '0'; \
357 typeof (val) v = val; \
358 \
359 do { \
360 unsigned int d = v % 0x10; \
361 if (d >= 0xA) { \
362 d += h; \
363 } \
364 *--p = d + '0'; \
365 v /= 0x10; \
366 } while (v); \
367 \
368 if (fmt_buf->length) { \
369 p[-1] = x; \
370 p[-2] = '0'; \
371 fmt_buf->length = 2; \
372 break; \
373 } \
374 } \
375 \
376 fmt_buf->length = 0; \
377 } while (0)
378
379#define CONVERT_RADIX_NOSIGN(val, fmt_buf, p, x) \
380 switch (x) \
381 { \
382 IF_FMT_RADIX_o( case 'o': \
383 CONVERT_RADIX_8(val, fmt_buf, p); \
384 break; ) \
385 case 'u': \
386 CONVERT_RADIX_10(val, fmt_buf, p); \
387 break; \
388 IF_FMT_RADIX_x( default: \
389 CONVERT_RADIX_16(val, fmt_buf, p, x); \
390 break; ) \
391 }
392
393struct fmt_buf {
394 const char *fmt_start; /* second character of formatter after '%' */
395 size_t length; /* length of formatted text (non-numeric)
396 or prefix (numeric) */
397 char buf[24]; /* work buffer */
398 char bufend[1]; /* buffer end marker and guard '0' */
399#if (FMT_RADIX & FMT_RADIX_floats)
400 int lenmod;
401 int radixchar;
402 int signchar;
403 int alignchar;
404 int width;
405 int precision;
406 char *p;
407#endif
408};
409
410#define PUSHCHAR(ch) \
411 ({ int __rc = push(userp, (ch)); \
412 count += __rc >= 0; \
413 if (__rc <= 0) { \
414 goto done; \
415 } })
416
417/* %d %i */
418static inline const char * format_d(int val,
419 struct fmt_buf *fmt_buf,
420 int signchar)
421{
422 char *p = fmt_buf->bufend;
423 CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, int);
424 return p;
425}
426
427/* %o %u %x %X */
428static inline const char * format_u(unsigned int val,
429 struct fmt_buf *fmt_buf,
430 int radixchar)
431{
432 char *p = fmt_buf->bufend;
433 CONVERT_RADIX_NOSIGN(val, fmt_buf, p, radixchar);
434 return p;
435}
436
437#if (FMT_LENMOD & FMT_LENMOD_l)
438#ifndef format_ld
439/* %ld %li */
440static inline const char * format_ld(long val,
441 struct fmt_buf *fmt_buf,
442 int signchar)
443{
444 char *p = fmt_buf->bufend;
445 CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, long);
446 return p;
447}
448#endif /* format_ld */
449
450#ifndef format_lu
451/* %lo %lu %lx %lX */
452static inline const char * format_lu(unsigned long val,
453 struct fmt_buf *fmt_buf,
454 int radixchar)
455{
456 char *p = fmt_buf->bufend;
457 CONVERT_RADIX_NOSIGN(val, fmt_buf, p, radixchar);
458 return p;
459}
460#endif /* format_lu */
461#endif /* FMT_LENMOD_l */
462
463#if (FMT_LENMOD & FMT_LENMOD_ll)
464#ifndef format_lld
465/* %lld %lli */
466static inline const char * format_lld(long long val,
467 struct fmt_buf *fmt_buf,
468 int signchar)
469{
470 char *p = fmt_buf->bufend;
471 CONVERT_RADIX_10_SIGN(val, fmt_buf, p, signchar, long long);
472 return p;
473}
474#endif /* format_lld */
475
476#ifndef format_llu
477/* %llo %llu %llx %llX */
478static inline const char * format_llu(unsigned long long val,
479 struct fmt_buf *fmt_buf,
480 int radixchar)
481{
482 char *p = fmt_buf->bufend;
483 CONVERT_RADIX_NOSIGN(val, fmt_buf, p, radixchar);
484 return p;
485}
486#endif /* format_llu */
487#endif /* FMT_LENMOD_ll */
488
489/* %c */
490static inline const char * format_c(int c,
491 struct fmt_buf *fmt_buf,
492 int lenmod)
493{
494 if (lenmod != LENMOD_NONE) {
495 return NULL; /* wchar_t support for now */
496 }
497
498 char *p = fmt_buf->bufend;
499 fmt_buf->length = 1;
500 *--p = (unsigned char)c;
501 return p;
502}
503
504/* %s */
505static inline const char * format_s(const void *str,
506 struct fmt_buf *fmt_buf,
507 int precision,
508 int lenmod)
509{
510 if (lenmod != LENMOD_NONE) {
511 return NULL; /* wchar_t support for now */
512 }
513
514 const char *s = str ? str : "(null)";
515 size_t len;
516 /* string length may be specified by precision instead of \0-
517 terminated; however, don't go past a \0 if one is there */
518 if (precision >= 0) {
519 const char *nil = memchr(s, '\0', (size_t) precision);
520
521 if (nil != NULL && (nil - s) < precision)
522 len = nil - s;
523 else
524 len = precision;
525 }
526 else
527 len = strlen(s);
528
529 fmt_buf->length = len;
530 return s;
531}
532
533#if (FMT_RADIX & FMT_RADIX_n)
534/* %n */
535static inline bool format_n(void *np,
536 int count,
537 int lenmod)
538{
539 if (lenmod != LENMOD_NONE) {
540 return false; /* int only for now */
541 }
542
543 *(int *)np = count;
544 return true;
545}
546#endif /* FMT_RADIX_n */
547
548#if (FMT_RADIX & FMT_RADIX_p)
549/* %p %P */
550static inline const char * format_p(const void *p,
551 struct fmt_buf *fmt_buf,
552 int radixchar,
553 bool *numericp)
554{
555 if (p) {
556 /* format as %#x or %#X */
557 *numericp = true;
558 radixchar -= 'P' - 'X';
559 fmt_buf->length = 2;
560 return LENMOD_INTCOMPAT_CALL(LENMOD_INTCOMPAT_SEL(uintptr_t, false),
561 (uintptr_t)p, fmt_buf, radixchar, false);
562 }
563 else {
564 /* format as %s */
565 fmt_buf->length = 5;
566 return "(nil)";
567 }
568}
569#endif /* FMT_RADIX_p */
570
571#if (FMT_RADIX & FMT_RADIX_floats)
572/* find out how many uint32_t chunks need to be allocated, if any
573 * if none are needed, finish the init for the number here */
574static long parse_ieee754_double(double f,
575 struct ap_int *ia,
576 struct ap_int *fa,
577 struct fmt_buf *fmt_buf)
578{
579 long rc = 0;
580
581 union {
582 double f;
583 uint64_t f64;
584 } u = { .f = f };
585
586 int e = ((int)(u.f64 >> 52) & 0x7ff) - 1023; /* -1023..1024 */
587 uint64_t mantissa = u.f64 & 0x000fffffffffffffull;
588
589 if (u.f64 >> 63) {
590 fmt_buf->signchar = '-';
591 }
592
593 if (LIKELY(e >= -8 && e <= 63)) { /* -8 to +63 */
594 /* integer, fraction and manipulations fit in uint64_t */
595 mantissa |= 0x0010000000000000ull;
596 ia->numchunks = 0;
597 ia->shift = 0;
598 fa->numchunks = 0;
599
600 if (e < 0) { /* -8 to -1 - fraction */
601 long fracbits = 52 - e;
602 /* int - none */
603 ia->len = 0;
604 ia->val = 0;
605 /* frac */
606 fa->len = fracbits - __builtin_ctzll(mantissa);
607 fa->shift = fracbits;
608 fa->val = mantissa;
609 }
610 else if (e <= 51) { /* 0 to +51 - integer|fraction */
611 long fracbits = 52 - e;
612 /* int */
613 ia->len = base10exp(e) + 2; /* go up + possibly 1 longer */
614 ia->val = mantissa >> fracbits;
615 /* frac */
616 fa->shift = fracbits;
617 fa->val = mantissa ^ (ia->val << fracbits);
618 fa->len = fa->val ? fracbits - __builtin_ctzll(mantissa) : 0;
619 }
620 else { /* +52 to +63 - integer */
621 /* int */
622 ia->len = base10exp(e) + 2;
623 ia->val = mantissa << (e - 52);
624 /* frac - none */
625 fa->len = 0;
626 fa->shift = 0;
627 fa->val = 0;
628 }
629 }
630 else if (e < 0) { /* -1023 to -9 - fraction */
631 /* int - none */
632 ia->numchunks = 0;
633 ia->len = 0;
634 ia->shift = 0;
635 ia->val = 0;
636 /* frac - left-justify on bit 31 of the chunk of the MSb */
637 if (e >= -1022) { /* normal */
638 mantissa |= 0x0010000000000000ull;
639 }
640 else { /* subnormal (including zero) */
641 e = -1022;
642 }
643
644 if (mantissa) {
645 long fracbits = 52 - e;
646 fa->len = fracbits - __builtin_ctzll(mantissa);
647 fa->shift = 31 - ((51 - e) % 32);
648 fa->val = mantissa;
649 fa->basechunk = (fa->shift + 52) / 32;
650 fa->numchunks = (51 - e + fa->shift) / 32 + 1;
651 rc = fa->numchunks;
652 }
653 else { /* zero */
654 fa->numchunks = 0;
655 fa->len = 0;
656 fa->shift = 0;
657 fa->val = 0;
658 }
659 }
660 else if (e <= 1023) { /* +64 to +1023 - integer */
661 /* int - right-justify on bit 0 of the first chunk */
662 ia->val = mantissa | 0x0010000000000000ull;
663 ia->len = base10exp(e) + 2;
664 ia->shift = (e - 52) % 32;
665 ia->basechunk = e / 32;
666 ia->numchunks = ia->basechunk + 1;
667 rc = ia->numchunks;
668 /* frac - none */
669 fa->numchunks = 0;
670 fa->len = 0;
671 fa->shift = 0;
672 fa->val = 0;
673 }
674 else { /* +1024: INF, NAN */
675 rc = -1 - !!mantissa;
676 }
677
678 return rc;
679}
680
681/* construct the arbitrary-precision value in the provided allocation */
682static void init_ieee754_double_chunks(struct ap_int *a,
683 uint32_t *a_chunks)
684{
685 long basechunk = a->basechunk;
686 long shift = a->shift;
687 uint64_t val = a->val;
688
689 a->chunks = a_chunks;
690
691 memset(a_chunks, 0, a->numchunks*sizeof (uint32_t));
692
693 if (shift < 12) {
694 a_chunks[basechunk - 1] = val << shift;
695 a_chunks[basechunk - 0] = val >> (32 - shift);
696 }
697 else {
698 a_chunks[basechunk - 2] = val << shift;
699 a_chunks[basechunk - 1] = val >> (32 - shift);
700 a_chunks[basechunk - 0] = val >> (64 - shift);
701 }
702}
703
704/* format inf, nan strings */
705static void format_inf_nan(struct fmt_buf *fmt_buf, long type)
706{
707 /* certain special values */
708 static const char text[2][2][3] =
709 {
710 { { 'I', 'N', 'F' }, { 'i', 'n', 'f' } },
711 { { 'N', 'A', 'N' }, { 'n', 'a', 'n' } },
712 };
713
714 char *p = fmt_buf->buf;
715 fmt_buf->p = p;
716 fmt_buf->length = 3;
717
718 /* they also have a sign */
719 if (fmt_buf->signchar) {
720 *p++ = fmt_buf->signchar;
721 fmt_buf->length++;
722 }
723
724 memcpy(p, &text[type][(fmt_buf->radixchar >> 5) & 0x1], 3);
725}
726
727/* %e %E %f %F %g %G */
728static int format_double_radix(double f,
729 struct fmt_buf *fmt_buf,
730 vuprintf_push_cb push,
731 void *userp)
732{
733 struct ap_int ia ={0};
734 struct ap_int fa ={0};
735 long rc = parse_double(f, &ia, &fa, fmt_buf);
736
737 if (UNLIKELY(rc < 0)) {
738 format_inf_nan(fmt_buf, -rc - 1);
739 return 0;
740 }
741
742 int count = 0;
743
744 /* default precision is 6 for all formats */
745 int prec_rem = fmt_buf->precision < 0 ? 6 : fmt_buf->precision;
746
747 int exp = exp;
748 int explen = 0;
749
750 switch (fmt_buf->radixchar & 0x3)
751 {
752 case 3: /* %g, %G */
753 fmt_buf->precision = prec_rem;
754 if (prec_rem) {
755 prec_rem--;
756 }
757 /* fallthrough */
758 case 1: /* %e, %E */
759 explen = 2;
760 break;
761 default:
762 break;
763 }
764
765 if (rc > 0 && ia.numchunks > 0) {
766 /* large integer required */
767 init_double_chunks(&ia, alloca(rc*sizeof(*ia.chunks)));
768 rc = 0;
769 }
770
771 const int bufoffs = 6; /* log rollover + round rollover + leading zeros (%g) */
772 long f_prec = MIN(fa.len, prec_rem + 1);
773 char buf[bufoffs + ia.len + f_prec + 1];
774 char *p_last = &buf[bufoffs + ia.len];
775 char *p_dec = p_last;
776 char *p_first = format_double_int10(&ia, p_last);
777
778 if (explen) {
779 if (!ia.val && fa.len) {
780 p_first = p_last = &buf[bufoffs];
781 f_prec = -f_prec - 1; /* no lead zeros */
782 }
783 else { /* handles 0e+0 too */
784 exp = ia.len - 1;
785
786 if (exp) {
787 prec_rem -= exp;
788
789 if (prec_rem < 0) {
790 p_last += prec_rem + 1;
791 f_prec = 0;
792 }
793 else {
794 f_prec = MIN(fa.len, prec_rem + 1);
795 }
796 }
797 }
798
799 p_dec = p_first + 1;
800 }
801
802 if (f_prec) {
803 if (rc > 0) {
804 /* large integer required */
805 init_double_chunks(&fa, alloca(rc*sizeof(*fa.chunks)));
806 }
807
808 p_last = format_double_frac10(&fa, p_last, f_prec);
809
810 if (f_prec < 0) {
811 f_prec = -f_prec - 1;
812 exp = f_prec - fa.len;
813 }
814
815 prec_rem -= f_prec;
816 }
817
818 if (prec_rem < 0) {
819 prec_rem = 0;
820 p_last--;
821
822 if (round_number_string10(p_last, p_last - p_first)) {
823 /* carried left */
824 p_first--;
825
826 if (explen) {
827 /* slide everything left by 1 */
828 exp++;
829 p_dec--;
830 p_last--;
831 }
832 }
833 }
834
835 if (explen) {
836 if ((fmt_buf->radixchar & 0x3) == 0x3) { /* g, G */
837 /* 'g' is some weird crap */
838 /* now that the final exponent is known and everything rounded,
839 it is possible to decide whether to format similarly to
840 'e' or 'f' */
841 if (fmt_buf->precision > exp && exp >= -4) { /* P > X >= -4 */
842 if (exp >= 0) {
843 /* integer digits will be in the buffer */
844 p_dec = p_first + exp + 1;
845 }
846 else {
847 /* we didn't keep leading zeros and need to regenerate
848 them; space was reserved just in case */
849 p_first = memset(p_dec + exp - 1, '0', -exp);
850 p_dec = p_first + 1;
851 }
852
853 /* suppress exponent */
854 explen = 0;
855 }
856
857 if (!fmt_buf->length) {
858 /* strip any trailing zeros from the fraction */
859 while (p_last > p_dec && p_last[-1] == '0') {
860 p_last--;
861 }
862
863 /* suppress trailing precision fill */
864 prec_rem = 0;
865 }
866 }
867
868 if (explen) {
869 /* build exponent string: 'e±dd' */
870 char *p = fmt_buf->bufend;
871 int signchar = '+';
872
873 if (exp < 0) {
874 signchar = '-';
875 exp = -exp;
876 }
877
878 while (exp || explen < 4) {
879 *--p = exp % 10 + '0';
880 exp /= 10;
881 explen++;
882 }
883
884 *--p = signchar;
885 *--p = fmt_buf->radixchar & ~0x2;
886 }
887 }
888
889 int width = fmt_buf->width;
890 int point = p_last > p_dec || prec_rem || fmt_buf->length;
891 int length = p_last - p_first + !!fmt_buf->signchar + point + explen;
892
893 if (width) {
894 if (width - length <= prec_rem) {
895 width = 0;
896 }
897 else {
898 width -= length + prec_rem;
899 }
900 }
901
902 rc = -1;
903
904 /* left padding */
905 if (fmt_buf->alignchar > '0') {
906 /* space-padded width -- before sign */
907 while (width > 0) {
908 PUSHCHAR(' ');
909 width--;
910 }
911 }
912
913 if (fmt_buf->signchar) {
914 PUSHCHAR(fmt_buf->signchar);
915 }
916
917 if (fmt_buf->alignchar == '0') {
918 /* zero-padded width -- after sign */
919 while (width > 0) {
920 PUSHCHAR('0');
921 width--;
922 }
923 }
924
925 /* integer part */
926 while (p_first < p_dec) {
927 PUSHCHAR(*p_first++);
928 }
929
930 /* decimal point */
931 if (point) {
932 PUSHCHAR('.');
933 }
934
935 /* fractional part */
936 while (p_first < p_last) {
937 PUSHCHAR(*p_first++);
938 }
939
940 /* precision 0-padding */
941 while (prec_rem > 0) {
942 PUSHCHAR('0');
943 prec_rem--;
944 }
945
946 /* exponent */
947 if (explen > 0) {
948 char *p = fmt_buf->bufend;
949 while (explen > 0) {
950 PUSHCHAR(p[-explen--]);
951 }
952 }
953
954 /* right padding */
955 while (width > 0) {
956 PUSHCHAR(' ');
957 width--;
958 }
959
960 rc = 1;
961done:
962 fmt_buf->length = count;
963 return rc;
964}
965#endif /* FMT_RADIX_floats */
966
967/* parse fixed width or precision field */
968static const char * parse_number_spec(const char *fmt,
969 int ch,
970 int *out)
971{
972 int i = ch - '0';
973
974 while (1) {
975 ch = *fmt - '0';
976
977 if (ch < 0 || ch > 9 || i > INT_MAX / 10 ||
978 (i == INT_MAX / 10 && ch > INT_MAX % 10)) {
979 break;
980 }
981
982 i = i * 10 + ch;
983 fmt++;
984 }
985
986 *out = i;
987 return fmt;
988}
989
990int vuprintf(vuprintf_push_cb push, /* call 'push()' for each output letter */
991 void *userp,
992 const char *fmt,
993 va_list ap)
994{
995 int count = 0;
996 int ch;
997
998 /* macrofied identifiers share a variable with another */
999 unsigned int val_d;
1000 unsigned int val_u;
1001 #ifndef val_ld
1002 unsigned long val_ld;
1003 #endif
1004 #ifndef val_lu
1005 unsigned long val_lu;
1006 #endif
1007 #ifndef val_lld
1008 unsigned long long val_lld;
1009 #endif
1010 #ifndef val_llu
1011 unsigned long long val_llu;
1012 #endif
1013
1014 struct fmt_buf fmt_buf;
1015 fmt_buf.bufend[0] = '0';
1016
1017 while (1) {
1018 while (1) {
1019 if ((ch = *fmt++) == '\0') {
1020 goto done;
1021 }
1022
1023 if (ch == '%' && (ch = *fmt++) != '%') {
1024 break;
1025 }
1026
1027 PUSHCHAR(ch);
1028 }
1029
1030 /* set to defaults */
1031 fmt_buf.fmt_start = fmt;
1032
1033 int signchar = 0;
1034 unsigned int width = 0;
1035 int lenmod = LENMOD_NONE;
1036 size_t length = 0;
1037 size_t pfxlen = 0;
1038 bool numeric = false;
1039 int alignchar = '0' + 1;
1040 int precision = -1;
1041 const char *buf = NULL;
1042
1043 /*** flags ***/
1044 while (1) {
1045 switch (ch)
1046 {
1047 case ' ': /* <space> before non-negative value (signed conversion) */
1048 case '+': /* '+' before non-negative value (signed conversion) */
1049 /* '+' overrides ' ' */
1050 if (ch > signchar) {
1051 signchar = ch;
1052 }
1053 break;
1054 case '-': /* left-justify in field */
1055 case '0': /* zero-pad to fill field */
1056 /* '-' overrides '0' */
1057 if (ch < alignchar) {
1058 alignchar = ch;
1059 }
1060 break;
1061 case '#': /* number prefix (nonzero %o:'0' %x/%X:'0x') */
1062 /* indicate; formatter updates with actual length */
1063 pfxlen = 1;
1064 break;
1065 #if 0
1066 case '\'': /* digit grouping (non-monetary) */
1067 break;
1068 #endif
1069 default:
1070 goto flags_done;
1071 }
1072
1073 ch = *fmt++;
1074 }
1075 flags_done:
1076
1077 /*** width ***/
1078 if (ch == '*') {
1079 /* variable width */
1080 int w = va_arg(ap, int);
1081 if (w < 0) {
1082 /* negative width is width with implied '-' */
1083 width = (unsigned int)-(w + 1) + 1;
1084 alignchar = '-';
1085 }
1086 else {
1087 width = w;
1088 }
1089
1090 ch = *fmt++;
1091 }
1092 else if (ch >= '1' && ch <= '9') {
1093 /* fixed width */
1094 fmt = parse_number_spec(fmt, ch, &width);
1095 ch = *fmt++;
1096 }
1097
1098 /*** precision ***/
1099 if (ch == '.') {
1100 ch = *fmt++;
1101
1102 if (ch == '*') {
1103 /* variable precision; negative precision is ignored */
1104 precision = va_arg (ap, int);
1105 ch = *fmt++;
1106 }
1107 else if (ch >= '0' && ch <= '9') {
1108 /* fixed precision */
1109 fmt = parse_number_spec(fmt, ch, &precision);
1110 ch = *fmt++;
1111 }
1112 }
1113
1114 /*** length modifier ***/
1115 #if FMT_LENMOD
1116 switch (ch)
1117 {
1118 #if (FMT_LENMOD & (FMT_LENMOD_h | FMT_LENMOD_hh))
1119 case 'h':
1120 #endif
1121 #if (FMT_LENMOD & FMT_LENMOD_j)
1122 case 'j':
1123 #endif
1124 #if (FMT_LENMOD & (FMT_LENMOD_l | FMT_LENMOD_ll))
1125 case 'l':
1126 #endif
1127 #if (FMT_LENMOD & FMT_LENMOD_t)
1128 case 't':
1129 #endif
1130 #if (FMT_LENMOD & FMT_LENMOD_z)
1131 case 'z':
1132 #endif
1133 #if (FMT_LENMOD & FMT_LENMOD_L)
1134 case 'L':
1135 #endif
1136 lenmod = ch;
1137 ch = *fmt++;
1138 #if (FMT_LENMOD & (FMT_LENMOD_hh | FMT_LENMOD_ll))
1139 /* doesn't matter if jj, tt or zz happen; they will be rejected
1140 by the radix handler */
1141 if (ch == lenmod) {
1142 lenmod = LENMOD2(lenmod, ch);
1143 ch = *fmt++;
1144 }
1145 #endif
1146 }
1147 #endif /* FMT_LENMOD */
1148
1149 /*** radix ***/
1150 switch (ch)
1151 {
1152 /** non-numeric **/
1153 case 'c':
1154 buf = format_c(va_arg(ap, int), &fmt_buf, lenmod);
1155 break;
1156 #if (FMT_RADIX & FMT_RADIX_n)
1157 case 'n':
1158 if (format_n(va_arg(ap, void *), count, lenmod)) {
1159 continue; /* no output */
1160 }
1161 break;
1162 #endif
1163 case 's':
1164 buf = format_s(va_arg(ap, const void *), &fmt_buf,
1165 precision, lenmod);
1166 break;
1167
1168 /** non-integer **/
1169 #if (FMT_RADIX & FMT_RADIX_p)
1170 case 'p':
1171 case 'P':
1172 buf = format_p(va_arg(ap, void *), &fmt_buf, ch,
1173 &numeric);
1174 break;
1175 #endif
1176
1177 #if (FMT_RADIX & FMT_RADIX_floats)
1178 /* any floats gets all of them (except with 'L' and %a, %A for now) */
1179 case 'e':
1180 case 'E':
1181 case 'f':
1182 case 'F':
1183 case 'g':
1184 case 'G':
1185 /* LENMOD_L isn't supported for now and will be rejected automatically */
1186
1187 /* floating point has very different spec interpretations to other
1188 formats and requires special handling */
1189 fmt_buf.length = pfxlen;
1190 fmt_buf.lenmod = lenmod;
1191 fmt_buf.radixchar = ch;
1192 fmt_buf.signchar = signchar;
1193 fmt_buf.alignchar = alignchar;
1194 fmt_buf.width = width;
1195 fmt_buf.precision = precision;
1196
1197 ch = format_double_radix(va_arg(ap, double), &fmt_buf, push, userp);
1198 if (ch) {
1199 count += fmt_buf.length;
1200 if (ch > 0) {
1201 continue;
1202 }
1203
1204 goto done;
1205 }
1206
1207 buf = fmt_buf.p;
1208 break;
1209 #endif
1210
1211 /** signed integer **/
1212 case 'd':
1213 case 'i':
1214 fmt_buf.length = pfxlen;
1215
1216 switch (lenmod)
1217 {
1218 case LENMOD_NONE:
1219 val_d = va_arg(ap, signed int);
1220 goto branch_fmt_d;
1221 #if (FMT_LENMOD & FMT_LENMOD_h)
1222 case LENMOD_h:
1223 val_d = (signed short)va_arg(ap, SSHRT_INT_ARG);
1224 goto branch_fmt_d;
1225 #endif
1226 #if (FMT_LENMOD & FMT_LENMOD_hh)
1227 case LENMOD_hh:
1228 val_d = (signed char)va_arg(ap, SCHAR_INT_ARG);
1229 goto branch_fmt_d;
1230 #endif
1231 #if (FMT_LENMOD & FMT_LENMOD_j)
1232 case LENMOD_j:
1233 LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(intmax_t, true),
1234 va_arg(ap, intmax_t), true);
1235 #endif
1236 #if (FMT_LENMOD & FMT_LENMOD_l)
1237 case LENMOD_l:
1238 val_ld = va_arg(ap, signed long);
1239 goto branch_fmt_ld;
1240 #endif
1241 #if (FMT_LENMOD & FMT_LENMOD_ll)
1242 case LENMOD_ll:
1243 val_lld = va_arg(ap, signed long long);
1244 goto branch_fmt_lld;
1245 #endif
1246 #if (FMT_LENMOD & FMT_LENMOD_t)
1247 case LENMOD_t:
1248 LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(ptrdiff_t, true),
1249 va_arg(ap, ptrdiff_t), true);
1250 #endif
1251 #if (FMT_LENMOD & FMT_LENMOD_z)
1252 case LENMOD_z:
1253 LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(ssize_t, true),
1254 va_arg(ap, ssize_t), true);
1255 #endif
1256 }
1257
1258 /* macrofied labels share a formatter with another */
1259 if (0) {
1260 branch_fmt_d:
1261 buf = format_d(val_d, &fmt_buf, signchar);
1262 } else if (0) {
1263 #ifndef val_ld
1264 branch_fmt_ld:
1265 buf = format_ld(val_ld, &fmt_buf, signchar);
1266 #endif
1267 } else if (0) {
1268 #ifndef val_lld
1269 branch_fmt_lld:
1270 buf = format_lld(val_lld, &fmt_buf, signchar);
1271 #endif
1272 }
1273
1274 numeric = true;
1275 break;
1276
1277 /** unsigned integer **/
1278 #if (FMT_RADIX & FMT_RADIX_o)
1279 case 'o':
1280 #endif
1281 case 'u':
1282 #if (FMT_RADIX & FMT_RADIX_x)
1283 case 'x':
1284 case 'X':
1285 #endif
1286 fmt_buf.length = pfxlen;
1287
1288 switch (lenmod)
1289 {
1290 case LENMOD_NONE:
1291 val_u = va_arg(ap, unsigned int);
1292 goto branch_fmt_u;
1293 #if (FMT_LENMOD & FMT_LENMOD_h)
1294 case LENMOD_h:
1295 val_u = (unsigned short)va_arg(ap, USHRT_INT_ARG);
1296 goto branch_fmt_u;
1297 #endif
1298 #if (FMT_LENMOD & FMT_LENMOD_hh)
1299 case LENMOD_hh:
1300 val_u = (unsigned char)va_arg(ap, UCHAR_INT_ARG);
1301 goto branch_fmt_u;
1302 #endif
1303 #if (FMT_LENMOD & FMT_LENMOD_j)
1304 case LENMOD_j:
1305 LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(uintmax_t, false),
1306 va_arg(ap, uintmax_t), false);
1307 #endif
1308 #if (FMT_LENMOD & FMT_LENMOD_l)
1309 case LENMOD_l:
1310 val_lu = va_arg(ap, unsigned long);
1311 goto branch_fmt_lu;
1312 #endif
1313 #if (FMT_LENMOD & FMT_LENMOD_ll)
1314 case LENMOD_ll:
1315 val_llu = va_arg(ap, unsigned long long);
1316 goto branch_fmt_llu;
1317 #endif
1318 #if (FMT_LENMOD & (FMT_LENMOD_t | FMT_LENMOD_z))
1319 /* format "uptrdiff_t" as size_t (unless it becomes standard) */
1320 #if (FMT_LENMOD & FMT_LENMOD_t)
1321 case LENMOD_t:
1322 #endif
1323 #if (FMT_LENMOD & FMT_LENMOD_z)
1324 case LENMOD_z:
1325 #endif
1326 LENMOD_INTCOMPAT_BRANCH(LENMOD_INTCOMPAT_SEL(size_t, false),
1327 va_arg(ap, size_t), false);
1328 #endif
1329 }
1330
1331 /* macrofied labels share a formatter with another */
1332 if (0) {
1333 branch_fmt_u:
1334 buf = format_u(val_u, &fmt_buf, ch);
1335 } else if (0) {
1336 #ifndef val_lu
1337 branch_fmt_lu:
1338 buf = format_lu(val_lu, &fmt_buf, ch);
1339 #endif
1340 } else if (0) {
1341 #ifndef val_llu
1342 branch_fmt_llu:
1343 buf = format_llu(val_llu, &fmt_buf, ch);
1344 #endif
1345 }
1346
1347 numeric = true;
1348 break;
1349 }
1350
1351 if (buf) {
1352 /** padding **/
1353 if (numeric) {
1354 /* numeric formats into fmt_buf.buf */
1355 pfxlen = fmt_buf.length;
1356 length = fmt_buf.bufend - buf;
1357
1358 size_t size = pfxlen + length;
1359
1360 if (precision >= 0) {
1361 /* explicit precision */
1362 precision -= (int)length;
1363
1364 if (precision > 0) {
1365 size += precision;
1366 }
1367
1368 width -= MIN(width, size);
1369 }
1370 else {
1371 /* default precision */
1372 if (!length) {
1373 length = 1;
1374 size++;
1375 }
1376
1377 width -= MIN(width, size);
1378
1379 if (alignchar == '0') {
1380 /* width zero-fill */
1381 precision = width;
1382 width = 0;
1383 }
1384 }
1385 }
1386 else {
1387 /* non-numeric: supress prefix and precision; keep length and
1388 width */
1389 pfxlen = 0;
1390 precision = 0;
1391 length = fmt_buf.length;
1392 width -= MIN(width, length);
1393 }
1394 }
1395 else {
1396 /* format not accepted; print it literally */
1397 buf = fmt_buf.fmt_start - 2;
1398 length = fmt - buf;
1399 width = 0;
1400 pfxlen = 0;
1401 precision = 0;
1402 }
1403
1404 /** push all the stuff **/
1405
1406 if (alignchar != '-') {
1407 /* left padding */
1408 while (width > 0) {
1409 PUSHCHAR(' ');
1410 width--;
1411 }
1412 }
1413
1414 /* prefix */
1415 while (pfxlen > 0) {
1416 PUSHCHAR(buf[-pfxlen]);
1417 pfxlen--;
1418 }
1419
1420 /* 0-padding */
1421 while (precision > 0) {
1422 PUSHCHAR('0');
1423 precision--;
1424 }
1425
1426 /* field */
1427 while (length > 0) {
1428 PUSHCHAR(*buf++);
1429 length--;
1430 }
1431
1432 /* right padding */
1433 while (width > 0) {
1434 PUSHCHAR(' ');
1435 width--;
1436 }
1437 }
1438
1439done:
1440 return count;
1441}