• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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