Removed 'This file is part of DevLib ...'
[bertos.git] / mware / formatwr.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
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.
10  *
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.
15  *
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
19  *
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.
28  *
29  * Copyright 2003, 2004, 2005 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \version $Id$
34  *
35  * \brief Basic "printf", "sprintf" and "fprintf" formatter.
36  *
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.
40  *
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:
44  *
45  * \code
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
51  * \endcode
52  *
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)
58  *
59  * Code/data size in words on DSP56K with CodeWarrior 6.0:
60  *   PRINTF_FULL         1493/45
61  *   PRINTF_NOFLOAT      795/45
62  *   PRINTF_REDUCED      482/0
63  *   PRINTF_NOMODIFIERS  301/0
64  *
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
68  * bytes of user RAM.
69  *
70  * The only formatting specifiers supported by the reduced formatter are:
71  * \code
72  *    %% %c %s %d %o %x %X and %hd %ho %hx %hX %ld %lo %lx %lX
73  * \endcode
74  *
75  * It means that real variables are not supported as well as field
76  * width and precision arguments.
77  */
78
79 /*#*
80  *#* $Log$
81  *#* Revision 1.19  2006/09/20 13:58:17  marco
82  *#* Added z modifier in string format.
83  *#*
84  *#* Revision 1.18  2006/07/19 12:56:27  bernie
85  *#* Convert to new Doxygen style.
86  *#*
87  *#* Revision 1.17  2005/11/04 17:43:27  bernie
88  *#* Fix for LP64 architectures; Add some more tests.
89  *#*
90  *#* Revision 1.16  2005/07/19 07:25:46  bernie
91  *#* Use appconfig.h instead of cfg/config.h.
92  *#*
93  *#* Revision 1.15  2005/04/11 19:10:28  bernie
94  *#* Include top-level headers from cfg/ subdir.
95  *#*
96  *#* Revision 1.14  2005/03/01 23:26:22  bernie
97  *#* Use shared hextab.
98  *#*
99  *#* Revision 1.13  2005/02/18 12:33:25  bernie
100  *#* Avoid strlen().
101  *#*
102  *#* Revision 1.12  2005/02/16 20:28:03  bernie
103  *#* Add %S formatter.
104  *#*
105  *#* Revision 1.11  2005/02/16 16:51:29  bernie
106  *#* Simplify float code.
107  *#*
108  *#* Revision 1.10  2004/10/26 09:01:35  bernie
109  *#* Fix spacing.
110  *#*
111  *#* Revision 1.9  2004/09/14 21:06:23  bernie
112  *#* Spelling fix.
113  *#*
114  *#* Revision 1.8  2004/08/25 14:12:09  rasky
115  *#* Aggiornato il comment block dei log RCS
116  *#*/
117
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 */
123
124 #ifndef CONFIG_PRINTF_N_FORMATTER
125         /** Disable the arcane %n formatter. */
126         #define CONFIG_PRINTF_N_FORMATTER 0
127 #endif
128
129 #ifndef CONFIG_PRINTF_OCTAL_FORMATTER
130         /** Disable the %o formatter. */
131         #define CONFIG_PRINTF_OCTAL_FORMATTER 0
132 #endif
133
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)
136
137 #if CONFIG_PRINTF
138
139 #if CONFIG_PRINTF > PRINTF_NOFLOAT
140         #include <float.h>
141
142         /* Maximum precision for floating point values */
143         typedef long double max_float_t;
144
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
148 #else
149         /*
150          * Conservative estimate. Should be (probably) 12 (which is the size necessary
151          * to represent (2^32-1) in octal plus the sign bit.
152          */
153         #define FRMWRI_BUFSIZE 16
154 #endif
155
156 /* Probably useful for fancy microcontrollers such as the PIC, nobody knows. */
157 #ifndef MEM_ATTRIBUTE
158 #define MEM_ATTRIBUTE
159 #endif
160
161 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
162         #define IS_SHORT (h_modifier || (sizeof(int) == 2 && !l_modifier))
163 #else
164         #define IS_SHORT (sizeof(int) == 2)
165 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
166
167
168 #if CONFIG_PRINTF > PRINTF_NOFLOAT
169
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)
176 {
177         MEM_ATTRIBUTE char *cp;
178         MEM_ATTRIBUTE char *buf_pointer;
179         MEM_ATTRIBUTE short n, i, dec_point_pos, integral_10_log;
180
181         buf_pointer = buf;
182         integral_10_log = 0;
183
184         if (value >= 1)
185         {
186                 while (value >= 1e11) /* To speed up things a bit */
187                 {
188                         value /= 1e10;
189                         integral_10_log += 10;
190                 }
191                 while (value >= 10)
192                 {
193                         value /= 10;
194                         integral_10_log++;
195                 }
196         }
197         else if (value) /* Not just 0.0 */
198         {
199                 while (value <= 1e-10) /* To speed up things a bit */
200                 {
201                         value *= 1e10;
202                         integral_10_log -= 10;
203                 }
204                 while (value < 1)
205                 {
206                         value *= 10;
207                         integral_10_log--;
208                 }
209         }
210         if (g_flag)
211         {
212                 if (integral_10_log < nr_of_digits && integral_10_log >= -4)
213                 {
214                         format_flag = 0;
215                         nr_of_digits -= integral_10_log;
216                 }
217                 nr_of_digits--;
218                 if (alternate_flag)
219                         /* %#G - No removal of trailing zeros */
220                         g_flag = 0;
221                 else
222                         /* %G - Removal of trailing zeros */
223                         alternate_flag = true;
224         }
225
226         /* %e or %E */
227         if (format_flag)
228         {
229                 dec_point_pos = 0;
230         }
231         else
232         {
233                 /* Less than one... */
234                 if (integral_10_log < 0)
235                 {
236                         *buf_pointer++ = '0';
237                         if ((n = nr_of_digits) || alternate_flag)
238                                 *buf_pointer++ = '.';
239                         i = 0;
240                         while (--i > integral_10_log && nr_of_digits)
241                         {
242                                 *buf_pointer++ = '0';
243                                 nr_of_digits--;
244                         }
245                         if (integral_10_log < (-n - 1))
246                                 /* Nothing more to do */
247                                 goto CLEAN_UP;
248                         dec_point_pos = 1;
249                 }
250                 else
251                 {
252                         dec_point_pos = - integral_10_log;
253                 }
254         }
255
256         i = dec_point_pos;
257         while (i <= nr_of_digits )
258         {
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++ = '.';
264         }
265
266         /* Rounding possible */
267         if (value >= 5)
268         {
269                 n = 1; /* Carry */
270                 cp = buf_pointer - 1;
271                 do
272                 {
273                         if (*cp != '.')
274                         {
275                                 if ( (*cp += n) == ('9' + 1) )
276                                 {
277                                         *cp = '0';
278                                         n = 1;
279                                 }
280                                 else
281                                         n = 0;
282                         }
283                 } while (cp-- > buf);
284                 if (n)
285                 {
286                         /* %e or %E */
287                         if (format_flag)
288                         {
289                                 cp = buf_pointer;
290                                 while (cp > buf)
291                                 {
292                                         if (*(cp - 1) == '.')
293                                         {
294                                                 *cp = *(cp - 2);
295                                                 cp--;
296                                         }
297                                         else
298                                                 *cp = *(cp - 1);
299                                         cp--;
300                                 }
301                                 integral_10_log++;
302                         }
303                         else
304                         {
305                                 cp = ++buf_pointer;
306                                 while (cp > buf)
307                                 {
308                                         *cp = *(cp - 1);
309                                         cp--;
310                                 }
311                         }
312                         *buf = '1';
313                 }
314         }
315
316 CLEAN_UP:
317         /* %G - Remove trailing zeros */
318         if (g_flag)
319         {
320                 while (*(buf_pointer - 1) == '0')
321                         buf_pointer--;
322                 if (*(buf_pointer - 1) == '.')
323                         buf_pointer--;
324         }
325
326         /* %e or %E */
327         if (format_flag)
328         {
329                 *buf_pointer++ = format_flag;
330                 if (integral_10_log < 0)
331                 {
332                         *buf_pointer++ = '-';
333                         integral_10_log = -integral_10_log;
334                 }
335                 else
336                         *buf_pointer++ = '+';
337                 n = 0;
338                 buf_pointer +=10;
339                 do
340                 {
341                         n++;
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);
347                 buf_pointer -= 10;
348         }
349         return (buf_pointer);
350 }
351
352 #endif /* CONFIG_PRINTF > PRINTF_NOFLOAT */
353
354 /**
355  * This routine forms the core and entry of the formatter.
356  *
357  * The conversion performed conforms to the ANSI specification for "printf".
358  */
359 int
360 PGM_FUNC(_formatted_write)(const char * PGM_ATTR format,
361                 void put_one_char(char, void *),
362                 void *secret_pointer,
363                 va_list ap)
364 {
365 #if CONFIG_PRINTF > PRINTF_REDUCED
366         MEM_ATTRIBUTE static char bad_conversion[] = "???";
367         MEM_ATTRIBUTE static char null_pointer[] = "<NULL>";
368
369         MEM_ATTRIBUTE int precision;
370         MEM_ATTRIBUTE int n;
371 #if CONFIG_PRINTF_COUNT_CHARS
372         MEM_ATTRIBUTE int nr_of_chars;
373 #endif
374         MEM_ATTRIBUTE int field_width;
375         MEM_ATTRIBUTE char format_flag;
376         enum PLUS_SPACE_FLAGS {
377                 PSF_NONE, PSF_PLUS, PSF_MINUS
378         };
379         enum DIV_FACTOR {
380                 DIV_DEC, DIV_HEX,
381 #if CONFIG_PRINTF_OCTAL_FORMATTER
382                 DIV_OCT,
383 #endif
384         };
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;
389 #else
390                 enum DIV_FACTOR div_factor : 1;
391 #endif
392                 bool left_adjust : 1;
393                 bool l_L_modifier : 1;
394                 bool h_modifier : 1;
395                 bool alternate_flag : 1;
396                 bool nonzero_value : 1;
397                 bool zeropad : 1;
398 #if CPU_HARVARD
399                 bool progmem : 1;
400 #endif
401         } flags;
402         MEM_ATTRIBUTE unsigned long ulong;
403
404 #if CONFIG_PRINTF >  PRINTF_NOFLOAT
405         MEM_ATTRIBUTE max_float_t fvalue;
406 #endif
407
408         MEM_ATTRIBUTE char *buf_pointer;
409         MEM_ATTRIBUTE char *ptr;
410         MEM_ATTRIBUTE const char *hex;
411         MEM_ATTRIBUTE char buf[FRMWRI_BUFSIZE];
412
413 #if CONFIG_PRINTF_COUNT_CHARS
414         nr_of_chars = 0;
415 #endif
416         for (;;)    /* Until full format string read */
417         {
418                 while ((format_flag = PGM_READ_CHAR(format++)) != '%')    /* Until '%' or '\0' */
419                 {
420                         if (!format_flag)
421 #if CONFIG_PRINTF_RETURN_COUNT
422                                 return (nr_of_chars);
423 #else
424                                 return 0;
425 #endif
426                         put_one_char(format_flag, secret_pointer);
427 #if CONFIG_PRINTF_COUNT_CHARS
428                         nr_of_chars++;
429 #endif
430                 }
431                 if (PGM_READ_CHAR(format) == '%')    /* %% prints as % */
432                 {
433                         format++;
434                         put_one_char('%', secret_pointer);
435 #if CONFIG_PRINTF_COUNT_CHARS
436                         nr_of_chars++;
437 #endif
438                         continue;
439                 }
440
441                 flags.left_adjust = false;
442                 flags.alternate_flag = false;
443                 flags.plus_space_flag = PSF_NONE;
444                 flags.zeropad = false;
445 #if CPU_HARVARD
446                 flags.progmem = false;
447 #endif
448                 ptr = buf_pointer = &buf[0];
449                 hex = HEX_tab;
450
451                 /* check for leading '-', '+', ' ','#' or '0' flags  */
452                 for (;;)
453                 {
454                         switch (PGM_READ_CHAR(format))
455                         {
456                                 case ' ':
457                                         if (flags.plus_space_flag)
458                                                 goto NEXT_FLAG;
459                                 case '+':
460                                         flags.plus_space_flag = PSF_PLUS;
461                                         goto NEXT_FLAG;
462                                 case '-':
463                                         flags.left_adjust = true;
464                                         goto NEXT_FLAG;
465                                 case '#':
466                                         flags.alternate_flag = true;
467                                         goto NEXT_FLAG;
468                                 case '0':
469                                         flags.zeropad = true;
470                                         goto NEXT_FLAG;
471                         }
472                         break;
473 NEXT_FLAG:
474                         format++;
475                 }
476
477                 /* Optional field width (may be '*') */
478                 if (PGM_READ_CHAR(format) == '*')
479                 {
480                         field_width = va_arg(ap, int);
481                         if (field_width < 0)
482                         {
483                                 field_width = -field_width;
484                                 flags.left_adjust = true;
485                         }
486                         format++;
487                 }
488                 else
489                 {
490                         field_width = 0;
491                         while (PGM_READ_CHAR(format) >= '0' && PGM_READ_CHAR(format) <= '9')
492                                 field_width = field_width * 10 + (PGM_READ_CHAR(format++) - '0');
493                 }
494
495                 if (flags.left_adjust)
496                         flags.zeropad = false;
497
498                 /* Optional precision (or '*') */
499                 if (PGM_READ_CHAR(format) == '.')
500                 {
501                         if (PGM_READ_CHAR(++format) == '*')
502                         {
503                                 precision = va_arg(ap, int);
504                                 format++;
505                         }
506                         else
507                         {
508                                 precision = 0;
509                                 while (PGM_READ_CHAR(format) >= '0' && PGM_READ_CHAR(format) <= '9')
510                                         precision = precision * 10 + (PGM_READ_CHAR(format++) - '0');
511                         }
512                 }
513                 else
514                         precision = -1;
515
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.
524                  */
525
526                 flags.l_L_modifier = false;
527                 flags.h_modifier = false;
528
529                 /* Optional 'l','L','z' or 'h' modifier? */
530                 switch (PGM_READ_CHAR(format))
531                 {
532                         case 'l':
533                         case 'L':
534                         case 'z':
535                                 flags.l_L_modifier = true;
536                                 format++;
537                                 break;
538                         case 'h':
539                                 flags.h_modifier = true;
540                                 format++;
541                                 break;
542                 }
543
544                 /*
545                  * At exit from the following switch, we will emit
546                  * the characters starting at "buf_pointer" and
547                  * ending at "ptr"-1
548                  */
549                 switch (format_flag = PGM_READ_CHAR(format++))
550                 {
551 #if CONFIG_PRINTF_N_FORMATTER
552                         case 'n':
553                                 if (sizeof(short) != sizeof(int))
554                                 {
555                                         if (sizeof(int) != sizeof(long))
556                                         {
557                                                 if (h_modifier)
558                                                         *va_arg(ap, short *) = nr_of_chars;
559                                                 else if (flags.l_L_modifier)
560                                                         *va_arg(ap, long *) = nr_of_chars;
561                                                 else
562                                                         *va_arg(ap, int *) = nr_of_chars;
563                                         }
564                                         else
565                                         {
566                                                 if (h_modifier)
567                                                         *va_arg(ap, short *) = nr_of_chars;
568                                                 else
569                                                         *va_arg(ap, int *) = nr_of_chars;
570                                         }
571                                 }
572                                 else
573                                 {
574                                         if (flags.l_L_modifier)
575                                                 *va_arg(ap, long *) = nr_of_chars;
576                                         else
577                                                 *va_arg(ap, int *) = nr_of_chars;
578                                 }
579                                 continue;
580 #endif
581                         case 'c':
582                                 buf[0] = va_arg(ap, int);
583                                 ptr++;
584                                 break;
585
586                         /* Custom formatter for strings in program memory. */
587                         case 'S':
588 #if CPU_HARVARD
589                                 flags.progmem = true;
590 #endif
591                                 /* Fall trough */
592
593                         case 's':
594                                 if ( !(buf_pointer = va_arg(ap, char *)) )
595                                         buf_pointer = null_pointer;
596                                 if (precision < 0)
597                                         precision = 10000;
598
599                                 /*
600                                  * Move `ptr' to the last character of the
601                                  * string that will be actually printed.
602                                  */
603                                 ptr = buf_pointer;
604 #if CPU_HARVARD
605                                 if (flags.progmem)
606                                 {
607                                         for (n=0; pgm_read_char(ptr) && n < precision; n++)
608                                                 ++ptr;
609                                 }
610                                 else
611 #endif
612                                 for (n=0; *ptr && n < precision; n++)
613                                         ++ptr;
614                                 break;
615
616 #if CONFIG_PRINTF_OCTAL_FORMATTER
617                         case 'o':
618                                 if (flags.alternate_flag && !precision)
619                                         precision++;
620 #endif
621                         case 'x':
622                                 hex = hex_tab;
623                         case 'u':
624                         case 'p':
625                         case 'X':
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);
636                                 else
637                                         ulong = va_arg(ap, unsigned int);
638
639                                 flags.div_factor =
640 #if CONFIG_PRINTF_OCTAL_FORMATTER
641                                         (format_flag == 'o') ? DIV_OCT :
642 #endif
643                                         (format_flag == 'u') ? DIV_DEC : DIV_HEX;
644                                 flags.plus_space_flag = PSF_NONE;
645                                 goto INTEGRAL_CONVERSION;
646
647                         case 'd':
648                         case 'i':
649                                 if (flags.l_L_modifier)
650                                         ulong = (unsigned long)(long)va_arg(ap, long);
651                                 else
652                                         ulong = (unsigned long)(long)va_arg(ap, int);
653
654                                 /* Extract sign */
655                                 if ((signed long)ulong < 0)
656                                 {
657                                         flags.plus_space_flag = PSF_MINUS;
658                                         ulong = (unsigned long)(-((signed long)ulong));
659                                 }
660
661                                 flags.div_factor = DIV_DEC;
662
663                                 /* Now convert to digits */
664 INTEGRAL_CONVERSION:
665                                 ptr = buf_pointer = &buf[FRMWRI_BUFSIZE - 1];
666                                 flags.nonzero_value = (ulong != 0);
667
668                                 /* No char if zero and zero precision */
669                                 if (precision != 0 || flags.nonzero_value)
670                                 {
671                                         switch (flags.div_factor)
672                                         {
673                                         case DIV_DEC:
674                                                 do
675                                                         *--buf_pointer = hex[ulong % 10];
676                                                 while (ulong /= 10);
677                                                 break;
678
679                                         case DIV_HEX:
680                                                 do
681                                                         *--buf_pointer = hex[ulong % 16];
682                                                 while (ulong /= 16);
683                                                 break;
684 #if CONFIG_PRINTF_OCTAL_FORMATTER
685                                         case DIV_OCT:
686                                                 do
687                                                         *--buf_pointer = hex[ulong % 8];
688                                                 while (ulong /= 8);
689                                                 break;
690 #endif
691                                         }
692                                 }
693
694                                 /* "precision" takes precedence */
695                                 if (precision < 0)
696                                         if (flags.zeropad)
697                                                 precision = field_width - (flags.plus_space_flag != PSF_NONE);
698                                 while (precision > (int)(ptr - buf_pointer))
699                                         *--buf_pointer = '0';
700
701                                 if (flags.alternate_flag && flags.nonzero_value)
702                                 {
703                                         if (format_flag == 'x' || format_flag == 'X')
704                                         {
705                                                 *--buf_pointer = format_flag;
706                                                 *--buf_pointer = '0';
707                                         }
708 #if CONFIG_PRINTF_OCTAL_FORMATTER
709                                         else if ((format_flag == 'o') && (*buf_pointer != '0'))
710                                         {
711                                                 *--buf_pointer = '0';
712                                         }
713 #endif
714                                 }
715                                 ASSERT(buf_pointer >= buf);
716                                 break;
717
718 #if CONFIG_PRINTF > PRINTF_NOFLOAT
719                         case 'g':
720                         case 'G':
721                                 n = 1;
722                                 format_flag -= 2;
723                                 if (! precision)
724                                 {
725                                         precision = 1;
726                                 }
727                                 goto FLOATING_CONVERSION;
728                         case 'f':
729                                 format_flag = 0;
730                         case 'e':
731                         case 'E':
732                                 n = 0;
733 FLOATING_CONVERSION:
734                                 if (precision < 0)
735                                 {
736                                         precision = 6;
737                                 }
738
739                                 if (sizeof(double) != sizeof(max_float_t))
740                                 {
741                                         fvalue = flags.l_L_modifier ?
742                                                 va_arg(ap,max_float_t) : va_arg(ap,double);
743                                 }
744                                 else
745                                         fvalue = va_arg(ap,max_float_t);
746
747                                 if (fvalue < 0)
748                                 {
749                                         flags.plus_space_flag = PSF_MINUS;
750                                         fvalue = -fvalue;
751                                 }
752                                 ptr = float_conversion (fvalue,
753                                                 (short)precision,
754                                                 buf_pointer += field_width,
755                                                 format_flag,
756                                                 (char)n,
757                                                 flags.alternate_flag);
758                                 if (flags.zeropad)
759                                 {
760                                         precision = field_width - (flags.plus_space_flag != PSF_NONE);
761                                         while (precision > ptr - buf_pointer)
762                                                 *--buf_pointer = '0';
763                                 }
764                                 break;
765
766 #endif /* CONFIG_PRINTF <= PRINTF_NOFLOAT */
767
768                         case '\0': /* Really bad place to find NUL in */
769                                 format--;
770
771                         default:
772                                 /* Undefined conversion! */
773                                 ptr = buf_pointer = bad_conversion;
774                                 ptr += sizeof(bad_conversion) - 1;
775                                 break;
776
777                 }
778
779                 /*
780                  * This part emittes the formatted string to "put_one_char".
781                  */
782
783                 /* If field_width == 0 then nothing should be written. */
784                 precision = ptr - buf_pointer;
785
786                 if ( precision > field_width)
787                 {
788                         n = 0;
789                 }
790                 else
791                 {
792                         n = field_width - precision - (flags.plus_space_flag != PSF_NONE);
793                 }
794
795                 /* emit any leading pad characters */
796                 if (!flags.left_adjust)
797                         while (--n >= 0)
798                         {
799                                 put_one_char(' ', secret_pointer);
800 #if CONFIG_PRINTF_COUNT_CHARS
801                                 nr_of_chars++;
802 #endif
803                         }
804
805                 /* emit flag characters (if any) */
806                 if (flags.plus_space_flag)
807                 {
808                         put_one_char(flags.plus_space_flag == PSF_PLUS ? '+' : '-', secret_pointer);
809 #if CONFIG_PRINTF_COUNT_CHARS
810                         nr_of_chars++;
811 #endif
812                 }
813
814 #if CPU_HARVARD
815                 if (flags.progmem)
816                 {
817                         while (--precision >= 0)
818                         {
819                                 put_one_char(pgm_read_char(buf_pointer++), secret_pointer);
820 #if CONFIG_PRINTF_COUNT_CHARS
821                                 nr_of_chars++;
822 #endif
823                         }
824                 }
825                 else
826 #endif /* CPU_HARVARD */
827                 {
828                         /* emit the string itself */
829                         while (--precision >= 0)
830                         {
831                                 put_one_char(*buf_pointer++, secret_pointer);
832 #if CONFIG_PRINTF_COUNT_CHARS
833                                 nr_of_chars++;
834 #endif
835                         }
836                 }
837
838                 /* emit trailing space characters */
839                 if (flags.left_adjust)
840                         while (--n >= 0)
841                         {
842                                 put_one_char(' ', secret_pointer);
843 #if CONFIG_PRINTF_COUNT_CHARS
844                                 nr_of_chars++;
845 #endif
846                         }
847         }
848
849 #else /* PRINTF_REDUCED starts here */
850
851 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
852         bool l_modifier, h_modifier;
853         unsigned long u_val, div_val;
854 #else
855         unsigned int u_val, div_val;
856 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
857
858         char format_flag;
859         unsigned int nr_of_chars, base;
860         char outChar;
861         char *ptr;
862
863         nr_of_chars = 0;
864         for (;;)    /* Until full format string read */
865         {
866                 while ((format_flag = PGM_READ_CHAR(format++)) != '%')    /* Until '%' or '\0' */
867                 {
868                         if (!format_flag)
869                                 return (nr_of_chars);
870                         put_one_char(format_flag, secret_pointer);
871                         nr_of_chars++;
872                 }
873
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))
880                 {
881                         case 'l':
882                                 l_modifier = true;
883                                 format++;
884                                 break;
885
886                         case 'h':
887                                 h_modifier = true;
888                                 format++;
889                                 break;
890                 }
891 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
892
893                 switch (format_flag = PGM_READ_CHAR(format++))
894                 {
895                         case 'c':
896                                 format_flag = va_arg(ap, int);
897                         default:
898                                 put_one_char(format_flag, secret_pointer);
899                                 nr_of_chars++;
900                                 continue;
901
902                         case 's':
903                                 ptr = va_arg(ap, char *);
904                                 while ((format_flag = *ptr++))
905                                 {
906                                         put_one_char(format_flag, secret_pointer);
907                                         nr_of_chars++;
908                                 }
909                                 continue;
910
911                         case 'o':
912                                 base = 8;
913                                 if (IS_SHORT)
914                                         div_val = 0x8000;
915                                 else
916                                         div_val = 0x40000000;
917                                 goto CONVERSION_LOOP;
918
919                         case 'd':
920                                 base = 10;
921                                 if (IS_SHORT)
922                                         div_val = 10000;
923                                 else
924                                         div_val = 1000000000;
925                                 goto CONVERSION_LOOP;
926
927                         case 'X':
928                         case 'x':
929                                 base = 16;
930                                 if (IS_SHORT)
931                                         div_val = 0x1000;
932                                 else
933                                         div_val = 0x10000000;
934
935 CONVERSION_LOOP:
936 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
937                                 if (h_modifier)
938                                         u_val = (format_flag == 'd') ?
939                                                 (short)va_arg(ap, int) : (unsigned short)va_arg(ap, int);
940                                 else if (l_modifier)
941                                         u_val = va_arg(ap, long);
942                                 else
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')
949                                 {
950                                         if (((int)u_val) < 0)
951                                         {
952                                                 u_val = - u_val;
953                                                 put_one_char('-', secret_pointer);
954                                                 nr_of_chars++;
955                                         }
956                                 }
957                                 while (div_val > 1 && div_val > u_val)
958                                 {
959                                         div_val /= base;
960                                 }
961                                 do
962                                 {
963                                         outChar = (u_val / div_val) + '0';
964                                         if (outChar > '9')
965                                         {
966                                                 if (format_flag == 'x')
967                                                         outChar += 'a'-'9'-1;
968                                                 else
969                                                         outChar += 'A'-'9'-1;
970                                         }
971                                         put_one_char(outChar, secret_pointer);
972                                         nr_of_chars++;
973                                         u_val %= div_val;
974                                         div_val /= base;
975                                 }
976                                 while (div_val);
977
978                 } /* end switch(format_flag...) */
979         }
980 #endif /* CONFIG_PRINTF > PRINTF_REDUCED */
981 }
982
983 #endif /* CONFIG_PRINTF */