1 | #include "stdio_impl.h"
|
---|
2 | #include <errno.h>
|
---|
3 | #include <ctype.h>
|
---|
4 | #include <limits.h>
|
---|
5 | #include <string.h>
|
---|
6 | #include <stdarg.h>
|
---|
7 | #include <stddef.h>
|
---|
8 | #include <wchar.h>
|
---|
9 | #include <inttypes.h>
|
---|
10 |
|
---|
11 | /* Convenient bit representation for modifier flags, which all fall
|
---|
12 | * within 31 codepoints of the space character. */
|
---|
13 |
|
---|
14 | #define ALT_FORM (1U<<'#'-' ')
|
---|
15 | #define ZERO_PAD (1U<<'0'-' ')
|
---|
16 | #define LEFT_ADJ (1U<<'-'-' ')
|
---|
17 | #define PAD_POS (1U<<' '-' ')
|
---|
18 | #define MARK_POS (1U<<'+'-' ')
|
---|
19 | #define GROUPED (1U<<'\''-' ')
|
---|
20 |
|
---|
21 | #define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS|GROUPED)
|
---|
22 |
|
---|
23 | /* State machine to accept length modifiers + conversion specifiers.
|
---|
24 | * Result is 0 on failure, or an argument type to pop on success. */
|
---|
25 |
|
---|
26 | enum {
|
---|
27 | BARE, LPRE, LLPRE, HPRE, HHPRE, BIGLPRE,
|
---|
28 | ZTPRE, JPRE,
|
---|
29 | STOP,
|
---|
30 | PTR, INT, UINT, ULLONG,
|
---|
31 | LONG, ULONG,
|
---|
32 | SHORT, USHORT, CHAR, UCHAR,
|
---|
33 | LLONG, SIZET, IMAX, UMAX, PDIFF, UIPTR,
|
---|
34 | DBL, LDBL,
|
---|
35 | NOARG,
|
---|
36 | MAXSTATE
|
---|
37 | };
|
---|
38 |
|
---|
39 | #define S(x) [(x)-'A']
|
---|
40 |
|
---|
41 | static const unsigned char states[]['z'-'A'+1] = {
|
---|
42 | { /* 0: bare types */
|
---|
43 | S('d') = INT, S('i') = INT,
|
---|
44 | S('o') = UINT, S('u') = UINT, S('x') = UINT, S('X') = UINT,
|
---|
45 | S('e') = DBL, S('f') = DBL, S('g') = DBL, S('a') = DBL,
|
---|
46 | S('E') = DBL, S('F') = DBL, S('G') = DBL, S('A') = DBL,
|
---|
47 | S('c') = CHAR, S('C') = INT,
|
---|
48 | S('s') = PTR, S('S') = PTR, S('p') = UIPTR, S('n') = PTR,
|
---|
49 | S('m') = NOARG,
|
---|
50 | S('l') = LPRE, S('h') = HPRE, S('L') = BIGLPRE,
|
---|
51 | S('z') = ZTPRE, S('j') = JPRE, S('t') = ZTPRE,
|
---|
52 | }, { /* 1: l-prefixed */
|
---|
53 | S('d') = LONG, S('i') = LONG,
|
---|
54 | S('o') = ULONG, S('u') = ULONG, S('x') = ULONG, S('X') = ULONG,
|
---|
55 | S('c') = INT, S('s') = PTR, S('n') = PTR,
|
---|
56 | S('l') = LLPRE,
|
---|
57 | }, { /* 2: ll-prefixed */
|
---|
58 | S('d') = LLONG, S('i') = LLONG,
|
---|
59 | S('o') = ULLONG, S('u') = ULLONG,
|
---|
60 | S('x') = ULLONG, S('X') = ULLONG,
|
---|
61 | S('n') = PTR,
|
---|
62 | }, { /* 3: h-prefixed */
|
---|
63 | S('d') = SHORT, S('i') = SHORT,
|
---|
64 | S('o') = USHORT, S('u') = USHORT,
|
---|
65 | S('x') = USHORT, S('X') = USHORT,
|
---|
66 | S('n') = PTR,
|
---|
67 | S('h') = HHPRE,
|
---|
68 | }, { /* 4: hh-prefixed */
|
---|
69 | S('d') = CHAR, S('i') = CHAR,
|
---|
70 | S('o') = UCHAR, S('u') = UCHAR,
|
---|
71 | S('x') = UCHAR, S('X') = UCHAR,
|
---|
72 | S('n') = PTR,
|
---|
73 | }, { /* 5: L-prefixed */
|
---|
74 | S('e') = LDBL, S('f') = LDBL, S('g') = LDBL, S('a') = LDBL,
|
---|
75 | S('E') = LDBL, S('F') = LDBL, S('G') = LDBL, S('A') = LDBL,
|
---|
76 | S('n') = PTR,
|
---|
77 | }, { /* 6: z- or t-prefixed (assumed to be same size) */
|
---|
78 | S('d') = PDIFF, S('i') = PDIFF,
|
---|
79 | S('o') = SIZET, S('u') = SIZET,
|
---|
80 | S('x') = SIZET, S('X') = SIZET,
|
---|
81 | S('n') = PTR,
|
---|
82 | }, { /* 7: j-prefixed */
|
---|
83 | S('d') = IMAX, S('i') = IMAX,
|
---|
84 | S('o') = UMAX, S('u') = UMAX,
|
---|
85 | S('x') = UMAX, S('X') = UMAX,
|
---|
86 | S('n') = PTR,
|
---|
87 | }
|
---|
88 | };
|
---|
89 |
|
---|
90 | #define OOB(x) ((unsigned)(x)-'A' > 'z'-'A')
|
---|
91 |
|
---|
92 | union arg
|
---|
93 | {
|
---|
94 | uintmax_t i;
|
---|
95 | long double f;
|
---|
96 | void *p;
|
---|
97 | };
|
---|
98 |
|
---|
99 | static void pop_arg(union arg *arg, int type, va_list *ap)
|
---|
100 | {
|
---|
101 | switch (type) {
|
---|
102 | case PTR: arg->p = va_arg(*ap, void *);
|
---|
103 | break; case INT: arg->i = va_arg(*ap, int);
|
---|
104 | break; case UINT: arg->i = va_arg(*ap, unsigned int);
|
---|
105 | break; case LONG: arg->i = va_arg(*ap, long);
|
---|
106 | break; case ULONG: arg->i = va_arg(*ap, unsigned long);
|
---|
107 | break; case ULLONG: arg->i = va_arg(*ap, unsigned long long);
|
---|
108 | break; case SHORT: arg->i = (short)va_arg(*ap, int);
|
---|
109 | break; case USHORT: arg->i = (unsigned short)va_arg(*ap, int);
|
---|
110 | break; case CHAR: arg->i = (signed char)va_arg(*ap, int);
|
---|
111 | break; case UCHAR: arg->i = (unsigned char)va_arg(*ap, int);
|
---|
112 | break; case LLONG: arg->i = va_arg(*ap, long long);
|
---|
113 | break; case SIZET: arg->i = va_arg(*ap, size_t);
|
---|
114 | break; case IMAX: arg->i = va_arg(*ap, intmax_t);
|
---|
115 | break; case UMAX: arg->i = va_arg(*ap, uintmax_t);
|
---|
116 | break; case PDIFF: arg->i = va_arg(*ap, ptrdiff_t);
|
---|
117 | break; case UIPTR: arg->i = (uintptr_t)va_arg(*ap, void *);
|
---|
118 | break; case DBL: arg->f = va_arg(*ap, double);
|
---|
119 | break; case LDBL: arg->f = va_arg(*ap, long double);
|
---|
120 | }
|
---|
121 | }
|
---|
122 |
|
---|
123 | static void out(FILE *f, const wchar_t *s, size_t l)
|
---|
124 | {
|
---|
125 | while (l-- && !(f->flags & F_ERR)) fputwc(*s++, f);
|
---|
126 | }
|
---|
127 |
|
---|
128 | static int getint(wchar_t **s) {
|
---|
129 | int i;
|
---|
130 | for (i=0; iswdigit(**s); (*s)++) {
|
---|
131 | if (i > INT_MAX/10U || **s-'0' > INT_MAX-10*i) i = -1;
|
---|
132 | else i = 10*i + (**s-'0');
|
---|
133 | }
|
---|
134 | return i;
|
---|
135 | }
|
---|
136 |
|
---|
137 | static const char sizeprefix['y'-'a'] = {
|
---|
138 | ['a'-'a']='L', ['e'-'a']='L', ['f'-'a']='L', ['g'-'a']='L',
|
---|
139 | ['d'-'a']='j', ['i'-'a']='j', ['o'-'a']='j', ['u'-'a']='j', ['x'-'a']='j',
|
---|
140 | ['p'-'a']='j'
|
---|
141 | };
|
---|
142 |
|
---|
143 | static int wprintf_core(FILE *f, const wchar_t *fmt, va_list *ap, union arg *nl_arg, int *nl_type)
|
---|
144 | {
|
---|
145 | wchar_t *a, *z, *s=(wchar_t *)fmt;
|
---|
146 | unsigned l10n=0, fl;
|
---|
147 | int w, p, xp;
|
---|
148 | union arg arg;
|
---|
149 | int argpos;
|
---|
150 | unsigned st, ps;
|
---|
151 | int cnt=0, l=0;
|
---|
152 | int i;
|
---|
153 | int t;
|
---|
154 | char *bs;
|
---|
155 | char charfmt[16];
|
---|
156 | wchar_t wc;
|
---|
157 |
|
---|
158 | for (;;) {
|
---|
159 | /* This error is only specified for snprintf, but since it's
|
---|
160 | * unspecified for other forms, do the same. Stop immediately
|
---|
161 | * on overflow; otherwise %n could produce wrong results. */
|
---|
162 | if (l > INT_MAX - cnt) goto overflow;
|
---|
163 |
|
---|
164 | /* Update output count, end loop when fmt is exhausted */
|
---|
165 | cnt += l;
|
---|
166 | if (!*s) break;
|
---|
167 |
|
---|
168 | /* Handle literal text and %% format specifiers */
|
---|
169 | for (a=s; *s && *s!='%'; s++);
|
---|
170 | for (z=s; s[0]=='%' && s[1]=='%'; z++, s+=2);
|
---|
171 | if (z-a > INT_MAX-cnt) goto overflow;
|
---|
172 | l = z-a;
|
---|
173 | if (f) out(f, a, l);
|
---|
174 | if (l) continue;
|
---|
175 |
|
---|
176 | if (iswdigit(s[1]) && s[2]=='$') {
|
---|
177 | l10n=1;
|
---|
178 | argpos = s[1]-'0';
|
---|
179 | s+=3;
|
---|
180 | } else {
|
---|
181 | argpos = -1;
|
---|
182 | s++;
|
---|
183 | }
|
---|
184 |
|
---|
185 | /* Read modifier flags */
|
---|
186 | for (fl=0; (unsigned)*s-' '<32 && (FLAGMASK&(1U<<*s-' ')); s++)
|
---|
187 | fl |= 1U<<*s-' ';
|
---|
188 |
|
---|
189 | /* Read field width */
|
---|
190 | if (*s=='*') {
|
---|
191 | if (iswdigit(s[1]) && s[2]=='$') {
|
---|
192 | l10n=1;
|
---|
193 | nl_type[s[1]-'0'] = INT;
|
---|
194 | w = nl_arg[s[1]-'0'].i;
|
---|
195 | s+=3;
|
---|
196 | } else if (!l10n) {
|
---|
197 | w = f ? va_arg(*ap, int) : 0;
|
---|
198 | s++;
|
---|
199 | } else goto inval;
|
---|
200 | if (w<0) fl|=LEFT_ADJ, w=-w;
|
---|
201 | } else if ((w=getint(&s))<0) goto overflow;
|
---|
202 |
|
---|
203 | /* Read precision */
|
---|
204 | if (*s=='.' && s[1]=='*') {
|
---|
205 | if (isdigit(s[2]) && s[3]=='$') {
|
---|
206 | nl_type[s[2]-'0'] = INT;
|
---|
207 | p = nl_arg[s[2]-'0'].i;
|
---|
208 | s+=4;
|
---|
209 | } else if (!l10n) {
|
---|
210 | p = f ? va_arg(*ap, int) : 0;
|
---|
211 | s+=2;
|
---|
212 | } else goto inval;
|
---|
213 | xp = (p>=0);
|
---|
214 | } else if (*s=='.') {
|
---|
215 | s++;
|
---|
216 | p = getint(&s);
|
---|
217 | xp = 1;
|
---|
218 | } else {
|
---|
219 | p = -1;
|
---|
220 | xp = 0;
|
---|
221 | }
|
---|
222 |
|
---|
223 | /* Format specifier state machine */
|
---|
224 | st=0;
|
---|
225 | do {
|
---|
226 | if (OOB(*s)) goto inval;
|
---|
227 | ps=st;
|
---|
228 | st=states[st]S(*s++);
|
---|
229 | } while (st-1<STOP);
|
---|
230 | if (!st) goto inval;
|
---|
231 |
|
---|
232 | /* Check validity of argument type (nl/normal) */
|
---|
233 | if (st==NOARG) {
|
---|
234 | if (argpos>=0) goto inval;
|
---|
235 | } else {
|
---|
236 | if (argpos>=0) nl_type[argpos]=st, arg=nl_arg[argpos];
|
---|
237 | else if (f) pop_arg(&arg, st, ap);
|
---|
238 | else return 0;
|
---|
239 | }
|
---|
240 |
|
---|
241 | if (!f) continue;
|
---|
242 | t = s[-1];
|
---|
243 | if (ps && (t&15)==3) t&=~32;
|
---|
244 |
|
---|
245 | switch (t) {
|
---|
246 | case 'n':
|
---|
247 | switch(ps) {
|
---|
248 | case BARE: *(int *)arg.p = cnt; break;
|
---|
249 | case LPRE: *(long *)arg.p = cnt; break;
|
---|
250 | case LLPRE: *(long long *)arg.p = cnt; break;
|
---|
251 | case HPRE: *(unsigned short *)arg.p = cnt; break;
|
---|
252 | case HHPRE: *(unsigned char *)arg.p = cnt; break;
|
---|
253 | case ZTPRE: *(size_t *)arg.p = cnt; break;
|
---|
254 | case JPRE: *(uintmax_t *)arg.p = cnt; break;
|
---|
255 | }
|
---|
256 | continue;
|
---|
257 | case 'c':
|
---|
258 | fputwc(btowc(arg.i), f);
|
---|
259 | l = 1;
|
---|
260 | continue;
|
---|
261 | case 'C':
|
---|
262 | fputwc(arg.i, f);
|
---|
263 | l = 1;
|
---|
264 | continue;
|
---|
265 | case 'S':
|
---|
266 | a = arg.p;
|
---|
267 | z = a + wcsnlen(a, p<0 ? INT_MAX : p);
|
---|
268 | if (p<0 && *z) goto overflow;
|
---|
269 | p = z-a;
|
---|
270 | if (w<p) w=p;
|
---|
271 | if (!(fl&LEFT_ADJ)) fprintf(f, "%*s", w-p, "");
|
---|
272 | out(f, a, p);
|
---|
273 | if ((fl&LEFT_ADJ)) fprintf(f, "%*s", w-p, "");
|
---|
274 | l=w;
|
---|
275 | continue;
|
---|
276 | case 'm':
|
---|
277 | arg.p = strerror(errno);
|
---|
278 | case 's':
|
---|
279 | if (!arg.p) arg.p = "(null)";
|
---|
280 | bs = arg.p;
|
---|
281 | for (i=l=0; l<(p<0?INT_MAX:p) && (i=mbtowc(&wc, bs, MB_LEN_MAX))>0; bs+=i, l++);
|
---|
282 | if (i<0) return -1;
|
---|
283 | if (p<0 && *bs) goto overflow;
|
---|
284 | p=l;
|
---|
285 | if (w<p) w=p;
|
---|
286 | if (!(fl&LEFT_ADJ)) fprintf(f, "%*s", w-p, "");
|
---|
287 | bs = arg.p;
|
---|
288 | while (l--) {
|
---|
289 | i=mbtowc(&wc, bs, MB_LEN_MAX);
|
---|
290 | bs+=i;
|
---|
291 | fputwc(wc, f);
|
---|
292 | }
|
---|
293 | if ((fl&LEFT_ADJ)) fprintf(f, "%*s", w-p, "");
|
---|
294 | l=w;
|
---|
295 | continue;
|
---|
296 | }
|
---|
297 |
|
---|
298 | if (xp && p<0) goto overflow;
|
---|
299 | snprintf(charfmt, sizeof charfmt, "%%%s%s%s%s%s*.*%c%c",
|
---|
300 | "#"+!(fl & ALT_FORM),
|
---|
301 | "+"+!(fl & MARK_POS),
|
---|
302 | "-"+!(fl & LEFT_ADJ),
|
---|
303 | " "+!(fl & PAD_POS),
|
---|
304 | "0"+!(fl & ZERO_PAD),
|
---|
305 | sizeprefix[(t|32)-'a'], t);
|
---|
306 |
|
---|
307 | switch (t|32) {
|
---|
308 | case 'a': case 'e': case 'f': case 'g':
|
---|
309 | l = fprintf(f, charfmt, w, p, arg.f);
|
---|
310 | break;
|
---|
311 | case 'd': case 'i': case 'o': case 'u': case 'x': case 'p':
|
---|
312 | l = fprintf(f, charfmt, w, p, arg.i);
|
---|
313 | break;
|
---|
314 | }
|
---|
315 | }
|
---|
316 |
|
---|
317 | if (f) return cnt;
|
---|
318 | if (!l10n) return 0;
|
---|
319 |
|
---|
320 | for (i=1; i<=NL_ARGMAX && nl_type[i]; i++)
|
---|
321 | pop_arg(nl_arg+i, nl_type[i], ap);
|
---|
322 | for (; i<=NL_ARGMAX && !nl_type[i]; i++);
|
---|
323 | if (i<=NL_ARGMAX) return -1;
|
---|
324 | return 1;
|
---|
325 |
|
---|
326 | inval:
|
---|
327 | errno = EINVAL;
|
---|
328 | return -1;
|
---|
329 | overflow:
|
---|
330 | errno = EOVERFLOW;
|
---|
331 | return -1;
|
---|
332 | }
|
---|
333 |
|
---|
334 | int vfwprintf(FILE *restrict f, const wchar_t *restrict fmt, va_list ap)
|
---|
335 | {
|
---|
336 | va_list ap2;
|
---|
337 | int nl_type[NL_ARGMAX] = {0};
|
---|
338 | union arg nl_arg[NL_ARGMAX];
|
---|
339 | int olderr;
|
---|
340 | int ret;
|
---|
341 |
|
---|
342 | /* the copy allows passing va_list* even if va_list is an array */
|
---|
343 | va_copy(ap2, ap);
|
---|
344 | if (wprintf_core(0, fmt, &ap2, nl_arg, nl_type) < 0) {
|
---|
345 | va_end(ap2);
|
---|
346 | return -1;
|
---|
347 | }
|
---|
348 |
|
---|
349 | FLOCK(f);
|
---|
350 | fwide(f, 1);
|
---|
351 | olderr = f->flags & F_ERR;
|
---|
352 | f->flags &= ~F_ERR;
|
---|
353 | ret = wprintf_core(f, fmt, &ap2, nl_arg, nl_type);
|
---|
354 | if (f->flags & F_ERR) ret = -1;
|
---|
355 | f->flags |= olderr;
|
---|
356 | FUNLOCK(f);
|
---|
357 | va_end(ap2);
|
---|
358 | return ret;
|
---|
359 | }
|
---|