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