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