/*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ /* FUNCTION <>, <>, <>---format argument list INDEX vscanf INDEX vfscanf INDEX vsscanf ANSI_SYNOPSIS #include #include int vscanf(const char *<[fmt]>, va_list <[list]>); int vfscanf(FILE *<[fp]>, const char *<[fmt]>, va_list <[list]>); int vsscanf(const char *<[str]>, const char *<[fmt]>, va_list <[list]>); int _vscanf_r(struct _reent *<[reent]>, const char *<[fmt]>, va_list <[list]>); int _vfscanf_r(struct _reent *<[reent]>, FILE *<[fp]>, const char *<[fmt]>, va_list <[list]>); int _vsscanf_r(struct _reent *<[reent]>, const char *<[str]>, const char *<[fmt]>, va_list <[list]>); TRAD_SYNOPSIS #include #include int vscanf( <[fmt]>, <[ist]>) char *<[fmt]>; va_list <[list]>; int vfscanf( <[fp]>, <[fmt]>, <[list]>) FILE *<[fp]>; char *<[fmt]>; va_list <[list]>; int vsscanf( <[str]>, <[fmt]>, <[list]>) char *<[str]>; char *<[fmt]>; va_list <[list]>; int _vscanf_r( <[reent]>, <[fmt]>, <[ist]>) struct _reent *<[reent]>; char *<[fmt]>; va_list <[list]>; int _vfscanf_r( <[reent]>, <[fp]>, <[fmt]>, <[list]>) struct _reent *<[reent]>; FILE *<[fp]>; char *<[fmt]>; va_list <[list]>; int _vsscanf_r( <[reent]>, <[str]>, <[fmt]>, <[list]>) struct _reent *<[reent]>; char *<[str]>; char *<[fmt]>; va_list <[list]>; DESCRIPTION <>, <>, and <> are (respectively) variants of <>, <>, and <>. They differ only in allowing their caller to pass the variable argument list as a <> object (initialized by <>) rather than directly accepting a variable number of arguments. RETURNS The return values are consistent with the corresponding functions: <> returns the number of input fields successfully scanned, converted, and stored; the return value does not include scanned fields which were not stored. If <> attempts to read at end-of-file, the return value is <>. If no fields were stored, the return value is <<0>>. The routines <<_vscanf_r>>, <<_vfscanf_f>>, and <<_vsscanf_r>> are reentrant versions which take an additional first parameter which points to the reentrancy structure. PORTABILITY These are GNU extensions. Supporting OS subroutines required: */ #include <_ansi.h> #include #include #include #include #include #include #include #include #include #include #include #include #include "local.h" #ifdef INTEGER_ONLY #define VFSCANF vfiscanf #define _VFSCANF_R _vfiscanf_r #define __SVFSCANF __svfiscanf #define __SVFSCANF_R __svfiscanf_r #else #define VFSCANF vfscanf #define _VFSCANF_R _vfscanf_r #define __SVFSCANF __svfscanf #define __SVFSCANF_R __svfscanf_r #ifndef NO_FLOATING_POINT #define FLOATING_POINT #endif #endif #ifdef FLOATING_POINT #include #include /* Currently a test is made to see if long double processing is warranted. This could be changed in the future should the _ldtoa_r code be preferred over _dtoa_r. */ #define _NO_LONGDBL #if defined _WANT_IO_LONG_DOUBLE && (LDBL_MANT_DIG > DBL_MANT_DIG) #undef _NO_LONGDBL extern _LONG_DOUBLE _strtold _PARAMS((char *s, char **sptr)); #endif #include "floatio.h" #if ((MAXEXP+MAXFRACT+3) > MB_LEN_MAX) # define BUF (MAXEXP+MAXFRACT+3) /* 3 = sign + decimal point + NUL */ #else # define BUF MB_LEN_MAX #endif /* An upper bound for how long a long prints in decimal. 4 / 13 approximates log (2). Add one char for roundoff compensation and one for the sign. */ #define MAX_LONG_LEN ((CHAR_BIT * sizeof (long) - 1) * 4 / 13 + 2) #else #define BUF 40 #endif #define _NO_LONGLONG #if defined _WANT_IO_LONG_LONG \ && (defined __GNUC__ || __STDC_VERSION__ >= 199901L) # undef _NO_LONGLONG #endif #define _NO_POS_ARGS #ifdef _WANT_IO_POS_ARGS # undef _NO_POS_ARGS # ifdef NL_ARGMAX # define MAX_POS_ARGS NL_ARGMAX # else # define MAX_POS_ARGS 32 # endif static void * get_arg (int, va_list *, int *, void **); #endif /* _WANT_IO_POS_ARGS */ /* * Flags used during conversion. */ #define LONG 0x01 /* l: long or double */ #define LONGDBL 0x02 /* L/ll: long double or long long */ #define SHORT 0x04 /* h: short */ #define CHAR 0x08 /* hh: 8 bit integer */ #define SUPPRESS 0x10 /* suppress assignment */ #define POINTER 0x20 /* weird %p pointer (`fake hex') */ #define NOSKIP 0x40 /* do not skip blanks */ /* * The following are used in numeric conversions only: * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point; * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral. */ #define SIGNOK 0x80 /* +/- is (still) legal */ #define NDIGITS 0x100 /* no digits detected */ #define DPTOK 0x200 /* (float) decimal point is still legal */ #define EXPOK 0x400 /* (float) exponent (e+3, etc) still legal */ #define PFXOK 0x200 /* 0x prefix is (still) legal */ #define NZDIGITS 0x400 /* no zero digits detected */ #define NNZDIGITS 0x800 /* no non-zero digits detected */ /* * Conversion types. */ #define CT_CHAR 0 /* %c conversion */ #define CT_CCL 1 /* %[...] conversion */ #define CT_STRING 2 /* %s conversion */ #define CT_INT 3 /* integer, i.e., strtol or strtoul */ #define CT_FLOAT 4 /* floating, i.e., strtod */ #if 0 #define u_char unsigned char #endif #define u_char char #define u_long unsigned long #ifndef _NO_LONGLONG typedef unsigned long long u_long_long; #endif /*static*/ u_char *__sccl (); /* * vfscanf */ #define BufferEmpty (fp->_r <= 0 && __srefill_r(rptr, fp)) #ifndef _REENT_ONLY int _DEFUN(VFSCANF, (fp, fmt, ap), register FILE *fp _AND _CONST char *fmt _AND va_list ap) { CHECK_INIT(_REENT, fp); return __SVFSCANF_R (_REENT, fp, fmt, ap); } int _DEFUN(__SVFSCANF, (fp, fmt0, ap), register FILE *fp _AND char _CONST *fmt0 _AND va_list ap) { return __SVFSCANF_R (_REENT, fp, fmt0, ap); } #endif /* !_REENT_ONLY */ int _DEFUN(_VFSCANF_R, (data, fp, fmt, ap), struct _reent *data _AND register FILE *fp _AND _CONST char *fmt _AND va_list ap) { CHECK_INIT(data, fp); return __SVFSCANF_R (data, fp, fmt, ap); } int _DEFUN(__SVFSCANF_R, (rptr, fp, fmt0, ap), struct _reent *rptr _AND register FILE *fp _AND char _CONST *fmt0 _AND va_list ap) { register u_char *fmt = (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 */ #ifndef _NO_POS_ARGS int N; /* arg number */ int arg_index = 0; /* index into args processed directly */ int numargs = 0; /* number of varargs read */ void *args[MAX_POS_ARGS]; /* positional args read */ int is_pos_arg; /* is current format positional? */ #endif int base = 0; /* base argument to strtol/strtoul */ int nbytes = 1; /* number of bytes read from fmt string */ wchar_t wc; /* wchar to use to read format string */ wchar_t *wcp; /* handy wide character pointer */ size_t mbslen; /* length of converted multibyte sequence */ mbstate_t state; /* value to keep track of multibyte state */ u_long (*ccfn) () = 0; /* conversion function (strtol/strtoul) */ char ccltab[256]; /* character class table for %[...] */ char buf[BUF]; /* buffer for numeric conversions */ char *lptr; /* literal pointer */ char *cp; short *sp; int *ip; #ifdef FLOATING_POINT float *flp; _LONG_DOUBLE *ldp; double *dp; #endif long *lp; #ifndef _NO_LONGLONG long long *llp; #endif /* `basefix' is used to avoid `if' tests in the integer scanner */ static _CONST short basefix[17] = {10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; /* Macro to support positional arguments */ #ifndef _NO_POS_ARGS # define GET_ARG(n, ap, type) \ ((type) (is_pos_arg \ ? (n < numargs \ ? args[n] \ : get_arg (n, &ap, &numargs, args)) \ : (arg_index++ < numargs \ ? args[n] \ : (numargs < MAX_POS_ARGS \ ? args[numargs++] = va_arg (ap, void *) \ : va_arg (ap, void *))))) #else # define GET_ARG(n, ap, type) (va_arg (ap, type)) #endif _flockfile (fp); nassigned = 0; nread = 0; for (;;) { #ifndef _MB_CAPABLE wc = *fmt; #else memset (&state, '\0', sizeof (state)); nbytes = _mbtowc_r (rptr, &wc, fmt, MB_CUR_MAX, &state); #endif fmt += nbytes; if (wc == 0) goto all_done; if (nbytes == 1 && isspace (wc)) { for (;;) { if (BufferEmpty || !isspace (*fp->_p)) break; nread++, fp->_r--, fp->_p++; } continue; } if (wc != '%') goto literal; width = 0; flags = 0; #ifndef _NO_POS_ARGS N = arg_index; is_pos_arg = 0; #endif /* * switch on the format. continue if done; break once format * type is derived. */ again: c = *fmt++; switch (c) { case '%': literal: lptr = fmt - nbytes; for (n = 0; n < nbytes; ++n) { if (BufferEmpty) goto input_failure; if (*fp->_p != *lptr) goto match_failure; fp->_r--, fp->_p++; nread++; ++lptr; } continue; case '*': flags |= SUPPRESS; goto again; case 'l': #if defined _WANT_IO_C99_FORMATS || !defined _NO_LONGLONG if (*fmt == 'l') /* Check for 'll' = long long (SUSv3) */ { ++fmt; flags |= LONGDBL; } else #endif flags |= LONG; goto again; case 'L': flags |= LONGDBL; goto again; case 'h': #ifdef _WANT_IO_C99_FORMATS if (*fmt == 'h') /* Check for 'hh' = char int (SUSv3) */ { ++fmt; flags |= CHAR; } else #endif flags |= SHORT; goto again; #ifdef _WANT_IO_C99_FORMATS case 'j': /* intmax_t */ if (sizeof (intmax_t) == sizeof (long)) flags |= LONG; else flags |= LONGDBL; goto again; case 't': /* ptrdiff_t */ if (sizeof (ptrdiff_t) < sizeof (int)) /* POSIX states ptrdiff_t is 16 or more bits, as is short. */ flags |= SHORT; else if (sizeof (ptrdiff_t) == sizeof (int)) /* no flag needed */; else if (sizeof (ptrdiff_t) <= sizeof (long)) flags |= LONG; else /* POSIX states that at least one programming environment must support ptrdiff_t no wider than long, but that means other environments can have ptrdiff_t as wide as long long. */ flags |= LONGDBL; goto again; case 'z': /* size_t */ if (sizeof (size_t) < sizeof (int)) /* POSIX states size_t is 16 or more bits, as is short. */ flags |= SHORT; else if (sizeof (size_t) == sizeof (int)) /* no flag needed */; else if (sizeof (size_t) <= sizeof (long)) flags |= LONG; else /* POSIX states that at least one programming environment must support size_t no wider than long, but that means other environments can have size_t as wide as long long. */ flags |= LONGDBL; goto again; #endif /* _WANT_IO_C99_FORMATS */ 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; #ifndef _NO_POS_ARGS case '$': if (width <= MAX_POS_ARGS) { N = width - 1; is_pos_arg = 1; width = 0; goto again; } rptr->_errno = EINVAL; goto input_failure; #endif /* !_NO_POS_ARGS */ /* * 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 = (u_long (*)())_strtol_r; base = 10; break; case 'i': c = CT_INT; ccfn = (u_long (*)())_strtol_r; base = 0; break; case 'O': /* compat */ flags |= LONG; /* FALLTHROUGH */ case 'o': c = CT_INT; ccfn = _strtoul_r; base = 8; break; case 'u': c = CT_INT; ccfn = _strtoul_r; base = 10; break; case 'X': case 'x': flags |= PFXOK; /* enable 0x prefixing */ c = CT_INT; ccfn = _strtoul_r; base = 16; break; #ifdef FLOATING_POINT # ifdef _WANT_IO_C99_FORMATS case 'a': case 'A': case 'F': # endif case 'E': case 'G': case 'e': case 'f': case 'g': c = CT_FLOAT; break; #endif #ifdef _WANT_IO_C99_FORMATS case 'S': flags |= LONG; /* FALLTHROUGH */ #endif case 's': c = CT_STRING; break; case '[': fmt = __sccl (ccltab, fmt); flags |= NOSKIP; c = CT_CCL; break; #ifdef _WANT_IO_C99_FORMATS case 'C': flags |= LONG; /* FALLTHROUGH */ #endif case 'c': flags |= NOSKIP; c = CT_CHAR; break; case 'p': /* pointer format is like hex */ flags |= POINTER | PFXOK; c = CT_INT; ccfn = _strtoul_r; base = 16; break; case 'n': if (flags & SUPPRESS) /* ??? */ continue; #ifdef _WANT_IO_C99_FORMATS if (flags & CHAR) { cp = GET_ARG (N, ap, char *); *cp = nread; } else #endif if (flags & SHORT) { sp = GET_ARG (N, ap, short *); *sp = nread; } else if (flags & LONG) { lp = GET_ARG (N, ap, long *); *lp = nread; } #ifndef _NO_LONGLONG else if (flags & LONGDBL) { llp = GET_ARG (N, ap, long long*); *llp = nread; } #endif else { ip = GET_ARG (N, ap, int *); *ip = nread; } continue; /* * Disgusting backwards compatibility hacks. XXX */ case '\0': /* compat */ _funlockfile (fp); return EOF; default: /* compat */ if (isupper (c)) flags |= LONG; c = CT_INT; ccfn = (u_long (*)())_strtol_r; base = 10; break; } /* * We have a conversion that requires input. */ if (BufferEmpty) 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_r (rptr, 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 = GET_ARG (N, ap, wchar_t *); else wcp = NULL; n = 0; while (width != 0) { if (n == MB_CUR_MAX) goto input_failure; buf[n++] = *fp->_p; fp->_r -= 1; fp->_p += 1; memset ((_PTR)&state, '\0', sizeof (mbstate_t)); if ((mbslen = _mbrtowc_r (rptr, wcp, buf, n, &state)) == (size_t)-1) goto input_failure; /* Invalid sequence */ if (mbslen == 0 && !(flags & SUPPRESS)) *wcp = L'\0'; if (mbslen != (size_t)-2) /* Incomplete sequence */ { nread += n; width -= 1; if (!(flags & SUPPRESS)) wcp += 1; n = 0; } if (BufferEmpty) { if (n != 0) goto input_failure; break; } } if (!(flags & SUPPRESS)) nassigned++; } else if (flags & SUPPRESS) { size_t sum = 0; for (;;) { if ((n = fp->_r) < (int)width) { sum += n; width -= n; fp->_p += n; if (__srefill_r (rptr, 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 ((_PTR) GET_ARG (N, 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 = ~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 (BufferEmpty) { if (n == 0) goto input_failure; break; } } if (n == 0) goto match_failure; } else { p0 = p = GET_ARG (N, ap, char *); while (ccltab[*fp->_p]) { fp->_r--; *p++ = *fp->_p++; if (--width == 0) break; if (BufferEmpty) { 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 & LONG) { /* Process %S and %ls placeholders */ if ((flags & SUPPRESS) == 0) wcp = GET_ARG (N, ap, wchar_t *); else wcp = &wc; n = 0; while (!isspace (*fp->_p) && width != 0) { if (n == MB_CUR_MAX) goto input_failure; buf[n++] = *fp->_p; fp->_r -= 1; fp->_p += 1; memset ((_PTR)&state, '\0', sizeof (mbstate_t)); if ((mbslen = _mbrtowc_r (rptr, wcp, buf, n, &state)) == (size_t)-1) goto input_failure; if (mbslen == 0) *wcp = L'\0'; if (mbslen != (size_t)-2) /* Incomplete sequence */ { if (iswspace(*wcp)) { while (n != 0) ungetc (buf[--n], fp); break; } nread += n; width -= 1; if ((flags & SUPPRESS) == 0) wcp += 1; n = 0; } if (BufferEmpty) { if (n != 0) goto input_failure; break; } } if (!(flags & SUPPRESS)) { *wcp = L'\0'; nassigned++; } } else if (flags & SUPPRESS) { n = 0; while (!isspace (*fp->_p)) { n++, fp->_r--, fp->_p++; if (--width == 0) break; if (BufferEmpty) break; } nread += n; } else { p0 = p = GET_ARG (N, ap, char *); while (!isspace (*fp->_p)) { fp->_r--; *p++ = *fp->_p++; if (--width == 0) break; if (BufferEmpty) break; } *p = 0; nread += p - p0; nassigned++; } continue; case CT_INT: { /* scan an integer as if by strtol/strtoul */ unsigned width_left = 0; int skips = 0; #ifdef hardway if (width == 0 || width > sizeof (buf) - 1) #else /* size_t is unsigned, hence this optimisation */ if (width - 1 > sizeof (buf) - 2) #endif { width_left = width - (sizeof (buf) - 1); width = sizeof (buf) - 1; } flags |= SIGNOK | NDIGITS | NZDIGITS | NNZDIGITS; 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 (! (flags & NNZDIGITS)) goto ok; if (base == 0) { base = 8; flags |= PFXOK; } if (flags & NZDIGITS) { flags &= ~(SIGNOK | NZDIGITS | NDIGITS); goto ok; } flags &= ~(SIGNOK | PFXOK | NDIGITS); if (width_left) { width_left--; width++; } ++skips; goto skip; /* 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 | NNZDIGITS); 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 | NNZDIGITS); 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 | NNZDIGITS); 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; /* We must reset the NZDIGITS and NDIGITS flags that would have been unset by seeing the zero that preceded the X or x. */ flags |= NZDIGITS | NDIGITS; 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; skip: if (--fp->_r > 0) fp->_p++; else if (__srefill_r (rptr, 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) _CAST_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) { u_long res; *p = 0; res = (*ccfn) (rptr, buf, (char **) NULL, base); if (flags & POINTER) *(GET_ARG (N, ap, _PTR *)) = (_PTR) (unsigned _POINTER_INT) res; #ifdef _WANT_IO_C99_FORMATS else if (flags & CHAR) { cp = GET_ARG (N, ap, char *); *cp = res; } #endif else if (flags & SHORT) { sp = GET_ARG (N, ap, short *); *sp = res; } else if (flags & LONG) { lp = GET_ARG (N, ap, long *); *lp = res; } #ifndef _NO_LONGLONG else if (flags & LONGDBL) { u_long_long resll; if (ccfn == _strtoul_r) resll = _strtoull_r (rptr, buf, (char **) NULL, base); else resll = _strtoll_r (rptr, buf, (char **) NULL, base); llp = GET_ARG (N, ap, long long*); *llp = resll; } #endif else { ip = GET_ARG (N, ap, int *); *ip = res; } nassigned++; } nread += p - buf + skips; break; } #ifdef FLOATING_POINT case CT_FLOAT: { /* scan a floating point number as if by strtod */ /* This code used to assume that the number of digits is reasonable. However, ANSI / ISO C makes no such stipulation; we have to get exact results even when there is an unreasonable amount of leading zeroes. */ long leading_zeroes = 0; long zeroes, exp_adjust; char *exp_start = NULL; unsigned width_left = 0; int nancount = 0; #ifdef hardway if (width == 0 || width > sizeof (buf) - 1) #else /* size_t is unsigned, hence this optimisation */ if (width - 1 > sizeof (buf) - 2) #endif { width_left = width - (sizeof (buf) - 1); width = sizeof (buf) - 1; } flags |= SIGNOK | NDIGITS | DPTOK | EXPOK; zeroes = 0; exp_adjust = 0; for (p = buf; width; ) { c = *fp->_p; /* * This code mimicks the integer conversion * code, but is much simpler. */ switch (c) { case '0': if (flags & NDIGITS) { flags &= ~SIGNOK; zeroes++; if (width_left) { width_left--; width++; } goto fskip; } /* Fall through. */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (nancount == 0) { flags &= ~(SIGNOK | NDIGITS); goto fok; } break; case '+': case '-': if (flags & SIGNOK) { flags &= ~SIGNOK; goto fok; } break; case 'n': case 'N': if (nancount == 0 && (flags & (SIGNOK | NDIGITS | DPTOK | EXPOK)) == (SIGNOK | NDIGITS | DPTOK | EXPOK)) { flags &= ~(SIGNOK | DPTOK | EXPOK | NDIGITS); nancount = 1; goto fok; } else if (nancount == 2) { nancount = 3; goto fok; } break; case 'a': case 'A': if (nancount == 1) { nancount = 2; goto fok; } break; case '.': if (flags & DPTOK) { flags &= ~(SIGNOK | DPTOK); leading_zeroes = zeroes; goto fok; } break; case 'e': case 'E': /* no exponent without some digits */ if ((flags & (NDIGITS | EXPOK)) == EXPOK || ((flags & EXPOK) && zeroes)) { if (! (flags & DPTOK)) { exp_adjust = zeroes - leading_zeroes; exp_start = p; } flags = (flags & ~(EXPOK | DPTOK)) | SIGNOK | NDIGITS; zeroes = 0; goto fok; } break; } break; fok: *p++ = c; fskip: width--; ++nread; if (--fp->_r > 0) fp->_p++; else if (__srefill_r (rptr, fp)) break; /* EOF */ } if (zeroes) flags &= ~NDIGITS; /* We may have a 'N' or possibly even a 'Na' as the start of 'NaN', only to run out of chars before it was complete (or having encountered a non- matching char). So check here if we have an outstanding nancount, and if so put back the chars we did swallow and treat as a failed match. */ if (nancount && nancount != 3) { /* Ok... what are we supposed to do in the event that the __srefill call above was triggered in the middle of the partial 'NaN' and so we can't put it all back? */ while (nancount-- && (p > buf)) { ungetc (*(u_char *)--p, fp); --nread; } goto match_failure; } /* * 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); --nread; } goto match_failure; } /* just a bad exponent (e and maybe sign) */ c = *(u_char *)-- p; --nread; if (c != 'e' && c != 'E') { _CAST_VOID ungetc (c, fp); /* sign */ c = *(u_char *)-- p; --nread; } _CAST_VOID ungetc (c, fp); } if ((flags & SUPPRESS) == 0) { double res = 0; #ifdef _NO_LONGDBL #define QUAD_RES res; #else /* !_NO_LONG_DBL */ long double qres = 0; #define QUAD_RES qres; #endif /* !_NO_LONG_DBL */ long new_exp = 0; *p = 0; if ((flags & (DPTOK | EXPOK)) == EXPOK) { exp_adjust = zeroes - leading_zeroes; new_exp = -exp_adjust; exp_start = p; } else if (exp_adjust) new_exp = _strtol_r (rptr, (exp_start + 1), NULL, 10) - exp_adjust; if (exp_adjust) { /* If there might not be enough space for the new exponent, truncate some trailing digits to make room. */ if (exp_start >= buf + sizeof (buf) - MAX_LONG_LEN) exp_start = buf + sizeof (buf) - MAX_LONG_LEN - 1; sprintf (exp_start, "e%ld", new_exp); } /* Current _strtold routine is markedly slower than _strtod_r. Only use it if we have a long double result. */ #ifndef _NO_LONGDBL /* !_NO_LONGDBL */ if (flags & LONGDBL) qres = _strtold (buf, NULL); else #endif res = _strtod_r (rptr, buf, NULL); if (flags & LONG) { dp = GET_ARG (N, ap, double *); *dp = res; } else if (flags & LONGDBL) { ldp = GET_ARG (N, ap, _LONG_DOUBLE *); *ldp = QUAD_RES; } else { flp = GET_ARG (N, ap, float *); if (isnan (res)) *flp = nanf (NULL); else *flp = res; } nassigned++; } break; } #endif /* FLOATING_POINT */ } } input_failure: _funlockfile (fp); return nassigned ? nassigned : -1; match_failure: all_done: _funlockfile (fp); return nassigned; } #ifndef _NO_POS_ARGS /* Process all intermediate arguments. Fortunately, with scanf, all intermediate arguments are sizeof(void*), so we don't need to scan ahead in the format string. */ static void * get_arg (int n, va_list *ap, int *numargs_p, void **args) { int numargs = *numargs_p; while (n >= numargs) args[numargs++] = va_arg (*ap, void *); *numargs_p = numargs; return args[n]; } #endif /* !_NO_POS_ARGS */