• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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