xref: /plan9/sys/src/libstdio/vfscanf.c (revision 43751f27fa93b002d5e921851faf5da5cbdcb417)
1 /*
2  * pANS stdio -- vfscanf
3  */
4 #include "iolib.h"
5 #include <ctype.h>
6 static int icvt_f(FILE *f, va_list *args, int store, int width, int type);
7 static int icvt_x(FILE *f, va_list *args, int store, int width, int type);
8 static int icvt_sq(FILE *f, va_list *args, int store, int width, int type);
9 static int icvt_c(FILE *f, va_list *args, int store, int width, int type);
10 static int icvt_d(FILE *f, va_list *args, int store, int width, int type);
11 static int icvt_i(FILE *f, va_list *args, int store, int width, int type);
12 static int icvt_n(FILE *f, va_list *args, int store, int width, int type);
13 static int icvt_o(FILE *f, va_list *args, int store, int width, int type);
14 static int icvt_p(FILE *f, va_list *args, int store, int width, int type);
15 static int icvt_s(FILE *f, va_list *args, int store, int width, int type);
16 static int icvt_u(FILE *f, va_list *args, int store, int width, int type);
17 static int (*icvt[])(FILE *, va_list *, int, int, int)={
18 0,	0,	0,	0,	0,	0,	0,	0,	/* ^@ ^A ^B ^C ^D ^E ^F ^G */
19 0,	0,	0,	0,	0,	0,	0,	0,	/* ^H ^I ^J ^K ^L ^M ^N ^O */
20 0,	0,	0,	0,	0,	0,	0,	0,	/* ^P ^Q ^R ^S ^T ^U ^V ^W */
21 0,	0,	0,	0,	0,	0,	0,	0,	/* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
22 0,	0,	0,	0,	0,	0,	0,	0,	/* sp  !  "  #  $  %  &  ' */
23 0,	0,	0,	0,	0,	0,	0,	0,	/*  (  )  *  +  ,  -  .  / */
24 0,	0,	0,	0,	0,	0,	0,	0,	/*  0  1  2  3  4  5  6  7 */
25 0,	0,	0,	0,	0,	0,	0,	0,	/*  8  9  :  ;  <  =  >  ? */
26 0,	0,	0,	0,	0,	icvt_f,	0,	icvt_f,	/*  @  A  B  C  D  E  F  G */
27 0,	0,	0,	0,	0,	0,	0,	0,	/*  H  I  J  K  L  M  N  O */
28 0,	0,	0,	0,	0,	0,	0,	0,	/*  P  Q  R  S  T  U  V  W */
29 icvt_x,	0,	0,	icvt_sq,0,	0,	0,	0,	/*  X  Y  Z  [  \  ]  ^  _ */
30 0,	0,	0,	icvt_c,	icvt_d,	icvt_f,	icvt_f,	icvt_f,	/*  `  a  b  c  d  e  f  g */
31 0,	icvt_i,	0,	0,	0,	0,	icvt_n,	icvt_o,	/*  h  i  j  k  l  m  n  o */
32 icvt_p,	0,	0,	icvt_s,	0,	icvt_u,	0,	0,	/*  p  q  r  s  t  u  v  w */
33 icvt_x,	0,	0,	0,	0,	0,	0,	0,	/*  x  y  z  {  |  }  ~ ^? */
34 
35 0,	0,	0,	0,	0,	0,	0,	0,
36 0,	0,	0,	0,	0,	0,	0,	0,
37 0,	0,	0,	0,	0,	0,	0,	0,
38 0,	0,	0,	0,	0,	0,	0,	0,
39 0,	0,	0,	0,	0,	0,	0,	0,
40 0,	0,	0,	0,	0,	0,	0,	0,
41 0,	0,	0,	0,	0,	0,	0,	0,
42 0,	0,	0,	0,	0,	0,	0,	0,
43 0,	0,	0,	0,	0,	0,	0,	0,
44 0,	0,	0,	0,	0,	0,	0,	0,
45 0,	0,	0,	0,	0,	0,	0,	0,
46 0,	0,	0,	0,	0,	0,	0,	0,
47 0,	0,	0,	0,	0,	0,	0,	0,
48 0,	0,	0,	0,	0,	0,	0,	0,
49 0,	0,	0,	0,	0,	0,	0,	0,
50 0,	0,	0,	0,	0,	0,	0,	0,
51 
52 };
53 #define	ngetc(f)		(nread++, getc(f))
54 #define	nungetc(c, f)		(--nread, ungetc((c), f))
55 #define	wgetc(c, f, out)	if(width--==0) goto out; (c)=ngetc(f)
56 #define	wungetc(c, f)		(++width, nungetc(c, f))
57 static int nread, ncvt;
58 static const char *fmtp;
59 
vfscanf(FILE * f,const char * s,va_list args)60 int vfscanf(FILE *f, const char *s, va_list args){
61 	int c, width, type, store;
62 	nread=0;
63 	ncvt=0;
64 	fmtp=s;
65 	for(;*fmtp;fmtp++) switch(*fmtp){
66 	default:
67 		if(isspace(*fmtp)){
68 			do
69 				c=ngetc(f);
70 			while(isspace(c));
71 			if(c==EOF) return ncvt?ncvt:EOF;
72 			nungetc(c, f);
73 			break;
74 		}
75 	NonSpecial:
76 		c=ngetc(f);
77 		if(c==EOF) return ncvt?ncvt:EOF;
78 		if(c!=*fmtp){
79 			nungetc(c, f);
80 			return ncvt;
81 		}
82 		break;
83 	case '%':
84 		fmtp++;
85 		if(*fmtp!='*') store=1;
86 		else{
87 			store=0;
88 			fmtp++;
89 		}
90 		if('0'<=*fmtp && *fmtp<='9'){
91 			width=0;
92 			while('0'<=*fmtp && *fmtp<='9') width=width*10 + *fmtp++ - '0';
93 		}
94 		else
95 			width=-1;
96 		type=*fmtp=='h' || *fmtp=='l' || *fmtp=='L'?*fmtp++:'n';
97 		if(!icvt[*fmtp]) goto NonSpecial;
98 		if(!(*icvt[*fmtp])(f, &args, store, width, type))
99 			return ncvt?ncvt:EOF;
100 		if(*fmtp=='\0') break;
101 		if(store) ncvt++;
102 	}
103 	return ncvt;
104 }
icvt_n(FILE * f,va_list * args,int store,int width,int type)105 static int icvt_n(FILE *f, va_list *args, int store, int width, int type){
106 #pragma ref f
107 #pragma ref width
108 	if(store){
109 		--ncvt;	/* this assignment doesn't count! */
110 		switch(type){
111 		case 'h': *va_arg(*args, short *)=nread; break;
112 		case 'n': *va_arg(*args, int *)=nread; break;
113 		case 'l':
114 		case 'L': *va_arg(*args, long *)=nread; break;
115 		}
116 	}
117 	return 1;
118 }
119 #define	SIGNED		1
120 #define	UNSIGNED	2
121 #define	POINTER		3
122 /*
123  * Generic fixed-point conversion
124  *	f is the input FILE *;
125  *	args is the va_list * into which to store the number;
126  *	store is a flag to enable storing;
127  *	width is the maximum field width;
128  *	type is 'h' 'l' or 'L', the scanf type modifier;
129  *	unsgned is SIGNED, UNSIGNED or POINTER, giving part of the type to store in;
130  *	base is the number base -- if 0, C number syntax is used.
131  */
icvt_fixed(FILE * f,va_list * args,int store,int width,int type,int unsgned,int base)132 static int icvt_fixed(FILE *f, va_list *args,
133 				int store, int width, int type, int unsgned, int base){
134 	unsigned long int num=0;
135 	int sign=1, ndig=0, dig;
136 	int c;
137 	do
138 		c=ngetc(f);
139 	while(isspace(c));
140 	if(width--==0){
141 		nungetc(c, f);
142 		goto Done;
143 	}
144 	if(c=='+'){
145 		wgetc(c, f, Done);
146 	}
147 	else if(c=='-'){
148 		sign=-1;
149 		wgetc(c, f, Done);
150 	}
151 	switch(base){
152 	case 0:
153 		if(c=='0'){
154 			wgetc(c, f, Done);
155 			if(c=='x' || c=='X'){
156 				wgetc(c, f, Done);
157 				base=16;
158 			}
159 			else{
160 				ndig=1;
161 				base=8;
162 			}
163 		}
164 		else
165 			base=10;
166 		break;
167 	case 16:
168 		if(c=='0'){
169 			wgetc(c, f, Done);
170 			if(c=='x' || c=='X'){
171 				wgetc(c, f, Done);
172 			}
173 			else ndig=1;
174 		}
175 		break;
176 	}
177 	while('0'<=c && c<='9' || 'a'<=c && c<='f' || 'A'<=c && c<='F'){
178 		dig='0'<=c && c<='9'?c-'0':'a'<=c && c<='f'?c-'a'+10:c-'A'+10;
179 		if(dig>=base) break;
180 		ndig++;
181 		num=num*base+dig;
182 		wgetc(c, f, Done);
183 	}
184 	nungetc(c, f);
185 Done:
186 	if(ndig==0) return 0;
187 	if(store){
188 		switch(unsgned){
189 		case SIGNED:
190 			switch(type){
191 			case 'h': *va_arg(*args,  short *)=num*sign; break;
192 			case 'n': *va_arg(*args,  int *)=num*sign; break;
193 			case 'l':
194 			case 'L': *va_arg(*args,  long *)=num*sign; break;
195 			}
196 			break;
197 		case UNSIGNED:
198 			switch(type){
199 			case 'h': *va_arg(*args, unsigned short *)=num*sign; break;
200 			case 'n': *va_arg(*args, unsigned int *)=num*sign; break;
201 			case 'l':
202 			case 'L': *va_arg(*args, unsigned long *)=num*sign; break;
203 			}
204 			break;
205 		case POINTER:
206 			*va_arg(*args, void **)=(void *)(num*sign); break;
207 		}
208 	}
209 	return 1;
210 }
icvt_d(FILE * f,va_list * args,int store,int width,int type)211 static int icvt_d(FILE *f, va_list *args, int store, int width, int type){
212 	return icvt_fixed(f, args, store, width, type, SIGNED, 10);
213 }
icvt_x(FILE * f,va_list * args,int store,int width,int type)214 static int icvt_x(FILE *f, va_list *args, int store, int width, int type){
215 	return icvt_fixed(f, args, store, width, type, UNSIGNED, 16);
216 }
icvt_o(FILE * f,va_list * args,int store,int width,int type)217 static int icvt_o(FILE *f, va_list *args, int store, int width, int type){
218 	return icvt_fixed(f, args, store, width, type, UNSIGNED, 8);
219 }
icvt_i(FILE * f,va_list * args,int store,int width,int type)220 static int icvt_i(FILE *f, va_list *args, int store, int width, int type){
221 	return icvt_fixed(f, args, store, width, type, SIGNED, 0);
222 }
icvt_u(FILE * f,va_list * args,int store,int width,int type)223 static int icvt_u(FILE *f, va_list *args, int store, int width, int type){
224 	return icvt_fixed(f, args, store, width, type, UNSIGNED, 10);
225 }
icvt_p(FILE * f,va_list * args,int store,int width,int type)226 static int icvt_p(FILE *f, va_list *args, int store, int width, int type){
227 	return icvt_fixed(f, args, store, width, type, POINTER, 16);
228 }
229 #define	NBUF	509
icvt_f(FILE * f,va_list * args,int store,int width,int type)230 static int icvt_f(FILE *f, va_list *args, int store, int width, int type){
231 	char buf[NBUF+1];
232 	char *s=buf;
233 	int c, ndig=0, ndpt=0, nexp=1;
234 	if(width<0 || NBUF<width) width=NBUF;	/* bug -- no limit specified in ansi */
235 	do
236 		c=ngetc(f);
237 	while(isspace(c));
238 	if(width--==0){
239 		nungetc(c, f);
240 		goto Done;
241 	}
242 	if(c=='+' || c=='-'){
243 		*s++=c;
244 		wgetc(c, f, Done);
245 	}
246 	while('0'<=c && c<='9' || ndpt==0 && c=='.'){
247 		if(c=='.') ndpt++;
248 		else ndig++;
249 		*s++=c;
250 		wgetc(c, f, Done);
251 	}
252 	if(c=='e' || c=='E'){
253 		*s++=c;
254 		nexp=0;
255 		wgetc(c, f, Done);
256 		if(c=='+' || c=='-'){
257 			*s++=c;
258 			wgetc(c, f, Done);
259 		}
260 		while('0'<=c && c<='9'){
261 			*s++=c;
262 			nexp++;
263 			wgetc(c, f, Done);
264 		}
265 	}
266 	nungetc(c, f);
267 Done:
268 	if(ndig==0 || nexp==0) return 0;
269 	*s='\0';
270 	if(store) switch(type){
271 	case 'h':
272 	case 'n': *va_arg(*args, float *)=atof(buf); break;
273 	case 'L': /* bug -- should store in a long double */
274 	case 'l': *va_arg(*args, double *)=atof(buf); break;
275 	}
276 	return 1;
277 }
icvt_s(FILE * f,va_list * args,int store,int width,int type)278 static int icvt_s(FILE *f, va_list *args, int store, int width, int type){
279 #pragma ref type
280 	int c, nn;
281 	register char *s;
282 	if(store) s=va_arg(*args, char *);
283 	do
284 		c=ngetc(f);
285 	while(isspace(c));
286 	if(width--==0){
287 		nungetc(c, f);
288 		goto Done;
289 	}
290 	nn=0;
291 	while(!isspace(c)){
292 		if(c==EOF){
293 			nread--;
294 			if(nn==0) return 0;
295 			else goto Done;
296 		}
297 		nn++;
298 		if(store) *s++=c;
299 		wgetc(c, f, Done);
300 	}
301 	nungetc(c, f);
302 Done:
303 	if(store) *s='\0';
304 	return 1;
305 }
icvt_c(FILE * f,va_list * args,int store,int width,int type)306 static int icvt_c(FILE *f, va_list *args, int store, int width, int type){
307 #pragma ref type
308 	int c;
309 	register char *s;
310 	if(store) s=va_arg(*args, char *);
311 	if(width<0) width=1;
312 	for(;;){
313 		wgetc(c, f, Done);
314 		if(c==EOF) return 0;
315 		if(store) *s++=c;
316 	}
317 Done:
318 	return 1;
319 }
match(int c,const char * pat)320 static int match(int c, const char *pat){
321 	int ok=1;
322 	if(*pat=='^'){
323 		ok=!ok;
324 		pat++;
325 	}
326 	while(pat!=fmtp){
327 		if(pat+2<fmtp && pat[1]=='-'){
328 			if(pat[0]<=c && c<=pat[2]
329 			|| pat[2]<=c && c<=pat[0])
330 				return ok;
331 			pat+=2;
332 		}
333 		else if(c==*pat) return ok;
334 		pat++;
335 	}
336 	return !ok;
337 }
icvt_sq(FILE * f,va_list * args,int store,int width,int type)338 static int icvt_sq(FILE *f, va_list *args, int store, int width, int type){
339 #pragma ref type
340 	int c, nn;
341 	register char *s;
342 	register const char *pat;
343 	pat=++fmtp;
344 	if(*fmtp=='^') fmtp++;
345 	if(*fmtp!='\0') fmtp++;
346 	while(*fmtp!='\0' && *fmtp!=']') fmtp++;
347 	if(store) s=va_arg(*args, char *);
348 	nn=0;
349 	for(;;){
350 		wgetc(c, f, Done);
351 		if(c==EOF){
352 			nread--;
353 			if(nn==0) return 0;
354 			else goto Done;
355 		}
356 		if(!match(c, pat)) break;
357 		if(store) *s++=c;
358 		nn++;
359 	}
360 	nungetc(c, f);
361 Done:
362 	if(store) *s='\0';
363 	return 1;
364 }
365