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