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))
24extern int parsefloat(FILE *f, char *buf, char *end);
25#endif
26
27static 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
49static 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
61int 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) {
357fmt_fail:
358alloc_fail:
359input_fail:
360		if (!matches) matches--;
361match_fail:
362		if (alloc) {
363			free(s);
364			free(wcs);
365		}
366	}
367	FUNLOCK(f);
368	return matches;
369}
370
371weak_alias(vfscanf,__isoc99_vfscanf);
372