4 * Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
5 * This file is part of DevLib - See devlib/README for information.
10 * \brief Basic "printf", "sprintf" and "fprintf" formatter.
12 * This module is 100% reentrant and can be adapted to user-defined routines
13 * that needs formatters with special properties like different output
14 * channels or new format specifiers.
16 * To reduce size in applications not using real numbers or long integers
17 * the formatter may be compiled to exclude certain parts. This is
18 * controlled by giving a -D option a compilation time:
21 * -D CONFIG_PRINTF=PRINTF_FULL Full ANSI printf formatter
22 * -D CONFIG_PRINTF=PRINTF_NOFLOAT Exclude support for floats
23 * -D CONFIG_PRINTF=PRINTF_REDUCED Simplified formatter (see below)
24 * -D CONFIG_PRINTF=PRINTF_NOMODIFIERS Exclude 'l' and 'h' modifiers in reduced version
25 * -D CONFIG_PRINTF=PRINTF_DISABLED No formatter at all
28 * Code size on AVR4 with GCC 3.4.1 (-O2):
29 * PRINTF_FULL 2912byte (0xB60)
30 * PRINTF_NOFLOAT 1684byte (0x694)
31 * PRINTF_REDUCED 924byte (0x39C)
32 * PRINTF_NOMODIFIERS 416byte (0x1A0)
34 * Code/data size in words on DSP56K with CodeWarrior 6.0:
36 * PRINTF_NOFLOAT 795/45
37 * PRINTF_REDUCED 482/0
38 * PRINTF_NOMODIFIERS 301/0
40 * The reduced version of formatter is suitable when program size is critical
41 * rather than formatting power. This routine uses less than 20 bytes of
42 * stack space which makes it practical even in systems with less than 256
45 * The only formatting specifiers supported by the reduced formatter are:
47 * %% %c %s %d %o %x %X and %hd %ho %hx %hX %ld %lo %lx %lX
50 * It means that real variables are not supported as well as field
51 * width and precision arguments.
56 * Revision 1.6 2004/07/30 14:34:10 rasky
57 * Vari fix per documentazione e commenti
58 * Aggiunte PP_CATn e STATIC_ASSERT
60 * Revision 1.5 2004/07/29 22:57:09 bernie
61 * Switch to new-style config handling.
63 * Revision 1.4 2004/07/21 00:20:20 bernie
64 * Allow completely disabling printf()-like formatter.
66 * Revision 1.3 2004/07/18 22:00:15 bernie
67 * Reorganize configuration parameters to match DevLib's convention.
69 * Revision 1.2 2004/06/03 11:27:09 bernie
70 * Add dual-license information.
72 * Revision 1.1 2004/05/23 15:43:16 bernie
73 * Import mware modules.
78 #include <compiler.h> /* progmem macros */
79 #include <config.h> /* CONFIG_ macros */
83 #if CONFIG_PRINTF > PRINTF_NOFLOAT
85 #endif /* CONFIG_PRINTF > PRINTF_NOFLOAT */
87 #ifndef FRMWRI_BUFSIZE
88 /*bernie: save some memory, who cares about floats with lots of decimals? */
89 /*#define FRMWRI_BUFSIZE 134*/
90 #define FRMWRI_BUFSIZE 32
97 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
98 #define IS_SHORT (h_modifier || (sizeof(int) == 2 && !l_modifier))
100 #define IS_SHORT (sizeof(int) == 2)
101 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
104 #if CONFIG_PRINTF > PRINTF_NOFLOAT
106 static char *float_conversion(MEM_ATTRIBUTE long double value,
107 MEM_ATTRIBUTE short nr_of_digits,
108 MEM_ATTRIBUTE char *buf,
109 MEM_ATTRIBUTE char format_flag,
110 MEM_ATTRIBUTE char g_flag,
111 MEM_ATTRIBUTE char alternate_flag)
113 MEM_ATTRIBUTE char *cp;
114 MEM_ATTRIBUTE char *buf_pointer;
115 MEM_ATTRIBUTE short n, i, dec_point_pos, integral_10_log;
122 while (value >= 1e11) /* To speed up things a bit */
125 integral_10_log += 10;
133 else if (value) /* Not just 0.0 */
135 while (value <= 1e-10) /* To speed up things a bit */
138 integral_10_log -= 10;
148 if (integral_10_log < nr_of_digits && integral_10_log >= -4)
151 nr_of_digits -= integral_10_log;
155 /* %#G - No removal of trailing zeros */
158 /* %G - Removal of trailing zeros */
169 /* Less than one... */
170 if (integral_10_log < 0)
172 *buf_pointer++ = '0';
173 if ((n = nr_of_digits) || alternate_flag)
174 *buf_pointer++ = '.';
176 while (--i > integral_10_log && nr_of_digits)
178 *buf_pointer++ = '0';
181 if (integral_10_log < (-n - 1))
182 /* Nothing more to do */
188 dec_point_pos = - integral_10_log;
193 while (i <= nr_of_digits )
195 value -= (long double)(n = (short)value); /* n=Digit value=Remainder */
196 value *= 10; /* Prepare for next shot */
197 *buf_pointer++ = n + '0';
198 if ( ! i++ && (nr_of_digits || alternate_flag))
199 *buf_pointer++ = '.';
202 /* Rounding possible */
206 cp = buf_pointer - 1;
211 if ( (*cp += n) == ('9' + 1) )
219 } while (cp-- > buf);
228 if (*(cp - 1) == '.')
253 /* %G - Remove trailing zeros */
256 while (*(buf_pointer - 1) == '0')
258 if (*(buf_pointer - 1) == '.')
265 *buf_pointer++ = format_flag;
266 if (integral_10_log < 0)
268 *buf_pointer++ = '-';
269 integral_10_log = -integral_10_log;
272 *buf_pointer++ = '+';
278 *buf_pointer++ = (integral_10_log % 10) + '0';
279 integral_10_log /= 10;
280 } while ( integral_10_log || n < 2 );
281 for ( i = n ; n > 0 ; n-- )
282 *(buf_pointer - 11 - i + n) = *(buf_pointer - n);
285 return (buf_pointer);
288 #endif /* CONFIG_PRINTF > PRINTF_NOFLOAT */
291 * This routine forms the core and entry of the formatter.
293 * The conversion performed conforms to the ANSI specification for "printf".
296 PGM_FUNC(_formatted_write)(const char * PGM_ATTR format,
297 void put_one_char(char, void *),
298 void *secret_pointer,
301 #if CONFIG_PRINTF > PRINTF_REDUCED
302 MEM_ATTRIBUTE static char bad_conversion[] = "???";
303 MEM_ATTRIBUTE static char null_pointer[] = "<NULL>";
305 MEM_ATTRIBUTE char format_flag;
306 MEM_ATTRIBUTE int precision;
308 MEM_ATTRIBUTE int field_width, nr_of_chars;
309 MEM_ATTRIBUTE char plus_space_flag, left_adjust, l_L_modifier;
310 MEM_ATTRIBUTE char h_modifier, alternate_flag;
311 MEM_ATTRIBUTE char nonzero_value;
312 MEM_ATTRIBUTE unsigned long ulong, div_factor;
314 #if CONFIG_PRINTF > PRINTF_NOFLOAT
315 MEM_ATTRIBUTE long double fvalue;
318 MEM_ATTRIBUTE char *buf_pointer;
319 MEM_ATTRIBUTE char *ptr;
320 MEM_ATTRIBUTE const char *hex;
321 MEM_ATTRIBUTE char zeropad;
322 MEM_ATTRIBUTE char buf[FRMWRI_BUFSIZE];
325 for (;;) /* Until full format string read */
327 while ((format_flag = PGM_READ_CHAR(format++)) != '%') /* Until '%' or '\0' */
330 return (nr_of_chars);
331 put_one_char(format_flag, secret_pointer);
334 if (PGM_READ_CHAR(format) == '%') /* %% prints as % */
337 put_one_char('%', secret_pointer);
342 plus_space_flag = left_adjust = alternate_flag = zeropad = 0;
343 ptr = buf_pointer = &buf[0];
344 hex = "0123456789ABCDEF";
346 /* check for leading '-', '+', ' ','#' or '0' flags */
349 switch (PGM_READ_CHAR(format))
355 plus_space_flag = PGM_READ_CHAR(format);
372 /* Optional field width (may be '*') */
373 if (PGM_READ_CHAR(format) == '*')
375 field_width = va_arg(ap, int);
378 field_width = -field_width;
386 while (PGM_READ_CHAR(format) >= '0' && PGM_READ_CHAR(format) <= '9')
387 field_width = field_width * 10 + (PGM_READ_CHAR(format++) - '0');
393 /* Optional precision (or '*') */
394 if (PGM_READ_CHAR(format) == '.')
396 if (PGM_READ_CHAR(++format) == '*')
398 precision = va_arg(ap, int);
404 while (PGM_READ_CHAR(format) >= '0' && PGM_READ_CHAR(format) <= '9')
405 precision = precision * 10 + (PGM_READ_CHAR(format++) - '0');
411 /* At this point, "left_adjust" is nonzero if there was
412 * a sign, "zeropad" is 1 if there was a leading zero
413 * and 0 otherwise, "field_width" and "precision"
414 * contain numbers corresponding to the digit strings
415 * before and after the decimal point, respectively,
416 * and "plus_space_flag" is either 0 (no flag) or
417 * contains a plus or space character. If there was no
418 * decimal point, "precision" will be -1.
421 l_L_modifier = h_modifier = 0;
423 /* Optional 'l','L' r 'h' modifier? */
424 switch (PGM_READ_CHAR(format))
438 * At exit from the following switch, we will emit
439 * the characters starting at "buf_pointer" and
442 switch (format_flag = PGM_READ_CHAR(format++))
446 if (sizeof(short) != sizeof(int))
448 if (sizeof(int) != sizeof(long))
451 *va_arg(ap, short *) = nr_of_chars;
452 else if (l_L_modifier)
453 *va_arg(ap, long *) = nr_of_chars;
455 *va_arg(ap, int *) = nr_of_chars;
460 *va_arg(ap, short *) = nr_of_chars;
462 *va_arg(ap, int *) = nr_of_chars;
468 *va_arg(ap, long *) = nr_of_chars;
470 *va_arg(ap, int *) = nr_of_chars;
475 buf[0] = va_arg(ap, int);
480 if ( !(buf_pointer = va_arg(ap, char *)) )
481 buf_pointer = null_pointer;
484 for (n=0; *buf_pointer++ && n < precision; n++)
491 if (alternate_flag && !precision)
494 hex = "0123456789abcdef";
498 if (format_flag == 'p')
499 #if defined(__AVR__) || defined(__I196__) /* 16bit pointers */
500 ulong = (unsigned long)(unsigned short)va_arg(ap, char *);
501 #else /* 32bit pointers */
502 ulong = (unsigned long)va_arg(ap, char *);
503 #endif /* 32bit pointers */
504 else if (sizeof(short) == sizeof(int))
505 ulong = l_L_modifier ?
506 va_arg(ap, unsigned long) : (unsigned long)va_arg(ap, unsigned int);
509 (unsigned long)(unsigned short) va_arg(ap, int)
510 : (unsigned long)va_arg(ap, int);
511 div_factor = (format_flag == 'o') ?
512 8 : (format_flag == 'u') ? 10 : 16;
514 goto INTEGRAL_CONVERSION;
518 if (sizeof(short) == sizeof(long))
520 if ( (long)(ulong = va_arg(ap, unsigned long)) < 0)
522 plus_space_flag = '-';
523 ulong = (unsigned long)(-((signed long)ulong));
526 else if (sizeof(short) == sizeof(int))
528 if ( (long)(ulong = l_L_modifier ?
529 va_arg(ap,unsigned long) : (unsigned long)va_arg(ap,int)) < 0)
531 plus_space_flag = '-';
532 ulong = (unsigned long)(-((signed long)ulong));
537 if ( (signed long)(ulong = (unsigned long) (h_modifier ?
538 (short) va_arg(ap, int) : va_arg(ap,int))) < 0)
540 plus_space_flag = '-';
541 ulong = (unsigned long)(-((signed long)ulong));
546 /* Now convert to digits */
548 ptr = buf_pointer = &buf[FRMWRI_BUFSIZE - 1];
549 nonzero_value = (ulong != 0);
551 /* No char if zero and zero precision */
552 if (precision != 0 || nonzero_value)
554 *--buf_pointer = hex[ulong % div_factor];
555 while (ulong /= div_factor);
557 /* "precision" takes precedence */
560 precision = field_width - (plus_space_flag != 0);
561 while (precision > (int)(ptr - buf_pointer))
562 *--buf_pointer = '0';
564 if (alternate_flag && nonzero_value)
566 if (format_flag == 'x' || format_flag == 'X')
568 *--buf_pointer = format_flag;
569 *--buf_pointer = '0';
571 else if ((format_flag == 'o') && (*buf_pointer != '0'))
573 *--buf_pointer = '0';
578 #if CONFIG_PRINTF > PRINTF_NOFLOAT
587 goto FLOATING_CONVERSION;
598 if (sizeof(double) != sizeof(long double))
600 if ( (fvalue = l_L_modifier ?
601 va_arg(ap,long double) : va_arg(ap,double)) < 0)
603 plus_space_flag = '-';
607 else if ( (fvalue = va_arg(ap,long double)) < 0)
609 plus_space_flag = '-';
612 ptr = float_conversion (fvalue,
614 buf_pointer += field_width,
620 precision = field_width - (plus_space_flag != 0);
621 while (precision > ptr - buf_pointer)
622 *--buf_pointer = '0';
626 #else /* CONFIG_PRINTF <= PRINTF_NOFLOAT */
632 ptr = buf_pointer = bad_conversion;
636 #endif /* CONFIG_PRINTF <= PRINTF_NOFLOAT */
638 case '\0': /* Really bad place to find NUL in */
642 /* Undefined conversion! */
643 ptr = buf_pointer = bad_conversion;
650 * This part emittes the formatted string to "put_one_char".
653 /* If field_width == 0 then nothing should be written. */
654 precision = ptr - buf_pointer;
656 if ( precision > field_width)
662 n = field_width - precision - (plus_space_flag != 0);
665 /* emit any leading pad characters */
669 put_one_char(' ', secret_pointer);
673 /* emit flag characters (if any) */
676 put_one_char(plus_space_flag, secret_pointer);
680 /* emit the string itself */
681 while (--precision >= 0)
683 put_one_char(*buf_pointer++, secret_pointer);
687 /* emit trailing space characters */
691 put_one_char(' ', secret_pointer);
696 #else /* PRINTF_REDUCED starts here */
698 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
699 char l_modifier, h_modifier;
700 unsigned long u_val, div_val;
702 unsigned int u_val, div_val;
703 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
706 unsigned int nr_of_chars, base;
711 for (;;) /* Until full format string read */
713 while ((format_flag = PGM_READ_CHAR(format++)) != '%') /* Until '%' or '\0' */
716 return (nr_of_chars);
717 put_one_char(format_flag, secret_pointer);
721 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
722 /*=================================*/
723 /* Optional 'l' or 'h' modifiers ? */
724 /*=================================*/
725 l_modifier = h_modifier = 0;
726 switch (PGM_READ_CHAR(format))
738 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
740 switch (format_flag = PGM_READ_CHAR(format++))
743 format_flag = va_arg(ap, int);
745 put_one_char(format_flag, secret_pointer);
750 ptr = va_arg(ap, char *);
751 while ((format_flag = *ptr++))
753 put_one_char(format_flag, secret_pointer);
763 div_val = 0x40000000;
764 goto CONVERSION_LOOP;
771 div_val = 1000000000;
772 goto CONVERSION_LOOP;
780 div_val = 0x10000000;
783 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
785 u_val = (format_flag == 'd') ?
786 (short)va_arg(ap, int) : (unsigned short)va_arg(ap, int);
788 u_val = va_arg(ap, long);
790 u_val = (format_flag == 'd') ?
791 va_arg(ap,int) : va_arg(ap,unsigned int);
792 #else /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
793 u_val = va_arg(ap,int);
794 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
795 if (format_flag == 'd')
797 if (((int)u_val) < 0)
800 put_one_char('-', secret_pointer);
804 while (div_val > 1 && div_val > u_val)
810 outChar = (u_val / div_val) + '0';
813 if (format_flag == 'x')
814 outChar += 'a'-'9'-1;
816 outChar += 'A'-'9'-1;
818 put_one_char(outChar, secret_pointer);
825 } /* end switch(format_flag...) */
827 #endif /* CONFIG_PRINTF > PRINTF_REDUCED */
830 #endif /* CONFIG_PRINTF */