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