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