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