newlib-cygwin/newlib/libc/stdio/vfwscanf.c

1470 lines
34 KiB
C

/*-
* 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
<<vfwscanf>>, <<vwscanf>>, <<vswscanf>>---scan and format argument list from wide character input
INDEX
vfwscanf
INDEX
_vfwscanf
INDEX
vwscanf
INDEX
_vwscanf
INDEX
vswscanf
INDEX
_vswscanf
ANSI_SYNOPSIS
#include <stdio.h>
#include <stdarg.h>
int vwscanf(const wchar_t *<[fmt]>, va_list <[list]>);
int vfwscanf(FILE *<[fp]>, const wchar_t *<[fmt]>, va_list <[list]>);
int vswscanf(const wchar_t *<[str]>, const wchar_t *<[fmt]>, va_list <[list]>);
int _vwscanf(struct _reent *<[reent]>, const wchar_t *<[fmt]>,
va_list <[list]>);
int _vfwscanf(struct _reent *<[reent]>, FILE *<[fp]>, const wchar_t *<[fmt]>,
va_list <[list]>);
int _vswscanf(struct _reent *<[reent]>, const wchar_t *<[str]>,
const wchar_t *<[fmt]>, va_list <[list]>);
TRAD_SYNOPSIS
#include <stdio.h>
#include <varargs.h>
int vwscanf( <[fmt]>, <[ist]>)
wchar_t *<[fmt]>;
va_list <[list]>;
int vfwscanf( <[fp]>, <[fmt]>, <[list]>)
FILE *<[fp]>;
wchar_t *<[fmt]>;
va_list <[list]>;
int vswscanf( <[str]>, <[fmt]>, <[list]>)
wchar_t *<[str]>;
wchar_t *<[fmt]>;
va_list <[list]>;
int _vwscanf( <[reent]>, <[fmt]>, <[ist]>)
struct _reent *<[reent]>;
wchar_t *<[fmt]>;
va_list <[list]>;
int _vfwscanf( <[reent]>, <[fp]>, <[fmt]>, <[list]>)
struct _reent *<[reent]>;
FILE *<[fp]>;
wchar_t *<[fmt]>;
va_list <[list]>;
int _vswscanf( <[reent]>, <[str]>, <[fmt]>, <[list]>)
struct _reent *<[reent]>;
wchar_t *<[str]>;
wchar_t *<[fmt]>;
va_list <[list]>;
DESCRIPTION
<<vwscanf>>, <<vfwscanf>>, and <<vswscanf>> are (respectively) variants
of <<wscanf>>, <<fwscanf>>, and <<swscanf>>. They differ only in
allowing their caller to pass the variable argument list as a
<<va_list>> object (initialized by <<va_start>>) rather than
directly accepting a variable number of arguments.
RETURNS
The return values are consistent with the corresponding functions:
<<vwscanf>> returns the number of input fields successfully scanned,
converted, and stored; the return value does not include scanned
fields which were not stored.
If <<vwscanf>> attempts to read at end-of-file, the return value
is <<EOF>>.
If no fields were stored, the return value is <<0>>.
The routines <<_vwscanf>>, <<_vfwscanf>>, and <<_vswscanf>> are
reentrant versions which take an additional first parameter which points
to the reentrancy structure.
PORTABILITY
C99, POSIX-1.2008
*/
#include <_ansi.h>
#include <reent.h>
#include <newlib.h>
#include <ctype.h>
#include <wctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include <wchar.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include "local.h"
#ifdef INTEGER_ONLY
#define VFWSCANF vfiwscanf
#define _VFWSCANF_R _vfiwscanf_r
#define __SVFWSCANF __svfiwscanf
#ifdef STRING_ONLY
# define __SVFWSCANF_R __ssvfiwscanf_r
#else
# define __SVFWSCANF_R __svfiwscanf_r
#endif
#else
#define VFWSCANF vfwscanf
#define _VFWSCANF_R _vfwscanf_r
#define __SVFWSCANF __svfwscanf
#ifdef STRING_ONLY
# define __SVFWSCANF_R __ssvfwscanf_r
#else
# define __SVFWSCANF_R __svfwscanf_r
#endif
#ifndef NO_FLOATING_POINT
#define FLOATING_POINT
#endif
#endif
#ifdef STRING_ONLY
#undef _flockfile
#undef _funlockfile
#define _flockfile(x) {}
#define _funlockfile(x) {}
#define _ungetwc_r _sungetwc_r
#define __srefill_r __ssrefill_r
#define _fgetwc_r _sfgetwc_r
#endif
#ifdef FLOATING_POINT
#include <math.h>
#include <float.h>
/* 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 _wcstold_r _PARAMS((wchar_t *s, wchar_t **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 HAVESIGN 0x10000 /* sign 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., wcstol or wcstoul */
#define CT_FLOAT 4 /* floating, i.e., wcstod */
#define INCCL(_c) \
(cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) : \
(wmemchr(ccls, (_c), ccle - ccls) != NULL))
/*
* vfwscanf
*/
#ifndef STRING_ONLY
#ifndef _REENT_ONLY
int
_DEFUN(VFWSCANF, (fp, fmt, ap),
register FILE *fp _AND
_CONST wchar_t *fmt _AND
va_list ap)
{
CHECK_INIT(_REENT, fp);
return __SVFWSCANF_R (_REENT, fp, fmt, ap);
}
int
_DEFUN(__SVFWSCANF, (fp, fmt0, ap),
register FILE *fp _AND
wchar_t _CONST *fmt0 _AND
va_list ap)
{
return __SVFWSCANF_R (_REENT, fp, fmt0, ap);
}
#endif /* !_REENT_ONLY */
int
_DEFUN(_VFWSCANF_R, (data, fp, fmt, ap),
struct _reent *data _AND
register FILE *fp _AND
_CONST wchar_t *fmt _AND
va_list ap)
{
CHECK_INIT(data, fp);
return __SVFWSCANF_R (data, fp, fmt, ap);
}
#endif /* !STRING_ONLY */
#ifdef STRING_ONLY
/* When dealing with the swscanf family, we don't want to use the
* regular ungetwc which will drag in file I/O items we don't need.
* So, we create our own trimmed-down version. */
static wint_t
_DEFUN(_sungetwc_r, (data, fp, ch),
struct _reent *data _AND
wint_t wc _AND
register FILE *fp)
{
if (wc == WEOF)
return (WEOF);
/* After ungetc, we won't be at eof anymore */
fp->_flags &= ~__SEOF;
/*
* If we are in the middle of ungetwc'ing, just continue.
* This may require expanding the current ungetc buffer.
*/
if (HASUB (fp))
{
if (fp->_r >= fp->_ub._size && __submore (data, fp))
{
return EOF;
}
fp->_p -= sizeof (wchar_t);
*fp->_p = (wchar_t) wc;
fp->_r += sizeof (wchar_t);
return wc;
}
/*
* If we can handle this by simply backing up, do so,
* but never replace the original character.
* (This makes swscanf() work when scanning `const' data.)
*/
if (fp->_bf._base != NULL && fp->_p > fp->_bf._base
&& ((wchar_t *)fp->_p)[-1] == wc)
{
fp->_p -= sizeof (wchar_t);
fp->_r += sizeof (wchar_t);
return wc;
}
/*
* Create an ungetc buffer.
* Initially, we will use the `reserve' buffer.
*/
fp->_ur = fp->_r;
fp->_up = fp->_p;
fp->_ub._base = fp->_ubuf;
fp->_ub._size = sizeof (fp->_ubuf);
fp->_p = &fp->_ubuf[sizeof (fp->_ubuf) - sizeof (wchar_t)];
*(wchar_t *) fp->_p = wc;
fp->_r = 2;
return wc;
}
extern int __ssrefill_r _PARAMS ((struct _reent *ptr, register FILE * fp));
static size_t
_DEFUN(_sfgetwc_r, (ptr, fp),
struct _reent * ptr _AND
FILE * fp)
{
wchar_t wc;
if (fp->_r <= 0 && __ssrefill_r (ptr, fp))
return (WEOF);
wc = *(wchar_t *) fp->_p;
fp->_p += sizeof (wchar_t);
fp->_r -= sizeof (wchar_t);
return (wc);
}
#endif /* STRING_ONLY */
int
_DEFUN(__SVFWSCANF_R, (rptr, fp, fmt0, ap),
struct _reent *rptr _AND
register FILE *fp _AND
wchar_t _CONST *fmt0 _AND
va_list ap)
{
register wchar_t *fmt = (wchar_t *) fmt0;
register wint_t c; /* character from format, or conversion */
register size_t width; /* field width, or 0 */
register wchar_t *p = NULL; /* points into all kinds of strings */
register int n; /* handy integer */
register int flags; /* flags as defined above */
register wchar_t *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 wcstol/wcstoul */
mbstate_t mbs; /* value to keep track of multibyte state */
#define CCFN_PARAMS _PARAMS((struct _reent *, const wchar_t *, wchar_t **, int))
unsigned long (*ccfn)CCFN_PARAMS=0; /* conversion function (wcstol/wcstoul) */
wchar_t buf[BUF]; /* buffer for numeric conversions */
const wchar_t *ccls; /* character class start */
const wchar_t *ccle; /* character class end */
int cclcompl = 0; /* ccl is complemented? */
wint_t wi; /* handy wint_t */
char *mbp = NULL; /* multibyte string pointer for %c %s %[ */
size_t nconv; /* number of bytes in mb. conversion */
char mbbuf[MB_LEN_MAX]; /* temporary mb. character buffer */
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
__sfp_lock_acquire ();
_flockfile (fp);
ORIENT (fp, 1);
nassigned = 0;
nread = 0;
ccls = ccle = NULL;
for (;;)
{
c = *fmt++;
if (c == L'\0')
goto all_done;
if (iswspace (c))
{
while ((c = _fgetwc_r (rptr, fp)) != WEOF && iswspace(c))
;
if (c != WEOF)
_ungetwc_r (rptr, c, fp);
continue;
}
if (c != L'%')
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 L'%':
literal:
if ((wi = _fgetwc_r (rptr, fp)) == WEOF)
goto input_failure;
if (wi != c)
{
_ungetwc_r (rptr, wi, fp);
goto input_failure;
}
nread++;
continue;
case L'*':
flags |= SUPPRESS;
goto again;
case L'l':
#if defined _WANT_IO_C99_FORMATS || !defined _NO_LONGLONG
if (*fmt == L'l') /* Check for 'll' = long long (SUSv3) */
{
++fmt;
flags |= LONGDBL;
}
else
#endif
flags |= LONG;
goto again;
case L'L':
flags |= LONGDBL;
goto again;
case L'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 L'j': /* intmax_t */
if (sizeof (intmax_t) == sizeof (long))
flags |= LONG;
else
flags |= LONGDBL;
goto again;
case L'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 L'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 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;
#ifndef _NO_POS_ARGS
case L'$':
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 */
case L'd':
c = CT_INT;
ccfn = (unsigned long (*)CCFN_PARAMS)_wcstol_r;
base = 10;
break;
case L'i':
c = CT_INT;
ccfn = (unsigned long (*)CCFN_PARAMS)_wcstol_r;
base = 0;
break;
case L'o':
c = CT_INT;
ccfn = _wcstoul_r;
base = 8;
break;
case L'u':
c = CT_INT;
ccfn = _wcstoul_r;
base = 10;
break;
case L'X':
case L'x':
flags |= PFXOK; /* enable 0x prefixing */
c = CT_INT;
ccfn = _wcstoul_r;
base = 16;
break;
#ifdef FLOATING_POINT
# ifdef _WANT_IO_C99_FORMATS
case L'A':
case L'a':
case L'F':
# endif
case L'E':
case L'G':
case L'e':
case L'f':
case L'g':
c = CT_FLOAT;
break;
#endif
#ifdef _WANT_IO_C99_FORMATS
case L'S':
flags |= LONG;
/* FALLTHROUGH */
#endif
case L's':
c = CT_STRING;
break;
case L'[':
ccls = fmt;
if (*fmt == '^')
{
cclcompl = 1;
++fmt;
}
else
cclcompl = 0;
if (*fmt == ']')
fmt++;
while (*fmt != '\0' && *fmt != ']')
fmt++;
ccle = fmt;
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 = _wcstoul_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 L'\0': /* compat */
_funlockfile (fp);
__sfp_lock_release ();
return EOF;
default: /* compat */
goto match_failure;
}
/*
* Consume leading white space, except for formats that
* suppress this.
*/
if ((flags & NOSKIP) == 0)
{
while ((wi = _fgetwc_r (rptr, fp)) != WEOF && iswspace (wi))
nread++;
if (wi == WEOF)
goto input_failure;
_ungetwc_r (rptr, wi, fp);
}
/*
* Do the conversion.
*/
switch (c)
{
case CT_CHAR:
/* scan arbitrary characters (sets NOSKIP) */
if (width == 0)
width = 1;
if (flags & LONG)
{
if (!(flags & SUPPRESS))
p = va_arg(ap, wchar_t *);
n = 0;
while (width-- != 0 && (wi = _fgetwc_r (rptr, fp)) != WEOF)
{
if (!(flags & SUPPRESS))
*p++ = (wchar_t) wi;
n++;
}
if (n == 0)
goto input_failure;
nread += n;
if (!(flags & SUPPRESS))
nassigned++;
}
else
{
if (!(flags & SUPPRESS))
mbp = va_arg(ap, char *);
n = 0;
memset ((_PTR)&mbs, '\0', sizeof (mbstate_t));
while (width != 0 && (wi = _fgetwc_r (rptr, fp)) != WEOF)
{
if (width >= MB_CUR_MAX && !(flags & SUPPRESS))
{
nconv = _wcrtomb_r (rptr, mbp, wi, &mbs);
if (nconv == (size_t) -1)
goto input_failure;
}
else
{
nconv = _wcrtomb_r (rptr, mbbuf, wi, &mbs);
if (nconv == (size_t) -1)
goto input_failure;
if (nconv > width)
{
_ungetwc_r (rptr, wi, fp);
break;
}
if (!(flags & SUPPRESS))
memcpy(mbp, mbbuf, nconv);
}
if (!(flags & SUPPRESS))
mbp += nconv;
width -= nconv;
n++;
}
if (n == 0)
goto input_failure;
nread += n;
if (!(flags & SUPPRESS))
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) && (flags & LONG))
{
n = 0;
while ((wi = _fgetwc_r (rptr, fp)) != WEOF
&& width-- != 0 && INCCL (wi))
n++;
if (wi != WEOF)
_ungetwc_r (rptr, wi, fp);
if (n == 0)
goto match_failure;
}
else if (flags & LONG)
{
p0 = p = va_arg(ap, wchar_t *);
while ((wi = _fgetwc_r (rptr, fp)) != WEOF
&& width-- != 0 && INCCL (wi))
*p++ = (wchar_t) wi;
if (wi != WEOF)
_ungetwc_r (rptr, wi, fp);
n = p - p0;
if (n == 0)
goto match_failure;
}
else
{
if (!(flags & SUPPRESS))
mbp = va_arg(ap, char *);
n = 0;
memset ((_PTR) &mbs, '\0', sizeof (mbstate_t));
while ((wi = _fgetwc_r (rptr, fp)) != WEOF
&& width-- != 0 && INCCL (wi))
{
if (width >= MB_CUR_MAX && !(flags & SUPPRESS))
{
nconv = _wcrtomb_r (rptr, mbp, wi, &mbs);
if (nconv == (size_t) -1)
goto input_failure;
}
else
{
nconv = wcrtomb(mbbuf, wi, &mbs);
if (nconv == (size_t) -1)
goto input_failure;
if (nconv > width)
break;
if (!(flags & SUPPRESS))
memcpy(mbp, mbbuf, nconv);
}
if (!(flags & SUPPRESS))
mbp += nconv;
width -= nconv;
n++;
}
if (wi != WEOF)
_ungetwc_r (rptr, wi, fp);
if (!(flags & SUPPRESS))
{
*mbp = 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) && (flags & LONG))
{
while ((wi = _fgetwc_r (rptr, fp)) != WEOF
&& width-- != 0 && !iswspace (wi))
nread++;
if (wi != WEOF)
_ungetwc_r (rptr, wi, fp);
}
else if (flags & LONG)
{
p0 = p = va_arg(ap, wchar_t *);
while ((wi = _fgetwc_r (rptr, fp)) != WEOF
&& width-- != 0 && !iswspace (wi))
{
*p++ = (wchar_t) wi;
nread++;
}
if (wi != WEOF)
_ungetwc_r (rptr, wi, fp);
*p = '\0';
nassigned++;
}
else
{
if (!(flags & SUPPRESS))
mbp = va_arg(ap, char *);
memset ((_PTR) &mbs, '\0', sizeof (mbstate_t));
while ((wi = _fgetwc_r (rptr, fp)) != WEOF
&& width != 0 && !iswspace (wi))
{
if (width >= MB_CUR_MAX && !(flags & SUPPRESS))
{
nconv = wcrtomb(mbp, wi, &mbs);
if (nconv == (size_t)-1)
goto input_failure;
}
else
{
nconv = wcrtomb(mbbuf, wi, &mbs);
if (nconv == (size_t)-1)
goto input_failure;
if (nconv > width)
break;
if (!(flags & SUPPRESS))
memcpy(mbp, mbbuf, nconv);
}
if (!(flags & SUPPRESS))
mbp += nconv;
width -= nconv;
nread++;
}
if (wi != WEOF)
_ungetwc_r (rptr, wi, fp);
if (!(flags & SUPPRESS))
{
*mbp = 0;
nassigned++;
}
}
continue;
case CT_INT:
{
/* scan an integer as if by wcstol/wcstoul */
if (width == 0 || width > sizeof (buf) / sizeof (*buf) - 1)
width = sizeof(buf) / sizeof (*buf) - 1;
flags |= SIGNOK | NDIGITS | NZDIGITS;
for (p = buf; width; width--)
{
c = _fgetwc_r (rptr, fp);
/*
* 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;
flags |= HAVESIGN;
goto ok;
}
break;
/* x ok iff flag still set & single 0 seen */
case L'x':
case L'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.
*/
if (c != WEOF)
_ungetwc_r (rptr, c, fp);
break;
ok:
/*
* c is legal: store it and look at the next.
*/
*p++ = (wchar_t) c;
}
/*
* 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'.
* Use of ungetc here and below assumes ASCII encoding; we are only
* pushing back 7-bit characters, so casting to unsigned char is
* not necessary.
*/
if (flags & NDIGITS)
{
if (p > buf)
_ungetwc_r (rptr, *--p, fp); /* [-+xX] */
goto match_failure;
}
c = p[-1];
if (c == L'x' || c == L'X')
{
--p;
_ungetwc_r (rptr, c, fp);
}
if ((flags & SUPPRESS) == 0)
{
unsigned long res;
*p = 0;
res = (*ccfn) (rptr, buf, (wchar_t **) NULL, base);
if (flags & POINTER)
{
void **vp = GET_ARG (N, ap, void **);
#ifndef _NO_LONGLONG
if (sizeof (uintptr_t) > sizeof (unsigned long))
{
unsigned long long resll;
resll = _wcstoull_r (rptr, buf, (wchar_t **) NULL, base);
*vp = (void *) (uintptr_t) resll;
}
else
#endif /* !_NO_LONGLONG */
*vp = (void *) (uintptr_t) 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)
{
unsigned long long resll;
if (ccfn == _wcstoul_r)
resll = _wcstoull_r (rptr, buf, (wchar_t **) NULL, base);
else
resll = _wcstoll_r (rptr, buf, (wchar_t **) 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;
break;
}
#ifdef FLOATING_POINT
case CT_FLOAT:
{
/* scan a floating point number as if by wcstod */
/* 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;
wchar_t *exp_start = NULL;
unsigned width_left = 0;
char nancount = 0;
char infcount = 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 = _fgetwc_r (rptr, fp);
/*
* This code mimicks the integer conversion
* code, but is much simpler.
*/
switch (c)
{
case L'0':
if (flags & NDIGITS)
{
flags &= ~SIGNOK;
zeroes++;
if (width_left)
{
width_left--;
width++;
}
goto fskip;
}
/* Fall through. */
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':
if (nancount + infcount == 0)
{
flags &= ~(SIGNOK | NDIGITS);
goto fok;
}
break;
case L'+':
case L'-':
if (flags & SIGNOK)
{
flags &= ~SIGNOK;
goto fok;
}
break;
case L'n':
case L'N':
if (nancount == 0 && zeroes == 0
&& (flags & (NDIGITS | DPTOK | EXPOK)) ==
(NDIGITS | DPTOK | EXPOK))
{
flags &= ~(SIGNOK | DPTOK | EXPOK | NDIGITS);
nancount = 1;
goto fok;
}
if (nancount == 2)
{
nancount = 3;
goto fok;
}
if (infcount == 1 || infcount == 4)
{
infcount++;
goto fok;
}
break;
case L'a':
case L'A':
if (nancount == 1)
{
nancount = 2;
goto fok;
}
break;
case L'i':
if (infcount == 0 && zeroes == 0
&& (flags & (NDIGITS | DPTOK | EXPOK)) ==
(NDIGITS | DPTOK | EXPOK))
{
flags &= ~(SIGNOK | DPTOK | EXPOK | NDIGITS);
infcount = 1;
goto fok;
}
if (infcount == 3 || infcount == 5)
{
infcount++;
goto fok;
}
break;
case L'f':
case L'F':
if (infcount == 2)
{
infcount = 3;
goto fok;
}
break;
case L't':
case L'T':
if (infcount == 6)
{
infcount = 7;
goto fok;
}
break;
case L'y':
case L'Y':
if (infcount == 7)
{
infcount = 8;
goto fok;
}
break;
case L'.':
if (flags & DPTOK)
{
flags &= ~(SIGNOK | DPTOK);
leading_zeroes = zeroes;
goto fok;
}
break;
case L'e':
case L'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;
}
if (c != WEOF)
_ungetwc_r (rptr, c, fp);
break;
fok:
*p++ = c;
fskip:
width--;
++nread;
}
if (zeroes)
flags &= ~NDIGITS;
/* We may have a 'N' or possibly even [sign] 'N' 'a' 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.
FIXME - we still don't handle NAN([0xdigits]). */
if (nancount - 1U < 2U) /* nancount && nancount < 3 */
{
/* Newlib's ungetc works even if we called __srefill in
the middle of a partial parse, but POSIX does not
guarantee that in all implementations of ungetc. */
while (p > buf)
{
_ungetwc_r (rptr, *--p, fp); /* [-+nNaA] */
--nread;
}
goto match_failure;
}
/* Likewise for 'inf' and 'infinity'. But be careful that
'infinite' consumes only 3 characters, leaving the stream
at the second 'i'. */
if (infcount - 1U < 7U) /* infcount && infcount < 8 */
{
if (infcount >= 3) /* valid 'inf', but short of 'infinity' */
while (infcount-- > 3)
{
_ungetwc_r (rptr, *--p, fp); /* [iInNtT] */
--nread;
}
else
{
while (p > buf)
{
_ungetwc_r (rptr, *--p, fp); /* [-+iInN] */
--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)
{
_ungetwc_r (rptr, *--p, fp); /* [-+.] */
--nread;
}
goto match_failure;
}
/* just a bad exponent (e and maybe sign) */
c = *--p;
--nread;
if (c != L'e' && c != L'E')
{
_ungetwc_r (rptr, c, fp); /* [-+] */
c = *--p;
--nread;
}
_ungetwc_r (rptr, c, fp); /* [eE] */
}
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 = _wcstol_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;
swprintf (exp_start, MAX_LONG_LEN, L"e%ld", new_exp);
}
/* FIXME: We don't have wcstold yet. */
#if 0//ndef _NO_LONGDBL /* !_NO_LONGDBL */
if (flags & LONGDBL)
qres = _wcstold_r (rptr, buf, NULL);
else
#endif
res = _wcstod_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:
/* On read failure, return EOF failure regardless of matches; errno
should have been set prior to here. On EOF failure (including
invalid format string), return EOF if no matches yet, else number
of matches made prior to failure. */
_funlockfile (fp);
__sfp_lock_release ();
return nassigned && !(fp->_flags & __SERR) ? nassigned : EOF;
match_failure:
all_done:
/* Return number of matches, which can be 0 on match failure. */
_funlockfile (fp);
__sfp_lock_release ();
return nassigned;
}
#ifndef _NO_POS_ARGS
/* Process all intermediate arguments. Fortunately, with wscanf, 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 */