1570af302Sopenharmony_ci#include <stdlib.h>
2570af302Sopenharmony_ci#include <stdarg.h>
3570af302Sopenharmony_ci#include <ctype.h>
4570af302Sopenharmony_ci#include <wchar.h>
5570af302Sopenharmony_ci#include <wctype.h>
6570af302Sopenharmony_ci#include <limits.h>
7570af302Sopenharmony_ci#include <string.h>
8570af302Sopenharmony_ci#include <stdint.h>
9570af302Sopenharmony_ci
10570af302Sopenharmony_ci#include "stdio_impl.h"
11570af302Sopenharmony_ci#include "shgetc.h"
12570af302Sopenharmony_ci#include "intscan.h"
13570af302Sopenharmony_ci#include "floatscan.h"
14570af302Sopenharmony_ci
15570af302Sopenharmony_ci#define SIZE_hh -2
16570af302Sopenharmony_ci#define SIZE_h  -1
17570af302Sopenharmony_ci#define SIZE_def 0
18570af302Sopenharmony_ci#define SIZE_l   1
19570af302Sopenharmony_ci#define SIZE_L   2
20570af302Sopenharmony_ci#define SIZE_ll  3
21570af302Sopenharmony_ci#define BUF_LEN 513
22570af302Sopenharmony_ci
23570af302Sopenharmony_ci#if (defined(MUSL_AARCH64_ARCH)) || (defined(MUSL_ARM_ARCH))
24570af302Sopenharmony_ciextern int parsefloat(FILE *f, char *buf, char *end);
25570af302Sopenharmony_ci#endif
26570af302Sopenharmony_ci
27570af302Sopenharmony_cistatic void store_int(void *dest, int size, unsigned long long i)
28570af302Sopenharmony_ci{
29570af302Sopenharmony_ci	if (!dest) return;
30570af302Sopenharmony_ci	switch (size) {
31570af302Sopenharmony_ci	case SIZE_hh:
32570af302Sopenharmony_ci		*(char *)dest = i;
33570af302Sopenharmony_ci		break;
34570af302Sopenharmony_ci	case SIZE_h:
35570af302Sopenharmony_ci		*(short *)dest = i;
36570af302Sopenharmony_ci		break;
37570af302Sopenharmony_ci	case SIZE_def:
38570af302Sopenharmony_ci		*(int *)dest = i;
39570af302Sopenharmony_ci		break;
40570af302Sopenharmony_ci	case SIZE_l:
41570af302Sopenharmony_ci		*(long *)dest = i;
42570af302Sopenharmony_ci		break;
43570af302Sopenharmony_ci	case SIZE_ll:
44570af302Sopenharmony_ci		*(long long *)dest = i;
45570af302Sopenharmony_ci		break;
46570af302Sopenharmony_ci	}
47570af302Sopenharmony_ci}
48570af302Sopenharmony_ci
49570af302Sopenharmony_cistatic void *arg_n(va_list ap, unsigned int n)
50570af302Sopenharmony_ci{
51570af302Sopenharmony_ci	void *p;
52570af302Sopenharmony_ci	unsigned int i;
53570af302Sopenharmony_ci	va_list ap2;
54570af302Sopenharmony_ci	va_copy(ap2, ap);
55570af302Sopenharmony_ci	for (i=n; i>1; i--) va_arg(ap2, void *);
56570af302Sopenharmony_ci	p = va_arg(ap2, void *);
57570af302Sopenharmony_ci	va_end(ap2);
58570af302Sopenharmony_ci	return p;
59570af302Sopenharmony_ci}
60570af302Sopenharmony_ci
61570af302Sopenharmony_ciint vfscanf(FILE *restrict f, const char *restrict fmt, va_list ap)
62570af302Sopenharmony_ci{
63570af302Sopenharmony_ci#if (defined(MUSL_AARCH64_ARCH)) || (defined(MUSL_ARM_ARCH))
64570af302Sopenharmony_ci	char buf[BUF_LEN];
65570af302Sopenharmony_ci	char *endptr;
66570af302Sopenharmony_ci#endif
67570af302Sopenharmony_ci	int width;
68570af302Sopenharmony_ci	int size;
69570af302Sopenharmony_ci	int alloc = 0;
70570af302Sopenharmony_ci	int base;
71570af302Sopenharmony_ci	const unsigned char *p;
72570af302Sopenharmony_ci	int c, t;
73570af302Sopenharmony_ci	char *s;
74570af302Sopenharmony_ci	wchar_t *wcs;
75570af302Sopenharmony_ci	mbstate_t st;
76570af302Sopenharmony_ci	void *dest=NULL;
77570af302Sopenharmony_ci	int invert;
78570af302Sopenharmony_ci	int matches=0;
79570af302Sopenharmony_ci	unsigned long long x;
80570af302Sopenharmony_ci	long double y;
81570af302Sopenharmony_ci	off_t pos = 0;
82570af302Sopenharmony_ci	unsigned char scanset[257];
83570af302Sopenharmony_ci	size_t i, k;
84570af302Sopenharmony_ci	wchar_t wc;
85570af302Sopenharmony_ci
86570af302Sopenharmony_ci	FLOCK(f);
87570af302Sopenharmony_ci
88570af302Sopenharmony_ci	if (!f->rpos) __toread(f);
89570af302Sopenharmony_ci	if (!f->rpos) goto input_fail;
90570af302Sopenharmony_ci
91570af302Sopenharmony_ci	for (p=(const unsigned char *)fmt; *p; p++) {
92570af302Sopenharmony_ci
93570af302Sopenharmony_ci		alloc = 0;
94570af302Sopenharmony_ci
95570af302Sopenharmony_ci		if (isspace(*p)) {
96570af302Sopenharmony_ci			while (isspace(p[1])) p++;
97570af302Sopenharmony_ci			shlim(f, 0);
98570af302Sopenharmony_ci			while (isspace(shgetc(f)));
99570af302Sopenharmony_ci			shunget(f);
100570af302Sopenharmony_ci			pos += shcnt(f);
101570af302Sopenharmony_ci			continue;
102570af302Sopenharmony_ci		}
103570af302Sopenharmony_ci		if (*p != '%' || p[1] == '%') {
104570af302Sopenharmony_ci			shlim(f, 0);
105570af302Sopenharmony_ci			if (*p == '%') {
106570af302Sopenharmony_ci				p++;
107570af302Sopenharmony_ci				while (isspace((c=shgetc(f))));
108570af302Sopenharmony_ci			} else {
109570af302Sopenharmony_ci				c = shgetc(f);
110570af302Sopenharmony_ci			}
111570af302Sopenharmony_ci			if (c!=*p) {
112570af302Sopenharmony_ci				shunget(f);
113570af302Sopenharmony_ci				if (c<0) goto input_fail;
114570af302Sopenharmony_ci				goto match_fail;
115570af302Sopenharmony_ci			}
116570af302Sopenharmony_ci			pos += shcnt(f);
117570af302Sopenharmony_ci			continue;
118570af302Sopenharmony_ci		}
119570af302Sopenharmony_ci
120570af302Sopenharmony_ci		p++;
121570af302Sopenharmony_ci		if (*p=='*') {
122570af302Sopenharmony_ci			dest = 0; p++;
123570af302Sopenharmony_ci		} else if (isdigit(*p) && p[1]=='$') {
124570af302Sopenharmony_ci			dest = arg_n(ap, *p-'0'); p+=2;
125570af302Sopenharmony_ci		} else {
126570af302Sopenharmony_ci			dest = va_arg(ap, void *);
127570af302Sopenharmony_ci		}
128570af302Sopenharmony_ci
129570af302Sopenharmony_ci		for (width=0; isdigit(*p); p++) {
130570af302Sopenharmony_ci			width = 10*width + *p - '0';
131570af302Sopenharmony_ci		}
132570af302Sopenharmony_ci
133570af302Sopenharmony_ci		if (*p=='m') {
134570af302Sopenharmony_ci			wcs = 0;
135570af302Sopenharmony_ci			s = 0;
136570af302Sopenharmony_ci			alloc = !!dest;
137570af302Sopenharmony_ci			p++;
138570af302Sopenharmony_ci		} else {
139570af302Sopenharmony_ci			alloc = 0;
140570af302Sopenharmony_ci		}
141570af302Sopenharmony_ci
142570af302Sopenharmony_ci		size = SIZE_def;
143570af302Sopenharmony_ci		switch (*p++) {
144570af302Sopenharmony_ci		case 'h':
145570af302Sopenharmony_ci			if (*p == 'h') p++, size = SIZE_hh;
146570af302Sopenharmony_ci			else size = SIZE_h;
147570af302Sopenharmony_ci			break;
148570af302Sopenharmony_ci		case 'l':
149570af302Sopenharmony_ci			if (*p == 'l') p++, size = SIZE_ll;
150570af302Sopenharmony_ci			else size = SIZE_l;
151570af302Sopenharmony_ci			break;
152570af302Sopenharmony_ci		case 'j':
153570af302Sopenharmony_ci			size = SIZE_ll;
154570af302Sopenharmony_ci			break;
155570af302Sopenharmony_ci		case 'z':
156570af302Sopenharmony_ci		case 't':
157570af302Sopenharmony_ci			size = SIZE_l;
158570af302Sopenharmony_ci			break;
159570af302Sopenharmony_ci		case 'L':
160570af302Sopenharmony_ci			size = SIZE_L;
161570af302Sopenharmony_ci			break;
162570af302Sopenharmony_ci		case 'd': case 'i': case 'o': case 'u': case 'x':
163570af302Sopenharmony_ci		case 'a': case 'e': case 'f': case 'g':
164570af302Sopenharmony_ci		case 'A': case 'E': case 'F': case 'G': case 'X':
165570af302Sopenharmony_ci		case 's': case 'c': case '[':
166570af302Sopenharmony_ci		case 'S': case 'C':
167570af302Sopenharmony_ci		case 'p': case 'n':
168570af302Sopenharmony_ci			p--;
169570af302Sopenharmony_ci			break;
170570af302Sopenharmony_ci		default:
171570af302Sopenharmony_ci			goto fmt_fail;
172570af302Sopenharmony_ci		}
173570af302Sopenharmony_ci
174570af302Sopenharmony_ci		t = *p;
175570af302Sopenharmony_ci
176570af302Sopenharmony_ci		/* C or S */
177570af302Sopenharmony_ci		if ((t&0x2f) == 3) {
178570af302Sopenharmony_ci			t |= 32;
179570af302Sopenharmony_ci			size = SIZE_l;
180570af302Sopenharmony_ci		}
181570af302Sopenharmony_ci
182570af302Sopenharmony_ci		switch (t) {
183570af302Sopenharmony_ci		case 'c':
184570af302Sopenharmony_ci			if (width < 1) width = 1;
185570af302Sopenharmony_ci		case '[':
186570af302Sopenharmony_ci			break;
187570af302Sopenharmony_ci		case 'n':
188570af302Sopenharmony_ci			store_int(dest, size, pos);
189570af302Sopenharmony_ci			/* do not increment match count, etc! */
190570af302Sopenharmony_ci			continue;
191570af302Sopenharmony_ci		default:
192570af302Sopenharmony_ci			shlim(f, 0);
193570af302Sopenharmony_ci			while (isspace(shgetc(f)));
194570af302Sopenharmony_ci			shunget(f);
195570af302Sopenharmony_ci			pos += shcnt(f);
196570af302Sopenharmony_ci		}
197570af302Sopenharmony_ci
198570af302Sopenharmony_ci		shlim(f, width);
199570af302Sopenharmony_ci		if (shgetc(f) < 0) goto input_fail;
200570af302Sopenharmony_ci		shunget(f);
201570af302Sopenharmony_ci
202570af302Sopenharmony_ci		switch (t) {
203570af302Sopenharmony_ci		case 's':
204570af302Sopenharmony_ci		case 'c':
205570af302Sopenharmony_ci		case '[':
206570af302Sopenharmony_ci			if (t == 'c' || t == 's') {
207570af302Sopenharmony_ci				memset(scanset, -1, sizeof scanset);
208570af302Sopenharmony_ci				scanset[0] = 0;
209570af302Sopenharmony_ci				if (t == 's') {
210570af302Sopenharmony_ci					scanset[1+'\t'] = 0;
211570af302Sopenharmony_ci					scanset[1+'\n'] = 0;
212570af302Sopenharmony_ci					scanset[1+'\v'] = 0;
213570af302Sopenharmony_ci					scanset[1+'\f'] = 0;
214570af302Sopenharmony_ci					scanset[1+'\r'] = 0;
215570af302Sopenharmony_ci					scanset[1+' '] = 0;
216570af302Sopenharmony_ci				}
217570af302Sopenharmony_ci			} else {
218570af302Sopenharmony_ci				if (*++p == '^') p++, invert = 1;
219570af302Sopenharmony_ci				else invert = 0;
220570af302Sopenharmony_ci				memset(scanset, invert, sizeof scanset);
221570af302Sopenharmony_ci				scanset[0] = 0;
222570af302Sopenharmony_ci				if (*p == '-') p++, scanset[1+'-'] = 1-invert;
223570af302Sopenharmony_ci				else if (*p == ']') p++, scanset[1+']'] = 1-invert;
224570af302Sopenharmony_ci				for (; *p != ']'; p++) {
225570af302Sopenharmony_ci					if (!*p) goto fmt_fail;
226570af302Sopenharmony_ci					if (*p=='-' && p[1] && p[1] != ']')
227570af302Sopenharmony_ci						for (c=p++[-1]; c<*p; c++)
228570af302Sopenharmony_ci							scanset[1+c] = 1-invert;
229570af302Sopenharmony_ci					scanset[1+*p] = 1-invert;
230570af302Sopenharmony_ci				}
231570af302Sopenharmony_ci			}
232570af302Sopenharmony_ci			wcs = 0;
233570af302Sopenharmony_ci			s = 0;
234570af302Sopenharmony_ci			i = 0;
235570af302Sopenharmony_ci			k = t=='c' ? width+1U : 31;
236570af302Sopenharmony_ci			if (size == SIZE_l) {
237570af302Sopenharmony_ci				if (alloc) {
238570af302Sopenharmony_ci					wcs = malloc(k*sizeof(wchar_t));
239570af302Sopenharmony_ci					if (!wcs) goto alloc_fail;
240570af302Sopenharmony_ci				} else {
241570af302Sopenharmony_ci					wcs = dest;
242570af302Sopenharmony_ci				}
243570af302Sopenharmony_ci				st = (mbstate_t){0};
244570af302Sopenharmony_ci				while (scanset[(c=shgetc(f))+1]) {
245570af302Sopenharmony_ci					switch (mbrtowc(&wc, &(char){c}, 1, &st)) {
246570af302Sopenharmony_ci					case -1:
247570af302Sopenharmony_ci						goto input_fail;
248570af302Sopenharmony_ci					case -2:
249570af302Sopenharmony_ci						continue;
250570af302Sopenharmony_ci					}
251570af302Sopenharmony_ci					if (wcs) wcs[i++] = wc;
252570af302Sopenharmony_ci					if (alloc && i==k) {
253570af302Sopenharmony_ci						k+=k+1;
254570af302Sopenharmony_ci						wchar_t *tmp = realloc(wcs, k*sizeof(wchar_t));
255570af302Sopenharmony_ci						if (!tmp) goto alloc_fail;
256570af302Sopenharmony_ci						wcs = tmp;
257570af302Sopenharmony_ci					}
258570af302Sopenharmony_ci				}
259570af302Sopenharmony_ci				if (!mbsinit(&st)) goto input_fail;
260570af302Sopenharmony_ci			} else if (alloc) {
261570af302Sopenharmony_ci				s = malloc(k);
262570af302Sopenharmony_ci				if (!s) goto alloc_fail;
263570af302Sopenharmony_ci				while (scanset[(c=shgetc(f))+1]) {
264570af302Sopenharmony_ci					s[i++] = c;
265570af302Sopenharmony_ci					if (i==k) {
266570af302Sopenharmony_ci						k+=k+1;
267570af302Sopenharmony_ci						char *tmp = realloc(s, k);
268570af302Sopenharmony_ci						if (!tmp) goto alloc_fail;
269570af302Sopenharmony_ci						s = tmp;
270570af302Sopenharmony_ci					}
271570af302Sopenharmony_ci				}
272570af302Sopenharmony_ci			} else if ((s = dest)) {
273570af302Sopenharmony_ci				while (scanset[(c=shgetc(f))+1])
274570af302Sopenharmony_ci					s[i++] = c;
275570af302Sopenharmony_ci			} else {
276570af302Sopenharmony_ci				while (scanset[(c=shgetc(f))+1]);
277570af302Sopenharmony_ci			}
278570af302Sopenharmony_ci			shunget(f);
279570af302Sopenharmony_ci			if (!shcnt(f)) goto match_fail;
280570af302Sopenharmony_ci			if (t == 'c' && shcnt(f) != width) goto match_fail;
281570af302Sopenharmony_ci			if (alloc) {
282570af302Sopenharmony_ci				if (size == SIZE_l) *(wchar_t **)dest = wcs;
283570af302Sopenharmony_ci				else *(char **)dest = s;
284570af302Sopenharmony_ci			}
285570af302Sopenharmony_ci			if (t != 'c') {
286570af302Sopenharmony_ci				if (wcs) wcs[i] = 0;
287570af302Sopenharmony_ci				if (s) s[i] = 0;
288570af302Sopenharmony_ci			}
289570af302Sopenharmony_ci			break;
290570af302Sopenharmony_ci		case 'p':
291570af302Sopenharmony_ci		case 'X':
292570af302Sopenharmony_ci		case 'x':
293570af302Sopenharmony_ci			base = 16;
294570af302Sopenharmony_ci			goto int_common;
295570af302Sopenharmony_ci		case 'o':
296570af302Sopenharmony_ci			base = 8;
297570af302Sopenharmony_ci			goto int_common;
298570af302Sopenharmony_ci		case 'd':
299570af302Sopenharmony_ci		case 'u':
300570af302Sopenharmony_ci			base = 10;
301570af302Sopenharmony_ci			goto int_common;
302570af302Sopenharmony_ci		case 'i':
303570af302Sopenharmony_ci			base = 0;
304570af302Sopenharmony_ci		int_common:
305570af302Sopenharmony_ci			x = __intscan(f, base, 0, ULLONG_MAX);
306570af302Sopenharmony_ci			if (!shcnt(f)) goto match_fail;
307570af302Sopenharmony_ci			if (t=='p' && dest) *(void **)dest = (void *)(uintptr_t)x;
308570af302Sopenharmony_ci			else store_int(dest, size, x);
309570af302Sopenharmony_ci			break;
310570af302Sopenharmony_ci		case 'a': case 'A':
311570af302Sopenharmony_ci		case 'e': case 'E':
312570af302Sopenharmony_ci		case 'f': case 'F':
313570af302Sopenharmony_ci		case 'g': case 'G':
314570af302Sopenharmony_ci#if (defined(MUSL_AARCH64_ARCH)) || (defined(MUSL_ARM_ARCH))
315570af302Sopenharmony_ci			if (width == 0 || width > sizeof(buf) - 1)
316570af302Sopenharmony_ci				width = sizeof(buf) - 1;
317570af302Sopenharmony_ci			int count = parsefloat(f, buf, buf + width);
318570af302Sopenharmony_ci			if (count == 0)
319570af302Sopenharmony_ci				goto match_fail;
320570af302Sopenharmony_ci			if (dest) switch (size) {
321570af302Sopenharmony_ci			case SIZE_def:
322570af302Sopenharmony_ci				y = strtof(buf, &endptr);
323570af302Sopenharmony_ci				*(float *)dest = y;
324570af302Sopenharmony_ci				break;
325570af302Sopenharmony_ci			case SIZE_l:
326570af302Sopenharmony_ci				y = strtod(buf, &endptr);
327570af302Sopenharmony_ci				*(double *)dest = y;
328570af302Sopenharmony_ci				break;
329570af302Sopenharmony_ci			case SIZE_L:
330570af302Sopenharmony_ci				y = strtold(buf, &endptr);
331570af302Sopenharmony_ci				*(long double *)dest = y;
332570af302Sopenharmony_ci				break;
333570af302Sopenharmony_ci			}
334570af302Sopenharmony_ci			break;
335570af302Sopenharmony_ci		}
336570af302Sopenharmony_ci#else
337570af302Sopenharmony_ci			y = __floatscan(f, size, 0);
338570af302Sopenharmony_ci			if (!shcnt(f)) goto match_fail;
339570af302Sopenharmony_ci			if (dest) switch (size) {
340570af302Sopenharmony_ci			case SIZE_def:
341570af302Sopenharmony_ci				*(float *)dest = y;
342570af302Sopenharmony_ci				break;
343570af302Sopenharmony_ci			case SIZE_l:
344570af302Sopenharmony_ci				*(double *)dest = y;
345570af302Sopenharmony_ci				break;
346570af302Sopenharmony_ci			case SIZE_L:
347570af302Sopenharmony_ci				*(long double *)dest = y;
348570af302Sopenharmony_ci				break;
349570af302Sopenharmony_ci			}
350570af302Sopenharmony_ci			break;
351570af302Sopenharmony_ci		}
352570af302Sopenharmony_ci#endif
353570af302Sopenharmony_ci		pos += shcnt(f);
354570af302Sopenharmony_ci		if (dest) matches++;
355570af302Sopenharmony_ci	}
356570af302Sopenharmony_ci	if (0) {
357570af302Sopenharmony_cifmt_fail:
358570af302Sopenharmony_cialloc_fail:
359570af302Sopenharmony_ciinput_fail:
360570af302Sopenharmony_ci		if (!matches) matches--;
361570af302Sopenharmony_cimatch_fail:
362570af302Sopenharmony_ci		if (alloc) {
363570af302Sopenharmony_ci			free(s);
364570af302Sopenharmony_ci			free(wcs);
365570af302Sopenharmony_ci		}
366570af302Sopenharmony_ci	}
367570af302Sopenharmony_ci	FUNLOCK(f);
368570af302Sopenharmony_ci	return matches;
369570af302Sopenharmony_ci}
370570af302Sopenharmony_ci
371570af302Sopenharmony_ciweak_alias(vfscanf,__isoc99_vfscanf);
372