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