update mappings to reflect recent changes
[AROS.git] / compiler / clib / __vcscan.c
blob396bfab8051d79a2ad840b8732d9dd67ef258d2b
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 #define FULL_SPECIFIERS
21 /* some macros to cut this short
22 * NEXT(c); read next character
23 * PREV(c); ungetc a character
24 * VAL(a) leads to 1 if a is true and valid
26 #define NEXT(c) ((c)=(*get_char)(data),size++,incount++)
27 #define PREV(c) do{if((c)!=EOF)(*unget_char)((c),data);size--;incount--;}while(0)
28 #define VAL(a) ((a)&&size<=width)
30 extern unsigned char *__decimalpoint;
32 #ifdef FULL_SPECIFIERS
33 const static unsigned char undef[3][sizeof(double)]= /* Undefined numeric values, IEEE */
35 { 0x7f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00 }, /* +inf */
36 { 0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00 }, /* -inf */
37 { 0x7f,0xf1,0x00,0x00,0x00,0x00,0x00,0x00 } /* NaN */
39 #endif
42 /*****************************************************************************
44 NAME */
46 int __vcscan (
48 /* SYNOPSIS */
49 void * data,
50 int (* get_char)(void *),
51 int (* unget_char)(int,void *),
52 const char * format,
53 va_list args)
55 /* FUNCTION
56 Scan an input stream as specified in format. The result of
57 the scan will be placed in args.
59 INPUTS
60 data - This is passed to the usercallback getc and ungetc
61 get_char - This function gets called when the routine wants to
62 read the next character. It whould return EOF when
63 no more characters are available.
64 unget_char - This function gets called when the routine wants to
65 put a read character back into the stream. The next
66 call to get_char() should return this character. It is possible
67 that this function is called more than once before the
68 next get_char().
69 format - A scanf() format string.
70 args - A list of arguments in which the result of the scan should
71 be placed.
73 RESULT
74 The number of arguments converted.
76 NOTES
78 EXAMPLE
80 BUGS
82 SEE ALSO
84 INTERNALS
86 ******************************************************************************/
88 size_t blocks=0,incount=0;
89 int c=0;
91 while(*format)
93 size_t size=0;
95 if(*format=='%')
97 size_t width=ULONG_MAX;
98 char type,subtype='i',ignore=0;
99 const unsigned char *ptr=format+1;
100 size_t i;
102 if(isdigit(*ptr))
104 width=0;
105 while(isdigit(*ptr))
106 width=width*10+(*ptr++-'0');
109 while(*ptr=='h'||*ptr=='l'||*ptr=='L'||*ptr=='*')
111 if(*ptr=='*')
112 ignore=1;
113 else
114 subtype=*ptr;
115 ptr++;
118 type=*ptr++;
120 if(type&&type!='%'&&type!='c'&&type!='n'&&type!='[')
122 do /* ignore leading whitespace characters */
123 NEXT(c);
124 while(isspace(c));
125 size=1;
126 } /* The first non-whitespace character is already read */
128 switch(type)
130 case 'c':
132 unsigned char *bp;
134 if(width==ULONG_MAX) /* Default */
135 width=1;
137 if(!ignore)
138 bp=va_arg(args,char *);
139 else
140 bp=NULL; /* Just to get the compiler happy */
142 NEXT(c); /* 'c' did not skip whitespace */
143 while(VAL(c!=EOF))
145 if(!ignore)
146 *bp++=c;
147 NEXT(c);
149 PREV(c);
151 if(!ignore&&size)
152 blocks++;
153 break;
155 case '[':
157 unsigned char *bp;
158 unsigned char tab[32],a,b;
159 char circflag=0;
161 if(*ptr=='^')
163 circflag=1;
164 ptr++;
166 for(i=0;i<sizeof(tab);i++)
167 tab[i]=circflag?255:0;
169 for(;;)
171 if(!*ptr)
172 break;
173 a=b=*ptr++;
174 if(*ptr=='-'&&ptr[1]&&ptr[1]!=']')
176 ptr++;
177 b=*ptr++;
179 for(i=a;i<=b;i++)
180 if(circflag)
181 tab[i/8]&=~(1<<(i&7));
182 else
183 tab[i/8]|=1<<(i&7);
184 if(*ptr==']')
186 ptr++;
187 break;
191 if(!ignore)
192 bp=va_arg(args,char *);
193 else
194 bp=NULL; /* Just to get the compiler happy */
196 NEXT(c);
197 while(VAL(c!=EOF&&tab[c/8]&(1<<(c&7))))
199 if(!ignore)
200 *bp++=c;
201 NEXT(c);
203 PREV(c);
205 if(!ignore&&size)
207 *bp++='\0';
208 blocks++;
210 break;
212 case 's':
214 unsigned char *bp;
216 if(!ignore)
217 bp=va_arg(args,char *);
218 else
219 bp=NULL; /* Just to get the compiler happy */
221 while(VAL(c!=EOF&&!isspace(c)))
223 if(!ignore)
224 *bp++=c;
225 NEXT(c);
227 PREV(c);
229 if(!ignore&&size)
231 *bp++='\0';
232 blocks++;
234 break;
236 #ifdef FULL_SPECIFIERS
237 case 'e':
238 case 'f':
239 case 'g':
241 double v;
242 int ex=0;
243 int min=0,mine=0; /* This is a workaround for gcc 2.3.3: should be char */
245 do /* This is there just to be able to break out */
247 if(VAL(c=='-'||c=='+'))
249 min=c;
250 NEXT(c);
253 if(VAL(tolower(c)=='i')) /* +- inf */
255 int d;
256 NEXT(d);
257 if(VAL(tolower(d)=='n'))
259 int e;
260 NEXT(e);
261 if(VAL(tolower(e)=='f'))
263 v=*(double *)&undef[min=='-'];
264 break;
265 } /* break out */
266 PREV(e);
268 PREV(d);
270 else if(VAL(toupper(c)=='N')) /* NaN */
272 int d;
273 NEXT(d);
274 if(VAL(tolower(d)=='a'))
276 int e;
277 NEXT(e);
278 if(VAL(toupper(e)=='N'))
280 v=*(double *)&undef[2];
281 break;
283 PREV(e);
285 PREV(d);
288 v=0.0;
289 while(VAL(isdigit(c)))
291 v=v*10.0+(c-'0');
292 NEXT(c);
295 if(VAL(c==__decimalpoint[0]))
297 double dp=0.1;
298 NEXT(c);
299 while(VAL(isdigit(c)))
301 v=v+dp*(c-'0');
302 dp=dp/10.0;
303 NEXT(c);
305 if(size==2+(min!=0)) /* No number read till now -> malformatted */
307 PREV(c);
308 c=__decimalpoint[0];
312 if(min&&size==2) /* No number read till now -> malformatted */
314 PREV(c);
315 c=min;
317 if(size==1) {
318 PREV(c);
319 break;
322 if(VAL(tolower(c)=='e'))
324 int d;
325 NEXT(d);
326 if(VAL(d=='-'||d=='+'))
328 mine=d;
329 NEXT(d);
332 if(VAL(isdigit(d)))
336 ex=ex*10+(d-'0');
337 NEXT(d);
338 }while(VAL(isdigit(d)&&ex<100));
339 c=d;
341 else
343 PREV(d);
344 if(mine)
345 PREV(mine);
348 PREV(c);
350 if(mine=='-')
351 v=v/pow(10.0,ex);
352 else
353 v=v*pow(10.0,ex);
355 if(min=='-')
356 v=-v;
358 }while(0);
360 if(!ignore&&size)
362 switch(subtype)
364 case 'l':
365 case 'L':
366 *va_arg(args,double *)=v;
367 break;
368 case 'i':
369 *va_arg(args,float *)=v;
370 break;
372 blocks++;
374 break;
376 #endif
377 case '%':
378 NEXT(c);
379 if(c!='%')
380 PREV(c); /* unget non-'%' character */
381 break;
382 case 'n':
383 if(!ignore)
384 *va_arg(args,int *)=incount;
385 size=1; /* fake a valid argument */
386 break;
387 default:
389 unsigned long v=0;
390 int base;
391 int min=0;
393 if(!type)
394 ptr--; /* unparse NUL character */
396 if(type=='p')
398 subtype='l'; /* This is the same as %lx */
399 type='x';
402 if(VAL((c=='-'&&type!='u')||c=='+'))
404 min=c;
405 NEXT(c);
408 if(type=='i') /* which one to use ? */
410 if(VAL(c=='0')) /* Could be octal or sedecimal */
412 int d;
413 NEXT(d); /* Get a look at next character */
414 if(VAL(tolower(d)=='x'))
416 int e;
417 NEXT(e); /* And the next */
418 if(VAL(isxdigit(c)))
419 type='x'; /* Is a valid x number with '0x?' */
420 PREV(e);
422 else
423 type='o';
424 PREV(d);
426 /* deadwood: Below code is wrong and left for reference.
427 Algoritm cannot assume that string starting with A-F is
428 a hexadecimal integer. There must be '0x' sequence -
429 validated via test/clib/sscanf.c */
430 //else
431 // if(VAL(!isdigit(c)&&isxdigit(c)))
432 // type='x'; /* Is a valid x number without '0x' */
435 while(type=='x'&&VAL(c=='0')) /* sedecimal */
437 int d;
438 NEXT(d);
439 if(VAL(tolower(d)=='x'))
441 int e;
442 NEXT(e);
443 if(VAL(isxdigit(e)))
445 c=e;
446 break;
447 } /* Used while just to do this ;-) */
448 PREV(e);
450 PREV(d);
451 break; /* Need no loop */
454 base=type=='x'||type=='X'?16:(type=='o'?8:10);
455 while(VAL(isxdigit(c)&&(base!=10||isdigit(c))&&(base!=8||c<='7')))
457 v=v*base+(isdigit(c)?c-'0':0)+(isupper(c)?c-'A'+10:0)+(islower(c)?c-'a'+10:0);
458 NEXT(c);
461 if(min&&size==2) /* If there is no valid character after sign, unget last */
463 PREV(c);
464 c=min;
467 PREV(c);
469 if(ignore||!size)
470 break;
472 if(type=='u')
474 switch(subtype)
476 case 'l':
477 case 'L':
478 *va_arg(args,unsigned long *)=v;
479 break;
480 case 'i':
481 *va_arg(args,unsigned int *)=v;
482 break;
483 case 'h':
484 *va_arg(args,unsigned short *)=v;
485 break;
488 else
490 signed long v2;
491 if(min=='-')
492 v2=-v;
493 else
494 v2=v;
495 switch(subtype)
497 case 'l':
498 case 'L':
499 *va_arg(args,signed long *)=v2;
500 break;
501 case 'i':
502 *va_arg(args,signed int *)=v2;
503 break;
504 case 'h':
505 *va_arg(args,signed short *)=v2;
506 break;
509 blocks++;
510 break;
513 format=ptr;
515 else
517 if(isspace(*format))
520 NEXT(c);
521 while(isspace(c));
522 PREV(c);
523 size=1;
525 else
527 NEXT(c);
528 if(c!=*format)
529 PREV(c);
532 format++;
534 if(!size)
535 break;
538 if(c==EOF&&!blocks)
539 return c;
540 else
541 return blocks;