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