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