Merge pull request #2616 from jmichelp/fix14b
[RRG-proxmark3.git] / armsrc / nprintf.c
blob458bfd8ddd488583df791fb57f022a3260737cd0
1 //-----------------------------------------------------------------------------
2 // Borrowed initially from https://github.com/mpaland/printf
3 // Copyright (C) Marco Paland 2014-2019, PALANDesign Hannover, Germany
4 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // See LICENSE.txt for the text of the license.
17 //-----------------------------------------------------------------------------
18 // Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on
19 // embedded systems with a very limited resources. These routines are thread
20 // safe and reentrant!
21 // Use this instead of the bloated standard/newlib printf cause these use
22 // malloc for printf (and may not be thread safe).
23 //-----------------------------------------------------------------------------
25 #include "nprintf.h"
28 // define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
29 // printf_config.h header file
30 // default: undefined
31 #ifdef PRINTF_INCLUDE_CONFIG_H
32 #include "printf_config.h"
33 #endif
36 // 'ntoa' conversion buffer size, this must be big enough to hold one converted
37 // numeric number including padded zeros (dynamically created on stack)
38 // default: 32 byte
39 #ifndef PRINTF_NTOA_BUFFER_SIZE
40 #define PRINTF_NTOA_BUFFER_SIZE 32U
41 #endif
43 // 'ftoa' conversion buffer size, this must be big enough to hold one converted
44 // float number including padded zeros (dynamically created on stack)
45 // default: 32 byte
46 #ifndef PRINTF_FTOA_BUFFER_SIZE
47 #define PRINTF_FTOA_BUFFER_SIZE 32U
48 #endif
50 // support for the floating point type (%f)
51 // default: activated
52 #ifndef PRINTF_DISABLE_SUPPORT_FLOAT
53 #define PRINTF_SUPPORT_FLOAT
54 #endif
56 // support for exponential floating point notation (%e/%g)
57 // default: activated
58 #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
59 #define PRINTF_SUPPORT_EXPONENTIAL
60 #endif
62 // define the default floating point precision
63 // default: 6 digits
64 #ifndef PRINTF_DEFAULT_FLOAT_PRECISION
65 #define PRINTF_DEFAULT_FLOAT_PRECISION 6U
66 #endif
68 // define the largest float suitable to print with %f
69 // default: 1e9
70 #ifndef PRINTF_MAX_FLOAT
71 #define PRINTF_MAX_FLOAT 1e9
72 #endif
74 // support for the long long types (%llu or %p)
75 // default: activated
76 #ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
77 #define PRINTF_SUPPORT_LONG_LONG
78 #endif
80 // support for the ptrdiff_t type (%t)
81 // ptrdiff_t is normally defined in <stddef.h> as long or long long type
82 // default: activated
83 #ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
84 #define PRINTF_SUPPORT_PTRDIFF_T
85 #endif
87 ///////////////////////////////////////////////////////////////////////////////
89 // internal flag definitions
90 #define FLAGS_ZEROPAD (1U << 0U)
91 #define FLAGS_LEFT (1U << 1U)
92 #define FLAGS_PLUS (1U << 2U)
93 #define FLAGS_SPACE (1U << 3U)
94 #define FLAGS_HASH (1U << 4U)
95 #define FLAGS_UPPERCASE (1U << 5U)
96 #define FLAGS_CHAR (1U << 6U)
97 #define FLAGS_SHORT (1U << 7U)
98 #define FLAGS_LONG (1U << 8U)
99 #define FLAGS_LONG_LONG (1U << 9U)
100 #define FLAGS_PRECISION (1U << 10U)
101 #define FLAGS_ADAPT_EXP (1U << 11U)
104 // import float.h for DBL_MAX
105 #if defined(PRINTF_SUPPORT_FLOAT)
106 #include <float.h>
107 #endif
110 // output function type
111 typedef void (*out_fct_type)(char character, void *buffer, size_t idx, size_t maxlen);
114 // wrapper (used as buffer) for output function type
115 typedef struct {
116 void (*fct)(char character, void *arg);
117 void *arg;
118 } out_fct_wrap_type;
121 // internal buffer output
122 static void _out_buffer(char character, void *buffer, size_t idx, size_t maxlen) {
123 if (idx < maxlen) {
124 ((char *)buffer)[idx] = character;
129 // internal null output
130 static void _out_null(char character, void *buffer, size_t idx, size_t maxlen) {
131 (void)character;
132 (void)buffer;
133 (void)idx;
134 (void)maxlen;
138 // internal _putchar wrapper
139 static void _out_char(char character, void *buffer, size_t idx, size_t maxlen) {
140 (void)buffer;
141 (void)idx;
142 (void)maxlen;
143 if (character) {
144 _putchar(character);
149 // internal output function wrapper
150 static void _out_fct(char character, void *buffer, size_t idx, size_t maxlen) {
151 (void)idx;
152 (void)maxlen;
153 if (character) {
154 // buffer is the output fct pointer
155 ((out_fct_wrap_type *)buffer)->fct(character, ((out_fct_wrap_type *)buffer)->arg);
160 // internal secure strlen
161 // \return The length of the string (excluding the terminating 0) limited by 'maxsize'
162 static unsigned int _strnlen_s(const char *str, size_t maxsize) {
163 const char *s;
164 for (s = str; *s && maxsize--; ++s);
165 return (unsigned int)(s - str);
169 // internal test if char is a digit (0-9)
170 // \return true if char is a digit
171 static bool _is_digit(char ch) {
172 return (ch >= '0') && (ch <= '9');
176 // internal ASCII string to unsigned int conversion
177 static unsigned int _atoi(const char **str) {
178 unsigned int i = 0U;
179 while (_is_digit(**str)) {
180 i = i * 10U + (unsigned int)(*((*str)++) - '0');
182 return i;
186 // output the specified string in reverse, taking care of any zero-padding
187 static 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) {
188 const size_t start_idx = idx;
190 // pad spaces up to given width
191 if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
192 for (size_t i = len; i < width; i++) {
193 out(' ', buffer, idx++, maxlen);
197 // reverse string
198 while (len) {
199 out(buf[--len], buffer, idx++, maxlen);
202 // append pad spaces up to given width
203 if (flags & FLAGS_LEFT) {
204 while (idx - start_idx < width) {
205 out(' ', buffer, idx++, maxlen);
209 return idx;
213 // internal itoa format
214 static 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) {
215 // pad leading zeros
216 if (!(flags & FLAGS_LEFT)) {
217 if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
218 width--;
220 while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
221 buf[len++] = '0';
223 while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
224 buf[len++] = '0';
228 // handle hash
229 if (flags & FLAGS_HASH) {
230 if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
231 len--;
232 if (len && (base == 16U)) {
233 len--;
236 if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
237 buf[len++] = 'x';
238 } else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
239 buf[len++] = 'X';
240 } else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
241 buf[len++] = 'b';
243 if (len < PRINTF_NTOA_BUFFER_SIZE) {
244 buf[len++] = '0';
248 if (len < PRINTF_NTOA_BUFFER_SIZE) {
249 if (negative) {
250 buf[len++] = '-';
251 } else if (flags & FLAGS_PLUS) {
252 buf[len++] = '+'; // ignore the space if the '+' exists
253 } else if (flags & FLAGS_SPACE) {
254 buf[len++] = ' ';
258 return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
262 // internal itoa for 'long' type
263 static size_t _ntoa_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) {
264 char buf[PRINTF_NTOA_BUFFER_SIZE];
265 size_t len = 0U;
267 // no hash for 0 values
268 if (!value) {
269 flags &= ~FLAGS_HASH;
272 // write if precision != 0 and value is != 0
273 if (!(flags & FLAGS_PRECISION) || value) {
274 do {
275 const char digit = (char)(value % base);
276 buf[len++] = digit < 10 ? '0' + digit : ((flags & FLAGS_UPPERCASE) ? 'A' : 'a') + digit - 10;
277 value /= base;
278 } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
281 return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
285 // internal itoa for 'long long' type
286 #if defined(PRINTF_SUPPORT_LONG_LONG)
287 static size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) {
288 char buf[PRINTF_NTOA_BUFFER_SIZE];
289 size_t len = 0U;
291 // no hash for 0 values
292 if (!value) {
293 flags &= ~FLAGS_HASH;
296 // write if precision != 0 and value is != 0
297 if (!(flags & FLAGS_PRECISION) || value) {
298 do {
299 const char digit = (char)(value % base);
300 buf[len++] = digit < 10 ? '0' + digit : ((flags & FLAGS_UPPERCASE) ? 'A' : 'a') + digit - 10;
301 value /= base;
302 } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
305 return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
307 #endif // PRINTF_SUPPORT_LONG_LONG
310 #if defined(PRINTF_SUPPORT_FLOAT)
312 #if defined(PRINTF_SUPPORT_EXPONENTIAL)
313 // forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
314 static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags);
315 #endif
318 // internal ftoa for fixed decimal floating point
319 static size_t _ftoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) {
320 char buf[PRINTF_FTOA_BUFFER_SIZE];
321 size_t len = 0U;
322 double diff = 0.0;
324 // powers of 10
325 static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
327 // test for special values
328 if (value != value)
329 return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags);
330 if (value < -DBL_MAX)
331 return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags);
332 if (value > DBL_MAX)
333 return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
335 // test for very large values
336 // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
337 if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
338 #if defined(PRINTF_SUPPORT_EXPONENTIAL)
339 return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
340 #else
341 return 0U;
342 #endif
345 // test for negative
346 bool negative = false;
347 if (value < 0) {
348 negative = true;
349 value = 0 - value;
352 // set default precision, if not set explicitly
353 if (!(flags & FLAGS_PRECISION)) {
354 prec = PRINTF_DEFAULT_FLOAT_PRECISION;
356 // limit precision to 9, cause a prec >= 10 can lead to overflow errors
357 while ((len < PRINTF_FTOA_BUFFER_SIZE)) {
358 buf[len++] = '0';
359 prec--;
362 if (prec > 9U) {
363 prec = 9U;
366 int whole = (int)value;
367 double tmp = (value - whole) * pow10[prec];
368 unsigned long frac = (unsigned long)tmp;
369 diff = tmp - frac;
371 if (diff > 0.5) {
372 ++frac;
373 // handle rollover, e.g. case 0.99 with prec 1 is 1.0
374 if (frac >= pow10[prec]) {
375 frac = 0;
376 ++whole;
378 } else if (diff < 0.5) {
379 } else if ((frac == 0U) || (frac & 1U)) {
380 // if halfway, round up if odd OR if last digit is 0
381 ++frac;
384 if (prec == 0U) {
385 diff = value - (double)whole;
386 if (((diff < 0.5) == false) || (whole & 1)) {
387 // exactly 0.5 and ODD, then round up
388 // 1.5 -> 2, but 2.5 -> 2
389 ++whole;
391 } else {
392 unsigned int count = prec;
393 // now do fractional part, as an unsigned number
394 while (len < PRINTF_FTOA_BUFFER_SIZE) {
395 --count;
396 buf[len++] = (char)(48U + (frac % 10U));
397 if (!(frac /= 10U)) {
398 break;
401 // add extra 0s
402 while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
403 buf[len++] = '0';
405 if (len < PRINTF_FTOA_BUFFER_SIZE) {
406 // add decimal
407 buf[len++] = '.';
411 // do whole part, number is reversed
412 while (len < PRINTF_FTOA_BUFFER_SIZE) {
413 buf[len++] = (char)(48 + (whole % 10));
414 if (!(whole /= 10)) {
415 break;
419 // pad leading zeros
420 if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
421 if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
422 width--;
424 while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
425 buf[len++] = '0';
429 if (len < PRINTF_FTOA_BUFFER_SIZE) {
430 if (negative) {
431 buf[len++] = '-';
432 } else if (flags & FLAGS_PLUS) {
433 buf[len++] = '+'; // ignore the space if the '+' exists
434 } else if (flags & FLAGS_SPACE) {
435 buf[len++] = ' ';
439 return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
443 #if defined(PRINTF_SUPPORT_EXPONENTIAL)
444 // internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
445 static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) {
446 // check for NaN and special values
447 if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
448 return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);
451 // determine the sign
452 const bool negative = value < 0;
453 if (negative) {
454 value = -value;
457 // default precision
458 if (!(flags & FLAGS_PRECISION)) {
459 prec = PRINTF_DEFAULT_FLOAT_PRECISION;
462 // determine the decimal exponent
463 // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
464 union {
465 uint64_t U;
466 double F;
467 } conv;
469 conv.F = value;
470 int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2
471 conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
472 // now approximate log10 from the log2 integer part and an expansion of ln around 1.5
473 int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
474 // now we want to compute 10^expval but we want to be sure it won't overflow
475 exp2 = (int)(expval * 3.321928094887362 + 0.5);
476 const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
477 const double z2 = z * z;
478 conv.U = (uint64_t)(exp2 + 1023) << 52U;
479 // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
480 conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
481 // correct for rounding errors
482 if (value < conv.F) {
483 expval--;
484 conv.F /= 10;
487 // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
488 unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
490 // in "%g" mode, "prec" is the number of *significant figures* not decimals
491 if (flags & FLAGS_ADAPT_EXP) {
492 // do we want to fall-back to "%f" mode?
493 if ((value >= 1e-4) && (value < 1e6)) {
494 if ((int)prec > expval) {
495 prec = (unsigned)((int)prec - expval - 1);
496 } else {
497 prec = 0;
499 flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
500 // no characters in exponent
501 minwidth = 0U;
502 expval = 0;
503 } else {
504 // we use one sigfig for the whole part
505 if ((prec > 0) && (flags & FLAGS_PRECISION)) {
506 --prec;
511 // will everything fit?
512 unsigned int fwidth = width;
513 if (width > minwidth) {
514 // we didn't fall-back so subtract the characters required for the exponent
515 fwidth -= minwidth;
516 } else {
517 // not enough characters, so go back to default sizing
518 fwidth = 0U;
520 if ((flags & FLAGS_LEFT) && minwidth) {
521 // if we're padding on the right, DON'T pad the floating part
522 fwidth = 0U;
525 // rescale the float value
526 if (expval) {
527 value /= conv.F;
530 // output the floating part
531 const size_t start_idx = idx;
532 idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
534 // output the exponent part
535 if (minwidth) {
536 // output the exponential symbol
537 out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);
538 // output the exponent value
539 idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth - 1, FLAGS_ZEROPAD | FLAGS_PLUS);
540 // might need to right-pad spaces
541 if (flags & FLAGS_LEFT) {
542 while (idx - start_idx < width) out(' ', buffer, idx++, maxlen);
545 return idx;
547 #endif // PRINTF_SUPPORT_EXPONENTIAL
548 #endif // PRINTF_SUPPORT_FLOAT
551 // internal vsnprintf
552 static int _vsnprintf(out_fct_type out, char *buffer, const size_t maxlen, const char *format, va_list va) {
553 unsigned int flags, width, precision, n;
554 size_t idx = 0U;
556 if (!buffer) {
557 // use null output function
558 out = _out_null;
561 while (*format) {
562 // format specifier? %[flags][width][.precision][length]
563 if (*format != '%') {
564 // no
565 out(*format, buffer, idx++, maxlen);
566 format++;
567 continue;
568 } else {
569 // yes, evaluate it
570 format++;
573 // evaluate flags
574 flags = 0U;
575 do {
576 switch (*format) {
577 case '0':
578 flags |= FLAGS_ZEROPAD;
579 format++;
580 n = 1U;
581 break;
582 case '-':
583 flags |= FLAGS_LEFT;
584 format++;
585 n = 1U;
586 break;
587 case '+':
588 flags |= FLAGS_PLUS;
589 format++;
590 n = 1U;
591 break;
592 case ' ':
593 flags |= FLAGS_SPACE;
594 format++;
595 n = 1U;
596 break;
597 case '#':
598 flags |= FLAGS_HASH;
599 format++;
600 n = 1U;
601 break;
602 default :
603 n = 0U;
604 break;
606 } while (n);
608 // evaluate width field
609 width = 0U;
610 if (_is_digit(*format)) {
611 width = _atoi(&format);
612 } else if (*format == '*') {
613 const int w = va_arg(va, int);
614 if (w < 0) {
615 flags |= FLAGS_LEFT; // reverse padding
616 width = (unsigned int) - w;
617 } else {
618 width = (unsigned int)w;
620 format++;
623 // evaluate precision field
624 precision = 0U;
625 if (*format == '.') {
626 flags |= FLAGS_PRECISION;
627 format++;
628 if (_is_digit(*format)) {
629 precision = _atoi(&format);
630 } else if (*format == '*') {
631 const int prec = (int)va_arg(va, int);
632 precision = prec > 0 ? (unsigned int)prec : 0U;
633 format++;
637 // evaluate length field
638 switch (*format) {
639 case 'l' :
640 flags |= FLAGS_LONG;
641 format++;
642 if (*format == 'l') {
643 flags |= FLAGS_LONG_LONG;
644 format++;
646 break;
647 case 'h' :
648 flags |= FLAGS_SHORT;
649 format++;
650 if (*format == 'h') {
651 flags |= FLAGS_CHAR;
652 format++;
654 break;
655 #if defined(PRINTF_SUPPORT_PTRDIFF_T)
656 case 't' :
657 flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
658 format++;
659 break;
660 #endif
661 case 'j' :
662 flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
663 format++;
664 break;
665 case 'z' :
666 flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
667 format++;
668 break;
669 default :
670 break;
673 // evaluate specifier
674 switch (*format) {
675 case 'd' :
676 case 'i' :
677 case 'u' :
678 case 'x' :
679 case 'X' :
680 case 'o' :
681 case 'b' : {
682 // set the base
683 unsigned int base;
684 if (*format == 'x' || *format == 'X') {
685 base = 16U;
686 } else if (*format == 'o') {
687 base = 8U;
688 } else if (*format == 'b') {
689 base = 2U;
690 } else {
691 base = 10U;
692 flags &= ~FLAGS_HASH; // no hash for dec format
694 // uppercase
695 if (*format == 'X') {
696 flags |= FLAGS_UPPERCASE;
699 // no plus or space flag for u, x, X, o, b
700 if ((*format != 'i') && (*format != 'd')) {
701 flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
704 // ignore '0' flag when precision is given
705 if (flags & FLAGS_PRECISION) {
706 flags &= ~FLAGS_ZEROPAD;
709 // convert the integer
710 if ((*format == 'i') || (*format == 'd')) {
711 // signed
712 if (flags & FLAGS_LONG_LONG) {
713 #if defined(PRINTF_SUPPORT_LONG_LONG)
714 const long long value = va_arg(va, long long);
715 idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
716 #endif
717 } else if (flags & FLAGS_LONG) {
718 const long value = va_arg(va, long);
719 idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
720 } else {
721 const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int);
722 idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
724 } else {
725 // unsigned
726 if (flags & FLAGS_LONG_LONG) {
727 #if defined(PRINTF_SUPPORT_LONG_LONG)
728 idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
729 #endif
730 } else if (flags & FLAGS_LONG) {
731 idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
732 } else {
733 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);
734 idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
737 format++;
738 break;
740 #if defined(PRINTF_SUPPORT_FLOAT)
741 case 'f' :
742 case 'F' :
743 if (*format == 'F') flags |= FLAGS_UPPERCASE;
744 idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
745 format++;
746 break;
747 #if defined(PRINTF_SUPPORT_EXPONENTIAL)
748 case 'e':
749 case 'E':
750 case 'g':
751 case 'G':
752 if ((*format == 'g') || (*format == 'G')) flags |= FLAGS_ADAPT_EXP;
753 if ((*format == 'E') || (*format == 'G')) flags |= FLAGS_UPPERCASE;
754 idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
755 format++;
756 break;
757 #endif // PRINTF_SUPPORT_EXPONENTIAL
758 #endif // PRINTF_SUPPORT_FLOAT
759 case 'c' : {
760 unsigned int l = 1U;
761 // pre padding
762 if (!(flags & FLAGS_LEFT)) {
763 while (l++ < width) {
764 out(' ', buffer, idx++, maxlen);
767 // char output
768 out((char)va_arg(va, int), buffer, idx++, maxlen);
769 // post padding
770 if (flags & FLAGS_LEFT) {
771 while (l++ < width) {
772 out(' ', buffer, idx++, maxlen);
775 format++;
776 break;
779 case 's' : {
780 const char *p = va_arg(va, char *);
781 unsigned int l = _strnlen_s(p, precision ? precision : (size_t) - 1);
782 // pre padding
783 if (flags & FLAGS_PRECISION) {
784 l = (l < precision ? l : precision);
786 if (!(flags & FLAGS_LEFT)) {
787 while (l++ < width) {
788 out(' ', buffer, idx++, maxlen);
791 // string output
792 while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
793 out(*(p++), buffer, idx++, maxlen);
795 // post padding
796 if (flags & FLAGS_LEFT) {
797 while (l++ < width) {
798 out(' ', buffer, idx++, maxlen);
801 format++;
802 break;
805 case 'p' : {
806 width = sizeof(void *) * 2U;
807 flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
808 #if defined(PRINTF_SUPPORT_LONG_LONG)
809 const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
810 if (is_ll) {
811 idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void *), false, 16U, precision, width, flags);
812 } else {
813 #endif
814 idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void *)), false, 16U, precision, width, flags);
815 #if defined(PRINTF_SUPPORT_LONG_LONG)
817 #endif
818 format++;
819 break;
822 case '%' :
823 out('%', buffer, idx++, maxlen);
824 format++;
825 break;
827 default :
828 out(*format, buffer, idx++, maxlen);
829 format++;
830 break;
834 // termination
835 out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
837 // return written chars without terminating \0
838 return (int)idx;
842 ///////////////////////////////////////////////////////////////////////////////
844 int printf_(const char *format, ...) {
845 va_list va;
846 va_start(va, format);
847 char buffer[1];
848 const int ret = _vsnprintf(_out_char, buffer, (size_t) - 1, format, va);
849 va_end(va);
850 return ret;
854 int sprintf_(char *buffer, const char *format, ...) {
855 va_list va;
856 va_start(va, format);
857 const int ret = _vsnprintf(_out_buffer, buffer, (size_t) - 1, format, va);
858 va_end(va);
859 return ret;
863 int snprintf_(char *buffer, size_t count, const char *format, ...) {
864 va_list va;
865 va_start(va, format);
866 const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
867 va_end(va);
868 return ret;
872 int vprintf_(const char *format, va_list va) {
873 char buffer[1];
874 return _vsnprintf(_out_char, buffer, (size_t) - 1, format, va);
878 int vsnprintf_(char *buffer, size_t count, const char *format, va_list va) {
879 return _vsnprintf(_out_buffer, buffer, count, format, va);
883 int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...) {
884 va_list va;
885 va_start(va, format);
886 const out_fct_wrap_type out_fct_wrap = { out, arg };
887 const int ret = _vsnprintf(_out_fct, (char *)(uintptr_t)&out_fct_wrap, (size_t) - 1, format, va);
888 va_end(va);
889 return ret;