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