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