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