diff options
author | Pixel <pixel@nobis-crew.org> | 2011-02-05 11:06:07 -0800 |
---|---|---|
committer | Pixel <pixel@nobis-crew.org> | 2011-02-05 11:06:07 -0800 |
commit | 8b1fadd6a89f12cbc81c5d621a75b91dd9eedec2 (patch) | |
tree | 0b130b02101391e206c07cb0a1c9031bb4d27f21 /libc/src | |
parent | 4830af498e2d3a5440e65c63a3595b91f1cd4ac9 (diff) |
Adding Sun's fdlibm, and libnix's scanf, tweaked to become an xscanf. Adding a few more libc inlined also.
Diffstat (limited to 'libc/src')
-rwxr-xr-x | libc/src/xscanf.c | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/libc/src/xscanf.c b/libc/src/xscanf.c new file mode 100755 index 0000000..ad6f190 --- /dev/null +++ b/libc/src/xscanf.c @@ -0,0 +1,402 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <limits.h> +#include <ctype.h> +#include <math.h> + +/* 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((c),opaque);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)(int,void*),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<sizeof(tab);i++) + tab[i]=circflag?255:0; + for(;;) + { if(!*ptr) + break; + a=b=*ptr++; + if(*ptr=='-'&&ptr[1]&&ptr[1]!=']') + { ptr++; + b=*ptr++; } + for(i=a;i<=b;i++) + if(circflag) + tab[i/8]&=~(1<<(i&7)); + else + tab[i/8]|=1<<(i&7); + if(*ptr==']') + { ptr++; + break; } + } + + if(!ignore) + bp=va_arg(args,unsigned char *); + else + bp=NULL; /* Just to get the compiler happy */ + + NEXT(c); + while(VAL(c!=EOF&&tab[c/8]&(1<<(c&7)))) + { if(!ignore) + *bp++=c; + NEXT(c); + } + PREV(c); + + if(!ignore&&size) + { *bp++='\0'; + blocks++; } + break; + } + case 's': + { unsigned char *bp; + + if(!ignore) + bp=va_arg(args,unsigned char *); + else + bp=NULL; /* Just to get the compiler happy */ + + while(VAL(c!=EOF&&!isspace(c))) + { if(!ignore) + *bp++=c; + NEXT(c); + } + PREV(c); + + if(!ignore&&size) + { *bp++='\0'; + blocks++; } + break; + } +#ifdef FULL_SPECIFIERS + case 'e': + case 'f': + case 'g': + { double v; + int ex=0; + int min=0,mine=0; /* This is a workaround for gcc 2.3.3: should be char */ + + do /* This is there just to be able to break out */ + { + if(VAL(c=='-'||c=='+')) + { min=c; + NEXT(c); } + + if(VAL(tolower(c)=='i')) /* +- inf */ + { int d; + NEXT(d); + if(VAL(tolower(d)=='n')) + { int e; + NEXT(e); + if(VAL(tolower(e)=='f')) + { v=*(double *)&undef[min=='-']; + break; } /* break out */ + PREV(e); + } + PREV(d); + } + else if(VAL(toupper(c)=='N')) /* NaN */ + { int d; + NEXT(d); + if(VAL(tolower(d)=='a')) + { int e; + NEXT(e); + if(VAL(toupper(e)=='N')) + { v=*(double *)&undef[2]; + break; } + PREV(e); + } + PREV(d); + } + + v=0.0; + while(VAL(isdigit(c))) + { v=v*10.0+(c-'0'); + NEXT(c); + } + + if(VAL(c==__decimalpoint[0])) + { double dp=0.1; + NEXT(c); + while(VAL(isdigit(c))) + { v=v+dp*(c-'0'); + dp=dp/10.0; + NEXT(c); } + if(size==2+(min!=0)) /* No number read till now -> 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; +} |