int vsscanf(const char *inp, char const *fmt0, va_list ap) { int inr; const u_char *fmt = (const u_char *)fmt0; int c; /* character from format, or conversion */ size_t width; /* field width, or 0 */ char *p; /* points into all kinds of strings */ int n; /* handy integer */ int flags; /* flags as defined above */ char *p0; /* saves original value of p when necessary */ int nassigned; /* number of fields assigned */ int nconversions; /* number of conversions */ int nread; /* number of characters consumed from fp */ int base; /* base argument to conversion function */ char ccltab[256]; /* character class table for %[...] */ char buf[BUF]; /* buffer for numeric conversions */ /* `basefix' is used to avoid `if' tests in the integer scanner */ static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; inr = strlen(inp); nassigned = 0; nconversions = 0; nread = 0; base = 0; /* XXX just to keep gcc happy */ for (;;) { c = *fmt++; if (c == 0) return (nassigned); if (isspace(c)) { while (inr > 0 && isspace(*inp)) nread++, inr--, inp++; continue; } if (c != '%') goto literal; width = 0; flags = 0; /* * switch on the format. continue if done; * break once format type is derived. */ again: c = *fmt++; switch (c) { case '%': literal: if (inr <= 0) goto input_failure; if (*inp != c) goto match_failure; inr--, inp++; nread++; continue; case '*': flags |= SUPPRESS; goto again; case 'l': if (flags & LONG) { flags &= ~LONG; flags |= LONGLONG; } else flags |= LONG; goto again; case 'q': flags |= LONGLONG; /* not quite */ goto again; case 'h': if (flags & SHORT) { flags &= ~SHORT; flags |= SHORTSHORT; } else flags |= SHORT; goto again; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': width = width * 10 + c - '0'; goto again; /* * Conversions. */ case 'd': c = CT_INT; base = 10; break; case 'i': c = CT_INT; base = 0; break; case 'o': c = CT_INT; flags |= UNSIGNED; base = 8; break; case 'u': c = CT_INT; flags |= UNSIGNED; base = 10; break; case 'X': case 'x': flags |= PFXOK; /* enable 0x prefixing */ c = CT_INT; flags |= UNSIGNED; base = 16; break; case 's': c = CT_STRING; break; case '[': fmt = __sccl(ccltab, fmt); flags |= NOSKIP; c = CT_CCL; break; case 'c': flags |= NOSKIP; c = CT_CHAR; break; case 'p': /* pointer format is like hex */ flags |= POINTER | PFXOK; c = CT_INT; flags |= UNSIGNED; base = 16; break; case 'n': nconversions++; if (flags & SUPPRESS) /* ??? */ continue; if (flags & SHORTSHORT) *va_arg(ap, char *) = nread; else if (flags & SHORT) *va_arg(ap, short *) = nread; else if (flags & LONG) *va_arg(ap, long *) = nread; else if (flags & LONGLONG) *va_arg(ap, long long *) = nread; else *va_arg(ap, int *) = nread; continue; } /* * We have a conversion that requires input. */ if (inr <= 0) goto input_failure; /* * Consume leading white space, except for formats * that suppress this. */ if ((flags & NOSKIP) == 0) { while (isspace(*inp)) { nread++; if (--inr > 0) inp++; else goto input_failure; } /* * Note that there is at least one character in * the buffer, so conversions that do not set NOSKIP * can no longer result in an input failure. */ } /* * Do the conversion. */ switch (c) { case CT_CHAR: /* scan arbitrary characters (sets NOSKIP) */ if (width == 0) width = 1; if (flags & SUPPRESS) { size_t sum = 0; for (;;) { if ((n = inr) < (int)width) { sum += n; width -= n; inp += n; if (sum == 0) goto input_failure; break; } else { sum += width; inr -= width; inp += width; break; } } nread += sum; } else { bcopy(inp, va_arg(ap, char *), width); inr -= width; inp += width; nread += width; nassigned++; } nconversions++; break; case CT_CCL: /* scan a (nonempty) character class (sets NOSKIP) */ if (width == 0) width = (size_t)~0; /* `infinity' */ /* take only those things in the class */ if (flags & SUPPRESS) { n = 0; while (ccltab[(unsigned char)*inp]) { n++, inr--, inp++; if (--width == 0) break; if (inr <= 0) { if (n == 0) goto input_failure; break; } } if (n == 0) goto match_failure; } else { p0 = p = va_arg(ap, char *); while (ccltab[(unsigned char)*inp]) { inr--; *p++ = *inp++; if (--width == 0) break; if (inr <= 0) { if (p == p0) goto input_failure; break; } } n = p - p0; if (n == 0) goto match_failure; *p = 0; nassigned++; } nread += n; nconversions++; break; case CT_STRING: /* like CCL, but zero-length string OK, & no NOSKIP */ if (width == 0) width = (size_t)~0; if (flags & SUPPRESS) { n = 0; while (!isspace(*inp)) { n++, inr--, inp++; if (--width == 0) break; if (inr <= 0) break; } nread += n; } else { p0 = p = va_arg(ap, char *); while (!isspace(*inp)) { inr--; *p++ = *inp++; if (--width == 0) break; if (inr <= 0) break; } *p = 0; nread += p - p0; nassigned++; } nconversions++; continue; case CT_INT: /* scan an integer as if by the conversion function */ #ifdef hardway if (width == 0 || width > sizeof(buf) - 1) width = sizeof(buf) - 1; #else /* size_t is unsigned, hence this optimisation */ if (--width > sizeof(buf) - 2) width = sizeof(buf) - 2; width++; #endif flags |= SIGNOK | NDIGITS | NZDIGITS; for (p = buf; width; width--) { c = *inp; /* * Switch on the character; `goto ok' * if we accept it as a part of number. */ switch (c) { /* * The digit 0 is always legal, but is * special. For %i conversions, if no * digits (zero or nonzero) have been * scanned (only signs), we will have * base==0. In that case, we should set * it to 8 and enable 0x prefixing. * Also, if we have not scanned zero digits * before this, do not turn off prefixing * (someone else will turn it off if we * have scanned any nonzero digits). */ case '0': if (base == 0) { base = 8; flags |= PFXOK; } if (flags & NZDIGITS) flags &= ~(SIGNOK|NZDIGITS|NDIGITS); else flags &= ~(SIGNOK|PFXOK|NDIGITS); goto ok; /* 1 through 7 always legal */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': base = basefix[base]; flags &= ~(SIGNOK | PFXOK | NDIGITS); goto ok; /* digits 8 and 9 ok iff decimal or hex */ case '8': case '9': base = basefix[base]; if (base <= 8) break; /* not legal here */ flags &= ~(SIGNOK | PFXOK | NDIGITS); goto ok; /* letters ok iff hex */ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': /* no need to fix base here */ if (base <= 10) break; /* not legal here */ flags &= ~(SIGNOK | PFXOK | NDIGITS); goto ok; /* sign ok only as first character */ case '+': case '-': if (flags & SIGNOK) { flags &= ~SIGNOK; goto ok; } break; /* x ok iff flag still set & 2nd char */ case 'x': case 'X': if (flags & PFXOK && p == buf + 1) { base = 16; /* if %i */ flags &= ~PFXOK; goto ok; } break; } /* * If we got here, c is not a legal character * for a number. Stop accumulating digits. */ break; ok: /* * c is legal: store it and look at the next. */ *p++ = c; if (--inr > 0) inp++; else break; /* end of input */ } /* * If we had only a sign, it is no good; push * back the sign. If the number ends in `x', * it was [sign] '0' 'x', so push back the x * and treat it as [sign] '0'. */ if (flags & NDIGITS) { if (p > buf) { inp--; inr++; } goto match_failure; } c = ((u_char *)p)[-1]; if (c == 'x' || c == 'X') { --p; inp--; inr++; } if ((flags & SUPPRESS) == 0) { u_quad_t res; *p = 0; if ((flags & UNSIGNED) == 0) res = strtoq(buf, (char **)NULL, base); else res = strtouq(buf, (char **)NULL, base); if (flags & POINTER) *va_arg(ap, void **) = (void *)(uintptr_t)res; else if (flags & SHORTSHORT) *va_arg(ap, char *) = res; else if (flags & SHORT) *va_arg(ap, short *) = res; else if (flags & LONG) *va_arg(ap, long *) = res; else if (flags & LONGLONG) *va_arg(ap, long long *) = res; else *va_arg(ap, int *) = res; nassigned++; } nread += p - buf; nconversions++; break; } } input_failure: return (nconversions != 0 ? nassigned : -1); match_failure: return (nassigned); }
/* * Internal, unlocked version of vfscanf */ int __svfscanf(FILE *fp, const char *fmt0, __va_list ap) { u_char *fmt = (u_char *)fmt0; int c; /* character from format, or conversion */ size_t width; /* field width, or 0 */ char *p; /* points into all kinds of strings */ int n; /* handy integer */ int flags; /* flags as defined above */ char *p0; /* saves original value of p when necessary */ int nassigned; /* number of fields assigned */ int nread; /* number of characters consumed from fp */ int base; /* base argument to strtoimax/strtouimax */ char ccltab[256]; /* character class table for %[...] */ char buf[BUF]; /* buffer for numeric conversions */ #ifdef SCANF_WIDE_CHAR wchar_t *wcp; /* handy wide character pointer */ size_t nconv; /* length of multibyte sequence converted */ mbstate_t mbs; #endif /* `basefix' is used to avoid `if' tests in the integer scanner */ static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; _SET_ORIENTATION(fp, -1); nassigned = 0; nread = 0; base = 0; /* XXX just to keep gcc happy */ for (;;) { c = *fmt++; if (c == 0) return (nassigned); if (isspace(c)) { while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p)) nread++, fp->_r--, fp->_p++; continue; } if (c != '%') goto literal; width = 0; flags = 0; /* * switch on the format. continue if done; * break once format type is derived. */ again: c = *fmt++; switch (c) { case '%': literal: if (fp->_r <= 0 && __srefill(fp)) goto input_failure; if (*fp->_p != c) goto match_failure; fp->_r--, fp->_p++; nread++; continue; case '*': flags |= SUPPRESS; goto again; case 'j': flags |= MAXINT; goto again; case 'L': flags |= LONGDBL; goto again; case 'h': if (*fmt == 'h') { fmt++; flags |= SHORTSHORT; } else { flags |= SHORT; } goto again; case 'l': if (*fmt == 'l') { fmt++; flags |= LLONG; } else { flags |= LONG; } goto again; case 'q': flags |= LLONG; /* deprecated */ goto again; case 't': flags |= PTRINT; goto again; case 'z': flags |= SIZEINT; goto again; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': width = width * 10 + c - '0'; goto again; /* * Conversions. * Those marked `compat' are for 4.[123]BSD compatibility. * * (According to ANSI, E and X formats are supposed * to the same as e and x. Sorry about that.) */ case 'D': /* compat */ flags |= LONG; /* FALLTHROUGH */ case 'd': c = CT_INT; base = 10; break; case 'i': c = CT_INT; base = 0; break; case 'O': /* compat */ flags |= LONG; /* FALLTHROUGH */ case 'o': c = CT_INT; flags |= UNSIGNED; base = 8; break; case 'u': c = CT_INT; flags |= UNSIGNED; base = 10; break; case 'X': case 'x': flags |= PFXOK; /* enable 0x prefixing */ c = CT_INT; flags |= UNSIGNED; base = 16; break; #ifdef FLOATING_POINT case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': case 'a': case 'A': c = CT_FLOAT; break; #endif case 's': c = CT_STRING; break; case '[': fmt = __sccl(ccltab, fmt); flags |= NOSKIP; c = CT_CCL; break; case 'c': flags |= NOSKIP; c = CT_CHAR; break; case 'p': /* pointer format is like hex */ flags |= POINTER | PFXOK; c = CT_INT; flags |= UNSIGNED; base = 16; break; case 'n': if (flags & SUPPRESS) continue; if (flags & SHORTSHORT) *va_arg(ap, signed char *) = nread; else if (flags & SHORT) *va_arg(ap, short *) = nread; else if (flags & LONG) *va_arg(ap, long *) = nread; else if (flags & SIZEINT) *va_arg(ap, ssize_t *) = nread; else if (flags & PTRINT) *va_arg(ap, ptrdiff_t *) = nread; else if (flags & LLONG) *va_arg(ap, long long *) = nread; else if (flags & MAXINT) *va_arg(ap, intmax_t *) = nread; else *va_arg(ap, int *) = nread; continue; /* * Disgusting backwards compatibility hacks. XXX */ case '\0': /* compat */ return (EOF); default: /* compat */ if (isupper(c)) flags |= LONG; c = CT_INT; base = 10; break; } /* * We have a conversion that requires input. */ if (fp->_r <= 0 && __srefill(fp)) goto input_failure; /* * Consume leading white space, except for formats * that suppress this. */ if ((flags & NOSKIP) == 0) { while (isspace(*fp->_p)) { nread++; if (--fp->_r > 0) fp->_p++; else if (__srefill(fp)) goto input_failure; } /* * Note that there is at least one character in * the buffer, so conversions that do not set NOSKIP * ca no longer result in an input failure. */ } /* * Do the conversion. */ switch (c) { case CT_CHAR: /* scan arbitrary characters (sets NOSKIP) */ if (width == 0) width = 1; #ifdef SCANF_WIDE_CHAR if (flags & LONG) { if ((flags & SUPPRESS) == 0) wcp = va_arg(ap, wchar_t *); else wcp = NULL; n = 0; while (width != 0) { if (n == MB_CUR_MAX) { fp->_flags |= __SERR; goto input_failure; } buf[n++] = *fp->_p; fp->_p++; fp->_r--; bzero(&mbs, sizeof(mbs)); nconv = mbrtowc(wcp, buf, n, &mbs); if (nconv == (size_t)-1) { fp->_flags |= __SERR; goto input_failure; } if (nconv == 0 && !(flags & SUPPRESS)) *wcp = L'\0'; if (nconv != (size_t)-2) { nread += n; width--; if (!(flags & SUPPRESS)) wcp++; n = 0; } if (fp->_r <= 0 && __srefill(fp)) { if (n != 0) { fp->_flags |= __SERR; goto input_failure; } break; } } if (!(flags & SUPPRESS)) nassigned++; } else #endif /* SCANF_WIDE_CHAR */ if (flags & SUPPRESS) { size_t sum = 0; for (;;) { if ((n = fp->_r) < width) { sum += n; width -= n; fp->_p += n; if (__srefill(fp)) { if (sum == 0) goto input_failure; break; } } else { sum += width; fp->_r -= width; fp->_p += width; break; } } nread += sum; } else { size_t r = fread((void *)va_arg(ap, char *), 1, width, fp); if (r == 0) goto input_failure; nread += r; nassigned++; } break; case CT_CCL: /* scan a (nonempty) character class (sets NOSKIP) */ if (width == 0) width = (size_t)~0; /* `infinity' */ #ifdef SCANF_WIDE_CHAR /* take only those things in the class */ if (flags & LONG) { wchar_t twc; int nchars; if ((flags & SUPPRESS) == 0) wcp = va_arg(ap, wchar_t *); else
/* * __svfscanf - non-MT-safe version of __vfscanf */ int __svfscanf(FILE* fp, const char* fmt0, va_list ap) { const u_char* fmt = (const u_char*)fmt0; int c; /* character from format, or conversion */ size_t width; /* field width, or 0 */ char* p; /* points into all kinds of strings */ int n; /* handy integer */ int flags; /* flags as defined above */ char* p0; /* saves original value of p when necessary */ int nassigned; /* number of fields assigned */ int nconversions; /* number of conversions */ int nread; /* number of characters consumed from fp */ int base; /* base argument to conversion function */ char ccltab[256]; /* character class table for %[...] */ char buf[BUF]; /* buffer for numeric and mb conversions */ wchar_t* wcp; /* handy wide character pointer */ size_t nconv; /* length of multibyte sequence converted */ static const mbstate_t initial; mbstate_t mbs; /* `basefix' is used to avoid `if' tests in the integer scanner */ static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; ORIENT(fp, -1); nassigned = 0; nconversions = 0; nread = 0; for (;;) { c = *fmt++; if (c == 0) { return (nassigned); } if (isspace(c)) { while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p)) { nread++, fp->_r--, fp->_p++; } continue; } if (c != '%') { goto literal; } width = 0; flags = 0; /* * switch on the format. continue if done; * break once format type is derived. */ again: c = *fmt++; switch (c) { case '%': literal: if (fp->_r <= 0 && __srefill(fp)) { goto input_failure; } if (*fp->_p != c) { goto match_failure; } fp->_r--, fp->_p++; nread++; continue; case '*': flags |= SUPPRESS; goto again; case 'j': flags |= INTMAXT; goto again; case 'l': if (flags & LONG) { flags &= ~LONG; flags |= LONGLONG; } else { flags |= LONG; } goto again; case 'q': flags |= LONGLONG; /* not quite */ goto again; case 't': flags |= PTRDIFFT; goto again; case 'z': flags |= SIZET; goto again; case 'L': flags |= LONGDBL; goto again; case 'h': if (flags & SHORT) { flags &= ~SHORT; flags |= SHORTSHORT; } else { flags |= SHORT; } goto again; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': width = width * 10 + c - '0'; goto again; /* * Conversions. */ case 'd': c = CT_INT; base = 10; break; case 'i': c = CT_INT; base = 0; break; case 'o': c = CT_INT; flags |= UNSIGNED; base = 8; break; case 'u': c = CT_INT; flags |= UNSIGNED; base = 10; break; case 'X': case 'x': flags |= PFXOK; /* enable 0x prefixing */ c = CT_INT; flags |= UNSIGNED; base = 16; break; #ifndef NO_FLOATING_POINT case 'A': case 'E': case 'F': case 'G': case 'a': case 'e': case 'f': case 'g': c = CT_FLOAT; break; #endif case 'S': flags |= LONG; /* FALLTHROUGH */ case 's': c = CT_STRING; break; case '[': fmt = __sccl(ccltab, fmt); flags |= NOSKIP; c = CT_CCL; break; case 'C': flags |= LONG; /* FALLTHROUGH */ case 'c': flags |= NOSKIP; c = CT_CHAR; break; case 'p': /* pointer format is like hex */ flags |= POINTER | PFXOK; c = CT_INT; /* assumes sizeof(uintmax_t) */ flags |= UNSIGNED; /* >= sizeof(uintptr_t) */ base = 16; break; case 'n': nconversions++; if (flags & SUPPRESS) { /* ??? */ continue; } if (flags & SHORTSHORT) { *va_arg(ap, char*) = nread; } else if (flags & SHORT) { *va_arg(ap, short*) = nread; } else if (flags & LONG) { *va_arg(ap, long*) = nread; } else if (flags & LONGLONG) { *va_arg(ap, long long*) = nread; } else if (flags & INTMAXT) { *va_arg(ap, intmax_t*) = nread; } else if (flags & SIZET) { *va_arg(ap, size_t*) = nread; } else if (flags & PTRDIFFT) { *va_arg(ap, ptrdiff_t*) = nread; } else { *va_arg(ap, int*) = nread; } continue; default: goto match_failure; /* * Disgusting backwards compatibility hack. XXX */ case '\0': /* compat */ return (EOF); } /* * We have a conversion that requires input. */ if (fp->_r <= 0 && __srefill(fp)) { goto input_failure; } /* * Consume leading white space, except for formats * that suppress this. */ if ((flags & NOSKIP) == 0) { while (isspace(*fp->_p)) { nread++; if (--fp->_r > 0) { fp->_p++; } else if (__srefill(fp)) { goto input_failure; } } /* * Note that there is at least one character in * the buffer, so conversions that do not set NOSKIP * ca no longer result in an input failure. */ } /* * Do the conversion. */ switch (c) { case CT_CHAR: /* scan arbitrary characters (sets NOSKIP) */ if (width == 0) { width = 1; } if (flags & LONG) { if ((flags & SUPPRESS) == 0) { wcp = va_arg(ap, wchar_t*); } else { wcp = NULL; } n = 0; while (width != 0) { if (n == MB_CUR_MAX) { fp->_flags |= __SERR; goto input_failure; } buf[n++] = *fp->_p; fp->_p++; fp->_r--; mbs = initial; nconv = mbrtowc(wcp, buf, n, &mbs); if (nconv == (size_t) - 1) { fp->_flags |= __SERR; goto input_failure; } if (nconv == 0 && !(flags & SUPPRESS)) { *wcp = L'\0'; } if (nconv != (size_t) - 2) { nread += n; width--; if (!(flags & SUPPRESS)) { wcp++; } n = 0; } if (fp->_r <= 0 && __srefill(fp)) { if (n != 0) { fp->_flags |= __SERR; goto input_failure; } break; } } if (!(flags & SUPPRESS)) { nassigned++; } } else if (flags & SUPPRESS) {
/* * vfscanf */ NA_EIDPROC Int32 __svfscanf(SCANBUF* fp, NAWchar const* fmt0, va_list ap) { register NAWchar *fmt = (NAWchar *)fmt0; register Int32 c; /* character from format, or conversion */ register UInt32 width; /* field width, or 0 */ register NAWchar *p; /* points into all kinds of strings */ register UInt32 n; /* handy integer */ register Int32 flags; /* flags as defined above */ register NAWchar *p0; /* saves original value of p when necessary */ Int32 nassigned; /* number of fields assigned */ Int32 nread; /* number of characters consumed from fp */ Int32 base; /* base argument to strtoq/strtouq */ ccfnT ccfn; /* conversion function (strtoq/strtouq) */ NAWchar ccltab[256]; /* character class table for %[...] */ NAWchar buf[BUF]; /* buffer for numeric conversions */ char sb_buf[BUF]; /* single byte buffer for numeric conversions */ /* `basefix' is used to avoid `if' tests in the integer scanner */ /* static */ short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; nassigned = 0; nread = 0; base = 0; /* XXX just to keep gcc happy */ ccfn = NULL; for (;;) { c = *fmt++; if (c == 0) return (nassigned); #pragma nowarn(1506) // warning elimination if (na_iswspace(c)) { #pragma warn(1506) // warning elimination for (;;) { if (fp->_r <= 0 && __srefill(fp)) return (nassigned); if (!na_iswspace(*fp->_p)) break; nread++, fp->_r--, fp->_p++; } continue; } if (c != L'%') goto literal; width = 0; flags = 0; /* * switch on the format. continue if done; * break once format type is derived. */ again: c = *fmt++; switch (c) { case L'%': literal: if (fp->_r <= 0 && __srefill(fp)) goto input_failure; if (*fp->_p != c) goto match_failure; fp->_r--, fp->_p++; nread++; continue; case L'*': flags |= SUPPRESS; goto again; case L'L': flags |= LONGDBL; goto again; case L'h': flags |= SHORT; goto again; case L'l': if (*fmt == L'l') { fmt++; flags |= QUAD; } else { flags |= LONG; } goto again; case L'q': flags |= QUAD; goto again; case L'0': case L'1': case L'2': case L'3': case L'4': case L'5': case L'6': case L'7': case L'8': case L'9': width = width * 10 + c - L'0'; goto again; /* * Conversions. * Those marked `compat' are for 4.[123]BSD compatibility. * * (According to ANSI, E and X formats are supposed * to the same as e and x. Sorry about that.) */ case L'D': /* compat */ flags |= LONG; /* FALLTHROUGH */ case L'd': c = CT_INT; if (flags & QUAD) ccfn = (ccfnT)na_wcstoll; else ccfn = (ccfnT)na_wcstol; base = 10; break; case L'i': c = CT_INT; ccfn = (ccfnT)na_wcstol; base = 0; break; case L'O': /* compat */ flags |= LONG; /* FALLTHROUGH */ case L'o': c = CT_INT; ccfn = (ccfnT)na_wcstol; base = 8; break; case L'u': c = CT_INT; ccfn = (ccfnT)na_wcstol; base = 10; break; case L'X': case L'x': flags |= PFXOK; /* enable 0x prefixing */ c = CT_INT; ccfn = (ccfnT)na_wcstol; base = 16; break; #ifdef FLOATING_POINT case L'E': case L'G': case L'e': case L'f': case L'g': c = CT_FLOAT; break; #endif case L's': c = CT_STRING; break; case L'[': fmt = __sccl(ccltab, fmt); flags |= NOSKIP; c = CT_CCL; break; case L'c': flags |= NOSKIP; c = CT_CHAR; break; case L'p': /* pointer format is like hex */ flags |= POINTER | PFXOK; c = CT_INT; ccfn = (ccfnT)na_wcstol; base = 16; break; case L'n': if (flags & SUPPRESS) /* ??? */ continue; if (flags & SHORT) #pragma nowarn(1506) // warning elimination *va_arg(ap, short *) = nread; #pragma warn(1506) // warning elimination else if (flags & LONG) *va_arg(ap, Lng32 *) = nread; else *va_arg(ap, Int32 *) = nread; continue; /* * Disgusting backwards compatibility hacks. XXX */ case L'\0': /* compat */ //return (EOF); return -1; default: /* compat */ if ( L'A' <= c && c <= L'Z' ) flags |= LONG; c = CT_INT; ccfn = (ccfnT)na_wcstol; base = 10; break; } /* * We have a conversion that requires input. */ if (fp->_r <= 0 && __srefill(fp)) goto input_failure; /* * Consume leading white space, except for formats * that suppress this. */ if ((flags & NOSKIP) == 0) { while (na_iswspace(*fp->_p)) { nread++; if (--fp->_r > 0) fp->_p++; else if (__srefill(fp)) goto input_failure; } /* * Note that there is at least one character in * the buffer, so conversions that do not set NOSKIP * ca no longer result in an input failure. */ } /* * Do the conversion. */ switch (c) { case CT_CHAR: /* scan arbitrary characters (sets NOSKIP) */ if (width == 0) width = 1; if (flags & SUPPRESS) { size_t sum = 0; for (;;) { if ((n = fp->_r) < width) { sum += n; width -= n; fp->_p += n; if (__srefill(fp)) { if (sum == 0) goto input_failure; break; } } else { sum += width; fp->_r -= width; fp->_p += width; break; } } nread += sum; } else { //size_t r = fread((void *)va_arg(ap, char *), 1, // width, fp); size_t r; if ( fp->_r >= width ) { na_wcsncpy((NAWchar*)va_arg(ap, NAWchar *), (NAWchar*)fp->_ptr, width); r = width; } else r = 0; if (r == 0) goto input_failure; nread += r; nassigned++; } break; case CT_CCL: /* scan a (nonempty) character class (sets NOSKIP) */ if (width == 0) width = ~0; /* `infinity' */ /* take only those things in the class */ if (flags & SUPPRESS) { n = 0; while (ccltab[*fp->_p]) { n++, fp->_r--, fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) { if (n == 0) goto input_failure; break; } } if (n == 0) goto match_failure; } else { p0 = p = va_arg(ap, NAWchar *); while (ccltab[*fp->_p]) { fp->_r--; *p++ = *fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) { if (p == p0) goto input_failure; break; } } n = p - p0; if (n == 0) goto match_failure; *p = 0; nassigned++; } nread += n; break; case CT_STRING: /* like CCL, but zero-length string OK, & no NOSKIP */ if (width == 0) width = ~0; if (flags & SUPPRESS) { n = 0; while (!na_iswspace(*fp->_p)) { n++, fp->_r--, fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) break; } nread += n; } else { p0 = p = va_arg(ap, NAWchar *); while (!na_iswspace(*fp->_p)) { fp->_r--; *p++ = *fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) break; } *p = 0; nread += p - p0; nassigned++; } continue; case CT_INT: /* scan an integer as if by strtoq/strtouq */ #ifdef hardway if (width == 0 || width > sizeof(buf) - 1) width = sizeof(buf) - 1; #else /* size_t is unsigned, hence this optimisation */ if (--width > sizeof(buf) - 2) width = sizeof(buf) - 2; width++; #endif flags |= SIGNOK | NDIGITS | NZDIGITS; for (p = buf; width; width--) { c = *fp->_p; /* * Switch on the character; `goto ok' * if we accept it as a part of number. */ switch (c) { /* * The digit 0 is always legal, but is * special. For %i conversions, if no * digits (zero or nonzero) have been * scanned (only signs), we will have * base==0. In that case, we should set * it to 8 and enable 0x prefixing. * Also, if we have not scanned zero digits * before this, do not turn off prefixing * (someone else will turn it off if we * have scanned any nonzero digits). */ case L'0': if (base == 0) { base = 8; flags |= PFXOK; } if (flags & NZDIGITS) flags &= ~(SIGNOK|NZDIGITS|NDIGITS); else flags &= ~(SIGNOK|PFXOK|NDIGITS); goto ok; /* 1 through 7 always legal */ case L'1': case L'2': case L'3': case L'4': case L'5': case L'6': case L'7': base = basefix[base]; flags &= ~(SIGNOK | PFXOK | NDIGITS); goto ok; /* digits 8 and 9 ok iff decimal or hex */ case L'8': case L'9': base = basefix[base]; if (base <= 8) break; /* not legal here */ flags &= ~(SIGNOK | PFXOK | NDIGITS); goto ok; /* letters ok iff hex */ case L'A': case L'B': case L'C': case L'D': case L'E': case L'F': case L'a': case L'b': case L'c': case L'd': case L'e': case L'f': /* no need to fix base here */ if (base <= 10) break; /* not legal here */ flags &= ~(SIGNOK | PFXOK | NDIGITS); goto ok; /* sign ok only as first character */ case L'+': case L'-': if (flags & SIGNOK) { flags &= ~SIGNOK; goto ok; } break; /* x ok iff flag still set & 2nd char */ case L'x': case L'X': if (flags & PFXOK && p == buf + 1) { base = 16; /* if %i */ flags &= ~PFXOK; goto ok; } break; } /* * If we got here, c is not a legal character * for a number. Stop accumulating digits. */ break; ok: /* * c is legal: store it and look at the next. */ #pragma nowarn(1506) // warning elimination *p++ = c; #pragma warn(1506) // warning elimination if (--fp->_r > 0) fp->_p++; else if (__srefill(fp)) break; /* EOF */ } /* * If we had only a sign, it is no good; push * back the sign. If the number ends in `x', * it was [sign] '0' 'x', so push back the x * and treat it as [sign] '0'. */ if (flags & NDIGITS) { if (p > buf) (void) ungetc(*(NAWchar *)--p, fp); goto match_failure; } c = ((NAWchar *)p)[-1]; if (c == L'x' || c == L'X') { --p; #pragma nowarn(1506) // warning elimination (void) ungetc(c, fp); #pragma warn(1506) // warning elimination } if ((flags & SUPPRESS) == 0) { u_quad_t res = NULL; *p = 0; if ( ccfn ) res = (*ccfn)(buf); // only 10 based //res = (*ccfn)(buf); if (flags & POINTER) *va_arg(ap, void **) = (void *)(Lng32)res; else if (flags & QUAD) #pragma nowarn(1506) // warning elimination *va_arg(ap, quad_t *) = res; #pragma warn(1506) // warning elimination else if (flags & LONG) #pragma nowarn(1506) // warning elimination *va_arg(ap, Lng32 *) = (Lng32) res; #pragma warn(1506) // warning elimination else if (flags & SHORT) *va_arg(ap, Int16 *) = (Int16) res; else #pragma nowarn(1506) // warning elimination *va_arg(ap, Int32 *) = (Int32) res; #pragma warn(1506) // warning elimination nassigned++; } nread += p - buf; break; #ifdef FLOATING_POINT case CT_FLOAT: /* scan a floating point number as if by strtod */ #ifdef hardway if (width == 0 || width > sizeof(buf) - 1) width = sizeof(buf) - 1; #else /* size_t is unsigned, hence this optimisation */ if (--width > sizeof(buf) - 2) width = sizeof(buf) - 2; width++; #endif flags |= SIGNOK | NDIGITS | DPTOK | EXPOK; char* q = sb_buf; for (p = buf; width; width--) { c = *fp->_p; /* * This code mimicks the integer conversion * code, but is much simpler. */ switch (c) { case L'0': case L'1': case L'2': case L'3': case L'4': case L'5': case L'6': case L'7': case L'8': case L'9': flags &= ~(SIGNOK | NDIGITS); goto fok; case L'+': case L'-': if (flags & SIGNOK) { flags &= ~SIGNOK; goto fok; } break; case L'.': if (flags & DPTOK) { flags &= ~(SIGNOK | DPTOK); goto fok; } break; case L'e': case L'E': /* no exponent without some digits */ if ((flags&(NDIGITS|EXPOK)) == EXPOK) { flags = (flags & ~(EXPOK|DPTOK)) | SIGNOK | NDIGITS; goto fok; } break; } break; fok: #pragma nowarn(1506) // warning elimination *p++ = c; *q++ = (char)c; #pragma warn(1506) // warning elimination if (--fp->_r > 0) fp->_p++; else if (__srefill(fp)) break; /* EOF */ } /* * If no digits, might be missing exponent digits * (just give back the exponent) or might be missing * regular digits, but had sign and/or decimal point. */ if (flags & NDIGITS) { if (flags & EXPOK) { /* no digits at all */ while (p > buf) ungetc(*(NAWchar *)--p, fp); goto match_failure; } /* just a bad exponent (e and maybe sign) */ c = *(NAWchar *)--p; if (c != L'e' && c != L'E') { #pragma nowarn(1506) // warning elimination (void) ungetc(c, fp);/* sign */ #pragma warn(1506) // warning elimination c = *(NAWchar *)--p; } #pragma nowarn(1506) // warning elimination (void) ungetc(c, fp); #pragma warn(1506) // warning elimination } if ((flags & SUPPRESS) == 0) { double res; *p = 0; //res = na_wcstod(buf, (NAWchar **) NULL); res = strtod(sb_buf, NULL); if (flags & LONGDBL) #pragma nowarn(1255) // warning elimination *va_arg(ap, long double *) = res; #pragma warn(1255) // warning elimination else if (flags & LONG) *va_arg(ap, double *) = res; else *va_arg(ap, float *) = (float) res; nassigned++; } nread += p - buf; break; #endif /* FLOATING_POINT */ }
/* * vfscanf */ int VFSCANF(FILE *fp, const char *fmt0, __va_list ap) { u_char *fmt = (u_char *)fmt0; int c; /* character from format, or conversion */ size_t width; /* field width, or 0 */ char *p; /* points into all kinds of strings */ int n; /* handy integer */ int flags; /* flags as defined above */ char *p0; /* saves original value of p when necessary */ int nassigned; /* number of fields assigned */ int nread; /* number of characters consumed from fp */ int base; /* base argument to strtoimax/strtouimax */ char ccltab[256]; /* character class table for %[...] */ char buf[BUF]; /* buffer for numeric conversions */ /* `basefix' is used to avoid `if' tests in the integer scanner */ static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; FLOCKFILE(fp); _SET_ORIENTATION(fp, -1); nassigned = 0; nread = 0; base = 0; /* XXX just to keep gcc happy */ for (;;) { c = *fmt++; if (c == 0) { FUNLOCKFILE(fp); return (nassigned); } if (isspace(c)) { while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p)) nread++, fp->_r--, fp->_p++; continue; } if (c != '%') goto literal; width = 0; flags = 0; /* * switch on the format. continue if done; * break once format type is derived. */ again: c = *fmt++; switch (c) { case '%': literal: if (fp->_r <= 0 && __srefill(fp)) goto input_failure; if (*fp->_p != c) goto match_failure; fp->_r--, fp->_p++; nread++; continue; case '*': flags |= SUPPRESS; goto again; case 'j': flags |= MAXINT; goto again; case 'L': flags |= (*fmt == 'd') ? LLONG : (*fmt == 'i') ? LLONG : (*fmt == 'o') ? LLONG : (*fmt == 'u') ? LLONG : (*fmt == 'x') ? LLONG : LONGDBL; goto again; case 'h': if (*fmt == 'h') { fmt++; flags |= SHORTSHORT; } else { flags |= SHORT; } goto again; case 'l': if (*fmt == 'l') { fmt++; flags |= LLONG; } else { flags |= LONG; } goto again; case 'q': flags |= LLONG; /* deprecated */ goto again; case 't': flags |= PTRINT; goto again; case 'z': flags |= SIZEINT; goto again; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': width = width * 10 + c - '0'; goto again; /* * Conversions. * Those marked `compat' are for 4.[123]BSD compatibility. * * (According to ANSI, E and X formats are supposed * to the same as e and x. Sorry about that.) */ case 'D': /* compat */ flags |= LONG; /* FALLTHROUGH */ case 'd': c = CT_INT; base = 10; break; case 'i': c = CT_INT; base = 0; break; case 'O': /* compat */ flags |= LONG; /* FALLTHROUGH */ case 'o': c = CT_INT; flags |= UNSIGNED; base = 8; break; case 'u': c = CT_INT; flags |= UNSIGNED; base = 10; break; case 'X': case 'x': flags |= PFXOK; /* enable 0x prefixing */ c = CT_INT; flags |= UNSIGNED; base = 16; break; #ifdef FLOATING_POINT case 'E': case 'G': case 'e': case 'f': case 'g': c = CT_FLOAT; break; #endif case 's': c = CT_STRING; break; case '[': fmt = __sccl(ccltab, fmt); flags |= NOSKIP; c = CT_CCL; break; case 'c': flags |= NOSKIP; c = CT_CHAR; break; case 'p': /* pointer format is like hex */ flags |= POINTER | PFXOK; c = CT_INT; flags |= UNSIGNED; base = 16; break; case 'n': if (flags & SUPPRESS) continue; if (flags & SHORTSHORT) *va_arg(ap, __signed char *) = nread; else if (flags & SHORT) *va_arg(ap, short *) = nread; else if (flags & LONG) *va_arg(ap, long *) = nread; else if (flags & SIZEINT) *va_arg(ap, ssize_t *) = nread; else if (flags & PTRINT) *va_arg(ap, ptrdiff_t *) = nread; else if (flags & LLONG) *va_arg(ap, long long *) = nread; else if (flags & MAXINT) *va_arg(ap, intmax_t *) = nread; else *va_arg(ap, int *) = nread; continue; /* * Disgusting backwards compatibility hacks. XXX */ case '\0': /* compat */ FUNLOCKFILE(fp); return (EOF); default: /* compat */ if (isupper(c)) flags |= LONG; c = CT_INT; base = 10; break; } /* * We have a conversion that requires input. */ if (fp->_r <= 0 && __srefill(fp)) goto input_failure; /* * Consume leading white space, except for formats * that suppress this. */ if ((flags & NOSKIP) == 0) { while (isspace(*fp->_p)) { nread++; if (--fp->_r > 0) fp->_p++; else if (__srefill(fp)) goto input_failure; } /* * Note that there is at least one character in * the buffer, so conversions that do not set NOSKIP * ca no longer result in an input failure. */ } /* * Do the conversion. */ switch (c) { case CT_CHAR: /* scan arbitrary characters (sets NOSKIP) */ if (width == 0) width = 1; if (flags & SUPPRESS) { size_t sum = 0; for (;;) { if ((n = fp->_r) < (int)width) { sum += n; width -= n; fp->_p += n; if (__srefill(fp)) { if (sum == 0) goto input_failure; break; } } else { sum += width; fp->_r -= width; fp->_p += width; break; } } nread += sum; } else { size_t r = fread((void *)va_arg(ap, char *), 1, width, fp); if (r == 0) goto input_failure; nread += r; nassigned++; } break; case CT_CCL: /* scan a (nonempty) character class (sets NOSKIP) */ if (width == 0) width = (size_t)~0; /* `infinity' */ /* take only those things in the class */ if (flags & SUPPRESS) { n = 0; while (ccltab[*fp->_p]) { n++, fp->_r--, fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) { if (n == 0) goto input_failure; break; } } if (n == 0) goto match_failure; } else { p0 = p = va_arg(ap, char *); while (ccltab[*fp->_p]) { fp->_r--; *p++ = *fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) { if (p == p0) goto input_failure; break; } } n = p - p0; if (n == 0) goto match_failure; *p = '\0'; nassigned++; } nread += n; break; case CT_STRING: /* like CCL, but zero-length string OK, & no NOSKIP */ if (width == 0) width = (size_t)~0; if (flags & SUPPRESS) { n = 0; while (!isspace(*fp->_p)) { n++, fp->_r--, fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) break; } nread += n; } else { p0 = p = va_arg(ap, char *); while (!isspace(*fp->_p)) { fp->_r--; *p++ = *fp->_p++; if (--width == 0) break; if (fp->_r <= 0 && __srefill(fp)) break; } *p = '\0'; nread += p - p0; nassigned++; } continue; case CT_INT: /* scan an integer as if by strtoimax/strtoumax */ #ifdef hardway if (width == 0 || width > sizeof(buf) - 1) width = sizeof(buf) - 1; #else /* size_t is unsigned, hence this optimisation */ if (--width > sizeof(buf) - 2) width = sizeof(buf) - 2; width++; #endif flags |= SIGNOK | NDIGITS | NZDIGITS; for (p = buf; width; width--) { c = *fp->_p; /* * Switch on the character; `goto ok' * if we accept it as a part of number. */ switch (c) { /* * The digit 0 is always legal, but is * special. For %i conversions, if no * digits (zero or nonzero) have been * scanned (only signs), we will have * base==0. In that case, we should set * it to 8 and enable 0x prefixing. * Also, if we have not scanned zero digits * before this, do not turn off prefixing * (someone else will turn it off if we * have scanned any nonzero digits). */ case '0': if (base == 0) { base = 8; flags |= PFXOK; } if (flags & NZDIGITS) flags &= ~(SIGNOK|NZDIGITS|NDIGITS); else flags &= ~(SIGNOK|PFXOK|NDIGITS); goto ok; /* 1 through 7 always legal */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': base = basefix[base]; flags &= ~(SIGNOK | PFXOK | NDIGITS); goto ok; /* digits 8 and 9 ok iff decimal or hex */ case '8': case '9': base = basefix[base]; if (base <= 8) break; /* not legal here */ flags &= ~(SIGNOK | PFXOK | NDIGITS); goto ok; /* letters ok iff hex */ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': /* no need to fix base here */ if (base <= 10) break; /* not legal here */ flags &= ~(SIGNOK | PFXOK | NDIGITS); goto ok; /* sign ok only as first character */ case '+': case '-': if (flags & SIGNOK) { flags &= ~SIGNOK; flags |= HAVESIGN; goto ok; } break; /* * x ok iff flag still set and 2nd char (or * 3rd char if we have a sign). */ case 'x': case 'X': if ((flags & PFXOK) && p == buf + 1 + !!(flags & HAVESIGN)) { base = 16; /* if %i */ flags &= ~PFXOK; goto ok; } break; } /* * If we got here, c is not a legal character * for a number. Stop accumulating digits. */ break; ok: /* * c is legal: store it and look at the next. */ *p++ = c; if (--fp->_r > 0) fp->_p++; else if (__srefill(fp)) break; /* EOF */ } /* * If we had only a sign, it is no good; push * back the sign. If the number ends in `x', * it was [sign] '0' 'x', so push back the x * and treat it as [sign] '0'. */ if (flags & NDIGITS) { if (p > buf) (void) ungetc(*(u_char *)--p, fp); goto match_failure; } c = ((u_char *)p)[-1]; if (c == 'x' || c == 'X') { --p; (void) ungetc(c, fp); } if ((flags & SUPPRESS) == 0) { uintmax_t res; *p = '\0'; if (flags & UNSIGNED) res = strtoumax(buf, NULL, base); else res = strtoimax(buf, NULL, base); if (flags & POINTER) *va_arg(ap, void **) = (void *)(uintptr_t)res; else if (flags & MAXINT) *va_arg(ap, intmax_t *) = res; else if (flags & LLONG) *va_arg(ap, long long *) = res; else if (flags & SIZEINT) *va_arg(ap, ssize_t *) = res; else if (flags & PTRINT) *va_arg(ap, ptrdiff_t *) = res; else if (flags & LONG) *va_arg(ap, long *) = res; else if (flags & SHORT) *va_arg(ap, short *) = res; else if (flags & SHORTSHORT) *va_arg(ap, __signed char *) = res; else *va_arg(ap, int *) = res; nassigned++; } nread += p - buf; break; #ifdef FLOATING_POINT case CT_FLOAT: /* scan a floating point number as if by strtod */ #ifdef hardway if (width == 0 || width > sizeof(buf) - 1) width = sizeof(buf) - 1; #else /* size_t is unsigned, hence this optimisation */ if (--width > sizeof(buf) - 2) width = sizeof(buf) - 2; width++; #endif flags |= SIGNOK | NDIGITS | DPTOK | EXPOK; for (p = buf; width; width--) { c = *fp->_p; /* * This code mimicks the integer conversion * code, but is much simpler. */ switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': flags &= ~(SIGNOK | NDIGITS); goto fok; case '+': case '-': if (flags & SIGNOK) { flags &= ~SIGNOK; goto fok; } break; case '.': if (flags & DPTOK) { flags &= ~(SIGNOK | DPTOK); goto fok; } break; case 'e': case 'E': /* no exponent without some digits */ if ((flags&(NDIGITS|EXPOK)) == EXPOK) { flags = (flags & ~(EXPOK|DPTOK)) | SIGNOK | NDIGITS; goto fok; } break; } break; fok: *p++ = c; if (--fp->_r > 0) fp->_p++; else if (__srefill(fp)) break; /* EOF */ } /* * If no digits, might be missing exponent digits * (just give back the exponent) or might be missing * regular digits, but had sign and/or decimal point. */ if (flags & NDIGITS) { if (flags & EXPOK) { /* no digits at all */ while (p > buf) ungetc(*(u_char *)--p, fp); goto match_failure; } /* just a bad exponent (e and maybe sign) */ c = *(u_char *)--p; if (c != 'e' && c != 'E') { (void) ungetc(c, fp);/* sign */ c = *(u_char *)--p; } (void) ungetc(c, fp); } if ((flags & SUPPRESS) == 0) { double res; *p = '\0'; res = strtod(buf, (char **) NULL); if (flags & LONGDBL) *va_arg(ap, long double *) = res; else if (flags & LONG) *va_arg(ap, double *) = res; else *va_arg(ap, float *) = res; nassigned++; } nread += p - buf; break; #endif /* FLOATING_POINT */ } } input_failure: if (nassigned == 0) nassigned = -1; match_failure: FUNLOCKFILE(fp); return (nassigned); }
int streambuf::vscan(char const *fmt0, _G_va_list ap, ios *stream /* = NULL */) { register const u_char *fmt = (const u_char *)fmt0; register int c; /* character from format, or conversion */ register size_t width; /* field width, or 0 */ register char *p; /* points into all kinds of strings */ register int n; /* handy integer */ register int flags; /* flags as defined above */ register char *p0; /* saves original value of p when necessary */ int nassigned; /* number of fields assigned */ int nread; /* number of characters consumed from fp */ // Assignments to base and ccfn are just to suppress warnings from gcc. int base = 0; /* base argument to strtol/strtoul */ typedef u_long (*strtoulfn)(const char*, char**, int); strtoulfn ccfn = 0; // conversion function (strtol/strtoul) char ccltab[256]; /* character class table for %[...] */ char buf[BUF]; /* buffer for numeric conversions */ int seen_eof = 0; /* `basefix' is used to avoid `if' tests in the integer scanner */ static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; nassigned = 0; nread = 0; for (;;) { c = *fmt++; if (c == 0) goto done; if (isspace(c)) { for (;;) { c = sbumpc(); if (c == EOF) goto eof_failure; if (!isspace(c)) { sputbackc(c); break; } nread++; } continue; } if (c != '%') goto literal; width = 0; flags = 0; /* * switch on the format. continue if done; * break once format type is derived. */ again: c = *fmt++; switch (c) { case '%': literal: n = sbumpc(); if (n == EOF) goto eof_failure; if (n != c) { sputbackc(n); goto match_failure; } nread++; continue; case '*': flags |= SUPPRESS; goto again; case 'l': flags |= LONG; goto again; case 'L': flags |= LONGDBL; goto again; case 'h': flags |= SHORT; goto again; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': width = width * 10 + c - '0'; goto again; /* * Conversions. * Those marked `compat' are for 4.[123]BSD compatibility. * * (According to ANSI, E and X formats are supposed * to the same as e and x. Sorry about that.) */ case 'D': /* compat */ flags |= LONG; /* FALLTHROUGH */ case 'd': c = CT_INT; ccfn = (strtoulfn)strtol; base = 10; break; case 'i': c = CT_INT; ccfn = (strtoulfn)strtol; base = 0; break; case 'O': /* compat */ flags |= LONG; /* FALLTHROUGH */ case 'o': c = CT_INT; ccfn = strtoul; base = 8; break; case 'u': c = CT_INT; ccfn = strtoul; base = 10; break; case 'X': case 'x': flags |= PFXOK; /* enable 0x prefixing */ c = CT_INT; ccfn = strtoul; base = 16; break; #ifdef FLOATING_POINT case 'E': case 'F': case 'e': case 'f': case 'g': c = CT_FLOAT; break; #endif case 's': c = CT_STRING; break; case '[': fmt = __sccl(ccltab, fmt); flags |= NOSKIP; c = CT_CCL; break; case 'c': flags |= NOSKIP; c = CT_CHAR; break; case 'p': /* pointer format is like hex */ flags |= POINTER | PFXOK; c = CT_INT; ccfn = strtoul; base = 16; break; case 'n': if (flags & SUPPRESS) /* ??? */ continue; if (flags & SHORT) *va_arg(ap, short *) = nread; else if (flags & LONG) *va_arg(ap, long *) = nread; else *va_arg(ap, int *) = nread; continue; /* * Disgusting backwards compatibility hacks. XXX */ case '\0': /* compat */ nassigned = EOF; goto done; default: /* compat */ if (isupper(c)) flags |= LONG; c = CT_INT; ccfn = (strtoulfn)strtol; base = 10; break; } /* * We have a conversion that requires input. */ if (sgetc() == EOF) goto eof_failure; /* * Consume leading white space, except for formats * that suppress this. */ if ((flags & NOSKIP) == 0) { n = (unsigned char)*_gptr; while (isspace(n)) { _gptr++; nread++; n = sgetc(); if (n == EOF) goto eof_failure; } // Note that there is at least one character in // the buffer, so conversions that do not set NOSKIP // can no longer result in an input failure. } /* * Do the conversion. */ switch (c) { case CT_CHAR: /* scan arbitrary characters (sets NOSKIP) */ if (width == 0) // FIXME! width = 1; if (flags & SUPPRESS) { size_t sum = 0; for (;;) { if ((n = _egptr - _gptr) < (int)width) { sum += n; width -= n; _gptr += n; if (underflow() == EOF) if (sum == 0) goto eof_failure; else { seen_eof++; break; } } else { sum += width; _gptr += width; break; } } nread += sum; } else { size_t r = sgetn((char*)va_arg(ap, char*), width); if (r != width) goto eof_failure; nread += r; nassigned++; } break; case CT_CCL: /* scan a (nonempty) character class (sets NOSKIP) */ if (width == 0) width = ~0; /* `infinity' */ /* take only those things in the class */ if (flags & SUPPRESS) { n = 0; while (ccltab[(unsigned char)*_gptr]) { n++, _gptr++; if (--width == 0) break; if (sgetc() == EOF) { if (n == 0) goto eof_failure; seen_eof++; break; } } if (n == 0) goto match_failure; } else { p0 = p = va_arg(ap, char *); while (ccltab[(unsigned char)*_gptr]) { *p++ = *_gptr++; if (--width == 0) break; if (sgetc() == EOF) { if (p == p0) goto eof_failure; seen_eof++; break; } } n = p - p0; if (n == 0) goto match_failure; *p = 0; nassigned++; } nread += n; break; case CT_STRING: /* like CCL, but zero-length string OK, & no NOSKIP */ if (width == 0) width = ~0; if (flags & SUPPRESS) { n = 0; while (!isspace((unsigned char)*_gptr)) { n++, _gptr++; if (--width == 0) break; if (sgetc() == EOF) { seen_eof++; break; } } nread += n; } else { p0 = p = va_arg(ap, char *); while (!isspace((unsigned char)*_gptr)) { *p++ = *_gptr++; if (--width == 0) break; if (sgetc() == EOF) { seen_eof++; break; } } *p = 0; nread += p - p0; nassigned++; } continue; case CT_INT: /* scan an integer as if by strtol/strtoul */ if (width == 0 || width > sizeof(buf) - 1) width = sizeof(buf) - 1; flags |= SIGNOK | NDIGITS | NZDIGITS; for (p = buf; width; width--) { c = (unsigned char)*_gptr; /* * Switch on the character; `goto ok' * if we accept it as a part of number. */ switch (c) { /* * The digit 0 is always legal, but is * special. For %i conversions, if no * digits (zero or nonzero) have been * scanned (only signs), we will have * base==0. In that case, we should set * it to 8 and enable 0x prefixing. * Also, if we have not scanned zero digits * before this, do not turn off prefixing * (someone else will turn it off if we * have scanned any nonzero digits). */ case '0': if (base == 0) { base = 8; flags |= PFXOK; } if (flags & NZDIGITS) flags &= ~(SIGNOK|NZDIGITS|NDIGITS); else flags &= ~(SIGNOK|PFXOK|NDIGITS); goto ok; /* 1 through 7 always legal */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': base = basefix[base]; flags &= ~(SIGNOK | PFXOK | NDIGITS); goto ok; /* digits 8 and 9 ok iff decimal or hex */ case '8': case '9': base = basefix[base]; if (base <= 8) break; /* not legal here */ flags &= ~(SIGNOK | PFXOK | NDIGITS); goto ok; /* letters ok iff hex */ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': /* no need to fix base here */ if (base <= 10) break; /* not legal here */ flags &= ~(SIGNOK | PFXOK | NDIGITS); goto ok; /* sign ok only as first character */ case '+': case '-': if (flags & SIGNOK) { flags &= ~SIGNOK; goto ok; } break; /* x ok iff flag still set & 2nd char */ case 'x': case 'X': if (flags & PFXOK && p == buf + 1) { base = 16; /* if %i */ flags &= ~PFXOK; goto ok; } break; } /* * If we got here, c is not a legal character * for a number. Stop accumulating digits. */ break; ok: /* * c is legal: store it and look at the next. */ *p++ = c; _gptr++; if (sgetc() == EOF) { seen_eof++; break; /* EOF */ } } /* * If we had only a sign, it is no good; push * back the sign. If the number ends in `x', * it was [sign] '0' 'x', so push back the x * and treat it as [sign] '0'. */ if (flags & NDIGITS) { if (p > buf) (void) sputbackc(*(u_char *)--p); goto match_failure; } c = ((u_char *)p)[-1]; if (c == 'x' || c == 'X') { --p; (void) sputbackc(c); } if ((flags & SUPPRESS) == 0) { u_long res; *p = 0; res = (*ccfn)(buf, (char **)NULL, base); if (flags & POINTER) *va_arg(ap, void **) = (void *)res; else if (flags & SHORT) *va_arg(ap, short *) = res; else if (flags & LONG) *va_arg(ap, long *) = res; else *va_arg(ap, int *) = res; nassigned++; } nread += p - buf; break; #ifdef FLOATING_POINT case CT_FLOAT: /* scan a floating point number as if by strtod */ if (width == 0 || width > sizeof(buf) - 1) width = sizeof(buf) - 1; flags |= SIGNOK | NDIGITS | DPTOK | EXPOK; for (p = buf; width; width--) { c = (unsigned char)*_gptr; /* * This code mimicks the integer conversion * code, but is much simpler. */ switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': flags &= ~(SIGNOK | NDIGITS); goto fok; case '+': case '-': if (flags & SIGNOK) { flags &= ~SIGNOK; goto fok; } break; case '.': if (flags & DPTOK) { flags &= ~(SIGNOK | DPTOK); goto fok; } break; case 'e': case 'E': /* no exponent without some digits */ if ((flags&(NDIGITS|EXPOK)) == EXPOK) { flags = (flags & ~(EXPOK|DPTOK)) | SIGNOK | NDIGITS; goto fok; } break; } break; fok: *p++ = c; _gptr++; if (sgetc() == EOF) { seen_eof++; break; /* EOF */ } } /* * If no digits, might be missing exponent digits * (just give back the exponent) or might be missing * regular digits, but had sign and/or decimal point. */ if (flags & NDIGITS) { if (flags & EXPOK) { /* no digits at all */ while (p > buf) sputbackc(*(u_char *)--p); goto match_failure; } /* just a bad exponent (e and maybe sign) */ c = *(u_char *)--p; if (c != 'e' && c != 'E') { (void)sputbackc(c);/* sign */ c = *(u_char *)--p; } (void) sputbackc(c); } if ((flags & SUPPRESS) == 0) { double res; *p = 0; #ifdef USE_DTOA res = strtod(buf, NULL); #else res = atof(buf); #endif if (flags & LONG) *va_arg(ap, double *) = res; else *va_arg(ap, float *) = res; nassigned++; } nread += p - buf; break; #endif /* FLOATING_POINT */ } } eof_failure: seen_eof++; input_failure: if (nassigned == 0) nassigned = -1; match_failure: if (stream) stream->set(ios::failbit); done: if (stream && seen_eof) stream->set(ios::eofbit); return (nassigned); }