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