#include #include #include #include #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 */ #define NEXT(c) ((c)=xgetc(opaque),size++,incount++) #define PREV(c) do{if((c)!=EOF)xungetc(opaque,(c));size--;incount--;}while(0) #define VAL(a) ((a)&&size<=width) #ifdef NOFLOATINGPOINT #undef FULL_SPECIFIERS #else #define FULL_SPECIFIERS #endif extern unsigned char *__decimalpoint; #ifdef FULL_SPECIFIERS static const 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 int vxscanf(int (*xgetc)(void *),void (*xungetc)(void*,int),void *opaque,const char *format,va_list args) { size_t blocks=0,incount=0; 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=(const unsigned char *)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,unsigned 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=(const char *)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; }