Prepare for SDCC 4.5.0 release.
[sdcc.git] / sdcc / device / lib / pic16 / libc / stdio / vfprintf.c
blobd022328ba9eb71f4f520feab7f55851f8cdcca6b
1 /*-------------------------------------------------------------------------
2 vfprintf.c - source file for reduced version of printf
4 Copyright (C) 1999, Sandeep Dutta <sandeep.dutta AT ieee.org>
5 Modified for pic16 port, by Vangelis Rokas, 2005 <vrokas AT otenet.gr>
6 Bug-fixed and feature-enhanced by Mauro Giachero, 2008 <mauro.giachero AT gmail.com>
8 This library is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 2, or (at your option) any
11 later version.
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this library; see the file COPYING. If not, write to the
20 Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
21 MA 02110-1301, USA.
23 As a special exception, if you link this library with other files,
24 some of which are compiled with SDCC, to produce an executable,
25 this library does not by itself cause the resulting executable to
26 be covered by the GNU General Public License. This exception does
27 not however invalidate any other reasons why the executable file
28 might be covered by the GNU General Public License.
29 -------------------------------------------------------------------------*/
31 /* following formats are supported :-
32 format output type argument-type
33 %% - -
34 %u unsigned int
35 %u* unsigned *
36 %b binary int
37 %lb binary long
38 %hb binary char
39 %d decimal int
40 %lu unsigned long
41 %hu unsigned char
42 %l[di] decimal long
43 %lu[di] unsigned long
44 %h[di] decimal char
45 %hu[di] unsigned char
46 %[xX] hexadecimal int
47 %l[xX] hexadecimal long
48 %h[xX] hexadecimal char
49 %o octal int
50 %lo octal long
51 %ho octal char
52 %c character char
53 %s character generic pointer
54 Also supported are:
55 - the '0', '-' and ' ' alignment modifiers
56 - the '+' and ' ' modifiers
57 - the width field for integral types
58 - the precision field for strings
61 #include <ctype.h>
62 #include <stdarg.h>
63 #include <stdio.h>
64 #include <stdlib.h>
66 /***********************************************************
67 * The following switches enable some "advanced" features. *
68 * With all the switches enabled: *
69 * ; Statistics: *
70 * ; code size: 2062 (0x080e) bytes ( 1.57%) *
71 * ; 1031 (0x0407) words *
72 * ; udata size: 16 (0x0010) bytes ( 1.25%) *
73 * ; access size: 31 (0x001f) bytes *
74 * With all the switches disabled: *
75 * ; Statistics: *
76 * ; code size: 1278 (0x04fe) bytes ( 0.98%) *
77 * ; 639 (0x027f) words *
78 * ; udata size: 16 (0x0010) bytes ( 1.25%) *
79 * ; access size: 25 (0x0019) bytes *
80 ***********************************************************/
82 * Define this to enable support of the field width, which
83 * allows to specify the minimum number of characters an
84 * integer must use.
85 * Costs ~200 code words and 3 bytes in access RAM.
87 #define FIELD_WIDTH
89 * Define this to enable support of the precision, which
90 * allows to specify the maximum number of characters a
91 * string can use. Note that this implementation doesn't
92 * use this field for integers (as it should).
93 * Costs ~85 code words and 1 byte in access RAM.
95 #define PRECISION
97 * Define this to enable support of the '+' and ' ' modifiers,
98 * which specify that a positive signed number must be
99 * preceded respectively with a '+' or a ' ' character.
100 * Costs ~70 code words and 2 words of access RAM
102 #define SIGN_MODIFIERS
104 * With this macro defined, trying to print a float number
105 * will generate the "<NO FLOAT>" string.
106 * Costs ~25 code words
108 #define FLOAT_PLACEHOLDER
110 * With this macro defined, printing floats will work.
111 * This also enables PRECISION and disables FLOAT_PLACEHOLDER.
113 #if defined(USE_FLOATS)
114 /* The configure script always defines USE_FLOATS to 0 or 1. */
115 # if USE_FLOATS < 1
116 # undef USE_FLOATS
117 # endif
118 #else
119 /* # define USE_FLOATS */
120 #endif
122 #if defined(USE_FLOATS)
123 #define PRECISION
124 #undef FLOAT_PLACEHOLDER
125 #endif
127 * This macro enables the use of the 'b' binary specifier and
128 * the use of "%b", "%hb" and "%lb"
130 /* #define BINARY_SPECIFIER */
132 * This macro enables the use of the 'i' integer specifier and
133 * the use of "%u", "%lu", ... in place of "%ud", "%lud", ... .
134 * costs ~10 code words
136 #define EXTRA_INTEGER
138 #if defined(USE_FLOATS)
139 /* x_ftoa requires up to 8 digits (integral part) + '.' + 24 digits
140 * (fractional part). Adding a sign and a NUL byte yields 35 byte. */
141 # define BUF_SIZE 36
142 #elif defined(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
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
157 vfprintf (FILE * stream, const char *fmt, va_list ap)
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 const 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 const char *ch;
183 char *str;
184 long val;
185 char buffer[BUF_SIZE];
186 char *stringbuffer;
188 if (0x80 == (unsigned char)(((unsigned long)stream) >> 16)) {
189 /* strmputchar will modify *(char **)stream, thus confusing the user */
190 stringbuffer = (char *) stream;
191 stream = (FILE *) &stringbuffer;
194 #if _DEBUG
195 io_str ("vfprintf: ");
196 io_long ((unsigned long) stream);
197 io_long ((unsigned long) fmt);
198 #endif
200 // va_start(ap,fmt);
201 ch = fmt;
203 while (*ch) //for (; *fmt ; fmt++ )
205 if (*ch == '%')
207 flong = 0;
208 fstr = 0;
209 fchar = 0;
210 #if defined(FLOAT_PLACEHOLDER) || defined(USE_FLOATS)
211 ffloat = 0;
212 #endif
213 nosign = 0;
214 radix = 10;
215 upcase = 0;
216 #ifdef FIELD_WIDTH
217 fieldwidth = 0;
218 lalign = 0;
219 padchar = ' ';
220 #endif
221 #ifdef PRECISION
222 // precision == -1 is used as an "unlimited" precision marker
223 precision = (unsigned char)-1;
224 #endif
225 #ifdef SIGN_MODIFIERS
226 printsign = 0;
227 positivechar = '+';
228 #endif
229 ++ch;
231 if (*ch == '%')
233 __stream_putchar (stream, *ch);
234 ++count;
235 ++ch;
236 continue;
239 #ifdef FIELD_WIDTH
240 if (*ch == '0')
242 padchar = '0';
243 ++ch;
246 if (*ch == '-')
248 lalign = 1;
249 ++ch;
251 #endif
252 #ifdef SIGN_MODIFIERS
253 if (*ch == ' ')
255 printsign = 1;
256 positivechar = ' ';
257 ++ch;
260 if (*ch == '+')
262 printsign = 1;
263 ++ch;
265 #endif
267 #ifdef FIELD_WIDTH
268 if ((*ch >= '1') && (*ch <= '9'))
270 while ((*ch >= '0') && (*ch <= '9'))
272 fieldwidth = 10 * fieldwidth + (*ch) - '0';
273 ++ch;
276 #endif
278 #ifdef PRECISION
279 if (*ch == '.')
281 ++ch;
282 precision = 0;
283 while ((*ch >= '0') && (*ch <= '9'))
285 precision = 10 * precision + (*ch) - '0';
286 ++ch;
289 #endif
291 if (*ch == 'l')
293 flong = 1;
294 ++ch;
296 else if (*ch == 'h')
298 fchar = 1;
299 ++ch;
302 if (*ch == 'u')
304 nosign = 1;
305 ++ch;
308 if (*ch == 's')
310 fstr = 1;
311 #ifdef FIELD_WIDTH
312 padchar = ' '; /* Strings are always space-padded */
313 #endif
315 else if (*ch == 'x')
316 radix = 16;
317 else if (*ch == 'X')
319 radix = 16;
320 upcase = 1;
322 else if (*ch == 'c')
323 radix = 0;
324 else if (*ch == 'o')
325 radix = 8;
326 #ifdef BINARX_SPECIFIER
327 else if (*ch == 'b')
328 radix = 2;
329 #endif
330 #if defined(FLOAT_PLACEHOLDER) || defined(USE_FLOATS)
331 else if (*ch == 'f')
333 ffloat = 1;
335 #endif
336 #ifdef EXTRA_INTEGER
337 else if ((*ch == 'd') || (*ch == 'i')) /* This is the default */
339 else if (nosign) /* %u alone is the same as %ud */
340 --ch;
341 #else
342 else if (*ch == 'd')
344 #endif
345 else
347 __stream_putchar (stream, *ch);
348 ++count;
349 ++ch;
350 continue;
353 if (fstr)
355 str = va_arg (ap, char *);
356 #if defined(USE_FLOATS)
358 else if (ffloat)
360 float f = va_arg(ap, float);
361 str = buffer;
362 x_ftoa (f, buffer, BUF_SIZE, precision);
363 precision = -1;
364 #elif defined(FLOAT_PLACEHOLDER)
366 else if (ffloat)
368 str = (char*)"<NO FLOAT>";
369 va_arg (ap, float);
370 #ifdef PRECISION
371 precision = (unsigned char)-1;
372 #endif /* PRECISION */
373 #endif /* FLOAT_PLACEHOLDER */
375 else
377 #ifdef PRECISION
378 precision = (unsigned char)-1; //FIXME: No support for the precision field on numerals
379 #endif
380 val = 0;
381 if (flong)
383 val = va_arg (ap, long);
384 #if _DEBUG
385 io_long (val);
386 #endif
388 else if (fchar)
390 val = (char) va_arg (ap, int); // FIXME: SDCC passes 1-byte char varargs as 2-byte ints...
391 if ((radix != 10) || nosign)
392 val = (unsigned char) val; //Avoid unwanted sign extension
393 #if _DEBUG
394 io_long (val);
395 #endif
397 else
399 val = va_arg (ap, int);
400 if ((radix != 10) || nosign)
401 val = (unsigned int) val; //Avoid unwanted sign extension
402 #if _DEBUG
403 io_long (val);
404 #endif
407 str = buffer + 1; //Reserve space for a forced '+'
408 if (radix)
410 if (nosign)
411 ultoa (val, buffer + 1, radix);
412 else
413 ltoa (val, buffer + 1, radix);
414 #ifdef SIGN_MODIFIERS
415 if (printsign && (*str != '-'))
417 --str;
418 *str = positivechar;
420 #endif
422 else
424 *str = (unsigned char) val;
425 *(str + 1) = '\0';
429 #ifdef FIELD_WIDTH
430 //Count how many pad chars are required in fieldwidth
431 str1 = str;
432 while (fieldwidth && *str1)
434 ++str1;
435 --fieldwidth;
437 //Left padding
438 if (!lalign)
440 while (fieldwidth)
442 __stream_putchar (stream, padchar);
443 ++count;
444 --fieldwidth;
447 #endif
448 while (*str
449 #ifdef PRECISION
450 && (!~precision || precision--)
451 #endif
454 radix = *str;
455 if (upcase)
457 radix = toupper (radix);
459 __stream_putchar (stream, radix);
460 ++str;
461 ++count;
462 if (fieldwidth)
464 fieldwidth--;
467 #ifdef FIELD_WIDTH
468 //Right padding (with spaces)
469 if (lalign)
471 while (fieldwidth)
473 __stream_putchar (stream, ' ');
474 ++count;
475 --fieldwidth;
478 #endif
480 else
482 __stream_putchar (stream, *ch);
483 ++count;
486 ++ch;
489 return count;