4 * This file is part of BeRTOS.
6 * Bertos 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 2 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 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 * As a special exception, you may use this file as part of a free software
21 * library without restriction. Specifically, if other files instantiate
22 * templates or use macros or inline functions from this file, or you compile
23 * this file and link it with other files to produce an executable, this
24 * file does not by itself cause the resulting executable to be covered by
25 * the GNU General Public License. This exception does not however
26 * invalidate any other reasons why the executable file might be covered by
27 * the GNU General Public License.
29 * Copyright 2003, 2004, 2005, 2008 Develer S.r.l. (http://www.develer.com/)
34 * \brief Basic "printf", "sprintf" and "fprintf" formatter.
36 * This module is 100% reentrant and can be adapted to user-defined routines
37 * that needs formatters with special properties like different output
38 * channels or new format specifiers.
40 * To reduce size in applications not using real numbers or long integers
41 * the formatter may be compiled to exclude certain parts. This is
42 * controlled by giving a -D option a compilation time:
45 * -D CONFIG_PRINTF=PRINTF_FULL Full ANSI printf formatter, with some C99 extensions
46 * -D CONFIG_PRINTF=PRINTF_NOFLOAT Exclude support for floats
47 * -D CONFIG_PRINTF=PRINTF_REDUCED Simplified formatter (see below)
48 * -D CONFIG_PRINTF=PRINTF_NOMODIFIERS Exclude "l", "z" and "h" modifiers in reduced version
49 * -D CONFIG_PRINTF=PRINTF_DISABLED No formatter at all
52 * Code size on AVR4 with GCC 3.4.1 (-O2):
53 * \li PRINTF_FULL 2912byte (0xB60)
54 * \li PRINTF_NOFLOAT 1684byte (0x694)
55 * \li PRINTF_REDUCED 924byte (0x39C)
56 * \li PRINTF_NOMODIFIERS 416byte (0x1A0)
58 * Code/data size in words on DSP56K with CodeWarrior 6.0:
59 * \li PRINTF_FULL 1493/45
60 * \li PRINTF_NOFLOAT 795/45
61 * \li PRINTF_REDUCED 482/0
62 * \li PRINTF_NOMODIFIERS 301/0
64 * The reduced version of formatter is suitable when program size is critical
65 * rather than formatting power. This routine uses less than 20 bytes of
66 * stack space which makes it practical even in systems with less than 256
69 * The only formatting specifiers supported by the reduced formatter are:
71 * %% %c %s %d %o %x %X and %hd %ho %hx %hX %ld %lo %lx %lX
74 * It means that real variables are not supported as well as field
75 * width and precision arguments.
81 #include "cfg/cfg_formatwr.h" /* CONFIG_ macros */
82 #include <cfg/debug.h> /* ASSERT */
85 #include <mware/hex.h>
87 #ifndef CONFIG_PRINTF_N_FORMATTER
88 /** Disable the arcane %n formatter. */
89 #define CONFIG_PRINTF_N_FORMATTER 0
92 #ifndef CONFIG_PRINTF_OCTAL_FORMATTER
93 /** Disable the %o formatter. */
94 #define CONFIG_PRINTF_OCTAL_FORMATTER 0
97 /* True if we must keep a count of the number of characters we print. */
98 #define CONFIG_PRINTF_COUNT_CHARS (CONFIG_PRINTF_RETURN_COUNT || CONFIG_PRINTF_N_FORMATTER)
102 #if CONFIG_PRINTF > PRINTF_NOFLOAT
105 /* Maximum precision for floating point values */
106 typedef long double max_float_t;
108 #if CONFIG_FRMWRI_BUFSIZE
109 #define FRMWRI_BUFSIZE CONFIG_FRMWRI_BUFSIZE
111 /* Conservative estimate. Max float is 3.40282e+038, so %f (but not %e or %g) must have
112 * space for: sign + all 38 digits + '.' + 6 decimal digits (default)
113 * Use a high value to avoid unexpected buffer overflows.
115 #define FRMWRI_BUFSIZE 134
118 #if CONFIG_FRMWRI_BUFSIZE
119 #define FRMWRI_BUFSIZE CONFIG_FRMWRI_BUFSIZE
122 * Conservative estimate. Should be (probably) 12 (which is the size necessary
123 * to represent (2^32-1) in octal plus the sign bit.
125 #define FRMWRI_BUFSIZE 16
129 /* Probably useful for fancy microcontrollers such as the PIC, nobody knows. */
130 #ifndef MEM_ATTRIBUTE
131 #define MEM_ATTRIBUTE
134 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
135 #define IS_SHORT (h_modifier || (sizeof(int) == 2 && !l_modifier))
137 #define IS_SHORT (sizeof(int) == 2)
138 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
141 #if CONFIG_PRINTF > PRINTF_NOFLOAT
143 static char *float_conversion(MEM_ATTRIBUTE max_float_t value,
144 MEM_ATTRIBUTE short nr_of_digits,
145 MEM_ATTRIBUTE char *buf,
146 MEM_ATTRIBUTE char format_flag,
147 MEM_ATTRIBUTE char g_flag,
148 MEM_ATTRIBUTE bool alternate_flag)
150 MEM_ATTRIBUTE char *cp;
151 MEM_ATTRIBUTE char *buf_pointer;
152 MEM_ATTRIBUTE short n, i, dec_point_pos, integral_10_log;
159 while (value >= 1e11) /* To speed up things a bit */
162 integral_10_log += 10;
170 else if (value) /* Not just 0.0 */
172 while (value <= 1e-10) /* To speed up things a bit */
175 integral_10_log -= 10;
185 if (integral_10_log < nr_of_digits && integral_10_log >= -4)
188 nr_of_digits -= integral_10_log;
192 /* %#G - No removal of trailing zeros */
195 /* %G - Removal of trailing zeros */
196 alternate_flag = true;
206 /* Less than one... */
207 if (integral_10_log < 0)
209 *buf_pointer++ = '0';
210 if ((n = nr_of_digits) || alternate_flag)
211 *buf_pointer++ = '.';
213 while (--i > integral_10_log && nr_of_digits)
215 *buf_pointer++ = '0';
218 if (integral_10_log < (-n - 1))
219 /* Nothing more to do */
225 dec_point_pos = - integral_10_log;
230 while (i <= nr_of_digits )
232 value -= (max_float_t)(n = (short)value); /* n=Digit value=Remainder */
233 value *= 10; /* Prepare for next shot */
234 *buf_pointer++ = n + '0';
235 if ( ! i++ && (nr_of_digits || alternate_flag))
236 *buf_pointer++ = '.';
239 /* Rounding possible */
243 cp = buf_pointer - 1;
248 if ( (*cp += n) == ('9' + 1) )
256 } while (cp-- > buf);
265 if (*(cp - 1) == '.')
290 /* %G - Remove trailing zeros */
293 while (*(buf_pointer - 1) == '0')
295 if (*(buf_pointer - 1) == '.')
302 *buf_pointer++ = format_flag;
303 if (integral_10_log < 0)
305 *buf_pointer++ = '-';
306 integral_10_log = -integral_10_log;
309 *buf_pointer++ = '+';
315 *buf_pointer++ = (integral_10_log % 10) + '0';
316 integral_10_log /= 10;
317 } while ( integral_10_log || n < 2 );
318 for ( i = n ; n > 0 ; n-- )
319 *(buf_pointer - 11 - i + n) = *(buf_pointer - n);
322 return (buf_pointer);
325 #endif /* CONFIG_PRINTF > PRINTF_NOFLOAT */
328 * This routine forms the core and entry of the formatter.
330 * The conversion performed conforms to the ANSI specification for "printf".
333 PGM_FUNC(_formatted_write)(const char * PGM_ATTR format,
334 void put_one_char(char, void *),
335 void *secret_pointer,
338 #if CONFIG_PRINTF > PRINTF_REDUCED
339 MEM_ATTRIBUTE static char bad_conversion[] = "???";
340 MEM_ATTRIBUTE static char null_pointer[] = "<NULL>";
342 MEM_ATTRIBUTE int precision;
344 #if CONFIG_PRINTF_COUNT_CHARS
345 MEM_ATTRIBUTE int nr_of_chars;
347 MEM_ATTRIBUTE int field_width;
348 MEM_ATTRIBUTE char format_flag;
349 enum PLUS_SPACE_FLAGS {
350 PSF_NONE, PSF_PLUS, PSF_MINUS
354 #if CONFIG_PRINTF_OCTAL_FORMATTER
358 MEM_ATTRIBUTE struct {
359 enum PLUS_SPACE_FLAGS plus_space_flag : 2;
360 #if CONFIG_PRINTF_OCTAL_FORMATTER
361 enum DIV_FACTOR div_factor : 2;
363 enum DIV_FACTOR div_factor : 1;
365 bool left_adjust : 1;
366 bool l_L_modifier : 1;
368 bool alternate_flag : 1;
369 bool nonzero_value : 1;
375 MEM_ATTRIBUTE unsigned long ulong;
377 #if CONFIG_PRINTF > PRINTF_NOFLOAT
378 MEM_ATTRIBUTE max_float_t fvalue;
381 MEM_ATTRIBUTE char *buf_pointer;
382 MEM_ATTRIBUTE char *ptr;
383 MEM_ATTRIBUTE const char *hex;
384 MEM_ATTRIBUTE char buf[FRMWRI_BUFSIZE];
386 #if CONFIG_PRINTF_COUNT_CHARS
389 for (;;) /* Until full format string read */
391 while ((format_flag = PGM_READ_CHAR(format++)) != '%') /* Until '%' or '\0' */
394 #if CONFIG_PRINTF_RETURN_COUNT
395 return (nr_of_chars);
399 put_one_char(format_flag, secret_pointer);
400 #if CONFIG_PRINTF_COUNT_CHARS
404 if (PGM_READ_CHAR(format) == '%') /* %% prints as % */
407 put_one_char('%', secret_pointer);
408 #if CONFIG_PRINTF_COUNT_CHARS
414 flags.left_adjust = false;
415 flags.alternate_flag = false;
416 flags.plus_space_flag = PSF_NONE;
417 flags.zeropad = false;
419 flags.progmem = false;
421 ptr = buf_pointer = &buf[0];
424 /* check for leading '-', '+', ' ','#' or '0' flags */
427 switch (PGM_READ_CHAR(format))
430 if (flags.plus_space_flag)
433 flags.plus_space_flag = PSF_PLUS;
436 flags.left_adjust = true;
439 flags.alternate_flag = true;
442 flags.zeropad = true;
450 /* Optional field width (may be '*') */
451 if (PGM_READ_CHAR(format) == '*')
453 field_width = va_arg(ap, int);
456 field_width = -field_width;
457 flags.left_adjust = true;
464 while (PGM_READ_CHAR(format) >= '0' && PGM_READ_CHAR(format) <= '9')
465 field_width = field_width * 10 + (PGM_READ_CHAR(format++) - '0');
468 if (flags.left_adjust)
469 flags.zeropad = false;
471 /* Optional precision (or '*') */
472 if (PGM_READ_CHAR(format) == '.')
474 if (PGM_READ_CHAR(++format) == '*')
476 precision = va_arg(ap, int);
482 while (PGM_READ_CHAR(format) >= '0' && PGM_READ_CHAR(format) <= '9')
483 precision = precision * 10 + (PGM_READ_CHAR(format++) - '0');
489 /* At this point, "left_adjust" is nonzero if there was
490 * a sign, "zeropad" is 1 if there was a leading zero
491 * and 0 otherwise, "field_width" and "precision"
492 * contain numbers corresponding to the digit strings
493 * before and after the decimal point, respectively,
494 * and "plus_space_flag" is either 0 (no flag) or
495 * contains a plus or space character. If there was no
496 * decimal point, "precision" will be -1.
499 flags.l_L_modifier = false;
500 flags.h_modifier = false;
502 /* Optional 'l','L','z' or 'h' modifier? */
503 switch (PGM_READ_CHAR(format))
507 #if SIZEOF_SIZE_T == SIZEOF_LONG
509 flags.l_L_modifier = true;
510 #elif SIZEOF_SIZE_T == SIZEOF_INT
511 flags.l_L_modifier = true;
518 flags.h_modifier = true;
525 * At exit from the following switch, we will emit
526 * the characters starting at "buf_pointer" and
529 switch (format_flag = PGM_READ_CHAR(format++))
531 #if CONFIG_PRINTF_N_FORMATTER
533 if (sizeof(short) != sizeof(int))
535 if (sizeof(int) != sizeof(long))
538 *va_arg(ap, short *) = nr_of_chars;
539 else if (flags.l_L_modifier)
540 *va_arg(ap, long *) = nr_of_chars;
542 *va_arg(ap, int *) = nr_of_chars;
547 *va_arg(ap, short *) = nr_of_chars;
549 *va_arg(ap, int *) = nr_of_chars;
554 if (flags.l_L_modifier)
555 *va_arg(ap, long *) = nr_of_chars;
557 *va_arg(ap, int *) = nr_of_chars;
562 buf[0] = va_arg(ap, int);
566 /* Custom formatter for strings in program memory. */
569 flags.progmem = true;
574 if ( !(buf_pointer = va_arg(ap, char *)) )
575 buf_pointer = null_pointer;
580 * Move `ptr' to the last character of the
581 * string that will be actually printed.
587 for (n=0; pgm_read_char(ptr) && n < precision; n++)
592 for (n=0; *ptr && n < precision; n++)
596 #if CONFIG_PRINTF_OCTAL_FORMATTER
598 if (flags.alternate_flag && !precision)
606 if (format_flag == 'p')
607 #if defined(__AVR__) || defined(__I196__) || defined(__MSP430__) /* 16bit pointers */
608 ulong = (unsigned long)(unsigned short)va_arg(ap, char *);
609 #else /* 32bit pointers */
610 ulong = (unsigned long)va_arg(ap, char *);
611 #endif /* 32bit pointers */
612 else if (flags.l_L_modifier)
613 ulong = va_arg(ap, unsigned long);
614 else if (flags.h_modifier)
615 ulong = (unsigned long)(unsigned short)va_arg(ap, unsigned int);
617 ulong = va_arg(ap, unsigned int);
620 #if CONFIG_PRINTF_OCTAL_FORMATTER
621 (format_flag == 'o') ? DIV_OCT :
623 (format_flag == 'u') ? DIV_DEC : DIV_HEX;
624 flags.plus_space_flag = PSF_NONE;
625 goto INTEGRAL_CONVERSION;
629 if (flags.l_L_modifier)
630 ulong = (unsigned long)(long)va_arg(ap, long);
632 ulong = (unsigned long)(long)va_arg(ap, int);
635 if ((signed long)ulong < 0)
637 flags.plus_space_flag = PSF_MINUS;
638 ulong = (unsigned long)(-((signed long)ulong));
641 flags.div_factor = DIV_DEC;
643 /* Now convert to digits */
645 ptr = buf_pointer = &buf[FRMWRI_BUFSIZE - 1];
646 flags.nonzero_value = (ulong != 0);
648 /* No char if zero and zero precision */
649 if (precision != 0 || flags.nonzero_value)
651 switch (flags.div_factor)
655 *--buf_pointer = hex[ulong % 10];
661 *--buf_pointer = hex[ulong % 16];
664 #if CONFIG_PRINTF_OCTAL_FORMATTER
667 *--buf_pointer = hex[ulong % 8];
674 /* "precision" takes precedence */
677 precision = field_width - (flags.plus_space_flag != PSF_NONE);
678 while (precision > (int)(ptr - buf_pointer))
679 *--buf_pointer = '0';
681 if (flags.alternate_flag && flags.nonzero_value)
683 if (format_flag == 'x' || format_flag == 'X')
685 *--buf_pointer = format_flag;
686 *--buf_pointer = '0';
688 #if CONFIG_PRINTF_OCTAL_FORMATTER
689 else if ((format_flag == 'o') && (*buf_pointer != '0'))
691 *--buf_pointer = '0';
695 ASSERT(buf_pointer >= buf);
698 #if CONFIG_PRINTF > PRINTF_NOFLOAT
707 goto FLOATING_CONVERSION;
719 if (sizeof(double) != sizeof(max_float_t))
721 fvalue = flags.l_L_modifier ?
722 va_arg(ap,max_float_t) : va_arg(ap,double);
725 fvalue = va_arg(ap,max_float_t);
729 flags.plus_space_flag = PSF_MINUS;
732 ptr = float_conversion (fvalue,
734 buf_pointer += field_width,
737 flags.alternate_flag);
740 precision = field_width - (flags.plus_space_flag != PSF_NONE);
741 while (precision > ptr - buf_pointer)
742 *--buf_pointer = '0';
746 #endif /* CONFIG_PRINTF <= PRINTF_NOFLOAT */
748 case '\0': /* Really bad place to find NUL in */
752 /* Undefined conversion! */
753 ptr = buf_pointer = bad_conversion;
754 ptr += sizeof(bad_conversion) - 1;
760 * This part emittes the formatted string to "put_one_char".
763 /* If field_width == 0 then nothing should be written. */
764 precision = ptr - buf_pointer;
766 if ( precision > field_width)
772 n = field_width - precision - (flags.plus_space_flag != PSF_NONE);
775 /* emit any leading pad characters */
776 if (!flags.left_adjust)
779 put_one_char(' ', secret_pointer);
780 #if CONFIG_PRINTF_COUNT_CHARS
785 /* emit flag characters (if any) */
786 if (flags.plus_space_flag)
788 put_one_char(flags.plus_space_flag == PSF_PLUS ? '+' : '-', secret_pointer);
789 #if CONFIG_PRINTF_COUNT_CHARS
797 while (--precision >= 0)
799 put_one_char(pgm_read_char(buf_pointer++), secret_pointer);
800 #if CONFIG_PRINTF_COUNT_CHARS
806 #endif /* CPU_HARVARD */
808 /* emit the string itself */
809 while (--precision >= 0)
811 put_one_char(*buf_pointer++, secret_pointer);
812 #if CONFIG_PRINTF_COUNT_CHARS
818 /* emit trailing space characters */
819 if (flags.left_adjust)
822 put_one_char(' ', secret_pointer);
823 #if CONFIG_PRINTF_COUNT_CHARS
829 #else /* PRINTF_REDUCED starts here */
831 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
832 bool l_modifier, h_modifier;
833 unsigned long u_val, div_val;
835 unsigned int u_val, div_val;
836 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
839 unsigned int nr_of_chars, base;
844 for (;;) /* Until full format string read */
846 while ((format_flag = PGM_READ_CHAR(format++)) != '%') /* Until '%' or '\0' */
849 return (nr_of_chars);
850 put_one_char(format_flag, secret_pointer);
854 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
856 * Optional 'l', 'z' or 'h' modifiers?
858 l_modifier = h_modifier = false;
859 switch (PGM_READ_CHAR(format))
862 #if SIZEOF_SIZE_T == SIZEOF_LONG
865 #elif SIZEOF_SIZE_T == SIZEOF_INT
877 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
879 switch (format_flag = PGM_READ_CHAR(format++))
882 format_flag = va_arg(ap, int);
884 put_one_char(format_flag, secret_pointer);
889 ptr = va_arg(ap, char *);
890 while ((format_flag = *ptr++))
892 put_one_char(format_flag, secret_pointer);
902 div_val = 0x40000000;
903 goto CONVERSION_LOOP;
910 div_val = 1000000000;
911 goto CONVERSION_LOOP;
919 div_val = 0x10000000;
922 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
925 if (format_flag == 'd')
926 u_val = (short)va_arg(ap, int);
928 u_val = (unsigned short)va_arg(ap, int);
931 u_val = va_arg(ap, long);
934 if (format_flag == 'd')
935 u_val = va_arg(ap, int);
937 u_val = va_arg(ap, unsigned int);
940 #else /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
941 u_val = va_arg(ap,int);
942 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
943 if (format_flag == 'd')
945 if (((int)u_val) < 0)
948 put_one_char('-', secret_pointer);
952 while (div_val > 1 && div_val > u_val)
958 outChar = (u_val / div_val) + '0';
961 if (format_flag == 'x')
962 outChar += 'a'-'9'-1;
964 outChar += 'A'-'9'-1;
966 put_one_char(outChar, secret_pointer);
973 } /* end switch(format_flag...) */
975 #endif /* CONFIG_PRINTF > PRINTF_REDUCED */
978 #endif /* CONFIG_PRINTF */