summaryrefslogtreecommitdiff
path: root/usr/src/lib/libc/port/stdio/doscan.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libc/port/stdio/doscan.c')
-rw-r--r--usr/src/lib/libc/port/stdio/doscan.c1333
1 files changed, 1333 insertions, 0 deletions
diff --git a/usr/src/lib/libc/port/stdio/doscan.c b/usr/src/lib/libc/port/stdio/doscan.c
new file mode 100644
index 0000000000..961afe65c8
--- /dev/null
+++ b/usr/src/lib/libc/port/stdio/doscan.c
@@ -0,0 +1,1333 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 (c) 1988 AT&T */
+/* All Rights Reserved */
+
+
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "synonyms.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 */
+ sargs; /* used to save the 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 ((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 ((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);
+}