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