Renamed include file aros/_timeval.h to aros/types/timeval_s.h.
[AROS.git] / compiler / clib / __vcscan.c
blob7ea0e82bce08247fd978905850ecbd3880ed7947
1 /*
2 Copyright © 1995-2001, The AROS Development Team. All rights reserved.
3 $Id$
5 Function to scan a string like scanf().
6 */
8 #define __vcscan __vcscan
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <stdarg.h>
13 #ifndef AROS_NO_LIMITS_H
14 # include <limits.h>
15 #else
16 # define ULONG_MAX 4294967295UL
17 #endif
18 #include <ctype.h>
19 #include <math.h>
21 #ifndef AROSC_ROM
22 #define FULL_SPECIFIERS
23 #endif
25 /* some macros to cut this short
26 * NEXT(c); read next character
27 * PREV(c); ungetc a character
28 * VAL(a) leads to 1 if a is true and valid
30 #define NEXT(c) ((c)=(*getc)(data),size++,incount++)
31 #define PREV(c) do{if((c)!=EOF)(*ungetc)((c),data);size--;incount--;}while(0)
32 #define VAL(a) ((a)&&size<=width)
34 extern unsigned char *__decimalpoint;
36 #ifdef FULL_SPECIFIERS
37 const static unsigned char undef[3][sizeof(double)]= /* Undefined numeric values, IEEE */
38 { { 0x7f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00 }, /* +inf */
39 { 0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00 }, /* -inf */
40 { 0x7f,0xf1,0x00,0x00,0x00,0x00,0x00,0x00 } /* NaN */
42 #endif
44 #undef getc
45 #undef ungetc
47 /*****************************************************************************
49 NAME */
51 int __vcscan (
53 /* SYNOPSIS */
54 void * data,
55 int (* getc)(void *),
56 int (* ungetc)(int,void *),
57 const char * format,
58 va_list args)
60 /* FUNCTION
61 Scan an input stream as specified in format. The result of
62 the scan will be placed in args.
64 INPUTS
65 data - This is passed to the usercallback getc and ungetc
66 getc - This function gets called when the routine wants to
67 read the next character. It whould return EOF when
68 no more characters are available.
69 ungetc - This function gets called when the routine wants to
70 put a read character back into the stream. The next
71 call to getc should return this character. It is possible
72 that this function is called more than once before the
73 next getc.
74 format - A scanf() format string.
75 args - A list of arguments in which the result of the scan should
76 be placed.
78 RESULT
79 The number of arguments converted.
81 NOTES
83 EXAMPLE
85 BUGS
87 SEE ALSO
89 INTERNALS
91 ******************************************************************************/
93 size_t blocks=0,incount=0;
94 int c=0;
96 while(*format)
98 size_t size=0;
100 if(*format=='%')
102 size_t width=ULONG_MAX;
103 char type,subtype='i',ignore=0;
104 const unsigned char *ptr=format+1;
105 size_t i;
107 if(isdigit(*ptr))
108 { width=0;
109 while(isdigit(*ptr))
110 width=width*10+(*ptr++-'0'); }
112 while(*ptr=='h'||*ptr=='l'||*ptr=='L'||*ptr=='*')
113 { if(*ptr=='*')
114 ignore=1;
115 else
116 subtype=*ptr;
117 ptr++;
120 type=*ptr++;
122 if(type&&type!='%'&&type!='c'&&type!='n'&&type!='[')
123 { do /* ignore leading whitespace characters */
124 NEXT(c);
125 while(isspace(c));
126 size=1; } /* The first non-whitespace character is already read */
128 switch(type)
129 { case 'c':
130 { unsigned char *bp;
132 if(width==ULONG_MAX) /* Default */
133 width=1;
135 if(!ignore)
136 bp=va_arg(args,char *);
137 else
138 bp=NULL; /* Just to get the compiler happy */
140 NEXT(c); /* 'c' did not skip whitespace */
141 while(VAL(c!=EOF))
142 { if(!ignore)
143 *bp++=c;
144 NEXT(c);
146 PREV(c);
148 if(!ignore&&size)
149 blocks++;
150 break;
152 case '[':
153 { unsigned char *bp;
154 unsigned char tab[32],a,b;
155 char circflag=0;
157 if(*ptr=='^')
158 { circflag=1;
159 ptr++; }
160 for(i=0;i<sizeof(tab);i++)
161 tab[i]=circflag?255:0;
162 for(;;)
163 { if(!*ptr)
164 break;
165 a=b=*ptr++;
166 if(*ptr=='-'&&ptr[1]&&ptr[1]!=']')
167 { ptr++;
168 b=*ptr++; }
169 for(i=a;i<=b;i++)
170 if(circflag)
171 tab[i/8]&=~(1<<(i&7));
172 else
173 tab[i/8]|=1<<(i&7);
174 if(*ptr==']')
175 { ptr++;
176 break; }
179 if(!ignore)
180 bp=va_arg(args,char *);
181 else
182 bp=NULL; /* Just to get the compiler happy */
184 NEXT(c);
185 while(VAL(c!=EOF&&tab[c/8]&(1<<(c&7))))
186 { if(!ignore)
187 *bp++=c;
188 NEXT(c);
190 PREV(c);
192 if(!ignore&&size)
193 { *bp++='\0';
194 blocks++; }
195 break;
197 case 's':
198 { unsigned char *bp;
200 if(!ignore)
201 bp=va_arg(args,char *);
202 else
203 bp=NULL; /* Just to get the compiler happy */
205 while(VAL(c!=EOF&&!isspace(c)))
206 { if(!ignore)
207 *bp++=c;
208 NEXT(c);
210 PREV(c);
212 if(!ignore&&size)
213 { *bp++='\0';
214 blocks++; }
215 break;
217 #ifdef FULL_SPECIFIERS
218 case 'e':
219 case 'f':
220 case 'g':
221 { double v;
222 int ex=0;
223 int min=0,mine=0; /* This is a workaround for gcc 2.3.3: should be char */
225 do /* This is there just to be able to break out */
227 if(VAL(c=='-'||c=='+'))
228 { min=c;
229 NEXT(c); }
231 if(VAL(tolower(c)=='i')) /* +- inf */
232 { int d;
233 NEXT(d);
234 if(VAL(tolower(d)=='n'))
235 { int e;
236 NEXT(e);
237 if(VAL(tolower(e)=='f'))
238 { v=*(double *)&undef[min=='-'];
239 break; } /* break out */
240 PREV(e);
242 PREV(d);
244 else if(VAL(toupper(c)=='N')) /* NaN */
245 { int d;
246 NEXT(d);
247 if(VAL(tolower(d)=='a'))
248 { int e;
249 NEXT(e);
250 if(VAL(toupper(e)=='N'))
251 { v=*(double *)&undef[2];
252 break; }
253 PREV(e);
255 PREV(d);
258 v=0.0;
259 while(VAL(isdigit(c)))
260 { v=v*10.0+(c-'0');
261 NEXT(c);
264 if(VAL(c==__decimalpoint[0]))
265 { double dp=0.1;
266 NEXT(c);
267 while(VAL(isdigit(c)))
268 { v=v+dp*(c-'0');
269 dp=dp/10.0;
270 NEXT(c); }
271 if(size==2+(min!=0)) /* No number read till now -> malformatted */
272 { PREV(c);
273 c=__decimalpoint[0]; }
276 if(min&&size==2) /* No number read till now -> malformatted */
277 { PREV(c);
278 c=min; }
279 if(size==1)
280 break;
282 if(VAL(tolower(c)=='e'))
283 { int d;
284 NEXT(d);
285 if(VAL(d=='-'||d=='+'))
286 { mine=d;
287 NEXT(d); }
289 if(VAL(isdigit(d)))
290 { do
291 { ex=ex*10+(d-'0');
292 NEXT(d);
293 }while(VAL(isdigit(d)&&ex<100));
294 c=d;
295 }else
296 { PREV(d);
297 if(mine)
298 PREV(mine);
301 PREV(c);
303 if(mine=='-')
304 v=v/pow(10.0,ex);
305 else
306 v=v*pow(10.0,ex);
308 if(min=='-')
309 v=-v;
311 }while(0);
313 if(!ignore&&size)
314 { switch(subtype)
315 { case 'l':
316 case 'L':
317 *va_arg(args,double *)=v;
318 break;
319 case 'i':
320 *va_arg(args,float *)=v;
321 break;
323 blocks++;
325 break;
327 #endif
328 case '%':
329 NEXT(c);
330 if(c!='%')
331 PREV(c); /* unget non-'%' character */
332 break;
333 case 'n':
334 if(!ignore)
335 *va_arg(args,int *)=incount;
336 size=1; /* fake a valid argument */
337 break;
338 default:
339 { unsigned long v=0;
340 int base;
341 int min=0;
343 if(!type)
344 ptr--; /* unparse NUL character */
346 if(type=='p')
347 { subtype='l'; /* This is the same as %lx */
348 type='x'; }
350 if(VAL((c=='-'&&type!='u')||c=='+'))
351 { min=c;
352 NEXT(c); }
354 if(type=='i') /* which one to use ? */
355 { if(VAL(c=='0')) /* Could be octal or sedecimal */
356 { int d;
357 NEXT(d); /* Get a look at next character */
358 if(VAL(tolower(d)=='x'))
359 { int e;
360 NEXT(e); /* And the next */
361 if(VAL(isxdigit(c)))
362 type='x'; /* Is a valid x number with '0x?' */
363 PREV(e);
364 }else
365 type='o';
366 PREV(d);
367 }else if(VAL(!isdigit(c)&&isxdigit(c)))
368 type='x'; /* Is a valid x number without '0x' */
371 while(type=='x'&&VAL(c=='0')) /* sedecimal */
372 { int d;
373 NEXT(d);
374 if(VAL(tolower(d)=='x'))
375 { int e;
376 NEXT(e);
377 if(VAL(isxdigit(e)))
378 { c=e;
379 break; } /* Used while just to do this ;-) */
380 PREV(e);
382 PREV(d);
383 break; /* Need no loop */
386 base=type=='x'||type=='X'?16:(type=='o'?8:10);
387 while(VAL(isxdigit(c)&&(base!=10||isdigit(c))&&(base!=8||c<='7')))
388 { v=v*base+(isdigit(c)?c-'0':0)+(isupper(c)?c-'A'+10:0)+(islower(c)?c-'a'+10:0);
389 NEXT(c);
392 if(min&&size==2) /* If there is no valid character after sign, unget last */
393 { PREV(c);
394 c=min; }
396 PREV(c);
398 if(ignore||!size)
399 break;
401 if(type=='u')
402 switch(subtype)
403 { case 'l':
404 case 'L':
405 *va_arg(args,unsigned long *)=v;
406 break;
407 case 'i':
408 *va_arg(args,unsigned int *)=v;
409 break;
410 case 'h':
411 *va_arg(args,unsigned short *)=v;
412 break;
414 else
415 { signed long v2;
416 if(min=='-')
417 v2=-v;
418 else
419 v2=v;
420 switch(subtype)
421 { case 'l':
422 case 'L':
423 *va_arg(args,signed long *)=v2;
424 break;
425 case 'i':
426 *va_arg(args,signed int *)=v2;
427 break;
428 case 'h':
429 *va_arg(args,signed short *)=v2;
430 break;
433 blocks++;
434 break;
437 format=ptr;
438 }else
439 { if(isspace(*format))
440 { do
441 NEXT(c);
442 while(isspace(c));
443 PREV(c);
444 size=1; }
445 else
446 { NEXT(c);
447 if(c!=*format)
448 PREV(c); }
449 format++;
451 if(!size)
452 break;
455 if(c==EOF&&!blocks)
456 return c;
457 else
458 return blocks;