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