xref: /plan9-contrib/sys/src/libstdio/vfscanf.c (revision 22df390c30710ddd2119f3e7bb6c92dc399cabb9)
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 	USED(f, width);
107 	if(store){
108 		--ncvt;	/* this assignment doesn't count! */
109 		switch(type){
110 		case 'h': *va_arg(*args, short *)=nread; break;
111 		case 'n': *va_arg(*args, int *)=nread; break;
112 		case 'l':
113 		case 'L': *va_arg(*args, long *)=nread; break;
114 		}
115 	}
116 	return 1;
117 }
118 #define	SIGNED		1
119 #define	UNSIGNED	2
120 #define	POINTER		3
121 /*
122  * Generic fixed-point conversion
123  *	f is the input FILE *;
124  *	args is the va_list * into which to store the number;
125  *	store is a flag to enable storing;
126  *	width is the maximum field width;
127  *	type is 'h' 'l' or 'L', the scanf type modifier;
128  *	unsgned is SIGNED, UNSIGNED or POINTER, giving part of the type to store in;
129  *	base is the number base -- if 0, C number syntax is used.
130  */
icvt_fixed(FILE * f,va_list * args,int store,int width,int type,int unsgned,int base)131 static int icvt_fixed(FILE *f, va_list *args,
132 				int store, int width, int type, int unsgned, int base){
133 	unsigned long int num=0;
134 	int sign=1, ndig=0, dig;
135 	int c;
136 	do
137 		c=ngetc(f);
138 	while(isspace(c));
139 	if(width--==0){
140 		nungetc(c, f);
141 		goto Done;
142 	}
143 	if(c=='+'){
144 		wgetc(c, f, Done);
145 	}
146 	else if(c=='-'){
147 		sign=-1;
148 		wgetc(c, f, Done);
149 	}
150 	switch(base){
151 	case 0:
152 		if(c=='0'){
153 			wgetc(c, f, Done);
154 			if(c=='x' || c=='X'){
155 				wgetc(c, f, Done);
156 				base=16;
157 			}
158 			else{
159 				ndig=1;
160 				base=8;
161 			}
162 		}
163 		else
164 			base=10;
165 		break;
166 	case 16:
167 		if(c=='0'){
168 			wgetc(c, f, Done);
169 			if(c=='x' || c=='X'){
170 				wgetc(c, f, Done);
171 			}
172 			else ndig=1;
173 		}
174 		break;
175 	}
176 	while('0'<=c && c<='9' || 'a'<=c && c<='f' || 'A'<=c && c<='F'){
177 		dig='0'<=c && c<='9'?c-'0':'a'<=c && c<='f'?c-'a'+10:c-'A'+10;
178 		if(dig>=base) break;
179 		ndig++;
180 		num=num*base+dig;
181 		wgetc(c, f, Done);
182 	}
183 	nungetc(c, f);
184 Done:
185 	if(ndig==0) return 0;
186 	if(store){
187 		switch(unsgned){
188 		case SIGNED:
189 			switch(type){
190 			case 'h': *va_arg(*args,  short *)=num*sign; break;
191 			case 'n': *va_arg(*args,  int *)=num*sign; break;
192 			case 'l':
193 			case 'L': *va_arg(*args,  long *)=num*sign; break;
194 			}
195 			break;
196 		case UNSIGNED:
197 			switch(type){
198 			case 'h': *va_arg(*args, unsigned short *)=num*sign; break;
199 			case 'n': *va_arg(*args, unsigned int *)=num*sign; break;
200 			case 'l':
201 			case 'L': *va_arg(*args, unsigned long *)=num*sign; break;
202 			}
203 			break;
204 		case POINTER:
205 			*va_arg(*args, void **)=(void *)(num*sign); break;
206 		}
207 	}
208 	return 1;
209 }
icvt_d(FILE * f,va_list * args,int store,int width,int type)210 static int icvt_d(FILE *f, va_list *args, int store, int width, int type){
211 	return icvt_fixed(f, args, store, width, type, SIGNED, 10);
212 }
icvt_x(FILE * f,va_list * args,int store,int width,int type)213 static int icvt_x(FILE *f, va_list *args, int store, int width, int type){
214 	return icvt_fixed(f, args, store, width, type, UNSIGNED, 16);
215 }
icvt_o(FILE * f,va_list * args,int store,int width,int type)216 static int icvt_o(FILE *f, va_list *args, int store, int width, int type){
217 	return icvt_fixed(f, args, store, width, type, UNSIGNED, 8);
218 }
icvt_i(FILE * f,va_list * args,int store,int width,int type)219 static int icvt_i(FILE *f, va_list *args, int store, int width, int type){
220 	return icvt_fixed(f, args, store, width, type, SIGNED, 0);
221 }
icvt_u(FILE * f,va_list * args,int store,int width,int type)222 static int icvt_u(FILE *f, va_list *args, int store, int width, int type){
223 	return icvt_fixed(f, args, store, width, type, UNSIGNED, 10);
224 }
icvt_p(FILE * f,va_list * args,int store,int width,int type)225 static int icvt_p(FILE *f, va_list *args, int store, int width, int type){
226 	return icvt_fixed(f, args, store, width, type, POINTER, 16);
227 }
228 #define	NBUF	509
icvt_f(FILE * f,va_list * args,int store,int width,int type)229 static int icvt_f(FILE *f, va_list *args, int store, int width, int type){
230 	char buf[NBUF+1];
231 	char *s=buf;
232 	int c, ndig=0, ndpt=0, nexp=1;
233 	if(width<0 || NBUF<width) width=NBUF;	/* bug -- no limit specified in ansi */
234 	do
235 		c=ngetc(f);
236 	while(isspace(c));
237 	if(width--==0){
238 		nungetc(c, f);
239 		goto Done;
240 	}
241 	if(c=='+' || c=='-'){
242 		*s++=c;
243 		wgetc(c, f, Done);
244 	}
245 	while('0'<=c && c<='9' || ndpt==0 && c=='.'){
246 		if(c=='.') ndpt++;
247 		else ndig++;
248 		*s++=c;
249 		wgetc(c, f, Done);
250 	}
251 	if(c=='e' || c=='E'){
252 		*s++=c;
253 		nexp=0;
254 		wgetc(c, f, Done);
255 		if(c=='+' || c=='-'){
256 			*s++=c;
257 			wgetc(c, f, Done);
258 		}
259 		while('0'<=c && c<='9'){
260 			*s++=c;
261 			nexp++;
262 			wgetc(c, f, Done);
263 		}
264 	}
265 	nungetc(c, f);
266 Done:
267 	if(ndig==0 || nexp==0) return 0;
268 	*s='\0';
269 	if(store) switch(type){
270 	case 'h':
271 	case 'n': *va_arg(*args, float *)=atof(buf); break;
272 	case 'L': /* bug -- should store in a long double */
273 	case 'l': *va_arg(*args, double *)=atof(buf); break;
274 	}
275 	return 1;
276 }
icvt_s(FILE * f,va_list * args,int store,int width,int type)277 static int icvt_s(FILE *f, va_list *args, int store, int width, int type){
278 	USED(type);
279 	int c, nn;
280 	register char *s;
281 	s = nil;				/* silence used and not set */
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 	USED(type);
308 	int c;
309 	register char *s;
310 	s = nil;				/* silence used and not set */
311 	if(store) s=va_arg(*args, char *);
312 	if(width<0) width=1;
313 	for(;;){
314 		wgetc(c, f, Done);
315 		if(c==EOF) return 0;
316 		if(store) *s++=c;
317 	}
318 Done:
319 	return 1;
320 }
match(int c,const char * pat)321 static int match(int c, const char *pat){
322 	int ok=1;
323 	if(*pat=='^'){
324 		ok=!ok;
325 		pat++;
326 	}
327 	while(pat!=fmtp){
328 		if(pat+2<fmtp && pat[1]=='-'){
329 			if(pat[0]<=c && c<=pat[2]
330 			|| pat[2]<=c && c<=pat[0])
331 				return ok;
332 			pat+=2;
333 		}
334 		else if(c==*pat) return ok;
335 		pat++;
336 	}
337 	return !ok;
338 }
icvt_sq(FILE * f,va_list * args,int store,int width,int type)339 static int icvt_sq(FILE *f, va_list *args, int store, int width, int type){
340 	USED(type);
341 	int c, nn;
342 	register char *s;
343 	register const char *pat;
344 	pat=++fmtp;
345 	if(*fmtp=='^') fmtp++;
346 	if(*fmtp!='\0') fmtp++;
347 	while(*fmtp!='\0' && *fmtp!=']') fmtp++;
348 	s = nil;				/* silence used and not set */
349 	if(store) s=va_arg(*args, char *);
350 	nn=0;
351 	for(;;){
352 		wgetc(c, f, Done);
353 		if(c==EOF){
354 			nread--;
355 			if(nn==0) return 0;
356 			else goto Done;
357 		}
358 		if(!match(c, pat)) break;
359 		if(store) *s++=c;
360 		nn++;
361 	}
362 	nungetc(c, f);
363 Done:
364 	if(store) *s='\0';
365 	return 1;
366 }
367