sl@0: /* VFSCANF.C sl@0: * sl@0: * Portions Copyright (c) 1990-2004 Nokia Corporation and/or its subsidiary(-ies). sl@0: * All rights reserved. sl@0: */ sl@0: sl@0: /* No user fns here. Pesch 15apr92. */ sl@0: sl@0: /* sl@0: * Copyright (c) 1990 The Regents of the University of California. sl@0: * All rights reserved. sl@0: * sl@0: * Redistribution and use in source and binary forms are permitted sl@0: * provided that the above copyright notice and this paragraph are sl@0: * duplicated in all such forms and that any documentation, sl@0: * advertising materials, and other materials related to such sl@0: * distribution and use acknowledge that the software was developed sl@0: * by the University of California, Berkeley. The name of the sl@0: * University may not be used to endorse or promote products derived sl@0: * from this software without specific prior written permission. sl@0: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR sl@0: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED sl@0: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. sl@0: */ sl@0: sl@0: #include <_ansi.h> sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include "LOCAL.H" sl@0: sl@0: #include "FLOATIO.H" sl@0: #define BUF (MAXEXP+MAXFRACT+3) /* 3 = sign + decimal point + NUL */ sl@0: sl@0: /* sl@0: * Flags used during conversion. sl@0: */ sl@0: sl@0: #define LONG 0x01 /* l: long or double */ sl@0: #define LONGDBL 0x02 /* L: long double; unimplemented */ sl@0: #define SHORT 0x04 /* h: short */ sl@0: #define SUPPRESS 0x08 /* suppress assignment */ sl@0: #define POINTER 0x10 /* weird %p pointer (`fake hex') */ sl@0: #define NOSKIP 0x20 /* do not skip blanks */ sl@0: sl@0: /* sl@0: * The following are used in numeric conversions only: sl@0: * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point; sl@0: * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral. sl@0: */ sl@0: sl@0: #define SIGNOK 0x40 /* +/- is (still) legal */ sl@0: #define NDIGITS 0x80 /* no digits detected */ sl@0: sl@0: #define DPTOK 0x100 /* (float) decimal point is still legal */ sl@0: #define EXPOK 0x200 /* (float) exponent (e+3, etc) still legal */ sl@0: sl@0: #define PFXOK 0x100 /* 0x prefix is (still) legal */ sl@0: #define NZDIGITS 0x200 /* no zero digits detected */ sl@0: sl@0: /* sl@0: * Conversion types. sl@0: */ sl@0: sl@0: #define CT_CHAR 0 /* %c conversion */ sl@0: #define CT_CCL 1 /* %[...] conversion */ sl@0: #define CT_STRING 2 /* %s conversion */ sl@0: #define CT_INT 3 /* integer, i.e., strtol or strtoul */ sl@0: #define CT_FLOAT 4 /* floating, i.e., strtod */ sl@0: sl@0: #define u_char char sl@0: #define u_long unsigned long sl@0: sl@0: /*static*/ u_char *__sccl (register char *tab,register u_char *fmt); sl@0: sl@0: /* sl@0: * vfscanf sl@0: */ sl@0: sl@0: #define BufferEmpty (fp->_r <= 0 && __srefill(fp)) sl@0: sl@0: int sl@0: __svfscanf (register FILE *fp,char const *fmt0, va_list ap) sl@0: { sl@0: register u_char *fmt = (u_char *) fmt0; sl@0: register int c; /* character from format, or conversion */ sl@0: register size_t width; /* field width, or 0 */ sl@0: register char *p; /* points into all kinds of strings */ sl@0: register int n; /* handy integer */ sl@0: register int flags; /* flags as defined above */ sl@0: register char *p0; /* saves original value of p when necessary */ sl@0: int nassigned; /* number of fields assigned */ sl@0: int nread; /* number of characters consumed from fp */ sl@0: int base = 0; /* base argument to strtol/strtoul */ sl@0: sl@0: u_long (*ccfn) (const char *_n, char **_end_PTR, int _base) = 0; /* conversion function (strtol/strtoul) */ sl@0: char ccltab[256]; /* character class table for %[...] */ sl@0: char buf[BUF]; /* buffer for numeric conversions */ sl@0: sl@0: short *sp; sl@0: int *ip; sl@0: float *flp; sl@0: double *dp; sl@0: long *lp; sl@0: sl@0: /* `basefix' is used to avoid `if' tests in the integer scanner */ sl@0: static const short basefix[17] = sl@0: {10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; sl@0: sl@0: nassigned = 0; sl@0: nread = 0; sl@0: for (;;) sl@0: { sl@0: c = *fmt++; sl@0: if (c == 0) sl@0: return nassigned; sl@0: if (isspace (c)) sl@0: { sl@0: for (;;) sl@0: { sl@0: if (BufferEmpty) sl@0: return nassigned; sl@0: if (!isspace (*fp->_p)) sl@0: break; sl@0: nread++, fp->_r--, fp->_p++; sl@0: } sl@0: continue; sl@0: } sl@0: if (c != '%') sl@0: goto literal; sl@0: width = 0; sl@0: flags = 0; sl@0: sl@0: /* sl@0: * switch on the format. continue if done; break once format sl@0: * type is derived. sl@0: */ sl@0: sl@0: again: sl@0: c = *fmt++; sl@0: sl@0: switch (c) sl@0: { sl@0: case '%': sl@0: literal: sl@0: if (BufferEmpty) sl@0: goto input_failure; sl@0: if (*fp->_p != c) sl@0: goto match_failure; sl@0: fp->_r--, fp->_p++; sl@0: nread++; sl@0: continue; sl@0: sl@0: case '*': sl@0: flags |= SUPPRESS; sl@0: goto again; sl@0: case 'l': sl@0: flags |= LONG; sl@0: goto again; sl@0: case 'L': sl@0: /* not supported flags |= LONGDBL; */ sl@0: goto again; sl@0: case 'h': sl@0: flags |= SHORT; sl@0: goto again; sl@0: sl@0: case '0': sl@0: case '1': sl@0: case '2': sl@0: case '3': sl@0: case '4': sl@0: case '5': sl@0: case '6': sl@0: case '7': sl@0: case '8': sl@0: case '9': sl@0: width = width * 10 + c - '0'; sl@0: goto again; sl@0: sl@0: /* sl@0: * Conversions. Those marked `compat' are for sl@0: * 4.[123]BSD compatibility. sl@0: * sl@0: * (According to ANSI, E and X formats are supposed to sl@0: * the same as e and x. Sorry about that.) sl@0: */ sl@0: sl@0: case 'D': /* compat */ sl@0: flags |= LONG; sl@0: /* FALLTHROUGH */ sl@0: case 'd': sl@0: c = CT_INT; sl@0: ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtol; sl@0: base = 10; sl@0: break; sl@0: sl@0: case 'i': sl@0: c = CT_INT; sl@0: ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtol; sl@0: base = 0; sl@0: break; sl@0: sl@0: case 'O': /* compat */ sl@0: flags |= LONG; sl@0: /* FALLTHROUGH */ sl@0: case 'o': sl@0: c = CT_INT; sl@0: ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtoul; sl@0: base = 8; sl@0: break; sl@0: sl@0: case 'u': sl@0: c = CT_INT; sl@0: ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtoul; sl@0: base = 10; sl@0: break; sl@0: sl@0: case 'X': /* compat XXX */ sl@0: case 'x': sl@0: flags |= PFXOK; /* enable 0x prefixing */ sl@0: c = CT_INT; sl@0: ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtoul; sl@0: base = 16; sl@0: break; sl@0: sl@0: case 'E': /* compat XXX */ sl@0: case 'G': /* compat XXX */ sl@0: /* ANSI says that E,G and X behave the same way as e,g,x */ sl@0: /* FALLTHROUGH */ sl@0: case 'e': sl@0: case 'f': sl@0: case 'g': sl@0: c = CT_FLOAT; sl@0: break; sl@0: sl@0: case 's': sl@0: c = CT_STRING; sl@0: break; sl@0: sl@0: case '[': sl@0: fmt = __sccl (ccltab, fmt); sl@0: flags |= NOSKIP; sl@0: c = CT_CCL; sl@0: break; sl@0: sl@0: case 'c': sl@0: flags |= NOSKIP; sl@0: c = CT_CHAR; sl@0: break; sl@0: sl@0: case 'p': /* pointer format is like hex */ sl@0: flags |= POINTER | PFXOK; sl@0: c = CT_INT; sl@0: ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtoul; sl@0: base = 16; sl@0: break; sl@0: sl@0: case 'n': sl@0: if (flags & SUPPRESS) /* ??? */ sl@0: continue; sl@0: if (flags & SHORT) sl@0: { sl@0: sp = va_arg (ap, short *); sl@0: *sp = (short)nread; sl@0: } sl@0: else if (flags & LONG) sl@0: { sl@0: lp = va_arg (ap, long *); sl@0: *lp = nread; sl@0: } sl@0: else sl@0: { sl@0: ip = va_arg (ap, int *); sl@0: *ip = nread; sl@0: } sl@0: continue; sl@0: sl@0: /* sl@0: * Disgusting backwards compatibility hacks. XXX sl@0: */ sl@0: case '\0': /* compat */ sl@0: return EOF; sl@0: sl@0: default: /* compat */ sl@0: if (isupper (c)) sl@0: flags |= LONG; sl@0: c = CT_INT; sl@0: ccfn = (u_long (*)(const char *_n, char **_end_PTR, int _base))strtol; sl@0: base = 10; sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: * We have a conversion that requires input. sl@0: */ sl@0: if (BufferEmpty) sl@0: goto input_failure; sl@0: sl@0: /* sl@0: * Consume leading white space, except for formats that sl@0: * suppress this. sl@0: */ sl@0: if ((flags & NOSKIP) == 0) sl@0: { sl@0: while (isspace (*fp->_p)) sl@0: { sl@0: nread++; sl@0: if (--fp->_r > 0) sl@0: fp->_p++; sl@0: else sl@0: if (__srefill (fp)) sl@0: goto input_failure; sl@0: } sl@0: /* sl@0: * Note that there is at least one character in the sl@0: * buffer, so conversions that do not set NOSKIP ca sl@0: * no longer result in an input failure. sl@0: */ sl@0: } sl@0: sl@0: /* sl@0: * Do the conversion. sl@0: */ sl@0: switch (c) sl@0: { sl@0: sl@0: case CT_CHAR: sl@0: /* scan arbitrary characters (sets NOSKIP) */ sl@0: if (width == 0) sl@0: width = 1; sl@0: if (flags & SUPPRESS) sl@0: { sl@0: size_t sum = 0; sl@0: sl@0: for (;;) sl@0: { sl@0: if ((n = fp->_r) < (int)width) sl@0: { sl@0: sum += n; sl@0: width -= n; sl@0: fp->_p += n; sl@0: if (__srefill (fp)) sl@0: { sl@0: if (sum == 0) sl@0: goto input_failure; sl@0: break; sl@0: } sl@0: } sl@0: else sl@0: { sl@0: sum += width; sl@0: fp->_r -= width; sl@0: fp->_p += width; sl@0: break; sl@0: } sl@0: } sl@0: nread += sum; sl@0: } sl@0: else sl@0: { sl@0: size_t r = fread ((void*) va_arg (ap, char *), 1, width, fp); sl@0: sl@0: if (r == 0) sl@0: goto input_failure; sl@0: nread += r; sl@0: nassigned++; sl@0: } sl@0: break; sl@0: sl@0: case CT_CCL: sl@0: /* scan a (nonempty) character class (sets NOSKIP) */ sl@0: if (width == 0) sl@0: width = (unsigned long)(~0); /* `infinity' */ sl@0: /* take only those things in the class */ sl@0: if (flags & SUPPRESS) sl@0: { sl@0: n = 0; sl@0: while (ccltab[*fp->_p]) sl@0: { sl@0: n++, fp->_r--, fp->_p++; sl@0: if (--width == 0) sl@0: break; sl@0: if (BufferEmpty) sl@0: { sl@0: if (n == 0) sl@0: goto input_failure; sl@0: break; sl@0: } sl@0: } sl@0: if (n == 0) sl@0: goto match_failure; sl@0: } sl@0: else sl@0: { sl@0: p0 = p = va_arg (ap, char *); sl@0: while (ccltab[*fp->_p]) sl@0: { sl@0: fp->_r--; sl@0: *p++ = *fp->_p++; sl@0: if (--width == 0) sl@0: break; sl@0: if (BufferEmpty) sl@0: { sl@0: if (p == p0) sl@0: goto input_failure; sl@0: break; sl@0: } sl@0: } sl@0: n = p - p0; sl@0: if (n == 0) sl@0: goto match_failure; sl@0: *p = 0; sl@0: nassigned++; sl@0: } sl@0: nread += n; sl@0: break; sl@0: sl@0: case CT_STRING: sl@0: /* like CCL, but zero-length string OK, & no NOSKIP */ sl@0: if (width == 0) sl@0: width = (unsigned long)(~0); sl@0: if (flags & SUPPRESS) sl@0: { sl@0: n = 0; sl@0: while (!isspace (*fp->_p)) sl@0: { sl@0: n++, fp->_r--, fp->_p++; sl@0: if (--width == 0) sl@0: break; sl@0: if (BufferEmpty) sl@0: break; sl@0: } sl@0: nread += n; sl@0: } sl@0: else sl@0: { sl@0: p0 = p = va_arg (ap, char *); sl@0: while (!isspace (*fp->_p)) sl@0: { sl@0: fp->_r--; sl@0: *p++ = *fp->_p++; sl@0: if (--width == 0) sl@0: break; sl@0: if (BufferEmpty) sl@0: break; sl@0: } sl@0: *p = 0; sl@0: nread += p - p0; sl@0: nassigned++; sl@0: } sl@0: continue; sl@0: sl@0: case CT_INT: sl@0: /* scan an integer as if by strtol/strtoul */ sl@0: #ifdef hardway sl@0: if (width == 0 || width > sizeof (buf) - 1) sl@0: width = sizeof (buf) - 1; sl@0: #else sl@0: /* size_t is unsigned, hence this optimisation */ sl@0: if (--width > sizeof (buf) - 2) sl@0: width = sizeof (buf) - 2; sl@0: width++; sl@0: #endif sl@0: flags |= SIGNOK | NDIGITS | NZDIGITS; sl@0: for (p = buf; width; width--) sl@0: { sl@0: c = *fp->_p; sl@0: /* sl@0: * Switch on the character; `goto ok' if we sl@0: * accept it as a part of number. sl@0: */ sl@0: switch (c) sl@0: { sl@0: /* sl@0: * The digit 0 is always legal, but is special. sl@0: * For %i conversions, if no digits (zero or nonzero) sl@0: * have been scanned (only signs), we will have base==0. sl@0: * In that case, we should set it to 8 and enable 0x sl@0: * prefixing. Also, if we have not scanned zero digits sl@0: * before this, do not turn off prefixing (someone else sl@0: * will turn it off if we have scanned any nonzero digits). sl@0: */ sl@0: case '0': sl@0: if (base == 0) sl@0: { sl@0: base = 8; sl@0: flags |= PFXOK; sl@0: } sl@0: if (flags & NZDIGITS) sl@0: flags &= ~(SIGNOK | NZDIGITS | NDIGITS); sl@0: else sl@0: flags &= ~(SIGNOK | PFXOK | NDIGITS); sl@0: goto ok; sl@0: sl@0: /* 1 through 7 always legal */ sl@0: case '1': sl@0: case '2': sl@0: case '3': sl@0: case '4': sl@0: case '5': sl@0: case '6': sl@0: case '7': sl@0: base = basefix[base]; sl@0: flags &= ~(SIGNOK | PFXOK | NDIGITS); sl@0: goto ok; sl@0: sl@0: /* digits 8 and 9 ok iff decimal or hex */ sl@0: case '8': sl@0: case '9': sl@0: base = basefix[base]; sl@0: if (base <= 8) sl@0: break; /* not legal here */ sl@0: flags &= ~(SIGNOK | PFXOK | NDIGITS); sl@0: goto ok; sl@0: sl@0: /* letters ok iff hex */ sl@0: case 'A': sl@0: case 'B': sl@0: case 'C': sl@0: case 'D': sl@0: case 'E': sl@0: case 'F': sl@0: case 'a': sl@0: case 'b': sl@0: case 'c': sl@0: case 'd': sl@0: case 'e': sl@0: case 'f': sl@0: /* no need to fix base here */ sl@0: if (base <= 10) sl@0: break; /* not legal here */ sl@0: flags &= ~(SIGNOK | PFXOK | NDIGITS); sl@0: goto ok; sl@0: sl@0: /* sign ok only as first character */ sl@0: case '+': sl@0: case '-': sl@0: if (flags & SIGNOK) sl@0: { sl@0: flags &= ~SIGNOK; sl@0: goto ok; sl@0: } sl@0: break; sl@0: sl@0: /* x ok iff flag still set & 2nd char */ sl@0: case 'x': sl@0: case 'X': sl@0: if (flags & PFXOK && p == buf + 1) sl@0: { sl@0: base = 16;/* if %i */ sl@0: flags &= ~PFXOK; sl@0: goto ok; sl@0: } sl@0: break; sl@0: } sl@0: sl@0: /* sl@0: * If we got here, c is not a legal character sl@0: * for a number. Stop accumulating digits. sl@0: */ sl@0: break; sl@0: ok: sl@0: /* sl@0: * c is legal: store it and look at the next. sl@0: */ sl@0: *p++ = (char)c; sl@0: if (--fp->_r > 0) sl@0: fp->_p++; sl@0: else sl@0: if (__srefill (fp)) sl@0: break; /* EOF */ sl@0: } sl@0: /* sl@0: * If we had only a sign, it is no good; push back the sign. sl@0: * If the number ends in `x', it was [sign] '0' 'x', so push back sl@0: * the x and treat it as [sign] '0'. sl@0: */ sl@0: if (flags & NDIGITS) sl@0: { sl@0: if (p > buf) sl@0: (void) ungetc (*(u_char *)-- p, fp); sl@0: goto match_failure; sl@0: } sl@0: c = ((u_char *) p)[-1]; sl@0: if (c == 'x' || c == 'X') sl@0: { sl@0: --p; sl@0: /*(void)*/ ungetc (c, fp); sl@0: } sl@0: if ((flags & SUPPRESS) == 0) sl@0: { sl@0: u_long res; sl@0: sl@0: *p = 0; sl@0: res = (*ccfn) (buf, (char **) NULL, base); sl@0: if (flags & POINTER) sl@0: *(va_arg (ap, void* *)) = (void*) res; sl@0: else if (flags & SHORT) sl@0: { sl@0: sp = va_arg (ap, short *); sl@0: *sp = (short)res; sl@0: } sl@0: else if (flags & LONG) sl@0: { sl@0: lp = va_arg (ap, long *); sl@0: *lp = res; sl@0: } sl@0: else sl@0: { sl@0: ip = va_arg (ap, int *); sl@0: *ip = res; sl@0: } sl@0: nassigned++; sl@0: } sl@0: nread += p - buf; sl@0: break; sl@0: sl@0: case CT_FLOAT: sl@0: /* scan a floating point number as if by strtod */ sl@0: #ifdef hardway sl@0: if (width == 0 || width > sizeof (buf) - 1) sl@0: width = sizeof (buf) - 1; sl@0: #else sl@0: /* size_t is unsigned, hence this optimisation */ sl@0: if (--width > sizeof (buf) - 2) sl@0: width = sizeof (buf) - 2; sl@0: width++; sl@0: #endif sl@0: flags |= SIGNOK | NDIGITS | DPTOK | EXPOK; sl@0: for (p = buf; width; width--) sl@0: { sl@0: c = *fp->_p; sl@0: /* sl@0: * This code mimicks the integer conversion sl@0: * code, but is much simpler. sl@0: */ sl@0: switch (c) sl@0: { sl@0: sl@0: case '0': sl@0: case '1': sl@0: case '2': sl@0: case '3': sl@0: case '4': sl@0: case '5': sl@0: case '6': sl@0: case '7': sl@0: case '8': sl@0: case '9': sl@0: flags &= ~(SIGNOK | NDIGITS); sl@0: goto fok; sl@0: sl@0: case '+': sl@0: case '-': sl@0: if (flags & SIGNOK) sl@0: { sl@0: flags &= ~SIGNOK; sl@0: goto fok; sl@0: } sl@0: break; sl@0: case '.': sl@0: if (flags & DPTOK) sl@0: { sl@0: flags &= ~(SIGNOK | DPTOK); sl@0: goto fok; sl@0: } sl@0: break; sl@0: case 'e': sl@0: case 'E': sl@0: /* no exponent without some digits */ sl@0: if ((flags & (NDIGITS | EXPOK)) == EXPOK) sl@0: { sl@0: flags = sl@0: (flags & ~(EXPOK | DPTOK)) | sl@0: SIGNOK | NDIGITS; sl@0: goto fok; sl@0: } sl@0: break; sl@0: } sl@0: break; sl@0: fok: sl@0: *p++ = (char)c; sl@0: if (--fp->_r > 0) sl@0: fp->_p++; sl@0: else sl@0: if (__srefill (fp)) sl@0: break; /* EOF */ sl@0: } sl@0: /* sl@0: * If no digits, might be missing exponent digits sl@0: * (just give back the exponent) or might be missing sl@0: * regular digits, but had sign and/or decimal point. sl@0: */ sl@0: if (flags & NDIGITS) sl@0: { sl@0: if (flags & EXPOK) sl@0: { sl@0: /* no digits at all */ sl@0: while (p > buf) sl@0: ungetc (*(u_char *)-- p, fp); sl@0: goto match_failure; sl@0: } sl@0: /* just a bad exponent (e and maybe sign) */ sl@0: c = *(u_char *)-- p; sl@0: if (c != 'e' && c != 'E') sl@0: { sl@0: (void) ungetc (c, fp); /* sign */ sl@0: c = *(u_char *)-- p; sl@0: } sl@0: (void) ungetc (c, fp); sl@0: } sl@0: if ((flags & SUPPRESS) == 0) sl@0: { sl@0: double res; sl@0: sl@0: *p = 0; sl@0: res = atof (buf); sl@0: if (flags & LONG) sl@0: { sl@0: dp = va_arg (ap, double *); sl@0: *dp = res; sl@0: } sl@0: else sl@0: { sl@0: flp = va_arg (ap, float *); sl@0: *flp = (float)res; sl@0: } sl@0: nassigned++; sl@0: } sl@0: nread += p - buf; sl@0: break; sl@0: } sl@0: } sl@0: input_failure: sl@0: return nassigned ? nassigned : -1; sl@0: match_failure: sl@0: return nassigned; sl@0: } sl@0: sl@0: /* sl@0: * Fill in the given table from the scanset at the given format sl@0: * (just after `['). Return a pointer to the character past the sl@0: * closing `]'. The table has a 1 wherever characters should be sl@0: * considered part of the scanset. sl@0: */ sl@0: sl@0: /*static*/ sl@0: u_char * sl@0: __sccl (register char *tab,register u_char *fmt) sl@0: { sl@0: register int c, n, v; sl@0: sl@0: /* first `clear' the whole table */ sl@0: c = *fmt++; /* first char hat => negated scanset */ sl@0: if (c == '^') sl@0: { sl@0: v = 1; /* default => accept */ sl@0: c = *fmt++; /* get new first char */ sl@0: } sl@0: else sl@0: v = 0; /* default => reject */ sl@0: /* should probably use memset here */ sl@0: for (n = 0; n < 256; n++) sl@0: tab[n] = (char)v; sl@0: if (c == 0) sl@0: return fmt - 1; /* format ended before closing ] */ sl@0: sl@0: /* sl@0: * Now set the entries corresponding to the actual scanset to the sl@0: * opposite of the above. sl@0: * sl@0: * The first character may be ']' (or '-') without being special; the sl@0: * last character may be '-'. sl@0: */ sl@0: sl@0: v = 1 - v; sl@0: for (;;) sl@0: { sl@0: tab[c] = (char)v; /* take character c */ sl@0: doswitch: sl@0: n = *fmt++; /* and examine the next */ sl@0: switch (n) sl@0: { sl@0: sl@0: case 0: /* format ended too soon */ sl@0: return fmt - 1; sl@0: sl@0: case '-': sl@0: /* sl@0: * A scanset of the form [01+-] is defined as `the digit 0, the sl@0: * digit 1, the character +, the character -', but the effect of a sl@0: * scanset such as [a-zA-Z0-9] is implementation defined. The V7 sl@0: * Unix scanf treats `a-z' as `the letters a through z', but treats sl@0: * `a-a' as `the letter a, the character -, and the letter a'. sl@0: * sl@0: * For compatibility, the `-' is not considerd to define a range if sl@0: * the character following it is either a close bracket (required by sl@0: * ANSI) or is not numerically greater than the character we just sl@0: * stored in the table (c). sl@0: */ sl@0: n = *fmt; sl@0: if (n == ']' || n < c) sl@0: { sl@0: c = '-'; sl@0: break; /* resume the for(;;) */ sl@0: } sl@0: fmt++; sl@0: do sl@0: { /* fill in the range */ sl@0: tab[++c] = (char)v; sl@0: } sl@0: while (c < n); sl@0: #if 1 /* XXX another disgusting compatibility hack */ sl@0: /* sl@0: * Alas, the V7 Unix scanf also treats formats such sl@0: * as [a-c-e] as `the letters a through e'. This too sl@0: * is permitted by the standard.... sl@0: */ sl@0: goto doswitch; sl@0: #else sl@0: c = *fmt++; sl@0: if (c == 0) sl@0: return fmt - 1; sl@0: if (c == ']') sl@0: return fmt; sl@0: sl@0: break; sl@0: #endif sl@0: sl@0: case ']': /* end of scanset */ sl@0: return fmt; sl@0: sl@0: default: /* just another character */ sl@0: c = n; sl@0: break; sl@0: } sl@0: } sl@0: /* NOTREACHED */ sl@0: }