1 #include <stdlib.h>
2 #include <stdarg.h>
3 #include <ctype.h>
4 #include <wchar.h>
5 #include <wctype.h>
6 #include <limits.h>
7 #include <string.h>
8 #include <stdint.h>
9
10 #include "stdio_impl.h"
11 #include "shgetc.h"
12 #include "intscan.h"
13 #include "floatscan.h"
14
15 #define SIZE_hh -2
16 #define SIZE_h -1
17 #define SIZE_def 0
18 #define SIZE_l 1
19 #define SIZE_L 2
20 #define SIZE_ll 3
21 #define BUF_LEN 513
22
23 #if (defined(MUSL_AARCH64_ARCH)) || (defined(MUSL_ARM_ARCH))
24 extern int parsefloat(FILE *f, char *buf, char *end);
25 #endif
26
store_int(void * dest,int size,unsigned long long i)27 static void store_int(void *dest, int size, unsigned long long i)
28 {
29 if (!dest) return;
30 switch (size) {
31 case SIZE_hh:
32 *(char *)dest = i;
33 break;
34 case SIZE_h:
35 *(short *)dest = i;
36 break;
37 case SIZE_def:
38 *(int *)dest = i;
39 break;
40 case SIZE_l:
41 *(long *)dest = i;
42 break;
43 case SIZE_ll:
44 *(long long *)dest = i;
45 break;
46 }
47 }
48
arg_n(va_list ap,unsigned int n)49 static void *arg_n(va_list ap, unsigned int n)
50 {
51 void *p;
52 unsigned int i;
53 va_list ap2;
54 va_copy(ap2, ap);
55 for (i=n; i>1; i--) va_arg(ap2, void *);
56 p = va_arg(ap2, void *);
57 va_end(ap2);
58 return p;
59 }
60
vfscanf(FILE * restrict f,const char * restrict fmt,va_list ap)61 int vfscanf(FILE *restrict f, const char *restrict fmt, va_list ap)
62 {
63 #if (defined(MUSL_AARCH64_ARCH)) || (defined(MUSL_ARM_ARCH))
64 char buf[BUF_LEN];
65 char *endptr;
66 #endif
67 int width;
68 int size;
69 int alloc = 0;
70 int base;
71 const unsigned char *p;
72 int c, t;
73 char *s;
74 wchar_t *wcs;
75 mbstate_t st;
76 void *dest=NULL;
77 int invert;
78 int matches=0;
79 unsigned long long x;
80 long double y;
81 off_t pos = 0;
82 unsigned char scanset[257];
83 size_t i, k;
84 wchar_t wc;
85
86 FLOCK(f);
87
88 if (!f->rpos) __toread(f);
89 if (!f->rpos) goto input_fail;
90
91 for (p=(const unsigned char *)fmt; *p; p++) {
92
93 alloc = 0;
94
95 if (isspace(*p)) {
96 while (isspace(p[1])) p++;
97 shlim(f, 0);
98 while (isspace(shgetc(f)));
99 shunget(f);
100 pos += shcnt(f);
101 continue;
102 }
103 if (*p != '%' || p[1] == '%') {
104 shlim(f, 0);
105 if (*p == '%') {
106 p++;
107 while (isspace((c=shgetc(f))));
108 } else {
109 c = shgetc(f);
110 }
111 if (c!=*p) {
112 shunget(f);
113 if (c<0) goto input_fail;
114 goto match_fail;
115 }
116 pos += shcnt(f);
117 continue;
118 }
119
120 p++;
121 if (*p=='*') {
122 dest = 0; p++;
123 } else if (isdigit(*p) && p[1]=='$') {
124 dest = arg_n(ap, *p-'0'); p+=2;
125 } else {
126 dest = va_arg(ap, void *);
127 }
128
129 for (width=0; isdigit(*p); p++) {
130 width = 10*width + *p - '0';
131 }
132
133 if (*p=='m') {
134 wcs = 0;
135 s = 0;
136 alloc = !!dest;
137 p++;
138 } else {
139 alloc = 0;
140 }
141
142 size = SIZE_def;
143 switch (*p++) {
144 case 'h':
145 if (*p == 'h') p++, size = SIZE_hh;
146 else size = SIZE_h;
147 break;
148 case 'l':
149 if (*p == 'l') p++, size = SIZE_ll;
150 else size = SIZE_l;
151 break;
152 case 'j':
153 size = SIZE_ll;
154 break;
155 case 'z':
156 case 't':
157 size = SIZE_l;
158 break;
159 case 'L':
160 size = SIZE_L;
161 break;
162 case 'd': case 'i': case 'o': case 'u': case 'x':
163 case 'a': case 'e': case 'f': case 'g':
164 case 'A': case 'E': case 'F': case 'G': case 'X':
165 case 's': case 'c': case '[':
166 case 'S': case 'C':
167 case 'p': case 'n':
168 p--;
169 break;
170 default:
171 goto fmt_fail;
172 }
173
174 t = *p;
175
176 /* C or S */
177 if ((t&0x2f) == 3) {
178 t |= 32;
179 size = SIZE_l;
180 }
181
182 switch (t) {
183 case 'c':
184 if (width < 1) width = 1;
185 case '[':
186 break;
187 case 'n':
188 store_int(dest, size, pos);
189 /* do not increment match count, etc! */
190 continue;
191 default:
192 shlim(f, 0);
193 while (isspace(shgetc(f)));
194 shunget(f);
195 pos += shcnt(f);
196 }
197
198 shlim(f, width);
199 if (shgetc(f) < 0) goto input_fail;
200 shunget(f);
201
202 switch (t) {
203 case 's':
204 case 'c':
205 case '[':
206 if (t == 'c' || t == 's') {
207 memset(scanset, -1, sizeof scanset);
208 scanset[0] = 0;
209 if (t == 's') {
210 scanset[1+'\t'] = 0;
211 scanset[1+'\n'] = 0;
212 scanset[1+'\v'] = 0;
213 scanset[1+'\f'] = 0;
214 scanset[1+'\r'] = 0;
215 scanset[1+' '] = 0;
216 }
217 } else {
218 if (*++p == '^') p++, invert = 1;
219 else invert = 0;
220 memset(scanset, invert, sizeof scanset);
221 scanset[0] = 0;
222 if (*p == '-') p++, scanset[1+'-'] = 1-invert;
223 else if (*p == ']') p++, scanset[1+']'] = 1-invert;
224 for (; *p != ']'; p++) {
225 if (!*p) goto fmt_fail;
226 if (*p=='-' && p[1] && p[1] != ']')
227 for (c=p++[-1]; c<*p; c++)
228 scanset[1+c] = 1-invert;
229 scanset[1+*p] = 1-invert;
230 }
231 }
232 wcs = 0;
233 s = 0;
234 i = 0;
235 k = t=='c' ? width+1U : 31;
236 if (size == SIZE_l) {
237 if (alloc) {
238 wcs = malloc(k*sizeof(wchar_t));
239 if (!wcs) goto alloc_fail;
240 } else {
241 wcs = dest;
242 }
243 st = (mbstate_t){0};
244 while (scanset[(c=shgetc(f))+1]) {
245 switch (mbrtowc(&wc, &(char){c}, 1, &st)) {
246 case -1:
247 goto input_fail;
248 case -2:
249 continue;
250 }
251 if (wcs) wcs[i++] = wc;
252 if (alloc && i==k) {
253 k+=k+1;
254 wchar_t *tmp = realloc(wcs, k*sizeof(wchar_t));
255 if (!tmp) goto alloc_fail;
256 wcs = tmp;
257 }
258 }
259 if (!mbsinit(&st)) goto input_fail;
260 } else if (alloc) {
261 s = malloc(k);
262 if (!s) goto alloc_fail;
263 while (scanset[(c=shgetc(f))+1]) {
264 s[i++] = c;
265 if (i==k) {
266 k+=k+1;
267 char *tmp = realloc(s, k);
268 if (!tmp) goto alloc_fail;
269 s = tmp;
270 }
271 }
272 } else if ((s = dest)) {
273 while (scanset[(c=shgetc(f))+1])
274 s[i++] = c;
275 } else {
276 while (scanset[(c=shgetc(f))+1]);
277 }
278 shunget(f);
279 if (!shcnt(f)) goto match_fail;
280 if (t == 'c' && shcnt(f) != width) goto match_fail;
281 if (alloc) {
282 if (size == SIZE_l) *(wchar_t **)dest = wcs;
283 else *(char **)dest = s;
284 }
285 if (t != 'c') {
286 if (wcs) wcs[i] = 0;
287 if (s) s[i] = 0;
288 }
289 break;
290 case 'p':
291 case 'X':
292 case 'x':
293 base = 16;
294 goto int_common;
295 case 'o':
296 base = 8;
297 goto int_common;
298 case 'd':
299 case 'u':
300 base = 10;
301 goto int_common;
302 case 'i':
303 base = 0;
304 int_common:
305 x = __intscan(f, base, 0, ULLONG_MAX);
306 if (!shcnt(f)) goto match_fail;
307 if (t=='p' && dest) *(void **)dest = (void *)(uintptr_t)x;
308 else store_int(dest, size, x);
309 break;
310 case 'a': case 'A':
311 case 'e': case 'E':
312 case 'f': case 'F':
313 case 'g': case 'G':
314 #if (defined(MUSL_AARCH64_ARCH)) || (defined(MUSL_ARM_ARCH))
315 if (width == 0 || width > sizeof(buf) - 1)
316 width = sizeof(buf) - 1;
317 int count = parsefloat(f, buf, buf + width);
318 if (count == 0)
319 goto match_fail;
320 if (dest) switch (size) {
321 case SIZE_def:
322 y = strtof(buf, &endptr);
323 *(float *)dest = y;
324 break;
325 case SIZE_l:
326 y = strtod(buf, &endptr);
327 *(double *)dest = y;
328 break;
329 case SIZE_L:
330 y = strtold(buf, &endptr);
331 *(long double *)dest = y;
332 break;
333 }
334 break;
335 }
336 #else
337 y = __floatscan(f, size, 0);
338 if (!shcnt(f)) goto match_fail;
339 if (dest) switch (size) {
340 case SIZE_def:
341 *(float *)dest = y;
342 break;
343 case SIZE_l:
344 *(double *)dest = y;
345 break;
346 case SIZE_L:
347 *(long double *)dest = y;
348 break;
349 }
350 break;
351 }
352 #endif
353 pos += shcnt(f);
354 if (dest) matches++;
355 }
356 if (0) {
357 fmt_fail:
358 alloc_fail:
359 input_fail:
360 if (!matches) matches--;
361 match_fail:
362 if (alloc) {
363 free(s);
364 free(wcs);
365 }
366 }
367 FUNLOCK(f);
368 return matches;
369 }
370
371 weak_alias(vfscanf,__isoc99_vfscanf);
372