struct / union in initializer, RFE #901.
[sdcc.git] / sdcc / device / lib / pic14 / libc / vfprintf.c
blob9bb1279e7df5b512c18264d9f57360c4d56e24e0
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 Modifications for PIC14 by
9 Copyright (C) 2019 Gonzalo Pérez de Olaguer Córdoba <salo@gpoc.es>
11 This library is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by the
13 Free Software Foundation; either version 2, or (at your option) any
14 later version.
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this library; see the file COPYING. If not, write to the
23 Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
24 MA 02110-1301, USA.
26 As a special exception, if you link this library with other files,
27 some of which are compiled with SDCC, to produce an executable,
28 this library does not by itself cause the resulting executable to
29 be covered by the GNU General Public License. This exception does
30 not however invalidate any other reasons why the executable file
31 might be covered by the GNU General Public License.
32 -------------------------------------------------------------------------*/
34 /* following formats are supported :-
35 format output type argument-type
36 %% - -
37 %u unsigned int
38 %u* unsigned *
39 %b binary int
40 %lb binary long
41 %hb binary char
42 %d decimal int
43 %lu unsigned long
44 %hu unsigned char
45 %l[di] decimal long
46 %lu[di] unsigned long
47 %h[di] decimal char
48 %hu[di] unsigned char
49 %[xX] hexadecimal int
50 %l[xX] hexadecimal long
51 %h[xX] hexadecimal char
52 %o octal int
53 %lo octal long
54 %ho octal char
55 %c character char
56 %s character generic pointer
57 Also supported are:
58 - the '0', '-' and ' ' alignment modifiers
59 - the '+' and ' ' modifiers
60 - the width field for integral types
61 - the precision field for strings
64 #include <ctype.h>
65 #include <stdarg.h>
66 #include <stdio.h>
67 #include <stdlib.h>
69 /***********************************************************
70 * The following switches enable some "advanced" features. *
71 * With all the switches enabled: *
72 * ; Statistics: *
73 * ; code size: 2062 (0x080e) bytes ( 1.57%) *
74 * ; 1031 (0x0407) words *
75 * ; udata size: 16 (0x0010) bytes ( 1.25%) *
76 * ; access size: 31 (0x001f) bytes *
77 * With all the switches disabled: *
78 * ; Statistics: *
79 * ; code size: 1278 (0x04fe) bytes ( 0.98%) *
80 * ; 639 (0x027f) words *
81 * ; udata size: 16 (0x0010) bytes ( 1.25%) *
82 * ; access size: 25 (0x0019) bytes *
83 ***********************************************************/
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.
91 #define FIELD_WIDTH
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.
99 #define PRECISION
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
106 #define SIGN_MODIFIERS
108 * With this macro defined, trying to print a float number
109 * will generate the "<NO FLOAT>" string.
110 * Costs ~25 code words
112 #define FLOAT_PLACEHOLDER
114 * With this macro defined, printing floats will work.
115 * This also enables PRECISION and disables FLOAT_PLACEHOLDER.
117 #define USE_FLOATS
119 * This macro enables the use of the 'b' binary specifier and
120 * the use of "%b", "%hb" and "%lb"
122 #define BINARY_SPECIFIER
124 * This macro enables the use of the 'i' integer specifier and
125 * the use of "%u", "%lu", ... in place of "%ud", "%lud", ... .
126 * costs ~10 code words
128 #define EXTRA_INTEGER
130 * Two ways to handle putchar and getchar.
132 #define _STATIC_PUTCHAR 0
133 #define _STATIC_GETCHAR 0
136 * END OF CONFIGURATION SETTINGS
139 #if defined(USE_FLOATS)
140 #define PRECISION
141 #undef FLOAT_PLACEHOLDER
142 /* x_ftoa requires up to 8 digits (integral part) + '.' + 24 digits
143 * (fractional part). Adding a sign and a NUL byte yields 35 byte. */
144 # define BUF_SIZE 36
145 #elif defined(BINARY_SPECIFIER)
146 /* "%lb" = "0" - "11111111111111111111111111111111" */
147 # define BUF_SIZE 33
148 #else
149 /* "%lo" = "0" - "37777777777" or "-20000000000" - "17777777777" */
150 # define BUF_SIZE 13
151 #endif
153 /* flags */
154 #define _LONG 0x0001
155 #define _STR 0x0002
156 #define _CHAR 0x0004
157 #define _PTR 0x0008
158 #define _FLOAT 0x1000 // only when USE_FLOATS or FLOAT_PLACEHOLDER
159 #define _UNSIGNED 0x0010
160 #define _UPCASE 0x0020
161 #define _LEFTALIGN 0x0100 // only when FIELD_WIDTH
162 #define _ZEROPAD 0x0200 // only when FIELD_WIDTH
163 #define _PRINTSIGN 0x0400 // only when SIGN_MODIFIERS
164 #define _PRINTBLANK 0x0800 // only when SIGN_MODIFIERS
166 /* precision value meaning unset */
167 #define _UNSET ((unsigned char)-1)
169 #if _STATIC_GETCHAR
171 static const char *_fmt;
172 static char ch;
173 static void _getchar (void)
175 ch = *_fmt++;
177 #define _GETCHAR() _getchar()
179 #else /* _STATIC_GETCHAR */
180 #define _GETCHAR() do { ch = *fmt++; } while(0)
181 #endif
183 #if _STATIC_PUTCHAR
185 static FILE *_stream;
186 static unsigned char count;
187 static int _putchar (unsigned char c)
189 if (fputc(c, _stream) < 0)
190 return 1;
191 count++;
192 return 0;
194 #define _PUTCHAR(c) do { if (_putchar(c)) goto _error; } while(0)
196 #else /* _STATIC_PUTCHAR */
197 #define _PUTCHAR(c) do { if (fputc(c, stream) < 0) goto _error; count++; } while(0)
198 #endif
201 vfprintf (FILE *stream, const char *fmt, va_list ap)
203 unsigned flags;
204 unsigned char radix; // 2(binary), 8(octal), 10(decimal), 16(hexadecimal) or 0(char)
206 #if defined(FLOAT_PLACEHOLDER) || defined(USE_FLOATS)
207 float f;
208 #endif
210 #ifdef FIELD_WIDTH
211 unsigned char fieldwidth; // field width in number of characters
212 #endif
214 #ifdef PRECISION
215 unsigned char precision; // precision width in number of characters
216 #endif
218 const char *cstr;
219 char *str;
220 long val;
221 char buffer[BUF_SIZE];
223 #if _STATIC_GETCHAR
224 _fmt = fmt;
225 #else
226 char ch;
227 #endif
229 #if _STATIC_PUTCHAR
230 _stream = stream;
231 count = 0;
232 #else
233 unsigned char count = 0;
234 #endif
236 while (1)
238 _GETCHAR();
239 if (ch == 0)
240 return count;
241 if (ch != '%')
243 _PUTCHAR(ch);
244 continue;
246 _GETCHAR();
247 if (ch == '%')
249 _PUTCHAR(ch);
250 continue;
253 flags = 0;
254 radix = 10;
256 #ifdef FIELD_WIDTH
257 fieldwidth = 0;
258 #endif
260 #ifdef PRECISION
261 // _UNSET is used as an "unlimited" precision marker
262 precision = _UNSET;
263 #endif
265 #ifdef FIELD_WIDTH
266 if (ch == '0')
268 flags |= _ZEROPAD;
269 _GETCHAR();
272 if (ch == '-')
274 flags |= _LEFTALIGN;
275 _GETCHAR();
277 #endif
279 #ifdef SIGN_MODIFIERS
280 if (ch == ' ')
282 flags |= _PRINTSIGN;
283 flags |= _PRINTBLANK;
284 _GETCHAR();
287 if (ch == '+')
289 flags |= _PRINTSIGN;
290 _GETCHAR();
292 #endif
294 #ifdef FIELD_WIDTH
295 if ((ch >= '1') && (ch <= '9'))
297 while ((ch >= '0') && (ch <= '9'))
299 fieldwidth = 10 * fieldwidth + (ch) - '0';
300 _GETCHAR();
303 #endif
305 #ifdef PRECISION
306 if (ch == '.')
308 _GETCHAR();
309 precision = 0;
310 while ((ch >= '0') && (ch <= '9'))
312 precision = 10 * precision + (ch) - '0';
313 _GETCHAR();
316 #endif
318 if (ch == 'l')
320 flags |= _LONG;
321 _GETCHAR();
323 else if (ch == 'h')
325 flags |= _CHAR;
326 _GETCHAR();
329 if (ch == 'u')
331 flags |= _UNSIGNED;
332 _GETCHAR();
335 if (ch == 's')
337 flags |= _STR;
338 #ifdef FIELD_WIDTH
339 flags &= ~_ZEROPAD; /* Strings are always space-padded */
340 #endif
342 else if (ch == 'p')
344 flags |= _PTR;
345 flags |= _UNSIGNED;
346 radix = 16;
348 else if (ch == 'x')
349 radix = 16;
350 else if (ch == 'X')
352 radix = 16;
353 flags |= _UPCASE;
355 else if (ch == 'c')
356 radix = 0;
357 else if (ch == 'o')
358 radix = 8;
359 #ifdef BINARY_SPECIFIER
360 else if (ch == 'b')
361 radix = 2;
362 #endif
363 #if defined(FLOAT_PLACEHOLDER) || defined(USE_FLOATS)
364 else if (ch == 'f')
366 flags |= _FLOAT;
368 #endif
369 #ifdef EXTRA_INTEGER
370 else if ((ch == 'd') || (ch == 'i')) /* This is the default */
372 else if (flags & _UNSIGNED) /* %u alone is the same as %ud */
373 #if _STATIC_GETCHAR
374 --_fmt;
375 #else
376 --fmt;
377 #endif
378 #else
379 else if (ch == 'd')
381 #endif
382 else
384 goto _error;
387 /* Here starts the formatting code */
389 if (flags & _STR)
391 str = va_arg (ap, char *);
393 #if defined(USE_FLOATS)
394 else if (flags & _FLOAT)
396 f = va_arg (ap, float);
397 str = buffer;
398 _ftoa (f, buffer, PREC(PREC_F, precision == _UNSET ? PREC_D : precision));
399 precision = _UNSET;
401 #elif defined(FLOAT_PLACEHOLDER)
402 else if (flags & _FLOAT)
404 /* read but ignore the argument */
405 f = va_arg (ap, float);
406 str = (char*)"<NO FLOAT>";
407 #ifdef PRECISION
408 precision = _UNSET;
409 #endif /* PRECISION */
411 #endif /* FLOAT_PLACEHOLDER */
412 else
414 #ifdef PRECISION
415 precision = _UNSET; //FIXME: No support for the precision field on numerals
416 #endif
417 val = 0;
418 if (flags & _LONG)
420 val = va_arg (ap, long);
422 else if (flags & _PTR)
424 str = va_arg (ap, void*);
425 val = 0x00ffffffUL & *(unsigned long*)&str;
427 else if (flags & _CHAR)
429 val = va_arg (ap, char);
430 if ((radix != 10) || (flags & _UNSIGNED))
431 val = (unsigned char) val; //Avoid unwanted sign extension
433 else
435 val = va_arg (ap, int);
436 if ((radix != 10) || (flags & _UNSIGNED))
437 val = (unsigned int) val; //Avoid unwanted sign extension
440 str = buffer + 1; //Reserve space for a forced '+'
441 if (radix)
443 if (flags & _UNSIGNED)
444 _ultoa (val, buffer + 1, radix);
445 else
446 _ltoa (val, buffer + 1, radix);
447 #ifdef SIGN_MODIFIERS
448 if ((flags & _PRINTSIGN) && (*str != '-'))
450 --str;
451 *str = (flags & _PRINTBLANK ? ' ' : '+');
453 #endif
455 else
457 *str = (unsigned char) val;
458 *(str + 1) = '\0';
462 #ifdef FIELD_WIDTH
463 //Count how many pad chars are required in fieldwidth
464 cstr = str;
465 while (fieldwidth && *cstr)
467 ++cstr;
468 --fieldwidth;
470 //Left padding
471 if (!(flags & _LEFTALIGN))
473 ch = flags & _ZEROPAD ? '0' : ' ';
474 while (fieldwidth)
476 _PUTCHAR(ch);
477 --fieldwidth;
480 #endif
482 #ifdef PRECISION
483 while (*str && ((precision == _UNSET) || precision--))
484 #else
485 while (*str)
486 #endif
488 ch = *str++;
489 if (radix == 16 && !(flags & _UPCASE))
491 ch = tolower (ch);
493 _PUTCHAR(ch);
496 #ifdef FIELD_WIDTH
497 //Right padding (with spaces)
498 if (flags & _LEFTALIGN)
500 while (fieldwidth)
502 _PUTCHAR(' ');
503 --fieldwidth;
506 #endif
510 _error:
511 return EOF;