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