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