arosc.library: Fix bug in __vcscan where junk characters were accepted as floats
[AROS.git] / compiler / clib / __vcscan.c
blob233eb9e1fa1f137f666728f02da6d14a4a6d9fa9
1 /*
2 Copyright © 1995-2012, The AROS Development Team. All rights reserved.
3 $Id$
5 Function to scan a string like scanf().
6 */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stdarg.h>
11 #ifndef AROS_NO_LIMITS_H
12 # include <limits.h>
13 #else
14 # define ULONG_MAX 4294967295UL
15 #endif
16 #include <ctype.h>
17 #include <math.h>
19 #ifndef AROSC_ROM
20 #define FULL_SPECIFIERS
21 #endif
23 /* some macros to cut this short
24 * NEXT(c); read next character
25 * PREV(c); ungetc a character
26 * VAL(a) leads to 1 if a is true and valid
28 #define NEXT(c) ((c)=(*get_char)(data),size++,incount++)
29 #define PREV(c) do{if((c)!=EOF)(*unget_char)((c),data);size--;incount--;}while(0)
30 #define VAL(a) ((a)&&size<=width)
32 extern unsigned char *__decimalpoint;
34 #ifdef FULL_SPECIFIERS
35 const static unsigned char undef[3][sizeof(double)]= /* Undefined numeric values, IEEE */
37 { 0x7f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00 }, /* +inf */
38 { 0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00 }, /* -inf */
39 { 0x7f,0xf1,0x00,0x00,0x00,0x00,0x00,0x00 } /* NaN */
41 #endif
44 /*****************************************************************************
46 NAME */
48 int __vcscan (
50 /* SYNOPSIS */
51 void * data,
52 int (* get_char)(void *),
53 int (* unget_char)(int,void *),
54 const char * format,
55 va_list args)
57 /* FUNCTION
58 Scan an input stream as specified in format. The result of
59 the scan will be placed in args.
61 INPUTS
62 data - This is passed to the usercallback getc and ungetc
63 get_char - This function gets called when the routine wants to
64 read the next character. It whould return EOF when
65 no more characters are available.
66 unget_char - This function gets called when the routine wants to
67 put a read character back into the stream. The next
68 call to get_char() should return this character. It is possible
69 that this function is called more than once before the
70 next get_char().
71 format - A scanf() format string.
72 args - A list of arguments in which the result of the scan should
73 be placed.
75 RESULT
76 The number of arguments converted.
78 NOTES
80 EXAMPLE
82 BUGS
84 SEE ALSO
86 INTERNALS
88 ******************************************************************************/
90 size_t blocks=0,incount=0;
91 int c=0;
93 while(*format)
95 size_t size=0;
97 if(*format=='%')
99 size_t width=ULONG_MAX;
100 char type,subtype='i',ignore=0;
101 const unsigned char *ptr=format+1;
102 size_t i;
104 if(isdigit(*ptr))
106 width=0;
107 while(isdigit(*ptr))
108 width=width*10+(*ptr++-'0');
111 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!='[')
124 do /* ignore leading whitespace characters */
125 NEXT(c);
126 while(isspace(c));
127 size=1;
128 } /* The first non-whitespace character is already read */
130 switch(type)
132 case 'c':
134 unsigned char *bp;
136 if(width==ULONG_MAX) /* Default */
137 width=1;
139 if(!ignore)
140 bp=va_arg(args,char *);
141 else
142 bp=NULL; /* Just to get the compiler happy */
144 NEXT(c); /* 'c' did not skip whitespace */
145 while(VAL(c!=EOF))
147 if(!ignore)
148 *bp++=c;
149 NEXT(c);
151 PREV(c);
153 if(!ignore&&size)
154 blocks++;
155 break;
157 case '[':
159 unsigned char *bp;
160 unsigned char tab[32],a,b;
161 char circflag=0;
163 if(*ptr=='^')
165 circflag=1;
166 ptr++;
168 for(i=0;i<sizeof(tab);i++)
169 tab[i]=circflag?255:0;
171 for(;;)
173 if(!*ptr)
174 break;
175 a=b=*ptr++;
176 if(*ptr=='-'&&ptr[1]&&ptr[1]!=']')
178 ptr++;
179 b=*ptr++;
181 for(i=a;i<=b;i++)
182 if(circflag)
183 tab[i/8]&=~(1<<(i&7));
184 else
185 tab[i/8]|=1<<(i&7);
186 if(*ptr==']')
188 ptr++;
189 break;
193 if(!ignore)
194 bp=va_arg(args,char *);
195 else
196 bp=NULL; /* Just to get the compiler happy */
198 NEXT(c);
199 while(VAL(c!=EOF&&tab[c/8]&(1<<(c&7))))
201 if(!ignore)
202 *bp++=c;
203 NEXT(c);
205 PREV(c);
207 if(!ignore&&size)
209 *bp++='\0';
210 blocks++;
212 break;
214 case 's':
216 unsigned char *bp;
218 if(!ignore)
219 bp=va_arg(args,char *);
220 else
221 bp=NULL; /* Just to get the compiler happy */
223 while(VAL(c!=EOF&&!isspace(c)))
225 if(!ignore)
226 *bp++=c;
227 NEXT(c);
229 PREV(c);
231 if(!ignore&&size)
233 *bp++='\0';
234 blocks++;
236 break;
238 #ifdef FULL_SPECIFIERS
239 case 'e':
240 case 'f':
241 case 'g':
243 double v;
244 int ex=0;
245 int min=0,mine=0; /* This is a workaround for gcc 2.3.3: should be char */
247 do /* This is there just to be able to break out */
249 if(VAL(c=='-'||c=='+'))
251 min=c;
252 NEXT(c);
255 if(VAL(tolower(c)=='i')) /* +- inf */
257 int d;
258 NEXT(d);
259 if(VAL(tolower(d)=='n'))
261 int e;
262 NEXT(e);
263 if(VAL(tolower(e)=='f'))
265 v=*(double *)&undef[min=='-'];
266 break;
267 } /* break out */
268 PREV(e);
270 PREV(d);
272 else if(VAL(toupper(c)=='N')) /* NaN */
274 int d;
275 NEXT(d);
276 if(VAL(tolower(d)=='a'))
278 int e;
279 NEXT(e);
280 if(VAL(toupper(e)=='N'))
282 v=*(double *)&undef[2];
283 break;
285 PREV(e);
287 PREV(d);
290 v=0.0;
291 while(VAL(isdigit(c)))
293 v=v*10.0+(c-'0');
294 NEXT(c);
297 if(VAL(c==__decimalpoint[0]))
299 double dp=0.1;
300 NEXT(c);
301 while(VAL(isdigit(c)))
303 v=v+dp*(c-'0');
304 dp=dp/10.0;
305 NEXT(c);
307 if(size==2+(min!=0)) /* No number read till now -> malformatted */
309 PREV(c);
310 c=__decimalpoint[0];
314 if(min&&size==2) /* No number read till now -> malformatted */
316 PREV(c);
317 c=min;
319 if(size==1) {
320 PREV(c);
321 break;
324 if(VAL(tolower(c)=='e'))
326 int d;
327 NEXT(d);
328 if(VAL(d=='-'||d=='+'))
330 mine=d;
331 NEXT(d);
334 if(VAL(isdigit(d)))
338 ex=ex*10+(d-'0');
339 NEXT(d);
340 }while(VAL(isdigit(d)&&ex<100));
341 c=d;
343 else
345 PREV(d);
346 if(mine)
347 PREV(mine);
350 PREV(c);
352 if(mine=='-')
353 v=v/pow(10.0,ex);
354 else
355 v=v*pow(10.0,ex);
357 if(min=='-')
358 v=-v;
360 }while(0);
362 if(!ignore&&size)
364 switch(subtype)
366 case 'l':
367 case 'L':
368 *va_arg(args,double *)=v;
369 break;
370 case 'i':
371 *va_arg(args,float *)=v;
372 break;
374 blocks++;
376 break;
378 #endif
379 case '%':
380 NEXT(c);
381 if(c!='%')
382 PREV(c); /* unget non-'%' character */
383 break;
384 case 'n':
385 if(!ignore)
386 *va_arg(args,int *)=incount;
387 size=1; /* fake a valid argument */
388 break;
389 default:
391 unsigned long v=0;
392 int base;
393 int min=0;
395 if(!type)
396 ptr--; /* unparse NUL character */
398 if(type=='p')
400 subtype='l'; /* This is the same as %lx */
401 type='x';
404 if(VAL((c=='-'&&type!='u')||c=='+'))
406 min=c;
407 NEXT(c);
410 if(type=='i') /* which one to use ? */
412 if(VAL(c=='0')) /* Could be octal or sedecimal */
414 int d;
415 NEXT(d); /* Get a look at next character */
416 if(VAL(tolower(d)=='x'))
418 int e;
419 NEXT(e); /* And the next */
420 if(VAL(isxdigit(c)))
421 type='x'; /* Is a valid x number with '0x?' */
422 PREV(e);
424 else
425 type='o';
426 PREV(d);
428 /* deadwood: Below code is wrong and left for reference.
429 Algoritm cannot assume that string starting with A-F is
430 a hexadecimal integer. There must be '0x' sequence -
431 validated via test/clib/sscanf.c */
432 //else
433 // if(VAL(!isdigit(c)&&isxdigit(c)))
434 // type='x'; /* Is a valid x number without '0x' */
437 while(type=='x'&&VAL(c=='0')) /* sedecimal */
439 int d;
440 NEXT(d);
441 if(VAL(tolower(d)=='x'))
443 int e;
444 NEXT(e);
445 if(VAL(isxdigit(e)))
447 c=e;
448 break;
449 } /* Used while just to do this ;-) */
450 PREV(e);
452 PREV(d);
453 break; /* Need no loop */
456 base=type=='x'||type=='X'?16:(type=='o'?8:10);
457 while(VAL(isxdigit(c)&&(base!=10||isdigit(c))&&(base!=8||c<='7')))
459 v=v*base+(isdigit(c)?c-'0':0)+(isupper(c)?c-'A'+10:0)+(islower(c)?c-'a'+10:0);
460 NEXT(c);
463 if(min&&size==2) /* If there is no valid character after sign, unget last */
465 PREV(c);
466 c=min;
469 PREV(c);
471 if(ignore||!size)
472 break;
474 if(type=='u')
476 switch(subtype)
478 case 'l':
479 case 'L':
480 *va_arg(args,unsigned long *)=v;
481 break;
482 case 'i':
483 *va_arg(args,unsigned int *)=v;
484 break;
485 case 'h':
486 *va_arg(args,unsigned short *)=v;
487 break;
490 else
492 signed long v2;
493 if(min=='-')
494 v2=-v;
495 else
496 v2=v;
497 switch(subtype)
499 case 'l':
500 case 'L':
501 *va_arg(args,signed long *)=v2;
502 break;
503 case 'i':
504 *va_arg(args,signed int *)=v2;
505 break;
506 case 'h':
507 *va_arg(args,signed short *)=v2;
508 break;
511 blocks++;
512 break;
515 format=ptr;
517 else
519 if(isspace(*format))
522 NEXT(c);
523 while(isspace(c));
524 PREV(c);
525 size=1;
527 else
529 NEXT(c);
530 if(c!=*format)
531 PREV(c);
534 format++;
536 if(!size)
537 break;
540 if(c==EOF&&!blocks)
541 return c;
542 else
543 return blocks;