/* * vsscanf * * the function that ANSI forgot... * * © 1994-1998 Straylight */ /*----- Licensing note ----------------------------------------------------* * * This file is part of Straylight's Steel library. * * Steel is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * Steel is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Steel. If not, write to the Free Software Foundation, * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include "vsscanf.h" #ifndef BOOL #define BOOL int #define TRUE 1 #define FALSE 0 #endif typedef enum { vsscanf__DEFAULT, vsscanf__SHORT, vsscanf__LONG, vsscanf__VERY_LONG } vsscanf__length; /* This union type is to avoid problems with converting between signed and unsigned longs. */ typedef union { unsigned long ul; signed long l; } vsscanf__genlongu; static int vsscanf__getWidth(char **p) { char *q=(*p); for (;isdigit(**p);(*p)++) /* blank loop */; return (atoi(q)); } static void vsscanf__skipSpace(char **p) { while (isspace(**p)) (*p)++; } static void vsscanf__readFloat ( char **p, BOOL ignore, int *processed, vsscanf__length length, va_list ap ) { /*-------------------------------------------------------------------------*/ /* Note: this code relies on the fact that long double and double are */ /* identical on this architecture. This can't be chacked at compile time, */ /* and it is pointless checking at run-time. The reason for this is that */ /* I don't have a function for turning a string into a long double. */ /*-------------------------------------------------------------------------*/ double result; vsscanf__skipSpace(p); result=strtod(*p,p); if (!ignore) { switch (length) { case vsscanf__DEFAULT: case vsscanf__SHORT: *va_arg(ap,float *)=(float)result; break; case vsscanf__LONG: case vsscanf__VERY_LONG: *va_arg(ap,double *)=(double)result; break; } (*processed)++; } } static void vsscanf__readScanset ( char **p, char **q, int width, BOOL ignore, int *processed, va_list ap ) { char trans[256]; /* A trt (translate and test) table */ int i; BOOL reversed; int done=0; BOOL finished=FALSE; char *dest=0; for (i=0;i<256;i++) trans[i]=FALSE; (*q)++; /* Move past the opening '[' */ if (**q=='^') { reversed=TRUE; (*q)++; } else reversed=FALSE; if (**q==']') { trans[']']=TRUE; /* euch using a char as an index, but it works */ (*q)++; } while (**q!=']' && **q!=0)/* results of incorrect syntax are 'undefined' */ { trans[**q]=TRUE; (*q)++; } if (!ignore) dest=va_arg(ap,char *); while (!finished) { if (**p!=0 && (width==-1 || width!=done) && (trans[**p] ^ reversed)) { if (!ignore) *dest=**p; (*p)++; dest++; done++; } else finished=TRUE; } if (!ignore) *dest=0; if (done && !ignore) (*processed)++; } static void vsscanf__readString ( char **p, int width, BOOL ignore, BOOL chars, int *processed, va_list ap ) { char *dest=0; int done=0; BOOL finished=FALSE; if (!ignore) dest=va_arg(ap,char *); if (!chars) vsscanf__skipSpace(p); else { if (width==-1) width=1; } while (!finished) { if ((!isspace(**p) || chars) && **p && (done!=width || width==-1)) { if (!ignore) *dest=**p; (*p)++; dest++; done++; } else finished=TRUE; } if (!ignore && !chars) *dest=0; /* Finish off the string */ if (done && !ignore) (*processed)++; } static void vsscanf__readInteger ( char **p, BOOL ignore, int *processed, BOOL isSigned, int base, vsscanf__length length, va_list ap ) { char *start; vsscanf__genlongu result; vsscanf__skipSpace(p); start=*p; if (base==0) { if (**p=='0') { if (tolower(*(++(*p)))=='x') { base=16; (*p)++; } else base=8; } else base=10; } if (isSigned) result.l=strtol(start,p,base); else result.ul=strtoul(start,p,base); if (!ignore) { switch (length) { case vsscanf__DEFAULT: case vsscanf__VERY_LONG: /* Ignore silly option */ *va_arg(ap,int *)=(int)result.l; break; case vsscanf__LONG: *va_arg(ap,long *)=(long)result.l; /* This is machine dependant - r.r[2] is an int, but longs are the same */ /* width */ break; case vsscanf__SHORT: *va_arg(ap,short *)=(short)result.l; /* If the number is too big, the results are wierd. */ break; } (*processed)++; } } static BOOL vsscanf__processFormat ( char **p, char **q, int *processed, va_list ap ) { BOOL ignore=FALSE; vsscanf__length length=vsscanf__DEFAULT; int width=-1; if (**q=='*') { ignore=TRUE; (*q)++; } if (isdigit(**q)) width=vsscanf__getWidth(q); if (**q=='h') { length=vsscanf__SHORT; (*q)++; } else if (**q=='l') { length=vsscanf__LONG; (*q)++; } else if (**q=='L') { length=vsscanf__VERY_LONG; (*q)++; } /* Right - now we come to the business end of the thing. **q is the */ /* actual format specifier. */ switch (**q) { case 'n': if (!ignore) *(va_arg(ap,int *))=*processed; (*q)++; break; case 's': vsscanf__readString(p,width,ignore,FALSE,processed,ap); (*q)++; break; case 'c': vsscanf__readString(p,width,ignore,TRUE,processed,ap); (*q)++; break; case 'd': vsscanf__readInteger(p,ignore,processed,TRUE,10,length,ap); (*q)++; break; case 'x': case 'X': case 'p': /* Flat memory model - pointer==hex number */ vsscanf__readInteger(p,ignore,processed,FALSE,16,length,ap); (*q)++; break; case 'o': vsscanf__readInteger(p,ignore,processed,FALSE,8,length,ap); (*q)++; break; case 'u': vsscanf__readInteger(p,ignore,processed,FALSE,10,length,ap); (*q)++; break; case 'i': vsscanf__readInteger(p,ignore,processed,TRUE,0,length,ap); (*q)++; break; case 'e': case 'f': case 'g': vsscanf__readFloat(p,ignore,processed,length,ap); (*q)++; break; case '[': vsscanf__readScanset(p,q,width,ignore,processed,ap); (*q)++; break; default: vsscanf__skipSpace(p); if (*((*q)++)!=*((*p)++)) return (TRUE); } return (FALSE); } int vsscanf(char *string,char *format,va_list ap) { char *p=string; char *q=format; int processed=0; char c; while (c=*(q++),c) { if (isspace(c)) /* Skip whitespace in format and source */ { vsscanf__skipSpace(&p); vsscanf__skipSpace(&q); } else if (c=='%') /* A format specifier */ { if (vsscanf__processFormat(&p,&q,&processed,ap)) return (processed); } else /* R&D (Read and Discard) */ { vsscanf__skipSpace(&p); if (c!=*(p++)) return (processed); } if (*p==0) /* This is the dreaded EOF (well, EOL) */ { if (!processed) return (EOF); else return (processed); } } return (processed); }