define __KERNEL_STRICT_NAMES to avoid inclusion of kernel types on systems that carry...
[cake.git] / compiler / clib / __vcscan.c
blobf6a351e1e7126fa1d699a4ac422e83af9e2b2355
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 /* 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 #ifndef AROS_NOFPU
27 # define FULL_SPECIFIERS
28 #endif
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 blocks++;
338 break;
339 default:
340 { unsigned long v=0;
341 int base;
342 int min=0;
344 if(!type)
345 ptr--; /* unparse NUL character */
347 if(type=='p')
348 { subtype='l'; /* This is the same as %lx */
349 type='x'; }
351 if(VAL((c=='-'&&type!='u')||c=='+'))
352 { min=c;
353 NEXT(c); }
355 if(type=='i') /* which one to use ? */
356 { if(VAL(c=='0')) /* Could be octal or sedecimal */
357 { int d;
358 NEXT(d); /* Get a look at next character */
359 if(VAL(tolower(d)=='x'))
360 { int e;
361 NEXT(e); /* And the next */
362 if(VAL(isxdigit(c)))
363 type='x'; /* Is a valid x number with '0x?' */
364 PREV(e);
365 }else
366 type='o';
367 PREV(d);
368 }else if(VAL(!isdigit(c)&&isxdigit(c)))
369 type='x'; /* Is a valid x number without '0x' */
372 while(type=='x'&&VAL(c=='0')) /* sedecimal */
373 { int d;
374 NEXT(d);
375 if(VAL(tolower(d)=='x'))
376 { int e;
377 NEXT(e);
378 if(VAL(isxdigit(e)))
379 { c=e;
380 break; } /* Used while just to do this ;-) */
381 PREV(e);
383 PREV(d);
384 break; /* Need no loop */
387 base=type=='x'||type=='X'?16:(type=='o'?8:10);
388 while(VAL(isxdigit(c)&&(base!=10||isdigit(c))&&(base!=8||c<='7')))
389 { v=v*base+(isdigit(c)?c-'0':0)+(isupper(c)?c-'A'+10:0)+(islower(c)?c-'a'+10:0);
390 NEXT(c);
393 if(min&&size==2) /* If there is no valid character after sign, unget last */
394 { PREV(c);
395 c=min; }
397 PREV(c);
399 if(ignore||!size)
400 break;
402 if(type=='u')
403 switch(subtype)
404 { case 'l':
405 case 'L':
406 *va_arg(args,unsigned long *)=v;
407 break;
408 case 'i':
409 *va_arg(args,unsigned int *)=v;
410 break;
411 case 'h':
412 *va_arg(args,unsigned short *)=v;
413 break;
415 else
416 { signed long v2;
417 if(min=='-')
418 v2=-v;
419 else
420 v2=v;
421 switch(subtype)
422 { case 'l':
423 case 'L':
424 *va_arg(args,signed long *)=v2;
425 break;
426 case 'i':
427 *va_arg(args,signed int *)=v2;
428 break;
429 case 'h':
430 *va_arg(args,signed short *)=v2;
431 break;
434 blocks++;
435 break;
438 format=ptr;
439 }else
440 { if(isspace(*format))
441 { do
442 NEXT(c);
443 while(isspace(c));
444 PREV(c);
445 size=1; }
446 else
447 { NEXT(c);
448 if(c!=*format)
449 PREV(c); }
450 format++;
452 if(!size)
453 break;
456 if(c==EOF&&!blocks)
457 return c;
458 else
459 return blocks;