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