blob: d700426428ffaff6f9365a6ae2ac8bc159147e19 [file] [log] [blame]
Soby Mathewb4c6df42022-11-09 11:13:29 +00001/*
2 * SPDX-License-Identifier: MIT
3 * SPDX-FileCopyrightText: Copyright Marco Paland (info@paland.com), PALANDesign Hannover, Germany
4 * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 *
24 * \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for
25 * speed on
26 * embedded systems with a very limited resources. These routines are thread
27 * safe and reentrant!
28 * Use this instead of the bloated standard/newlib printf cause these use
29 * malloc for printf (and may not be thread safe).
30 */
31
32/*
33 * fine this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
34 * printf_config.h header file
35 * default: undefined
36 */
37/*
38 * support for the floating point type (%f)
39 * default: activated
40 */
41#ifndef PRINTF_DISABLE_SUPPORT_FLOAT
42#define PRINTF_SUPPORT_FLOAT
43#endif
44
45/* import float.h for DBL_MAX */
46#if defined(PRINTF_SUPPORT_FLOAT)
47#include <float.h>
48#endif
49
50#ifdef PRINTF_INCLUDE_CONFIG_H
51#include "printf_config.h"
52#endif
53
54#include <stdbool.h>
55#include <stdint.h>
56#include <stdio.h>
57
58/*
59 * 'ntoa' conversion buffer size, this must be big enough to hold one converted
60 * numeric number including padded zeros (dynamically created on stack)
61 * default: 32 byte
62 */
63#ifndef PRINTF_NTOA_BUFFER_SIZE
64#define PRINTF_NTOA_BUFFER_SIZE 32U
65#endif
66
67/*
68 *'ftoa' conversion buffer size, this must be big enough to hold one converted
69 * float number including padded zeros (dynamically created on stack)
70 * default: 32 byte
71 */
72#ifndef PRINTF_FTOA_BUFFER_SIZE
73#define PRINTF_FTOA_BUFFER_SIZE 32U
74#endif
75
76/*
77 * support for exponential floating point notation (%e/%g)
78 * default: activated
79 */
80#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
81#define PRINTF_SUPPORT_EXPONENTIAL
82#endif
83
84/*
85 * define the default floating point precision
86 * default: 6 digits
87 */
88#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
89#define PRINTF_DEFAULT_FLOAT_PRECISION 6U
90#endif
91
92/*
93 * define the largest float suitable to print with %f
94 * default: 1e9
95 */
96#ifndef PRINTF_MAX_FLOAT
97#define PRINTF_MAX_FLOAT 1e9
98#endif
99
100/*
101 * support for the long long types (%llu or %p)
102 * default: activated
103 */
104#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
105#define PRINTF_SUPPORT_LONG_LONG
106#endif
107
108/*
109 * support for the ptrdiff_t type (%t)
110 * ptrdiff_t is normally defined in <stddef.h> as long or long long type
111 * default: activated
112 */
113#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
114#define PRINTF_SUPPORT_PTRDIFF_T
115#endif
116
117/******************************************************************************/
118
119/* internal flag definitions */
120#define FLAGS_ZEROPAD (1U << 0U)
121#define FLAGS_LEFT (1U << 1U)
122#define FLAGS_PLUS (1U << 2U)
123#define FLAGS_SPACE (1U << 3U)
124#define FLAGS_HASH (1U << 4U)
125#define FLAGS_UPPERCASE (1U << 5U)
126#define FLAGS_CHAR (1U << 6U)
127#define FLAGS_SHORT (1U << 7U)
128#define FLAGS_LONG (1U << 8U)
129#define FLAGS_LONG_LONG (1U << 9U)
130#define FLAGS_PRECISION (1U << 10U)
131#define FLAGS_ADAPT_EXP (1U << 11U)
132
133/* output function type */
134typedef void (*out_fct_type)(char character, void *buffer, size_t idx,
135 size_t maxlen);
136
137
138/* wrapper (used as buffer) for output function type */
139typedef struct {
140 void (*fct)(char character, void *arg);
141 void *arg;
142} out_fct_wrap_type;
143
144
145/* internal buffer output */
146static inline void _out_buffer(char character, void *buffer, size_t idx,
147 size_t maxlen)
148{
149 if (idx < maxlen) {
150 ((char *)buffer)[idx] = character;
151 }
152}
153
154
155/* internal null output */
156static inline void _out_null(char character, void *buffer, size_t idx,
157 size_t maxlen)
158{
159 (void)character; (void)buffer; (void)idx; (void)maxlen;
160}
161
162
163/* internal _putchar wrapper */
164static inline void _out_char(char character, void *buffer, size_t idx,
165 size_t maxlen)
166{
167 (void)buffer; (void)idx; (void)maxlen;
168 if (character) {
169 _putchar(character);
170 }
171}
172
173
174/* internal output function wrapper */
175static inline void _out_fct(char character, void *buffer, size_t idx,
176 size_t maxlen)
177{
178 (void)idx; (void)maxlen;
179 if (character) {
180 /* buffer is the output fct pointer */
181 ((out_fct_wrap_type *)buffer)->fct(character,
182 ((out_fct_wrap_type *)buffer)->arg);
183 }
184}
185
186
187/*
188 * internal secure strlen
189 * \return The length of the string (excluding the terminating 0) limited by
190 * 'maxsize'
191 */
192static inline unsigned int _strnlen_s(const char *str, size_t maxsize)
193{
194 const char *s;
195
196 for (s = str; *s && maxsize--; ++s) {
197 ;
198 }
199
200 return (unsigned int)(s - str);
201}
202
203/*
204 * internal test if char is a digit (0-9)
205 * \return true if char is a digit
206 */
207static inline bool _is_digit(char ch)
208{
209 return (ch >= '0') && (ch <= '9');
210}
211
212
213/* internal ASCII string to unsigned int conversion */
214static unsigned int _atoi(const char **str)
215{
216 unsigned int i = 0U;
217
218 while (_is_digit(**str)) {
219 i = i * 10U + (unsigned int)(*((*str)++) - '0');
220 }
221 return i;
222}
223
224
225/* output the specified string in reverse, taking care of any zero-padding */
226static size_t _out_rev(out_fct_type out, char *buffer, size_t idx, size_t maxlen, const char *buf, size_t len, unsigned int width, unsigned int flags)
227{
228 const size_t start_idx = idx;
229
230 /* pad spaces up to given width */
231 if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
232 for (size_t i = len; i < width; i++) {
233 out(' ', buffer, idx++, maxlen);
234 }
235 }
236
237 /* reverse string */
238 while (len) {
239 out(buf[--len], buffer, idx++, maxlen);
240 }
241
242 /* append pad spaces up to given width */
243 if (flags & FLAGS_LEFT) {
244 while (idx - start_idx < width) {
245 out(' ', buffer, idx++, maxlen);
246 }
247 }
248
249 return idx;
250}
251
252
253/* internal itoa format */
254static size_t _ntoa_format(out_fct_type out, char *buffer, size_t idx, size_t maxlen, char *buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
255{
256 /* pad leading zeros */
257 if (!(flags & FLAGS_LEFT)) {
258 if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
259 width--;
260 }
261 while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
262 buf[len++] = '0';
263 }
264 while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
265 buf[len++] = '0';
266 }
267 }
268
269 /* handle hash */
270 if (flags & FLAGS_HASH) {
271 if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
272 len--;
273 if (len && (base == 16U)) {
274 len--;
275 }
276 }
277 if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
278 buf[len++] = 'x';
279 } else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
280 buf[len++] = 'X';
281 } else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
282 buf[len++] = 'b';
283 }
284 if (len < PRINTF_NTOA_BUFFER_SIZE) {
285 buf[len++] = '0';
286 }
287 }
288
289 if (len < PRINTF_NTOA_BUFFER_SIZE) {
290 if (negative) {
291 buf[len++] = '-';
292 } else if (flags & FLAGS_PLUS) {
293 /* ignore the space if the '+' exists */
294 buf[len++] = '+';
295 } else if (flags & FLAGS_SPACE) {
296 buf[len++] = ' ';
297 }
298 }
299
300 return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
301}
302
303
304/* internal itoa for 'long' type */
305static size_t _ntoa_long(out_fct_type out, char *buffer, size_t idx,
306 size_t maxlen, unsigned long value, bool negative,
307 unsigned long base, unsigned int prec,
308 unsigned int width, unsigned int flags)
309{
310 char buf[PRINTF_NTOA_BUFFER_SIZE];
311 size_t len = 0U;
312
313 /* no hash for 0 values */
314 if (!value) {
315 flags &= ~FLAGS_HASH;
316 }
317
318 /* write if precision != 0 and value is != 0 */
319 if (!(flags & FLAGS_PRECISION) || value) {
320 do {
321 const char digit = (char)(value % base);
322
323 buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
324 value /= base;
325 } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
326 }
327
328 return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative,
329 (unsigned int)base, prec, width, flags);
330}
331
332
333/* internal itoa for 'long long' type */
334#if defined(PRINTF_SUPPORT_LONG_LONG)
335static size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx,
336 size_t maxlen, unsigned long long value,
337 bool negative, unsigned long long base,
338 unsigned int prec, unsigned int width,
339 unsigned int flags)
340{
341 char buf[PRINTF_NTOA_BUFFER_SIZE];
342 size_t len = 0U;
343
344 /* no hash for 0 values */
345 if (!value) {
346 flags &= ~FLAGS_HASH;
347 }
348
349 /* write if precision != 0 and value is != 0 */
350 if (!(flags & FLAGS_PRECISION) || value) {
351 do {
352 const char digit = (char)(value % base);
353
354 buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
355 value /= base;
356 } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
357 }
358
359 return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative,
360 (unsigned int)base, prec, width, flags);
361}
362#endif /* PRINTF_SUPPORT_LONG_LONG */
363
364
365#if defined(PRINTF_SUPPORT_FLOAT)
366
367#if defined(PRINTF_SUPPORT_EXPONENTIAL)
368/*
369 * forward declaration so that _ftoa can switch to exp notation for
370 * values > PRINTF_MAX_FLOAT
371 */
372static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen,
373 double value, unsigned int prec, unsigned int width,
374 unsigned int flags);
375#endif
376
377
378/* internal ftoa for fixed decimal floating point */
379static size_t _ftoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen,
380 double value, unsigned int prec, unsigned int width,
381 unsigned int flags)
382{
383 char buf[PRINTF_FTOA_BUFFER_SIZE];
384 size_t len = 0U;
385 double diff = 0.0;
386
387 /* powers of 10 */
388 static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000,
389 1000000, 10000000, 100000000, 1000000000 };
390
391 /* test for special values */
392 if (value != value) {
393 return _out_rev(out, buffer, idx, maxlen, "nan", 3, width,
394 flags);
395 }
396
397 if (value < -DBL_MAX) {
398 return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width,
399 flags);
400 }
401
402 if (value > DBL_MAX) {
403 return _out_rev(out, buffer, idx, maxlen,
404 (flags & FLAGS_PLUS) ? "fni+" : "fni",
405 (flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
406 }
407
408 /*
409 * test for very large values
410 * standard printf behavior is to print EVERY whole number digit --
411 * which could be 100s of characters overflowing your buffers == bad
412 */
413 if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
414#if defined(PRINTF_SUPPORT_EXPONENTIAL)
415 return _etoa(out, buffer, idx, maxlen, value, prec, width,
416 flags);
417#else
418 return 0U;
419#endif
420 }
421
422 /* test for negative */
423 bool negative = false;
424
425 if (value < 0) {
426 negative = true;
427 value = 0 - value;
428 }
429
430 /* set default precision, if not set explicitly */
431 if (!(flags & FLAGS_PRECISION)) {
432 prec = PRINTF_DEFAULT_FLOAT_PRECISION;
433 }
434 /*
435 * limit precision to 9, cause a prec >= 10 can lead to overflow
436 * errors
437 */
438 while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
439 buf[len++] = '0';
440 prec--;
441 }
442
443 int whole = (int)value;
444 double tmp = (value - whole) * pow10[prec];
445 unsigned long frac = (unsigned long)tmp;
446
447 diff = tmp - frac;
448
449 if (diff > 0.5) {
450 ++frac;
451 /* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
452 if (frac >= pow10[prec]) {
453 frac = 0;
454 ++whole;
455 }
456 } else if (diff < 0.5) {
457 /* skip */
458 } else if ((frac == 0U) || (frac & 1U)) {
459 /* if halfway, round up if odd OR if last digit is 0 */
460 ++frac;
461 }
462
463 if (prec == 0U) {
464 diff = value - (double)whole;
465 if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
466 /*
467 * exactly 0.5 and ODD, then round up
468 * 1.5 -> 2, but 2.5 -> 2
469 */
470 ++whole;
471 }
472 } else {
473 unsigned int count = prec;
474
475 /* now do fractional part, as an unsigned number */
476 while (len < PRINTF_FTOA_BUFFER_SIZE) {
477 --count;
478 buf[len++] = (char)(48U + (frac % 10U));
479 frac /= 10U;
480 if (!frac) {
481 break;
482 }
483 }
484 /* add extra 0s */
485 while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
486 buf[len++] = '0';
487 }
488 if (len < PRINTF_FTOA_BUFFER_SIZE) {
489 /* add decimal */
490 buf[len++] = '.';
491 }
492 }
493
494 /* do whole part, number is reversed */
495 while (len < PRINTF_FTOA_BUFFER_SIZE) {
496 buf[len++] = (char)(48 + (whole % 10));
497 whole /= 10;
498 if (!whole) {
499 break;
500 }
501 }
502
503 /* pad leading zeros */
504 if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
505 if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
506 width--;
507 }
508 while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
509 buf[len++] = '0';
510 }
511 }
512
513 if (len < PRINTF_FTOA_BUFFER_SIZE) {
514 if (negative) {
515 buf[len++] = '-';
516 } else if (flags & FLAGS_PLUS) {
517 /* ignore the space if the '+' exists */
518 buf[len++] = '+';
519 } else if (flags & FLAGS_SPACE) {
520 buf[len++] = ' ';
521 }
522 }
523
524 return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
525}
526
527
528#if defined(PRINTF_SUPPORT_EXPONENTIAL)
529/*
530 * internal ftoa variant for exponential floating-point type, contributed by
531 * Martijn Jasperse <m.jasperse@gmail.com>
532 */
533static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen,
534 double value, unsigned int prec, unsigned int width,
535 unsigned int flags)
536{
537 /* check for NaN and special values */
538 if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
539 return _ftoa(out, buffer, idx, maxlen, value, prec, width,
540 flags);
541 }
542
543 /* determine the sign */
544 const bool negative = value < 0;
545
546 if (negative) {
547 value = -value;
548 }
549
550 /* default precision */
551 if (!(flags & FLAGS_PRECISION)) {
552 prec = PRINTF_DEFAULT_FLOAT_PRECISION;
553 }
554
555 /*
556 * determine the decimal exponent
557 * based on the algorithm by David Gay
558 * (https://www.ampl.com/netlib/fp/dtoa.c)
559 */
560 union {
561 uint64_t U;
562 double F;
563 } conv;
564
565 conv.F = value;
566 /* effectively log2 */
567 int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023;
568
569 /* drop the exponent so conv.F is now in [1,2) */
570 conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U);
571
572 /*
573 * now approximate log10 from the log2 integer part and an expansion
574 * of ln around 1.5
575 */
576 int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
577 /*
578 * now we want to compute 10^expval but we want to be sure it won't
579 * overflow
580 */
581 exp2 = (int)(expval * 3.321928094887362 + 0.5);
582 const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
583 const double z2 = z * z;
584
585 conv.U = (uint64_t)(exp2 + 1023) << 52U;
586 /*
587 * compute exp(z) using continued fractions, see
588 * https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
589 */
590 conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
591 /* correct for rounding errors */
592 if (value < conv.F) {
593 expval--;
594 conv.F /= 10;
595 }
596
597 /*
598 * the exponent format is "%+03d" and largest value is "307", so set
599 * aside 4-5 characters
600 */
601 unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
602
603 /*
604 * in "%g" mode, "prec" is the number of *significant figures* not
605 * decimals
606 */
607 if (flags & FLAGS_ADAPT_EXP) {
608 /* do we want to fall-back to "%f" mode? */
609 if ((value >= 1e-4) && (value < 1e6)) {
610 if ((int)prec > expval) {
611 prec = (unsigned int)((int)prec - expval - 1);
612 } else {
613 prec = 0;
614 }
615 /* make sure _ftoa respects precision */
616 flags |= FLAGS_PRECISION;
617 /* no characters in exponent */
618 minwidth = 0U;
619 expval = 0;
620 } else {
621 /* we use one sigfig for the whole part */
622 if ((prec > 0) && (flags & FLAGS_PRECISION)) {
623 --prec;
624 }
625 }
626 }
627
628 /* will everything fit? */
629 unsigned int fwidth = width;
630
631 if (width > minwidth) {
632 /*
633 * we didn't fall-back so subtract the characters required for
634 * the exponent
635 */
636 fwidth -= minwidth;
637 } else {
638 /* not enough characters, so go back to default sizing */
639 fwidth = 0U;
640 }
641 if ((flags & FLAGS_LEFT) && minwidth) {
642 /* if we're padding on the right, DON'T pad the floating part */
643 fwidth = 0U;
644 }
645
646 /* rescale the float value */
647 if (expval) {
648 value /= conv.F;
649 }
650
651 /* output the floating part */
652 const size_t start_idx = idx;
653
654 idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
655
656 /* output the exponent part */
657 if (minwidth) {
658 /* output the exponential symbol */
659 out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++,
660 maxlen);
661 /* output the exponent value */
662 idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS);
663 /* might need to right-pad spaces */
664 if (flags & FLAGS_LEFT) {
665 while (idx - start_idx < width) {
666 out(' ', buffer, idx++, maxlen);
667 }
668 }
669 }
670 return idx;
671}
672#endif /* PRINTF_SUPPORT_EXPONENTIAL */
673#endif /* PRINTF_SUPPORT_FLOAT */
674
675
676/* internal vsnprintf */
677static int _vsnprintf(out_fct_type out, char *buffer, const size_t maxlen,
678 const char *format, va_list va)
679{
680 unsigned int flags, width, precision, n;
681 size_t idx = 0U;
682
683 if (!buffer) {
684 /* use null output function */
685 out = _out_null;
686 }
687
688 while (*format) {
689 /* format specifier? %[flags][width][.precision][length] */
690 if (*format != '%') {
691 /* no */
692 out(*format, buffer, idx++, maxlen);
693 format++;
694 continue;
695 } else {
696 /* yes, evaluate it */
697 format++;
698 }
699
700 /* evaluate flags */
701 flags = 0U;
702 do {
703 switch (*format) {
704 case '0':
705 flags |= FLAGS_ZEROPAD; format++; n = 1U;
706 break;
707 case '-':
708 flags |= FLAGS_LEFT; format++; n = 1U;
709 break;
710 case '+':
711 flags |= FLAGS_PLUS; format++; n = 1U;
712 break;
713 case ' ':
714 flags |= FLAGS_SPACE; format++; n = 1U;
715 break;
716 case '#':
717 flags |= FLAGS_HASH; format++; n = 1U;
718 break;
719 default:
720 n = 0U;
721 break;
722 }
723 } while (n);
724
725 /* evaluate width field */
726 width = 0U;
727 if (_is_digit(*format)) {
728 width = _atoi(&format);
729 } else if (*format == '*') {
730 const int w = va_arg(va, int);
731
732 if (w < 0) {
733 flags |= FLAGS_LEFT; /* reverse padding */
734 width = (unsigned int)-w;
735 } else {
736 width = (unsigned int)w;
737 }
738 format++;
739 }
740
741 /* evaluate precision field */
742 precision = 0U;
743 if (*format == '.') {
744 flags |= FLAGS_PRECISION;
745 format++;
746 if (_is_digit(*format)) {
747 precision = _atoi(&format);
748 } else if (*format == '*') {
749 const int prec = (int)va_arg(va, int);
750
751 precision = prec > 0 ? (unsigned int)prec : 0U;
752 format++;
753 }
754 }
755
756 /* evaluate length field */
757 switch (*format) {
758 case 'l':
759 flags |= FLAGS_LONG;
760 format++;
761 if (*format == 'l') {
762 flags |= FLAGS_LONG_LONG;
763 format++;
764 }
765 break;
766 case 'h':
767 flags |= FLAGS_SHORT;
768 format++;
769 if (*format == 'h') {
770 flags |= FLAGS_CHAR;
771 format++;
772 }
773 break;
774#if defined(PRINTF_SUPPORT_PTRDIFF_T)
775 case 't':
776 flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
777 format++;
778 break;
779#endif
780 case 'j':
781 flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
782 format++;
783 break;
784 case 'z':
785 flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
786 format++;
787 break;
788 default:
789 break;
790 }
791
792 /* evaluate specifier */
793 switch (*format) {
794 case 'd':
795 case 'i':
796 case 'u':
797 case 'x':
798 case 'X':
799 case 'o':
800 case 'b': {
801 /* set the base */
802 unsigned int base;
803
804 if (*format == 'x' || *format == 'X') {
805 base = 16U;
806 } else if (*format == 'o') {
807 base = 8U;
808 } else if (*format == 'b') {
809 base = 2U;
810 } else {
811 base = 10U;
812 /* no hash for dec format */
813 flags &= ~FLAGS_HASH;
814 }
815 /* uppercase */
816 if (*format == 'X') {
817 flags |= FLAGS_UPPERCASE;
818 }
819
820 /* no plus or space flag for u, x, X, o, b */
821 if ((*format != 'i') && (*format != 'd')) {
822 flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
823 }
824
825 /* ignore '0' flag when precision is given */
826 if (flags & FLAGS_PRECISION) {
827 flags &= ~FLAGS_ZEROPAD;
828 }
829
830 /* convert the integer */
831 if ((*format == 'i') || (*format == 'd')) {
832 /* signed */
833 if (flags & FLAGS_LONG_LONG) {
834#if defined(PRINTF_SUPPORT_LONG_LONG)
835 const long long value = va_arg(va, long long);
836
837 idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
838#endif
839 } else if (flags & FLAGS_LONG) {
840 const long value = va_arg(va, long);
841
842 idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
843 } else {
844 const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int);
845
846 idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
847 }
848 } else {
849 /* unsigned */
850 if (flags & FLAGS_LONG_LONG) {
851#if defined(PRINTF_SUPPORT_LONG_LONG)
852 idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
853#endif
854 } else if (flags & FLAGS_LONG) {
855 idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
856 } else {
857 const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);
858
859 idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
860 }
861 }
862 format++;
863 break;
864 }
865#if defined(PRINTF_SUPPORT_FLOAT)
866 case 'f':
867 case 'F':
868 if (*format == 'F') {
869 flags |= FLAGS_UPPERCASE;
870 }
871
872 idx = _ftoa(out, buffer, idx, maxlen,
873 va_arg(va, double), precision, width, flags);
874 format++;
875 break;
876#if defined(PRINTF_SUPPORT_EXPONENTIAL)
877 case 'e':
878 case 'E':
879 case 'g':
880 case 'G':
881 if ((*format == 'g') || (*format == 'G')) {
882 flags |= FLAGS_ADAPT_EXP;
883 }
884
885 if ((*format == 'E') || (*format == 'G')) {
886 flags |= FLAGS_UPPERCASE;
887 }
888
889 idx = _etoa(out, buffer, idx, maxlen,
890 va_arg(va, double), precision, width, flags);
891 format++;
892 break;
893#endif /* PRINTF_SUPPORT_EXPONENTIAL */
894#endif /* PRINTF_SUPPORT_FLOAT */
895 case 'c': {
896 unsigned int l = 1U;
897 /* pre padding */
898 if (!(flags & FLAGS_LEFT)) {
899 while (l++ < width) {
900 out(' ', buffer, idx++, maxlen);
901 }
902 }
903 /* char output */
904 out((char)va_arg(va, int), buffer, idx++, maxlen);
905 /* post padding */
906 if (flags & FLAGS_LEFT) {
907 while (l++ < width) {
908 out(' ', buffer, idx++, maxlen);
909 }
910 }
911 format++;
912 break;
913 }
914
915 case 's': {
916 const char *p = va_arg(va, char*);
917 unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
918 /* pre padding */
919 if (flags & FLAGS_PRECISION) {
920 l = (l < precision ? l : precision);
921 }
922 if (!(flags & FLAGS_LEFT)) {
923 while (l++ < width) {
924 out(' ', buffer, idx++, maxlen);
925 }
926 }
927 /* string output */
928 while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
929 out(*(p++), buffer, idx++, maxlen);
930 }
931 /* post padding */
932 if (flags & FLAGS_LEFT) {
933 while (l++ < width) {
934 out(' ', buffer, idx++, maxlen);
935 }
936 }
937 format++;
938 break;
939 }
940
941 case 'p': {
942 width = sizeof(void *) * 2U;
943 flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
944#if defined(PRINTF_SUPPORT_LONG_LONG)
945 const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
946
947 if (is_ll) {
948 idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void *), false, 16U, precision, width, flags);
949 } else {
950#endif
951 idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void *)), false, 16U, precision, width, flags);
952#if defined(PRINTF_SUPPORT_LONG_LONG)
953 }
954#endif
955 format++;
956 break;
957 }
958
959 case '%':
960 out('%', buffer, idx++, maxlen);
961 format++;
962 break;
963
964 default:
965 out(*format, buffer, idx++, maxlen);
966 format++;
967 break;
968 }
969 }
970
971 /* termination */
972 out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
973
974 /* return written chars without terminating \0 */
975 return (int)idx;
976}
977
978
979/*****************************************************************************/
980
981int printf_(const char *format, ...)
982{
983 va_list va;
984
985 va_start(va, format);
986 char buffer[1];
987 const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
988
989 va_end(va);
990 return ret;
991}
992
993
994int sprintf_(char *buffer, const char *format, ...)
995{
996 va_list va;
997
998 va_start(va, format);
999 const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);
1000
1001 va_end(va);
1002 return ret;
1003}
1004
1005
1006int snprintf_(char *buffer, size_t count, const char *format, ...)
1007{
1008 va_list va;
1009
1010 va_start(va, format);
1011 const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
1012
1013 va_end(va);
1014 return ret;
1015}
1016
1017
1018int vprintf_(const char *format, va_list va)
1019{
1020 char buffer[1];
1021
1022 return _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
1023}
1024
1025
1026int vsnprintf_(char *buffer, size_t count, const char *format, va_list va)
1027{
1028 return _vsnprintf(_out_buffer, buffer, count, format, va);
1029}
1030
1031
1032int fctprintf(void (*out)(char character, void *arg), void *arg,
1033 const char *format, ...)
1034{
1035 va_list va;
1036
1037 va_start(va, format);
1038 const out_fct_wrap_type out_fct_wrap = { out, arg };
1039 const int ret = _vsnprintf(_out_fct, (char *)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va);
1040
1041 va_end(va);
1042 return ret;
1043}