📄 vfscanf.c
字号:
/* Copyright (C) 1991-2002, 2003, 2004 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */#include <assert.h>#include <errno.h>#include <limits.h>#include <ctype.h>#include <stdarg.h>#include <stdio.h>#include <stdint.h>#include <stdlib.h>#include <string.h>#include <wchar.h>#include <wctype.h>#include <bits/libc-lock.h>#include <locale/localeinfo.h>#ifdef __GNUC__# define HAVE_LONGLONG# define LONGLONG long long#else# define LONGLONG long#endif/* Determine whether we have to handle `long long' at all. */#if LONG_MAX == LONG_LONG_MAX# define need_longlong 0#else# define need_longlong 1#endif/* Determine whether we have to handle `long'. */#if INT_MAX == LONG_MAX# define need_long 0#else# define need_long 1#endif/* Those are flags in the conversion format. */#define LONG 0x001 /* l: long or double */#define LONGDBL 0x002 /* L: long long or long double */#define SHORT 0x004 /* h: short */#define SUPPRESS 0x008 /* *: suppress assignment */#define POINTER 0x010 /* weird %p pointer (`fake hex') */#define NOSKIP 0x020 /* do not skip blanks */#define WIDTH 0x040 /* width was given */#define GROUP 0x080 /* ': group numbers */#define MALLOC 0x100 /* a: malloc strings */#define CHAR 0x200 /* hh: char */#define I18N 0x400 /* I: use locale's digits */#include <locale/localeinfo.h>#include <libioP.h>#include <libio.h>#undef va_list#define va_list _IO_va_list#ifdef COMPILE_WSCANF# define ungetc(c, s) ((void) (c == WEOF \ || (--read_in, \ INTUSE(_IO_sputbackwc) (s, c))))# define ungetc_not_eof(c, s) ((void) (--read_in, \ INTUSE(_IO_sputbackwc) (s, c)))# define inchar() (c == WEOF ? ((errno = inchar_errno), WEOF) \ : ((c = _IO_getwc_unlocked (s)), \ (void) (c != WEOF \ ? ++read_in \ : (size_t) (inchar_errno = errno)), c))# define MEMCPY(d, s, n) __wmemcpy (d, s, n)# define ISSPACE(Ch) iswspace (Ch)# define ISDIGIT(Ch) iswdigit (Ch)# define ISXDIGIT(Ch) iswxdigit (Ch)# define TOLOWER(Ch) towlower (Ch)# define ORIENT if (_IO_fwide (s, 1) != 1) return WEOF# define __strtoll_internal __wcstoll_internal# define __strtoull_internal __wcstoull_internal# define __strtol_internal __wcstol_internal# define __strtoul_internal __wcstoul_internal# define __strtold_internal __wcstold_internal# define __strtod_internal __wcstod_internal# define __strtof_internal __wcstof_internal# define L_(Str) L##Str# define CHAR_T wchar_t# define UCHAR_T unsigned int# define WINT_T wint_t# undef EOF# define EOF WEOF#else# define ungetc(c, s) ((void) ((int) c == EOF \ || (--read_in, \ INTUSE(_IO_sputbackc) (s, (unsigned char) c))))# define ungetc_not_eof(c, s) ((void) (--read_in, \ INTUSE(_IO_sputbackc) (s, (unsigned char) c)))# define inchar() (c == EOF ? ((errno = inchar_errno), EOF) \ : ((c = _IO_getc_unlocked (s)), \ (void) (c != EOF \ ? ++read_in \ : (size_t) (inchar_errno = errno)), c))# define MEMCPY(d, s, n) memcpy (d, s, n)# define ISSPACE(Ch) __isspace_l (Ch, loc)# define ISDIGIT(Ch) __isdigit_l (Ch, loc)# define ISXDIGIT(Ch) __isxdigit_l (Ch, loc)# define TOLOWER(Ch) __tolower_l ((unsigned char) (Ch), loc)# define ORIENT if (_IO_vtable_offset (s) == 0 \ && _IO_fwide (s, -1) != -1) \ return EOF# define L_(Str) Str# define CHAR_T char# define UCHAR_T unsigned char# define WINT_T int#endif#define encode_error() do { \ errval = 4; \ __set_errno (EILSEQ); \ goto errout; \ } while (0)#define conv_error() do { \ errval = 2; \ goto errout; \ } while (0)#define input_error() do { \ errval = 1; \ if (done == 0) done = EOF; \ goto errout; \ } while (0)#define memory_error() do { \ __set_errno (ENOMEM); \ done = EOF; \ goto errout; \ } while (0)#define ARGCHECK(s, format) \ do \ { \ /* Check file argument for consistence. */ \ CHECK_FILE (s, EOF); \ if (s->_flags & _IO_NO_READS) \ { \ __set_errno (EBADF); \ return EOF; \ } \ else if (format == NULL) \ { \ MAYBE_SET_EINVAL; \ return EOF; \ } \ } while (0)#define LOCK_STREAM(S) \ __libc_cleanup_region_start (1, (void (*) (void *)) &_IO_funlockfile, (S)); \ _IO_flockfile (S)#define UNLOCK_STREAM(S) \ _IO_funlockfile (S); \ __libc_cleanup_region_end (0)/* Read formatted input from S according to the format string FORMAT, using the argument list in ARG. Return the number of assignments made, or -1 for an input error. */#ifdef COMPILE_WSCANFint_IO_vfwscanf (s, format, argptr, errp) _IO_FILE *s; const wchar_t *format; _IO_va_list argptr; int *errp;#elseint_IO_vfscanf (s, format, argptr, errp) _IO_FILE *s; const char *format; _IO_va_list argptr; int *errp;#endif{ va_list arg; register const CHAR_T *f = format; register UCHAR_T fc; /* Current character of the format. */ register WINT_T done = 0; /* Assignments done. */ register size_t read_in = 0; /* Chars read in. */ register WINT_T c = 0; /* Last char read. */ register int width; /* Maximum field width. */ register int flags; /* Modifiers for current format element. */ int errval = 0;#ifndef COMPILE_WSCANF __locale_t loc = _NL_CURRENT_LOCALE; struct locale_data *const curctype = loc->__locales[LC_CTYPE];#endif /* Errno of last failed inchar call. */ int inchar_errno = 0; /* Status for reading F-P nums. */ char got_dot, got_e, negative; /* If a [...] is a [^...]. */ CHAR_T not_in;#define exp_char not_in /* Base for integral numbers. */ int base; /* Signedness for integral numbers. */ int number_signed;#define is_hexa number_signed /* Decimal point character. */#ifdef COMPILE_WSCANF wint_t decimal;#else const char *decimal;#endif /* The thousands character of the current locale. */#ifdef COMPILE_WSCANF wint_t thousands;#else const char *thousands;#endif /* State for the conversions. */ mbstate_t state; /* Integral holding variables. */ union { long long int q; unsigned long long int uq; long int l; unsigned long int ul; } num; /* Character-buffer pointer. */ char *str = NULL; wchar_t *wstr = NULL; char **strptr = NULL; ssize_t strsize = 0; /* We must not react on white spaces immediately because they can possibly be matched even if in the input stream no character is available anymore. */ int skip_space = 0; /* Nonzero if we are reading a pointer. */ int read_pointer; /* Workspace. */ CHAR_T *tw; /* Temporary pointer. */ CHAR_T *wp = NULL; /* Workspace. */ size_t wpmax = 0; /* Maximal size of workspace. */ size_t wpsize; /* Currently used bytes in workspace. */#define ADDW(Ch) \ do \ { \ if (wpsize == wpmax) \ { \ CHAR_T *old = wp; \ wpmax = (UCHAR_MAX + 1 > 2 * wpmax ? UCHAR_MAX + 1 : 2 * wpmax); \ wp = (CHAR_T *) alloca (wpmax * sizeof (wchar_t)); \ if (old != NULL) \ MEMCPY (wp, old, wpsize); \ } \ wp[wpsize++] = (Ch); \ } \ while (0)#ifdef __va_copy __va_copy (arg, argptr);#else arg = (va_list) argptr;#endif#ifdef ORIENT ORIENT;#endif ARGCHECK (s, format); {#ifndef COMPILE_WSCANF struct locale_data *const curnumeric = loc->__locales[LC_NUMERIC];#endif /* Figure out the decimal point character. */#ifdef COMPILE_WSCANF decimal = _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_DECIMAL_POINT_WC);#else decimal = curnumeric->values[_NL_ITEM_INDEX (DECIMAL_POINT)].string;#endif /* Figure out the thousands separator character. */#ifdef COMPILE_WSCANF thousands = _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_THOUSANDS_SEP_WC);#else thousands = curnumeric->values[_NL_ITEM_INDEX (THOUSANDS_SEP)].string; if (*thousands == '\0') thousands = NULL;#endif } /* Lock the stream. */ LOCK_STREAM (s);#ifndef COMPILE_WSCANF /* From now on we use `state' to convert the format string. */ memset (&state, '\0', sizeof (state));#endif /* Run through the format string. */ while (*f != '\0') { unsigned int argpos; /* Extract the next argument, which is of type TYPE. For a %N$... spec, this is the Nth argument from the beginning; otherwise it is the next argument after the state now in ARG. */#ifdef __va_copy# define ARG(type) (argpos == 0 ? va_arg (arg, type) : \ ({ unsigned int pos = argpos; \ va_list arg; \ __va_copy (arg, argptr); \ while (--pos > 0) \ (void) va_arg (arg, void *); \ va_arg (arg, type); \ }))#else# if 0 /* XXX Possible optimization. */# define ARG(type) (argpos == 0 ? va_arg (arg, type) : \ ({ va_list arg = (va_list) argptr; \ arg = (va_list) ((char *) arg \ + (argpos - 1) \ * __va_rounded_size (void *)); \ va_arg (arg, type); \ }))# else# define ARG(type) (argpos == 0 ? va_arg (arg, type) : \ ({ unsigned int pos = argpos; \ va_list arg = (va_list) argptr; \ while (--pos > 0) \ (void) va_arg (arg, void *); \ va_arg (arg, type); \ }))# endif#endif#ifndef COMPILE_WSCANF if (!isascii ((unsigned char) *f)) { /* Non-ASCII, may be a multibyte. */ int len = __mbrlen (f, strlen (f), &state); if (len > 0) { do { c = inchar (); if (c == EOF) input_error (); else if (c != (unsigned char) *f++) { ungetc_not_eof (c, s); conv_error (); } } while (--len > 0); continue; } }#endif fc = *f++; if (fc != '%') { /* Remember to skip spaces. */ if (ISSPACE (fc)) { skip_space = 1; continue; } /* Read a character. */ c = inchar (); /* Characters other than format specs must just match. */ if (c == EOF) input_error (); /* We saw white space char as the last character in the format string. Now it's time to skip all leading white space. */ if (skip_space) { while (ISSPACE (c)) if (inchar () == EOF) input_error (); skip_space = 0; } if (c != fc) { ungetc (c, s); conv_error (); } continue; } /* This is the start of the conversion string. */ flags = 0; /* Not yet decided whether we read a pointer or not. */ read_pointer = 0; /* Initialize state of modifiers. */ argpos = 0; /* Prepare temporary buffer. */ wpsize = 0; /* Check for a positional parameter specification. */ if (ISDIGIT ((UCHAR_T) *f)) { argpos = (UCHAR_T) *f++ - L_('0'); while (ISDIGIT ((UCHAR_T) *f)) argpos = argpos * 10 + ((UCHAR_T) *f++ - L_('0')); if (*f == L_('$')) ++f; else { /* Oops; that was actually the field width. */ width = argpos; flags |= WIDTH; argpos = 0; goto got_width; } } /* Check for the assignment-suppressing, the number grouping flag, and the signal to use the locale's digit representation. */ while (*f == L_('*') || *f == L_('\'') || *f == L_('I')) switch (*f++) { case L_('*'): flags |= SUPPRESS; break; case L_('\''): flags |= GROUP; break; case L_('I'): flags |= I18N; break; } /* We have seen width. */ if (ISDIGIT ((UCHAR_T) *f)) flags |= WIDTH; /* Find the maximum field width. */ width = 0; while (ISDIGIT ((UCHAR_T) *f)) { width *= 10; width += (UCHAR_T) *f++ - L_('0'); } got_width: if (width == 0) width = -1; /* Check for type modifiers. */ switch (*f++) { case L_('h'): /* ints are short ints or chars. */ if (*f == L_('h')) { ++f; flags |= CHAR; } else flags |= SHORT; break; case L_('l'): if (*f == L_('l')) { /* A double `l' is equivalent to an `L'. */ ++f; flags |= LONGDBL | LONG; } else /* ints are long ints. */ flags |= LONG; break; case L_('q'): case L_('L'): /* doubles are long doubles, and ints are long long ints. */ flags |= LONGDBL | LONG; break; case L_('a'): /* The `a' is used as a flag only if followed by `s', `S' or `['. */ if (*f != L_('s') && *f != L_('S') && *f != L_('[')) { --f; break; } /* String conversions (%s, %[) take a `char **' arg and fill it in with a malloc'd pointer. */ flags |= MALLOC; break; case L_('z'): if (need_longlong && sizeof (size_t) > sizeof (unsigned long int)) flags |= LONGDBL; else if (sizeof (size_t) > sizeof (unsigned int)) flags |= LONG; break; case L_('j'): if (need_longlong && sizeof (uintmax_t) > sizeof (unsigned long int)) flags |= LONGDBL; else if (sizeof (uintmax_t) > sizeof (unsigned int)) flags |= LONG; break; case L_('t'): if (need_longlong && sizeof (ptrdiff_t) > sizeof (long int)) flags |= LONGDBL; else if (sizeof (ptrdiff_t) > sizeof (int)) flags |= LONG; break; default: /* Not a recognized modifier. Backup. */ --f; break; } /* End of the format string? */ if (*f == L_('\0')) conv_error (); /* Find the conversion specifier. */ fc = *f++; if (skip_space || (fc != L_('[') && fc != L_('c') && fc != L_('C') && fc != L_('n'))) { /* Eat whitespace. */ int save_errno = errno; errno = 0; do if (inchar () == EOF && errno == EINTR) input_error (); while (ISSPACE (c)); errno = save_errno; ungetc (c, s); skip_space = 0; } switch (fc) { case L_('%'): /* Must match a literal '%'. */ c = inchar (); if (c == EOF) input_error (); if (c != fc) { ungetc_not_eof (c, s); conv_error (); } break; case L_('n'): /* Answer number of assignments done. */ /* Corrigendum 1 to ISO C 1990 describes the allowed flags with the 'n' conversion specifier. */ if (!(flags & SUPPRESS)) { /* Don't count the read-ahead. */ if (need_longlong && (flags & LONGDBL)) *ARG (long long int *) = read_in; else if (need_long && (flags & LONG)) *ARG (long int *) = read_in; else if (flags & SHORT) *ARG (short int *) = read_in; else if (!(flags & CHAR)) *ARG (int *) = read_in; else *ARG (char *) = read_in;#ifdef NO_BUG_IN_ISO_C_CORRIGENDUM_1 /* We have a severe problem here. The ISO C standard contradicts itself in explaining the effect of the %n format in `scanf'. While in ISO C:1990 and the ISO C Amendement 1:1995 the result is described as Execution of a %n directive does not effect the assignment count returned at the completion of execution of the f(w)scanf function. in ISO C Corrigendum 1:1994 the following was added: Subclause 7.9.6.2 Add the following fourth example: In: #include <stdio.h> int d1, d2, n1, n2, i; i = sscanf("123", "%d%n%n%d", &d1, &n1, &n2, &d2); the value 123 is assigned to d1 and the value3 to n1. Because %n can never get an input failure the value of 3 is also assigned to n2. The value of d2 is not affected. The value 3 is assigned to i. We go for now with the historically correct code from ISO C, i.e., we don't count the %n assignments. When it ever should proof to be wrong just remove the #ifdef above. */ ++done;#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -