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