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