]>
git.proxmox.com Git - wasi-libc.git/blob - libc-top-half/musl/src/stdio/vfwprintf.c
1 #include "stdio_impl.h"
11 #ifdef __wasilibc_unmodified_upstream
13 #include "printscan.h"
16 /* Convenient bit representation for modifier flags, which all fall
17 * within 31 codepoints of the space character. */
19 #define ALT_FORM (1U<<'#'-' ')
20 #define ZERO_PAD (1U<<'0'-' ')
21 #define LEFT_ADJ (1U<<'-'-' ')
22 #define PAD_POS (1U<<' '-' ')
23 #define MARK_POS (1U<<'+'-' ')
24 #define GROUPED (1U<<'\''-' ')
26 #define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS|GROUPED)
28 /* State machine to accept length modifiers + conversion specifiers.
29 * Result is 0 on failure, or an argument type to pop on success. */
32 BARE
, LPRE
, LLPRE
, HPRE
, HHPRE
, BIGLPRE
,
35 PTR
, INT
, UINT
, ULLONG
,
37 SHORT
, USHORT
, CHAR
, UCHAR
,
38 LLONG
, SIZET
, IMAX
, UMAX
, PDIFF
, UIPTR
,
44 #define S(x) [(x)-'A']
46 static const unsigned char states
[]['z'-'A'+1] = {
48 S('d') = INT
, S('i') = INT
,
49 S('o') = UINT
, S('u') = UINT
, S('x') = UINT
, S('X') = UINT
,
50 S('e') = DBL
, S('f') = DBL
, S('g') = DBL
, S('a') = DBL
,
51 S('E') = DBL
, S('F') = DBL
, S('G') = DBL
, S('A') = DBL
,
52 S('c') = CHAR
, S('C') = INT
,
53 S('s') = PTR
, S('S') = PTR
, S('p') = UIPTR
, S('n') = PTR
,
55 S('l') = LPRE
, S('h') = HPRE
, S('L') = BIGLPRE
,
56 S('z') = ZTPRE
, S('j') = JPRE
, S('t') = ZTPRE
,
57 }, { /* 1: l-prefixed */
58 S('d') = LONG
, S('i') = LONG
,
59 S('o') = ULONG
, S('u') = ULONG
, S('x') = ULONG
, S('X') = ULONG
,
60 S('c') = INT
, S('s') = PTR
, S('n') = PTR
,
62 }, { /* 2: ll-prefixed */
63 S('d') = LLONG
, S('i') = LLONG
,
64 S('o') = ULLONG
, S('u') = ULLONG
,
65 S('x') = ULLONG
, S('X') = ULLONG
,
67 }, { /* 3: h-prefixed */
68 S('d') = SHORT
, S('i') = SHORT
,
69 S('o') = USHORT
, S('u') = USHORT
,
70 S('x') = USHORT
, S('X') = USHORT
,
73 }, { /* 4: hh-prefixed */
74 S('d') = CHAR
, S('i') = CHAR
,
75 S('o') = UCHAR
, S('u') = UCHAR
,
76 S('x') = UCHAR
, S('X') = UCHAR
,
78 }, { /* 5: L-prefixed */
79 S('e') = LDBL
, S('f') = LDBL
, S('g') = LDBL
, S('a') = LDBL
,
80 S('E') = LDBL
, S('F') = LDBL
, S('G') = LDBL
, S('A') = LDBL
,
82 }, { /* 6: z- or t-prefixed (assumed to be same size) */
83 S('d') = PDIFF
, S('i') = PDIFF
,
84 S('o') = SIZET
, S('u') = SIZET
,
85 S('x') = SIZET
, S('X') = SIZET
,
87 }, { /* 7: j-prefixed */
88 S('d') = IMAX
, S('i') = IMAX
,
89 S('o') = UMAX
, S('u') = UMAX
,
90 S('x') = UMAX
, S('X') = UMAX
,
95 #define OOB(x) ((unsigned)(x)-'A' > 'z'-'A')
100 #if !defined(__wasilibc_printscan_no_floating_point)
101 #if defined(__wasilibc_printscan_no_long_double)
110 static void pop_arg(union arg
*arg
, int type
, va_list *ap
)
113 case PTR
: arg
->p
= va_arg(*ap
, void *);
114 break; case INT
: arg
->i
= va_arg(*ap
, int);
115 break; case UINT
: arg
->i
= va_arg(*ap
, unsigned int);
116 break; case LONG
: arg
->i
= va_arg(*ap
, long);
117 break; case ULONG
: arg
->i
= va_arg(*ap
, unsigned long);
118 break; case ULLONG
: arg
->i
= va_arg(*ap
, unsigned long long);
119 break; case SHORT
: arg
->i
= (short)va_arg(*ap
, int);
120 break; case USHORT
: arg
->i
= (unsigned short)va_arg(*ap
, int);
121 break; case CHAR
: arg
->i
= (signed char)va_arg(*ap
, int);
122 break; case UCHAR
: arg
->i
= (unsigned char)va_arg(*ap
, int);
123 break; case LLONG
: arg
->i
= va_arg(*ap
, long long);
124 break; case SIZET
: arg
->i
= va_arg(*ap
, size_t);
125 break; case IMAX
: arg
->i
= va_arg(*ap
, intmax_t);
126 break; case UMAX
: arg
->i
= va_arg(*ap
, uintmax_t);
127 break; case PDIFF
: arg
->i
= va_arg(*ap
, ptrdiff_t);
128 break; case UIPTR
: arg
->i
= (uintptr_t)va_arg(*ap
, void *);
129 #if defined(__wasilibc_printscan_no_floating_point)
131 break; case LDBL
: floating_point_not_supported();
133 break; case DBL
: arg
->f
= va_arg(*ap
, double);
134 #if defined(__wasilibc_printscan_no_long_double)
135 break; case LDBL
: long_double_not_supported();
137 break; case LDBL
: arg
->f
= va_arg(*ap
, long double);
143 static void out(FILE *f
, const wchar_t *s
, size_t l
)
145 while (l
-- && !(f
->flags
& F_ERR
)) fputwc(*s
++, f
);
148 static int getint(wchar_t **s
) {
150 for (i
=0; iswdigit(**s
); (*s
)++) {
151 if (i
> INT_MAX
/10U || **s
-'0' > INT_MAX
-10*i
) i
= -1;
152 else i
= 10*i
+ (**s
-'0');
157 static const char sizeprefix
['y'-'a'] = {
158 ['a'-'a']='L', ['e'-'a']='L', ['f'-'a']='L', ['g'-'a']='L',
159 ['d'-'a']='j', ['i'-'a']='j', ['o'-'a']='j', ['u'-'a']='j', ['x'-'a']='j',
163 static int wprintf_core(FILE *f
, const wchar_t *fmt
, va_list *ap
, union arg
*nl_arg
, int *nl_type
)
165 wchar_t *a
, *z
, *s
=(wchar_t *)fmt
;
179 /* This error is only specified for snprintf, but since it's
180 * unspecified for other forms, do the same. Stop immediately
181 * on overflow; otherwise %n could produce wrong results. */
182 if (l
> INT_MAX
- cnt
) goto overflow
;
184 /* Update output count, end loop when fmt is exhausted */
188 /* Handle literal text and %% format specifiers */
189 for (a
=s
; *s
&& *s
!='%'; s
++);
190 for (z
=s
; s
[0]=='%' && s
[1]=='%'; z
++, s
+=2);
191 if (z
-a
> INT_MAX
-cnt
) goto overflow
;
196 if (iswdigit(s
[1]) && s
[2]=='$') {
205 /* Read modifier flags */
206 for (fl
=0; (unsigned)*s
-' '<32 && (FLAGMASK
&(1U<<*s
-' ')); s
++)
209 /* Read field width */
211 if (iswdigit(s
[1]) && s
[2]=='$') {
213 nl_type
[s
[1]-'0'] = INT
;
214 w
= nl_arg
[s
[1]-'0'].i
;
217 w
= f
? va_arg(*ap
, int) : 0;
220 if (w
<0) fl
|=LEFT_ADJ
, w
=-w
;
221 } else if ((w
=getint(&s
))<0) goto overflow
;
224 if (*s
=='.' && s
[1]=='*') {
225 if (isdigit(s
[2]) && s
[3]=='$') {
226 nl_type
[s
[2]-'0'] = INT
;
227 p
= nl_arg
[s
[2]-'0'].i
;
230 p
= f
? va_arg(*ap
, int) : 0;
234 } else if (*s
=='.') {
243 /* Format specifier state machine */
246 if (OOB(*s
)) goto inval
;
248 st
=states
[st
]S(*s
++);
252 /* Check validity of argument type (nl/normal) */
254 if (argpos
>=0) goto inval
;
256 if (argpos
>=0) nl_type
[argpos
]=st
, arg
=nl_arg
[argpos
];
257 else if (f
) pop_arg(&arg
, st
, ap
);
263 if (ps
&& (t
&15)==3) t
&=~32;
268 case BARE
: *(int *)arg
.p
= cnt
; break;
269 case LPRE
: *(long *)arg
.p
= cnt
; break;
270 case LLPRE
: *(long long *)arg
.p
= cnt
; break;
271 case HPRE
: *(unsigned short *)arg
.p
= cnt
; break;
272 case HHPRE
: *(unsigned char *)arg
.p
= cnt
; break;
273 case ZTPRE
: *(size_t *)arg
.p
= cnt
; break;
274 case JPRE
: *(uintmax_t *)arg
.p
= cnt
; break;
279 if (w
>1 && !(fl
&LEFT_ADJ
)) fprintf(f
, "%*s", w
-1, "");
280 fputwc(btowc(arg
.i
), f
);
281 if (w
>1 && (fl
&LEFT_ADJ
)) fprintf(f
, "%*s", w
-1, "");
290 z
= a
+ wcsnlen(a
, p
<0 ? INT_MAX
: p
);
291 if (p
<0 && *z
) goto overflow
;
294 if (!(fl
&LEFT_ADJ
)) fprintf(f
, "%*s", w
-p
, "");
296 if ((fl
&LEFT_ADJ
)) fprintf(f
, "%*s", w
-p
, "");
300 arg
.p
= strerror(errno
);
302 if (!arg
.p
) arg
.p
= "(null)";
304 for (i
=l
=0; l
<(p
<0?INT_MAX
:p
) && (i
=mbtowc(&wc
, bs
, MB_LEN_MAX
))>0; bs
+=i
, l
++);
306 if (p
<0 && *bs
) goto overflow
;
309 if (!(fl
&LEFT_ADJ
)) fprintf(f
, "%*s", w
-p
, "");
312 i
=mbtowc(&wc
, bs
, MB_LEN_MAX
);
316 if ((fl
&LEFT_ADJ
)) fprintf(f
, "%*s", w
-p
, "");
321 if (xp
&& p
<0) goto overflow
;
322 #if defined(__wasilibc_printscan_no_long_double)
323 // Omit the 'L' modifier for floating-point cases.
325 case 'a': case 'e': case 'f': case 'g':
326 snprintf(charfmt
, sizeof charfmt
, "%%%s%s%s%s%s*.*%c",
327 "#"+!(fl
& ALT_FORM
),
328 "+"+!(fl
& MARK_POS
),
329 "-"+!(fl
& LEFT_ADJ
),
331 "0"+!(fl
& ZERO_PAD
),
334 l
= fprintf(f
, charfmt
, w
, p
, arg
.f
);
336 case 'd': case 'i': case 'o': case 'u': case 'x': case 'p':
337 snprintf(charfmt
, sizeof charfmt
, "%%%s%s%s%s%s*.*%c%c",
338 "#"+!(fl
& ALT_FORM
),
339 "+"+!(fl
& MARK_POS
),
340 "-"+!(fl
& LEFT_ADJ
),
342 "0"+!(fl
& ZERO_PAD
),
343 sizeprefix
[(t
|32)-'a'], t
);
345 l
= fprintf(f
, charfmt
, w
, p
, arg
.i
);
349 snprintf(charfmt
, sizeof charfmt
, "%%%s%s%s%s%s*.*%c%c",
350 "#"+!(fl
& ALT_FORM
),
351 "+"+!(fl
& MARK_POS
),
352 "-"+!(fl
& LEFT_ADJ
),
354 "0"+!(fl
& ZERO_PAD
),
355 sizeprefix
[(t
|32)-'a'], t
);
358 case 'a': case 'e': case 'f': case 'g':
359 l
= fprintf(f
, charfmt
, w
, p
, arg
.f
);
361 case 'd': case 'i': case 'o': case 'u': case 'x': case 'p':
362 l
= fprintf(f
, charfmt
, w
, p
, arg
.i
);
371 for (i
=1; i
<=NL_ARGMAX
&& nl_type
[i
]; i
++)
372 pop_arg(nl_arg
+i
, nl_type
[i
], ap
);
373 for (; i
<=NL_ARGMAX
&& !nl_type
[i
]; i
++);
374 if (i
<=NL_ARGMAX
) return -1;
385 int vfwprintf(FILE *restrict f
, const wchar_t *restrict fmt
, va_list ap
)
388 int nl_type
[NL_ARGMAX
] = {0};
389 union arg nl_arg
[NL_ARGMAX
];
393 /* the copy allows passing va_list* even if va_list is an array */
395 if (wprintf_core(0, fmt
, &ap2
, nl_arg
, nl_type
) < 0) {
402 olderr
= f
->flags
& F_ERR
;
404 ret
= wprintf_core(f
, fmt
, &ap2
, nl_arg
, nl_type
);
405 if (f
->flags
& F_ERR
) ret
= -1;