vfprintf.c - scc - simple c99 compiler
(HTM) git clone git://git.simple-cc.org/scc
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) Submodules
(DIR) README
(DIR) LICENSE
---
vfprintf.c (6547B)
---
1 #include <ctype.h>
2 #include <limits.h>
3 #include <stdarg.h>
4 #include <stdint.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include <wchar.h>
8
9 #include "../libc.h"
10
11 #define MAXPREC 50
12
13 #undef vfprintf
14
15 enum {
16 LONG = 1 << 0,
17 LLONG = 1 << 1,
18 SHORT = 1 << 2,
19 CHAR = 1 << 3,
20 SIZET = 1 << 4,
21 PTRDIFF = 1 << 5,
22 INTMAX = 1 << 6,
23 VOIDPTR = 1 << 7,
24 UNSIGNED = 1 << 8,
25 ALTFORM = 1 << 9,
26 };
27
28 struct conv {
29 int sign;
30 size_t prec;
31 char *digs;
32 int base;
33 };
34
35 static uintmax_t
36 getnum(va_list *va, int flags, int *sign)
37 {
38 uintmax_t uval;
39 intmax_t val;
40
41 if (flags & CHAR) {
42 val = va_arg(*va, int);
43 uval = (unsigned char) val;
44 } else if (flags & SHORT) {
45 val = va_arg(*va, int);
46 uval = (unsigned short) val;
47 } else if (flags & LONG) {
48 val = va_arg(*va, long);
49 uval = (unsigned long) val;
50 } else if (flags & LLONG) {
51 val = va_arg(*va, long long);
52 uval = (unsigned long long) val;
53 } else if (flags & SIZET) {
54 val = 0;
55 uval = va_arg(*va, size_t);
56 } else if (flags & INTMAX) {
57 val = va_arg(*va, intmax_t);
58 uval = (uintmax_t) val;
59 } else if (flags & VOIDPTR) {
60 uval = (uintmax_t) va_arg(*va, void *);
61 } else {
62 val = va_arg(*va, int);
63 uval = (unsigned) val;
64 }
65
66 if ((flags & UNSIGNED) == 0 && val < 0) {
67 *sign = '-';
68 uval = -val;
69 }
70 return uval;
71 }
72
73 static char *
74 numtostr(uintmax_t val, int flags, struct conv *conv, char *buf)
75 {
76 char *buf0 = buf;
77 int base = conv->base, prec = conv->prec;
78 uintmax_t oval = val;
79
80 *buf = '\0';
81 do {
82 *--buf = conv->digs[val % base];
83 val /= base;
84 } while (val > 0);
85
86 while (buf0 - buf < prec)
87 *--buf = '0';
88
89 if (flags & ALTFORM) {
90 if (base == 8 && *buf != '0') {
91 *--buf = '0';
92 } else if (base == 16 && oval != 0) {
93 *--buf = conv->digs[16];
94 *--buf = '0';
95 }
96 }
97 if (conv->sign)
98 *--buf = conv->sign;
99
100 return buf;
101 }
102
103 static void
104 savecnt(va_list *va, int flags, size_t cnt)
105 {
106 if (flags & CHAR)
107 *va_arg(*va, char*) = cnt;
108 else if (flags & SHORT)
109 *va_arg(*va, short*) = cnt;
110 else if (flags & LONG)
111 *va_arg(*va, long*) = cnt;
112 else if (flags & LLONG)
113 *va_arg(*va, long long*) = cnt;
114 else if (flags & SIZET)
115 *va_arg(*va, size_t*) = cnt;
116 else if (flags & INTMAX)
117 *va_arg(*va, intmax_t*) = cnt;
118 else
119 *va_arg(*va, int*) = cnt;
120 }
121
122 static size_t
123 wstrout(wchar_t *ws, size_t len, int width, int fill, FILE *restrict fp)
124 {
125 int left = 0, adjust, n;
126 size_t cnt = 0;
127 wchar_t wc;
128
129 if (width < 0) {
130 left = 1;
131 width = -width;
132 }
133
134 adjust = len < width ? width - len : 0;
135 if (left)
136 adjust = -adjust;
137
138 for ( ; adjust > 0; adjust--) {
139 _fputwc(fill, fp, &n);
140 cnt += n;
141 }
142
143 for ( ; len-- > 0 && (wc = *ws) != '\0'; ++ws) {
144 _fputwc(wc, fp, &n);
145 cnt += n;
146 }
147
148 for ( ; adjust < 0; adjust++) {
149 _fputwc(' ', fp, &n);
150 cnt += n;
151 }
152
153 return cnt;
154 }
155
156 static size_t
157 strout(char *s, size_t len, int width, int fill, FILE *restrict fp)
158 {
159 int left = 0, adjust, ch, prefix;
160 size_t cnt = 0;
161
162 if (width < 0) {
163 left = 1;
164 width = -width;
165 }
166
167 adjust = len < width ? width - len : 0;
168 if (left)
169 adjust = -adjust;
170
171 if (fill == '0') {
172 if (*s == '-' || *s == '+')
173 prefix = 1;
174 else if (*s == '0' && toupper(s[1]) == 'X')
175 prefix = 2;
176 else
177 prefix = 0;
178 while (prefix--) {
179 putc(*s++, fp);
180 ++cnt;
181 --len;
182 }
183 }
184
185 for ( ; adjust > 0; adjust--) {
186 putc(fill, fp);
187 ++cnt;
188 }
189
190 for ( ; len-- > 0 && (ch = *s) != '\0'; ++s) {
191 putc(ch, fp);
192 ++cnt;
193 }
194
195 for ( ; adjust < 0; adjust++) {
196 putc(' ', fp);
197 ++cnt;
198 }
199
200 return cnt;
201 }
202
203 int
204 vfprintf(FILE *restrict fp, const char *restrict fmt, va_list va)
205 {
206 int cnt, ch, n;
207 int flags, width, left, fill, prec;
208 size_t inc, len;
209 char *s;
210 wchar_t *ws;
211 struct conv conv;
212 char buf[MAXPREC+1];
213 wchar_t wbuf[2];
214 va_list va2;
215
216 va_copy(va2, va);
217 for (cnt = 0; ch = *fmt++; cnt += inc) {
218 if (ch != '%') {
219 putc(ch, fp);
220 inc = 1;
221 continue;
222 }
223
224 fill = ' ';
225 prec = left = flags = width = 0;
226 conv.prec = 0;
227 conv.base = 10;
228 conv.sign = '\0';
229 conv.digs = "0123456789ABCDEFX";
230
231 flags:
232 switch (*fmt++) {
233 case ' ':
234 if (conv.sign == '\0')
235 conv.sign = ' ';
236 goto flags;
237 case '+':
238 conv.sign = '+';
239 goto flags;
240 case '#':
241 flags |= ALTFORM;
242 goto flags;
243 case '.':
244 if (*fmt == '*') {
245 fmt++;
246 n = va_arg(va2, int);
247 } else {
248 for (n = 0; isdigit(ch = *fmt); fmt++)
249 n = n * 10 + ch - '0';
250 }
251 if (n >= 0) {
252 conv.prec = n;
253 prec = 1;
254 }
255 goto flags;
256 case '*':
257 width = va_arg(va2, int);
258 goto flags;
259 case '-':
260 left = 1;
261 ++fmt;
262 case '1':
263 case '2':
264 case '3':
265 case '4':
266 case '5':
267 case '6':
268 case '7':
269 case '8':
270 case '9':
271 --fmt;
272 for (n = 0; isdigit(ch = *fmt); ++fmt)
273 n = n * 10 + ch - '0';
274 if (left)
275 n = -n;
276 width = n;
277 goto flags;
278 case '0':
279 fill = '0';
280 goto flags;
281 case 'l':
282 flags += LONG;
283 goto flags;
284 case 'h':
285 flags += SHORT;
286 goto flags;
287 case '%':
288 ch = '%';
289 goto cout;
290 case 'c':
291 if (flags & LONG) {
292 wbuf[0] = va_arg(va2, wint_t);
293 wbuf[1] = L'\0';
294 ws = wbuf;
295 len = 1;
296 goto wstrout;
297 }
298 ch = va_arg(va2, int);
299 cout:
300 buf[0] = ch;
301 buf[1] = '\0';
302 s = buf;
303 len = 1;
304 goto strout;
305 case 'j':
306 flags |= INTMAX;
307 goto flags;
308 case 't':
309 flags |= PTRDIFF;
310 goto flags;
311 case 'z':
312 flags |= SIZET;
313 goto flags;
314 case 'u':
315 flags |= UNSIGNED;
316 case 'i':
317 case 'd':
318 conv.base = 10;
319 goto numeric;
320 case 'p':
321 flags |= VOIDPTR | ALTFORM;
322 case 'x':
323 conv.digs = "0123456789abcdefx";
324 case 'X':
325 numeric16:
326 conv.base = 16;
327 flags |= UNSIGNED;
328 goto numeric;
329 case 'o':
330 conv.base = 8;
331 flags |= UNSIGNED;
332 numeric:
333 if (conv.prec > MAXPREC)
334 conv.prec = MAXPREC;
335 s = numtostr(getnum(&va2, flags, &conv.sign),
336 flags,
337 &conv,
338 &buf[MAXPREC]);
339 len = &buf[MAXPREC] - s;
340 goto strout;
341 case 'L':
342 case 'a':
343 case 'A':
344 case 'e':
345 case 'E':
346 case 'f':
347 case 'g':
348 case 'G':
349 /* TODO */
350 break;
351 case 's':
352 if (flags & LONG) {
353 ws = va_arg(va2, wchar_t *);
354 len = wcslen(ws);
355 if (prec && len > conv.prec)
356 len = conv.prec;
357 goto wstrout;
358 } else {
359 s = va_arg(va2, char *);
360 len = strlen(s);
361 if (prec && len > conv.prec)
362 len = conv.prec;
363 goto strout;
364 }
365 wstrout:
366 inc = wstrout(ws, len, width, fill, fp);
367 break;
368 strout:
369 inc = strout(s, len, width, fill, fp);
370 break;
371 case 'n':
372 savecnt(&va2, flags, cnt);
373 break;
374 case '\0':
375 goto out_loop;
376 }
377 }
378
379 out_loop:
380 return (ferror(fp)) ? EOF : cnt;
381 }