Imported Upstream version 2.9.0
[debian/cc1111] / device / lib / pic16 / libc / stdio / vfprintf.c
1 /*-----------------------------------------------------------------
2     vfprintf.c - source file for reduced version of printf
3
4     Modified for pic16 port, by Vangelis Rokas, 2005 (vrokas@otenet.gr)
5     Bug-fixed and feature-enhanced by Mauro Giachero, 2008 (mauro.giachero@gmail.com)
6
7     Written By - Sandeep Dutta . sandeep.dutta@usa.net (1999)
8
9     This library is free software; you can redistribute it and/or modify it
10     under the terms of the GNU Library General Public License as published by the
11     Free Software Foundation; either version 2, or (at your option) any
12     later version.
13
14     This library is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU Library General Public License for more details.
18
19     You should have received a copy of the GNU Library General Public License
20     along with this program; if not, write to the Free Software
21     Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23     In other words, you are welcome to use, share and improve this program.
24     You are forbidden to forbid anyone else to use, share and improve
25     what you give them.   Help stamp out software-hoarding!
26
27    As a special exception, if you link this library with other files,
28    some of which are compiled with SDCC, to produce an executable,
29    this library does not by itself cause the resulting executable
30    to be covered by the GNU General Public License.
31    This exception does not however invalidate any other reasons why
32    the executable file might be covered by the GNU General Public License.
33 -------------------------------------------------------------------------*/
34
35 /* following formats are supported :-
36    format     output type       argument-type
37      %%        -                   -
38      %u        unsigned            int
39      %u*       unsigned            *
40      %b        binary              int
41      %lb       binary              long
42      %hb       binary              char
43      %d        decimal             int
44      %lu       unsigned            long
45      %hu       unsigned            char
46      %l[di]    decimal             long
47      %lu[di]   unsigned            long
48      %h[di]    decimal             char
49      %hu[di]   unsigned            char
50      %[xX]     hexadecimal         int
51      %l[xX]    hexadecimal         long
52      %h[xX]    hexadecimal         char
53      %o        octal               int
54      %lo       octal               long
55      %ho       octal               char
56      %c        character           char
57      %s        character           generic pointer
58    Also supported are:
59    - the '0', '-' and ' ' alignment modifiers
60    - the '+' and ' ' modifiers
61    - the width field for integral types
62    - the precision field for strings
63 */
64
65 #include <ctype.h>
66 #include <stdarg.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69
70 /***********************************************************
71  * The following switches enable some "advanced" features. *
72  * With all the switches enabled:                          *
73  * ; Statistics:                                           *
74  * ; code size:     2062 (0x080e) bytes ( 1.57%)           *
75  * ;                1031 (0x0407) words                    *
76  * ; udata size:      16 (0x0010) bytes ( 1.25%)           *
77  * ; access size:     31 (0x001f) bytes                    *
78  * With all the switches disabled:                         *
79  * ; Statistics:                                           *
80  * ; code size:     1278 (0x04fe) bytes ( 0.98%)           *
81  * ;                 639 (0x027f) words                    *
82  * ; udata size:      16 (0x0010) bytes ( 1.25%)           *
83  * ; access size:     25 (0x0019) bytes                    *
84  ***********************************************************/
85 /*
86  * Define this to enable support of the field width, which
87  * allows to specify the minimum number of characters an
88  * integer must use.
89  * Costs ~200 code words and 3 bytes in access RAM.
90  */
91 #define FIELD_WIDTH
92 /*
93  * Define this to enable support of the precision, which
94  * allows to specify the maximum number of characters a
95  * string can use. Note that this implementation doesn't
96  * use this field for integers (as it should).
97  * Costs ~85 code words and 1 byte in access RAM.
98  */
99 #define PRECISION
100 /*
101  * Define this to enable support of the '+' and ' ' modifiers,
102  * which specify that a positive signed number must be
103  * preceded respectively with a '+' or a ' ' character.
104  * Costs ~70 code words and 2 words of access RAM
105  */
106 #define SIGN_MODIFIERS
107 /*
108  * With this macro defined, trying to print a float number
109  * will generate the "<NO FLOAT>" string.
110  * Costs ~25 code words
111  */
112 #define FLOAT_PLACEHOLDER
113 /*
114  * With this macro defined, printing floats will work.
115  * This also enables PRECISION and disables FLOAT_PLACEHOLDER.
116  */
117 #if defined(USE_FLOATS)
118   /* The configure script always defines USE_FLOATS to 0 or 1. */
119 # if USE_FLOATS < 1
120 #  undef USE_FLOATS
121 # endif
122 #else
123 /* # define USE_FLOATS */
124 #endif
125
126 #if defined(USE_FLOATS)
127 #define PRECISION
128 #undef FLOAT_PLACEHOLDER
129 #endif
130 /*
131  * This macro enables the use of the 'b' binary specifier and
132  * the use of "%b", "%hb" and "%lb"
133  */
134 /* #define BINARY_SPECIFIER */
135 /*
136  * This macro enables the use of the 'i' integer specifier and
137  * the use of "%u", "%lu", ... in place of "%ud", "%lud", ... .
138  * costs ~10 code words
139  */
140 #define EXTRA_INTEGER
141
142 #if defined(USE_FLOATS)
143 /* x_ftoa requires up to 8 digits (integral part) + '.' + 24 digits
144  * (fractional part). Adding a sign and a NUL byte yields 35 byte. */
145 # define BUF_SIZE       36
146 #elif defined(BINARY_SPECIFIER)
147 /* "%lb" = "0" - "11111111111111111111111111111111" */
148 # define BUF_SIZE       33
149 #else
150 /* "%lo" = "0" - "37777777777" or  "-20000000000" - "17777777777" */
151 # define BUF_SIZE       13
152 #endif
153
154 #if _DEBUG
155 extern void io_long (unsigned long);
156 extern void io_str (char *);
157 extern void io_int (unsigned int);
158 #endif
159
160 int
161 vfprintf (FILE * stream, const char *fmt, va_list ap)
162 {
163   unsigned char radix;
164   unsigned char flong;
165   unsigned char fstr;
166   unsigned char fchar;
167 #if defined(FLOAT_PLACEHOLDER) || defined(USE_FLOATS)
168   unsigned char ffloat;
169 #endif
170   unsigned char nosign;
171   unsigned char upcase;
172 #ifdef FIELD_WIDTH
173   unsigned char fieldwidth;
174   unsigned char lalign;
175   char padchar;
176   char *str1;
177 #endif
178 #ifdef PRECISION
179   unsigned char precision;
180 #endif
181 #ifdef SIGN_MODIFIERS
182   unsigned char printsign;
183   char positivechar;
184 #endif
185   int count = 0;
186   char *str, *ch;
187   long val;
188   char buffer[BUF_SIZE];
189   char *stringbuffer;
190
191   if (0x80 == (unsigned char)(((unsigned long)stream) >> 16)) {
192     /* strmputchar will modify *(char **)stream, thus confusing the user */
193     stringbuffer = (char *) stream;
194     stream = (FILE *) &stringbuffer;
195   }
196
197 #if _DEBUG
198   io_str ("vfprintf: ");
199   io_long ((unsigned long) stream);
200   io_long ((unsigned long) fmt);
201 #endif
202
203 //    va_start(ap,fmt);
204   ch = fmt;
205
206   while (*ch) //for (; *fmt ; fmt++ )
207     {
208       if (*ch == '%')
209         {
210           flong = 0;
211           fstr = 0;
212           fchar = 0;
213 #if defined(FLOAT_PLACEHOLDER) || defined(USE_FLOATS)
214           ffloat = 0;
215 #endif
216           nosign = 0;
217           radix = 10;
218           upcase = 0;
219 #ifdef FIELD_WIDTH
220           fieldwidth = 0;
221           lalign = 0;
222           padchar = ' ';
223 #endif
224 #ifdef PRECISION
225           // precision == -1 is used as an "unlimited" precision marker
226           precision = -1;
227 #endif
228 #ifdef SIGN_MODIFIERS
229           printsign = 0;
230           positivechar = '+';
231 #endif
232           ++ch;
233
234           if (*ch == '%')
235             {
236               __stream_putchar (stream, *ch);
237               ++count;
238               ++ch;
239               continue;
240             }
241
242 #ifdef FIELD_WIDTH
243           if (*ch == '0')
244             {
245               padchar = '0';
246               ++ch;
247             }
248
249           if (*ch == '-')
250             {
251               lalign = 1;
252               ++ch;
253             }
254 #endif
255 #ifdef SIGN_MODIFIERS
256           if (*ch == ' ')
257             {
258               printsign = 1;
259               positivechar = ' ';
260               ++ch;
261             }
262
263           if (*ch == '+')
264             {
265               printsign = 1;
266               ++ch;
267             }
268 #endif
269
270 #ifdef FIELD_WIDTH
271           if ((*ch >= '1') && (*ch <= '9'))
272             {
273               while ((*ch >= '0') && (*ch <= '9'))
274                 {
275                   fieldwidth = 10 * fieldwidth + (*ch) - '0';
276                   ++ch;
277                 }
278             }
279 #endif
280
281 #ifdef PRECISION
282           if (*ch == '.')
283             {
284               ++ch;
285               precision = 0;
286               while ((*ch >= '0') && (*ch <= '9'))
287                 {
288                   precision = 10 * precision + (*ch) - '0';
289                   ++ch;
290                 }
291             }
292 #endif
293
294           if (*ch == 'l')
295             {
296               flong = 1;
297               ++ch;
298             }
299           else if (*ch == 'h')
300             {
301               fchar = 1;
302               ++ch;
303             }
304
305           if (*ch == 'u')
306             {
307               nosign = 1;
308               ++ch;
309             }
310
311           if (*ch == 's')
312             {
313               fstr = 1;
314 #ifdef FIELD_WIDTH
315               padchar = ' ';    /* Strings are always space-padded */
316 #endif
317             }
318           else if (*ch == 'x')
319             radix = 16;
320           else if (*ch == 'X')
321             {
322               radix = 16;
323               upcase = 1;
324             }
325           else if (*ch == 'c')
326             radix = 0;
327           else if (*ch == 'o')
328             radix = 8;
329 #ifdef BINARX_SPECIFIER
330           else if (*ch == 'b')
331             radix = 2;
332 #endif
333 #if defined(FLOAT_PLACEHOLDER) || defined(USE_FLOATS)
334           else if (*ch == 'f')
335             {
336               ffloat = 1;
337             }
338 #endif
339 #ifdef EXTRA_INTEGER
340           else if ((*ch == 'd') || (*ch == 'i'))  /* This is the default */
341             ;
342           else if (nosign)                        /* %u alone is the same as %ud */
343             --ch;
344 #else
345           else if (*ch == 'd')
346             ;
347 #endif
348           else
349             {
350               __stream_putchar (stream, *ch);
351               ++count;
352               ++ch;
353               continue;
354             }
355
356           if (fstr)
357             {
358               str = va_arg (ap, char *);
359 #if defined(USE_FLOATS)
360             }
361           else if (ffloat)
362             {
363               float f = va_arg(ap, float);
364               str = buffer;
365               x_ftoa (f, buffer, BUF_SIZE, precision);
366               precision = -1;
367 #elif defined(FLOAT_PLACEHOLDER)
368             }
369           else if (ffloat)
370             {
371               str = "<NO FLOAT>";
372               va_arg (ap, float);
373 #ifdef PRECISION
374               precision = -1;
375 #endif /* PRECISION */
376 #endif /* FLOAT_PLACEHOLDER */
377             }
378           else
379             {
380 #ifdef PRECISION
381               precision = -1; //FIXME: No support for the precision field on numerals
382 #endif
383               val = 0;
384               if (flong)
385                 {
386                   val = va_arg (ap, long);
387 #if _DEBUG
388                   io_long (val);
389 #endif
390                 }
391               else if (fchar)
392                 {
393                   val = (char) va_arg (ap, int);  // FIXME: SDCC passes 1-byte char varargs as 2-byte ints...
394                   if ((radix != 10) || nosign)
395                     val = (unsigned char) val;    //Avoid unwanted sign extension
396 #if _DEBUG
397                   io_long (val);
398 #endif
399                 }
400               else
401                 {
402                   val = va_arg (ap, int);
403                   if ((radix != 10) || nosign)
404                     val = (unsigned int) val;   //Avoid unwanted sign extension
405 #if _DEBUG
406                   io_long (val);
407 #endif
408                 }
409
410               str = buffer + 1; //Reserve space for a forced '+'
411               if (radix)
412                 {
413                   if (nosign)
414                     ultoa (val, buffer + 1, radix);
415                   else
416                     ltoa (val, buffer + 1, radix);
417 #ifdef SIGN_MODIFIERS
418                   if (printsign && (*str != '-'))
419                     {
420                       --str;
421                       *str = positivechar;
422                     }
423 #endif
424                 }
425               else
426                 {
427                   *str = (unsigned char) val;
428                   *(str + 1) = '\0';
429                 }
430             }
431
432 #ifdef FIELD_WIDTH
433           //Count how many pad chars are required in fieldwidth
434           str1 = str;
435           while (fieldwidth && *str1)
436             {
437               ++str1;
438               --fieldwidth;
439             }
440           //Left padding
441           if (!lalign)
442             {
443               while (fieldwidth)
444                 {
445                   __stream_putchar (stream, padchar);
446                   ++count;
447                   --fieldwidth;
448                 }
449             }
450 #endif
451           while (*str
452 #ifdef PRECISION
453                  && (!~precision || precision--)
454 #endif
455             )
456             {
457               radix = *str;
458               if (upcase)
459                 {
460                   radix = toupper (radix);
461                 }
462               __stream_putchar (stream, radix);
463               ++str;
464               ++count;
465               if (fieldwidth)
466                 {
467                   fieldwidth--;
468                 }
469             }
470 #ifdef FIELD_WIDTH
471           //Right padding (with spaces)
472           if (lalign)
473             {
474               while (fieldwidth)
475                 {
476                   __stream_putchar (stream, ' ');
477                   ++count;
478                   --fieldwidth;
479                 }
480             }
481 #endif
482         }
483       else
484         {
485           __stream_putchar (stream, *ch);
486           ++count;
487         }
488
489       ++ch;
490     }
491
492   return count;
493 }