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