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
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,
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
50 %l[xX] hexadecimal long
51 %h[xX] hexadecimal char
56 %s character generic pointer
58 - the '0', '-' and ' ' alignment modifiers
59 - the '+' and ' ' modifiers
60 - the width field for integral types
61 - the precision field for strings
69 /***********************************************************
70 * The following switches enable some "advanced" features. *
71 * With all the switches enabled: *
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: *
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
89 * Costs ~200 code words and 3 bytes in access RAM.
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.
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.
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)
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. */
145 #elif defined(BINARY_SPECIFIER)
146 /* "%lb" = "0" - "11111111111111111111111111111111" */
149 /* "%lo" = "0" - "37777777777" or "-20000000000" - "17777777777" */
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)
171 static const char *_fmt
;
173 static void _getchar (void)
177 #define _GETCHAR() _getchar()
179 #else /* _STATIC_GETCHAR */
180 #define _GETCHAR() do { ch = *fmt++; } while(0)
185 static FILE *_stream
;
186 static unsigned char count
;
187 static int _putchar (unsigned char c
)
189 if (fputc(c
, _stream
) < 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)
201 vfprintf (FILE *stream
, const char *fmt
, va_list ap
)
204 unsigned char radix
; // 2(binary), 8(octal), 10(decimal), 16(hexadecimal) or 0(char)
206 #if defined(FLOAT_PLACEHOLDER) || defined(USE_FLOATS)
211 unsigned char fieldwidth
; // field width in number of characters
215 unsigned char precision
; // precision width in number of characters
221 char buffer
[BUF_SIZE
];
233 unsigned char count
= 0;
261 // _UNSET is used as an "unlimited" precision marker
279 #ifdef SIGN_MODIFIERS
283 flags
|= _PRINTBLANK
;
295 if ((ch
>= '1') && (ch
<= '9'))
297 while ((ch
>= '0') && (ch
<= '9'))
299 fieldwidth
= 10 * fieldwidth
+ (ch
) - '0';
310 while ((ch
>= '0') && (ch
<= '9'))
312 precision
= 10 * precision
+ (ch
) - '0';
339 flags
&= ~_ZEROPAD
; /* Strings are always space-padded */
359 #ifdef BINARY_SPECIFIER
363 #if defined(FLOAT_PLACEHOLDER) || defined(USE_FLOATS)
370 else if ((ch
== 'd') || (ch
== 'i')) /* This is the default */
372 else if (flags
& _UNSIGNED
) /* %u alone is the same as %ud */
387 /* Here starts the formatting code */
391 str
= va_arg (ap
, char *);
393 #if defined(USE_FLOATS)
394 else if (flags
& _FLOAT
)
396 f
= va_arg (ap
, float);
398 _ftoa (f
, buffer
, PREC(PREC_F
, precision
== _UNSET
? PREC_D
: precision
));
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>";
409 #endif /* PRECISION */
411 #endif /* FLOAT_PLACEHOLDER */
415 precision
= _UNSET
; //FIXME: No support for the precision field on numerals
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
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 '+'
443 if (flags
& _UNSIGNED
)
444 _ultoa (val
, buffer
+ 1, radix
);
446 _ltoa (val
, buffer
+ 1, radix
);
447 #ifdef SIGN_MODIFIERS
448 if ((flags
& _PRINTSIGN
) && (*str
!= '-'))
451 *str
= (flags
& _PRINTBLANK
? ' ' : '+');
457 *str
= (unsigned char) val
;
463 //Count how many pad chars are required in fieldwidth
465 while (fieldwidth
&& *cstr
)
471 if (!(flags
& _LEFTALIGN
))
473 ch
= flags
& _ZEROPAD
? '0' : ' ';
483 while (*str
&& ((precision
== _UNSET
) || precision
--))
489 if (radix
== 16 && !(flags
& _UPCASE
))
497 //Right padding (with spaces)
498 if (flags
& _LEFTALIGN
)