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