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