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