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