vfscanf.c raw

   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  
  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  
  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  
  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