/* Copyright 1995-2001, The AROS Development Team. All rights reserved. $Id: __vcscan.c 19653 2003-09-05 23:08:41Z falemagn $ Function to scan a string like scanf(). */ #include "precompile.h" #define __vcscan __vcscan #include #include #include #ifndef AROS_NO_LIMITS_H # include #else # define ULONG_MAX 4294967295UL #endif #include #include /* some macros to cut this short * NEXT(c); read next character * PREV(c); ungetc a character * VAL(a) leads to 1 if a is true and valid */ #ifndef AROS_NOFPU # define FULL_SPECIFIERS #endif #define NEXT(c) ((c)=(*getc)(data),size++,incount++) #define PREV(c) do{if((c)!=EOF)(*ungetc)((c),data);size--;incount--;}while(0) #define VAL(a) ((a)&&size<=width) #ifdef FULL_SPECIFIERS const static unsigned char undef[3][sizeof(double)]= /* Undefined numeric values, IEEE */ { { 0x7f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00 }, /* +inf */ { 0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00 }, /* -inf */ { 0x7f,0xf1,0x00,0x00,0x00,0x00,0x00,0x00 } /* NaN */ }; #endif #undef getc #undef ungetc /***************************************************************************** NAME */ int __vcscan ( /* SYNOPSIS */ void * data, int (* getc)(void *), int (* ungetc)(int,void *), const char * format, va_list args) /* FUNCTION Scan an input stream as specified in format. The result of the scan will be placed in args. INPUTS data - This is passed to the usercallback getc and ungetc getc - This function gets called when the routine wants to read the next character. It whould return EOF when no more characters are available. ungetc - This function gets called when the routine wants to put a read character back into the stream. The next call to getc should return this character. It is possible that this function is called more than once before the next getc. format - A scanf() format string. args - A list of arguments in which the result of the scan should be placed. RESULT The number of arguments converted. NOTES EXAMPLE BUGS SEE ALSO INTERNALS ******************************************************************************/ { size_t blocks=0,incount=0; unsigned char *__decimalpoint = "."; int c=0; while(*format) { size_t size=0; if(*format=='%') { size_t width=ULONG_MAX; char type,subtype='i',ignore=0; const unsigned char *ptr=format+1; size_t i; if(isdigit(*ptr)) { width=0; while(isdigit(*ptr)) width=width*10+(*ptr++-'0'); } while(*ptr=='h'||*ptr=='l'||*ptr=='L'||*ptr=='*') { if(*ptr=='*') ignore=1; else subtype=*ptr; ptr++; } type=*ptr++; if(type&&type!='%'&&type!='c'&&type!='n'&&type!='[') { do /* ignore leading whitespace characters */ NEXT(c); while(isspace(c)); size=1; } /* The first non-whitespace character is already read */ switch(type) { case 'c': { unsigned char *bp; if(width==ULONG_MAX) /* Default */ width=1; if(!ignore) bp=va_arg(args,char *); else bp=NULL; /* Just to get the compiler happy */ NEXT(c); /* 'c' did not skip whitespace */ while(VAL(c!=EOF)) { if(!ignore) *bp++=c; NEXT(c); } PREV(c); if(!ignore&&size) blocks++; break; } case '[': { unsigned char *bp; unsigned char tab[32],a,b; char circflag=0; if(*ptr=='^') { circflag=1; ptr++; } for(i=0;i malformatted */ { PREV(c); c=__decimalpoint[0]; } } if(min&&size==2) /* No number read till now -> malformatted */ { PREV(c); c=min; } if(size==1) break; if(VAL(tolower(c)=='e')) { int d; NEXT(d); if(VAL(d=='-'||d=='+')) { mine=d; NEXT(d); } if(VAL(isdigit(d))) { do { ex=ex*10+(d-'0'); NEXT(d); }while(VAL(isdigit(d)&&ex<100)); c=d; }else { PREV(d); if(mine) PREV(mine); } } PREV(c); if(mine=='-') v=v/pow(10.0,ex); else v=v*pow(10.0,ex); if(min=='-') v=-v; }while(0); if(!ignore&&size) { switch(subtype) { case 'l': case 'L': *va_arg(args,double *)=v; break; case 'i': *va_arg(args,float *)=v; break; } blocks++; } break; } #endif case '%': NEXT(c); if(c!='%') PREV(c); /* unget non-'%' character */ break; case 'n': if(!ignore) *va_arg(args,int *)=incount; size=1; /* fake a valid argument */ blocks++; break; default: { unsigned long v=0; int base; int min=0; if(!type) ptr--; /* unparse NUL character */ if(type=='p') { subtype='l'; /* This is the same as %lx */ type='x'; } if(VAL((c=='-'&&type!='u')||c=='+')) { min=c; NEXT(c); } if(type=='i') /* which one to use ? */ { if(VAL(c=='0')) /* Could be octal or sedecimal */ { int d; NEXT(d); /* Get a look at next character */ if(VAL(tolower(d)=='x')) { int e; NEXT(e); /* And the next */ if(VAL(isxdigit(c))) type='x'; /* Is a valid x number with '0x?' */ PREV(e); }else type='o'; PREV(d); }else if(VAL(!isdigit(c)&&isxdigit(c))) type='x'; /* Is a valid x number without '0x' */ } while(type=='x'&&VAL(c=='0')) /* sedecimal */ { int d; NEXT(d); if(VAL(tolower(d)=='x')) { int e; NEXT(e); if(VAL(isxdigit(e))) { c=e; break; } /* Used while just to do this ;-) */ PREV(e); } PREV(d); break; /* Need no loop */ } base=type=='x'||type=='X'?16:(type=='o'?8:10); while(VAL(isxdigit(c)&&(base!=10||isdigit(c))&&(base!=8||c<='7'))) { v=v*base+(isdigit(c)?c-'0':0)+(isupper(c)?c-'A'+10:0)+(islower(c)?c-'a'+10:0); NEXT(c); } if(min&&size==2) /* If there is no valid character after sign, unget last */ { PREV(c); c=min; } PREV(c); if(ignore||!size) break; if(type=='u') switch(subtype) { case 'l': case 'L': *va_arg(args,unsigned long *)=v; break; case 'i': *va_arg(args,unsigned int *)=v; break; case 'h': *va_arg(args,unsigned short *)=v; break; } else { signed long v2; if(min=='-') v2=-v; else v2=v; switch(subtype) { case 'l': case 'L': *va_arg(args,signed long *)=v2; break; case 'i': *va_arg(args,signed int *)=v2; break; case 'h': *va_arg(args,signed short *)=v2; break; } } blocks++; break; } } format=ptr; }else { if(isspace(*format)) { do NEXT(c); while(isspace(c)); PREV(c); size=1; } else { NEXT(c); if(c!=*format) PREV(c); } format++; } if(!size) break; } if(c==EOF&&!blocks) return c; else return blocks; }