* support/regression/tests/bug1057979.c:
[fw/sdcc] / 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  * This macro enables the use of the 'b' binary specifier and
115  * the use of "%b", "%hb" and "%lb"
116  */
117 /* #define BINARY_SPECIFIER */
118 /*
119  * This macro enables the use of the 'i' integer specifier and
120  * the use of "%u", "%lu", ... in place of "%ud", "%lud", ... .
121  * costs ~10 code words
122  */
123 #define EXTRA_INTEGER
124
125 #ifdef BINARY_SPECIFIER
126 /* "%lb" = "0" - "11111111111111111111111111111111" */
127 # define BUF_SIZE       33
128 #else
129 /* "%lo" = "0" - "37777777777" or  "-20000000000" - "17777777777" */
130 # define BUF_SIZE       13
131 #endif
132
133 #if _DEBUG
134 extern void io_long (unsigned long);
135 extern void io_str (char *);
136 extern void io_int (unsigned int);
137 #endif
138
139 int
140 vfprintf (FILE * stream, const char *fmt, va_list ap)
141 {
142   unsigned char radix;
143   unsigned char flong;
144   unsigned char fstr;
145   unsigned char fchar;
146 #ifdef FLOAT_PLACEHOLDER
147   unsigned char ffloat;
148 #endif
149   unsigned char nosign;
150   unsigned char upcase;
151 #ifdef FIELD_WIDTH
152   unsigned char fieldwidth;
153   unsigned char lalign;
154   char padchar;
155   char *str1;
156 #endif
157 #ifdef PRECISION
158   unsigned char precision;
159 #endif
160 #ifdef SIGN_MODIFIERS
161   unsigned char printsign;
162   char positivechar;
163 #endif
164   int count = 0;
165   char *str, *ch;
166   long val;
167   char buffer[BUF_SIZE];
168
169 #if _DEBUG
170   io_str ("vfprintf: ");
171   io_long ((unsigned long) stream);
172   io_long ((unsigned long) fmt);
173 #endif
174
175 //    va_start(ap,fmt);
176   ch = fmt;
177
178   while (*ch) //for (; *fmt ; fmt++ )
179     {
180       if (*ch == '%')
181         {
182           flong = 0;
183           fstr = 0;
184           fchar = 0;
185 #ifdef FLOAT_PLACEHOLDER
186           ffloat = 0;
187 #endif
188           nosign = 0;
189           radix = 10;
190           upcase = 0;
191 #ifdef FIELD_WIDTH
192           fieldwidth = 0;
193           lalign = 0;
194           padchar = ' ';
195 #endif
196 #ifdef PRECISION
197           // precision == -1 is used as an "unlimited" precision marker
198           precision = -1;
199 #endif
200 #ifdef SIGN_MODIFIERS
201           printsign = 0;
202           positivechar = '+';
203 #endif
204           ++ch;
205
206           if (*ch == '%')
207             {
208               __stream_putchar (stream, *ch);
209               ++count;
210               ++ch;
211               continue;
212             }
213
214 #ifdef FIELD_WIDTH
215           if (*ch == '0')
216             {
217               padchar = '0';
218               ++ch;
219             }
220
221           if (*ch == '-')
222             {
223               lalign = 1;
224               ++ch;
225             }
226 #endif
227 #ifdef SIGN_MODIFIERS
228           if (*ch == ' ')
229             {
230               printsign = 1;
231               positivechar = ' ';
232               ++ch;
233             }
234
235           if (*ch == '+')
236             {
237               printsign = 1;
238               ++ch;
239             }
240 #endif
241
242 #ifdef FIELD_WIDTH
243           if ((*ch >= '1') && (*ch <= '9'))
244             {
245               while ((*ch >= '0') && (*ch <= '9'))
246                 {
247                   fieldwidth = 10 * fieldwidth + (*ch) - '0';
248                   ++ch;
249                 }
250             }
251 #endif
252
253 #ifdef PRECISION
254           if (*ch == '.')
255             {
256               ++ch;
257               precision = 0;
258               while ((*ch >= '0') && (*ch <= '9'))
259                 {
260                   precision = 10 * precision + (*ch) - '0';
261                   ++ch;
262                 }
263             }
264 #endif
265
266           if (*ch == 'l')
267             {
268               flong = 1;
269               ++ch;
270             }
271           else if (*ch == 'h')
272             {
273               fchar = 1;
274               ++ch;
275             }
276
277           if (*ch == 'u')
278             {
279               nosign = 1;
280               ++ch;
281             }
282
283           if (*ch == 's')
284             {
285               fstr = 1;
286 #ifdef FIELD_WIDTH
287               padchar = ' ';    /* Strings are always space-padded */
288 #endif
289             }
290           else if (*ch == 'x')
291             radix = 16;
292           else if (*ch == 'X')
293             {
294               radix = 16;
295               upcase = 1;
296             }
297           else if (*ch == 'c')
298             radix = 0;
299           else if (*ch == 'o')
300             radix = 8;
301 #ifdef BINARX_SPECIFIER
302           else if (*ch == 'b')
303             radix = 2;
304 #endif
305 #ifdef FLOAT_PLACEHOLDER
306           else if (*ch == 'f')
307             {
308               ffloat = 1;
309 # ifdef PRECISION
310               precision = -1;
311 # endif
312             }
313 #endif
314 #ifdef EXTRA_INTEGER
315           else if ((*ch == 'd') || (*ch == 'i'))  /* This is the default */
316             ;
317           else if (nosign)                        /* %u alone is the same as %ud */
318             --ch;
319 #else
320           else if (*ch == 'd')
321             ;
322 #endif
323           else
324             {
325               __stream_putchar (stream, *ch);
326               ++count;
327               ++ch;
328               continue;
329             }
330
331           if (fstr)
332             {
333               str = va_arg (ap, char *);
334 #ifdef FLOAT_PLACEHOLDER
335             }
336           else if (ffloat)
337             {
338               str = "<NO FLOAT>";
339               va_arg (ap, float);
340 #endif
341             }
342           else
343             {
344 #ifdef PRECISION
345               precision = -1; //FIXME: No support for the precision field on numerals
346 #endif
347               val = 0;
348               if (flong)
349                 {
350                   val = va_arg (ap, long);
351 #if _DEBUG
352                   io_long (val);
353 #endif
354                 }
355               else if (fchar)
356                 {
357                   val = (char) va_arg (ap, int);  // FIXME: SDCC passes 1-byte char varargs as 2-byte ints...
358                   if ((radix != 10) || nosign)
359                     val = (unsigned char) val;    //Avoid unwanted sign extension
360 #if _DEBUG
361                   io_long (val);
362 #endif
363                 }
364               else
365                 {
366                   val = va_arg (ap, int);
367                   if ((radix != 10) || nosign)
368                     val = (unsigned int) val;   //Avoid unwanted sign extension
369 #if _DEBUG
370                   io_long (val);
371 #endif
372                 }
373
374               str = buffer + 1; //Reserve space for a forced '+'
375               if (radix)
376                 {
377                   if (nosign)
378                     ultoa (val, buffer + 1, radix);
379                   else
380                     ltoa (val, buffer + 1, radix);
381 #ifdef SIGN_MODIFIERS
382                   if (printsign && (*str != '-'))
383                     {
384                       --str;
385                       *str = positivechar;
386                     }
387 #endif
388                 }
389               else
390                 {
391                   *str = (unsigned char) val;
392                   *(str + 1) = '\0';
393                 }
394             }
395
396 #ifdef FIELD_WIDTH
397           //Count how many pad chars are required in fieldwidth
398           str1 = str;
399           while (fieldwidth && *str1)
400             {
401               ++str1;
402               --fieldwidth;
403             }
404           //Left padding
405           if (!lalign)
406             {
407               while (fieldwidth)
408                 {
409                   __stream_putchar (stream, padchar);
410                   ++count;
411                   --fieldwidth;
412                 }
413             }
414 #endif
415           while (*str
416 #ifdef PRECISION
417                  && (!~precision || precision--)
418 #endif
419             )
420             {
421               radix = *str;
422               if (upcase)
423                 {
424                   radix = toupper (radix);
425                 }
426               __stream_putchar (stream, radix);
427               ++str;
428               ++count;
429             }
430 #ifdef FIELD_WIDTH
431           //Right padding (with spaces)
432           if (lalign)
433             {
434               while (fieldwidth)
435                 {
436                   __stream_putchar (stream, ' ');
437                   ++count;
438                   --fieldwidth;
439                 }
440             }
441 #endif
442         }
443       else
444         {
445           __stream_putchar (stream, *ch);
446           ++count;
447         }
448
449       ++ch;
450     }
451
452   return count;
453 }