1 /*
2 Copyright (C) 2005-2012 Rich Felker
3
4 Permission is hereby granted, free of charge, to any person obtaining
5 a copy of this software and associated documentation files (the
6 "Software"), to deal in the Software without restriction, including
7 without limitation the rights to use, copy, modify, merge, publish,
8 distribute, sublicense, and/or sell copies of the Software, and to
9 permit persons to whom the Software is furnished to do so, subject to
10 the following conditions:
11
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23 Modified in 2013 for the Android Open Source Project.
24 */
25
26 //#include "stdio_impl.h"
27 #include <errno.h>
28 #include <ctype.h>
29 #include <limits.h>
30 #include <string.h>
31 #include <stdarg.h>
32 #include <wchar.h>
33 #include <inttypes.h>
34
35 // A wrapper around either a FILE* or a string buffer, used to output
36 // the result of formatted expansion.
37 typedef struct {
38 FILE* file;
39 wchar_t* buffer;
40 size_t buffer_pos;
41 size_t buffer_size;
42 } Out;
43
out_init_file(Out * out,FILE * f)44 void out_init_file(Out* out, FILE* f) {
45 memset(out, 0, sizeof(*out));
46 out->file = f;
47 }
48
out_init_buffer(Out * out,wchar_t * buffer,size_t buffer_size)49 void out_init_buffer(Out* out, wchar_t* buffer, size_t buffer_size) {
50 memset(out, 0, sizeof(*out));
51 out->buffer = buffer;
52 out->buffer_pos = 0;
53 out->buffer_size = buffer_size;
54 }
55
out_write(Out * out,const wchar_t * text,size_t length)56 void out_write(Out* out, const wchar_t* text, size_t length) {
57 if (out->file != NULL) {
58 // Write into a file the UTF-8 encoded version.
59 // TODO(digit): Support locale-specific encoding.
60 size_t mb_len = wcstombs(NULL, text, length);
61 char* mb_buffer = malloc(mb_len);
62 wcstombs(mb_buffer, text, length);
63 fwrite(mb_buffer, 1, mb_len, out->file);
64 free(mb_buffer);
65 } else {
66 // Write into a bounded buffer.
67 size_t avail = out->buffer_size - out->buffer_pos;
68 if (length > avail)
69 length = avail;
70 memcpy((char*)(out->buffer + out->buffer_pos),
71 (const char*)text,
72 length * sizeof(wchar_t));
73 out->buffer_pos += length;
74 }
75 }
76
out_putwc(Out * out,wchar_t wc)77 void out_putwc(Out* out, wchar_t wc) {
78 if (out->file)
79 fputwc(wc, out->file);
80 else {
81 if (out->buffer_pos < out->buffer_size)
82 out->buffer[out->buffer_pos++] = wc;
83 }
84 }
85
out_printf(Out * out,const char * format,...)86 int out_printf(Out* out, const char* format, ...) {
87 va_list args;
88 va_start(args, format);
89 if (out->file)
90 return vfprintf(out->file, format, args);
91 else {
92 // TODO(digit): Make this faster.
93 // First, generate formatted byte output.
94 int mb_len = vsnprintf(NULL, 0, format, args);
95 char* mb_buffer = malloc(mb_len + 1);
96 vsnprintf(mb_buffer, mb_len + 1, format, args);
97 // Then convert to wchar_t buffer.
98 size_t wide_len = mbstowcs(NULL, mb_buffer, mb_len);
99 wchar_t* wide_buffer = malloc((wide_len + 1) * sizeof(wchar_t));
100 mbstowcs(wide_buffer, mb_buffer, mb_len);
101 // Add to buffer.
102 out_write(out, wide_buffer, wide_len);
103 // finished
104 free(wide_buffer);
105 free(mb_buffer);
106
107 return wide_len;
108 }
109 va_end(args);
110 }
out_error(Out * out)111 int out_error(Out* out) {
112 if (out->file != NULL)
113 return ferror(out->file);
114
115 return 0;
116 }
117
out_overflow(Out * out)118 int out_overflow(Out* out) {
119 if (out->file != NULL)
120 return feof(out->file);
121 else
122 return (out->buffer_pos >= out->buffer_size);
123 }
124
125 /* Convenient bit representation for modifier flags, which all fall
126 * within 31 codepoints of the space character. */
127
128 #define ALT_FORM (1U<<'#'-' ')
129 #define ZERO_PAD (1U<<'0'-' ')
130 #define LEFT_ADJ (1U<<'-'-' ')
131 #define PAD_POS (1U<<' '-' ')
132 #define MARK_POS (1U<<'+'-' ')
133 #define GROUPED (1U<<'\''-' ')
134
135 #define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS|GROUPED)
136
137 #if UINT_MAX == ULONG_MAX
138 #define LONG_IS_INT
139 #endif
140
141 #if SIZE_MAX != ULONG_MAX || UINTMAX_MAX != ULLONG_MAX
142 #define ODD_TYPES
143 #endif
144
145 /* State machine to accept length modifiers + conversion specifiers.
146 * Result is 0 on failure, or an argument type to pop on success. */
147
148 enum {
149 BARE, LPRE, LLPRE, HPRE, HHPRE, BIGLPRE,
150 ZTPRE, JPRE,
151 STOP,
152 PTR, INT, UINT, ULLONG,
153 #ifndef LONG_IS_INT
154 LONG, ULONG,
155 #else
156 #define LONG INT
157 #define ULONG UINT
158 #endif
159 SHORT, USHORT, CHAR, UCHAR,
160 #ifdef ODD_TYPES
161 LLONG, SIZET, IMAX, UMAX, PDIFF, UIPTR,
162 #else
163 #define LLONG ULLONG
164 #define SIZET ULONG
165 #define IMAX LLONG
166 #define UMAX ULLONG
167 #define PDIFF LONG
168 #define UIPTR ULONG
169 #endif
170 DBL, LDBL,
171 NOARG,
172 MAXSTATE
173 };
174
175 #define S(x) [(x)-'A']
176
177 static const unsigned char states[]['z'-'A'+1] = {
178 { /* 0: bare types */
179 S('d') = INT, S('i') = INT,
180 S('o') = UINT, S('u') = UINT, S('x') = UINT, S('X') = UINT,
181 S('e') = DBL, S('f') = DBL, S('g') = DBL, S('a') = DBL,
182 S('E') = DBL, S('F') = DBL, S('G') = DBL, S('A') = DBL,
183 S('c') = CHAR, S('C') = INT,
184 S('s') = PTR, S('S') = PTR, S('p') = UIPTR, S('n') = PTR,
185 S('m') = NOARG,
186 S('l') = LPRE, S('h') = HPRE, S('L') = BIGLPRE,
187 S('z') = ZTPRE, S('j') = JPRE, S('t') = ZTPRE,
188 }, { /* 1: l-prefixed */
189 S('d') = LONG, S('i') = LONG,
190 S('o') = ULONG, S('u') = ULONG, S('x') = ULONG, S('X') = ULONG,
191 S('c') = INT, S('s') = PTR, S('n') = PTR,
192 S('l') = LLPRE,
193 }, { /* 2: ll-prefixed */
194 S('d') = LLONG, S('i') = LLONG,
195 S('o') = ULLONG, S('u') = ULLONG,
196 S('x') = ULLONG, S('X') = ULLONG,
197 S('n') = PTR,
198 }, { /* 3: h-prefixed */
199 S('d') = SHORT, S('i') = SHORT,
200 S('o') = USHORT, S('u') = USHORT,
201 S('x') = USHORT, S('X') = USHORT,
202 S('n') = PTR,
203 S('h') = HHPRE,
204 }, { /* 4: hh-prefixed */
205 S('d') = CHAR, S('i') = CHAR,
206 S('o') = UCHAR, S('u') = UCHAR,
207 S('x') = UCHAR, S('X') = UCHAR,
208 S('n') = PTR,
209 }, { /* 5: L-prefixed */
210 S('e') = LDBL, S('f') = LDBL, S('g') = LDBL, S('a') = LDBL,
211 S('E') = LDBL, S('F') = LDBL, S('G') = LDBL, S('A') = LDBL,
212 S('n') = PTR,
213 }, { /* 6: z- or t-prefixed (assumed to be same size) */
214 S('d') = PDIFF, S('i') = PDIFF,
215 S('o') = SIZET, S('u') = SIZET,
216 S('x') = SIZET, S('X') = SIZET,
217 S('n') = PTR,
218 }, { /* 7: j-prefixed */
219 S('d') = IMAX, S('i') = IMAX,
220 S('o') = UMAX, S('u') = UMAX,
221 S('x') = UMAX, S('X') = UMAX,
222 S('n') = PTR,
223 }
224 };
225
226 #define OOB(x) ((unsigned)(x)-'A' > 'z'-'A')
227
228 union arg
229 {
230 uintmax_t i;
231 long double f;
232 void *p;
233 };
234
pop_arg(union arg * arg,int type,va_list * ap)235 static void pop_arg(union arg *arg, int type, va_list *ap)
236 {
237 /* Give the compiler a hint for optimizing the switch. */
238 if ((unsigned)type > MAXSTATE) return;
239 switch (type) {
240 case PTR: arg->p = va_arg(*ap, void *);
241 break; case INT: arg->i = va_arg(*ap, int);
242 break; case UINT: arg->i = va_arg(*ap, unsigned int);
243 #ifndef LONG_IS_INT
244 break; case LONG: arg->i = va_arg(*ap, long);
245 break; case ULONG: arg->i = va_arg(*ap, unsigned long);
246 #endif
247 break; case ULLONG: arg->i = va_arg(*ap, unsigned long long);
248 break; case SHORT: arg->i = (short)va_arg(*ap, int);
249 break; case USHORT: arg->i = (unsigned short)va_arg(*ap, int);
250 break; case CHAR: arg->i = (signed char)va_arg(*ap, int);
251 break; case UCHAR: arg->i = (unsigned char)va_arg(*ap, int);
252 #ifdef ODD_TYPES
253 break; case LLONG: arg->i = va_arg(*ap, long long);
254 break; case SIZET: arg->i = va_arg(*ap, size_t);
255 break; case IMAX: arg->i = va_arg(*ap, intmax_t);
256 break; case UMAX: arg->i = va_arg(*ap, uintmax_t);
257 break; case PDIFF: arg->i = va_arg(*ap, ptrdiff_t);
258 break; case UIPTR: arg->i = (uintptr_t)va_arg(*ap, void *);
259 #endif
260 break; case DBL: arg->f = va_arg(*ap, double);
261 break; case LDBL: arg->f = va_arg(*ap, long double);
262 }
263 }
264
getint(wchar_t ** s)265 static int getint(wchar_t **s) {
266 int i;
267 for (i=0; iswdigit(**s); (*s)++)
268 i = 10*i + (**s-'0');
269 return i;
270 }
271
272 static const char sizeprefix['y'-'a'] = {
273 ['a'-'a']='L', ['e'-'a']='L', ['f'-'a']='L', ['g'-'a']='L',
274 ['d'-'a']='j', ['i'-'a']='j', ['o'-'a']='j', ['u'-'a']='j', ['x'-'a']='j',
275 ['p'-'a']='j'
276 };
277
wprintf_core(Out * out,const wchar_t * fmt,va_list * ap,union arg * nl_arg,int * nl_type)278 static int wprintf_core(Out *out, const wchar_t *fmt, va_list *ap, union arg *nl_arg, int *nl_type)
279 {
280 wchar_t *a, *z, *s=(wchar_t *)fmt, *s0;
281 unsigned l10n=0, litpct, fl;
282 int w, p;
283 union arg arg;
284 int argpos;
285 unsigned st, ps;
286 int cnt=0, l=0;
287 int i;
288 int t;
289 char *bs;
290 char charfmt[16];
291 wchar_t wc;
292
293 for (;;) {
294 /* Update output count, end loop when fmt is exhausted */
295 if (cnt >= 0) {
296 if (l > INT_MAX - cnt) {
297 if (!out_error(out)) errno = EOVERFLOW;
298 cnt = -1;
299 } else cnt += l;
300 }
301 if (!*s) break;
302
303 /* Handle literal text and %% format specifiers */
304 for (a=s; *s && *s!='%'; s++);
305 litpct = wcsspn(s, L"%")/2; /* Optimize %%%% runs */
306 z = s+litpct;
307 s += 2*litpct;
308 l = z-a;
309 if (out) out_write(out, a, l);
310 if (l) continue;
311
312 if (iswdigit(s[1]) && s[2]=='$') {
313 l10n=1;
314 argpos = s[1]-'0';
315 s+=3;
316 } else {
317 argpos = -1;
318 s++;
319 }
320
321 /* Read modifier flags */
322 for (fl=0; (unsigned)*s-' '<32 && (FLAGMASK&(1U<<*s-' ')); s++)
323 fl |= 1U<<*s-' ';
324
325 /* Read field width */
326 if (*s=='*') {
327 if (iswdigit(s[1]) && s[2]=='$') {
328 l10n=1;
329 nl_type[s[1]-'0'] = INT;
330 w = nl_arg[s[1]-'0'].i;
331 s+=3;
332 } else if (!l10n) {
333 w = out ? va_arg(*ap, int) : 0;
334 s++;
335 } else return -1;
336 if (w<0) fl|=LEFT_ADJ, w=-w;
337 } else if ((w=getint(&s))<0) return -1;
338
339 /* Read precision */
340 if (*s=='.' && s[1]=='*') {
341 if (isdigit(s[2]) && s[3]=='$') {
342 nl_type[s[2]-'0'] = INT;
343 p = nl_arg[s[2]-'0'].i;
344 s+=4;
345 } else if (!l10n) {
346 p = out ? va_arg(*ap, int) : 0;
347 s+=2;
348 } else return -1;
349 } else if (*s=='.') {
350 s++;
351 p = getint(&s);
352 } else p = -1;
353
354 /* Format specifier state machine */
355 s0=s;
356 st=0;
357 do {
358 if (OOB(*s)) return -1;
359 ps=st;
360 st=states[st]S(*s++);
361 } while (st-1<STOP);
362 if (!st) return -1;
363
364 /* Check validity of argument type (nl/normal) */
365 if (st==NOARG) {
366 if (argpos>=0) return -1;
367 else if (!out) continue;
368 } else {
369 if (argpos>=0) nl_type[argpos]=st, arg=nl_arg[argpos];
370 else if (out) pop_arg(&arg, st, ap);
371 else return 0;
372 }
373
374 if (!out) continue;
375 t = s[-1];
376 if (ps && (t&15)==3) t&=~32;
377
378 switch (t) {
379 case 'n':
380 switch(ps) {
381 case BARE: *(int *)arg.p = cnt; break;
382 case LPRE: *(long *)arg.p = cnt; break;
383 case LLPRE: *(long long *)arg.p = cnt; break;
384 case HPRE: *(unsigned short *)arg.p = cnt; break;
385 case HHPRE: *(unsigned char *)arg.p = cnt; break;
386 case ZTPRE: *(size_t *)arg.p = cnt; break;
387 case JPRE: *(uintmax_t *)arg.p = cnt; break;
388 }
389 continue;
390 case 'c':
391 out_putwc(out, btowc(arg.i));
392 l = 1;
393 continue;
394 case 'C':
395 out_putwc(out, arg.i);
396 l = 1;
397 continue;
398 case 'S':
399 a = arg.p;
400 z = wmemchr(a, 0, p);
401 if (!z) z=a+p;
402 else p=z-a;
403 if (w<p) w=p;
404 if (!(fl&LEFT_ADJ)) out_printf(out, "%.*s", w-p, "");
405 out_write(out, a, p);
406 if ((fl&LEFT_ADJ)) out_printf(out, "%.*s", w-p, "");
407 l=w;
408 continue;
409 case 's':
410 bs = arg.p;
411 if (p<0) p = INT_MAX;
412 for (i=l=0; l<p && (i=mbtowc(&wc, bs, MB_LEN_MAX))>0; bs+=i, l++);
413 if (i<0) return -1;
414 p=l;
415 if (w<p) w=p;
416 if (!(fl&LEFT_ADJ)) out_printf(out, "%.*s", w-p, "");
417 bs = arg.p;
418 while (l--) {
419 i=mbtowc(&wc, bs, MB_LEN_MAX);
420 bs+=i;
421 out_putwc(out, wc);
422 }
423 if ((fl&LEFT_ADJ)) out_printf(out, "%.*s", w-p, "");
424 l=w;
425 continue;
426 }
427
428 snprintf(charfmt, sizeof charfmt, "%%%s%s%s%s%s*.*%c%c",
429 "#"+!(fl & ALT_FORM),
430 "+"+!(fl & MARK_POS),
431 "-"+!(fl & LEFT_ADJ),
432 " "+!(fl & PAD_POS),
433 "0"+!(fl & ZERO_PAD),
434 sizeprefix[(t|32)-'a'], t);
435
436 switch (t|32) {
437 case 'a': case 'e': case 'f': case 'g':
438 l = out_printf(out, charfmt, w, p, arg.f);
439 break;
440 case 'd': case 'i': case 'o': case 'u': case 'x': case 'p':
441 l = out_printf(out, charfmt, w, p, arg.i);
442 break;
443 }
444 }
445
446 if (out) return cnt;
447 if (!l10n) return 0;
448
449 for (i=1; i<=NL_ARGMAX && nl_type[i]; i++)
450 pop_arg(nl_arg+i, nl_type[i], ap);
451 for (; i<=NL_ARGMAX && !nl_type[i]; i++);
452 if (i<=NL_ARGMAX) return -1;
453 return 1;
454 }
455
vfwprintf(FILE * restrict f,const wchar_t * restrict fmt,va_list ap)456 int vfwprintf(FILE *restrict f, const wchar_t *restrict fmt, va_list ap)
457 {
458 va_list ap2;
459 int nl_type[NL_ARGMAX] = {0};
460 union arg nl_arg[NL_ARGMAX];
461 int ret;
462 Out out[1];
463 out_init_file(out, f);
464 va_copy(ap2, ap);
465 // Check for error in format string before writing anything to file.
466 if (wprintf_core(0, fmt, &ap2, nl_arg, nl_type) < 0) {
467 va_end(ap2);
468 return -1;
469 }
470 ret = wprintf_core(out, fmt, &ap2, nl_arg, nl_type);
471 va_end(ap2);
472 return ret;
473 }
474
vswprintf(wchar_t * restrict s,size_t l,const wchar_t * restrict fmt,va_list ap)475 int vswprintf(wchar_t *restrict s, size_t l, const wchar_t *restrict fmt, va_list ap)
476 {
477 va_list ap2;
478 int nl_type[NL_ARGMAX] = {0};
479 union arg nl_arg[NL_ARGMAX];
480 int ret;
481 Out out[1];
482 out_init_buffer(out, s, l);
483 va_copy(ap2, ap);
484 ret = wprintf_core(out, fmt, &ap2, nl_arg, nl_type);
485 va_end(ap2);
486 if (out_overflow(out)) return -1;
487 return ret;
488 }
489