Use old default value for formatwr buffer.
[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                         case 'z':
509                                 flags.l_L_modifier = true;
510                                 format++;
511                                 break;
512                         case 'h':
513                                 flags.h_modifier = true;
514                                 format++;
515                                 break;
516                 }
517
518                 /*
519                  * At exit from the following switch, we will emit
520                  * the characters starting at "buf_pointer" and
521                  * ending at "ptr"-1
522                  */
523                 switch (format_flag = PGM_READ_CHAR(format++))
524                 {
525 #if CONFIG_PRINTF_N_FORMATTER
526                         case 'n':
527                                 if (sizeof(short) != sizeof(int))
528                                 {
529                                         if (sizeof(int) != sizeof(long))
530                                         {
531                                                 if (h_modifier)
532                                                         *va_arg(ap, short *) = nr_of_chars;
533                                                 else if (flags.l_L_modifier)
534                                                         *va_arg(ap, long *) = nr_of_chars;
535                                                 else
536                                                         *va_arg(ap, int *) = nr_of_chars;
537                                         }
538                                         else
539                                         {
540                                                 if (h_modifier)
541                                                         *va_arg(ap, short *) = nr_of_chars;
542                                                 else
543                                                         *va_arg(ap, int *) = nr_of_chars;
544                                         }
545                                 }
546                                 else
547                                 {
548                                         if (flags.l_L_modifier)
549                                                 *va_arg(ap, long *) = nr_of_chars;
550                                         else
551                                                 *va_arg(ap, int *) = nr_of_chars;
552                                 }
553                                 continue;
554 #endif
555                         case 'c':
556                                 buf[0] = va_arg(ap, int);
557                                 ptr++;
558                                 break;
559
560                         /* Custom formatter for strings in program memory. */
561                         case 'S':
562 #if CPU_HARVARD
563                                 flags.progmem = true;
564 #endif
565                                 /* Fall trough */
566
567                         case 's':
568                                 if ( !(buf_pointer = va_arg(ap, char *)) )
569                                         buf_pointer = null_pointer;
570                                 if (precision < 0)
571                                         precision = 10000;
572
573                                 /*
574                                  * Move `ptr' to the last character of the
575                                  * string that will be actually printed.
576                                  */
577                                 ptr = buf_pointer;
578 #if CPU_HARVARD
579                                 if (flags.progmem)
580                                 {
581                                         for (n=0; pgm_read_char(ptr) && n < precision; n++)
582                                                 ++ptr;
583                                 }
584                                 else
585 #endif
586                                 for (n=0; *ptr && n < precision; n++)
587                                         ++ptr;
588                                 break;
589
590 #if CONFIG_PRINTF_OCTAL_FORMATTER
591                         case 'o':
592                                 if (flags.alternate_flag && !precision)
593                                         precision++;
594 #endif
595                         case 'x':
596                                 hex = hex_tab;
597                         case 'u':
598                         case 'p':
599                         case 'X':
600                                 if (format_flag == 'p')
601 #if defined(__AVR__) || defined(__I196__) /* 16bit pointers */
602                                         ulong = (unsigned long)(unsigned short)va_arg(ap, char *);
603 #else /* 32bit pointers */
604                                         ulong = (unsigned long)va_arg(ap, char *);
605 #endif /* 32bit pointers */
606                                 else if (flags.l_L_modifier)
607                                         ulong = va_arg(ap, unsigned long);
608                                 else if (flags.h_modifier)
609                                         ulong = (unsigned long)(unsigned short)va_arg(ap, unsigned int);
610                                 else
611                                         ulong = va_arg(ap, unsigned int);
612
613                                 flags.div_factor =
614 #if CONFIG_PRINTF_OCTAL_FORMATTER
615                                         (format_flag == 'o') ? DIV_OCT :
616 #endif
617                                         (format_flag == 'u') ? DIV_DEC : DIV_HEX;
618                                 flags.plus_space_flag = PSF_NONE;
619                                 goto INTEGRAL_CONVERSION;
620
621                         case 'd':
622                         case 'i':
623                                 if (flags.l_L_modifier)
624                                         ulong = (unsigned long)(long)va_arg(ap, long);
625                                 else
626                                         ulong = (unsigned long)(long)va_arg(ap, int);
627
628                                 /* Extract sign */
629                                 if ((signed long)ulong < 0)
630                                 {
631                                         flags.plus_space_flag = PSF_MINUS;
632                                         ulong = (unsigned long)(-((signed long)ulong));
633                                 }
634
635                                 flags.div_factor = DIV_DEC;
636
637                                 /* Now convert to digits */
638 INTEGRAL_CONVERSION:
639                                 ptr = buf_pointer = &buf[FRMWRI_BUFSIZE - 1];
640                                 flags.nonzero_value = (ulong != 0);
641
642                                 /* No char if zero and zero precision */
643                                 if (precision != 0 || flags.nonzero_value)
644                                 {
645                                         switch (flags.div_factor)
646                                         {
647                                         case DIV_DEC:
648                                                 do
649                                                         *--buf_pointer = hex[ulong % 10];
650                                                 while (ulong /= 10);
651                                                 break;
652
653                                         case DIV_HEX:
654                                                 do
655                                                         *--buf_pointer = hex[ulong % 16];
656                                                 while (ulong /= 16);
657                                                 break;
658 #if CONFIG_PRINTF_OCTAL_FORMATTER
659                                         case DIV_OCT:
660                                                 do
661                                                         *--buf_pointer = hex[ulong % 8];
662                                                 while (ulong /= 8);
663                                                 break;
664 #endif
665                                         }
666                                 }
667
668                                 /* "precision" takes precedence */
669                                 if (precision < 0)
670                                         if (flags.zeropad)
671                                                 precision = field_width - (flags.plus_space_flag != PSF_NONE);
672                                 while (precision > (int)(ptr - buf_pointer))
673                                         *--buf_pointer = '0';
674
675                                 if (flags.alternate_flag && flags.nonzero_value)
676                                 {
677                                         if (format_flag == 'x' || format_flag == 'X')
678                                         {
679                                                 *--buf_pointer = format_flag;
680                                                 *--buf_pointer = '0';
681                                         }
682 #if CONFIG_PRINTF_OCTAL_FORMATTER
683                                         else if ((format_flag == 'o') && (*buf_pointer != '0'))
684                                         {
685                                                 *--buf_pointer = '0';
686                                         }
687 #endif
688                                 }
689                                 ASSERT(buf_pointer >= buf);
690                                 break;
691
692 #if CONFIG_PRINTF > PRINTF_NOFLOAT
693                         case 'g':
694                         case 'G':
695                                 n = 1;
696                                 format_flag -= 2;
697                                 if (! precision)
698                                 {
699                                         precision = 1;
700                                 }
701                                 goto FLOATING_CONVERSION;
702                         case 'f':
703                                 format_flag = 0;
704                         case 'e':
705                         case 'E':
706                                 n = 0;
707 FLOATING_CONVERSION:
708                                 if (precision < 0)
709                                 {
710                                         precision = 6;
711                                 }
712
713                                 if (sizeof(double) != sizeof(max_float_t))
714                                 {
715                                         fvalue = flags.l_L_modifier ?
716                                                 va_arg(ap,max_float_t) : va_arg(ap,double);
717                                 }
718                                 else
719                                         fvalue = va_arg(ap,max_float_t);
720
721                                 if (fvalue < 0)
722                                 {
723                                         flags.plus_space_flag = PSF_MINUS;
724                                         fvalue = -fvalue;
725                                 }
726                                 ptr = float_conversion (fvalue,
727                                                 (short)precision,
728                                                 buf_pointer += field_width,
729                                                 format_flag,
730                                                 (char)n,
731                                                 flags.alternate_flag);
732                                 if (flags.zeropad)
733                                 {
734                                         precision = field_width - (flags.plus_space_flag != PSF_NONE);
735                                         while (precision > ptr - buf_pointer)
736                                                 *--buf_pointer = '0';
737                                 }
738                                 break;
739
740 #endif /* CONFIG_PRINTF <= PRINTF_NOFLOAT */
741
742                         case '\0': /* Really bad place to find NUL in */
743                                 format--;
744
745                         default:
746                                 /* Undefined conversion! */
747                                 ptr = buf_pointer = bad_conversion;
748                                 ptr += sizeof(bad_conversion) - 1;
749                                 break;
750
751                 }
752
753                 /*
754                  * This part emittes the formatted string to "put_one_char".
755                  */
756
757                 /* If field_width == 0 then nothing should be written. */
758                 precision = ptr - buf_pointer;
759
760                 if ( precision > field_width)
761                 {
762                         n = 0;
763                 }
764                 else
765                 {
766                         n = field_width - precision - (flags.plus_space_flag != PSF_NONE);
767                 }
768
769                 /* emit any leading pad characters */
770                 if (!flags.left_adjust)
771                         while (--n >= 0)
772                         {
773                                 put_one_char(' ', secret_pointer);
774 #if CONFIG_PRINTF_COUNT_CHARS
775                                 nr_of_chars++;
776 #endif
777                         }
778
779                 /* emit flag characters (if any) */
780                 if (flags.plus_space_flag)
781                 {
782                         put_one_char(flags.plus_space_flag == PSF_PLUS ? '+' : '-', secret_pointer);
783 #if CONFIG_PRINTF_COUNT_CHARS
784                         nr_of_chars++;
785 #endif
786                 }
787
788 #if CPU_HARVARD
789                 if (flags.progmem)
790                 {
791                         while (--precision >= 0)
792                         {
793                                 put_one_char(pgm_read_char(buf_pointer++), secret_pointer);
794 #if CONFIG_PRINTF_COUNT_CHARS
795                                 nr_of_chars++;
796 #endif
797                         }
798                 }
799                 else
800 #endif /* CPU_HARVARD */
801                 {
802                         /* emit the string itself */
803                         while (--precision >= 0)
804                         {
805                                 put_one_char(*buf_pointer++, secret_pointer);
806 #if CONFIG_PRINTF_COUNT_CHARS
807                                 nr_of_chars++;
808 #endif
809                         }
810                 }
811
812                 /* emit trailing space characters */
813                 if (flags.left_adjust)
814                         while (--n >= 0)
815                         {
816                                 put_one_char(' ', secret_pointer);
817 #if CONFIG_PRINTF_COUNT_CHARS
818                                 nr_of_chars++;
819 #endif
820                         }
821         }
822
823 #else /* PRINTF_REDUCED starts here */
824
825 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
826         bool l_modifier, h_modifier;
827         unsigned long u_val, div_val;
828 #else
829         unsigned int u_val, div_val;
830 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
831
832         char format_flag;
833         unsigned int nr_of_chars, base;
834         char outChar;
835         char *ptr;
836
837         nr_of_chars = 0;
838         for (;;)    /* Until full format string read */
839         {
840                 while ((format_flag = PGM_READ_CHAR(format++)) != '%')    /* Until '%' or '\0' */
841                 {
842                         if (!format_flag)
843                                 return (nr_of_chars);
844                         put_one_char(format_flag, secret_pointer);
845                         nr_of_chars++;
846                 }
847
848 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
849                 /*
850                  * Optional 'l', 'z' or 'h' modifiers?
851                  */
852                 l_modifier = h_modifier = false;
853                 switch (PGM_READ_CHAR(format))
854                 {
855                         case 'l':
856                         case 'z':
857                                 /* for the 'z' modifier, we make this assumption */
858                                 STATIC_ASSERT(sizeof(size_t) == sizeof(long));
859                                 l_modifier = true;
860                                 format++;
861                                 break;
862
863                         case 'h':
864                                 h_modifier = true;
865                                 format++;
866                                 break;
867                 }
868 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
869
870                 switch (format_flag = PGM_READ_CHAR(format++))
871                 {
872                         case 'c':
873                                 format_flag = va_arg(ap, int);
874                         default:
875                                 put_one_char(format_flag, secret_pointer);
876                                 nr_of_chars++;
877                                 continue;
878
879                         case 's':
880                                 ptr = va_arg(ap, char *);
881                                 while ((format_flag = *ptr++))
882                                 {
883                                         put_one_char(format_flag, secret_pointer);
884                                         nr_of_chars++;
885                                 }
886                                 continue;
887
888                         case 'o':
889                                 base = 8;
890                                 if (IS_SHORT)
891                                         div_val = 0x8000;
892                                 else
893                                         div_val = 0x40000000;
894                                 goto CONVERSION_LOOP;
895
896                         case 'd':
897                                 base = 10;
898                                 if (IS_SHORT)
899                                         div_val = 10000;
900                                 else
901                                         div_val = 1000000000;
902                                 goto CONVERSION_LOOP;
903
904                         case 'X':
905                         case 'x':
906                                 base = 16;
907                                 if (IS_SHORT)
908                                         div_val = 0x1000;
909                                 else
910                                         div_val = 0x10000000;
911
912 CONVERSION_LOOP:
913 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
914                                 if (h_modifier)
915                                 {
916                                         if (format_flag == 'd')
917                                                 u_val = (short)va_arg(ap, int);
918                                         else
919                                                 u_val = (unsigned short)va_arg(ap, int);
920                                 }
921                                 else if (l_modifier)
922                                         u_val = va_arg(ap, long);
923                                 else
924                                 {
925                                         if (format_flag == 'd')
926                                                 u_val = va_arg(ap, int);
927                                         else
928                                                 u_val = va_arg(ap, unsigned int);
929                                 }
930
931 #else /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
932                                 u_val = va_arg(ap,int);
933 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
934                                 if (format_flag == 'd')
935                                 {
936                                         if (((int)u_val) < 0)
937                                         {
938                                                 u_val = - u_val;
939                                                 put_one_char('-', secret_pointer);
940                                                 nr_of_chars++;
941                                         }
942                                 }
943                                 while (div_val > 1 && div_val > u_val)
944                                 {
945                                         div_val /= base;
946                                 }
947                                 do
948                                 {
949                                         outChar = (u_val / div_val) + '0';
950                                         if (outChar > '9')
951                                         {
952                                                 if (format_flag == 'x')
953                                                         outChar += 'a'-'9'-1;
954                                                 else
955                                                         outChar += 'A'-'9'-1;
956                                         }
957                                         put_one_char(outChar, secret_pointer);
958                                         nr_of_chars++;
959                                         u_val %= div_val;
960                                         div_val /= base;
961                                 }
962                                 while (div_val);
963
964                 } /* end switch(format_flag...) */
965         }
966 #endif /* CONFIG_PRINTF > PRINTF_REDUCED */
967 }
968
969 #endif /* CONFIG_PRINTF */