Fix: 64-bit compatibility
[AROS.git] / compiler / stdc / __vcscan.c
blob83497cbc22560a57325ddbd6f287c8b437367fd1
1 /*
2 Copyright © 1995-2013, 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 STDC_STATIC
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 *__stdc_char_decimalpoint;
34 struct vcs_ieeetype
36 union
38 double doub;
39 unsigned char uchar[sizeof(double)];
43 #ifdef FULL_SPECIFIERS
44 const static struct vcs_ieeetype undef[3] = /* Undefined numeric values, IEEE */
46 { .uchar = { 0x7f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00 }}, /* +inf */
47 { .uchar = { 0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00 }}, /* -inf */
48 { .uchar = { 0x7f,0xf1,0x00,0x00,0x00,0x00,0x00,0x00 }} /* NaN */
50 #endif
53 /*****************************************************************************
55 NAME */
57 int __vcscan (
59 /* SYNOPSIS */
60 void * data,
61 int (* get_char)(void *),
62 int (* unget_char)(int,void *),
63 const char * format,
64 va_list args)
66 /* FUNCTION
67 Scan an input stream as specified in format. The result of
68 the scan will be placed in args.
70 INPUTS
71 data - This is passed to the usercallback getc and ungetc
72 get_char - This function gets called when the routine wants to
73 read the next character. It whould return EOF when
74 no more characters are available.
75 unget_char - This function gets called when the routine wants to
76 put a read character back into the stream. The next
77 call to get_char() should return this character. It is possible
78 that this function is called more than once before the
79 next get_char().
80 format - A scanf() format string.
81 args - A list of arguments in which the result of the scan should
82 be placed.
84 RESULT
85 The number of arguments converted.
87 NOTES
89 EXAMPLE
91 BUGS
93 SEE ALSO
95 INTERNALS
97 ******************************************************************************/
99 size_t blocks=0,incount=0;
100 int c=0;
102 while(*format)
104 size_t size=0;
106 if(*format=='%')
108 size_t width=ULONG_MAX;
109 char type,subtype='i',ignore=0;
110 const unsigned char *ptr=format+1;
111 size_t i;
113 if(isdigit(*ptr))
115 width=0;
116 while(isdigit(*ptr))
117 width=width*10+(*ptr++-'0');
120 while(*ptr=='h'||*ptr=='l'||*ptr=='L'||*ptr=='*')
122 if(*ptr=='*')
123 ignore=1;
124 else
125 subtype=*ptr;
126 ptr++;
129 type=*ptr++;
131 if(type&&type!='%'&&type!='c'&&type!='n'&&type!='[')
133 do /* ignore leading whitespace characters */
134 NEXT(c);
135 while(isspace(c));
136 size=1;
137 } /* The first non-whitespace character is already read */
139 switch(type)
141 case 'c':
143 unsigned char *bp;
145 if(width==ULONG_MAX) /* Default */
146 width=1;
148 if(!ignore)
149 bp=va_arg(args,char *);
150 else
151 bp=NULL; /* Just to get the compiler happy */
153 NEXT(c); /* 'c' did not skip whitespace */
154 while(VAL(c!=EOF))
156 if(!ignore)
157 *bp++=c;
158 NEXT(c);
160 PREV(c);
162 if(!ignore&&size)
163 blocks++;
164 break;
166 case '[':
168 unsigned char *bp;
169 unsigned char tab[32],a,b;
170 char circflag=0;
172 if(*ptr=='^')
174 circflag=1;
175 ptr++;
177 for(i=0;i<sizeof(tab);i++)
178 tab[i]=circflag?255:0;
180 for(;;)
182 if(!*ptr)
183 break;
184 a=b=*ptr++;
185 if(*ptr=='-'&&ptr[1]&&ptr[1]!=']')
187 ptr++;
188 b=*ptr++;
190 for(i=a;i<=b;i++)
191 if(circflag)
192 tab[i/8]&=~(1<<(i&7));
193 else
194 tab[i/8]|=1<<(i&7);
195 if(*ptr==']')
197 ptr++;
198 break;
202 if(!ignore)
203 bp=va_arg(args,char *);
204 else
205 bp=NULL; /* Just to get the compiler happy */
207 NEXT(c);
208 while(VAL(c!=EOF&&tab[c/8]&(1<<(c&7))))
210 if(!ignore)
211 *bp++=c;
212 NEXT(c);
214 PREV(c);
216 if(!ignore&&size)
218 *bp++='\0';
219 blocks++;
221 break;
223 case 's':
225 unsigned char *bp;
227 if(!ignore)
228 bp=va_arg(args,char *);
229 else
230 bp=NULL; /* Just to get the compiler happy */
232 while(VAL(c!=EOF&&!isspace(c)))
234 if(!ignore)
235 *bp++=c;
236 NEXT(c);
238 PREV(c);
240 if(!ignore&&size)
242 *bp++='\0';
243 blocks++;
245 break;
247 #ifdef FULL_SPECIFIERS
248 case 'e':
249 case 'f':
250 case 'g':
252 double v;
253 int ex=0;
254 int min=0,mine=0; /* This is a workaround for gcc 2.3.3: should be char */
256 do /* This is there just to be able to break out */
258 if(VAL(c=='-'||c=='+'))
260 min=c;
261 NEXT(c);
264 if(VAL(tolower(c)=='i')) /* +- inf */
266 int d;
267 NEXT(d);
268 if(VAL(tolower(d)=='n'))
270 int e;
271 NEXT(e);
272 if(VAL(tolower(e)=='f'))
274 v=*(double *)&undef[min=='-'];
275 break;
276 } /* break out */
277 PREV(e);
279 PREV(d);
281 else if(VAL(toupper(c)=='N')) /* NaN */
283 int d;
284 NEXT(d);
285 if(VAL(tolower(d)=='a'))
287 int e;
288 NEXT(e);
289 if(VAL(toupper(e)=='N'))
291 v=*(double *)&undef[2];
292 break;
294 PREV(e);
296 PREV(d);
299 v=0.0;
300 while(VAL(isdigit(c)))
302 v=v*10.0+(c-'0');
303 NEXT(c);
306 if(VAL(c==__stdc_char_decimalpoint[0]))
308 double dp=0.1;
309 NEXT(c);
310 while(VAL(isdigit(c)))
312 v=v+dp*(c-'0');
313 dp=dp/10.0;
314 NEXT(c);
316 if(size==2+(min!=0)) /* No number read till now -> malformatted */
318 PREV(c);
319 c=__stdc_char_decimalpoint[0];
323 if(min&&size==2) /* No number read till now -> malformatted */
325 PREV(c);
326 c=min;
328 if(size==1) {
329 PREV(c);
330 break;
333 if(VAL(tolower(c)=='e'))
335 int d;
336 NEXT(d);
337 if(VAL(d=='-'||d=='+'))
339 mine=d;
340 NEXT(d);
343 if(VAL(isdigit(d)))
347 ex=ex*10+(d-'0');
348 NEXT(d);
349 }while(VAL(isdigit(d)&&ex<100));
350 c=d;
352 else
354 PREV(d);
355 if(mine)
356 PREV(mine);
359 PREV(c);
361 if(mine=='-')
362 v=v/pow(10.0,ex);
363 else
364 v=v*pow(10.0,ex);
366 if(min=='-')
367 v=-v;
369 }while(0);
371 if(!ignore&&size)
373 switch(subtype)
375 case 'l':
376 case 'L':
377 *va_arg(args,double *)=v;
378 break;
379 case 'i':
380 *va_arg(args,float *)=v;
381 break;
383 blocks++;
385 break;
387 #endif
388 case '%':
389 NEXT(c);
390 if(c!='%')
391 PREV(c); /* unget non-'%' character */
392 break;
393 case 'n':
394 if(!ignore)
395 *va_arg(args,int *)=incount;
396 size=1; /* fake a valid argument */
397 break;
398 default:
400 unsigned long v=0;
401 int base;
402 int min=0;
404 if(!type)
405 ptr--; /* unparse NUL character */
407 if(type=='p')
409 subtype='l'; /* This is the same as %lx */
410 type='x';
413 if(VAL((c=='-'&&type!='u')||c=='+'))
415 min=c;
416 NEXT(c);
419 if(type=='i') /* which one to use ? */
421 if(VAL(c=='0')) /* Could be octal or sedecimal */
423 int d;
424 NEXT(d); /* Get a look at next character */
425 if(VAL(tolower(d)=='x'))
427 int e;
428 NEXT(e); /* And the next */
429 if(VAL(isxdigit(c)))
430 type='x'; /* Is a valid x number with '0x?' */
431 PREV(e);
433 else
434 type='o';
435 PREV(d);
437 /* deadwood: Below code is wrong and left for reference.
438 Algoritm cannot assume that string starting with A-F is
439 a hexadecimal integer. There must be '0x' sequence -
440 validated via test/clib/sscanf.c */
441 //else
442 // if(VAL(!isdigit(c)&&isxdigit(c)))
443 // type='x'; /* Is a valid x number without '0x' */
446 while(type=='x'&&VAL(c=='0')) /* sedecimal */
448 int d;
449 NEXT(d);
450 if(VAL(tolower(d)=='x'))
452 int e;
453 NEXT(e);
454 if(VAL(isxdigit(e)))
456 c=e;
457 break;
458 } /* Used while just to do this ;-) */
459 PREV(e);
461 PREV(d);
462 break; /* Need no loop */
465 base=type=='x'||type=='X'?16:(type=='o'?8:10);
466 while(VAL(isxdigit(c)&&(base!=10||isdigit(c))&&(base!=8||c<='7')))
468 v=v*base+(isdigit(c)?c-'0':0)+(isupper(c)?c-'A'+10:0)+(islower(c)?c-'a'+10:0);
469 NEXT(c);
472 if(min&&size==2) /* If there is no valid character after sign, unget last */
474 PREV(c);
475 c=min;
478 PREV(c);
480 if(ignore||!size)
481 break;
483 if(type=='u')
485 switch(subtype)
487 case 'l':
488 case 'L':
489 *va_arg(args,unsigned long *)=v;
490 break;
491 case 'i':
492 *va_arg(args,unsigned int *)=v;
493 break;
494 case 'h':
495 *va_arg(args,unsigned short *)=v;
496 break;
499 else
501 signed long v2;
502 if(min=='-')
503 v2=-v;
504 else
505 v2=v;
506 switch(subtype)
508 case 'l':
509 case 'L':
510 *va_arg(args,signed long *)=v2;
511 break;
512 case 'i':
513 *va_arg(args,signed int *)=v2;
514 break;
515 case 'h':
516 *va_arg(args,signed short *)=v2;
517 break;
520 blocks++;
521 break;
524 format=ptr;
526 else
528 if(isspace(*format))
531 NEXT(c);
532 while(isspace(c));
533 PREV(c);
534 size=1;
536 else
538 NEXT(c);
539 if(c!=*format)
540 PREV(c);
543 format++;
545 if(!size)
546 break;
549 if(c==EOF&&!blocks)
550 return c;
551 else
552 return blocks;