xref: /plan9-contrib/sys/src/libstdio/vfscanf.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
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 
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 }
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  */
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 }
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 }
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 }
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 }
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 }
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 }
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
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 }
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 			if(nn==0) return 0;
294 			else goto Done;
295 		}
296 		nn++;
297 		if(store) *s++=c;
298 		wgetc(c, f, Done);
299 	}
300 	nungetc(c, f);
301 Done:
302 	if(store) *s='\0';
303 	return 1;
304 }
305 static int icvt_c(FILE *f, va_list *args, int store, int width, int type){
306 #pragma ref type
307 	int c;
308 	register char *s;
309 	if(store) s=va_arg(*args, char *);
310 	if(width<0) width=1;
311 	for(;;){
312 		wgetc(c, f, Done);
313 		if(c==EOF) return 0;
314 		if(store) *s++=c;
315 	}
316 Done:
317 	return 1;
318 }
319 static int match(int c, const char *pat){
320 	int ok=1;
321 	if(*pat=='^'){
322 		ok=!ok;
323 		pat++;
324 	}
325 	while(pat!=fmtp){
326 		if(pat+2<fmtp && pat[1]=='-'){
327 			if(pat[0]<=c && c<=pat[2]
328 			|| pat[2]<=c && c<=pat[0])
329 				return ok;
330 			pat+=2;
331 		}
332 		else if(c==*pat) return ok;
333 		pat++;
334 	}
335 	return !ok;
336 }
337 static int icvt_sq(FILE *f, va_list *args, int store, int width, int type){
338 #pragma ref type
339 	int c, nn;
340 	register char *s;
341 	register const char *pat;
342 	pat=++fmtp;
343 	if(*fmtp=='^') fmtp++;
344 	if(*fmtp!='\0') fmtp++;
345 	while(*fmtp!='\0' && *fmtp!=']') fmtp++;
346 	if(store) s=va_arg(*args, char *);
347 	nn=0;
348 	for(;;){
349 		wgetc(c, f, Done);
350 		if(c==EOF){
351 			if(nn==0) return 0;
352 			else goto Done;
353 		}
354 		if(!match(c, pat)) break;
355 		if(store) *s++=c;
356 		nn++;
357 	}
358 	nungetc(c, f);
359 Done:
360 	if(store) *s='\0';
361 	return 1;
362 }
363