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