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 Develer S.r.l. (http://www.develer.com/)
35 * \brief Basic "printf", "sprintf" and "fprintf" formatter.
37 * This module is 100% reentrant and can be adapted to user-defined routines
38 * that needs formatters with special properties like different output
39 * channels or new format specifiers.
41 * To reduce size in applications not using real numbers or long integers
42 * the formatter may be compiled to exclude certain parts. This is
43 * controlled by giving a -D option a compilation time:
46 * -D CONFIG_PRINTF=PRINTF_FULL Full ANSI printf formatter
47 * -D CONFIG_PRINTF=PRINTF_NOFLOAT Exclude support for floats
48 * -D CONFIG_PRINTF=PRINTF_REDUCED Simplified formatter (see below)
49 * -D CONFIG_PRINTF=PRINTF_NOMODIFIERS Exclude 'l' and 'h' modifiers in reduced version
50 * -D CONFIG_PRINTF=PRINTF_DISABLED No formatter at all
53 * Code size on AVR4 with GCC 3.4.1 (-O2):
54 * PRINTF_FULL 2912byte (0xB60)
55 * PRINTF_NOFLOAT 1684byte (0x694)
56 * PRINTF_REDUCED 924byte (0x39C)
57 * PRINTF_NOMODIFIERS 416byte (0x1A0)
59 * Code/data size in words on DSP56K with CodeWarrior 6.0:
61 * PRINTF_NOFLOAT 795/45
62 * PRINTF_REDUCED 482/0
63 * PRINTF_NOMODIFIERS 301/0
65 * The reduced version of formatter is suitable when program size is critical
66 * rather than formatting power. This routine uses less than 20 bytes of
67 * stack space which makes it practical even in systems with less than 256
70 * The only formatting specifiers supported by the reduced formatter are:
72 * %% %c %s %d %o %x %X and %hd %ho %hx %hX %ld %lo %lx %lX
75 * It means that real variables are not supported as well as field
76 * width and precision arguments.
81 *#* Revision 1.19 2006/09/20 13:58:17 marco
82 *#* Added z modifier in string format.
84 *#* Revision 1.18 2006/07/19 12:56:27 bernie
85 *#* Convert to new Doxygen style.
87 *#* Revision 1.17 2005/11/04 17:43:27 bernie
88 *#* Fix for LP64 architectures; Add some more tests.
90 *#* Revision 1.16 2005/07/19 07:25:46 bernie
91 *#* Use appconfig.h instead of cfg/config.h.
93 *#* Revision 1.15 2005/04/11 19:10:28 bernie
94 *#* Include top-level headers from cfg/ subdir.
96 *#* Revision 1.14 2005/03/01 23:26:22 bernie
97 *#* Use shared hextab.
99 *#* Revision 1.13 2005/02/18 12:33:25 bernie
102 *#* Revision 1.12 2005/02/16 20:28:03 bernie
103 *#* Add %S formatter.
105 *#* Revision 1.11 2005/02/16 16:51:29 bernie
106 *#* Simplify float code.
108 *#* Revision 1.10 2004/10/26 09:01:35 bernie
111 *#* Revision 1.9 2004/09/14 21:06:23 bernie
114 *#* Revision 1.8 2004/08/25 14:12:09 rasky
115 *#* Aggiornato il comment block dei log RCS
118 #include "formatwr.h"
119 #include <mware/pgm.h>
120 #include <mware/hex.h>
121 #include <cfg/debug.h> /* ASSERT */
122 #include <appconfig.h> /* CONFIG_ macros */
124 #ifndef CONFIG_PRINTF_N_FORMATTER
125 /** Disable the arcane %n formatter. */
126 #define CONFIG_PRINTF_N_FORMATTER 0
129 #ifndef CONFIG_PRINTF_OCTAL_FORMATTER
130 /** Disable the %o formatter. */
131 #define CONFIG_PRINTF_OCTAL_FORMATTER 0
134 /* True if we must keep a count of the number of characters we print. */
135 #define CONFIG_PRINTF_COUNT_CHARS (CONFIG_PRINTF_RETURN_COUNT || CONFIG_PRINTF_N_FORMATTER)
139 #if CONFIG_PRINTF > PRINTF_NOFLOAT
142 /* Maximum precision for floating point values */
143 typedef long double max_float_t;
145 /*bernie: save some memory, who cares about floats with lots of decimals? */
146 #define FRMWRI_BUFSIZE 134
147 #warning 134 is too much, the code must be fixed to have a lower precision limit
150 * Conservative estimate. Should be (probably) 12 (which is the size necessary
151 * to represent (2^32-1) in octal plus the sign bit.
153 #define FRMWRI_BUFSIZE 16
156 /* Probably useful for fancy microcontrollers such as the PIC, nobody knows. */
157 #ifndef MEM_ATTRIBUTE
158 #define MEM_ATTRIBUTE
161 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
162 #define IS_SHORT (h_modifier || (sizeof(int) == 2 && !l_modifier))
164 #define IS_SHORT (sizeof(int) == 2)
165 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
168 #if CONFIG_PRINTF > PRINTF_NOFLOAT
170 static char *float_conversion(MEM_ATTRIBUTE max_float_t value,
171 MEM_ATTRIBUTE short nr_of_digits,
172 MEM_ATTRIBUTE char *buf,
173 MEM_ATTRIBUTE char format_flag,
174 MEM_ATTRIBUTE char g_flag,
175 MEM_ATTRIBUTE bool alternate_flag)
177 MEM_ATTRIBUTE char *cp;
178 MEM_ATTRIBUTE char *buf_pointer;
179 MEM_ATTRIBUTE short n, i, dec_point_pos, integral_10_log;
186 while (value >= 1e11) /* To speed up things a bit */
189 integral_10_log += 10;
197 else if (value) /* Not just 0.0 */
199 while (value <= 1e-10) /* To speed up things a bit */
202 integral_10_log -= 10;
212 if (integral_10_log < nr_of_digits && integral_10_log >= -4)
215 nr_of_digits -= integral_10_log;
219 /* %#G - No removal of trailing zeros */
222 /* %G - Removal of trailing zeros */
223 alternate_flag = true;
233 /* Less than one... */
234 if (integral_10_log < 0)
236 *buf_pointer++ = '0';
237 if ((n = nr_of_digits) || alternate_flag)
238 *buf_pointer++ = '.';
240 while (--i > integral_10_log && nr_of_digits)
242 *buf_pointer++ = '0';
245 if (integral_10_log < (-n - 1))
246 /* Nothing more to do */
252 dec_point_pos = - integral_10_log;
257 while (i <= nr_of_digits )
259 value -= (max_float_t)(n = (short)value); /* n=Digit value=Remainder */
260 value *= 10; /* Prepare for next shot */
261 *buf_pointer++ = n + '0';
262 if ( ! i++ && (nr_of_digits || alternate_flag))
263 *buf_pointer++ = '.';
266 /* Rounding possible */
270 cp = buf_pointer - 1;
275 if ( (*cp += n) == ('9' + 1) )
283 } while (cp-- > buf);
292 if (*(cp - 1) == '.')
317 /* %G - Remove trailing zeros */
320 while (*(buf_pointer - 1) == '0')
322 if (*(buf_pointer - 1) == '.')
329 *buf_pointer++ = format_flag;
330 if (integral_10_log < 0)
332 *buf_pointer++ = '-';
333 integral_10_log = -integral_10_log;
336 *buf_pointer++ = '+';
342 *buf_pointer++ = (integral_10_log % 10) + '0';
343 integral_10_log /= 10;
344 } while ( integral_10_log || n < 2 );
345 for ( i = n ; n > 0 ; n-- )
346 *(buf_pointer - 11 - i + n) = *(buf_pointer - n);
349 return (buf_pointer);
352 #endif /* CONFIG_PRINTF > PRINTF_NOFLOAT */
355 * This routine forms the core and entry of the formatter.
357 * The conversion performed conforms to the ANSI specification for "printf".
360 PGM_FUNC(_formatted_write)(const char * PGM_ATTR format,
361 void put_one_char(char, void *),
362 void *secret_pointer,
365 #if CONFIG_PRINTF > PRINTF_REDUCED
366 MEM_ATTRIBUTE static char bad_conversion[] = "???";
367 MEM_ATTRIBUTE static char null_pointer[] = "<NULL>";
369 MEM_ATTRIBUTE int precision;
371 #if CONFIG_PRINTF_COUNT_CHARS
372 MEM_ATTRIBUTE int nr_of_chars;
374 MEM_ATTRIBUTE int field_width;
375 MEM_ATTRIBUTE char format_flag;
376 enum PLUS_SPACE_FLAGS {
377 PSF_NONE, PSF_PLUS, PSF_MINUS
381 #if CONFIG_PRINTF_OCTAL_FORMATTER
385 MEM_ATTRIBUTE struct {
386 enum PLUS_SPACE_FLAGS plus_space_flag : 2;
387 #if CONFIG_PRINTF_OCTAL_FORMATTER
388 enum DIV_FACTOR div_factor : 2;
390 enum DIV_FACTOR div_factor : 1;
392 bool left_adjust : 1;
393 bool l_L_modifier : 1;
395 bool alternate_flag : 1;
396 bool nonzero_value : 1;
402 MEM_ATTRIBUTE unsigned long ulong;
404 #if CONFIG_PRINTF > PRINTF_NOFLOAT
405 MEM_ATTRIBUTE max_float_t fvalue;
408 MEM_ATTRIBUTE char *buf_pointer;
409 MEM_ATTRIBUTE char *ptr;
410 MEM_ATTRIBUTE const char *hex;
411 MEM_ATTRIBUTE char buf[FRMWRI_BUFSIZE];
413 #if CONFIG_PRINTF_COUNT_CHARS
416 for (;;) /* Until full format string read */
418 while ((format_flag = PGM_READ_CHAR(format++)) != '%') /* Until '%' or '\0' */
421 #if CONFIG_PRINTF_RETURN_COUNT
422 return (nr_of_chars);
426 put_one_char(format_flag, secret_pointer);
427 #if CONFIG_PRINTF_COUNT_CHARS
431 if (PGM_READ_CHAR(format) == '%') /* %% prints as % */
434 put_one_char('%', secret_pointer);
435 #if CONFIG_PRINTF_COUNT_CHARS
441 flags.left_adjust = false;
442 flags.alternate_flag = false;
443 flags.plus_space_flag = PSF_NONE;
444 flags.zeropad = false;
446 flags.progmem = false;
448 ptr = buf_pointer = &buf[0];
451 /* check for leading '-', '+', ' ','#' or '0' flags */
454 switch (PGM_READ_CHAR(format))
457 if (flags.plus_space_flag)
460 flags.plus_space_flag = PSF_PLUS;
463 flags.left_adjust = true;
466 flags.alternate_flag = true;
469 flags.zeropad = true;
477 /* Optional field width (may be '*') */
478 if (PGM_READ_CHAR(format) == '*')
480 field_width = va_arg(ap, int);
483 field_width = -field_width;
484 flags.left_adjust = true;
491 while (PGM_READ_CHAR(format) >= '0' && PGM_READ_CHAR(format) <= '9')
492 field_width = field_width * 10 + (PGM_READ_CHAR(format++) - '0');
495 if (flags.left_adjust)
496 flags.zeropad = false;
498 /* Optional precision (or '*') */
499 if (PGM_READ_CHAR(format) == '.')
501 if (PGM_READ_CHAR(++format) == '*')
503 precision = va_arg(ap, int);
509 while (PGM_READ_CHAR(format) >= '0' && PGM_READ_CHAR(format) <= '9')
510 precision = precision * 10 + (PGM_READ_CHAR(format++) - '0');
516 /* At this point, "left_adjust" is nonzero if there was
517 * a sign, "zeropad" is 1 if there was a leading zero
518 * and 0 otherwise, "field_width" and "precision"
519 * contain numbers corresponding to the digit strings
520 * before and after the decimal point, respectively,
521 * and "plus_space_flag" is either 0 (no flag) or
522 * contains a plus or space character. If there was no
523 * decimal point, "precision" will be -1.
526 flags.l_L_modifier = false;
527 flags.h_modifier = false;
529 /* Optional 'l','L','z' or 'h' modifier? */
530 switch (PGM_READ_CHAR(format))
535 flags.l_L_modifier = true;
539 flags.h_modifier = true;
545 * At exit from the following switch, we will emit
546 * the characters starting at "buf_pointer" and
549 switch (format_flag = PGM_READ_CHAR(format++))
551 #if CONFIG_PRINTF_N_FORMATTER
553 if (sizeof(short) != sizeof(int))
555 if (sizeof(int) != sizeof(long))
558 *va_arg(ap, short *) = nr_of_chars;
559 else if (flags.l_L_modifier)
560 *va_arg(ap, long *) = nr_of_chars;
562 *va_arg(ap, int *) = nr_of_chars;
567 *va_arg(ap, short *) = nr_of_chars;
569 *va_arg(ap, int *) = nr_of_chars;
574 if (flags.l_L_modifier)
575 *va_arg(ap, long *) = nr_of_chars;
577 *va_arg(ap, int *) = nr_of_chars;
582 buf[0] = va_arg(ap, int);
586 /* Custom formatter for strings in program memory. */
589 flags.progmem = true;
594 if ( !(buf_pointer = va_arg(ap, char *)) )
595 buf_pointer = null_pointer;
600 * Move `ptr' to the last character of the
601 * string that will be actually printed.
607 for (n=0; pgm_read_char(ptr) && n < precision; n++)
612 for (n=0; *ptr && n < precision; n++)
616 #if CONFIG_PRINTF_OCTAL_FORMATTER
618 if (flags.alternate_flag && !precision)
626 if (format_flag == 'p')
627 #if defined(__AVR__) || defined(__I196__) /* 16bit pointers */
628 ulong = (unsigned long)(unsigned short)va_arg(ap, char *);
629 #else /* 32bit pointers */
630 ulong = (unsigned long)va_arg(ap, char *);
631 #endif /* 32bit pointers */
632 else if (flags.l_L_modifier)
633 ulong = va_arg(ap, unsigned long);
634 else if (flags.h_modifier)
635 ulong = (unsigned long)(unsigned short)va_arg(ap, unsigned int);
637 ulong = va_arg(ap, unsigned int);
640 #if CONFIG_PRINTF_OCTAL_FORMATTER
641 (format_flag == 'o') ? DIV_OCT :
643 (format_flag == 'u') ? DIV_DEC : DIV_HEX;
644 flags.plus_space_flag = PSF_NONE;
645 goto INTEGRAL_CONVERSION;
649 if (flags.l_L_modifier)
650 ulong = (unsigned long)(long)va_arg(ap, long);
652 ulong = (unsigned long)(long)va_arg(ap, int);
655 if ((signed long)ulong < 0)
657 flags.plus_space_flag = PSF_MINUS;
658 ulong = (unsigned long)(-((signed long)ulong));
661 flags.div_factor = DIV_DEC;
663 /* Now convert to digits */
665 ptr = buf_pointer = &buf[FRMWRI_BUFSIZE - 1];
666 flags.nonzero_value = (ulong != 0);
668 /* No char if zero and zero precision */
669 if (precision != 0 || flags.nonzero_value)
671 switch (flags.div_factor)
675 *--buf_pointer = hex[ulong % 10];
681 *--buf_pointer = hex[ulong % 16];
684 #if CONFIG_PRINTF_OCTAL_FORMATTER
687 *--buf_pointer = hex[ulong % 8];
694 /* "precision" takes precedence */
697 precision = field_width - (flags.plus_space_flag != PSF_NONE);
698 while (precision > (int)(ptr - buf_pointer))
699 *--buf_pointer = '0';
701 if (flags.alternate_flag && flags.nonzero_value)
703 if (format_flag == 'x' || format_flag == 'X')
705 *--buf_pointer = format_flag;
706 *--buf_pointer = '0';
708 #if CONFIG_PRINTF_OCTAL_FORMATTER
709 else if ((format_flag == 'o') && (*buf_pointer != '0'))
711 *--buf_pointer = '0';
715 ASSERT(buf_pointer >= buf);
718 #if CONFIG_PRINTF > PRINTF_NOFLOAT
727 goto FLOATING_CONVERSION;
739 if (sizeof(double) != sizeof(max_float_t))
741 fvalue = flags.l_L_modifier ?
742 va_arg(ap,max_float_t) : va_arg(ap,double);
745 fvalue = va_arg(ap,max_float_t);
749 flags.plus_space_flag = PSF_MINUS;
752 ptr = float_conversion (fvalue,
754 buf_pointer += field_width,
757 flags.alternate_flag);
760 precision = field_width - (flags.plus_space_flag != PSF_NONE);
761 while (precision > ptr - buf_pointer)
762 *--buf_pointer = '0';
766 #endif /* CONFIG_PRINTF <= PRINTF_NOFLOAT */
768 case '\0': /* Really bad place to find NUL in */
772 /* Undefined conversion! */
773 ptr = buf_pointer = bad_conversion;
774 ptr += sizeof(bad_conversion) - 1;
780 * This part emittes the formatted string to "put_one_char".
783 /* If field_width == 0 then nothing should be written. */
784 precision = ptr - buf_pointer;
786 if ( precision > field_width)
792 n = field_width - precision - (flags.plus_space_flag != PSF_NONE);
795 /* emit any leading pad characters */
796 if (!flags.left_adjust)
799 put_one_char(' ', secret_pointer);
800 #if CONFIG_PRINTF_COUNT_CHARS
805 /* emit flag characters (if any) */
806 if (flags.plus_space_flag)
808 put_one_char(flags.plus_space_flag == PSF_PLUS ? '+' : '-', secret_pointer);
809 #if CONFIG_PRINTF_COUNT_CHARS
817 while (--precision >= 0)
819 put_one_char(pgm_read_char(buf_pointer++), secret_pointer);
820 #if CONFIG_PRINTF_COUNT_CHARS
826 #endif /* CPU_HARVARD */
828 /* emit the string itself */
829 while (--precision >= 0)
831 put_one_char(*buf_pointer++, secret_pointer);
832 #if CONFIG_PRINTF_COUNT_CHARS
838 /* emit trailing space characters */
839 if (flags.left_adjust)
842 put_one_char(' ', secret_pointer);
843 #if CONFIG_PRINTF_COUNT_CHARS
849 #else /* PRINTF_REDUCED starts here */
851 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
852 bool l_modifier, h_modifier;
853 unsigned long u_val, div_val;
855 unsigned int u_val, div_val;
856 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
859 unsigned int nr_of_chars, base;
864 for (;;) /* Until full format string read */
866 while ((format_flag = PGM_READ_CHAR(format++)) != '%') /* Until '%' or '\0' */
869 return (nr_of_chars);
870 put_one_char(format_flag, secret_pointer);
874 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
875 /*=================================*/
876 /* Optional 'l' or 'h' modifiers ? */
877 /*=================================*/
878 l_modifier = h_modifier = false;
879 switch (PGM_READ_CHAR(format))
891 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
893 switch (format_flag = PGM_READ_CHAR(format++))
896 format_flag = va_arg(ap, int);
898 put_one_char(format_flag, secret_pointer);
903 ptr = va_arg(ap, char *);
904 while ((format_flag = *ptr++))
906 put_one_char(format_flag, secret_pointer);
916 div_val = 0x40000000;
917 goto CONVERSION_LOOP;
924 div_val = 1000000000;
925 goto CONVERSION_LOOP;
933 div_val = 0x10000000;
936 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
938 u_val = (format_flag == 'd') ?
939 (short)va_arg(ap, int) : (unsigned short)va_arg(ap, int);
941 u_val = va_arg(ap, long);
943 u_val = (format_flag == 'd') ?
944 va_arg(ap,int) : va_arg(ap,unsigned int);
945 #else /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
946 u_val = va_arg(ap,int);
947 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
948 if (format_flag == 'd')
950 if (((int)u_val) < 0)
953 put_one_char('-', secret_pointer);
957 while (div_val > 1 && div_val > u_val)
963 outChar = (u_val / div_val) + '0';
966 if (format_flag == 'x')
967 outChar += 'a'-'9'-1;
969 outChar += 'A'-'9'-1;
971 put_one_char(outChar, secret_pointer);
978 } /* end switch(format_flag...) */
980 #endif /* CONFIG_PRINTF > PRINTF_REDUCED */
983 #endif /* CONFIG_PRINTF */