OpenSolaris_b135/lib/libc/port/stdio/doscan.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1988 AT&T	*/
/*	  All Rights Reserved  	*/

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include "lint.h"
#include <sys/types.h>
#include "mtlib.h"
#include "file64.h"
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
#include <values.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <thread.h>
#include <synch.h>
#include <stdlib.h>
#include <fnmatch.h>
#include <limits.h>
#include <wchar.h>
#include <unistd.h>
#include "libc.h"
#include "stdiom.h"
#include "xpg6.h"

#define	NCHARS	(1 << BITSPERBYTE)

/* if the _IOWRT flag is set, this must be a call from sscanf */
#define	locgetc(cnt)	(cnt += 1, (iop->_flag & _IOWRT) ? \
				((*iop->_ptr == '\0') ? EOF : *iop->_ptr++) : \
				GETC(iop))
#define	locungetc(cnt, x) (cnt -= 1, (x == EOF) ? EOF : \
				((iop->_flag & _IOWRT) ? *(--iop->_ptr) : \
				    (++iop->_cnt, *(--iop->_ptr))))

#define	wlocgetc()	((iop->_flag & _IOWRT) ? \
				((*iop->_ptr == '\0') ? EOF : *iop->_ptr++) : \
				GETC(iop))
#define	wlocungetc(x) ((x == EOF) ? EOF : \
				((iop->_flag & _IOWRT) ? *(--iop->_ptr) : \
				    UNGETC(x, iop)))

#define	MAXARGS	30	/* max. number of args for fast positional paramters */

/*
 * stva_list is used to subvert C's restriction that a variable with an
 * array type can not appear on the left hand side of an assignment operator.
 * By putting the array inside a structure, the functionality of assigning to
 * the whole array through a simple assignment is achieved..
 */
typedef struct stva_list {
	va_list	ap;
} stva_list;

static int number(int *, int *, int, int, int, int, FILE *, va_list *);
static int readchar(FILE *, int *);
static int string(int *, int *, int, int, int, char *, FILE *, va_list *);
static int wstring(int *, int *, int, int, int, FILE *, va_list *);
static int	wbrstring(int *, int *, int, int, int, FILE *,
	unsigned char *, va_list *);
#ifdef	_WIDE
static int	brstring(int *, int *, int, int, int, FILE *,
	unsigned char *, va_list *);
#endif
static int _bi_getwc(FILE *);
static int _bi_ungetwc(wint_t, FILE *);

#ifdef	_WIDE
static int _mkarglst(const wchar_t *, stva_list, stva_list[]);
static wint_t	_wd_getwc(int *, FILE *);
static wint_t	_wd_ungetwc(int *, wchar_t, FILE *);
static int	_watoi(wchar_t *);
#else  /* _WIDE */
static int _mkarglst(const char *, stva_list, stva_list[]);
#endif /* _WIDE */

#ifndef	_WIDE
int
_doscan(FILE *iop, const char *fmt, va_list va_Alist)
{
	int ret;
	rmutex_t *lk;

	if (iop->_flag & _IOWRT)
		ret = __doscan_u(iop, fmt, va_Alist, 0);
	else {
		FLOCKFILE(lk, iop);
		ret = __doscan_u(iop, fmt, va_Alist, 0);
		FUNLOCKFILE(lk);
	}
	return (ret);
}
#endif  /* _WIDE */

/* ARGSUSED3 */
#ifdef	_WIDE
int
__wdoscan_u(FILE *iop, const wchar_t *fmt, va_list va_Alist, int scflag)
#else  /* _WIDE */
int
__doscan_u(FILE *iop, const char *sfmt, va_list va_Alist, int scflag)
#endif /* _WIDE */
{
#ifdef	_WIDE
	wchar_t	ch;
	wchar_t	inchar, size;
	int	nmatch = 0, len, stow;
#else  /* _WIDE */
	int	ch;
	int		nmatch = 0, len, inchar, stow, size;
#endif /* _WIDE */

	unsigned char	*bracket_str = NULL;
	int		chcount, flag_eof;
	char	tab[NCHARS];

	/* variables for postional parameters */
#ifdef	_WIDE
	const wchar_t	*sformat = fmt;	/* save the beginning of the format */
#else  /* _WIDE */
	const unsigned char	*fmt = (const unsigned char *)sfmt;
	const char	*sformat = sfmt; /* save the beginning of the format */
#endif /* _WIDE */
	int		fpos = 1;	/* 1 if first postional parameter */
	stva_list	args;	/* used to step through the argument list */
	stva_list	sargs;	/* used to save start of the argument list */
	stva_list	arglst[MAXARGS];
					/*
					 * array giving the appropriate values
					 * for va_arg() to retrieve the
					 * corresponding argument:
					 * arglst[0] is the first argument
					 * arglst[1] is the second argument,etc.
					 */
	/* Check if readable stream */
	if (!(iop->_flag & (_IOREAD | _IORW))) {
		errno = EBADF;
		return (EOF);
	}

	/*
	 * Initialize args and sargs to the start of the argument list.
	 * We don't know any portable way to copy an arbitrary C object
	 * so we use a system-specific routine(probably a macro) from
	 * stdarg.h.  (Remember that if va_list is an array, in_args will
	 * be a pointer and &in_args won't be what we would want for
	 * memcpy.)
	 */
	va_copy(args.ap, va_Alist);

	sargs = args;

	chcount = 0; flag_eof = 0;

	/*
	 * ****************************************************
	 * Main loop: reads format to determine a pattern,
	 *		and then goes to read input stream
	 *		in attempt to match the pattern.
	 * ****************************************************
	 */
	for (; ; ) {
		if ((ch = *fmt++) == '\0') {
			return (nmatch); /* end of format */
		}
#ifdef	_WIDE
		if (iswspace(ch)) {
			if (!flag_eof) {
				while (iswspace(inchar =
				    _wd_getwc(&chcount, iop)))
					;
				if (_wd_ungetwc(&chcount, inchar, iop) == WEOF)
					flag_eof = 1;
			}
			continue;
		}
		if (ch != '%' || (ch = *fmt++) == '%') {
			if (ch == '%') {
				if (!flag_eof) {
					while (iswspace(inchar =
					    _wd_getwc(&chcount, iop)))
						;
					if (_wd_ungetwc(&chcount, inchar, iop)
					    == WEOF)
						flag_eof = 1;
				}
			}
			if ((inchar = _wd_getwc(&chcount, iop)) == ch)
				continue;
			if (_wd_ungetwc(&chcount, inchar, iop) != WEOF) {
				return (nmatch); /* failed to match input */
			}
			break;
		}
#else  /* _WIDE */
		if (isspace(ch)) {
			if (!flag_eof) {
				while (isspace(inchar = locgetc(chcount)))
					;
				if (locungetc(chcount, inchar) == EOF)
					flag_eof = 1;

			}
			continue;
		}
		if (ch != '%' || (ch = *fmt++) == '%') {
			if (ch == '%') {
				if (!flag_eof) {
					while (isspace(inchar =
					    locgetc(chcount)))
						;
					if (locungetc(chcount, inchar) == EOF)
						flag_eof = 1;
				}
			}
			if ((inchar = locgetc(chcount)) == ch)
				continue;
			if (locungetc(chcount, inchar) != EOF) {
				return (nmatch); /* failed to match input */
			}
			break;
		}
#endif /* _WIDE */

charswitch:	/* target of a goto 8-( */

		if (ch == '*') {
			stow = 0;
			ch = *fmt++;
		} else
			stow = 1;

#ifdef	_WIDE
		for (len = 0; ((ch >= 0) && (ch < 256) && isdigit(ch));
		    ch = *fmt++)
			len = len * 10 + ch - '0';
#else  /* _WIDE */
		for (len = 0; isdigit(ch); ch = *fmt++)
			len = len * 10 + ch - '0';
#endif /* _WIDE */

		if (ch == '$') {
			/*
			 * positional parameter handling - the number
			 * specified in len gives the argument to which
			 * the next conversion should be applied.
			 * WARNING: This implementation of positional
			 * parameters assumes that the sizes of all pointer
			 * types are the same. (Code similar to that
			 * in the portable doprnt.c should be used if this
			 * assumption does not hold for a particular
			 * port.)
			 */
			if (fpos) {
				if (_mkarglst(sformat, sargs, arglst) != 0) {
					return (EOF);
				} else {
					fpos = 0;
				}
			}
			if (len <= MAXARGS) {
				args = arglst[len - 1];
			} else {
				args = arglst[MAXARGS - 1];
				for (len -= MAXARGS; len > 0; len--)
					(void) va_arg(args.ap, void *);
			}
			len = 0;
			ch = *fmt++;
			goto charswitch;
		}

		if (len == 0)
			len = MAXINT;
#ifdef	_WIDE
		if ((size = ch) == 'l' || (size == 'h') || (size == 'L') ||
		    (size == 'j') || (size == 't') || (size == 'z'))
			ch = *fmt++;
#else  /* _WIDE */
		if ((size = ch) == 'l' || (size == 'h') || (size == 'L') ||
		    (size == 'w') || (size == 'j') || (size == 't') ||
		    (size == 'z'))
			ch = *fmt++;
#endif /* _WIDE */
		if (size == 'l' && ch == 'l') {
			size = 'm';		/* size = 'm' if long long */
			ch = *fmt++;
		} else if (size == 'h' && ch == 'h') {
			size = 'b';		/* use size = 'b' if char */
			ch = *fmt++;
		} else if ((size == 't') || (size == 'z')) {
			size = 'l';
		} else if (size == 'j') {
#ifndef _LP64
			/* check scflag for size of u/intmax_t (32-bit libc) */
			if (!(scflag & _F_INTMAX32)) {
#endif
				size = 'm';
#ifndef _LP64
			}
#endif
		}
		if (ch == '\0') {
			return (EOF);		/* unexpected end of format */
		}
#ifdef	_WIDE
		if (ch == '[') {
			wchar_t	c;
			size_t	len;
			int	negflg = 0;
			wchar_t	*p;
			wchar_t	*wbracket_str;
			size_t	wlen, clen;

			/* p points to the address of '[' */
			p = (wchar_t *)fmt - 1;
			len = 0;
			if (*fmt == '^') {
				len++;
				fmt++;
				negflg = 1;
			}
			if (((c = *fmt) == ']') || (c == '-')) {
				len++;
				fmt++;
			}
			while ((c = *fmt) != ']') {
				if (c == '\0') {
					return (EOF); /* unexpected EOF */
				} else {
					len++;
					fmt++;
				}
			}
			fmt++;
			len += 2;
			wbracket_str = (wchar_t *)
			    malloc(sizeof (wchar_t) * (len + 1));
			if (wbracket_str == NULL) {
				errno = ENOMEM;
				return (EOF);
			} else {
				(void) wmemcpy(wbracket_str,
				    (const wchar_t *)p, len);
				*(wbracket_str + len) = L'\0';
				if (negflg && *(wbracket_str + 1) == '^') {
					*(wbracket_str + 1) = L'!';
				}
			}
			wlen = wcslen(wbracket_str);
			clen = wcstombs((char *)NULL, wbracket_str, 0);
			if (clen == (size_t)-1) {
				free(wbracket_str);
				return (EOF);
			}
			bracket_str = (unsigned char *)
			    malloc(sizeof (unsigned char) * (clen + 1));
			if (bracket_str == NULL) {
				free(wbracket_str);
				errno = ENOMEM;
				return (EOF);
			}
			clen = wcstombs((char *)bracket_str, wbracket_str,
			    wlen + 1);
			free(wbracket_str);
			if (clen == (size_t)-1) {
				free(bracket_str);
				return (EOF);
			}
		}
#else  /* _WIDE */
		if (ch == '[') {
			if (size == 'l') {
				int	c, len, i;
				int	negflg = 0;
				unsigned char 	*p;

				p = (unsigned char *)(fmt - 1);
				len = 0;
				if (*fmt == '^') {
					len++;
					fmt++;
					negflg = 1;
				}
				if (((c = *fmt) == ']') || (c == '-')) {
					len++;
					fmt++;
				}
				while ((c = *fmt) != ']') {
					if (c == '\0') {
						return (EOF);
					} else if (isascii(c)) {
						len++;
						fmt++;
					} else {
						i = mblen((const char *)fmt,
						    MB_CUR_MAX);
						if (i <= 0) {
							return (EOF);
						} else {
							len += i;
							fmt += i;
						}
					}
				}
				fmt++;
				len += 2;
				bracket_str = (unsigned char *)
				    malloc(sizeof (unsigned char) * (len + 1));
				if (bracket_str == NULL) {
					errno = ENOMEM;
					return (EOF);
				} else {
					(void) strncpy((char *)bracket_str,
					    (const char *)p, len);
					*(bracket_str + len) = '\0';
					if (negflg &&
					    *(bracket_str + 1) == '^') {
						*(bracket_str + 1) = '!';
					}
				}
			} else {
				int	t = 0;
				int	b, c, d;

				if (*fmt == '^') {
					t++;
					fmt++;
				}
				(void) memset(tab, !t, NCHARS);
				if ((c = *fmt) == ']' || c == '-') {
					tab[c] = t;
					fmt++;
				}

				while ((c = *fmt) != ']') {
					if (c == '\0') {
						return (EOF);
					}
					b = *(fmt - 1);
					d = *(fmt + 1);
					if ((c == '-') && (d != ']') &&
					    (b < d)) {
						(void) memset(&tab[b], t,
						    d - b + 1);
						fmt += 2;
					} else {
						tab[c] = t;
						fmt++;
					}
				}
				fmt++;
			}
		}
#endif /* _WIDE */

#ifdef	_WIDE
		if ((ch >= 0) && (ch < 256) &&
		    isupper((int)ch)) { /* no longer documented */
			if (_lib_version == c_issue_4) {
				if (size != 'm' && size != 'L')
					size = 'l';
			}
			ch = _tolower((int)ch);
		}
		if (ch != 'n' && !flag_eof) {
			if (ch != 'c' && ch != 'C' && ch != '[') {
				while (iswspace(inchar =
				    _wd_getwc(&chcount, iop)))
					;
				if (_wd_ungetwc(&chcount, inchar, iop) == WEOF)
					break;

			}
		}
#else  /* _WIDE */
		if (isupper(ch)) { /* no longer documented */
			if (_lib_version == c_issue_4) {
				if (size != 'm' && size != 'L')
					size = 'l';
			}
			ch = _tolower(ch);
		}
		if (ch != 'n' && !flag_eof) {
			if (ch != 'c' && ch != 'C' && ch != '[') {
				while (isspace(inchar = locgetc(chcount)))
					;
				if (locungetc(chcount, inchar) == EOF)
					break;
			}
		}
#endif /* _WIDE */

		switch (ch) {
		case 'C':
		case 'S':
		case 'c':
		case 's':
#ifdef	_WIDE
			if ((size == 'l') || (size == 'C') || (size == 'S'))
#else  /* _WIDE */
			if ((size == 'w') || (size == 'l') || (size == 'C') ||
			    (size == 'S'))
#endif /* _WIDE */
			{
				size = wstring(&chcount, &flag_eof, stow,
				    (int)ch, len, iop, &args.ap);
			} else {
				size = string(&chcount, &flag_eof, stow,
				    (int)ch, len, tab, iop, &args.ap);
			}
			break;
		case '[':
			if (size == 'l') {
				size = wbrstring(&chcount, &flag_eof, stow,
				    (int)ch, len, iop, bracket_str, &args.ap);
				free(bracket_str);
				bracket_str = NULL;
			} else {
#ifdef	_WIDE
				size = brstring(&chcount, &flag_eof, stow,
				    (int)ch, len, iop, bracket_str, &args.ap);
				free(bracket_str);
				bracket_str = NULL;
#else  /* _WIDE */
				size = string(&chcount, &flag_eof, stow,
				    ch, len, tab, iop, &args.ap);
#endif /* _WIDE */
			}
			break;

		case 'n':
			if (stow == 0)
				continue;
			if (size == 'b')	/* char */
				*va_arg(args.ap, char *) = (char)chcount;
			else if (size == 'h')
				*va_arg(args.ap, short *) = (short)chcount;
			else if (size == 'l')
				*va_arg(args.ap, long *) = (long)chcount;
			else if (size == 'm') /* long long */
				*va_arg(args.ap, long long *) =
				    (long long) chcount;
			else
				*va_arg(args.ap, int *) = (int)chcount;
			continue;

		case 'i':
		default:
			size = number(&chcount, &flag_eof, stow, (int)ch,
			    len, (int)size, iop, &args.ap);
			break;
		}
		if (size)
			nmatch += stow;
		else {
			return ((flag_eof && !nmatch) ? EOF : nmatch);
		}
		continue;
	}
	if (bracket_str)
		free(bracket_str);
	return (nmatch != 0 ? nmatch : EOF); /* end of input */
}

/* ****************************************************************** */
/* Functions to read the input stream in an attempt to match incoming */
/* data to the current pattern from the main loop of _doscan(). */
/* ****************************************************************** */
static int
number(int *chcount, int *flag_eof, int stow, int type, int len, int size,
	FILE *iop, va_list *listp)
{
	char	numbuf[64];
	char	*np = numbuf;
	int	c, base, inchar, lookahead;
	int	digitseen = 0, floater = 0, negflg = 0;
	int	lc;
	long long	lcval = 0LL;

	switch (type) {
	case 'e':
	case 'f':
	case 'g':
		/*
		 * lc = 0 corresponds to c90 mode: do not recognize
		 *	hexadecimal fp strings; attempt to push back
		 *	all unused characters read
		 *
		 * lc = -1 corresponds to c99 mode: recognize hexa-
		 *	decimal fp strings; push back at most one
		 *	unused character
		 */
		lc = (__xpg6 & _C99SUSv3_recognize_hexfp)? -1 : 0;
		floater = 1;
		break;

	case 'a':
		lc = -1;
		floater = 1;
		break;

	case 'd':
	case 'u':
	case 'i':
		base = 10;
		break;
	case 'o':
		base = 8;
		break;
	case 'p':
#ifdef	_LP64
		size = 'l'; /* pointers are long in LP64 */
#endif	/*	_LP64	*/
		/* FALLTHROUGH */
	case 'x':
		base = 16;
		break;
	default:
		return (0); /* unrecognized conversion character */
	}

	if (floater != 0) {
		/*
		 * Handle floating point with
		 * file_to_decimal.
		 */
		decimal_mode		dm;
		decimal_record		dr;
		fp_exception_field_type	efs;
		enum decimal_string_form form;
		char			*echar;
		int			nread;
		char			buffer[1024+1];
		char			*nb = buffer;

		if (len > 1024)
			len = 1024;
		file_to_decimal(&nb, len, lc, &dr, &form, &echar, iop, &nread);
		if (lc == -1) {
			/*
			 * In C99 mode, the entire string read has to be
			 * accepted in order to qualify as a match
			 */
			if (nb != buffer + nread)
				form = invalid_form;
		}
		if (stow && (form != invalid_form)) {
#if defined(__sparc)
			dm.rd = _QgetRD();
			if (size == 'L') {		/* long double */
				if ((int)form < 0)
					__hex_to_quadruple(&dr, dm.rd,
					    va_arg(*listp, quadruple *), &efs);
				else
					decimal_to_quadruple(
					    va_arg(*listp, quadruple *),
					    &dm, &dr, &efs);
			}
#elif defined(__i386) || defined(__amd64)
			dm.rd = __xgetRD();
			if (size == 'L') {		/* long double */
				if ((int)form < 0)
					__hex_to_extended(&dr, dm.rd,
					    va_arg(*listp, extended *), &efs);
				else
					decimal_to_extended(
					    va_arg(*listp, extended *),
					    &dm, &dr, &efs);
			}
#else
#error Unknown architecture
#endif
			else if (size == 'l') {		/* double */
				if ((int)form < 0)
					__hex_to_double(&dr, dm.rd,
					    va_arg(*listp, double *), &efs);
				else
					decimal_to_double(
					    va_arg(*listp, double *),
					    &dm, &dr, &efs);
			} else {			/* float */
				if ((int)form < 0)
					__hex_to_single(&dr, dm.rd,
					    va_arg(*listp, single *), &efs);
				else
					decimal_to_single((single *)
					    va_arg(*listp, single *),
					    &dm, &dr, &efs);
			}
			if ((efs & (1 << fp_overflow)) != 0) {
				errno = ERANGE;
			}
			if ((efs & (1 << fp_underflow)) != 0) {
				errno = ERANGE;
			}
		}
		(*chcount) += nread;	/* Count characters read. */
		c = locgetc((*chcount));
		if (locungetc((*chcount), c) == EOF)
			*flag_eof = 1;
		return ((form == invalid_form) ? 0 : 1);
				/* successful match if non-zero */
	}

	switch (c = locgetc((*chcount))) {
	case '-':
		negflg++;
		/* FALLTHROUGH */
	case '+':
		if (--len <= 0)
			break;
		if ((c = locgetc((*chcount))) != '0')
			break;
		/* FALLTHROUGH */
	case '0':
		/*
		 * If %i or %x, the characters 0x or 0X may optionally precede
		 * the sequence of letters and digits (base 16).
		 */
		if ((type != 'i' && type != 'x') || (len <= 1))
			break;
		if (((inchar = locgetc((*chcount))) == 'x') ||
		    (inchar == 'X')) {
			lookahead = readchar(iop, chcount);
			if (isxdigit(lookahead)) {
				base = 16;

				if (len <= 2) {
					(void) locungetc((*chcount), lookahead);
					/* Take into account the 'x' */
					len -= 1;
				} else {
					c = lookahead;
					/* Take into account '0x' */
					len -= 2;
				}
			} else {
				(void) locungetc((*chcount), lookahead);
				(void) locungetc((*chcount), inchar);
			}
		} else {
			/* inchar wans't 'x'. */
			(void) locungetc((*chcount), inchar); /* Put it back. */
			if (type == 'i') /* Only %i accepts an octal. */
				base = 8;
		}
	}
	for (; --len  >= 0; *np++ = (char)c, c = locgetc((*chcount))) {
		if (np > numbuf + 62) {
			errno = ERANGE;
			return (0);
		}
		if (isdigit(c) || base == 16 && isxdigit(c)) {
			int digit = c - (isdigit(c) ? '0' :
			    isupper(c) ? 'A' - 10 : 'a' - 10);
			if (digit >= base)
				break;
			if (stow)
				lcval = base * lcval + digit;
			digitseen++;
			continue;
		}
		break;
	}

	if (stow && digitseen) {
		/* suppress possible overflow on 2's-comp negation */
		if (negflg && lcval != (1ULL << 63))
			lcval = -lcval;
		switch (size) {
			case 'm':
				*va_arg(*listp, long long *) = lcval;
				break;
			case 'l':
				*va_arg(*listp, long *) = (long)lcval;
				break;
			case 'h':
				*va_arg(*listp, short *) = (short)lcval;
				break;
			case 'b':
				*va_arg(*listp, char *) = (char)lcval;
				break;
			default:
				*va_arg(*listp, int *) = (int)lcval;
				break;
		}
	}
	if (locungetc((*chcount), c) == EOF)
		*flag_eof = 1;
	return (digitseen); /* successful match if non-zero */
}

/* Get a character. If not using sscanf and at the buffer's end */
/* then do a direct read(). Characters read via readchar() */
/* can be  pushed back on the input stream by locungetc((*chcount),) */
/* since there is padding allocated at the end of the stream buffer. */
static int
readchar(FILE *iop, int *chcount)
{
	int	inchar;
	char	buf[1];

	if ((iop->_flag & _IOWRT) || (iop->_cnt != 0))
		inchar = locgetc((*chcount));
	else {
		if (read(FILENO(iop), buf, 1) != 1)
			return (EOF);
		inchar = (int)buf[0];
		(*chcount) += 1;
	}
	return (inchar);
}

static int
string(int *chcount, int *flag_eof, int stow, int type, int len, char *tab,
	FILE *iop, va_list *listp)
{
	int	ch;
	char	*ptr;
	char	*start;

	start = ptr = stow ? va_arg(*listp, char *) : NULL;
	if (((type == 'c') || (type == 'C')) && len == MAXINT)
		len = 1;
#ifdef	_WIDE
	while ((ch = locgetc((*chcount))) != EOF &&
	    !(((type == 's') || (type == 'S')) && isspace(ch))) {
#else  /* _WIDE */
	while ((ch = locgetc((*chcount))) != EOF &&
	    !(((type == 's') || (type == 'S')) &&
	    isspace(ch) || type == '[' && tab[ch])) {
#endif /* _WIDE */
		if (stow)
			*ptr = (char)ch;
		ptr++;
		if (--len <= 0)
			break;
	}
	if (ch == EOF) {
		(*flag_eof) = 1;
		(*chcount) -= 1;
	} else if (len > 0 && locungetc((*chcount), ch) == EOF)
		(*flag_eof) = 1;
	if (ptr == start)
		return (0);	/* no match */
	if (stow && ((type != 'c') && (type != 'C')))
		*ptr = '\0';
	return (1);	/* successful match */
}

/* This function initializes arglst, to contain the appropriate */
/* va_list values for the first MAXARGS arguments. */
/* WARNING: this code assumes that the sizes of all pointer types */
/* are the same. (Code similar to that in the portable doprnt.c */
/* should be used if this assumption is not true for a */
/* particular port.) */

#ifdef	_WIDE
static int
_mkarglst(const wchar_t *fmt, stva_list args, stva_list arglst[])
#else  /* _WIDE */
static int
_mkarglst(const char *fmt, stva_list args, stva_list arglst[])
#endif /* _WIDE */
{
#ifdef	_WIDE
#define	STRCHR	wcschr
#define	STRSPN	wcsspn
#define	ATOI(x)	_watoi((wchar_t *)x)
#define	SPNSTR1	L"01234567890"
#define	SPNSTR2	L"# +-.0123456789hL$"
#else  /* _WIDE */
#define	STRCHR	strchr
#define	STRSPN	strspn
#define	ATOI(x)	atoi(x)
#define	SPNSTR1	"01234567890"
#define	SPNSTR2	"# +-.0123456789hL$"
#endif /* _WIDE */

	int maxnum, curargno;
	size_t n;

	maxnum = -1;
	curargno = 0;

	while ((fmt = STRCHR(fmt, '%')) != NULL) {
		fmt++;	/* skip % */
		if (*fmt == '*' || *fmt == '%')
			continue;
		if (fmt[n = STRSPN(fmt, SPNSTR1)] == L'$') {
			/* convert to zero base */
			curargno = ATOI(fmt) - 1;
			fmt += n + 1;
		}

		if (maxnum < curargno)
			maxnum = curargno;
		curargno++;	/* default to next in list */

		fmt += STRSPN(fmt, SPNSTR2);
		if (*fmt == '[') {
			int	i;
			fmt++; /* has to be at least on item in scan list */
			if (*fmt == ']') {
				fmt++;
			}
			while (*fmt != ']') {
				if (*fmt == L'\0') {
					return (-1); /* bad format */
#ifdef	_WIDE
				} else {
					fmt++;
				}
#else  /* _WIDE */
				} else if (isascii(*fmt)) {
					fmt++;
				} else {
					i = mblen((const char *)
					    fmt, MB_CUR_MAX);
					if (i <= 0) {
						return (-1);
					} else {
						fmt += i;
					}
				}
#endif /* _WIDE */
			}
		}
	}
	if (maxnum > MAXARGS)
		maxnum = MAXARGS;
	for (n = 0; n <= maxnum; n++) {
		arglst[n] = args;
		(void) va_arg(args.ap, void *);
	}
	return (0);
}


/*
 * For wide character handling
 */

#ifdef	_WIDE
static int
wstring(int *chcount, int *flag_eof, int stow, int type,
	int len, FILE *iop, va_list *listp)
{
	wint_t	wch;
	wchar_t	*ptr;
	wchar_t	*wstart;
	int	dummy;

	wstart = ptr = stow ? va_arg(*listp, wchar_t *) : NULL;

	if ((type == 'c') && len == MAXINT)
		len = 1;
	while (((wch = _wd_getwc(chcount, iop)) != WEOF) &&
	    !(type == 's' && iswspace(wch))) {
		if (stow)
			*ptr = wch;
		ptr++;
		if (--len <= 0)
			break;
	}
	if (wch == WEOF) {
		*flag_eof = 1;
		(*chcount) -= 1;
	} else {
		if (len > 0 && _wd_ungetwc(chcount, wch, iop) == WEOF)
			*flag_eof = 1;
	}
	if (ptr == wstart)
		return (0); /* no match */
	if (stow && (type != 'c'))
		*ptr = '\0';
	return (1); /* successful match */
}

#else  /* _WIDE */
static int
wstring(int *chcount, int *flag_eof, int stow, int type, int len, FILE *iop,
	va_list *listp)
{
	int	wch;
	wchar_t	*ptr;
	wchar_t	*wstart;

	wstart = ptr = stow ? va_arg(*listp, wchar_t *) : NULL;

	if ((type == 'c') && len == MAXINT)
		len = 1;
	while (((wch = _bi_getwc(iop)) != EOF) &&
	    !(type == 's' && (isascii(wch) ? isspace(wch) : 0))) {
		(*chcount) += _scrwidth((wchar_t)wch);
		if (stow)
			*ptr = wch;
		ptr++;
		if (--len <= 0)
			break;
	}
	if (wch == EOF) {
		(*flag_eof) = 1;
		(*chcount) -= 1;
	} else {
		if (len > 0 && _bi_ungetwc(wch, iop) == EOF)
			(*flag_eof) = 1;
	}
	if (ptr == wstart)
		return (0); /* no match */
	if (stow && (type != 'c'))
		*ptr = '\0';
	return (1); /* successful match */
}
#endif /* _WIDE */

#ifdef	_WIDE
static wint_t
_wd_getwc(int *chcount, FILE *iop)
{
	wint_t	wc;
	int	len;

	if (!(iop->_flag & _IOWRT)) {
		/* call from fwscanf, wscanf */
		wc = __fgetwc_xpg5(iop);
		(*chcount)++;
		return (wc);
	} else {
		/* call from swscanf */
		if (*iop->_ptr == '\0')
			return (WEOF);
		len = mbtowc((wchar_t *)&wc, (const char *)iop->_ptr,
		    MB_CUR_MAX);
		if (len == -1)
			return (WEOF);
		iop->_ptr += len;
		(*chcount)++;
		return (wc);
	}
}

static wint_t
_wd_ungetwc(int *chcount, wchar_t wc, FILE *iop)
{
	wint_t	ret;
	int	len;
	char	mbs[MB_LEN_MAX];

	if (wc == WEOF)
		return (WEOF);

	if (!(iop->_flag & _IOWRT)) {
		/* call from fwscanf, wscanf */
		ret = __ungetwc_xpg5((wint_t)wc, iop);
		if (ret != (wint_t)wc)
			return (WEOF);
		(*chcount)--;
		return (ret);
	} else {
		/* call from swscanf */
		len = wctomb(mbs, wc);
		if (len == -1)
			return (WEOF);
		iop->_ptr -= len;
		(*chcount)--;
		return ((wint_t)wc);
	}
}

static int
_watoi(wchar_t *fmt)
{
	int	n = 0;
	wchar_t	ch;

	ch = *fmt;
	if ((ch >= 0) && (ch < 256) && isdigit((int)ch)) {
		n = ch - '0';
		while (((ch = *++fmt) >= 0) && (ch < 256) &&
		    isdigit((int)ch)) {
			n *= 10;
			n += ch - '0';
		}
	}
	return (n);
}
#endif /* _WIDE */

/* ARGSUSED3 */
static int
wbrstring(int *chcount, int *flag_eof, int stow, int type,
	int len, FILE *iop, unsigned char *brstr, va_list *listp)
{
	wint_t	wch;
	int	i;
	char	str[MB_LEN_MAX + 1]; /* include null termination */
	wchar_t	*ptr, *start;
#ifdef	_WIDE
	int	dummy;
#endif /* _WIDE */

	start = ptr = stow ? va_arg(*listp, wchar_t *) : NULL;

#ifdef	_WIDE
	while ((wch = _wd_getwc(&dummy, iop)) != WEOF) {
#else  /* _WIDE */
	while ((wch = _bi_getwc(iop)) != WEOF) {
#endif /* _WIDE */
		i = wctomb(str, (wchar_t)wch);
		if (i == -1) {
			return (0);
		}
		str[i] = '\0';
		if (fnmatch((const char *)brstr, (const char *)str,
		    FNM_NOESCAPE)) {
			break;
		} else {
			if (len > 0) {
#ifdef	_WIDE
				(*chcount)++;
#else  /* _WIDE */
				(*chcount) += _scrwidth(wch);
#endif /* _WIDE */
				len--;
				if (stow) {
					*ptr = wch;
				}
				ptr++;
				if (len <= 0)
					break;
			} else {
				break;
			}
		}
	}
	if (wch == WEOF) {
		*flag_eof = 1;
	} else {
#ifdef	_WIDE
		if (len > 0 && _wd_ungetwc(&dummy, wch, iop) == WEOF)
#else  /* _WIDE */
		if (len > 0 && _bi_ungetwc(wch, iop) == WEOF)
#endif /* _WIDE */
			*flag_eof = 1;
	}
	if (ptr == start)
		return (0);				/* no match */
	if (stow)
		*ptr = L'\0';
	return (1);					/* successful match */
}

#ifdef	_WIDE
static int
brstring(int *chcount, int *flag_eof, int stow, int type,
	int len, FILE *iop, unsigned char *brstr, va_list *listp)
{
	wint_t	wch;
	int	i;
	char	str[MB_LEN_MAX + 1]; /* include null termination */
	char	*ptr, *start, *p;
	int	dummy;

	start = ptr = stow ? va_arg(*listp, char *) : NULL;

	while ((wch = _wd_getwc(&dummy, iop)) != WEOF) {
		p = str;
		i = wctomb(str, (wchar_t)wch);
		if (i == -1) {
			return (0);
		}
		str[i] = '\0';
		if (fnmatch((const char *)brstr, (const char *)str,
		    FNM_NOESCAPE)) {
			break;
		} else {
			if (len >= i) {
				(*chcount)++;
				len -= i;
				if (stow) {
					while (i-- > 0) {
						*ptr++ = *p++;
					}
				} else {
					while (i-- > 0) {
						ptr++;
					}
				}
				if (len <= 0)
					break;
			} else {
				break;
			}
		}
	}
	if (wch == WEOF) {
		*flag_eof = 1;
	} else {
		if (len > 0 && _wd_ungetwc(&dummy, wch, iop) == WEOF)
			*flag_eof = 1;
	}
	if (ptr == start)
		return (0);				/* no match */
	if (stow)
		*ptr = '\0';
	return (1);					/* successful match */
}
#endif /* _WIDE */

/*
 * Locally define getwc and ungetwc
 */
static int
_bi_getwc(FILE *iop)
{
	int c;
	wchar_t intcode;
	int i, nbytes, cur_max;
	char buff[MB_LEN_MAX];

	if ((c = wlocgetc()) == EOF)
		return (WEOF);

	if (isascii(c))	/* ASCII code */
		return ((wint_t)c);

	buff[0] = (char)c;

	cur_max = (int)MB_CUR_MAX;
	/* MB_CUR_MAX doen't exeed the value of MB_LEN_MAX */
	/* So we use MB_CUR_MAX instead of MB_LEN_MAX for */
	/* improving the performance. */
	for (i = 1; i < cur_max; i++) {
		c = wlocgetc();
		if (c == '\n') {
			(void) wlocungetc(c);
			break;
		}
		if (c == EOF) {
			/* this still may be a valid multibyte character */
			break;
		}
		buff[i] = (char)c;
	}

	if ((nbytes = mbtowc(&intcode, buff, i)) == -1) {
		/*
		 * If mbtowc fails, the input was not a legal character.
		 *	ungetc all but one character.
		 *
		 * Note:  the number of pushback characters that
		 *	ungetc() can handle must be >= (MB_LEN_MAX - 1).
		 *	In Solaris 2.x, the number of pushback
		 *	characters is 4.
		 */
		while (i-- > 1) {
			(void) wlocungetc((signed char)buff[i]);
		}
		errno = EILSEQ;
		return (WEOF); /* Illegal EUC sequence. */
	}

	while (i-- > nbytes) {
		/*
		 * Note:  the number of pushback characters that
		 *	ungetc() can handle must be >= (MB_LEN_MAX - 1).
		 *	In Solaris 2.x, the number of pushback
		 *	characters is 4.
		 */
		(void) wlocungetc((signed char)buff[i]);
	}
	return ((int)intcode);
}

static int
_bi_ungetwc(wint_t wc, FILE *iop)
{
	char mbs[MB_LEN_MAX];
	unsigned char *p;
	int n;

	if ((wc == WEOF) || ((iop->_flag & _IOREAD) == 0))
		return (WEOF);

	n = wctomb(mbs, (wchar_t)wc);
	if (n <= 0)
		return (WEOF);

	if (iop->_ptr <= iop->_base) {
		if (iop->_base == NULL) {
			return (WEOF);
		}
		if ((iop->_ptr == iop->_base) && (iop->_cnt == 0)) {
			++iop->_ptr;
		} else if ((iop->_ptr - n) < (iop->_base - PUSHBACK)) {
			return (WEOF);
		}
	}

	p = (unsigned char *)(mbs+n-1); /* p points the last byte */
	/* if _IOWRT is set to iop->_flag, it means this is */
	/* an invocation from sscanf(), and in that time we */
	/* don't touch iop->_cnt.  Otherwise, which means an */
	/* invocation from fscanf() or scanf(), we touch iop->_cnt */
	if ((iop->_flag & _IOWRT) == 0) {
		/* scanf() and fscanf() */
		iop->_cnt += n;
		while (n--) {
			*--iop->_ptr = *(p--);
		}
	} else {
		/* sscanf() */
		iop->_ptr -= n;
	}
	return (wc);
}