/* * 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 */ #include "lint.h" #include #include "mtlib.h" #include "file64.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 __unused) #else /* _WIDE */ int __doscan_u(FILE *iop, const char *sfmt, va_list va_Alist, int scflag __unused) #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 (_xread(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 __unused, 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 == '[') { 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 { int i; 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; 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 */ static int wbrstring(int *chcount, int *flag_eof, int stow, int type __unused, 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 __unused, 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); }