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