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