4 * Copyright (C) 2003,2004 Develer S.r.l. (http://www.develer.com/)
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 chann-
14 * els 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.
20 * -DFLOAT_SUPPORT Full ANSI formatter
21 * -DNO_FLOATS Full ANSI except floats
22 * -DREDUCED_SUPPORT Reduced 'int' type of converter
24 * The reduced version of formatter is suitable when program size is critical
25 * rather than formatting power. This routine uses less than 20 bytes of
26 * stack space which makes it practical even in systems with less than 256
29 * The only formatting specifiers supported by the reduced formatter are:
31 * %% %c %s %d %o %x %X and %hd %ho %hx %hX %ld %lo %lx %lX
34 * It means that real variables are not supported as well as field
35 * width and precision arguments.
37 * The last eight (h and l modifiers) can easily be removed by
38 * removing the line "#define MODIFIERS_IN_REDUCED".
43 * Revision 1.1 2004/05/23 15:43:16 bernie
44 * Import mware modules.
51 #undef REDUCED_SUPPORT
55 #ifndef REDUCED_SUPPORT
56 #error -DFLOAT_SUPPORT, -DNO_FLOATS or -DREDUCED_SUPPORT missing
61 /* Make it easy for customers to disable h and l
62 modifiers in REDUCED_SUPPORT by removing the next #define */
63 #define MODIFIERS_IN_REDUCED
66 #include "compiler.h" /* progmem macros */
69 #ifndef FRMWRI_BUFSIZE
70 /*bernie: save some memory, who cares about floats with lots of decimals? */
71 /*#define FRMWRI_BUFSIZE 134*/
72 #define FRMWRI_BUFSIZE 32
79 #ifdef MODIFIERS_IN_REDUCED
80 #define IS_SHORT (h_modifier || (sizeof(int) == 2 && !l_modifier))
82 #define IS_SHORT (sizeof(int) == 2)
88 static char *float_conversion(MEM_ATTRIBUTE long double value,
89 MEM_ATTRIBUTE short nr_of_digits,
90 MEM_ATTRIBUTE char *buf,
91 MEM_ATTRIBUTE char format_flag,
92 MEM_ATTRIBUTE char g_flag,
93 MEM_ATTRIBUTE char alternate_flag)
95 MEM_ATTRIBUTE char *cp;
96 MEM_ATTRIBUTE char *buf_pointer;
97 MEM_ATTRIBUTE short n, i, dec_point_pos, integral_10_log;
104 while (value >= 1e11) /* To speed up things a bit */
107 integral_10_log += 10;
115 else if (value) /* Not just 0.0 */
117 while (value <= 1e-10) /* To speed up things a bit */
120 integral_10_log -= 10;
130 if (integral_10_log < nr_of_digits && integral_10_log >= -4)
133 nr_of_digits -= integral_10_log;
138 /* %#G - No removal of trailing zeros */
143 /* %G - Removal of trailing zeros */
155 /* Less than one... */
156 if (integral_10_log < 0)
158 *buf_pointer++ = '0';
159 if ((n = nr_of_digits) || alternate_flag)
161 *buf_pointer++ = '.';
164 while (--i > integral_10_log && nr_of_digits)
166 *buf_pointer++ = '0';
169 if (integral_10_log < (-n - 1))
171 /* Nothing more to do */
178 dec_point_pos = - integral_10_log;
183 while (i <= nr_of_digits )
185 value -= (long double)(n = (short)value); /* n=Digit value=Remainder */
186 value *= 10; /* Prepare for next shot */
187 *buf_pointer++ = n + '0';
188 if ( ! i++ && (nr_of_digits || alternate_flag))
190 *buf_pointer++ = '.';
194 /* Rounding possible */
198 cp = buf_pointer - 1;
203 if ( (*cp += n) == ('9' + 1) )
213 } while (cp-- > buf);
222 if (*(cp - 1) == '.')
249 /* %G - Remove trailing zeros */
252 while (*(buf_pointer - 1) == '0')
256 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;
273 *buf_pointer++ = '+';
280 *buf_pointer++ = (integral_10_log % 10) + '0';
281 integral_10_log /= 10;
282 } while ( integral_10_log || n < 2 );
283 for ( i = n ; n > 0 ; n-- )
284 *(buf_pointer - 11 - i + n) = *(buf_pointer - n);
287 return (buf_pointer);
290 #endif /* FLOAT_SUPPORT */
293 * This routine forms the core and entry of the formatter.
295 * The conversion performed conforms to the ANSI specification for "printf".
298 PGM_FUNC(_formatted_write)(const char * PGM_ATTR format,
299 void put_one_char(char, void *),
300 void *secret_pointer,
303 MEM_ATTRIBUTE static char bad_conversion[] = "???";
304 MEM_ATTRIBUTE static char null_pointer[] = "<NULL>";
306 #ifndef REDUCED_SUPPORT
307 MEM_ATTRIBUTE char format_flag;
308 MEM_ATTRIBUTE int precision;
310 MEM_ATTRIBUTE int field_width, nr_of_chars;
311 MEM_ATTRIBUTE char plus_space_flag, left_adjust, l_L_modifier;
312 MEM_ATTRIBUTE char h_modifier, alternate_flag;
313 MEM_ATTRIBUTE char nonzero_value;
314 MEM_ATTRIBUTE unsigned long ulong, div_factor;
317 MEM_ATTRIBUTE long double fvalue;
320 MEM_ATTRIBUTE char *buf_pointer;
321 MEM_ATTRIBUTE char *ptr;
322 MEM_ATTRIBUTE const char *hex;
323 MEM_ATTRIBUTE char zeropad;
324 MEM_ATTRIBUTE char buf[FRMWRI_BUFSIZE];
327 for (;;) /* Until full format string read */
329 while ((format_flag = PGM_READ_CHAR(format++)) != '%') /* Until '%' or '\0' */
332 return (nr_of_chars);
333 put_one_char (format_flag, secret_pointer);
336 if (PGM_READ_CHAR(format) == '%') /* %% prints as % */
339 put_one_char('%', secret_pointer);
344 plus_space_flag = left_adjust = alternate_flag = zeropad = 0;
345 ptr = buf_pointer = &buf[0];
346 hex = "0123456789ABCDEF";
348 /* check for leading '-', '+', ' ','#' or '0' flags */
351 switch (PGM_READ_CHAR(format))
357 plus_space_flag = PGM_READ_CHAR(format);
374 /* Optional field width (may be '*') */
375 if (PGM_READ_CHAR(format) == '*')
377 field_width = va_arg(ap, int);
380 field_width = -field_width;
388 while (PGM_READ_CHAR(format) >= '0' && PGM_READ_CHAR(format) <= '9')
389 field_width = field_width * 10 + (PGM_READ_CHAR(format++) - '0');
395 /* Optional precision (or '*') */
396 if (PGM_READ_CHAR(format) == '.')
398 if (PGM_READ_CHAR(++format) == '*')
400 precision = va_arg(ap, int);
406 while (PGM_READ_CHAR(format) >= '0' && PGM_READ_CHAR(format) <= '9')
407 precision = precision * 10 + (PGM_READ_CHAR(format++) - '0');
413 /* At this point, "left_adjust" is nonzero if there was
414 * a sign, "zeropad" is 1 if there was a leading zero
415 * and 0 otherwise, "field_width" and "precision"
416 * contain numbers corresponding to the digit strings
417 * before and after the decimal point, respectively,
418 * and "plus_space_flag" is either 0 (no flag) or
419 * contains a plus or space character. If there was no
420 * decimal point, "precision" will be -1.
423 l_L_modifier = h_modifier = 0;
425 /* Optional 'l','L' r 'h' modifier? */
426 switch (PGM_READ_CHAR(format))
440 * At exit from the following switch, we will emit
441 * the characters starting at "buf_pointer" and
444 switch (format_flag = PGM_READ_CHAR(format++))
448 if (sizeof(short) != sizeof(int))
450 if (sizeof(int) != sizeof(long))
453 *va_arg(ap, short *) = nr_of_chars;
454 else if (l_L_modifier)
455 *va_arg(ap, long *) = nr_of_chars;
457 *va_arg(ap, int *) = nr_of_chars;
462 *va_arg(ap, short *) = nr_of_chars;
464 *va_arg(ap, int *) = nr_of_chars;
470 *va_arg(ap, long *) = nr_of_chars;
472 *va_arg(ap, int *) = nr_of_chars;
477 buf[0] = va_arg(ap, int);
482 if ( !(buf_pointer = va_arg(ap, char *)) )
483 buf_pointer = null_pointer;
486 for (n=0; *buf_pointer++ && n < precision; n++)
493 if (alternate_flag && !precision)
496 hex = "0123456789abcdef";
500 if (format_flag == 'p')
501 #if defined(__AVR__) || defined(__I196__) /* 16bit pointers */
502 ulong = (unsigned long)(unsigned short)va_arg(ap, char *);
503 #else /* 32bit pointers */
504 ulong = (unsigned long)va_arg(ap, char *);
505 #endif /* 32bit pointers */
506 else if (sizeof(short) == sizeof(int))
507 ulong = l_L_modifier ?
508 va_arg(ap, unsigned long) : (unsigned long)va_arg(ap, unsigned int);
511 (unsigned long)(unsigned short) va_arg(ap, int)
512 : (unsigned long)va_arg(ap, int);
513 div_factor = (format_flag == 'o') ?
514 8 : (format_flag == 'u') ? 10 : 16;
516 goto INTEGRAL_CONVERSION;
520 if (sizeof(short) == sizeof(long))
522 if ( (long)(ulong = va_arg(ap, unsigned long)) < 0)
524 plus_space_flag = '-';
525 ulong = (unsigned long)(-((signed long)ulong));
528 else if (sizeof(short) == sizeof(int))
530 if ( (long)(ulong = l_L_modifier ?
531 va_arg(ap,unsigned long) : (unsigned long)va_arg(ap,int)) < 0)
533 plus_space_flag = '-';
534 ulong = (unsigned long)(-((signed long)ulong));
539 if ( (signed long)(ulong = (unsigned long) (h_modifier ?
540 (short) va_arg(ap, int) : va_arg(ap,int))) < 0)
542 plus_space_flag = '-';
543 ulong = (unsigned long)(-((signed long)ulong));
548 /* Now convert to digits */
550 ptr = buf_pointer = &buf[FRMWRI_BUFSIZE - 1];
551 nonzero_value = (ulong != 0);
553 /* No char if zero and zero precision */
554 if (precision != 0 || nonzero_value)
556 *--buf_pointer = hex[ulong % div_factor];
557 while (ulong /= div_factor);
559 /* "precision" takes precedence */
562 precision = field_width - (plus_space_flag != 0);
563 while (precision > (int)(ptr - buf_pointer))
564 *--buf_pointer = '0';
566 if (alternate_flag && nonzero_value)
568 if (format_flag == 'x' || format_flag == 'X')
570 *--buf_pointer = format_flag;
571 *--buf_pointer = '0';
573 else if ((format_flag == 'o') && (*buf_pointer != '0'))
575 *--buf_pointer = '0';
589 goto FLOATING_CONVERSION;
600 if (sizeof(double) != sizeof(long double))
602 if ( (fvalue = l_L_modifier ?
603 va_arg(ap,long double) : va_arg(ap,double)) < 0)
605 plus_space_flag = '-';
609 else if ( (fvalue = va_arg(ap,long double)) < 0)
611 plus_space_flag = '-';
614 ptr = float_conversion (fvalue,
616 buf_pointer += field_width,
622 precision = field_width - (plus_space_flag != 0);
623 while (precision > ptr - buf_pointer)
624 *--buf_pointer = '0';
628 #else /* !FLOAT_SUPPORT */
634 ptr = buf_pointer = bad_conversion;
638 #endif /* !FLOAT_SUPPORT */
640 case '\0': /* Really bad place to find NUL in */
644 /* Undefined conversion! */
645 ptr = buf_pointer = bad_conversion;
652 * This part emittes the formatted string to "put_one_char".
655 /* If field_width == 0 then nothing should be written. */
656 precision = ptr - buf_pointer;
658 if ( precision > field_width)
664 n = field_width - precision - (plus_space_flag != 0);
667 /* emit any leading pad characters */
671 put_one_char(' ', secret_pointer);
675 /* emit flag characters (if any) */
678 put_one_char(plus_space_flag, secret_pointer);
682 /* emit the string itself */
683 while (--precision >= 0)
685 put_one_char(*buf_pointer++, secret_pointer);
689 /* emit trailing space characters */
693 put_one_char(' ', secret_pointer);
698 #else /* REDUCED_SUPPORT STARTS HERE */
700 #ifdef MODIFIERS_IN_REDUCED
701 char l_modifier, h_modifier;
702 unsigned long u_val, div_val;
704 unsigned int u_val, div_val;
707 unsigned int nr_of_chars, base;
712 for (;;) /* Until full format string read */
714 while ((format_flag = PGM_READ_CHAR(format++)) != '%') /* Until '%' or '\0' */
717 return (nr_of_chars);
718 put_one_char (format_flag, secret_pointer);
722 #ifdef MODIFIERS_IN_REDUCED
723 /*=================================*/
724 /* Optional 'l' or 'h' modifiers ? */
725 /*=================================*/
726 l_modifier = h_modifier = 0;
727 switch (PGM_READ_CHAR(format))
741 switch (format_flag = PGM_READ_CHAR(format++))
744 format_flag = va_arg(ap, int);
746 put_one_char (format_flag, secret_pointer);
751 ptr = va_arg(ap, char *);
752 while (format_flag = *ptr++)
754 put_one_char (format_flag, secret_pointer);
764 div_val = 0x40000000;
765 goto CONVERSION_LOOP;
772 div_val = 1000000000;
773 goto CONVERSION_LOOP;
781 div_val = 0x10000000;
784 #ifdef MODIFIERS_IN_REDUCED
786 u_val = (format_flag == 'd') ?
787 (short)va_arg(ap, int) : (unsigned short)va_arg(ap, int);
789 u_val = va_arg(ap, long);
791 u_val = (format_flag == 'd') ?
792 va_arg(ap,int) : va_arg(ap,unsigned int);
794 u_val = va_arg(ap,int);
796 if (format_flag == 'd')
798 if (((int)u_val) < 0)
801 put_one_char ('-', secret_pointer);
805 while (div_val > 1 && div_val > u_val)
811 outChar = (u_val / div_val) + '0';
813 if (format_flag == 'x')
814 outChar += 'a'-'9'-1;
816 outChar += 'A'-'9'-1;
817 put_one_char (outChar, secret_pointer);
824 } /* end switch(format_flag...) */