From caed8420ef5be27f4b6e2b07a39e21642e6d1ceb Mon Sep 17 00:00:00 2001 From: "Peter S. Mazinger" Date: Mon, 13 Feb 2006 07:36:40 +0000 Subject: More renames for IMA --- libc/misc/wctype/Makefile.in | 2 +- libc/misc/wctype/_wctype.c | 947 ++++++++++++++++++ libc/misc/wctype/wctype.c | 947 ------------------ libc/stdio/Makefile.in | 4 +- libc/stdio/_scanf.c | 2268 ++++++++++++++++++++++++++++++++++++++++++ libc/stdio/_vfprintf.c | 1951 ++++++++++++++++++++++++++++++++++++ libc/stdio/scanf.c | 2268 ------------------------------------------ libc/stdio/vfprintf.c | 1951 ------------------------------------ 8 files changed, 5169 insertions(+), 5169 deletions(-) create mode 100644 libc/misc/wctype/_wctype.c delete mode 100644 libc/misc/wctype/wctype.c create mode 100644 libc/stdio/_scanf.c create mode 100644 libc/stdio/_vfprintf.c delete mode 100644 libc/stdio/scanf.c delete mode 100644 libc/stdio/vfprintf.c (limited to 'libc') diff --git a/libc/misc/wctype/Makefile.in b/libc/misc/wctype/Makefile.in index dc008ec7d..556b98856 100644 --- a/libc/misc/wctype/Makefile.in +++ b/libc/misc/wctype/Makefile.in @@ -6,7 +6,7 @@ # Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. # -MSRC:=wctype.c +MSRC:=_wctype.c MOBJ:= iswalnum.o iswalpha.o iswcntrl.o iswdigit.o iswgraph.o \ iswlower.o iswprint.o iswpunct.o iswspace.o iswupper.o \ iswxdigit.o iswblank.o wctrans.o towctrans.o \ diff --git a/libc/misc/wctype/_wctype.c b/libc/misc/wctype/_wctype.c new file mode 100644 index 000000000..a7d111039 --- /dev/null +++ b/libc/misc/wctype/_wctype.c @@ -0,0 +1,947 @@ +/* Copyright (C) 2002, 2003 Manuel Novoa III + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! + * + * Besides uClibc, I'm using this code in my libc for elks, which is + * a 16-bit environment with a fairly limited compiler. It would make + * things much easier for me if this file isn't modified unnecessarily. + * In particular, please put any new or replacement functions somewhere + * else, and modify the makefile to use your version instead. + * Thanks. Manuel + * + * ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! */ + +#define __NO_CTYPE + +#include +#include +#include +#include +#include +#include +#include +#include + +libc_hidden_proto(strcmp) +libc_hidden_proto(__C_ctype_tolower) +libc_hidden_proto(tolower) +libc_hidden_proto(__C_ctype_toupper) +libc_hidden_proto(toupper) +libc_hidden_proto(towlower) +libc_hidden_proto(towupper) +libc_hidden_proto(towctrans) +libc_hidden_proto(iswctype) + +#if defined(__LOCALE_C_ONLY) && defined(__UCLIBC_DO_XLOCALE) +#error xlocale functionality is not supported in stub locale mode. +#endif + +#ifdef __UCLIBC_HAS_XLOCALE__ +#include +libc_hidden_proto(towlower_l) +libc_hidden_proto(towupper_l) +libc_hidden_proto(towctrans_l) +libc_hidden_proto(iswctype_l) +#else +libc_hidden_proto(__ctype_b) +#endif /* __UCLIBC_HAS_XLOCALE__ */ +#ifdef __UCLIBC_HAS_LOCALE__ +libc_hidden_proto(__global_locale) +#endif + +/* We know wide char support is enabled. We wouldn't be here otherwise. */ + +/* Define this if you want to unify the towupper and towlower code in the + * towctrans function. */ +/* #define SMALL_UPLOW */ + +/**********************************************************************/ +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_iswspace +/* generates one warning */ +#warning TODO: Fix the __CTYPE_* codes! +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#if 1 +/* Taking advantage of the C99 mutual-exclusion guarantees for the various + * (w)ctype classes, including the descriptions of printing and control + * (w)chars, we can place each in one of the following mutually-exlusive + * subsets. Since there are less than 16, we can store the data for + * each (w)chars in a nibble. In contrast, glibc uses an unsigned int + * per (w)char, with one bit flag for each is* type. While this allows + * a simple '&' operation to determine the type vs. a range test and a + * little special handling for the "blank" and "xdigit" types in my + * approach, it also uses 8 times the space for the tables on the typical + * 32-bit archs we supported.*/ +enum { + __CTYPE_unclassified = 0, + __CTYPE_alpha_nonupper_nonlower, + __CTYPE_alpha_lower, + __CTYPE_alpha_upper_lower, + __CTYPE_alpha_upper, + __CTYPE_digit, + __CTYPE_punct, + __CTYPE_graph, + __CTYPE_print_space_nonblank, + __CTYPE_print_space_blank, + __CTYPE_space_nonblank_noncntrl, + __CTYPE_space_blank_noncntrl, + __CTYPE_cntrl_space_nonblank, + __CTYPE_cntrl_space_blank, + __CTYPE_cntrl_nonspace +}; +#endif + + +/* The following is used to implement wctype(), but it is defined + * here because the ordering must agree with that of the enumeration + * below (ignoring unclassified). */ +#define __CTYPE_TYPESTRING \ + "\6alnum\0\6alpha\0\6blank\0\6cntrl\0\6digit\0\6graph\0\6lower\0" \ + "\6print\0\6punct\0\6space\0\6upper\0\7xdigit\0\0" + + +/* The values for wctype_t. */ +enum { + _CTYPE_unclassified = 0, + _CTYPE_isalnum, + _CTYPE_isalpha, + _CTYPE_isblank, + _CTYPE_iscntrl, + _CTYPE_isdigit, + _CTYPE_isgraph, + _CTYPE_islower, + _CTYPE_isprint, + _CTYPE_ispunct, + _CTYPE_isspace, + _CTYPE_isupper, + _CTYPE_isxdigit /* _MUST_ be last of the standard classes! */ +}; + +/* The following is used to implement wctrans(). */ + +#define __CTYPE_TRANSTRING "\10tolower\0\10toupper\0\10totitle\0\0" + +enum { + _CTYPE_tolower = 1, + _CTYPE_toupper, + _CTYPE_totitle +}; + +/*--------------------------------------------------------------------*/ + +#define _CTYPE_iswxdigit (_CTYPE_isxdigit) + +/*--------------------------------------------------------------------*/ + +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_iswspace +/* generates one warning */ +#warning TODO: Fix WC* defines! +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#define ENCODING ((__UCLIBC_CURLOCALE_DATA).encoding) + +#define WCctype ((__UCLIBC_CURLOCALE_DATA).tblwctype) +#define WCuplow ((__UCLIBC_CURLOCALE_DATA).tblwuplow) +#define WCcmob ((__UCLIBC_CURLOCALE_DATA).tblwcomb) +#define WCuplow_diff ((__UCLIBC_CURLOCALE_DATA).tblwuplow_diff) + + +#define WC_TABLE_DOMAIN_MAX __LOCALE_DATA_WC_TABLE_DOMAIN_MAX + +#define WCctype_II_LEN __LOCALE_DATA_WCctype_II_LEN +#define WCctype_TI_LEN __LOCALE_DATA_WCctype_TI_LEN +#define WCctype_UT_LEN __LOCALE_DATA_WCctype_UT_LEN +#define WCctype_II_SHIFT __LOCALE_DATA_WCctype_II_SHIFT +#define WCctype_TI_SHIFT __LOCALE_DATA_WCctype_TI_SHIFT + +#define WCuplow_II_LEN __LOCALE_DATA_WCuplow_II_LEN +#define WCuplow_TI_LEN __LOCALE_DATA_WCuplow_TI_LEN +#define WCuplow_UT_LEN __LOCALE_DATA_WCuplow_UT_LEN +#define WCuplow_II_SHIFT __LOCALE_DATA_WCuplow_II_SHIFT +#define WCuplow_TI_SHIFT __LOCALE_DATA_WCuplow_TI_SHIFT + + +#define WCctype_TI_MASK ((1 << (WCctype_TI_SHIFT)) - 1) +#define WCctype_II_MASK ((1 << (WCctype_II_SHIFT)) - 1) + +/**********************************************************************/ + +#undef __PASTE2 +#undef __PASTE3 +#define __PASTE2(X,Y) X ## Y +#define __PASTE3(X,Y,Z) X ## Y ## Z + +#ifdef __UCLIBC_DO_XLOCALE + +#define ISW_FUNC_BODY(NAME) \ +libc_hidden_proto(__PASTE3(isw,NAME,_l)); \ +int __PASTE3(isw,NAME,_l) (wint_t wc, __locale_t l) \ +{ \ + return iswctype_l(wc, __PASTE2(_CTYPE_is,NAME), l); \ +} \ +libc_hidden_def(__PASTE3(isw,NAME,_l)) + +#else /* __UCLIBC_DO_XLOCALE */ + +#define ISW_FUNC_BODY(NAME) \ +libc_hidden_proto(__PASTE2(isw,NAME)); \ +int __PASTE2(isw,NAME) (wint_t wc) \ +{ \ + return iswctype(wc, __PASTE2(_CTYPE_is,NAME)); \ +} \ +libc_hidden_def(__PASTE2(isw,NAME)) + +#endif /* __UCLIBC_DO_XLOCALE */ +/**********************************************************************/ +#if defined(L_iswalnum) || defined(L_iswalnum_l) + +ISW_FUNC_BODY(alnum); + +#endif +/**********************************************************************/ +#if defined(L_iswalpha) || defined(L_iswalpha_l) + +ISW_FUNC_BODY(alpha); + +#endif +/**********************************************************************/ +#if defined(L_iswblank) || defined(L_iswblank_l) + +ISW_FUNC_BODY(blank); + +#endif +/**********************************************************************/ +#if defined(L_iswcntrl) || defined(L_iswcntrl_l) + +ISW_FUNC_BODY(cntrl); + +#endif +/**********************************************************************/ +#if defined(L_iswdigit) || defined(L_iswdigit_l) + +ISW_FUNC_BODY(digit); + +#endif +/**********************************************************************/ +#if defined(L_iswgraph) || defined(L_iswgraph_l) + +ISW_FUNC_BODY(graph); + +#endif +/**********************************************************************/ +#if defined(L_iswlower) || defined(L_iswlower_l) + +ISW_FUNC_BODY(lower); + +#endif +/**********************************************************************/ +#if defined(L_iswprint) || defined(L_iswprint_l) + +ISW_FUNC_BODY(print); + +#endif +/**********************************************************************/ +#if defined(L_iswpunct) || defined(L_iswpunct_l) + +ISW_FUNC_BODY(punct); + +#endif +/**********************************************************************/ +#if defined(L_iswspace) || defined(L_iswspace_l) + +ISW_FUNC_BODY(space); + +#endif +/**********************************************************************/ +#if defined(L_iswupper) || defined(L_iswupper_l) + +ISW_FUNC_BODY(upper); + +#endif +/**********************************************************************/ +#if defined(L_iswxdigit) || defined(L_iswxdigit_l) + +ISW_FUNC_BODY(xdigit); + +#endif +/**********************************************************************/ +#if defined(L_towlower) || defined(L_towlower_l) + +#ifdef L_towlower +#define TOWLOWER(w) towlower(w) +#else /* L_towlower */ +#define TOWLOWER(w) towlower_l(w, __locale_t locale) +#undef __UCLIBC_CURLOCALE_DATA +#undef __UCLIBC_CURLOCALE +#define __UCLIBC_CURLOCALE_DATA (*locale) +#define __UCLIBC_CURLOCALE (locale) +#endif /* L_towlower */ + +#ifdef __UCLIBC_HAS_XLOCALE__ +#define TOWCTRANS(w,d) towctrans_l(w,d, __UCLIBC_CURLOCALE) +#else /* __UCLIBC_HAS_XLOCALE__ */ +#define TOWCTRANS(w,d) towctrans(w,d) +#endif /* __UCLIBC_HAS_XLOCALE__ */ + +#define __C_towlower(wc) \ + ((((__uwchar_t)(wc)) <= 0x7f) ? (__C_ctype_tolower)[(wc)] : (wc)) + +#ifdef __LOCALE_C_ONLY + +wint_t towlower(wint_t wc) +{ +#ifdef __UCLIBC_HAS_CTYPE_TABLES__ + return __C_towlower(wc); +#else + return (wc == ((unsigned int)(wc))) + ? __C_tolower(((unsigned int)(wc))) + : 0; +#endif +} + +#else /* __LOCALE_C_ONLY */ + +#ifdef SMALL_UPLOW + +#if defined(L_towlower) && defined(__UCLIBC_HAS_XLOCALE__) + +wint_t towlower(wint_t wc) +{ + return towctrans_l(wc, _CTYPE_tolower, __UCLIBC_CURLOCALE); +} + +#else /* defined(L_towlower) && defined(__UCLIBC_HAS_XLOCALE__) */ + +wint_t TOWLOWER(wint_t wc) +{ + return TOWCTRANS(wc, _CTYPE_tolower); +} + +#endif /* defined(L_towlower) && defined(__UCLIBC_HAS_XLOCALE__) */ + +#else /* SMALL_UPLOW */ + +#if defined(L_towlower) && defined(__UCLIBC_HAS_XLOCALE__) + +wint_t towlower(wint_t wc) +{ + return towlower_l(wc, __UCLIBC_CURLOCALE); +} + +#else /* defined(L_towlower) && defined(__UCLIBC_HAS_XLOCALE__) */ + +wint_t TOWLOWER(wint_t wc) +{ + unsigned int sc, n, i; + __uwchar_t u = wc; + + if (ENCODING == __ctype_encoding_7_bit) { + /* We're in the C/POSIX locale, so ignore the tables. */ + return __C_towlower(wc); + } + + if (u <= WC_TABLE_DOMAIN_MAX) { + sc = u & ((1 << WCuplow_TI_SHIFT) - 1); + u >>= WCuplow_TI_SHIFT; + n = u & ((1 << WCuplow_II_SHIFT) - 1); + u >>= WCuplow_II_SHIFT; + + i = ((unsigned int) WCuplow[u]) << WCuplow_II_SHIFT; + i = ((unsigned int) WCuplow[WCuplow_II_LEN + i + n]) + << WCuplow_TI_SHIFT; + i = ((unsigned int) WCuplow[WCuplow_II_LEN + WCuplow_TI_LEN + + i + sc]) << 1; + wc += WCuplow_diff[i + 1]; + } + return wc; +} + +#endif /* defined(L_towlower) && defined(__UCLIBC_HAS_XLOCALE__) */ + +#endif /* SMALL_UPLOW */ + +#ifdef L_towlower_l +libc_hidden_def(towlower_l) +#endif /* L_towlower_l */ + +#endif /* __LOCALE_C_ONLY */ + +#ifndef L_towlower_l +libc_hidden_def(towlower) +#endif + +#endif +/**********************************************************************/ +#if defined(L_towupper) || defined(L_towupper_l) + +#ifdef L_towupper +#define TOWUPPER(w) towupper(w) +#else /* L_towupper */ +#define TOWUPPER(w) towupper_l(w, __locale_t locale) +#undef __UCLIBC_CURLOCALE_DATA +#undef __UCLIBC_CURLOCALE +#define __UCLIBC_CURLOCALE_DATA (*locale) +#define __UCLIBC_CURLOCALE (locale) +#endif /* L_towupper */ + +#ifdef __UCLIBC_HAS_XLOCALE__ +#define TOWCTRANS(w,d) towctrans_l(w,d, __UCLIBC_CURLOCALE) +#else /* __UCLIBC_HAS_XLOCALE__ */ +#define TOWCTRANS(w,d) towctrans(w,d) +#endif /* __UCLIBC_HAS_XLOCALE__ */ + +#define __C_towupper(wc) \ + ((((__uwchar_t)(wc)) <= 0x7f) ? (__C_ctype_toupper)[(wc)] : (wc)) + +#ifdef __LOCALE_C_ONLY + +wint_t towupper(wint_t wc) +{ +#ifdef __UCLIBC_HAS_CTYPE_TABLES__ + return __C_towupper(wc); +#else + return (wc == ((unsigned int)(wc))) + ? __C_toupper(((unsigned int)(wc))) + : 0; +#endif + +} + +#else /* __LOCALE_C_ONLY */ + +#ifdef SMALL_UPLOW + +#if defined(L_towupper) && defined(__UCLIBC_HAS_XLOCALE__) + +wint_t towupper(wint_t wc) +{ + return towctrans_l(wc, _CTYPE_toupper, __UCLIBC_CURLOCALE); +} + +#else /* defined(L_towupper) && defined(__UCLIBC_HAS_XLOCALE__) */ + +wint_t TOWUPPER(wint_t wc) +{ + return TOWCTRANS(wc, _CTYPE_toupper); +} + +#endif /* defined(L_towupper) && defined(__UCLIBC_HAS_XLOCALE__) */ + +#else /* SMALL_UPLOW */ + +#if defined(L_towupper) && defined(__UCLIBC_HAS_XLOCALE__) + +wint_t towupper(wint_t wc) +{ + return towupper_l(wc, __UCLIBC_CURLOCALE); +} + +#else /* defined(L_towupper) && defined(__UCLIBC_HAS_XLOCALE__) */ + +wint_t TOWUPPER(wint_t wc) +{ + unsigned int sc, n, i; + __uwchar_t u = wc; + + if (ENCODING == __ctype_encoding_7_bit) { + /* We're in the C/POSIX locale, so ignore the tables. */ + return __C_towupper(wc); + } + + if (u <= WC_TABLE_DOMAIN_MAX) { + sc = u & ((1 << WCuplow_TI_SHIFT) - 1); + u >>= WCuplow_TI_SHIFT; + n = u & ((1 << WCuplow_II_SHIFT) - 1); + u >>= WCuplow_II_SHIFT; + + i = ((unsigned int) WCuplow[u]) << WCuplow_II_SHIFT; + i = ((unsigned int) WCuplow[WCuplow_II_LEN + i + n]) + << WCuplow_TI_SHIFT; + i = ((unsigned int) WCuplow[WCuplow_II_LEN + WCuplow_TI_LEN + + i + sc]) << 1; + wc += WCuplow_diff[i]; + } + return wc; +} + +#endif /* defined(L_towupper) && defined(__UCLIBC_HAS_XLOCALE__) */ + +#endif /* SMALL_UPLOW */ + +#ifdef L_towupper_l +libc_hidden_def(towupper_l) +#endif /* L_towupper_l */ + +#endif /* __LOCALE_C_ONLY */ + +#ifndef L_towupper_l +libc_hidden_def(towupper) +#endif + +#endif +/**********************************************************************/ +#ifdef L_wctype + +static const unsigned char typestring[] = __CTYPE_TYPESTRING; +/* extern const unsigned char typestring[]; */ + +libc_hidden_proto(wctype) +wctype_t wctype(const char *property) +{ + const unsigned char *p; + int i; + + p = typestring; + i = 1; + do { + if (!strcmp(property, ++p)) { + return i; + } + ++i; + p += p[-1]; + } while (*p); + + /* TODO - Add locale-specific classifications. */ + return 0; +} +libc_hidden_def(wctype) + +#endif +/**********************************************************************/ +#ifdef L_wctype_l + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning REMINDER: Currently wctype_l simply calls wctype. +#endif /* __UCLIBC_MJN3_ONLY__ */ + +libc_hidden_proto(wctype) + +libc_hidden_proto(wctype_l) +wctype_t wctype_l (const char *property, __locale_t locale) +{ + return wctype(property); +} +libc_hidden_def(wctype_l) + +#endif +/**********************************************************************/ +#if defined(L_iswctype) || defined(L_iswctype_l) + +#define __C_iswdigit(c) \ + ((sizeof(c) == sizeof(char)) \ + ? (((unsigned char)((c) - '0')) < 10) \ + : (((__uwchar_t)((c) - '0')) < 10)) +#define __C_iswxdigit(c) \ + (__C_iswdigit(c) \ + || ((sizeof(c) == sizeof(char)) \ + ? (((unsigned char)((((c)) | 0x20) - 'a')) < 6) \ + : (((__uwchar_t)((((c)) | 0x20) - 'a')) < 6))) + +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_iswctype +#warning CONSIDER: Change to bit shift? would need to sync with wctype.h +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + + +#ifdef __UCLIBC_HAS_CTYPE_TABLES__ +#if !defined(__UCLIBC_HAS_XLOCALE__) || defined(L_iswctype_l) + +static const unsigned short int desc2flag[] = { + [_CTYPE_unclassified] = 0, + [_CTYPE_isalnum] = (unsigned short int) _ISwalnum, + [_CTYPE_isalpha] = (unsigned short int) _ISwalpha, + [_CTYPE_isblank] = (unsigned short int) _ISwblank, + [_CTYPE_iscntrl] = (unsigned short int) _ISwcntrl, + [_CTYPE_isdigit] = (unsigned short int) _ISwdigit, + [_CTYPE_isgraph] = (unsigned short int) _ISwgraph, + [_CTYPE_islower] = (unsigned short int) _ISwlower, + [_CTYPE_isprint] = (unsigned short int) _ISwprint, + [_CTYPE_ispunct] = (unsigned short int) _ISwpunct, + [_CTYPE_isspace] = (unsigned short int) _ISwspace, + [_CTYPE_isupper] = (unsigned short int) _ISwupper, + [_CTYPE_isxdigit] = (unsigned short int) _ISwxdigit, +}; + +#endif /* defined(L_iswctype_L) || defined(__LOCALE_C_ONLY) */ +#endif /* __UCLIBC_HAS_CTYPE_TABLES__ */ + +#ifdef __LOCALE_C_ONLY + +#ifdef __UCLIBC_HAS_CTYPE_TABLES__ + +int iswctype(wint_t wc, wctype_t desc) +{ + /* Note... wctype_t is unsigned. */ + + if ((((__uwchar_t) wc) <= 0x7f) + && (desc < (sizeof(desc2flag)/sizeof(desc2flag[0]))) + ) { + return __isctype(wc, desc2flag[desc]); + } + return 0; +} + +#else /* __UCLIBC_HAS_CTYPE_TABLES__ */ + +int iswctype(wint_t wc, wctype_t desc) +{ + /* This is lame, but it is here just to get it working for now. */ + + if (wc == ((unsigned int)(wc))) { + switch(desc) { + case _CTYPE_isupper: + return __C_isupper((unsigned int)(wc)); + case _CTYPE_islower: + return __C_islower((unsigned int)(wc)); + case _CTYPE_isalpha: + return __C_isalpha((unsigned int)(wc)); + case _CTYPE_isdigit: + return __C_isdigit((unsigned int)(wc)); + case _CTYPE_isxdigit: + return __C_isxdigit((unsigned int)(wc)); + case _CTYPE_isspace: + return __C_isspace((unsigned int)(wc)); + case _CTYPE_isprint: + return __C_isprint((unsigned int)(wc)); + case _CTYPE_isgraph: + return __C_isgraph((unsigned int)(wc)); + case _CTYPE_isblank: + return __C_isblank((unsigned int)(wc)); + case _CTYPE_iscntrl: + return __C_iscntrl((unsigned int)(wc)); + case _CTYPE_ispunct: + return __C_ispunct((unsigned int)(wc)); + case _CTYPE_isalnum: + return __C_isalnum((unsigned int)(wc)); + default: + break; + } + } + return 0; +} + +#endif /* __UCLIBC_HAS_CTYPE_TABLES__ */ + +#else /* __LOCALE_C_ONLY */ + +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_iswctype +#warning CONSIDER: Handle combining class? +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#ifdef L_iswctype +#define ISWCTYPE(w,d) iswctype(w,d) +#else /* L_iswctype */ +#define ISWCTYPE(w,d) iswctype_l(w,d, __locale_t locale) +#undef __UCLIBC_CURLOCALE_DATA +#undef __UCLIBC_CURLOCALE +#define __UCLIBC_CURLOCALE_DATA (*locale) +#define __UCLIBC_CURLOCALE (locale) +#endif /* L_iswctype */ + +#if defined(L_iswctype) && defined(__UCLIBC_HAS_XLOCALE__) + +int iswctype(wint_t wc, wctype_t desc) +{ + return iswctype_l(wc, desc, __UCLIBC_CURLOCALE); +} + +#else /* defined(L_iswctype) && defined(__UCLIBC_HAS_XLOCALE__) */ + +int ISWCTYPE(wint_t wc, wctype_t desc) +{ + unsigned int sc, n, i0, i1; + unsigned char d = __CTYPE_unclassified; + + if ((ENCODING != __ctype_encoding_7_bit) || (((__uwchar_t) wc) <= 0x7f)){ + if (desc < _CTYPE_iswxdigit) { + if (((__uwchar_t) wc) <= WC_TABLE_DOMAIN_MAX) { + /* From here on, we know wc > 0. */ + sc = wc & WCctype_TI_MASK; + wc >>= WCctype_TI_SHIFT; + n = wc & WCctype_II_MASK; + wc >>= WCctype_II_SHIFT; + + i0 = WCctype[wc]; + i0 <<= WCctype_II_SHIFT; + i1 = WCctype[WCctype_II_LEN + i0 + n]; + i1 <<= (WCctype_TI_SHIFT-1); + d = WCctype[WCctype_II_LEN + WCctype_TI_LEN + i1 + (sc >> 1)]; + + d = (sc & 1) ? (d >> 4) : (d & 0xf); + } else if ( ((((__uwchar_t)(wc - 0xe0020UL)) <= 0x5f) + || (wc == 0xe0001UL)) + || ( (((__uwchar_t)(wc - 0xf0000UL)) < 0x20000UL) + && ((wc & 0xffffU) <= 0xfffdU)) + ) { + d = __CTYPE_punct; + } + +#if 0 + return ( ((unsigned char)(d - ctype_range[2*desc])) + <= ctype_range[2*desc + 1] ) + && ((desc != _CTYPE_iswblank) || (d & 1)); +#else + return (__UCLIBC_CURLOCALE_DATA).code2flag[d] & desc2flag[desc]; +#endif + } + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: xdigit really needs to be handled better. Remember only for ascii! +#endif /* __UCLIBC_MJN3_ONLY__ */ + /* TODO - Add locale-specific classifications. */ + return (desc == _CTYPE_iswxdigit) ? __C_iswxdigit(wc) : 0; + } + return 0; +} + +#endif /* defined(L_iswctype) && defined(__UCLIBC_HAS_XLOCALE__) */ + +#ifdef L_iswctype_l +libc_hidden_def(iswctype_l) +#endif /* L_iswctype_l */ + +#endif /* __LOCALE_C_ONLY */ + +#ifdef L_iswctype +libc_hidden_def(iswctype) +#endif /* L_iswctype */ + +#endif +/**********************************************************************/ +#if defined(L_towctrans) || defined(L_towctrans_l) + +#ifdef __LOCALE_C_ONLY + +/* Minimal support for C/POSIX locale. */ + +#ifndef _tolower +#warning _tolower is undefined! +#define _tolower(c) tolower(c) +#endif +#ifndef _toupper +#warning _toupper is undefined! +#define _toupper(c) toupper(c) +#endif + +wint_t towctrans(wint_t wc, wctrans_t desc) +{ + if (((unsigned int)(desc - _CTYPE_tolower)) + <= (_CTYPE_toupper - _CTYPE_tolower) + ) { + /* Transliteration is either tolower or toupper. */ + if (((__uwchar_t) wc) <= 0x7f) { + return (desc == _CTYPE_tolower) ? _tolower(wc) : _toupper(wc); + } + } else { + __set_errno(EINVAL); /* Invalid transliteration. */ + } + return wc; +} + +#else /* __LOCALE_C_ONLY */ + +#ifdef L_towctrans +#define TOWCTRANS(w,d) towctrans(w,d) +#else /* L_towctrans */ +#define TOWCTRANS(w,d) towctrans_l(w,d, __locale_t locale) +#undef __UCLIBC_CURLOCALE_DATA +#undef __UCLIBC_CURLOCALE +#define __UCLIBC_CURLOCALE_DATA (*locale) +#define __UCLIBC_CURLOCALE (locale) +#endif /* L_towctrans */ + +#ifdef __UCLIBC_HAS_XLOCALE__ +#define TOWLOWER(w,l) towlower_l(w,l) +#define TOWUPPER(w,l) towupper_l(w,l) +#else /* __UCLIBC_HAS_XLOCALE__ */ +#define TOWLOWER(w,l) towlower(w) +#define TOWUPPER(w,l) towupper(w) +#endif /* __UCLIBC_HAS_XLOCALE__ */ + +#if defined(L_towctrans) && defined(__UCLIBC_HAS_XLOCALE__) + +wint_t towctrans(wint_t wc, wctrans_t desc) +{ + return towctrans_l(wc, desc, __UCLIBC_CURLOCALE); +} + +#else /* defined(L_towctrans) && defined(__UCLIBC_HAS_XLOCALE__) */ + +#ifdef SMALL_UPLOW + +wint_t TOWCTRANS(wint_t wc, wctrans_t desc) +{ + unsigned int sc, n, i; + __uwchar_t u = wc; + + /* TODO - clean up */ + if (ENCODING == __ctype_encoding_7_bit) { + if ((((__uwchar_t) wc) > 0x7f) + || (((unsigned int)(desc - _CTYPE_tolower)) + > (_CTYPE_toupper - _CTYPE_tolower)) + ){ + /* We're in the C/POSIX locale, so ignore non-ASCII values + * as well an any mappings other than toupper or tolower. */ + return wc; + } + } + + if (((unsigned int)(desc - _CTYPE_tolower)) + <= (_CTYPE_totitle - _CTYPE_tolower) + ) { + if (u <= WC_TABLE_DOMAIN_MAX) { + sc = u & ((1 << WCuplow_TI_SHIFT) - 1); + u >>= WCuplow_TI_SHIFT; + n = u & ((1 << WCuplow_II_SHIFT) - 1); + u >>= WCuplow_II_SHIFT; + + i = ((unsigned int) WCuplow[u]) << WCuplow_II_SHIFT; + i = ((unsigned int) WCuplow[WCuplow_II_LEN + i + n]) + << WCuplow_TI_SHIFT; + i = ((unsigned int) WCuplow[WCuplow_II_LEN + WCuplow_TI_LEN + + i + sc]) << 1; + if (desc == _CTYPE_tolower) { + ++i; + } + wc += WCuplow_diff[i]; + if (desc == _CTYPE_totitle) { +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Verify totitle special cases! +#endif /* __UCLIBC_MJN3_ONLY__ */ + /* WARNING! These special cases work for glibc 2.2.4. Changes + * may be needed if the glibc locale tables are updated. */ + if ( (((__uwchar_t)(wc - 0x1c4)) <= (0x1cc - 0x1c4)) + || (wc == 0x1f1) + ) { + ++wc; + } + } + } + } else { + /* TODO - Deal with other transliterations. */ + __set_errno(EINVAL); + } + + return wc; +} + +#else /* SMALL_UPLOW */ + +wint_t TOWCTRANS(wint_t wc, wctrans_t desc) +{ + if (ENCODING == __ctype_encoding_7_bit) { + if ((((__uwchar_t) wc) > 0x7f) + || (((unsigned int)(desc - _CTYPE_tolower)) + > (_CTYPE_toupper - _CTYPE_tolower)) + ){ + /* We're in the C/POSIX locale, so ignore non-ASCII values + * as well an any mappings other than toupper or tolower. */ + return wc; + } + } + + if (desc == _CTYPE_tolower) { + return TOWLOWER(wc, __UCLIBC_CURLOCALE); + } else if (((unsigned int)(desc - _CTYPE_toupper)) + <= (_CTYPE_totitle - _CTYPE_toupper) + ) { + wc = TOWUPPER(wc, __UCLIBC_CURLOCALE); + if (desc == _CTYPE_totitle) { +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Verify totitle special cases! +#endif /* __UCLIBC_MJN3_ONLY__ */ + /* WARNING! These special cases work for glibc 2.2.4. Changes + * may be needed if the glibc locale tables are updated. */ + if ( (((__uwchar_t)(wc - 0x1c4)) <= (0x1cc - 0x1c4)) + || (wc == 0x1f1) + ) { + ++wc; + } + } + } else { + /* TODO - Deal with other transliterations. */ + __set_errno(EINVAL); + } + return wc; +} + +#endif /* SMALL_UPLOW */ + +#endif /* defined(L_towctrans) && defined(__UCLIBC_HAS_XLOCALE__) */ + +#ifdef L_towctrans_l +libc_hidden_def(towctrans_l) +#endif /* L_towctrans_l */ + +#endif /* __LOCALE_C_ONLY */ + +#ifndef L_towctrans_l +libc_hidden_def(towctrans) +#endif + +#endif +/**********************************************************************/ +#ifdef L_wctrans + +static const char transstring[] = __CTYPE_TRANSTRING; + +libc_hidden_proto(wctrans) +wctrans_t wctrans(const char *property) +{ + const unsigned char *p; + int i; + + p = transstring; + i = 1; + do { + if (!strcmp(property, ++p)) { + return i; + } + ++i; + p += p[-1]; + } while (*p); + + /* TODO - Add locale-specific translations. */ + return 0; +} +libc_hidden_def(wctrans) + +#endif +/**********************************************************************/ +#ifdef L_wctrans_l + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning REMINDER: Currently wctrans_l simply calls wctrans. +#endif /* __UCLIBC_MJN3_ONLY__ */ + +libc_hidden_proto(wctrans) + +wctrans_t wctrans_l(const char *property, __locale_t locale) +{ + return wctrans(property); +} + +#endif +/**********************************************************************/ diff --git a/libc/misc/wctype/wctype.c b/libc/misc/wctype/wctype.c deleted file mode 100644 index a7d111039..000000000 --- a/libc/misc/wctype/wctype.c +++ /dev/null @@ -1,947 +0,0 @@ -/* Copyright (C) 2002, 2003 Manuel Novoa III - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! - * - * Besides uClibc, I'm using this code in my libc for elks, which is - * a 16-bit environment with a fairly limited compiler. It would make - * things much easier for me if this file isn't modified unnecessarily. - * In particular, please put any new or replacement functions somewhere - * else, and modify the makefile to use your version instead. - * Thanks. Manuel - * - * ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! */ - -#define __NO_CTYPE - -#include -#include -#include -#include -#include -#include -#include -#include - -libc_hidden_proto(strcmp) -libc_hidden_proto(__C_ctype_tolower) -libc_hidden_proto(tolower) -libc_hidden_proto(__C_ctype_toupper) -libc_hidden_proto(toupper) -libc_hidden_proto(towlower) -libc_hidden_proto(towupper) -libc_hidden_proto(towctrans) -libc_hidden_proto(iswctype) - -#if defined(__LOCALE_C_ONLY) && defined(__UCLIBC_DO_XLOCALE) -#error xlocale functionality is not supported in stub locale mode. -#endif - -#ifdef __UCLIBC_HAS_XLOCALE__ -#include -libc_hidden_proto(towlower_l) -libc_hidden_proto(towupper_l) -libc_hidden_proto(towctrans_l) -libc_hidden_proto(iswctype_l) -#else -libc_hidden_proto(__ctype_b) -#endif /* __UCLIBC_HAS_XLOCALE__ */ -#ifdef __UCLIBC_HAS_LOCALE__ -libc_hidden_proto(__global_locale) -#endif - -/* We know wide char support is enabled. We wouldn't be here otherwise. */ - -/* Define this if you want to unify the towupper and towlower code in the - * towctrans function. */ -/* #define SMALL_UPLOW */ - -/**********************************************************************/ -#ifdef __UCLIBC_MJN3_ONLY__ -#ifdef L_iswspace -/* generates one warning */ -#warning TODO: Fix the __CTYPE_* codes! -#endif -#endif /* __UCLIBC_MJN3_ONLY__ */ - -#if 1 -/* Taking advantage of the C99 mutual-exclusion guarantees for the various - * (w)ctype classes, including the descriptions of printing and control - * (w)chars, we can place each in one of the following mutually-exlusive - * subsets. Since there are less than 16, we can store the data for - * each (w)chars in a nibble. In contrast, glibc uses an unsigned int - * per (w)char, with one bit flag for each is* type. While this allows - * a simple '&' operation to determine the type vs. a range test and a - * little special handling for the "blank" and "xdigit" types in my - * approach, it also uses 8 times the space for the tables on the typical - * 32-bit archs we supported.*/ -enum { - __CTYPE_unclassified = 0, - __CTYPE_alpha_nonupper_nonlower, - __CTYPE_alpha_lower, - __CTYPE_alpha_upper_lower, - __CTYPE_alpha_upper, - __CTYPE_digit, - __CTYPE_punct, - __CTYPE_graph, - __CTYPE_print_space_nonblank, - __CTYPE_print_space_blank, - __CTYPE_space_nonblank_noncntrl, - __CTYPE_space_blank_noncntrl, - __CTYPE_cntrl_space_nonblank, - __CTYPE_cntrl_space_blank, - __CTYPE_cntrl_nonspace -}; -#endif - - -/* The following is used to implement wctype(), but it is defined - * here because the ordering must agree with that of the enumeration - * below (ignoring unclassified). */ -#define __CTYPE_TYPESTRING \ - "\6alnum\0\6alpha\0\6blank\0\6cntrl\0\6digit\0\6graph\0\6lower\0" \ - "\6print\0\6punct\0\6space\0\6upper\0\7xdigit\0\0" - - -/* The values for wctype_t. */ -enum { - _CTYPE_unclassified = 0, - _CTYPE_isalnum, - _CTYPE_isalpha, - _CTYPE_isblank, - _CTYPE_iscntrl, - _CTYPE_isdigit, - _CTYPE_isgraph, - _CTYPE_islower, - _CTYPE_isprint, - _CTYPE_ispunct, - _CTYPE_isspace, - _CTYPE_isupper, - _CTYPE_isxdigit /* _MUST_ be last of the standard classes! */ -}; - -/* The following is used to implement wctrans(). */ - -#define __CTYPE_TRANSTRING "\10tolower\0\10toupper\0\10totitle\0\0" - -enum { - _CTYPE_tolower = 1, - _CTYPE_toupper, - _CTYPE_totitle -}; - -/*--------------------------------------------------------------------*/ - -#define _CTYPE_iswxdigit (_CTYPE_isxdigit) - -/*--------------------------------------------------------------------*/ - -#ifdef __UCLIBC_MJN3_ONLY__ -#ifdef L_iswspace -/* generates one warning */ -#warning TODO: Fix WC* defines! -#endif -#endif /* __UCLIBC_MJN3_ONLY__ */ - -#define ENCODING ((__UCLIBC_CURLOCALE_DATA).encoding) - -#define WCctype ((__UCLIBC_CURLOCALE_DATA).tblwctype) -#define WCuplow ((__UCLIBC_CURLOCALE_DATA).tblwuplow) -#define WCcmob ((__UCLIBC_CURLOCALE_DATA).tblwcomb) -#define WCuplow_diff ((__UCLIBC_CURLOCALE_DATA).tblwuplow_diff) - - -#define WC_TABLE_DOMAIN_MAX __LOCALE_DATA_WC_TABLE_DOMAIN_MAX - -#define WCctype_II_LEN __LOCALE_DATA_WCctype_II_LEN -#define WCctype_TI_LEN __LOCALE_DATA_WCctype_TI_LEN -#define WCctype_UT_LEN __LOCALE_DATA_WCctype_UT_LEN -#define WCctype_II_SHIFT __LOCALE_DATA_WCctype_II_SHIFT -#define WCctype_TI_SHIFT __LOCALE_DATA_WCctype_TI_SHIFT - -#define WCuplow_II_LEN __LOCALE_DATA_WCuplow_II_LEN -#define WCuplow_TI_LEN __LOCALE_DATA_WCuplow_TI_LEN -#define WCuplow_UT_LEN __LOCALE_DATA_WCuplow_UT_LEN -#define WCuplow_II_SHIFT __LOCALE_DATA_WCuplow_II_SHIFT -#define WCuplow_TI_SHIFT __LOCALE_DATA_WCuplow_TI_SHIFT - - -#define WCctype_TI_MASK ((1 << (WCctype_TI_SHIFT)) - 1) -#define WCctype_II_MASK ((1 << (WCctype_II_SHIFT)) - 1) - -/**********************************************************************/ - -#undef __PASTE2 -#undef __PASTE3 -#define __PASTE2(X,Y) X ## Y -#define __PASTE3(X,Y,Z) X ## Y ## Z - -#ifdef __UCLIBC_DO_XLOCALE - -#define ISW_FUNC_BODY(NAME) \ -libc_hidden_proto(__PASTE3(isw,NAME,_l)); \ -int __PASTE3(isw,NAME,_l) (wint_t wc, __locale_t l) \ -{ \ - return iswctype_l(wc, __PASTE2(_CTYPE_is,NAME), l); \ -} \ -libc_hidden_def(__PASTE3(isw,NAME,_l)) - -#else /* __UCLIBC_DO_XLOCALE */ - -#define ISW_FUNC_BODY(NAME) \ -libc_hidden_proto(__PASTE2(isw,NAME)); \ -int __PASTE2(isw,NAME) (wint_t wc) \ -{ \ - return iswctype(wc, __PASTE2(_CTYPE_is,NAME)); \ -} \ -libc_hidden_def(__PASTE2(isw,NAME)) - -#endif /* __UCLIBC_DO_XLOCALE */ -/**********************************************************************/ -#if defined(L_iswalnum) || defined(L_iswalnum_l) - -ISW_FUNC_BODY(alnum); - -#endif -/**********************************************************************/ -#if defined(L_iswalpha) || defined(L_iswalpha_l) - -ISW_FUNC_BODY(alpha); - -#endif -/**********************************************************************/ -#if defined(L_iswblank) || defined(L_iswblank_l) - -ISW_FUNC_BODY(blank); - -#endif -/**********************************************************************/ -#if defined(L_iswcntrl) || defined(L_iswcntrl_l) - -ISW_FUNC_BODY(cntrl); - -#endif -/**********************************************************************/ -#if defined(L_iswdigit) || defined(L_iswdigit_l) - -ISW_FUNC_BODY(digit); - -#endif -/**********************************************************************/ -#if defined(L_iswgraph) || defined(L_iswgraph_l) - -ISW_FUNC_BODY(graph); - -#endif -/**********************************************************************/ -#if defined(L_iswlower) || defined(L_iswlower_l) - -ISW_FUNC_BODY(lower); - -#endif -/**********************************************************************/ -#if defined(L_iswprint) || defined(L_iswprint_l) - -ISW_FUNC_BODY(print); - -#endif -/**********************************************************************/ -#if defined(L_iswpunct) || defined(L_iswpunct_l) - -ISW_FUNC_BODY(punct); - -#endif -/**********************************************************************/ -#if defined(L_iswspace) || defined(L_iswspace_l) - -ISW_FUNC_BODY(space); - -#endif -/**********************************************************************/ -#if defined(L_iswupper) || defined(L_iswupper_l) - -ISW_FUNC_BODY(upper); - -#endif -/**********************************************************************/ -#if defined(L_iswxdigit) || defined(L_iswxdigit_l) - -ISW_FUNC_BODY(xdigit); - -#endif -/**********************************************************************/ -#if defined(L_towlower) || defined(L_towlower_l) - -#ifdef L_towlower -#define TOWLOWER(w) towlower(w) -#else /* L_towlower */ -#define TOWLOWER(w) towlower_l(w, __locale_t locale) -#undef __UCLIBC_CURLOCALE_DATA -#undef __UCLIBC_CURLOCALE -#define __UCLIBC_CURLOCALE_DATA (*locale) -#define __UCLIBC_CURLOCALE (locale) -#endif /* L_towlower */ - -#ifdef __UCLIBC_HAS_XLOCALE__ -#define TOWCTRANS(w,d) towctrans_l(w,d, __UCLIBC_CURLOCALE) -#else /* __UCLIBC_HAS_XLOCALE__ */ -#define TOWCTRANS(w,d) towctrans(w,d) -#endif /* __UCLIBC_HAS_XLOCALE__ */ - -#define __C_towlower(wc) \ - ((((__uwchar_t)(wc)) <= 0x7f) ? (__C_ctype_tolower)[(wc)] : (wc)) - -#ifdef __LOCALE_C_ONLY - -wint_t towlower(wint_t wc) -{ -#ifdef __UCLIBC_HAS_CTYPE_TABLES__ - return __C_towlower(wc); -#else - return (wc == ((unsigned int)(wc))) - ? __C_tolower(((unsigned int)(wc))) - : 0; -#endif -} - -#else /* __LOCALE_C_ONLY */ - -#ifdef SMALL_UPLOW - -#if defined(L_towlower) && defined(__UCLIBC_HAS_XLOCALE__) - -wint_t towlower(wint_t wc) -{ - return towctrans_l(wc, _CTYPE_tolower, __UCLIBC_CURLOCALE); -} - -#else /* defined(L_towlower) && defined(__UCLIBC_HAS_XLOCALE__) */ - -wint_t TOWLOWER(wint_t wc) -{ - return TOWCTRANS(wc, _CTYPE_tolower); -} - -#endif /* defined(L_towlower) && defined(__UCLIBC_HAS_XLOCALE__) */ - -#else /* SMALL_UPLOW */ - -#if defined(L_towlower) && defined(__UCLIBC_HAS_XLOCALE__) - -wint_t towlower(wint_t wc) -{ - return towlower_l(wc, __UCLIBC_CURLOCALE); -} - -#else /* defined(L_towlower) && defined(__UCLIBC_HAS_XLOCALE__) */ - -wint_t TOWLOWER(wint_t wc) -{ - unsigned int sc, n, i; - __uwchar_t u = wc; - - if (ENCODING == __ctype_encoding_7_bit) { - /* We're in the C/POSIX locale, so ignore the tables. */ - return __C_towlower(wc); - } - - if (u <= WC_TABLE_DOMAIN_MAX) { - sc = u & ((1 << WCuplow_TI_SHIFT) - 1); - u >>= WCuplow_TI_SHIFT; - n = u & ((1 << WCuplow_II_SHIFT) - 1); - u >>= WCuplow_II_SHIFT; - - i = ((unsigned int) WCuplow[u]) << WCuplow_II_SHIFT; - i = ((unsigned int) WCuplow[WCuplow_II_LEN + i + n]) - << WCuplow_TI_SHIFT; - i = ((unsigned int) WCuplow[WCuplow_II_LEN + WCuplow_TI_LEN - + i + sc]) << 1; - wc += WCuplow_diff[i + 1]; - } - return wc; -} - -#endif /* defined(L_towlower) && defined(__UCLIBC_HAS_XLOCALE__) */ - -#endif /* SMALL_UPLOW */ - -#ifdef L_towlower_l -libc_hidden_def(towlower_l) -#endif /* L_towlower_l */ - -#endif /* __LOCALE_C_ONLY */ - -#ifndef L_towlower_l -libc_hidden_def(towlower) -#endif - -#endif -/**********************************************************************/ -#if defined(L_towupper) || defined(L_towupper_l) - -#ifdef L_towupper -#define TOWUPPER(w) towupper(w) -#else /* L_towupper */ -#define TOWUPPER(w) towupper_l(w, __locale_t locale) -#undef __UCLIBC_CURLOCALE_DATA -#undef __UCLIBC_CURLOCALE -#define __UCLIBC_CURLOCALE_DATA (*locale) -#define __UCLIBC_CURLOCALE (locale) -#endif /* L_towupper */ - -#ifdef __UCLIBC_HAS_XLOCALE__ -#define TOWCTRANS(w,d) towctrans_l(w,d, __UCLIBC_CURLOCALE) -#else /* __UCLIBC_HAS_XLOCALE__ */ -#define TOWCTRANS(w,d) towctrans(w,d) -#endif /* __UCLIBC_HAS_XLOCALE__ */ - -#define __C_towupper(wc) \ - ((((__uwchar_t)(wc)) <= 0x7f) ? (__C_ctype_toupper)[(wc)] : (wc)) - -#ifdef __LOCALE_C_ONLY - -wint_t towupper(wint_t wc) -{ -#ifdef __UCLIBC_HAS_CTYPE_TABLES__ - return __C_towupper(wc); -#else - return (wc == ((unsigned int)(wc))) - ? __C_toupper(((unsigned int)(wc))) - : 0; -#endif - -} - -#else /* __LOCALE_C_ONLY */ - -#ifdef SMALL_UPLOW - -#if defined(L_towupper) && defined(__UCLIBC_HAS_XLOCALE__) - -wint_t towupper(wint_t wc) -{ - return towctrans_l(wc, _CTYPE_toupper, __UCLIBC_CURLOCALE); -} - -#else /* defined(L_towupper) && defined(__UCLIBC_HAS_XLOCALE__) */ - -wint_t TOWUPPER(wint_t wc) -{ - return TOWCTRANS(wc, _CTYPE_toupper); -} - -#endif /* defined(L_towupper) && defined(__UCLIBC_HAS_XLOCALE__) */ - -#else /* SMALL_UPLOW */ - -#if defined(L_towupper) && defined(__UCLIBC_HAS_XLOCALE__) - -wint_t towupper(wint_t wc) -{ - return towupper_l(wc, __UCLIBC_CURLOCALE); -} - -#else /* defined(L_towupper) && defined(__UCLIBC_HAS_XLOCALE__) */ - -wint_t TOWUPPER(wint_t wc) -{ - unsigned int sc, n, i; - __uwchar_t u = wc; - - if (ENCODING == __ctype_encoding_7_bit) { - /* We're in the C/POSIX locale, so ignore the tables. */ - return __C_towupper(wc); - } - - if (u <= WC_TABLE_DOMAIN_MAX) { - sc = u & ((1 << WCuplow_TI_SHIFT) - 1); - u >>= WCuplow_TI_SHIFT; - n = u & ((1 << WCuplow_II_SHIFT) - 1); - u >>= WCuplow_II_SHIFT; - - i = ((unsigned int) WCuplow[u]) << WCuplow_II_SHIFT; - i = ((unsigned int) WCuplow[WCuplow_II_LEN + i + n]) - << WCuplow_TI_SHIFT; - i = ((unsigned int) WCuplow[WCuplow_II_LEN + WCuplow_TI_LEN - + i + sc]) << 1; - wc += WCuplow_diff[i]; - } - return wc; -} - -#endif /* defined(L_towupper) && defined(__UCLIBC_HAS_XLOCALE__) */ - -#endif /* SMALL_UPLOW */ - -#ifdef L_towupper_l -libc_hidden_def(towupper_l) -#endif /* L_towupper_l */ - -#endif /* __LOCALE_C_ONLY */ - -#ifndef L_towupper_l -libc_hidden_def(towupper) -#endif - -#endif -/**********************************************************************/ -#ifdef L_wctype - -static const unsigned char typestring[] = __CTYPE_TYPESTRING; -/* extern const unsigned char typestring[]; */ - -libc_hidden_proto(wctype) -wctype_t wctype(const char *property) -{ - const unsigned char *p; - int i; - - p = typestring; - i = 1; - do { - if (!strcmp(property, ++p)) { - return i; - } - ++i; - p += p[-1]; - } while (*p); - - /* TODO - Add locale-specific classifications. */ - return 0; -} -libc_hidden_def(wctype) - -#endif -/**********************************************************************/ -#ifdef L_wctype_l - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning REMINDER: Currently wctype_l simply calls wctype. -#endif /* __UCLIBC_MJN3_ONLY__ */ - -libc_hidden_proto(wctype) - -libc_hidden_proto(wctype_l) -wctype_t wctype_l (const char *property, __locale_t locale) -{ - return wctype(property); -} -libc_hidden_def(wctype_l) - -#endif -/**********************************************************************/ -#if defined(L_iswctype) || defined(L_iswctype_l) - -#define __C_iswdigit(c) \ - ((sizeof(c) == sizeof(char)) \ - ? (((unsigned char)((c) - '0')) < 10) \ - : (((__uwchar_t)((c) - '0')) < 10)) -#define __C_iswxdigit(c) \ - (__C_iswdigit(c) \ - || ((sizeof(c) == sizeof(char)) \ - ? (((unsigned char)((((c)) | 0x20) - 'a')) < 6) \ - : (((__uwchar_t)((((c)) | 0x20) - 'a')) < 6))) - -#ifdef __UCLIBC_MJN3_ONLY__ -#ifdef L_iswctype -#warning CONSIDER: Change to bit shift? would need to sync with wctype.h -#endif -#endif /* __UCLIBC_MJN3_ONLY__ */ - - -#ifdef __UCLIBC_HAS_CTYPE_TABLES__ -#if !defined(__UCLIBC_HAS_XLOCALE__) || defined(L_iswctype_l) - -static const unsigned short int desc2flag[] = { - [_CTYPE_unclassified] = 0, - [_CTYPE_isalnum] = (unsigned short int) _ISwalnum, - [_CTYPE_isalpha] = (unsigned short int) _ISwalpha, - [_CTYPE_isblank] = (unsigned short int) _ISwblank, - [_CTYPE_iscntrl] = (unsigned short int) _ISwcntrl, - [_CTYPE_isdigit] = (unsigned short int) _ISwdigit, - [_CTYPE_isgraph] = (unsigned short int) _ISwgraph, - [_CTYPE_islower] = (unsigned short int) _ISwlower, - [_CTYPE_isprint] = (unsigned short int) _ISwprint, - [_CTYPE_ispunct] = (unsigned short int) _ISwpunct, - [_CTYPE_isspace] = (unsigned short int) _ISwspace, - [_CTYPE_isupper] = (unsigned short int) _ISwupper, - [_CTYPE_isxdigit] = (unsigned short int) _ISwxdigit, -}; - -#endif /* defined(L_iswctype_L) || defined(__LOCALE_C_ONLY) */ -#endif /* __UCLIBC_HAS_CTYPE_TABLES__ */ - -#ifdef __LOCALE_C_ONLY - -#ifdef __UCLIBC_HAS_CTYPE_TABLES__ - -int iswctype(wint_t wc, wctype_t desc) -{ - /* Note... wctype_t is unsigned. */ - - if ((((__uwchar_t) wc) <= 0x7f) - && (desc < (sizeof(desc2flag)/sizeof(desc2flag[0]))) - ) { - return __isctype(wc, desc2flag[desc]); - } - return 0; -} - -#else /* __UCLIBC_HAS_CTYPE_TABLES__ */ - -int iswctype(wint_t wc, wctype_t desc) -{ - /* This is lame, but it is here just to get it working for now. */ - - if (wc == ((unsigned int)(wc))) { - switch(desc) { - case _CTYPE_isupper: - return __C_isupper((unsigned int)(wc)); - case _CTYPE_islower: - return __C_islower((unsigned int)(wc)); - case _CTYPE_isalpha: - return __C_isalpha((unsigned int)(wc)); - case _CTYPE_isdigit: - return __C_isdigit((unsigned int)(wc)); - case _CTYPE_isxdigit: - return __C_isxdigit((unsigned int)(wc)); - case _CTYPE_isspace: - return __C_isspace((unsigned int)(wc)); - case _CTYPE_isprint: - return __C_isprint((unsigned int)(wc)); - case _CTYPE_isgraph: - return __C_isgraph((unsigned int)(wc)); - case _CTYPE_isblank: - return __C_isblank((unsigned int)(wc)); - case _CTYPE_iscntrl: - return __C_iscntrl((unsigned int)(wc)); - case _CTYPE_ispunct: - return __C_ispunct((unsigned int)(wc)); - case _CTYPE_isalnum: - return __C_isalnum((unsigned int)(wc)); - default: - break; - } - } - return 0; -} - -#endif /* __UCLIBC_HAS_CTYPE_TABLES__ */ - -#else /* __LOCALE_C_ONLY */ - -#ifdef __UCLIBC_MJN3_ONLY__ -#ifdef L_iswctype -#warning CONSIDER: Handle combining class? -#endif -#endif /* __UCLIBC_MJN3_ONLY__ */ - -#ifdef L_iswctype -#define ISWCTYPE(w,d) iswctype(w,d) -#else /* L_iswctype */ -#define ISWCTYPE(w,d) iswctype_l(w,d, __locale_t locale) -#undef __UCLIBC_CURLOCALE_DATA -#undef __UCLIBC_CURLOCALE -#define __UCLIBC_CURLOCALE_DATA (*locale) -#define __UCLIBC_CURLOCALE (locale) -#endif /* L_iswctype */ - -#if defined(L_iswctype) && defined(__UCLIBC_HAS_XLOCALE__) - -int iswctype(wint_t wc, wctype_t desc) -{ - return iswctype_l(wc, desc, __UCLIBC_CURLOCALE); -} - -#else /* defined(L_iswctype) && defined(__UCLIBC_HAS_XLOCALE__) */ - -int ISWCTYPE(wint_t wc, wctype_t desc) -{ - unsigned int sc, n, i0, i1; - unsigned char d = __CTYPE_unclassified; - - if ((ENCODING != __ctype_encoding_7_bit) || (((__uwchar_t) wc) <= 0x7f)){ - if (desc < _CTYPE_iswxdigit) { - if (((__uwchar_t) wc) <= WC_TABLE_DOMAIN_MAX) { - /* From here on, we know wc > 0. */ - sc = wc & WCctype_TI_MASK; - wc >>= WCctype_TI_SHIFT; - n = wc & WCctype_II_MASK; - wc >>= WCctype_II_SHIFT; - - i0 = WCctype[wc]; - i0 <<= WCctype_II_SHIFT; - i1 = WCctype[WCctype_II_LEN + i0 + n]; - i1 <<= (WCctype_TI_SHIFT-1); - d = WCctype[WCctype_II_LEN + WCctype_TI_LEN + i1 + (sc >> 1)]; - - d = (sc & 1) ? (d >> 4) : (d & 0xf); - } else if ( ((((__uwchar_t)(wc - 0xe0020UL)) <= 0x5f) - || (wc == 0xe0001UL)) - || ( (((__uwchar_t)(wc - 0xf0000UL)) < 0x20000UL) - && ((wc & 0xffffU) <= 0xfffdU)) - ) { - d = __CTYPE_punct; - } - -#if 0 - return ( ((unsigned char)(d - ctype_range[2*desc])) - <= ctype_range[2*desc + 1] ) - && ((desc != _CTYPE_iswblank) || (d & 1)); -#else - return (__UCLIBC_CURLOCALE_DATA).code2flag[d] & desc2flag[desc]; -#endif - } - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: xdigit really needs to be handled better. Remember only for ascii! -#endif /* __UCLIBC_MJN3_ONLY__ */ - /* TODO - Add locale-specific classifications. */ - return (desc == _CTYPE_iswxdigit) ? __C_iswxdigit(wc) : 0; - } - return 0; -} - -#endif /* defined(L_iswctype) && defined(__UCLIBC_HAS_XLOCALE__) */ - -#ifdef L_iswctype_l -libc_hidden_def(iswctype_l) -#endif /* L_iswctype_l */ - -#endif /* __LOCALE_C_ONLY */ - -#ifdef L_iswctype -libc_hidden_def(iswctype) -#endif /* L_iswctype */ - -#endif -/**********************************************************************/ -#if defined(L_towctrans) || defined(L_towctrans_l) - -#ifdef __LOCALE_C_ONLY - -/* Minimal support for C/POSIX locale. */ - -#ifndef _tolower -#warning _tolower is undefined! -#define _tolower(c) tolower(c) -#endif -#ifndef _toupper -#warning _toupper is undefined! -#define _toupper(c) toupper(c) -#endif - -wint_t towctrans(wint_t wc, wctrans_t desc) -{ - if (((unsigned int)(desc - _CTYPE_tolower)) - <= (_CTYPE_toupper - _CTYPE_tolower) - ) { - /* Transliteration is either tolower or toupper. */ - if (((__uwchar_t) wc) <= 0x7f) { - return (desc == _CTYPE_tolower) ? _tolower(wc) : _toupper(wc); - } - } else { - __set_errno(EINVAL); /* Invalid transliteration. */ - } - return wc; -} - -#else /* __LOCALE_C_ONLY */ - -#ifdef L_towctrans -#define TOWCTRANS(w,d) towctrans(w,d) -#else /* L_towctrans */ -#define TOWCTRANS(w,d) towctrans_l(w,d, __locale_t locale) -#undef __UCLIBC_CURLOCALE_DATA -#undef __UCLIBC_CURLOCALE -#define __UCLIBC_CURLOCALE_DATA (*locale) -#define __UCLIBC_CURLOCALE (locale) -#endif /* L_towctrans */ - -#ifdef __UCLIBC_HAS_XLOCALE__ -#define TOWLOWER(w,l) towlower_l(w,l) -#define TOWUPPER(w,l) towupper_l(w,l) -#else /* __UCLIBC_HAS_XLOCALE__ */ -#define TOWLOWER(w,l) towlower(w) -#define TOWUPPER(w,l) towupper(w) -#endif /* __UCLIBC_HAS_XLOCALE__ */ - -#if defined(L_towctrans) && defined(__UCLIBC_HAS_XLOCALE__) - -wint_t towctrans(wint_t wc, wctrans_t desc) -{ - return towctrans_l(wc, desc, __UCLIBC_CURLOCALE); -} - -#else /* defined(L_towctrans) && defined(__UCLIBC_HAS_XLOCALE__) */ - -#ifdef SMALL_UPLOW - -wint_t TOWCTRANS(wint_t wc, wctrans_t desc) -{ - unsigned int sc, n, i; - __uwchar_t u = wc; - - /* TODO - clean up */ - if (ENCODING == __ctype_encoding_7_bit) { - if ((((__uwchar_t) wc) > 0x7f) - || (((unsigned int)(desc - _CTYPE_tolower)) - > (_CTYPE_toupper - _CTYPE_tolower)) - ){ - /* We're in the C/POSIX locale, so ignore non-ASCII values - * as well an any mappings other than toupper or tolower. */ - return wc; - } - } - - if (((unsigned int)(desc - _CTYPE_tolower)) - <= (_CTYPE_totitle - _CTYPE_tolower) - ) { - if (u <= WC_TABLE_DOMAIN_MAX) { - sc = u & ((1 << WCuplow_TI_SHIFT) - 1); - u >>= WCuplow_TI_SHIFT; - n = u & ((1 << WCuplow_II_SHIFT) - 1); - u >>= WCuplow_II_SHIFT; - - i = ((unsigned int) WCuplow[u]) << WCuplow_II_SHIFT; - i = ((unsigned int) WCuplow[WCuplow_II_LEN + i + n]) - << WCuplow_TI_SHIFT; - i = ((unsigned int) WCuplow[WCuplow_II_LEN + WCuplow_TI_LEN - + i + sc]) << 1; - if (desc == _CTYPE_tolower) { - ++i; - } - wc += WCuplow_diff[i]; - if (desc == _CTYPE_totitle) { -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Verify totitle special cases! -#endif /* __UCLIBC_MJN3_ONLY__ */ - /* WARNING! These special cases work for glibc 2.2.4. Changes - * may be needed if the glibc locale tables are updated. */ - if ( (((__uwchar_t)(wc - 0x1c4)) <= (0x1cc - 0x1c4)) - || (wc == 0x1f1) - ) { - ++wc; - } - } - } - } else { - /* TODO - Deal with other transliterations. */ - __set_errno(EINVAL); - } - - return wc; -} - -#else /* SMALL_UPLOW */ - -wint_t TOWCTRANS(wint_t wc, wctrans_t desc) -{ - if (ENCODING == __ctype_encoding_7_bit) { - if ((((__uwchar_t) wc) > 0x7f) - || (((unsigned int)(desc - _CTYPE_tolower)) - > (_CTYPE_toupper - _CTYPE_tolower)) - ){ - /* We're in the C/POSIX locale, so ignore non-ASCII values - * as well an any mappings other than toupper or tolower. */ - return wc; - } - } - - if (desc == _CTYPE_tolower) { - return TOWLOWER(wc, __UCLIBC_CURLOCALE); - } else if (((unsigned int)(desc - _CTYPE_toupper)) - <= (_CTYPE_totitle - _CTYPE_toupper) - ) { - wc = TOWUPPER(wc, __UCLIBC_CURLOCALE); - if (desc == _CTYPE_totitle) { -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Verify totitle special cases! -#endif /* __UCLIBC_MJN3_ONLY__ */ - /* WARNING! These special cases work for glibc 2.2.4. Changes - * may be needed if the glibc locale tables are updated. */ - if ( (((__uwchar_t)(wc - 0x1c4)) <= (0x1cc - 0x1c4)) - || (wc == 0x1f1) - ) { - ++wc; - } - } - } else { - /* TODO - Deal with other transliterations. */ - __set_errno(EINVAL); - } - return wc; -} - -#endif /* SMALL_UPLOW */ - -#endif /* defined(L_towctrans) && defined(__UCLIBC_HAS_XLOCALE__) */ - -#ifdef L_towctrans_l -libc_hidden_def(towctrans_l) -#endif /* L_towctrans_l */ - -#endif /* __LOCALE_C_ONLY */ - -#ifndef L_towctrans_l -libc_hidden_def(towctrans) -#endif - -#endif -/**********************************************************************/ -#ifdef L_wctrans - -static const char transstring[] = __CTYPE_TRANSTRING; - -libc_hidden_proto(wctrans) -wctrans_t wctrans(const char *property) -{ - const unsigned char *p; - int i; - - p = transstring; - i = 1; - do { - if (!strcmp(property, ++p)) { - return i; - } - ++i; - p += p[-1]; - } while (*p); - - /* TODO - Add locale-specific translations. */ - return 0; -} -libc_hidden_def(wctrans) - -#endif -/**********************************************************************/ -#ifdef L_wctrans_l - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning REMINDER: Currently wctrans_l simply calls wctrans. -#endif /* __UCLIBC_MJN3_ONLY__ */ - -libc_hidden_proto(wctrans) - -wctrans_t wctrans_l(const char *property, __locale_t locale) -{ - return wctrans(property); -} - -#endif -/**********************************************************************/ diff --git a/libc/stdio/Makefile.in b/libc/stdio/Makefile.in index 3a6f739b4..3e0d7aeac 100644 --- a/libc/stdio/Makefile.in +++ b/libc/stdio/Makefile.in @@ -55,7 +55,7 @@ CUSRC := \ # putc_unlocked -> alias for fputc_unlocked # vfprintf and support functions -MSRC1 := vfprintf.c +MSRC1 := _vfprintf.c ifneq ($(USE_OLD_VFPRINTF),y) MOBJ1 := \ vfprintf.o \ @@ -67,7 +67,7 @@ CSRC += old_vfprintf.c endif # vfscanf and support functions plus other *scanf funcs -MSRC2 := scanf.c +MSRC2 := _scanf.c MOBJ2 := \ vfscanf.o __scan_cookie.o __psfs_parse_spec.o __psfs_do_numeric.o \ scanf.o sscanf.o fscanf.o vscanf.o vsscanf.o diff --git a/libc/stdio/_scanf.c b/libc/stdio/_scanf.c new file mode 100644 index 000000000..df78e7ad3 --- /dev/null +++ b/libc/stdio/_scanf.c @@ -0,0 +1,2268 @@ +/* Copyright (C) 2002-2004 Manuel Novoa III + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Aug 1, 2003 + * New *scanf implementation with lots of bug fixes and *wscanf support. + * Also now optionally supports hexadecimal float notation, positional + * args, and glibc locale-specific digit grouping. Should now be + * standards compliant. + * + * Aug 18, 2003 + * Bug fix: scanf %lc,%ls,%l[ would always set mb_fail on eof or error, + * even when just starting a new mb char. + * Bug fix: wscanf would incorrectly unget in certain situations. + * + * Sep 5, 2003 + * Bug fix: store flag wasn't respected if no positional args. + * Implement vs{n}scanf for the non-buffered stdio no-wchar case. + * + * Sep 13, 2003 + * Bug fix: Fix a problem reported by Atsushi Nemoto + * for environments where long and long long are the same. + * + * Sep 21, 2003 + * Ugh... EOF handling by scanf was completely broken. :-( Regretably, + * I got my mind fixed in one mode and didn't comply with the standards. + * Things should be fixed now, but comparision testing is difficult when + * glibc's scanf is broken and they stubbornly refuse to even acknowledge + * that it is... even when confronted by specific examples from the C99 + * standards and from an official C standard defect report. + */ + +#define _ISOC99_SOURCE /* for LLONG_MAX primarily... */ +#include +#include "_stdio.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __UCLIBC_HAS_WCHAR__ +#include +#include +#include +#endif /* __UCLIBC_HAS_WCHAR__ */ + +#include +#include + +#include +#include + +#ifdef __UCLIBC_HAS_THREADS__ +#include +#include +#endif /* __UCLIBC_HAS_THREADS__ */ + +#ifdef __UCLIBC_HAS_FLOATS__ +#include +#include +#endif /* __UCLIBC_HAS_FLOATS__ */ + +libc_hidden_proto(memcmp) +libc_hidden_proto(memset) +libc_hidden_proto(strcpy) +libc_hidden_proto(strlen) +libc_hidden_proto(ungetc) +libc_hidden_proto(vfscanf) +libc_hidden_proto(vsscanf) +libc_hidden_proto(fclose) +libc_hidden_proto(getc_unlocked) +libc_hidden_proto(__fgetc_unlocked) +#ifdef __UCLIBC_HAS_WCHAR__ +libc_hidden_proto(wcslen) +libc_hidden_proto(vfwscanf) +libc_hidden_proto(vswscanf) +libc_hidden_proto(mbsrtowcs) +libc_hidden_proto(mbrtowc) +libc_hidden_proto(wcrtomb) +libc_hidden_proto(ungetwc) +libc_hidden_proto(iswspace) +libc_hidden_proto(fgetwc_unlocked) +#endif +#ifdef __UCLIBC_HAS_XLOCALE__ +libc_hidden_proto(__ctype_b_loc) +#else +#ifdef __UCLIBC_HAS_LOCALE__ +libc_hidden_proto(__global_locale) +#endif +libc_hidden_proto(__ctype_b) +#endif + +#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ +#ifdef L_vfscanf +/* only emit this once */ +#warning Forcing undef of __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ until implemented! +#endif +#undef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ +#endif + +#undef __STDIO_HAS_VSSCANF +#if defined(__STDIO_BUFFERS) || !defined(__UCLIBC_HAS_WCHAR__) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) +#define __STDIO_HAS_VSSCANF 1 + +#if !defined(__STDIO_BUFFERS) && !defined(__UCLIBC_HAS_WCHAR__) +typedef struct { + FILE f; + unsigned char *bufread; /* pointer to 1 past end of buffer */ + unsigned char *bufpos; +} __FILE_vsscanf; +#endif + +#endif + +extern void _store_inttype(void *dest, int desttype, uintmax_t val); + +#if defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) + +extern unsigned long long +_stdlib_strto_ll(register const char * __restrict str, + char ** __restrict endptr, int base, int sflag); +#if (ULLONG_MAX == UINTMAX_MAX) +#define STRTOUIM(s,e,b,sf) _stdlib_strto_ll(s,e,b,sf) +#endif + +#else /* defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) */ + +extern unsigned long +_stdlib_strto_l(register const char * __restrict str, + char ** __restrict endptr, int base, int sflag); + +#if (ULONG_MAX == UINTMAX_MAX) +#define STRTOUIM(s,e,b,sf) _stdlib_strto_l(s,e,b,sf) +#endif + +#endif /* defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) */ + +#ifndef STRTOUIM +#error STRTOUIM conversion function is undefined! +#endif + +/**********************************************************************/ + +/* The standards require EOF < 0. */ +#if EOF >= CHAR_MIN +#define __isdigit_char_or_EOF(C) __isdigit_char((C)) +#else +#define __isdigit_char_or_EOF(C) __isdigit_int((C)) +#endif + +/**********************************************************************/ +#ifdef L_fscanf + +libc_hidden_proto(fscanf) +int fscanf(FILE * __restrict stream, const char * __restrict format, ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = vfscanf(stream, format, arg); + va_end(arg); + + return rv; +} +libc_hidden_def(fscanf) + +#endif +/**********************************************************************/ +#ifdef L_scanf + +int scanf(const char * __restrict format, ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = vfscanf(stdin, format, arg); + va_end(arg); + + return rv; +} + +#endif +/**********************************************************************/ +#ifdef L_sscanf + +#ifdef __STDIO_HAS_VSSCANF + +libc_hidden_proto(sscanf) +int sscanf(const char * __restrict str, const char * __restrict format, ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = vsscanf(str, format, arg); + va_end(arg); + + return rv; +} +libc_hidden_def(sscanf) + +#else /* __STDIO_HAS_VSSCANF */ +#warning Skipping sscanf since no vsscanf! +#endif /* __STDIO_HAS_VSSCANF */ + +#endif +/**********************************************************************/ +#ifdef L_vscanf + +libc_hidden_proto(vscanf) +int vscanf(const char * __restrict format, va_list arg) +{ + return vfscanf(stdin, format, arg); +} +libc_hidden_def(vscanf) + +#endif +/**********************************************************************/ +#ifdef L_vsscanf + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning WISHLIST: Implement vsscanf for non-buf and no custom stream case. +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#ifdef __STDIO_BUFFERS + +int vsscanf(__const char *sp, __const char *fmt, va_list ap) +{ + FILE f; + +/* __STDIO_STREAM_RESET_GCS(&f); */ +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + f.__cookie = &(f.__filedes); + f.__gcs.read = NULL; + f.__gcs.write = NULL; + f.__gcs.seek = NULL; + f.__gcs.close = NULL; +#endif + + f.__filedes = __STDIO_STREAM_FAKE_VSSCANF_FILEDES; + f.__modeflags = (__FLAG_NARROW|__FLAG_READONLY|__FLAG_READING); + +#ifdef __UCLIBC_HAS_WCHAR__ + f.__ungot_width[0] = 0; +#endif +#ifdef __STDIO_MBSTATE + __INIT_MBSTATE(&(f.__state)); +#endif + +#ifdef __UCLIBC_HAS_THREADS__ + f.__user_locking = 1; /* Set user locking. */ + __stdio_init_mutex(&f.__lock); +#endif + f.__nextopen = NULL; + + /* Set these last since __bufgetc initialization depends on + * __user_locking and only gets set if user locking is on. */ + f.__bufstart = + f.__bufpos = (unsigned char *) ((void *) sp); + f.__bufread = + f.__bufend = f.__bufstart + strlen(sp); + __STDIO_STREAM_ENABLE_GETC(&f); + __STDIO_STREAM_DISABLE_PUTC(&f); + + return vfscanf(&f, fmt, ap); +} +libc_hidden_def(vsscanf) + +#elif !defined(__UCLIBC_HAS_WCHAR__) + +int vsscanf(__const char *sp, __const char *fmt, va_list ap) +{ + __FILE_vsscanf f; + + f.bufpos = (unsigned char *) ((void *) sp); + f.bufread = f.bufpos + strlen(sp); + +/* __STDIO_STREAM_RESET_GCS(&f.f); */ +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + f.f.__cookie = &(f.f.__filedes); + f.f.__gcs.read = NULL; + f.f.__gcs.write = NULL; + f.f.__gcs.seek = NULL; + f.f.__gcs.close = NULL; +#endif + + f.f.__filedes = __STDIO_STREAM_FAKE_VSSCANF_FILEDES_NB; + f.f.__modeflags = (__FLAG_NARROW|__FLAG_READONLY|__FLAG_READING); + +/* #ifdef __UCLIBC_HAS_WCHAR__ */ +/* f.f.__ungot_width[0] = 0; */ +/* #endif */ +#ifdef __STDIO_MBSTATE +#error __STDIO_MBSTATE is defined! +/* __INIT_MBSTATE(&(f.f.__state)); */ +#endif + +#ifdef __UCLIBC_HAS_THREADS__ + f.f.__user_locking = 1; /* Set user locking. */ + __stdio_init_mutex(&f.f.__lock); +#endif + f.f.__nextopen = NULL; + + return vfscanf(&f.f, fmt, ap); +} +libc_hidden_def(vsscanf) + +#elif defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) + +int vsscanf(__const char *sp, __const char *fmt, va_list ap) +{ + FILE *f; + int rv = EOF; + + if ((f = fmemopen((char *)sp, strlen(sp), "r")) != NULL) { + rv = vfscanf(f, fmt, ap); + fclose(f); + } + + return rv; +} +libc_hidden_def(vsscanf) + +#else +#warning Skipping vsscanf since no buffering, no custom streams, and wchar enabled! +#ifdef __STDIO_HAS_VSSCANF +#error WHOA! __STDIO_HAS_VSSCANF is defined! +#endif +#endif + +#endif +/**********************************************************************/ +#ifdef L_fwscanf + +int fwscanf(FILE * __restrict stream, const wchar_t * __restrict format, ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = vfwscanf(stream, format, arg); + va_end(arg); + + return rv; +} + +#endif +/**********************************************************************/ +#ifdef L_wscanf + +int wscanf(const wchar_t * __restrict format, ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = vfwscanf(stdin, format, arg); + va_end(arg); + + return rv; +} + +#endif +/**********************************************************************/ +#ifdef L_swscanf + +#ifdef __STDIO_BUFFERS + +int swscanf(const wchar_t * __restrict str, const wchar_t * __restrict format, + ...) +{ + va_list arg; + int rv; + + va_start(arg, format); + rv = vswscanf(str, format, arg); + va_end(arg); + + return rv; +} +#else /* __STDIO_BUFFERS */ +#warning Skipping swscanf since no buffering! +#endif /* __STDIO_BUFFERS */ + +#endif +/**********************************************************************/ +#ifdef L_vwscanf + +int vwscanf(const wchar_t * __restrict format, va_list arg) +{ + return vfwscanf(stdin, format, arg); +} + +#endif +/**********************************************************************/ +#ifdef L_vswscanf + +#ifdef __STDIO_BUFFERS + +int vswscanf(const wchar_t * __restrict str, const wchar_t * __restrict format, + va_list arg) +{ + FILE f; + + f.__bufstart = + f.__bufpos = (char *) str; + f.__bufread = + f.__bufend = (char *)(str + wcslen(str)); + __STDIO_STREAM_DISABLE_GETC(&f); + __STDIO_STREAM_DISABLE_PUTC(&f); + +/* __STDIO_STREAM_RESET_GCS(&f); */ +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ + f.__cookie = &(f.__filedes); + f.__gcs.read = NULL; + f.__gcs.write = NULL; + f.__gcs.seek = NULL; + f.__gcs.close = NULL; +#endif + + f.__filedes = __STDIO_STREAM_FAKE_VSWSCANF_FILEDES; + f.__modeflags = (__FLAG_WIDE|__FLAG_READONLY|__FLAG_READING); + +#ifdef __UCLIBC_HAS_WCHAR__ + f.__ungot_width[0] = 0; +#endif /* __UCLIBC_HAS_WCHAR__ */ +#ifdef __STDIO_MBSTATE + __INIT_MBSTATE(&(f.__state)); +#endif /* __STDIO_MBSTATE */ + +#ifdef __UCLIBC_HAS_THREADS__ + f.__user_locking = 1; /* Set user locking. */ + __stdio_init_mutex(&f.__lock); +#endif + f.__nextopen = NULL; + + return vfwscanf(&f, format, arg); +} +libc_hidden_def(vswscanf) +#else /* __STDIO_BUFFERS */ +#warning Skipping vswscanf since no buffering! +#endif /* __STDIO_BUFFERS */ + +#endif +/**********************************************************************/ +/**********************************************************************/ + + + +/* float layout 0123456789012345678901 repeat n for "l[" */ +#define SPEC_CHARS "npxXoudifFeEgGaACSncs[" +/* npxXoudif eEgG CS cs[ */ + +/* NOTE: Ordering is important! In particular, CONV_LEFTBRACKET + * must immediately precede CONV_c. */ + +enum { + CONV_n = 0, + CONV_p, + CONV_x, CONV_X, CONV_o, CONV_u, CONV_d, CONV_i, + CONV_f, CONV_F, CONV_e, CONV_E, CONV_g, CONV_G, CONV_a, CONV_A, + CONV_C, CONV_S, CONV_LEFTBRACKET, CONV_c, CONV_s, CONV_leftbracket, + CONV_percent, CONV_whitespace /* not in SPEC_* and no flags */ +}; + +#ifdef __UCLIBC_HAS_FLOATS__ +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ +/* p x X o u d i f F e E g G a A */ +#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +#else +/* p x X o u d i f F e E g G a A */ +#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, 10 } +#endif +#else /* __UCLIBC_HAS_FLOATS__ */ +/* p x X o u d i f F e E g G a A */ +#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 0 } +#endif /* __UCLIBC_HAS_FLOATS__ */ + +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_vfscanf +/* emit once */ +#warning CONSIDER: Add a '0' flag to eat 0 padding when grouping? +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#define SPEC_FLAGS "*'I" + +enum { + FLAG_SURPRESS = 0x10, /* MUST BE 1ST!! See DO_FLAGS. */ + FLAG_THOUSANDS = 0x20, + FLAG_I18N = 0x40, /* only works for d, i, u */ + FLAG_MALLOC = 0x80, /* only works for s, S, and [ (and l[)*/ +}; + + +#define SPEC_RANGES { CONV_n, CONV_p, CONV_i, CONV_A, \ + CONV_C, CONV_LEFTBRACKET, \ + CONV_c, CONV_leftbracket } + +/* Note: We treat L and ll as synonymous... for ints and floats. */ + +#define SPEC_ALLOWED_FLAGS { \ + /* n */ (0x0f|FLAG_SURPRESS), \ + /* p */ ( 0|FLAG_SURPRESS), \ + /* oxXudi */ (0x0f|FLAG_SURPRESS|FLAG_THOUSANDS|FLAG_I18N), \ + /* fFeEgGaA */ (0x0c|FLAG_SURPRESS|FLAG_THOUSANDS|FLAG_I18N), \ + /* C */ ( 0|FLAG_SURPRESS), \ + /* S and l[ */ ( 0|FLAG_SURPRESS|FLAG_MALLOC), \ + /* c */ (0x04|FLAG_SURPRESS), \ + /* s and [ */ (0x04|FLAG_SURPRESS|FLAG_MALLOC), \ +} + + +/**********************************************************************/ +/* + * In order to ease translation to what arginfo and _print_info._flags expect, + * we map: 0:int 1:char 2:longlong 4:long 8:short + * and then _flags |= (((q << 7) + q) & 0x701) and argtype |= (_flags & 0x701) + */ + +/* TODO -- Fix the table below to take into account stdint.h. */ +/* #ifndef LLONG_MAX */ +/* #error fix QUAL_CHARS for no long long! Affects 'L', 'j', 'q', 'll'. */ +/* #else */ +/* #if LLONG_MAX != INTMAX_MAX */ +/* #error fix QUAL_CHARS intmax_t entry 'j'! */ +/* #endif */ +/* #endif */ + +#ifdef PDS +#error PDS already defined! +#endif +#ifdef SS +#error SS already defined! +#endif +#ifdef IMS +#error IMS already defined! +#endif + +#if PTRDIFF_MAX == INT_MAX +#define PDS 0 +#elif PTRDIFF_MAX == LONG_MAX +#define PDS 4 +#elif defined(LLONG_MAX) && (PTRDIFF_MAX == LLONG_MAX) +#define PDS 8 +#else +#error fix QUAL_CHARS ptrdiff_t entry 't'! +#endif + +#if SIZE_MAX == UINT_MAX +#define SS 0 +#elif SIZE_MAX == ULONG_MAX +#define SS 4 +#elif defined(LLONG_MAX) && (SIZE_MAX == ULLONG_MAX) +#define SS 8 +#else +#error fix QUAL_CHARS size_t entries 'z', 'Z'! +#endif + +#if INTMAX_MAX == INT_MAX +#define IMS 0 +#elif INTMAX_MAX == LONG_MAX +#define IMS 4 +#elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX) +#define IMS 8 +#else +#error fix QUAL_CHARS ptrdiff_t entry 't'! +#endif + +#define QUAL_CHARS { \ + /* j:(u)intmax_t z:(s)size_t t:ptrdiff_t \0:int q:long_long */ \ + 'h', 'l', 'L', 'j', 'z', 't', 'q', 0, \ + 2, 4, 8, IMS, SS, PDS, 8, 0, /* TODO -- fix!!! */\ + 1, 8 } + + +/**********************************************************************/ + +#ifdef L_vfwscanf +#if WINT_MIN > EOF +#error Unfortunately, we currently need wint_t to be able to store EOF. Sorry. +#endif +#define W_EOF WEOF +#define Wint wint_t +#define Wchar wchar_t +#define Wuchar __uwchar_t +#define ISSPACE(C) iswspace((C)) +#define VFSCANF vfwscanf +#define GETC(SC) (SC)->sc_getc((SC)) +#else +typedef unsigned char __uchar_t; +#define W_EOF EOF +#define Wint int +#define Wchar char +#define Wuchar __uchar_t +#define ISSPACE(C) isspace((C)) +#define VFSCANF vfscanf +#ifdef __UCLIBC_HAS_WCHAR__ +#define GETC(SC) (SC)->sc_getc((SC)) +#else /* __UCLIBC_HAS_WCHAR__ */ +#define GETC(SC) getc_unlocked((SC)->fp) +#endif /* __UCLIBC_HAS_WCHAR__ */ +#endif + +struct scan_cookie { + Wint cc; + Wint ungot_char; + FILE *fp; + int nread; + int width; + +#ifdef __UCLIBC_HAS_WCHAR__ + wchar_t app_ungot; /* Match FILE struct member type. */ + unsigned char ungot_wchar_width; +#else /* __UCLIBC_HAS_WCHAR__ */ + unsigned char app_ungot; /* Match FILE struct member type. */ +#endif /* __UCLIBC_HAS_WCHAR__ */ + + char ungot_flag; + +#ifdef __UCLIBC_HAS_WCHAR__ + char ungot_wflag; /* vfwscanf */ + char mb_fail; /* vfscanf */ + mbstate_t mbstate; /* vfscanf */ + wint_t wc; + wint_t ungot_wchar; /* to support __scan_getc */ + int (*sc_getc)(struct scan_cookie *); +#endif /* __UCLIBC_HAS_WCHAR__ */ + +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + const char *grouping; + const unsigned char *thousands_sep; + int tslen; +#ifdef __UCLIBC_HAS_WCHAR__ + wchar_t thousands_sep_wc; +#endif /* __UCLIBC_HAS_WCHAR__ */ +#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ + +#ifdef __UCLIBC_HAS_FLOATS__ + const unsigned char *decpt; + int decpt_len; +#ifdef __UCLIBC_HAS_WCHAR__ + wchar_t decpt_wc; +#endif /* __UCLIBC_HAS_WCHAR__ */ + const unsigned char *fake_decpt; +#endif /* __UCLIBC_HAS_FLOATS__ */ + +}; + +typedef struct { +#if defined(NL_ARGMAX) && (NL_ARGMAX > 0) +#if NL_ARGMAX > 10 +#warning NL_ARGMAX > 10, and space is allocated on the stack for positional args. +#endif + void *pos_args[NL_ARGMAX]; + int num_pos_args; /* Must start at -1. */ + int cur_pos_arg; +#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */ + void *cur_ptr; + const unsigned char *fmt; + int cnt, dataargtype, conv_num, max_width; + unsigned char store, flags; +} psfs_t; /* parse scanf format state */ + + +/**********************************************************************/ +/**********************************************************************/ + +extern void __init_scan_cookie(register struct scan_cookie *sc, + register FILE *fp) attribute_hidden; +extern int __scan_getc(register struct scan_cookie *sc) attribute_hidden; +extern void __scan_ungetc(register struct scan_cookie *sc) attribute_hidden; + +#ifdef __UCLIBC_HAS_FLOATS__ +extern int __scan_strtold(long double *ld, struct scan_cookie *sc); +#endif /* __UCLIBC_HAS_FLOATS__ */ + +extern int __psfs_parse_spec(psfs_t *psfs) attribute_hidden; +extern int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc) attribute_hidden; + +/**********************************************************************/ +#ifdef L___scan_cookie + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Remove dependence on decpt_str and fake_decpt in stub locale mode. +#endif +#ifndef __UCLIBC_HAS_LOCALE__ +static const char decpt_str[] = "."; +#endif + +void attribute_hidden __init_scan_cookie(register struct scan_cookie *sc, + register FILE *fp) +{ + sc->fp = fp; + sc->nread = 0; + sc->ungot_flag = 0; + sc->app_ungot = ((fp->__modeflags & __FLAG_UNGOT) ? fp->__ungot[1] : 0); +#ifdef __UCLIBC_HAS_WCHAR__ + sc->ungot_wflag = 0; /* vfwscanf */ + sc->mb_fail = 0; +#endif /* __UCLIBC_HAS_WCHAR__ */ + +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + if (*(sc->grouping = __UCLIBC_CURLOCALE_DATA.grouping)) { + sc->thousands_sep = __UCLIBC_CURLOCALE_DATA.thousands_sep; + sc->tslen = __UCLIBC_CURLOCALE_DATA.thousands_sep_len; +#ifdef __UCLIBC_HAS_WCHAR__ + sc->thousands_sep_wc = __UCLIBC_CURLOCALE_DATA.thousands_sep_wc; +#endif /* __UCLIBC_HAS_WCHAR__ */ + } +#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ + +#ifdef __UCLIBC_HAS_FLOATS__ +#ifdef __UCLIBC_HAS_LOCALE__ + sc->decpt = __UCLIBC_CURLOCALE_DATA.decimal_point; + sc->decpt_len = __UCLIBC_CURLOCALE_DATA.decimal_point_len; +#else /* __UCLIBC_HAS_LOCALE__ */ + sc->fake_decpt = sc->decpt = decpt_str; + sc->decpt_len = 1; +#endif /* __UCLIBC_HAS_LOCALE__ */ +#ifdef __UCLIBC_HAS_WCHAR__ +#ifdef __UCLIBC_HAS_LOCALE__ + sc->decpt_wc = __UCLIBC_CURLOCALE_DATA.decimal_point_wc; +#else + sc->decpt_wc = '.'; +#endif +#endif /* __UCLIBC_HAS_WCHAR__ */ +#endif /* __UCLIBC_HAS_FLOATS__ */ + +} + +int attribute_hidden __scan_getc(register struct scan_cookie *sc) +{ + int c; + +#ifdef __UCLIBC_HAS_WCHAR__ + assert(!sc->mb_fail); +#endif /* __UCLIBC_HAS_WCHAR__ */ + + sc->cc = EOF; + + if (--sc->width < 0) { + sc->ungot_flag |= 2; + return -1; + } + + if (sc->ungot_flag == 0) { +#if !defined(__STDIO_BUFFERS) && !defined(__UCLIBC_HAS_WCHAR__) + if (!__STDIO_STREAM_IS_FAKE_VSSCANF_NB(sc->fp)) { + c = GETC(sc); + } else { + __FILE_vsscanf *fv = (__FILE_vsscanf *)(sc->fp); + if (fv->bufpos < fv->bufread) { + c = *fv->bufpos++; + } else { + c = EOF; + sc->fp->__modeflags |= __FLAG_EOF; + } + } + if (c == EOF) { + sc->ungot_flag |= 2; + return -1; + } +#else + if ((c = GETC(sc)) == EOF) { + sc->ungot_flag |= 2; + return -1; + } +#endif + sc->ungot_char = c; + } else { + assert(sc->ungot_flag == 1); + sc->ungot_flag = 0; + } + + ++sc->nread; + return sc->cc = sc->ungot_char; +} + +void attribute_hidden __scan_ungetc(register struct scan_cookie *sc) +{ + ++sc->width; + if (sc->ungot_flag == 2) { /* last was EOF */ + sc->ungot_flag = 0; + sc->cc = sc->ungot_char; + } else if (sc->ungot_flag == 0) { + sc->ungot_flag = 1; + --sc->nread; + } else { + assert(0); + } +} + +#endif +/**********************************************************************/ +#ifdef L___psfs_parse_spec + +#ifdef SPEC_FLAGS +static const unsigned char spec_flags[] = SPEC_FLAGS; +#endif /* SPEC_FLAGS */ +static const unsigned char spec_chars[] = SPEC_CHARS; +static const unsigned char qual_chars[] = QUAL_CHARS; +static const unsigned char spec_ranges[] = SPEC_RANGES; +static const unsigned short spec_allowed[] = SPEC_ALLOWED_FLAGS; + +int attribute_hidden __psfs_parse_spec(register psfs_t *psfs) +{ + const unsigned char *p; + const unsigned char *fmt0 = psfs->fmt; + int i; +#ifdef SPEC_FLAGS + int j; +#endif +#if defined(NL_ARGMAX) && (NL_ARGMAX > 0) + unsigned char fail = 0; + + i = 0; /* Do this here to avoid a warning. */ + + if (!__isdigit_char(*psfs->fmt)) { /* Not a positional arg. */ + fail = 1; + goto DO_FLAGS; + } + + /* parse the positional arg (or width) value */ + do { + if (i <= ((INT_MAX - 9)/10)) { + i = (i * 10) + (*psfs->fmt++ - '0'); + } + } while (__isdigit_char(*psfs->fmt)); + + if (*psfs->fmt != '$') { /* This is a max field width. */ + if (psfs->num_pos_args >= 0) { /* Already saw a pos arg! */ + goto ERROR_EINVAL; + } + psfs->max_width = i; + psfs->num_pos_args = -2; + goto DO_QUALIFIER; + } + ++psfs->fmt; /* Advance past '$'. */ +#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */ + +#if defined(SPEC_FLAGS) || (defined(NL_ARGMAX) && (NL_ARGMAX > 0)) + DO_FLAGS: +#endif /* defined(SPEC_FLAGS) || (defined(NL_ARGMAX) && (NL_ARGMAX > 0)) */ +#ifdef SPEC_FLAGS + p = spec_flags; + j = FLAG_SURPRESS; + do { + if (*p == *psfs->fmt) { + ++psfs->fmt; + psfs->flags |= j; + goto DO_FLAGS; + } + j += j; + } while (*++p); + + if (psfs->flags & FLAG_SURPRESS) { /* Suppress assignment. */ + psfs->store = 0; + goto DO_WIDTH; + } +#else /* SPEC_FLAGS */ + if (*psfs->fmt == '*') { /* Suppress assignment. */ + ++psfs->fmt; + psfs->store = 0; + goto DO_WIDTH; + } +#endif /* SPEC_FLAGS */ + + +#if defined(NL_ARGMAX) && (NL_ARGMAX > 0) + if (fail) { + /* Must be a non-positional arg */ + if (psfs->num_pos_args >= 0) { /* Already saw a pos arg! */ + goto ERROR_EINVAL; + } + psfs->num_pos_args = -2; + } else { + if ((psfs->num_pos_args == -2) || (((unsigned int)(--i)) >= NL_ARGMAX)) { + /* Already saw a non-pos arg or (0-based) num too large. */ + goto ERROR_EINVAL; + } + psfs->cur_pos_arg = i; + } +#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */ + + DO_WIDTH: + for (i = 0 ; __isdigit_char(*psfs->fmt) ; ) { + if (i <= ((INT_MAX - 9)/10)) { + i = (i * 10) + (*psfs->fmt++ - '0'); + psfs->max_width = i; + } + } + +#if defined(NL_ARGMAX) && (NL_ARGMAX > 0) + DO_QUALIFIER: +#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */ + p = qual_chars; + do { + if (*psfs->fmt == *p) { + ++psfs->fmt; + break; + } + } while (*++p); + if ((p - qual_chars < 2) && (*psfs->fmt == *p)) { + p += ((sizeof(qual_chars)-2) / 2); + ++psfs->fmt; + } + psfs->dataargtype = ((int)(p[(sizeof(qual_chars)-2) / 2])) << 8; + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Should we validate that psfs->max_width > 0 in __psfs_parse_spec()? It would avoid whitespace consumption... +#warning CONSIDER: Should INT_MAX be a valid width (%c/%C)? See __psfs_parse_spec(). +#endif /* __UCLIBC_MJN3_ONLY__ */ + + p = spec_chars; + do { + if (*psfs->fmt == *p) { + int p_m_spec_chars = p - spec_chars; + +#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ +#error implement gnu a flag + if ((*p == 'a') + && ((psfs->fmt[1] == '[') || ((psfs->fmt[1]|0x20) == 's')) + ) { /* Assumes ascii for 's' and 'S' test. */ + psfs->flags |= FLAG_MALLOC; + ++psfs->fmt; + ++p; + continue; /* The related conversions follow 'a'. */ + } +#endif /* __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ */ + + for (p = spec_ranges; p_m_spec_chars > *p ; ++p) {} + if (((psfs->dataargtype >> 8) | psfs->flags) + & ~spec_allowed[(int)(p - spec_ranges)] + ) { + goto ERROR_EINVAL; + } + + if ((p_m_spec_chars >= CONV_c) + && (psfs->dataargtype & PA_FLAG_LONG)) { + p_m_spec_chars -= 3; /* lc -> C, ls -> S, l[ -> ?? */ + } + + psfs->conv_num = p_m_spec_chars; + return psfs->fmt - fmt0; + } + if (!*++p) { + ERROR_EINVAL: + __set_errno(EINVAL); + return -1; + } + } while(1); + + assert(0); +} + +#endif +/**********************************************************************/ +#if defined(L_vfscanf) || defined(L_vfwscanf) + +#ifdef __UCLIBC_HAS_WCHAR__ +#ifdef L_vfscanf +static int sc_getc(register struct scan_cookie *sc) +{ + return (getc_unlocked)(sc->fp); /* Disable the macro. */ +} + +static int scan_getwc(register struct scan_cookie *sc) +{ + size_t r; + int width; + wchar_t wc[1]; + char b[1]; + + if (--sc->width < 0) { + sc->ungot_flag |= 2; + return -1; + } + + width = sc->width; /* Preserve width. */ + sc->width = INT_MAX; /* MB_CUR_MAX can invoke a function. */ + + assert(!sc->mb_fail); + + r = (size_t)(-3); + while (__scan_getc(sc) >= 0) { + *b = sc->cc; + + r = mbrtowc(wc, b, 1, &sc->mbstate); + if (((ssize_t) r) >= 0) { /* Successful completion of a wc. */ + sc->wc = *wc; + goto SUCCESS; + } else if (r == ((size_t) -2)) { + /* Potentially valid but incomplete. */ + continue; + } + break; + } + + if (r == ((size_t)(-3))) { /* EOF or ERROR on first read */ + sc->wc = WEOF; + r = (size_t)(-1); + } else { + /* If we reach here, either r == ((size_t)-1) and + * mbrtowc set errno to EILSEQ, or r == ((size_t)-2) + * and stream is in an error state or at EOF with a + * partially complete wchar. */ + __set_errno(EILSEQ); /* In case of incomplete conversion. */ + sc->mb_fail = 1; + } + + SUCCESS: + sc->width = width; /* Restore width. */ + + return (int)((ssize_t) r); +} + +#endif /* L_vfscanf */ + +#ifdef L_vfwscanf + +/* This gets called by __scan_getc. __scan_getc is called by vfwscanf + * when the next wide char is expected to be valid ascii (digits). + */ +static int sc_getc(register struct scan_cookie *sc) +{ + wint_t wc; + + if (__STDIO_STREAM_IS_FAKE_VSWSCANF(sc->fp)) { + if (sc->fp->__bufpos < sc->fp->__bufend) { + wc = *((wchar_t *)(sc->fp->__bufpos)); + sc->fp->__bufpos += sizeof(wchar_t); + } else { + sc->fp->__modeflags |= __FLAG_EOF; + return EOF; + } + } else if ((wc = fgetwc_unlocked(sc->fp)) == WEOF) { + return EOF; + } + + sc->ungot_wflag = 1; + sc->ungot_wchar = wc; + sc->ungot_wchar_width = sc->fp->__ungot_width[0]; + +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + if (wc == sc->thousands_sep_wc) { + wc = ','; + } else +#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ +#ifdef __UCLIBC_HAS_FLOATS__ + if (wc == sc->decpt_wc) { + wc = '.'; + } else +#endif /* __UCLIBC_HAS_FLOATS__ */ + if (!__isascii(wc)) { + wc = '?'; + } + sc->wc = sc->ungot_char = wc; + + return (int) wc; +} + +static int scan_getwc(register struct scan_cookie *sc) +{ + wint_t wc; + + sc->wc = WEOF; + + if (--sc->width < 0) { + sc->ungot_flag |= 2; + return -1; + } + + if (sc->ungot_flag == 0) { + if (__STDIO_STREAM_IS_FAKE_VSWSCANF(sc->fp)) { + if (sc->fp->__bufpos < sc->fp->__bufend) { + wc = *((wchar_t *)(sc->fp->__bufpos)); + sc->fp->__bufpos += sizeof(wchar_t); + } else { + sc->ungot_flag |= 2; + return -1; + } + } else if ((wc = fgetwc_unlocked(sc->fp)) == WEOF) { + sc->ungot_flag |= 2; + return -1; + } + sc->ungot_wflag = 1; + sc->ungot_char = wc; + sc->ungot_wchar_width = sc->fp->__ungot_width[0]; + } else { + assert(sc->ungot_flag == 1); + sc->ungot_flag = 0; + } + + ++sc->nread; + sc->wc = sc->ungot_char; + + return 0; +} + + +#endif /* L_vfwscanf */ +#endif /* __UCLIBC_HAS_WCHAR__ */ + +static __inline void kill_scan_cookie(register struct scan_cookie *sc) +{ +#ifdef L_vfscanf + + if (sc->ungot_flag & 1) { +#if !defined(__STDIO_BUFFERS) && !defined(__UCLIBC_HAS_WCHAR__) + if (!__STDIO_STREAM_IS_FAKE_VSSCANF_NB(sc->fp)) { + ungetc(sc->ungot_char, sc->fp); + } +#else + ungetc(sc->ungot_char, sc->fp); +#endif + /* Deal with distiction between user and scanf ungots. */ + if (sc->nread == 0) { /* Only one char was read... app ungot? */ + sc->fp->__ungot[1] = sc->app_ungot; /* restore ungot state. */ + } else { + sc->fp->__ungot[1] = 0; + } + } + +#else + + if ((sc->ungot_flag & 1) && (sc->ungot_wflag & 1) + && !__STDIO_STREAM_IS_FAKE_VSWSCANF(sc->fp) + && (sc->fp->__state.__mask == 0) + ) { + ungetwc(sc->ungot_char, sc->fp); + /* Deal with distiction between user and scanf ungots. */ + if (sc->nread == 0) { /* Only one char was read... app ungot? */ + sc->fp->__ungot[1] = sc->app_ungot; /* restore ungot state. */ + } else { + sc->fp->__ungot[1] = 0; + } + sc->fp->__ungot_width[1] = sc->ungot_wchar_width; + } + +#endif +} + +#ifdef L_vfwscanf +#ifdef __UCLIBC_HAS_FLOATS__ +static const char fake_decpt_str[] = "."; +#endif +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ +static const char fake_thousands_sep_str[] = ","; +#endif +#endif /* L_vfwscanf */ + + +int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) +{ + const Wuchar *fmt; + unsigned char *b; + + +#ifdef L_vfwscanf + wchar_t wbuf[1]; + wchar_t *wb; +#endif /* L_vfwscanf */ + +#if defined(__UCLIBC_HAS_LOCALE__) && !defined(L_vfwscanf) || !defined(L_vfscanf) + mbstate_t mbstate; +#endif + + struct scan_cookie sc; + psfs_t psfs; + + int i; + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Fix MAX_DIGITS. We do not do binary, so...! +#endif +#define MAX_DIGITS 65 /* Allow one leading 0. */ + unsigned char buf[MAX_DIGITS+2]; +#ifdef L_vfscanf + unsigned char scanset[UCHAR_MAX + 1]; + unsigned char invert; /* Careful! Meaning changes. */ +#endif /* L_vfscanf */ + unsigned char fail; + unsigned char zero_conversions = 1; + __STDIO_AUTO_THREADLOCK_VAR; + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Make checking of the format string in C locale an option. +#endif + /* To support old programs, don't check mb validity if in C locale. */ +#if defined(__UCLIBC_HAS_LOCALE__) && !defined(L_vfwscanf) + /* ANSI/ISO C99 requires format string to be a valid multibyte string + * beginning and ending in its initial shift state. */ + if (((__UCLIBC_CURLOCALE_DATA).encoding) != __ctype_encoding_7_bit) { + const char *p = format; + mbstate.__mask = 0; /* Initialize the mbstate. */ + if (mbsrtowcs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) { + __set_errno(EINVAL); /* Format string is invalid. */ + return 0; + } + } +#endif /* defined(__UCLIBC_HAS_LOCALE__) && !defined(L_vfwscanf) */ + +#if defined(NL_ARGMAX) && (NL_ARGMAX > 0) + psfs.num_pos_args = -1; /* Must start at -1. */ + /* Initialize positional arg ptrs to NULL. */ + memset(psfs.pos_args, 0, sizeof(psfs.pos_args)); +#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */ + + __STDIO_AUTO_THREADLOCK(fp); + + __STDIO_STREAM_VALIDATE(fp); + + __init_scan_cookie(&sc,fp); +#ifdef __UCLIBC_HAS_WCHAR__ + sc.sc_getc = sc_getc; + sc.ungot_wchar_width = sc.fp->__ungot_width[1]; + +#ifdef L_vfwscanf + +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + if (*sc.grouping) { + sc.thousands_sep = fake_thousands_sep_str; + sc.tslen = 1; + } +#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ + +#ifdef __UCLIBC_HAS_FLOATS__ + sc.fake_decpt = fake_decpt_str; +#endif /* __UCLIBC_HAS_FLOATS__ */ + +#else /* L_vfwscanf */ + +#ifdef __UCLIBC_HAS_FLOATS__ + sc.fake_decpt = sc.decpt; +#endif /* __UCLIBC_HAS_FLOATS__ */ + +#endif /* L_vfwscanf */ + +#endif /* __UCLIBC_HAS_WCHAR__ */ + psfs.cnt = 0; + + /* Note: If we ever wanted to support non-nice codesets, we + * would really need to do a mb->wc conversion here in the + * vfscanf case. Related changes would have to be made in + * the code that follows... basicly wherever fmt appears. */ + for (fmt = (const Wuchar *) format ; *fmt ; /* ++fmt */) { + + psfs.store = 1; + psfs.flags = 0; +#ifndef NDEBUG + psfs.cur_ptr = NULL; /* Debugging aid. */ +#endif /* NDEBUG */ + + + sc.ungot_flag &= 1; /* Clear (possible fake) EOF. */ + sc.width = psfs.max_width = INT_MAX; + + /* Note: According to the standards, vfscanf does use isspace + * here. So, if we did a mb->wc conversion, we would have to do + * something like + * ((((__uwchar_t)wc) < UCHAR_MAX) && isspace(wc)) + * because wc might not be in the allowed domain. */ + if (ISSPACE(*fmt)) { + do { + ++fmt; + } while (ISSPACE(*fmt)); + --fmt; + psfs.conv_num = CONV_whitespace; + goto DO_WHITESPACE; + } + + if (*fmt == '%') { /* Conversion specification. */ + if (*++fmt == '%') { /* Remember, '%' eats whitespace too. */ + /* Note: The standard says no conversion occurs. + * So do not reset zero_conversions flag. */ + psfs.conv_num = CONV_percent; + goto DO_CONVERSION; + } + + +#ifdef L_vfscanf + psfs.fmt = fmt; +#else /* L_vfscanf */ + { + const __uwchar_t *wf = fmt; + psfs.fmt = b = buf; + + while (*wf && __isascii(*wf) && (b < buf + sizeof(buf) - 1)) { + *b++ = *wf++; + } +#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ +#error this is wrong... we need to ched in __psfs_parse_spec instead since this checks last char in buffer and conversion my have stopped before it. + if ((*b == 'a') && ((*wf == '[') || ((*wf|0x20) == 's'))) { + goto DONE; /* Spec was excessively long. */ + } +#endif /* __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ */ + *b = 0; + if (b == buf) { /* Bad conversion specifier! */ + goto DONE; + } + } +#endif /* L_vfscanf */ + if ((i = __psfs_parse_spec(&psfs)) < 0) { /* Bad conversion specifier! */ + goto DONE; + } + fmt += i; + + if (psfs.store) { +#if defined(NL_ARGMAX) && (NL_ARGMAX > 0) + if (psfs.num_pos_args == -2) { + psfs.cur_ptr = va_arg(arg, void *); + } else { + while (psfs.cur_pos_arg > psfs.num_pos_args) { + psfs.pos_args[++psfs.num_pos_args] = va_arg(arg, void *); + } + psfs.cur_ptr = psfs.pos_args[psfs.cur_pos_arg]; + } +#else /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */ + psfs.cur_ptr = va_arg(arg, void *); +#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */ + } + + DO_CONVERSION: + /* First, consume white-space if not n, c, [, C, or l[. */ + if ((((1L << CONV_n)|(1L << CONV_C)|(1L << CONV_c) + |(1L << CONV_LEFTBRACKET)|(1L << CONV_leftbracket)) + & (1L << psfs.conv_num)) == 0 + ) { + DO_WHITESPACE: + while ((__scan_getc(&sc) >= 0) +#ifdef L_vfscanf + && isspace(sc.cc) +#else /* L_vfscanf */ + && iswspace(sc.wc) +#endif /* L_vfscanf */ + ) {} + __scan_ungetc(&sc); + if (psfs.conv_num == CONV_whitespace) { + goto NEXT_FMT; + } + } + + sc.width = psfs.max_width; /* Now limit the max width. */ + + if (sc.width == 0) { /* 0 width is forbidden. */ + goto DONE; + } + + + if (psfs.conv_num == CONV_percent) { + goto MATCH_CHAR; + } + + if (psfs.conv_num == CONV_n) { +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Should %n count as a conversion as far as EOF return value? +#endif +/* zero_conversions = 0; */ + if (psfs.store) { + _store_inttype(psfs.cur_ptr, psfs.dataargtype, + (uintmax_t) sc.nread); + } + goto NEXT_FMT; + } + + if (psfs.conv_num <= CONV_A) { /* pointer, integer, or float spec */ + int r = __psfs_do_numeric(&psfs, &sc); +#ifndef L_vfscanf + if (sc.ungot_wflag == 1) { /* fix up '?', '.', and ',' hacks */ + sc.cc = sc.ungot_char = sc.ungot_wchar; + } +#endif + if (r != -1) { /* Either success or a matching failure. */ + zero_conversions = 0; + } + if (r < 0) { + goto DONE; + } + goto NEXT_FMT; + } + + /* Do string conversions here since they are not common code. */ + + +#ifdef L_vfscanf + + if +#ifdef __UCLIBC_HAS_WCHAR__ + (psfs.conv_num >= CONV_LEFTBRACKET) +#else /* __UCLIBC_HAS_WCHAR__ */ + (psfs.conv_num >= CONV_c) +#endif /* __UCLIBC_HAS_WCHAR__ */ + { + b = (psfs.store ? ((unsigned char *) psfs.cur_ptr) : buf); + fail = 1; + + + if (psfs.conv_num == CONV_c) { + if (sc.width == INT_MAX) { + sc.width = 1; + } + + while (__scan_getc(&sc) >= 0) { + zero_conversions = 0; + *b = sc.cc; + b += psfs.store; + } + __scan_ungetc(&sc); + if (sc.width > 0) { /* Failed to read all required. */ + goto DONE; + } + psfs.cnt += psfs.store; + goto NEXT_FMT; + } + + if (psfs.conv_num == CONV_s) { + /* Yes, believe it or not, a %s conversion can store nuls. */ + while ((__scan_getc(&sc) >= 0) && !isspace(sc.cc)) { + zero_conversions = 0; + *b = sc.cc; + b += psfs.store; + fail = 0; + } + } else { +#ifdef __UCLIBC_HAS_WCHAR__ + assert((psfs.conv_num == CONV_LEFTBRACKET) || \ + (psfs.conv_num == CONV_leftbracket)); +#else /* __UCLIBC_HAS_WCHAR__ */ + assert((psfs.conv_num == CONV_leftbracket)); +#endif /* __UCLIBC_HAS_WCHAR__ */ + + invert = 0; + + if (*++fmt == '^') { + ++fmt; + invert = 1; + } + memset(scanset, invert, sizeof(scanset)); + invert = 1-invert; + + if (*fmt == ']') { + scanset[(int)(']')] = invert; + ++fmt; + } + + while (*fmt != ']') { + if (!*fmt) { /* No closing ']'. */ + goto DONE; + } + if ((*fmt == '-') && (fmt[1] != ']') + && (fmt[-1] < fmt[1]) /* sorted? */ + ) { /* range */ + ++fmt; + i = fmt[-2]; + /* Note: scanset[i] should already have been done + * in the previous iteration. */ + do { + scanset[++i] = invert; + } while (i < *fmt); + /* Safe to fall through, and a bit smaller. */ + } + /* literal char */ + scanset[(int) *fmt] = invert; + ++fmt; + } + +#ifdef __UCLIBC_HAS_WCHAR__ + if (psfs.conv_num == CONV_LEFTBRACKET) { + goto DO_LEFTBRACKET; + } +#endif /* __UCLIBC_HAS_WCHAR__ */ + + + while (__scan_getc(&sc) >= 0) { + zero_conversions = 0; + if (!scanset[sc.cc]) { + break; + } + *b = sc.cc; + b += psfs.store; + fail = 0; + } + } + /* Common tail for processing of %s and %[. */ + + __scan_ungetc(&sc); + if (fail) { /* nothing stored! */ + goto DONE; + } + *b = 0; /* Nul-terminate string. */ + psfs.cnt += psfs.store; + goto NEXT_FMT; + } + +#ifdef __UCLIBC_HAS_WCHAR__ + DO_LEFTBRACKET: /* Need to do common wide init. */ + if (psfs.conv_num >= CONV_C) { + wchar_t wbuf[1]; + wchar_t *wb; + + sc.mbstate.__mask = 0; + + wb = (psfs.store ? ((wchar_t *) psfs.cur_ptr) : wbuf); + fail = 1; + + if (psfs.conv_num == CONV_C) { + if (sc.width == INT_MAX) { + sc.width = 1; + } + + while (scan_getwc(&sc) >= 0) { + zero_conversions = 0; + assert(sc.width >= 0); + *wb = sc.wc; + wb += psfs.store; + } + + __scan_ungetc(&sc); + if (sc.width > 0) { /* Failed to read all required. */ + goto DONE; + } + psfs.cnt += psfs.store; + goto NEXT_FMT; + } + + + if (psfs.conv_num == CONV_S) { + /* Yes, believe it or not, a %s conversion can store nuls. */ + while (scan_getwc(&sc) >= 0) { + zero_conversions = 0; + if ((((__uwchar_t)(sc.wc)) <= UCHAR_MAX) && isspace(sc.wc)) { + break; + } + *wb = sc.wc; + wb += psfs.store; + fail = 0; + } + } else { + assert(psfs.conv_num == CONV_LEFTBRACKET); + + while (scan_getwc(&sc) >= 0) { + zero_conversions = 0; + if (((__uwchar_t) sc.wc) <= UCHAR_MAX) { + if (!scanset[sc.wc]) { + break; + } + } else if (invert) { + break; + } + *wb = sc.wc; + wb += psfs.store; + fail = 0; + } + } + /* Common tail for processing of %ls and %l[. */ + + __scan_ungetc(&sc); + if (fail || sc.mb_fail) { /* Nothing stored or mb error. */ + goto DONE; + } + *wb = 0; /* Nul-terminate string. */ + psfs.cnt += psfs.store; + goto NEXT_FMT; + + } + +#endif /* __UCLIBC_HAS_WCHAR__ */ +#else /* L_vfscanf */ + + if (psfs.conv_num >= CONV_C) { + b = buf; + wb = wbuf; + if (psfs.conv_num >= CONV_c) { + mbstate.__mask = 0; /* Initialize the mbstate. */ + if (psfs.store) { + b = (unsigned char *) psfs.cur_ptr; + } + } else { + if (psfs.store) { + wb = (wchar_t *) psfs.cur_ptr; + } + } + fail = 1; + + + if ((psfs.conv_num == CONV_C) || (psfs.conv_num == CONV_c)) { + if (sc.width == INT_MAX) { + sc.width = 1; + } + + while (scan_getwc(&sc) >= 0) { + zero_conversions = 0; + if (psfs.conv_num == CONV_C) { + *wb = sc.wc; + wb += psfs.store; + } else { + i = wcrtomb(b, sc.wc, &mbstate); + if (i < 0) { /* Conversion failure. */ + goto DONE_DO_UNGET; + } + if (psfs.store) { + b += i; + } + } + } + __scan_ungetc(&sc); + if (sc.width > 0) { /* Failed to read all required. */ + goto DONE; + } + psfs.cnt += psfs.store; + goto NEXT_FMT; + } + + if ((psfs.conv_num == CONV_S) || (psfs.conv_num == CONV_s)) { + /* Yes, believe it or not, a %s conversion can store nuls. */ + while (scan_getwc(&sc) >= 0) { + zero_conversions = 0; + if (iswspace(sc.wc)) { + break; + } + if (psfs.conv_num == CONV_S) { + *wb = sc.wc; + wb += psfs.store; + } else { + i = wcrtomb(b, sc.wc, &mbstate); + if (i < 0) { /* Conversion failure. */ + goto DONE_DO_UNGET; + } + if (psfs.store) { + b += i; + } + } + fail = 0; + } + } else { + const wchar_t *sss; + const wchar_t *ssp; + unsigned char invert = 0; + + assert((psfs.conv_num == CONV_LEFTBRACKET) + || (psfs.conv_num == CONV_leftbracket)); + + if (*++fmt == '^') { + ++fmt; + invert = 1; + } + sss = (const wchar_t *) fmt; + if (*fmt == ']') { + ++fmt; + } + while (*fmt != ']') { + if (!*fmt) { /* No closing ']'. */ + goto DONE; + } + if ((*fmt == '-') && (fmt[1] != ']') + && (fmt[-1] < fmt[1]) /* sorted? */ + ) { /* range */ + ++fmt; + } + ++fmt; + } + /* Ok... a valid scanset spec. */ + + while (scan_getwc(&sc) >= 0) { + zero_conversions = 0; + ssp = sss; + do { /* We know sss < fmt. */ + if (*ssp == '-') { /* possible range... */ + /* Note: We accept a-c-e (ordered) as + * equivalent to a-e. */ + if (ssp > sss) { + if ((++ssp < (const wchar_t *) fmt) + && (ssp[-2] < *ssp) /* sorted? */ + ) { /* yes */ + if ((sc.wc >= ssp[-2]) + && (sc.wc <= *ssp)) { + break; + } + continue; /* not in range */ + } + --ssp; /* oops... '-' at end, so back up */ + } + /* false alarm... a literal '-' */ + } + if (sc.wc == *ssp) { /* Matched literal char. */ + break; + } + } while (++ssp < (const wchar_t *) fmt); + + if ((ssp == (const wchar_t *) fmt) ^ invert) { + /* no match and not inverting + * or match and inverting */ + break; + } + if (psfs.conv_num == CONV_LEFTBRACKET) { + *wb = sc.wc; + wb += psfs.store; + } else { + i = wcrtomb(b, sc.wc, &mbstate); + if (i < 0) { /* Conversion failure. */ + goto DONE_DO_UNGET; + } + if (psfs.store) { + b += i; + } + } + fail = 0; + } + } + /* Common tail for processing of %s and %[. */ + + __scan_ungetc(&sc); + if (fail) { /* nothing stored! */ + goto DONE; + } + *wb = 0; /* Nul-terminate string. */ + *b = 0; + psfs.cnt += psfs.store; + goto NEXT_FMT; + } + +#endif /* L_vfscanf */ + + assert(0); + goto DONE; + } /* conversion specification */ + + MATCH_CHAR: + if (__scan_getc(&sc) != *fmt) { +#ifdef L_vfwscanf + DONE_DO_UNGET: +#endif /* L_vfwscanf */ + __scan_ungetc(&sc); + goto DONE; + } + + NEXT_FMT: + ++fmt; + if (__FERROR_UNLOCKED(fp)) { + break; + } + } + + DONE: + if (__FERROR_UNLOCKED(fp) || (*fmt && zero_conversions && __FEOF_UNLOCKED(fp))) { + psfs.cnt = EOF; /* Yes, vfwscanf also returns EOF. */ + } + + kill_scan_cookie(&sc); + + __STDIO_STREAM_VALIDATE(fp); + + __STDIO_AUTO_THREADUNLOCK(fp); + + return psfs.cnt; +} +libc_hidden_def(VFSCANF) +#endif +/**********************************************************************/ +#ifdef L___psfs_do_numeric + +static const unsigned char spec_base[] = SPEC_BASE; +static const unsigned char nil_string[] = "(nil)"; + +int attribute_hidden __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc) +{ + unsigned char *b; + const unsigned char *p; + +#ifdef __UCLIBC_HAS_FLOATS__ + int exp_adjust = 0; +#endif +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Fix MAX_DIGITS. We do not do binary, so...! +#warning TODO: Fix buf! +#endif +#define MAX_DIGITS 65 /* Allow one leading 0. */ + unsigned char buf[MAX_DIGITS+2+ 100]; + unsigned char usflag, base; + unsigned char nonzero = 0; + unsigned char seendigit = 0; + + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: What should be returned for an invalid conversion specifier? +#endif +#ifndef __UCLIBC_HAS_FLOATS__ + if (psfs->conv_num > CONV_i) { /* floating point */ + goto DONE; + } +#endif + + base = spec_base[psfs->conv_num - CONV_p]; + usflag = (psfs->conv_num <= CONV_u); /* (1)0 if (un)signed */ + b = buf; + + + if (psfs->conv_num == CONV_p) { /* Pointer */ + p = nil_string; + do { + if ((__scan_getc(sc) < 0) || (*p != sc->cc)) { + __scan_ungetc(sc); + if (p > nil_string) { + /* We matched at least the '(' so even if we + * are at eof, we can not match a pointer. */ + return -2; /* Matching failure */ + } + break; + } + if (!*++p) { /* Matched (nil), so no unget necessary. */ + if (psfs->store) { + ++psfs->cnt; + _store_inttype(psfs->cur_ptr, psfs->dataargtype, + (uintmax_t) NULL); + } + return 0; + } + } while (1); + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Should we require a 0x prefix and disallow +/- for pointer %p? +#endif /* __UCLIBC_MJN3_ONLY__ */ + } + + __scan_getc(sc); + if (sc->cc < 0) { + return -1; /* Input failure (nothing read yet). */ + } + + if ((sc->cc == '+') || (sc->cc == '-')) { /* Handle leading sign.*/ + *b++ = sc->cc; + __scan_getc(sc); + } + + if ((base & 0xef) == 0) { /* 0xef is ~16, so 16 or 0. */ + if (sc->cc == '0') { /* Possibly set base and handle prefix. */ + __scan_getc(sc); + if ((sc->cc|0x20) == 'x') { /* Assumes ascii.. x or X. */ + if (__scan_getc(sc) < 0) { + /* Either EOF or error (including wc outside char range). + * If EOF or error, this is a matching failure (we read 0x). + * If wc outside char range, this is also a matching failure. + * Hence, we do an unget (although not really necessary here + * and fail. */ + goto DONE_DO_UNGET; /* matching failure */ + } + base = 16; /* Base 16 for sure now. */ +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + /* The prefix is required for hexadecimal floats. */ + *b++ = '0'; + *b++ = 'x'; +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + } else { /* oops... back up */ + __scan_ungetc(sc); + sc->cc = '0'; /* NASTY HACK! */ + + base = (base >> 1) + 8; /* 0->8, 16->16. no 'if' */ +#ifdef __UCLIBC_HAS_FLOATS__ + if (psfs->conv_num > CONV_i) { /* floating point */ + base = 10; + } +#endif + } + } else if (!base) { + base = 10; + } + } + + /***************** digit grouping **********************/ +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + + if ((psfs->flags & FLAG_THOUSANDS) && (base == 10) + && *(p = sc->grouping) + ) { + + int nblk1, nblk2, nbmax, lastblock, pass, i; + + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Should we initalize the grouping blocks in __init_scan_cookie()? +#endif /* __UCLIBC_MJN3_ONLY__ */ + nbmax = nblk2 = nblk1 = *p; + if (*++p) { + nblk2 = *p; + if (nbmax < nblk2) { + nbmax = nblk2; + } + assert(!p[1]); + } + + /* Note: for printf, if 0 and \' flags appear then + * grouping is done before 0-padding. Should we + * strip leading 0's first? Or add a 0 flag? */ + + /* For vfwscanf, sc_getc translates, so the value of sc->cc is + * either EOF or a char. */ + + if (!__isdigit_char_or_EOF(sc->cc)) { /* No starting digit! */ +#ifdef __UCLIBC_HAS_FLOATS__ + if (psfs->conv_num > CONV_i) { /* floating point */ + goto NO_STARTING_DIGIT; + } +#endif + goto DONE_DO_UNGET; + } + + if (sc->cc == '0') { + seendigit = 1; + *b++ = '0'; /* Store the first 0. */ +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Should leading 0s be skipped before digit grouping? (printf 0 pad) +#endif /* __UCLIBC_MJN3_ONLY__ */ +#if 0 + do { /* But ignore all subsequent 0s. */ + __scan_getc(sc); + } while (sc->cc == '0'); +#endif + } + pass = 0; + lastblock = 0; + do { + i = 0; + while (__isdigit_char_or_EOF(sc->cc)) { + seendigit = 1; + if (i == nbmax) { /* too many digits for a block */ +#ifdef __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__ + if (!pass) { /* treat as nongrouped */ + if (nonzero) { + goto DO_NO_GROUP; + } + goto DO_TRIM_LEADING_ZEROS; + } +#endif + if (nbmax > nblk1) { + goto DONE_DO_UNGET; /* matching failure */ + } + goto DONE_GROUPING_DO_UNGET; /* nbmax == nblk1 */ + } + ++i; + + if (nonzero || (sc->cc != '0')) { + if (b < buf + MAX_DIGITS) { + *b++ = sc->cc; + nonzero = 1; +#ifdef __UCLIBC_HAS_FLOATS__ + } else { + ++exp_adjust; +#endif + } + } + + __scan_getc(sc); + } + + if (i) { /* we saw digits digits */ + if ((i == nblk2) || ((i < nblk2) && !pass)) { + /* (possible) outer grp */ + p = sc->thousands_sep; + if (*p == sc->cc) { /* first byte matches... */ + /* so check if grouping mb char */ + /* Since 1st matched, either match or fail now + * unless EOF (yuk) */ + __scan_getc(sc); + MBG_LOOP: + if (!*++p) { /* is a grouping mb char */ + lastblock = i; + ++pass; + continue; + } + if (*p == sc->cc) { + __scan_getc(sc); + goto MBG_LOOP; + } + /* bad grouping mb char! */ + __scan_ungetc(sc); + if ((sc->cc >= 0) || (p > sc->thousands_sep + 1)) { +#ifdef __UCLIBC_HAS_FLOATS__ + /* We failed to match a thousep mb char, and + * we've read too much to recover. But if + * this is a floating point conversion and + * the initial portion of the decpt mb char + * matches, then we may still be able to + * recover. */ + int k = p - sc->thousands_sep - 1; + + if ((psfs->conv_num > CONV_i) /* float conversion */ + && (!pass || (i == nblk1)) /* possible last */ + && !memcmp(sc->thousands_sep, sc->fake_decpt, k) + /* and prefix matched, so could be decpt */ + ) { + __scan_getc(sc); + p = sc->fake_decpt + k; + do { + if (!*++p) { + strcpy(b, sc->decpt); + b += sc->decpt_len; + goto GOT_DECPT; + } + if (*p != sc->cc) { + __scan_ungetc(sc); + break; /* failed */ + } + __scan_getc(sc); + } while (1); + } +#endif /* __UCLIBC_HAS_FLOATS__ */ + goto DONE; + } + /* was EOF and 1st, so recoverable. */ + } + } + if ((i == nblk1) || ((i < nblk1) && !pass)) { + /* got an inner group */ + goto DONE_GROUPING_DO_UNGET; + } + goto DONE_DO_UNGET; /* Matching failure. */ + } /* i != 0 */ + + assert(pass); + + goto DONE_DO_UNGET; + } while (1); + + assert(0); /* Should never get here. */ + } + +#endif /***************** digit grouping **********************/ + + /* Not grouping so first trim all but one leading 0. */ +#ifdef __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__ + DO_TRIM_LEADING_ZEROS: +#endif /* __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__ */ + if (sc->cc == '0') { + seendigit = 1; + *b++ = '0'; /* Store the first 0. */ + do { /* But ignore all subsequent 0s. */ + __scan_getc(sc); + } while (sc->cc == '0'); + } + +#ifdef __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__ + DO_NO_GROUP: +#endif /* __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__ */ + /* At this point, we're ready to start reading digits. */ + +#define valid_digit(cc,base) (isxdigit(cc) && ((base == 16) || (cc - '0' < base))) + + while (valid_digit(sc->cc,base)) { /* Now for significant digits.*/ + if (b - buf < MAX_DIGITS) { + nonzero = seendigit = 1; /* Set nonzero too 0s trimmed above. */ + *b++ = sc->cc; +#ifdef __UCLIBC_HAS_FLOATS__ + } else { + ++exp_adjust; +#endif + } + __scan_getc(sc); + } + +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + DONE_GROUPING_DO_UNGET: +#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ + if (psfs->conv_num <= CONV_i) { /* integer conversion */ + __scan_ungetc(sc); + *b = 0; /* null-terminate */ + if (!seendigit) { + goto DONE; /* No digits! */ + } + if (psfs->store) { + if (*buf == '-') { + usflag = 0; + } + ++psfs->cnt; + _store_inttype(psfs->cur_ptr, psfs->dataargtype, + (uintmax_t) STRTOUIM(buf, NULL, base, 1-usflag)); + } + return 0; + } + +#ifdef __UCLIBC_HAS_FLOATS__ + + /* At this point, we have everything left of the decimal point or exponent. */ +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + NO_STARTING_DIGIT: +#endif + p = sc->fake_decpt; + do { + if (!*p) { + strcpy(b, sc->decpt); + b += sc->decpt_len; + break; + } + if (*p != sc->cc) { + if (p > sc->fake_decpt) { + goto DONE_DO_UNGET; /* matching failure (read some of decpt) */ + } + goto DO_DIGIT_CHECK; + } + ++p; + __scan_getc(sc); + } while (1); + +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + GOT_DECPT: +#endif + if (!nonzero) { + if (sc->cc == '0') { + assert(exp_adjust == 0); + *b++ = '0'; + ++exp_adjust; + seendigit = 1; + do { + --exp_adjust; + __scan_getc(sc); + } while (sc->cc == '0'); + } + } + + while (valid_digit(sc->cc,base)) { /* Process fractional digits.*/ + if (b - buf < MAX_DIGITS) { + seendigit = 1; + *b++ = sc->cc; + } + __scan_getc(sc); + } + + DO_DIGIT_CHECK: + /* Hmm... no decimal point. */ + if (!seendigit) { + static const unsigned char nan_inf_str[] = "an\0nfinity"; + + if (base == 16) { /* We had a prefix, but no digits! */ + goto DONE_DO_UNGET; /* matching failure */ + } + + /* Avoid tolower problems for INFINITY in the tr_TR locale. (yuk)*/ +#undef TOLOWER +#define TOLOWER(C) ((C)|0x20) + + switch (TOLOWER(sc->cc)) { + case 'i': + p = nan_inf_str + 3; + break; + case 'n': + p = nan_inf_str; + break; + default: + /* No digits and not inf or nan. */ + goto DONE_DO_UNGET; + } + + *b++ = sc->cc; + + do { + __scan_getc(sc); + if (TOLOWER(sc->cc) == *p) { + *b++ = sc->cc; + ++p; + continue; + } + if (!*p || (p == nan_inf_str + 5)) { /* match nan/infinity or inf */ + goto GOT_FLOAT; + } + /* Unrecoverable. Even if on 1st char, we had no digits. */ + goto DONE_DO_UNGET; + } while (1); + } + + /* If we get here, we had some digits. */ + + if ( +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + ((base == 16) && (((sc->cc)|0x20) == 'p')) || +#endif + (((sc->cc)|0x20) == 'e') + ) { /* Process an exponent. */ + *b++ = sc->cc; + + __scan_getc(sc); + if (sc->cc < 0) { + goto DONE_DO_UNGET; /* matching failure.. no exponent digits */ + } + + if ((sc->cc == '+') || (sc->cc == '-')) { /* Signed exponent? */ + *b++ = sc->cc; + __scan_getc(sc); + } + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Fix MAX_EXP_DIGITS! +#endif +#define MAX_EXP_DIGITS 20 + assert(seendigit); + seendigit = 0; + nonzero = 0; + + if (sc->cc == '0') { + seendigit = 1; + *b++ = '0'; + do { + __scan_getc(sc); + } while (sc->cc == '0'); + } + + while (__isdigit_char_or_EOF(sc->cc)) { /* Exponent digits (base 10).*/ + if (seendigit < MAX_EXP_DIGITS) { + ++seendigit; + *b++ = sc->cc; + } + __scan_getc(sc); + } + + if (!seendigit) { /* No digits. Unrecoverable. */ + goto DONE_DO_UNGET; + } + } + + + GOT_FLOAT: + *b = 0; + { + __fpmax_t x; + char *e; + x = __strtofpmax(buf, &e, exp_adjust); + assert(!*e); + if (psfs->store) { + if (psfs->dataargtype & PA_FLAG_LONG_LONG) { + *((long double *)psfs->cur_ptr) = (long double) x; + } else if (psfs->dataargtype & PA_FLAG_LONG) { + *((double *)psfs->cur_ptr) = (double) x; + } else { + *((float *)psfs->cur_ptr) = (float) x; + } + ++psfs->cnt; + } + __scan_ungetc(sc); + return 0; + } +#endif /* __UCLIBC_HAS_FLOATS__ */ + + DONE_DO_UNGET: + __scan_ungetc(sc); + DONE: + return -2; /* Matching failure. */ + +} +#endif +/**********************************************************************/ diff --git a/libc/stdio/_vfprintf.c b/libc/stdio/_vfprintf.c new file mode 100644 index 000000000..a32960f2e --- /dev/null +++ b/libc/stdio/_vfprintf.c @@ -0,0 +1,1951 @@ +/* Copyright (C) 2002-2004 Manuel Novoa III + * My stdio library for linux and (soon) elks. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* This code needs a lot of clean up. Some of that is on hold until uClibc + * gets a better configuration system (on Erik's todo list). + * The other cleanup will take place during the implementation/integration of + * the wide char (un)formatted i/o functions which I'm currently working on. + */ + +/* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! + * + * This code is currently under development. Also, I plan to port + * it to elks which is a 16-bit environment with a fairly limited + * compiler. Therefore, please refrain from modifying this code + * and, instead, pass any bug-fixes, etc. to me. Thanks. Manuel + * + * ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! */ + + +/* April 1, 2002 + * Initialize thread locks for fake files in vsnprintf and vdprintf. + * reported by Erik Andersen (andersen@codepoet.com) + * Fix an arg promotion handling bug in _do_one_spec for %c. + * reported by Ilguiz Latypov + * + * May 10, 2002 + * Remove __isdigit and use new ctype.h version. + * Add conditional setting of QUAL_CHARS for size_t and ptrdiff_t. + * + * Aug 16, 2002 + * Fix two problems that showed up with the python 2.2.1 tests; one + * involving %o and one involving %f. + * + * Oct 28, 2002 + * Fix a problem in vasprintf (reported by vodz a while back) when built + * without custom stream support. In that case, it is necessary to do + * a va_copy. + * Make sure each va_copy has a matching va_end, as required by C99. + * + * Nov 4, 2002 + * Add locale-specific grouping support for integer decimal conversion. + * Add locale-specific decimal point support for floating point conversion. + * Note: grouping will have to wait for _dtostr() rewrite. + * Add printf wchar support for %lc (%C) and %ls (%S). + * Require printf format strings to be valid multibyte strings beginning and + * ending in their initial shift state, as per the stds. + * + * Nov 21, 2002 + * Add *wprintf functions. Currently they don't support floating point + * conversions. That will wait until the rewrite of _dtostr. + * + * Aug 1, 2003 + * Optional hexadecimal float notation support for %a/%A. + * Floating point output now works for *wprintf. + * Support for glibc locale-specific digit grouping for floats. + * Misc bug fixes. + * + * Aug 31, 2003 + * Fix precision bug for %g conversion specifier when using %f style. + * + * Sep 5, 2003 + * Implement *s*scanf for the non-buffered stdio case with old_vfprintf. + * + * Sep 23, 2003 + * vfprintf was not always checking for narrow stream orientation. + */ + +/* TODO: + * + * Should we validate that *printf format strings are valid multibyte + * strings in the current locale? ANSI/ISO C99 seems to imply this + * and Plauger's printf implementation in his Standard C Library book + * treats this as an error. + */ + +#define _ISOC99_SOURCE /* for ULLONG primarily... */ +#include +#include "_stdio.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __PRINTF_INFO_NO_BITFIELD +#include + +#ifdef __UCLIBC_HAS_THREADS__ +#include +#include +#endif /* __UCLIBC_HAS_THREADS__ */ + +#ifdef __UCLIBC_HAS_WCHAR__ +#include +#endif /* __UCLIBC_HAS_WCHAR__ */ + +#include +#include + +libc_hidden_proto(memcpy) +libc_hidden_proto(memset) +libc_hidden_proto(strlen) +libc_hidden_proto(strnlen) +libc_hidden_proto(__glibc_strerror_r) +libc_hidden_proto(fputs_unlocked) +libc_hidden_proto(abort) +#ifdef __UCLIBC_HAS_WCHAR__ +libc_hidden_proto(wcslen) +libc_hidden_proto(wcsnlen) +libc_hidden_proto(mbsrtowcs) +libc_hidden_proto(wcsrtombs) +libc_hidden_proto(btowc) +libc_hidden_proto(wcrtomb) +libc_hidden_proto(fputws) +#endif + +/* Some older or broken gcc toolchains define LONG_LONG_MAX but not + * LLONG_MAX. Since LLONG_MAX is part of the standard, that's what + * we use. So complain if we do not have it but should. + */ +#if !defined(LLONG_MAX) && defined(LONG_LONG_MAX) +#error Apparently, LONG_LONG_MAX is defined but LLONG_MAX is not. You need to fix your toolchain headers to support the standard macros for (unsigned) long long. +#endif + +/**********************************************************************/ +/* These provide some control over printf's feature set */ + +/* This is undefined below depeding on uClibc's configuration. */ +#define __STDIO_PRINTF_FLOAT 1 + +/* Now controlled by uClibc_stdio.h. */ +/* #define __UCLIBC_HAS_PRINTF_M_SPEC__ */ + + +/**********************************************************************/ + +#if defined(__UCLIBC__) && !defined(__UCLIBC_HAS_FLOATS__) +#undef __STDIO_PRINTF_FLOAT +#endif + +#ifdef __BCC__ +#undef __STDIO_PRINTF_FLOAT +#endif + +#ifdef __STDIO_PRINTF_FLOAT +#include +#include +#else /* __STDIO_PRINTF_FLOAT */ +#undef L__fpmaxtostr +#endif /* __STDIO_PRINTF_FLOAT */ + + +#undef __STDIO_HAS_VSNPRINTF +#if defined(__STDIO_BUFFERS) || defined(__USE_OLD_VFPRINTF__) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) +#define __STDIO_HAS_VSNPRINTF 1 +#endif + +/**********************************************************************/ + +/* Now controlled by uClibc_stdio.h. */ +/* #define __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + +/* TODO -- move these to a configuration section? */ +#define MAX_FIELD_WIDTH 4095 + +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_register_printf_function +/* emit only once */ +#warning WISHLIST: Make MAX_USER_SPEC configurable? +#warning WISHLIST: Make MAX_ARGS_PER_SPEC configurable? +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ + +#define MAX_USER_SPEC 10 +#define MAX_ARGS_PER_SPEC 5 + +#else /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + +#undef MAX_USER_SPEC +#define MAX_ARGS_PER_SPEC 1 + +#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + +#if MAX_ARGS_PER_SPEC < 1 +#error MAX_ARGS_PER_SPEC < 1! +#undef MAX_ARGS_PER_SPEC +#define MAX_ARGS_PER_SPEC 1 +#endif + +#if defined(NL_ARGMAX) && (NL_ARGMAX < 9) +#error NL_ARGMAX < 9! +#endif + +#if defined(NL_ARGMAX) && (NL_ARGMAX >= (MAX_ARGS_PER_SPEC + 2)) +#define MAX_ARGS NL_ARGMAX +#else +/* N for spec itself, plus 1 each for width and precision */ +#define MAX_ARGS (MAX_ARGS_PER_SPEC + 2) +#endif + +/**********************************************************************/ + +#define __PA_FLAG_INTMASK \ + (__PA_FLAG_CHAR|PA_FLAG_SHORT|__PA_FLAG_INT|PA_FLAG_LONG|PA_FLAG_LONG_LONG) + +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ +extern printf_function _custom_printf_handler[MAX_USER_SPEC] attribute_hidden; +extern printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC] attribute_hidden; +extern char *_custom_printf_spec attribute_hidden; +#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + +/**********************************************************************/ + +#define SPEC_FLAGS " +0-#'I" +enum { + FLAG_SPACE = 0x01, + FLAG_PLUS = 0x02, /* must be 2 * FLAG_SPACE */ + FLAG_ZERO = 0x04, + FLAG_MINUS = 0x08, /* must be 2 * FLAG_ZERO */ + FLAG_HASH = 0x10, + FLAG_THOUSANDS = 0x20, + FLAG_I18N = 0x40, /* only works for d, i, u */ + FLAG_WIDESTREAM = 0x80 +}; + +/**********************************************************************/ + +/* float layout 01234567890123456789 TODO: B?*/ +#define SPEC_CHARS "npxXoudifFeEgGaACScs" +enum { + CONV_n = 0, + CONV_p, + CONV_x, CONV_X, CONV_o, CONV_u, CONV_d, CONV_i, + CONV_f, CONV_F, CONV_e, CONV_E, CONV_g, CONV_G, CONV_a, CONV_A, + CONV_C, CONV_S, CONV_c, CONV_s, +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ + CONV_m, +#endif + CONV_custom0 /* must be last */ +}; + +/* p x X o u d i */ +#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 10 } + +#define SPEC_RANGES { CONV_n, CONV_p, CONV_i, CONV_A, \ + CONV_C, CONV_S, CONV_c, CONV_s, CONV_custom0 } + +#define SPEC_OR_MASK { \ + /* n */ (PA_FLAG_PTR|PA_INT), \ + /* p */ PA_POINTER, \ + /* oxXudi */ PA_INT, \ + /* fFeEgGaA */ PA_DOUBLE, \ + /* C */ PA_WCHAR, \ + /* S */ PA_WSTRING, \ + /* c */ PA_CHAR, \ + /* s */ PA_STRING, \ +} + +#define SPEC_AND_MASK { \ + /* n */ (PA_FLAG_PTR|__PA_INTMASK), \ + /* p */ PA_POINTER, \ + /* oxXudi */ (__PA_INTMASK), \ + /* fFeEgGaA */ (PA_FLAG_LONG_DOUBLE|PA_DOUBLE), \ + /* C */ (PA_WCHAR), \ + /* S */ (PA_WSTRING), \ + /* c */ (PA_CHAR), \ + /* s */ (PA_STRING), \ +} + +/**********************************************************************/ +/* + * In order to ease translation to what arginfo and _print_info._flags expect, + * we map: 0:int 1:char 2:longlong 4:long 8:short + * and then _flags |= (((q << 7) + q) & 0x701) and argtype |= (_flags & 0x701) + */ + +/* TODO -- Fix the table below to take into account stdint.h. */ +/* #ifndef LLONG_MAX */ +/* #error fix QUAL_CHARS for no long long! Affects 'L', 'j', 'q', 'll'. */ +/* #else */ +/* #if LLONG_MAX != INTMAX_MAX */ +/* #error fix QUAL_CHARS intmax_t entry 'j'! */ +/* #endif */ +/* #endif */ + +#ifdef PDS +#error PDS already defined! +#endif +#ifdef SS +#error SS already defined! +#endif +#ifdef IMS +#error IMS already defined! +#endif + +#if PTRDIFF_MAX == INT_MAX +#define PDS 0 +#elif PTRDIFF_MAX == LONG_MAX +#define PDS 4 +#elif defined(LLONG_MAX) && (PTRDIFF_MAX == LLONG_MAX) +#define PDS 8 +#else +#error fix QUAL_CHARS ptrdiff_t entry 't'! +#endif + +#if SIZE_MAX == UINT_MAX +#define SS 0 +#elif SIZE_MAX == ULONG_MAX +#define SS 4 +#elif defined(LLONG_MAX) && (SIZE_MAX == ULLONG_MAX) +#define SS 8 +#else +#error fix QUAL_CHARS size_t entries 'z', 'Z'! +#endif + +#if INTMAX_MAX == INT_MAX +#define IMS 0 +#elif INTMAX_MAX == LONG_MAX +#define IMS 4 +#elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX) +#define IMS 8 +#else +#error fix QUAL_CHARS intmax_t entry 'j'! +#endif + +#define QUAL_CHARS { \ + /* j:(u)intmax_t z:(s)size_t t:ptrdiff_t \0:int */ \ + /* q:long_long Z:(s)size_t */ \ + 'h', 'l', 'L', 'j', 'z', 't', 'q', 'Z', 0, \ + 2, 4, 8, IMS, SS, PDS, 8, SS, 0, /* TODO -- fix!!! */\ + 1, 8 \ +} + +/**********************************************************************/ + +#ifdef __STDIO_VA_ARG_PTR +#ifdef __BCC__ +#define __va_arg_ptr(ap,type) (((type *)(ap += sizeof(type))) - 1) +#endif + +#if 1 +#ifdef __GNUC__ +/* TODO -- need other than for 386 as well! */ + +#ifndef __va_rounded_size +#define __va_rounded_size(TYPE) \ + (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int)) +#endif +#define __va_arg_ptr(AP, TYPE) \ + (AP = (va_list) ((char *) (AP) + __va_rounded_size (TYPE)), \ + ((void *) ((char *) (AP) - __va_rounded_size (TYPE)))) +#endif +#endif +#endif /* __STDIO_VA_ARG_PTR */ + +#ifdef __va_arg_ptr +#define GET_VA_ARG(AP,F,TYPE,ARGS) (*(AP) = __va_arg_ptr(ARGS,TYPE)) +#define GET_ARG_VALUE(AP,F,TYPE) (*((TYPE *)(*(AP)))) +#else +typedef union { + wchar_t wc; + unsigned int u; + unsigned long ul; +#ifdef ULLONG_MAX + unsigned long long ull; +#endif +#ifdef __STDIO_PRINTF_FLOAT + double d; + long double ld; +#endif /* __STDIO_PRINTF_FLOAT */ + void *p; +} argvalue_t; + +#define GET_VA_ARG(AU,F,TYPE,ARGS) (AU->F = va_arg(ARGS,TYPE)) +#define GET_ARG_VALUE(AU,F,TYPE) ((TYPE)((AU)->F)) +#endif + +typedef struct { + const char *fmtpos; /* TODO: move below struct?? */ + struct printf_info info; +#ifdef NL_ARGMAX + int maxposarg; /* > 0 if args are positional, 0 if not, -1 if unknown */ +#endif /* NL_ARGMAX */ + int num_data_args; /* TODO: use sentinal??? */ + unsigned int conv_num; + unsigned char argnumber[4]; /* width | prec | 1st data | unused */ + int argtype[MAX_ARGS]; + va_list arg; +#ifdef __va_arg_ptr + void *argptr[MAX_ARGS]; +#else +/* if defined(NL_ARGMAX) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__) */ + /* While this is wasteful of space in the case where pos args aren't + * enabled, it is also needed to support custom printf handlers. */ + argvalue_t argvalue[MAX_ARGS]; +#endif +} ppfs_t; /* parse printf format state */ + +/**********************************************************************/ + +/* TODO: fix printf to return 0 and set errno if format error. Standard says + only returns -1 if sets error indicator for the stream. */ + +#ifdef __STDIO_PRINTF_FLOAT +typedef size_t (__fp_outfunc_t)(FILE *fp, intptr_t type, intptr_t len, + intptr_t buf); + +extern ssize_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, + __fp_outfunc_t fp_outfunc) attribute_hidden; +#endif + +extern int _ppfs_init(ppfs_t *ppfs, const char *fmt0) attribute_hidden; /* validates */ +extern void _ppfs_prepargs(ppfs_t *ppfs, va_list arg) attribute_hidden; /* sets posargptrs */ +extern void _ppfs_setargs(ppfs_t *ppfs) attribute_hidden; /* sets argptrs for current spec */ +extern int _ppfs_parsespec(ppfs_t *ppfs) attribute_hidden; /* parses specifier */ + +extern void _store_inttype(void *dest, int desttype, uintmax_t val) attribute_hidden; +extern uintmax_t _load_inttype(int desttype, const void *src, int uflag) attribute_hidden; + +/**********************************************************************/ +#ifdef L_parse_printf_format + +/* NOTE: This function differs from the glibc version in that parsing stops + * upon encountering an invalid conversion specifier. Since this is the way + * my printf functions work, I think it makes sense to do it that way here. + * Unfortunately, since glibc sets the return type as size_t, we have no way + * of returning that the template is illegal, other than returning 0. + */ + +size_t parse_printf_format(register const char *template, + size_t n, register int *argtypes) +{ + ppfs_t ppfs; + size_t i; + size_t count = 0; + + if (_ppfs_init(&ppfs, template) >= 0) { +#ifdef NL_ARGMAX + if (ppfs.maxposarg > 0) { /* Using positional args. */ + count = ppfs.maxposarg; + if (n > count) { + n = count; + } + for (i = 0 ; i < n ; i++) { + *argtypes++ = ppfs.argtype[i]; + } + } else { /* Not using positional args. */ +#endif /* NL_ARGMAX */ + while (*template) { + if ((*template == '%') && (*++template != '%')) { + ppfs.fmtpos = template; + _ppfs_parsespec(&ppfs); /* Can't fail. */ + template = ppfs.fmtpos; /* Update to one past spec end. */ + if (ppfs.info.width == INT_MIN) { + ++count; + if (n > 0) { + *argtypes++ = PA_INT; + --n; + } + } + if (ppfs.info.prec == INT_MIN) { + ++count; + if (n > 0) { + *argtypes++ = PA_INT; + --n; + } + } + for (i = 0 ; i < ppfs.num_data_args ; i++) { + if ((ppfs.argtype[i]) != __PA_NOARG) { + ++count; + if (n > 0) { + *argtypes++ = ppfs.argtype[i]; + --n; + } + } + } + } else { + ++template; + } + } +#ifdef NL_ARGMAX + } +#endif /* NL_ARGMAX */ + } + + return count; +} + +#endif +/**********************************************************************/ +#ifdef L__ppfs_init + +#ifdef __UCLIBC_HAS_LOCALE__ +libc_hidden_proto(__global_locale) +#endif + +int attribute_hidden _ppfs_init(register ppfs_t *ppfs, const char *fmt0) +{ + int r; + + /* First, zero out everything... argnumber[], argtype[], argptr[] */ + memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */ +#ifdef NL_ARGMAX + --ppfs->maxposarg; /* set to -1 */ +#endif /* NL_ARGMAX */ + ppfs->fmtpos = fmt0; +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Make checking of the format string in C locale an option. +#endif +#ifdef __UCLIBC_HAS_LOCALE__ + /* To support old programs, don't check mb validity if in C locale. */ + if (((__UCLIBC_CURLOCALE_DATA).encoding) != __ctype_encoding_7_bit) { + /* ANSI/ISO C99 requires format string to be a valid multibyte string + * beginning and ending in its initial shift state. */ + static const char invalid_mbs[] = "Invalid multibyte format string."; + mbstate_t mbstate; + const char *p; + mbstate.__mask = 0; /* Initialize the mbstate. */ + p = fmt0; + if (mbsrtowcs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) { + ppfs->fmtpos = invalid_mbs; + return -1; + } + } +#endif /* __UCLIBC_HAS_LOCALE__ */ + /* now set all argtypes to no-arg */ + { +#if 1 + /* TODO - use memset here since already "paid for"? */ + register int *p = ppfs->argtype; + + r = MAX_ARGS; + do { + *p++ = __PA_NOARG; + } while (--r); +#else + /* TODO -- get rid of this?? */ + register char *p = (char *) ((MAX_ARGS-1) * sizeof(int)); + + do { + *((int *)(((char *)ppfs) + ((int)p) + offsetof(ppfs_t,argtype))) = __PA_NOARG; + p -= sizeof(int); + } while (p); +#endif + } + + /* + * Run through the entire format string to validate it and initialize + * the positional arg numbers (if any). + */ + { + register const char *fmt = fmt0; + + while (*fmt) { + if ((*fmt == '%') && (*++fmt != '%')) { + ppfs->fmtpos = fmt; /* back up to the '%' */ + if ((r = _ppfs_parsespec(ppfs)) < 0) { + return -1; + } + fmt = ppfs->fmtpos; /* update to one past end of spec */ + } else { + ++fmt; + } + } + ppfs->fmtpos = fmt0; /* rewind */ + } + +#ifdef NL_MAX_ARG + /* If we have positional args, make sure we know all the types. */ + { + register int *p = ppfs->argtype; + r = ppfs->maxposarg; + while (--r >= 0) { + if ( *p == __PA_NOARG ) { /* missing arg type!!! */ + return -1; + } + ++p; + } + } +#endif /* NL_MAX_ARG */ + + return 0; +} +#endif +/**********************************************************************/ +#ifdef L__ppfs_prepargs +void attribute_hidden _ppfs_prepargs(register ppfs_t *ppfs, va_list arg) +{ + int i; + + va_copy(ppfs->arg, arg); + +#ifdef NL_ARGMAX + if ((i = ppfs->maxposarg) > 0) { /* init for positional args */ + ppfs->num_data_args = i; + ppfs->info.width = ppfs->info.prec = ppfs->maxposarg = 0; + _ppfs_setargs(ppfs); + ppfs->maxposarg = i; + } +#endif /* NL_ARGMAX */ +} +#endif +/**********************************************************************/ +#ifdef L__ppfs_setargs + +void attribute_hidden _ppfs_setargs(register ppfs_t *ppfs) +{ +#ifdef __va_arg_ptr + register void **p = ppfs->argptr; +#else + register argvalue_t *p = ppfs->argvalue; +#endif + int i; + +#ifdef NL_ARGMAX + if (ppfs->maxposarg == 0) { /* initing for or no pos args */ +#endif /* NL_ARGMAX */ + if (ppfs->info.width == INT_MIN) { + ppfs->info.width = +#ifdef __va_arg_ptr + *(int *) +#endif + GET_VA_ARG(p,u,unsigned int,ppfs->arg); + } + if (ppfs->info.prec == INT_MIN) { + ppfs->info.prec = +#ifdef __va_arg_ptr + *(int *) +#endif + GET_VA_ARG(p,u,unsigned int,ppfs->arg); + } + i = 0; + while (i < ppfs->num_data_args) { + switch(ppfs->argtype[i++]) { + case (PA_INT|PA_FLAG_LONG_LONG): +#ifdef ULLONG_MAX + GET_VA_ARG(p,ull,unsigned long long,ppfs->arg); + break; +#endif + case (PA_INT|PA_FLAG_LONG): +#if ULONG_MAX != UINT_MAX + GET_VA_ARG(p,ul,unsigned long,ppfs->arg); + break; +#endif + case PA_CHAR: /* TODO - be careful */ + /* ... users could use above and really want below!! */ + case (PA_INT|__PA_FLAG_CHAR):/* TODO -- translate this!!! */ + case (PA_INT|PA_FLAG_SHORT): + case PA_INT: + GET_VA_ARG(p,u,unsigned int,ppfs->arg); + break; + case PA_WCHAR: /* TODO -- assume int? */ + /* we're assuming wchar_t is at least an int */ + GET_VA_ARG(p,wc,wchar_t,ppfs->arg); + break; +#ifdef __STDIO_PRINTF_FLOAT + /* PA_FLOAT */ + case PA_DOUBLE: + GET_VA_ARG(p,d,double,ppfs->arg); + break; + case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE): + GET_VA_ARG(p,ld,long double,ppfs->arg); + break; +#else /* __STDIO_PRINTF_FLOAT */ + case PA_DOUBLE: + case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE): + assert(0); + continue; +#endif /* __STDIO_PRINTF_FLOAT */ + default: + /* TODO -- really need to ensure this can't happen */ + assert(ppfs->argtype[i-1] & PA_FLAG_PTR); + case PA_POINTER: + case PA_STRING: + case PA_WSTRING: + GET_VA_ARG(p,p,void *,ppfs->arg); + break; + case __PA_NOARG: + continue; + } + ++p; + } +#ifdef NL_ARGMAX + } else { + if (ppfs->info.width == INT_MIN) { + ppfs->info.width + = (int) GET_ARG_VALUE(p + ppfs->argnumber[0] - 1,u,unsigned int); + } + if (ppfs->info.prec == INT_MIN) { + ppfs->info.prec + = (int) GET_ARG_VALUE(p + ppfs->argnumber[1] - 1,u,unsigned int); + } + } +#endif /* NL_ARGMAX */ + + /* Now we know the width and precision. */ + if (ppfs->info.width < 0) { + ppfs->info.width = -ppfs->info.width; + PRINT_INFO_SET_FLAG(&(ppfs->info),left); + PRINT_INFO_CLR_FLAG(&(ppfs->info),space); + ppfs->info.pad = ' '; + } +#if 0 + /* NOTE -- keep neg for now so float knows! */ + if (ppfs->info.prec < 0) { /* spec says treat as omitted. */ + /* so use default prec... 1 for everything but floats and strings. */ + ppfs->info.prec = 1; + } +#endif +} +#endif +/**********************************************************************/ +#ifdef L__ppfs_parsespec + +#ifdef __UCLIBC_HAS_XLOCALE__ +libc_hidden_proto(__ctype_b_loc) +#else +libc_hidden_proto(__ctype_b) +#endif + +/* Notes: argtype differs from glibc for the following: + * mine glibc + * lc PA_WCHAR PA_CHAR the standard says %lc means %C + * ls PA_WSTRING PA_STRING the standard says %ls means %S + * {*}n {*}|PA_FLAG_PTR PA_FLAG_PTR size of n can be qualified + */ + +/* TODO: store positions of positional args */ + +/* TODO -- WARNING -- assumes aligned on integer boundaries!!! */ + +/* TODO -- disable if not using positional args!!! */ +#define _OVERLAPPING_DIFFERENT_ARGS + +/* TODO -- rethink this -- perhaps we should set to largest type??? */ + +#ifdef _OVERLAPPING_DIFFERENT_ARGS + +#define PROMOTED_SIZE_OF(X) ((sizeof(X) + sizeof(int) - 1) / sizeof(X)) + +static const short int type_codes[] = { + __PA_NOARG, /* must be first entry */ + PA_POINTER, + PA_STRING, + PA_WSTRING, + PA_CHAR, + PA_INT|PA_FLAG_SHORT, + PA_INT, + PA_INT|PA_FLAG_LONG, + PA_INT|PA_FLAG_LONG_LONG, + PA_WCHAR, +#ifdef __STDIO_PRINTF_FLOAT + /* PA_FLOAT, */ + PA_DOUBLE, + PA_DOUBLE|PA_FLAG_LONG_DOUBLE, +#endif /* __STDIO_PRINTF_FLOAT */ +}; + +static const unsigned char type_sizes[] = { + /* unknown type consumes no arg */ + 0, /* must be first entry */ + PROMOTED_SIZE_OF(void *), + PROMOTED_SIZE_OF(char *), + PROMOTED_SIZE_OF(wchar_t *), + PROMOTED_SIZE_OF(char), + PROMOTED_SIZE_OF(short), + PROMOTED_SIZE_OF(int), + PROMOTED_SIZE_OF(long), +#ifdef ULLONG_MAX + PROMOTED_SIZE_OF(long long), +#else + PROMOTED_SIZE_OF(long), /* TODO -- is this correct? (above too) */ +#endif + PROMOTED_SIZE_OF(wchar_t), +#ifdef __STDIO_PRINTF_FLOAT + /* PROMOTED_SIZE_OF(float), */ + PROMOTED_SIZE_OF(double), + PROMOTED_SIZE_OF(long double), +#endif /* __STDIO_PRINTF_FLOAT */ +}; + +static int _promoted_size(int argtype) +{ + register const short int *p; + + /* note -- since any unrecognized type is treated as a pointer */ + p = type_codes + sizeof(type_codes)/sizeof(type_codes[0]); + do { + if (*--p == argtype) { + break; + } + } while (p > type_codes); + + return type_sizes[(int)(p - type_codes)]; +} + +static int _is_equal_or_bigger_arg(int curtype, int newtype) +{ + /* Quick test */ + if (newtype == __PA_NOARG) { + return 0; + } + if ((curtype == __PA_NOARG) || (curtype == newtype)) { + return 1; + } + /* Ok... slot is already filled and types are different in name. */ + /* So, compare promoted sizes of curtype and newtype args. */ + return _promoted_size(curtype) <= _promoted_size(newtype); +} + +#else + +#define _is_equal_or_bigger_arg(C,N) (((C) == __PA_NOARG) || ((C) == (N))) + +#endif + +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ +/* TODO - do this differently? */ +static char _bss_custom_printf_spec[MAX_USER_SPEC]; /* 0-init'd for us. */ + +attribute_hidden char *_custom_printf_spec = _bss_custom_printf_spec; +attribute_hidden printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC]; +attribute_hidden printf_function _custom_printf_handler[MAX_USER_SPEC]; +#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + +int attribute_hidden _ppfs_parsespec(ppfs_t *ppfs) +{ + register const char *fmt; + register const char *p; + int preci; + int width; + int flags; + int dataargtype; + int i; + int dpoint; +#ifdef NL_ARGMAX + int maxposarg; +#endif /* NL_ARGMAX */ + int p_m_spec_chars; + int n; + int argtype[MAX_ARGS_PER_SPEC+2]; + int argnumber[3]; /* width, precision, 1st data arg */ + static const char spec_flags[] = SPEC_FLAGS; + static const char spec_chars[] = SPEC_CHARS;/* TODO: b? */ + static const char spec_ranges[] = SPEC_RANGES; + static const short spec_or_mask[] = SPEC_OR_MASK; + static const short spec_and_mask[] = SPEC_AND_MASK; + static const char qual_chars[] = QUAL_CHARS; +#ifdef __UCLIBC_HAS_WCHAR__ + char buf[32]; +#endif /* __UCLIBC_HAS_WCHAR__ */ + + /* WIDE note: we can test against '%' here since we don't allow */ + /* WIDE note: other mappings of '%' in the wide char set. */ + preci = -1; + argnumber[0] = 0; + argnumber[1] = 0; + argtype[0] = __PA_NOARG; + argtype[1] = __PA_NOARG; +#ifdef NL_ARGMAX + maxposarg = ppfs->maxposarg; +#endif /* NL_ARGMAX */ + +#ifdef __UCLIBC_HAS_WCHAR__ + /* This is somewhat lame, but saves a lot of code. If we're dealing with + * a wide stream, that means the format is a wchar string. So, copy it + * char-by-char into a normal char buffer for processing. Make the buffer + * (buf) big enough so that any reasonable format specifier will fit. + * While there a legal specifiers that won't, the all involve duplicate + * flags or outrageous field widths/precisions. */ + width = dpoint = 0; + if ((flags = ppfs->info._flags & FLAG_WIDESTREAM) == 0) { + fmt = ppfs->fmtpos; + } else { + fmt = buf + 1; + i = 0; + do { + if ((buf[i] = (char) (((wchar_t *) ppfs->fmtpos)[i-1])) + != (((wchar_t *) ppfs->fmtpos)[i-1]) + ) { + return -1; + } + } while (buf[i++]); + buf[sizeof(buf)-1] = 0; + } +#else /* __UCLIBC_HAS_WCHAR__ */ + width = flags = dpoint = 0; + fmt = ppfs->fmtpos; +#endif /* __UCLIBC_HAS_WCHAR__ */ + + assert(fmt[-1] == '%'); + assert(fmt[0] != '%'); + + /* Process arg pos and/or flags and/or width and/or precision. */ + width_precision: + p = fmt; + if (*fmt == '*') { + argtype[-dpoint] = PA_INT; + ++fmt; + } + i = 0; + while (isdigit(*fmt)) { + if (i < MAX_FIELD_WIDTH) { /* Avoid overflow. */ + i = (i * 10) + (*fmt - '0'); + } + ++fmt; + } + if (p[-1] == '%') { /* Check for a position. */ + + /* TODO: if val not in range, then error */ + +#ifdef NL_ARGMAX + if ((*fmt == '$') && (i > 0)) {/* Positional spec. */ + ++fmt; + if (maxposarg == 0) { + return -1; + } + if ((argnumber[2] = i) > maxposarg) { + maxposarg = i; + } + /* Now fall through to check flags. */ + } else { + if (maxposarg > 0) { +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Support prec and width for %m when positional args used + /* Actually, positional arg processing will fail in general + * for specifiers that don't require an arg. */ +#endif /* __UCLIBC_MJN3_ONLY__ */ + if (*fmt == 'm') { + goto PREC_WIDTH; + } +#endif /* __UCLIBC_HAS_PRINTF_M_SPEC__ */ + return -1; + } + maxposarg = 0; /* Possible redundant store, but cuts size. */ + + if ((fmt > p) && (*p != '0')) { + goto PREC_WIDTH; + } + + fmt = p; /* Back up for possible '0's flag. */ + /* Now fall through to check flags. */ + } +#else /* NL_ARGMAX */ + if (*fmt == '$') { /* Positional spec. */ + return -1; + } + + if ((fmt > p) && (*p != '0')) { + goto PREC_WIDTH; + } + + fmt = p; /* Back up for possible '0's flag. */ + /* Now fall through to check flags. */ +#endif /* NL_ARGMAX */ + + restart_flags: /* Process flags. */ + i = 1; + p = spec_flags; + + do { + if (*fmt == *p++) { + ++fmt; + flags |= i; + goto restart_flags; + } + i += i; /* Better than i <<= 1 for bcc */ + } while (*p); + i = 0; + + /* If '+' then ignore ' ', and if '-' then ignore '0'. */ + /* Note: Need to ignore '0' when prec is an arg with val < 0, */ + /* but that test needs to wait until the arg is retrieved. */ + flags &= ~((flags & (FLAG_PLUS|FLAG_MINUS)) >> 1); + /* Note: Ignore '0' when prec is specified < 0 too (in printf). */ + + if (fmt[-1] != '%') { /* If we've done anything, loop for width. */ + goto width_precision; + } + } + PREC_WIDTH: + if (*p == '*') { /* Prec or width takes an arg. */ +#ifdef NL_ARGMAX + if (maxposarg) { + if ((*fmt++ != '$') || (i <= 0)) { + /* Using pos args and no $ or invalid arg number. */ + return -1; + } + argnumber[-dpoint] = i; + } else +#endif /* NL_ARGMAX */ + if (++p != fmt) { + /* Not using pos args but digits followed *. */ + return -1; + } + i = INT_MIN; + } + + if (!dpoint) { + width = i; + if (*fmt == '.') { + ++fmt; + dpoint = -1; /* To use as default precison. */ + goto width_precision; + } + } else { + preci = i; + } + + /* Process qualifier. */ + p = qual_chars; + do { + if (*fmt == *p) { + ++fmt; + break; + } + } while (*++p); + if ((p - qual_chars < 2) && (*fmt == *p)) { + p += ((sizeof(qual_chars)-2) / 2); + ++fmt; + } + dataargtype = ((int)(p[(sizeof(qual_chars)-2) / 2])) << 8; + + /* Process conversion specifier. */ + if (!*fmt) { + return -1; + } + + p = spec_chars; + + do { + if (*fmt == *p) { + p_m_spec_chars = p - spec_chars; + + if ((p_m_spec_chars >= CONV_c) + && (dataargtype & PA_FLAG_LONG)) { + p_m_spec_chars -= 2; /* lc -> C and ls -> S */ + } + + ppfs->conv_num = p_m_spec_chars; + p = spec_ranges-1; + while (p_m_spec_chars > *++p) {} + + i = p - spec_ranges; + argtype[2] = (dataargtype | spec_or_mask[i]) & spec_and_mask[i]; + p = spec_chars; + break; + } + } while(*++p); + + ppfs->info.spec = *fmt; + ppfs->info.prec = preci; + ppfs->info.width = width; + ppfs->info.pad = ((flags & FLAG_ZERO) ? '0' : ' '); + ppfs->info._flags = (flags & ~FLAG_ZERO) | (dataargtype & __PA_INTMASK); + ppfs->num_data_args = 1; + + if (!*p) { +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ + if (*fmt == 'm') { + ppfs->conv_num = CONV_m; + ppfs->num_data_args = 0; + goto DONE; + } +#endif +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ + + /* Handle custom arg -- WARNING -- overwrites p!!! */ + ppfs->conv_num = CONV_custom0; + p = _custom_printf_spec; + do { + if (*p == *fmt) { + if ((ppfs->num_data_args + = ((*_custom_printf_arginfo[(int)(p-_custom_printf_spec)]) + (&(ppfs->info), MAX_ARGS_PER_SPEC, argtype+2))) + > MAX_ARGS_PER_SPEC) { + break; /* Error -- too many args! */ + } + goto DONE; + } + } while (++p < (_custom_printf_spec + MAX_USER_SPEC)); +#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + /* Otherwise error. */ + return -1; + } + +#if defined(__UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__) || defined(__UCLIBC_HAS_PRINTF_M_SPEC__) + DONE: +#endif + +#ifdef NL_ARGMAX + if (maxposarg > 0) { + i = 0; + do { + /* Update maxposarg and check that NL_ARGMAX is not exceeded. */ + n = ((i <= 2) + ? (ppfs->argnumber[i] = argnumber[i]) + : argnumber[2] + (i-2)); + if (n > maxposarg) { + if ((maxposarg = n) > NL_ARGMAX) { + return -1; + } + } + --n; + /* Record argtype with largest size (current, new). */ + if (_is_equal_or_bigger_arg(ppfs->argtype[n], argtype[i])) { + ppfs->argtype[n] = argtype[i]; + } + } while (++i < ppfs->num_data_args + 2); + } else { +#endif /* NL_ARGMAX */ + ppfs->argnumber[2] = 1; + memcpy(ppfs->argtype, argtype + 2, ppfs->num_data_args * sizeof(int)); +#ifdef NL_ARGMAX + } + + ppfs->maxposarg = maxposarg; +#endif /* NL_ARGMAX */ + +#ifdef __UCLIBC_HAS_WCHAR__ + if ((flags = ppfs->info._flags & FLAG_WIDESTREAM) == 0) { + ppfs->fmtpos = ++fmt; + } else { + ppfs->fmtpos = (const char *) (((const wchar_t *)(ppfs->fmtpos)) + + (fmt - buf) ); + } +#else /* __UCLIBC_HAS_WCHAR__ */ + ppfs->fmtpos = ++fmt; +#endif /* __UCLIBC_HAS_WCHAR__ */ + + return ppfs->num_data_args + 2; +} + +#endif +/**********************************************************************/ +#ifdef L_register_printf_function + +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ + +int register_printf_function(int spec, printf_function handler, + printf_arginfo_function arginfo) +{ + register char *r; + register char *p; + + if (spec && (arginfo != NULL)) { /* TODO -- check if spec is valid char */ + r = NULL; + p = _custom_printf_spec + MAX_USER_SPEC; + do { + --p; + if (!*p) { + r = p; + } +#ifdef __BCC__ + else /* bcc generates less code with fall-through */ +#endif + if (*p == spec) { + r = p; + p = _custom_printf_spec; + } + } while (p > _custom_printf_spec); + + if (r) { + if (handler) { + *r = spec; + _custom_printf_handler[(int)(r - p)] = handler; + _custom_printf_arginfo[(int)(r - p)] = arginfo; + } else { + *r = 0; + } + return 0; + } + /* TODO -- if asked to unregister a non-existent spec, return what? */ + } + return -1; +} + +#endif + +#endif +/**********************************************************************/ +#if defined(L_vfprintf) || defined(L_vfwprintf) + +/* We only support ascii digits (or their USC equivalent codes) in + * precision and width settings in *printf (wide) format strings. + * In other words, we don't currently support glibc's 'I' flag. + * We do accept it, but it is currently ignored. */ + +static size_t _charpad(FILE * __restrict stream, int padchar, size_t numpad); + +#ifdef L_vfprintf + +#define VFPRINTF vfprintf +#define FMT_TYPE char +#define OUTNSTR _outnstr +#define STRLEN strlen +#define _PPFS_init _ppfs_init +#define OUTPUT(F,S) fputs_unlocked(S,F) +/* #define _outnstr(stream, string, len) __stdio_fwrite(string, len, stream) */ +#define _outnstr(stream, string, len) ((len > 0) ? __stdio_fwrite(string, len, stream) : 0) +#define FP_OUT _fp_out_narrow + +#ifdef __STDIO_PRINTF_FLOAT + +static size_t _fp_out_narrow(FILE *fp, intptr_t type, intptr_t len, intptr_t buf) +{ + size_t r = 0; + + if (type & 0x80) { /* Some type of padding needed. */ + int buflen = strlen((const char *) buf); + if ((len -= buflen) > 0) { + if ((r = _charpad(fp, (type & 0x7f), len)) != len) { + return r; + } + } + len = buflen; + } + return r + OUTNSTR(fp, (const char *) buf, len); +} + +#endif /* __STDIO_PRINTF_FLOAT */ + +#else /* L_vfprintf */ + +#define VFPRINTF vfwprintf +#define FMT_TYPE wchar_t +#define OUTNSTR _outnwcs +#define STRLEN wcslen +#define _PPFS_init _ppwfs_init +#define OUTPUT(F,S) fputws(S,F) +#define _outnwcs(stream, wstring, len) _wstdio_fwrite(wstring, len, stream) +#define FP_OUT _fp_out_wide + +#ifdef __UCLIBC_HAS_LOCALE__ +libc_hidden_proto(__global_locale) +#endif + +static size_t _outnstr(FILE *stream, const char *s, size_t wclen) +{ + /* NOTE!!! len here is the number of wchars we want to generate!!! */ + wchar_t wbuf[64]; + mbstate_t mbstate; + size_t todo, r, n; + + mbstate.__mask = 0; + todo = wclen; + + while (todo) { + r = mbsrtowcs(wbuf, &s, + ((todo <= sizeof(wbuf)/sizeof(wbuf[0])) + ? todo + : sizeof(wbuf)/sizeof(wbuf[0])), + &mbstate); + assert(((ssize_t)r) > 0); + n = _outnwcs(stream, wbuf, r); + todo -= n; + if (n != r) { + break; + } + } + + return wclen - todo; +} + +#ifdef __STDIO_PRINTF_FLOAT + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Move defines from _fpmaxtostr. Put them in a common header. +#endif + +/* The following defines are from _fpmaxtostr.*/ +#define DIGITS_PER_BLOCK 9 +#define NUM_DIGIT_BLOCKS ((DECIMAL_DIG+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK) +#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK ) + +static size_t _fp_out_wide(FILE *fp, intptr_t type, intptr_t len, intptr_t buf) +{ + wchar_t wbuf[BUF_SIZE]; + const char *s = (const char *) buf; + size_t r = 0; + int i; + + if (type & 0x80) { /* Some type of padding needed */ + int buflen = strlen(s); + if ((len -= buflen) > 0) { + if ((r = _charpad(fp, (type & 0x7f), len)) != len) { + return r; + } + } + len = buflen; + } + + if (len > 0) { + i = 0; + do { +#ifdef __LOCALE_C_ONLY + wbuf[i] = s[i]; +#else /* __LOCALE_C_ONLY */ + +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + if (s[i] == ',') { + wbuf[i] = __UCLIBC_CURLOCALE_DATA.thousands_sep_wc; + } else +#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ + if (s[i] == '.') { + wbuf[i] = __UCLIBC_CURLOCALE_DATA.decimal_point_wc; + } else { + wbuf[i] = s[i]; + } +#endif /* __LOCALE_C_ONLY */ + + } while (++i < len); + + r += OUTNSTR(fp, wbuf, len); + } + + return r; +} + +#endif /* __STDIO_PRINTF_FLOAT */ + +static int _ppwfs_init(register ppfs_t *ppfs, const wchar_t *fmt0) +{ + static const wchar_t invalid_wcs[] = L"Invalid wide format string."; + int r; + + /* First, zero out everything... argnumber[], argtype[], argptr[] */ + memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */ +#ifdef NL_ARGMAX + --ppfs->maxposarg; /* set to -1 */ +#endif /* NL_ARGMAX */ + ppfs->fmtpos = (const char *) fmt0; + ppfs->info._flags = FLAG_WIDESTREAM; + + { + mbstate_t mbstate; + const wchar_t *p; + mbstate.__mask = 0; /* Initialize the mbstate. */ + p = fmt0; + if (wcsrtombs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) { + ppfs->fmtpos = (const char *) invalid_wcs; + return -1; + } + } + + /* now set all argtypes to no-arg */ + { +#if 1 + /* TODO - use memset here since already "paid for"? */ + register int *p = ppfs->argtype; + + r = MAX_ARGS; + do { + *p++ = __PA_NOARG; + } while (--r); +#else + /* TODO -- get rid of this?? */ + register char *p = (char *) ((MAX_ARGS-1) * sizeof(int)); + + do { + *((int *)(((char *)ppfs) + ((int)p) + offsetof(ppfs_t,argtype))) = __PA_NOARG; + p -= sizeof(int); + } while (p); +#endif + } + + /* + * Run through the entire format string to validate it and initialize + * the positional arg numbers (if any). + */ + { + register const wchar_t *fmt = fmt0; + + while (*fmt) { + if ((*fmt == '%') && (*++fmt != '%')) { + ppfs->fmtpos = (const char *) fmt; /* back up to the '%' */ + if ((r = _ppfs_parsespec(ppfs)) < 0) { + return -1; + } + fmt = (const wchar_t *) ppfs->fmtpos; /* update to one past end of spec */ + } else { + ++fmt; + } + } + ppfs->fmtpos = (const char *) fmt0; /* rewind */ + } + +#ifdef NL_ARGMAX + /* If we have positional args, make sure we know all the types. */ + { + register int *p = ppfs->argtype; + r = ppfs->maxposarg; + while (--r >= 0) { + if ( *p == __PA_NOARG ) { /* missing arg type!!! */ + return -1; + } + ++p; + } + } +#endif /* NL_ARGMAX */ + + return 0; +} + +#endif /* L_vfprintf */ + +static size_t _charpad(FILE * __restrict stream, int padchar, size_t numpad) +{ + size_t todo = numpad; + + /* TODO -- Use a buffer to cut down on function calls... */ + FMT_TYPE pad[1]; + + *pad = padchar; + while (todo && (OUTNSTR(stream, pad, 1) == 1)) { + --todo; + } + + return numpad - todo; +} + +/* TODO -- Dynamically allocate work space to accomodate stack-poor archs? */ +static int _do_one_spec(FILE * __restrict stream, + register ppfs_t *ppfs, int *count) +{ + static const char spec_base[] = SPEC_BASE; +#ifdef L_vfprintf + static const char prefix[] = "+\0-\0 \0000x\0000X"; + /* 0 2 4 6 9 11*/ +#else /* L_vfprintf */ + static const wchar_t prefix[] = L"+\0-\0 \0000x\0000X"; +#endif /* L_vfprintf */ + enum { + PREFIX_PLUS = 0, + PREFIX_MINUS = 2, + PREFIX_SPACE = 4, + PREFIX_LWR_X = 6, + PREFIX_UPR_X = 9, + PREFIX_NONE = 11 + }; + +#ifdef __va_arg_ptr + const void * const *argptr; +#else + const void * argptr[MAX_ARGS_PER_SPEC]; +#endif + int *argtype; +#ifdef __UCLIBC_HAS_WCHAR__ + const wchar_t *ws = NULL; + mbstate_t mbstate; +#endif /* __UCLIBC_HAS_WCHAR__ */ + size_t slen; +#ifdef L_vfprintf +#define SLEN slen +#else + size_t SLEN; + wchar_t wbuf[2]; +#endif + int base; + int numpad; + int alphacase; + int numfill = 0; /* TODO: fix */ + int prefix_num = PREFIX_NONE; + char padchar = ' '; +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Determine appropriate buf size. +#endif /* __UCLIBC_MJN3_ONLY__ */ + /* TODO: buf needs to be big enough for any possible error return strings + * and also for any locale-grouped long long integer strings generated. + * This should be large enough for any of the current archs/locales, but + * eventually this should be handled robustly. */ + char buf[128]; + +#ifdef NDEBUG + _ppfs_parsespec(ppfs); +#else + if (_ppfs_parsespec(ppfs) < 0) { /* TODO: just for debugging */ + abort(); + } +#endif + _ppfs_setargs(ppfs); + + argtype = ppfs->argtype + ppfs->argnumber[2] - 1; + /* Deal with the argptr vs argvalue issue. */ +#ifdef __va_arg_ptr + argptr = (const void * const *) ppfs->argptr; +#ifdef NL_ARGMAX + if (ppfs->maxposarg > 0) { /* Using positional args... */ + argptr += ppfs->argnumber[2] - 1; + } +#endif /* NL_ARGMAX */ +#else + /* Need to build a local copy... */ + { + register argvalue_t *p = ppfs->argvalue; + int i; +#ifdef NL_ARGMAX + if (ppfs->maxposarg > 0) { /* Using positional args... */ + p += ppfs->argnumber[2] - 1; + } +#endif /* NL_ARGMAX */ + for (i = 0 ; i < ppfs->num_data_args ; i++ ) { + argptr[i] = (void *) p++; + } + } +#endif + { + register char *s = NULL; /* TODO: Should s be unsigned char * ? */ + + if (ppfs->conv_num == CONV_n) { + _store_inttype(*(void **)*argptr, + ppfs->info._flags & __PA_INTMASK, + (intmax_t) (*count)); + return 0; + } + if (ppfs->conv_num <= CONV_i) { /* pointer or (un)signed int */ + alphacase = __UIM_LOWER; + +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_vfprintf +#warning CONSIDER: Should we ignore these flags if stub locale? What about custom specs? +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + if ((base = spec_base[(int)(ppfs->conv_num - CONV_p)]) == 10) { + if (PRINT_INFO_FLAG_VAL(&(ppfs->info),group)) { + alphacase = __UIM_GROUP; + } + if (PRINT_INFO_FLAG_VAL(&(ppfs->info),i18n)) { + alphacase |= 0x80; + } + } + + if (ppfs->conv_num <= CONV_u) { /* pointer or unsigned int */ + if (ppfs->conv_num == CONV_X) { + alphacase = __UIM_UPPER; + } + if (ppfs->conv_num == CONV_p) { /* pointer */ + prefix_num = PREFIX_LWR_X; + } else { /* unsigned int */ + } + } else { /* signed int */ + base = -base; + } + if (ppfs->info.prec < 0) { /* Ignore '0' flag if prec specified. */ + padchar = ppfs->info.pad; + } +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_vfprintf +#warning CONSIDER: If using outdigits and/or grouping, how should we interpret precision? +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + s = _uintmaxtostr(buf + sizeof(buf) - 1, + (uintmax_t) + _load_inttype(*argtype & __PA_INTMASK, + *argptr, base), base, alphacase); + if (ppfs->conv_num > CONV_u) { /* signed int */ + if (*s == '-') { + PRINT_INFO_SET_FLAG(&(ppfs->info),showsign); + ++s; /* handle '-' in the prefix string */ + prefix_num = PREFIX_MINUS; + } else if (PRINT_INFO_FLAG_VAL(&(ppfs->info),showsign)) { + prefix_num = PREFIX_PLUS; + } else if (PRINT_INFO_FLAG_VAL(&(ppfs->info),space)) { + prefix_num = PREFIX_SPACE; + } + } + slen = (char *)(buf + sizeof(buf) - 1) - s; +#ifdef L_vfwprintf + { + const char *q = s; + mbstate.__mask = 0; /* Initialize the mbstate. */ + SLEN = mbsrtowcs(NULL, &q, 0, &mbstate); + } +#endif + numfill = ((ppfs->info.prec < 0) ? 1 : ppfs->info.prec); + if (PRINT_INFO_FLAG_VAL(&(ppfs->info),alt)) { + if (ppfs->conv_num <= CONV_x) { /* x or p */ + prefix_num = PREFIX_LWR_X; + } + if (ppfs->conv_num == CONV_X) { + prefix_num = PREFIX_UPR_X; + } + if ((ppfs->conv_num == CONV_o) && (numfill <= SLEN)) { + numfill = ((*s == '0') ? 1 : SLEN + 1); + } + } + if (*s == '0') { + if (prefix_num >= PREFIX_LWR_X) { + prefix_num = PREFIX_NONE; + } + if (ppfs->conv_num == CONV_p) {/* null pointer */ + s = "(nil)"; +#ifdef L_vfwprintf + SLEN = +#endif + slen = 5; + numfill = 0; + } else if (numfill == 0) { /* if precision 0, no output */ +#ifdef L_vfwprintf + SLEN = +#endif + slen = 0; + } + } + numfill = ((numfill > SLEN) ? numfill - SLEN : 0); + } else if (ppfs->conv_num <= CONV_A) { /* floating point */ +#ifdef __STDIO_PRINTF_FLOAT + ssize_t nf; + nf = _fpmaxtostr(stream, + (__fpmax_t) + (PRINT_INFO_FLAG_VAL(&(ppfs->info),is_long_double) + ? *(long double *) *argptr + : (long double) (* (double *) *argptr)), + &ppfs->info, FP_OUT ); + if (nf < 0) { + return -1; + } + *count += nf; + + return 0; +#else /* __STDIO_PRINTF_FLOAT */ + return -1; /* TODO -- try to continue? */ +#endif /* __STDIO_PRINTF_FLOAT */ + } else if (ppfs->conv_num <= CONV_S) { /* wide char or string */ +#ifdef L_vfprintf + +#ifdef __UCLIBC_HAS_WCHAR__ + mbstate.__mask = 0; /* Initialize the mbstate. */ + if (ppfs->conv_num == CONV_S) { /* wide string */ + if (!(ws = *((const wchar_t **) *argptr))) { + goto NULL_STRING; + } + /* We use an awful uClibc-specific hack here, passing + * (char*) &ws as the conversion destination. This signals + * uClibc's wcsrtombs that we want a "restricted" length + * such that the mbs fits in a buffer of the specified + * size with no partial conversions. */ + if ((slen = wcsrtombs((char *) &ws, &ws, /* Use awful hack! */ + ((ppfs->info.prec >= 0) + ? ppfs->info.prec + : SIZE_MAX), &mbstate)) + == ((size_t)-1) + ) { + return -1; /* EILSEQ */ + } + } else { /* wide char */ + s = buf; + slen = wcrtomb(s, (*((const wchar_t *) *argptr)), &mbstate); + if (slen == ((size_t)-1)) { + return -1; /* EILSEQ */ + } + s[slen] = 0; /* TODO - Is this necessary? */ + } +#else /* __UCLIBC_HAS_WCHAR__ */ + return -1; +#endif /* __UCLIBC_HAS_WCHAR__ */ + } else if (ppfs->conv_num <= CONV_s) { /* char or string */ + if (ppfs->conv_num == CONV_s) { /* string */ + s = *((char **) (*argptr)); + if (s) { +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ + SET_STRING_LEN: +#endif + slen = strnlen(s, ((ppfs->info.prec >= 0) + ? ppfs->info.prec : SIZE_MAX)); + } else { +#ifdef __UCLIBC_HAS_WCHAR__ + NULL_STRING: +#endif + s = "(null)"; + slen = 6; + } + } else { /* char */ + s = buf; + *s = (unsigned char)(*((const int *) *argptr)); + s[1] = 0; + slen = 1; + } + +#else /* L_vfprintf */ + + if (ppfs->conv_num == CONV_S) { /* wide string */ + ws = *((wchar_t **) (*argptr)); + if (!ws) { + goto NULL_STRING; + } + SLEN = wcsnlen(ws, ((ppfs->info.prec >= 0) + ? ppfs->info.prec : SIZE_MAX)); + } else { /* wide char */ + *wbuf = (wchar_t)(*((const wint_t *) *argptr)); + CHAR_CASE: + ws = wbuf; + wbuf[1] = 0; + SLEN = 1; + } + + } else if (ppfs->conv_num <= CONV_s) { /* char or string */ + + if (ppfs->conv_num == CONV_s) { /* string */ +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Fix %s for vfwprintf... output upto illegal sequence? +#endif /* __UCLIBC_MJN3_ONLY__ */ + s = *((char **) (*argptr)); + if (s) { +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ + SET_STRING_LEN: +#endif + /* We use an awful uClibc-specific hack here, passing + * (wchar_t*) &mbstate as the conversion destination. + * This signals uClibc's mbsrtowcs that we want a + * "restricted" length such that the mbs fits in a buffer + * of the specified size with no partial conversions. */ + { + const char *q = s; + mbstate.__mask = 0; /* Initialize the mbstate. */ + SLEN = mbsrtowcs((wchar_t *) &mbstate, &q, + ((ppfs->info.prec >= 0) + ? ppfs->info.prec : SIZE_MAX), + &mbstate); + } + if (SLEN == ((size_t)(-1))) { + return -1; /* EILSEQ */ + } + } else { + NULL_STRING: + s = "(null)"; + SLEN = slen = 6; + } + } else { /* char */ + *wbuf = btowc( (unsigned char)(*((const int *) *argptr)) ); + goto CHAR_CASE; + } + +#endif /* L_vfprintf */ + +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ + } else if (ppfs->conv_num == CONV_m) { + s = __glibc_strerror_r(errno, buf, sizeof(buf)); + goto SET_STRING_LEN; +#endif + } else { +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ + assert(ppfs->conv_num == CONV_custom0); + + s = _custom_printf_spec; + do { + if (*s == ppfs->info.spec) { + int rv; + /* TODO -- check return value for sanity? */ + rv = (*_custom_printf_handler + [(int)(s-_custom_printf_spec)]) + (stream, &ppfs->info, argptr); + if (rv < 0) { + return -1; + } + *count += rv; + return 0; + } + } while (++s < (_custom_printf_spec + MAX_USER_SPEC)); +#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ + assert(0); + return -1; + } + +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_vfprintf +#warning CONSIDER: If using outdigits and/or grouping, how should we pad? +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + { + size_t t; + + t = SLEN + numfill; + if (prefix_num != PREFIX_NONE) { + t += ((prefix_num < PREFIX_LWR_X) ? 1 : 2); + } + numpad = ((ppfs->info.width > t) ? (ppfs->info.width - t) : 0); + *count += t + numpad; + } + if (padchar == '0') { /* TODO: check this */ + numfill += numpad; + numpad = 0; + } + + /* Now handle the output itself. */ + if (!PRINT_INFO_FLAG_VAL(&(ppfs->info),left)) { + if (_charpad(stream, ' ', numpad) != numpad) { + return -1; + } + numpad = 0; + } + OUTPUT(stream, prefix + prefix_num); + if (_charpad(stream, '0', numfill) != numfill) { + return -1; + } + +#ifdef L_vfprintf + +#ifdef __UCLIBC_HAS_WCHAR__ + if (!ws) { + assert(s); + if (_outnstr(stream, s, slen) != slen) { + return -1; + } + } else { /* wide string */ + size_t t; + mbstate.__mask = 0; /* Initialize the mbstate. */ + while (slen) { + t = (slen <= sizeof(buf)) ? slen : sizeof(buf); + t = wcsrtombs(buf, &ws, t, &mbstate); + assert (t != ((size_t)(-1))); + if (_outnstr(stream, buf, t) != t) { + return -1; + } + slen -= t; + } + } +#else /* __UCLIBC_HAS_WCHAR__ */ + if (_outnstr(stream, s, slen) != slen) { + return -1; + } +#endif /* __UCLIBC_HAS_WCHAR__ */ + +#else /* L_vfprintf */ + + if (!ws) { + assert(s); + if (_outnstr(stream, s, SLEN) != SLEN) { + return -1; + } + } else { + if (_outnwcs(stream, ws, SLEN) != SLEN) { + return -1; + } + } + +#endif /* L_vfprintf */ + if (_charpad(stream, ' ', numpad) != numpad) { + return -1; + } + } + + return 0; +} + +libc_hidden_proto(fprintf) + +libc_hidden_proto(VFPRINTF) +int VFPRINTF (FILE * __restrict stream, + register const FMT_TYPE * __restrict format, + va_list arg) +{ + ppfs_t ppfs; + int count, r; + register const FMT_TYPE *s; + __STDIO_AUTO_THREADLOCK_VAR; + + __STDIO_AUTO_THREADLOCK(stream); + + count = 0; + s = format; + + if +#ifdef L_vfprintf + (!__STDIO_STREAM_IS_NARROW_WRITING(stream) + && __STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_NARROW)) +#else + (!__STDIO_STREAM_IS_WIDE_WRITING(stream) + && __STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_WIDE)) +#endif + { + count = -1; + } else if (_PPFS_init(&ppfs, format) < 0) { /* Bad format string. */ + OUTNSTR(stream, (const FMT_TYPE *) ppfs.fmtpos, + STRLEN((const FMT_TYPE *)(ppfs.fmtpos))); +#if defined(L_vfprintf) && !defined(NDEBUG) + fprintf(stderr,"\nIMbS: \"%s\"\n\n", format); +#endif + count = -1; + } else { + _ppfs_prepargs(&ppfs, arg); /* This did a va_copy!!! */ + + do { + while (*format && (*format != '%')) { + ++format; + } + + if (format-s) { /* output any literal text in format string */ + if ( (r = OUTNSTR(stream, s, format-s)) != (format-s)) { + count = -1; + break; + } + count += r; + } + + if (!*format) { /* we're done */ + break; + } + + if (format[1] != '%') { /* if we get here, *format == '%' */ + /* TODO: _do_one_spec needs to know what the output funcs are!!! */ + ppfs.fmtpos = (const char *)(++format); + /* TODO: check -- should only fail on stream error */ + if ( (r = _do_one_spec(stream, &ppfs, &count)) < 0) { + count = -1; + break; + } + s = format = (const FMT_TYPE *) ppfs.fmtpos; + } else { /* %% means literal %, so start new string */ + s = ++format; + ++format; + } + } while (1); + + va_end(ppfs.arg); /* Need to clean up after va_copy! */ + } + +/* #if defined(L_vfprintf) && defined(__UCLIBC_HAS_WCHAR__) */ +/* DONE: */ +/* #endif */ + + __STDIO_AUTO_THREADUNLOCK(stream); + + return count; +} +libc_hidden_def(VFPRINTF) +#endif +/**********************************************************************/ diff --git a/libc/stdio/scanf.c b/libc/stdio/scanf.c deleted file mode 100644 index df78e7ad3..000000000 --- a/libc/stdio/scanf.c +++ /dev/null @@ -1,2268 +0,0 @@ -/* Copyright (C) 2002-2004 Manuel Novoa III - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* Aug 1, 2003 - * New *scanf implementation with lots of bug fixes and *wscanf support. - * Also now optionally supports hexadecimal float notation, positional - * args, and glibc locale-specific digit grouping. Should now be - * standards compliant. - * - * Aug 18, 2003 - * Bug fix: scanf %lc,%ls,%l[ would always set mb_fail on eof or error, - * even when just starting a new mb char. - * Bug fix: wscanf would incorrectly unget in certain situations. - * - * Sep 5, 2003 - * Bug fix: store flag wasn't respected if no positional args. - * Implement vs{n}scanf for the non-buffered stdio no-wchar case. - * - * Sep 13, 2003 - * Bug fix: Fix a problem reported by Atsushi Nemoto - * for environments where long and long long are the same. - * - * Sep 21, 2003 - * Ugh... EOF handling by scanf was completely broken. :-( Regretably, - * I got my mind fixed in one mode and didn't comply with the standards. - * Things should be fixed now, but comparision testing is difficult when - * glibc's scanf is broken and they stubbornly refuse to even acknowledge - * that it is... even when confronted by specific examples from the C99 - * standards and from an official C standard defect report. - */ - -#define _ISOC99_SOURCE /* for LLONG_MAX primarily... */ -#include -#include "_stdio.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __UCLIBC_HAS_WCHAR__ -#include -#include -#include -#endif /* __UCLIBC_HAS_WCHAR__ */ - -#include -#include - -#include -#include - -#ifdef __UCLIBC_HAS_THREADS__ -#include -#include -#endif /* __UCLIBC_HAS_THREADS__ */ - -#ifdef __UCLIBC_HAS_FLOATS__ -#include -#include -#endif /* __UCLIBC_HAS_FLOATS__ */ - -libc_hidden_proto(memcmp) -libc_hidden_proto(memset) -libc_hidden_proto(strcpy) -libc_hidden_proto(strlen) -libc_hidden_proto(ungetc) -libc_hidden_proto(vfscanf) -libc_hidden_proto(vsscanf) -libc_hidden_proto(fclose) -libc_hidden_proto(getc_unlocked) -libc_hidden_proto(__fgetc_unlocked) -#ifdef __UCLIBC_HAS_WCHAR__ -libc_hidden_proto(wcslen) -libc_hidden_proto(vfwscanf) -libc_hidden_proto(vswscanf) -libc_hidden_proto(mbsrtowcs) -libc_hidden_proto(mbrtowc) -libc_hidden_proto(wcrtomb) -libc_hidden_proto(ungetwc) -libc_hidden_proto(iswspace) -libc_hidden_proto(fgetwc_unlocked) -#endif -#ifdef __UCLIBC_HAS_XLOCALE__ -libc_hidden_proto(__ctype_b_loc) -#else -#ifdef __UCLIBC_HAS_LOCALE__ -libc_hidden_proto(__global_locale) -#endif -libc_hidden_proto(__ctype_b) -#endif - -#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ -#ifdef L_vfscanf -/* only emit this once */ -#warning Forcing undef of __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ until implemented! -#endif -#undef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ -#endif - -#undef __STDIO_HAS_VSSCANF -#if defined(__STDIO_BUFFERS) || !defined(__UCLIBC_HAS_WCHAR__) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) -#define __STDIO_HAS_VSSCANF 1 - -#if !defined(__STDIO_BUFFERS) && !defined(__UCLIBC_HAS_WCHAR__) -typedef struct { - FILE f; - unsigned char *bufread; /* pointer to 1 past end of buffer */ - unsigned char *bufpos; -} __FILE_vsscanf; -#endif - -#endif - -extern void _store_inttype(void *dest, int desttype, uintmax_t val); - -#if defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) - -extern unsigned long long -_stdlib_strto_ll(register const char * __restrict str, - char ** __restrict endptr, int base, int sflag); -#if (ULLONG_MAX == UINTMAX_MAX) -#define STRTOUIM(s,e,b,sf) _stdlib_strto_ll(s,e,b,sf) -#endif - -#else /* defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) */ - -extern unsigned long -_stdlib_strto_l(register const char * __restrict str, - char ** __restrict endptr, int base, int sflag); - -#if (ULONG_MAX == UINTMAX_MAX) -#define STRTOUIM(s,e,b,sf) _stdlib_strto_l(s,e,b,sf) -#endif - -#endif /* defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) */ - -#ifndef STRTOUIM -#error STRTOUIM conversion function is undefined! -#endif - -/**********************************************************************/ - -/* The standards require EOF < 0. */ -#if EOF >= CHAR_MIN -#define __isdigit_char_or_EOF(C) __isdigit_char((C)) -#else -#define __isdigit_char_or_EOF(C) __isdigit_int((C)) -#endif - -/**********************************************************************/ -#ifdef L_fscanf - -libc_hidden_proto(fscanf) -int fscanf(FILE * __restrict stream, const char * __restrict format, ...) -{ - va_list arg; - int rv; - - va_start(arg, format); - rv = vfscanf(stream, format, arg); - va_end(arg); - - return rv; -} -libc_hidden_def(fscanf) - -#endif -/**********************************************************************/ -#ifdef L_scanf - -int scanf(const char * __restrict format, ...) -{ - va_list arg; - int rv; - - va_start(arg, format); - rv = vfscanf(stdin, format, arg); - va_end(arg); - - return rv; -} - -#endif -/**********************************************************************/ -#ifdef L_sscanf - -#ifdef __STDIO_HAS_VSSCANF - -libc_hidden_proto(sscanf) -int sscanf(const char * __restrict str, const char * __restrict format, ...) -{ - va_list arg; - int rv; - - va_start(arg, format); - rv = vsscanf(str, format, arg); - va_end(arg); - - return rv; -} -libc_hidden_def(sscanf) - -#else /* __STDIO_HAS_VSSCANF */ -#warning Skipping sscanf since no vsscanf! -#endif /* __STDIO_HAS_VSSCANF */ - -#endif -/**********************************************************************/ -#ifdef L_vscanf - -libc_hidden_proto(vscanf) -int vscanf(const char * __restrict format, va_list arg) -{ - return vfscanf(stdin, format, arg); -} -libc_hidden_def(vscanf) - -#endif -/**********************************************************************/ -#ifdef L_vsscanf - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning WISHLIST: Implement vsscanf for non-buf and no custom stream case. -#endif /* __UCLIBC_MJN3_ONLY__ */ - -#ifdef __STDIO_BUFFERS - -int vsscanf(__const char *sp, __const char *fmt, va_list ap) -{ - FILE f; - -/* __STDIO_STREAM_RESET_GCS(&f); */ -#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ - f.__cookie = &(f.__filedes); - f.__gcs.read = NULL; - f.__gcs.write = NULL; - f.__gcs.seek = NULL; - f.__gcs.close = NULL; -#endif - - f.__filedes = __STDIO_STREAM_FAKE_VSSCANF_FILEDES; - f.__modeflags = (__FLAG_NARROW|__FLAG_READONLY|__FLAG_READING); - -#ifdef __UCLIBC_HAS_WCHAR__ - f.__ungot_width[0] = 0; -#endif -#ifdef __STDIO_MBSTATE - __INIT_MBSTATE(&(f.__state)); -#endif - -#ifdef __UCLIBC_HAS_THREADS__ - f.__user_locking = 1; /* Set user locking. */ - __stdio_init_mutex(&f.__lock); -#endif - f.__nextopen = NULL; - - /* Set these last since __bufgetc initialization depends on - * __user_locking and only gets set if user locking is on. */ - f.__bufstart = - f.__bufpos = (unsigned char *) ((void *) sp); - f.__bufread = - f.__bufend = f.__bufstart + strlen(sp); - __STDIO_STREAM_ENABLE_GETC(&f); - __STDIO_STREAM_DISABLE_PUTC(&f); - - return vfscanf(&f, fmt, ap); -} -libc_hidden_def(vsscanf) - -#elif !defined(__UCLIBC_HAS_WCHAR__) - -int vsscanf(__const char *sp, __const char *fmt, va_list ap) -{ - __FILE_vsscanf f; - - f.bufpos = (unsigned char *) ((void *) sp); - f.bufread = f.bufpos + strlen(sp); - -/* __STDIO_STREAM_RESET_GCS(&f.f); */ -#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ - f.f.__cookie = &(f.f.__filedes); - f.f.__gcs.read = NULL; - f.f.__gcs.write = NULL; - f.f.__gcs.seek = NULL; - f.f.__gcs.close = NULL; -#endif - - f.f.__filedes = __STDIO_STREAM_FAKE_VSSCANF_FILEDES_NB; - f.f.__modeflags = (__FLAG_NARROW|__FLAG_READONLY|__FLAG_READING); - -/* #ifdef __UCLIBC_HAS_WCHAR__ */ -/* f.f.__ungot_width[0] = 0; */ -/* #endif */ -#ifdef __STDIO_MBSTATE -#error __STDIO_MBSTATE is defined! -/* __INIT_MBSTATE(&(f.f.__state)); */ -#endif - -#ifdef __UCLIBC_HAS_THREADS__ - f.f.__user_locking = 1; /* Set user locking. */ - __stdio_init_mutex(&f.f.__lock); -#endif - f.f.__nextopen = NULL; - - return vfscanf(&f.f, fmt, ap); -} -libc_hidden_def(vsscanf) - -#elif defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) - -int vsscanf(__const char *sp, __const char *fmt, va_list ap) -{ - FILE *f; - int rv = EOF; - - if ((f = fmemopen((char *)sp, strlen(sp), "r")) != NULL) { - rv = vfscanf(f, fmt, ap); - fclose(f); - } - - return rv; -} -libc_hidden_def(vsscanf) - -#else -#warning Skipping vsscanf since no buffering, no custom streams, and wchar enabled! -#ifdef __STDIO_HAS_VSSCANF -#error WHOA! __STDIO_HAS_VSSCANF is defined! -#endif -#endif - -#endif -/**********************************************************************/ -#ifdef L_fwscanf - -int fwscanf(FILE * __restrict stream, const wchar_t * __restrict format, ...) -{ - va_list arg; - int rv; - - va_start(arg, format); - rv = vfwscanf(stream, format, arg); - va_end(arg); - - return rv; -} - -#endif -/**********************************************************************/ -#ifdef L_wscanf - -int wscanf(const wchar_t * __restrict format, ...) -{ - va_list arg; - int rv; - - va_start(arg, format); - rv = vfwscanf(stdin, format, arg); - va_end(arg); - - return rv; -} - -#endif -/**********************************************************************/ -#ifdef L_swscanf - -#ifdef __STDIO_BUFFERS - -int swscanf(const wchar_t * __restrict str, const wchar_t * __restrict format, - ...) -{ - va_list arg; - int rv; - - va_start(arg, format); - rv = vswscanf(str, format, arg); - va_end(arg); - - return rv; -} -#else /* __STDIO_BUFFERS */ -#warning Skipping swscanf since no buffering! -#endif /* __STDIO_BUFFERS */ - -#endif -/**********************************************************************/ -#ifdef L_vwscanf - -int vwscanf(const wchar_t * __restrict format, va_list arg) -{ - return vfwscanf(stdin, format, arg); -} - -#endif -/**********************************************************************/ -#ifdef L_vswscanf - -#ifdef __STDIO_BUFFERS - -int vswscanf(const wchar_t * __restrict str, const wchar_t * __restrict format, - va_list arg) -{ - FILE f; - - f.__bufstart = - f.__bufpos = (char *) str; - f.__bufread = - f.__bufend = (char *)(str + wcslen(str)); - __STDIO_STREAM_DISABLE_GETC(&f); - __STDIO_STREAM_DISABLE_PUTC(&f); - -/* __STDIO_STREAM_RESET_GCS(&f); */ -#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ - f.__cookie = &(f.__filedes); - f.__gcs.read = NULL; - f.__gcs.write = NULL; - f.__gcs.seek = NULL; - f.__gcs.close = NULL; -#endif - - f.__filedes = __STDIO_STREAM_FAKE_VSWSCANF_FILEDES; - f.__modeflags = (__FLAG_WIDE|__FLAG_READONLY|__FLAG_READING); - -#ifdef __UCLIBC_HAS_WCHAR__ - f.__ungot_width[0] = 0; -#endif /* __UCLIBC_HAS_WCHAR__ */ -#ifdef __STDIO_MBSTATE - __INIT_MBSTATE(&(f.__state)); -#endif /* __STDIO_MBSTATE */ - -#ifdef __UCLIBC_HAS_THREADS__ - f.__user_locking = 1; /* Set user locking. */ - __stdio_init_mutex(&f.__lock); -#endif - f.__nextopen = NULL; - - return vfwscanf(&f, format, arg); -} -libc_hidden_def(vswscanf) -#else /* __STDIO_BUFFERS */ -#warning Skipping vswscanf since no buffering! -#endif /* __STDIO_BUFFERS */ - -#endif -/**********************************************************************/ -/**********************************************************************/ - - - -/* float layout 0123456789012345678901 repeat n for "l[" */ -#define SPEC_CHARS "npxXoudifFeEgGaACSncs[" -/* npxXoudif eEgG CS cs[ */ - -/* NOTE: Ordering is important! In particular, CONV_LEFTBRACKET - * must immediately precede CONV_c. */ - -enum { - CONV_n = 0, - CONV_p, - CONV_x, CONV_X, CONV_o, CONV_u, CONV_d, CONV_i, - CONV_f, CONV_F, CONV_e, CONV_E, CONV_g, CONV_G, CONV_a, CONV_A, - CONV_C, CONV_S, CONV_LEFTBRACKET, CONV_c, CONV_s, CONV_leftbracket, - CONV_percent, CONV_whitespace /* not in SPEC_* and no flags */ -}; - -#ifdef __UCLIBC_HAS_FLOATS__ -#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ -/* p x X o u d i f F e E g G a A */ -#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0 } -#else -/* p x X o u d i f F e E g G a A */ -#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, 10 } -#endif -#else /* __UCLIBC_HAS_FLOATS__ */ -/* p x X o u d i f F e E g G a A */ -#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 0 } -#endif /* __UCLIBC_HAS_FLOATS__ */ - -#ifdef __UCLIBC_MJN3_ONLY__ -#ifdef L_vfscanf -/* emit once */ -#warning CONSIDER: Add a '0' flag to eat 0 padding when grouping? -#endif -#endif /* __UCLIBC_MJN3_ONLY__ */ - -#define SPEC_FLAGS "*'I" - -enum { - FLAG_SURPRESS = 0x10, /* MUST BE 1ST!! See DO_FLAGS. */ - FLAG_THOUSANDS = 0x20, - FLAG_I18N = 0x40, /* only works for d, i, u */ - FLAG_MALLOC = 0x80, /* only works for s, S, and [ (and l[)*/ -}; - - -#define SPEC_RANGES { CONV_n, CONV_p, CONV_i, CONV_A, \ - CONV_C, CONV_LEFTBRACKET, \ - CONV_c, CONV_leftbracket } - -/* Note: We treat L and ll as synonymous... for ints and floats. */ - -#define SPEC_ALLOWED_FLAGS { \ - /* n */ (0x0f|FLAG_SURPRESS), \ - /* p */ ( 0|FLAG_SURPRESS), \ - /* oxXudi */ (0x0f|FLAG_SURPRESS|FLAG_THOUSANDS|FLAG_I18N), \ - /* fFeEgGaA */ (0x0c|FLAG_SURPRESS|FLAG_THOUSANDS|FLAG_I18N), \ - /* C */ ( 0|FLAG_SURPRESS), \ - /* S and l[ */ ( 0|FLAG_SURPRESS|FLAG_MALLOC), \ - /* c */ (0x04|FLAG_SURPRESS), \ - /* s and [ */ (0x04|FLAG_SURPRESS|FLAG_MALLOC), \ -} - - -/**********************************************************************/ -/* - * In order to ease translation to what arginfo and _print_info._flags expect, - * we map: 0:int 1:char 2:longlong 4:long 8:short - * and then _flags |= (((q << 7) + q) & 0x701) and argtype |= (_flags & 0x701) - */ - -/* TODO -- Fix the table below to take into account stdint.h. */ -/* #ifndef LLONG_MAX */ -/* #error fix QUAL_CHARS for no long long! Affects 'L', 'j', 'q', 'll'. */ -/* #else */ -/* #if LLONG_MAX != INTMAX_MAX */ -/* #error fix QUAL_CHARS intmax_t entry 'j'! */ -/* #endif */ -/* #endif */ - -#ifdef PDS -#error PDS already defined! -#endif -#ifdef SS -#error SS already defined! -#endif -#ifdef IMS -#error IMS already defined! -#endif - -#if PTRDIFF_MAX == INT_MAX -#define PDS 0 -#elif PTRDIFF_MAX == LONG_MAX -#define PDS 4 -#elif defined(LLONG_MAX) && (PTRDIFF_MAX == LLONG_MAX) -#define PDS 8 -#else -#error fix QUAL_CHARS ptrdiff_t entry 't'! -#endif - -#if SIZE_MAX == UINT_MAX -#define SS 0 -#elif SIZE_MAX == ULONG_MAX -#define SS 4 -#elif defined(LLONG_MAX) && (SIZE_MAX == ULLONG_MAX) -#define SS 8 -#else -#error fix QUAL_CHARS size_t entries 'z', 'Z'! -#endif - -#if INTMAX_MAX == INT_MAX -#define IMS 0 -#elif INTMAX_MAX == LONG_MAX -#define IMS 4 -#elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX) -#define IMS 8 -#else -#error fix QUAL_CHARS ptrdiff_t entry 't'! -#endif - -#define QUAL_CHARS { \ - /* j:(u)intmax_t z:(s)size_t t:ptrdiff_t \0:int q:long_long */ \ - 'h', 'l', 'L', 'j', 'z', 't', 'q', 0, \ - 2, 4, 8, IMS, SS, PDS, 8, 0, /* TODO -- fix!!! */\ - 1, 8 } - - -/**********************************************************************/ - -#ifdef L_vfwscanf -#if WINT_MIN > EOF -#error Unfortunately, we currently need wint_t to be able to store EOF. Sorry. -#endif -#define W_EOF WEOF -#define Wint wint_t -#define Wchar wchar_t -#define Wuchar __uwchar_t -#define ISSPACE(C) iswspace((C)) -#define VFSCANF vfwscanf -#define GETC(SC) (SC)->sc_getc((SC)) -#else -typedef unsigned char __uchar_t; -#define W_EOF EOF -#define Wint int -#define Wchar char -#define Wuchar __uchar_t -#define ISSPACE(C) isspace((C)) -#define VFSCANF vfscanf -#ifdef __UCLIBC_HAS_WCHAR__ -#define GETC(SC) (SC)->sc_getc((SC)) -#else /* __UCLIBC_HAS_WCHAR__ */ -#define GETC(SC) getc_unlocked((SC)->fp) -#endif /* __UCLIBC_HAS_WCHAR__ */ -#endif - -struct scan_cookie { - Wint cc; - Wint ungot_char; - FILE *fp; - int nread; - int width; - -#ifdef __UCLIBC_HAS_WCHAR__ - wchar_t app_ungot; /* Match FILE struct member type. */ - unsigned char ungot_wchar_width; -#else /* __UCLIBC_HAS_WCHAR__ */ - unsigned char app_ungot; /* Match FILE struct member type. */ -#endif /* __UCLIBC_HAS_WCHAR__ */ - - char ungot_flag; - -#ifdef __UCLIBC_HAS_WCHAR__ - char ungot_wflag; /* vfwscanf */ - char mb_fail; /* vfscanf */ - mbstate_t mbstate; /* vfscanf */ - wint_t wc; - wint_t ungot_wchar; /* to support __scan_getc */ - int (*sc_getc)(struct scan_cookie *); -#endif /* __UCLIBC_HAS_WCHAR__ */ - -#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ - const char *grouping; - const unsigned char *thousands_sep; - int tslen; -#ifdef __UCLIBC_HAS_WCHAR__ - wchar_t thousands_sep_wc; -#endif /* __UCLIBC_HAS_WCHAR__ */ -#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ - -#ifdef __UCLIBC_HAS_FLOATS__ - const unsigned char *decpt; - int decpt_len; -#ifdef __UCLIBC_HAS_WCHAR__ - wchar_t decpt_wc; -#endif /* __UCLIBC_HAS_WCHAR__ */ - const unsigned char *fake_decpt; -#endif /* __UCLIBC_HAS_FLOATS__ */ - -}; - -typedef struct { -#if defined(NL_ARGMAX) && (NL_ARGMAX > 0) -#if NL_ARGMAX > 10 -#warning NL_ARGMAX > 10, and space is allocated on the stack for positional args. -#endif - void *pos_args[NL_ARGMAX]; - int num_pos_args; /* Must start at -1. */ - int cur_pos_arg; -#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */ - void *cur_ptr; - const unsigned char *fmt; - int cnt, dataargtype, conv_num, max_width; - unsigned char store, flags; -} psfs_t; /* parse scanf format state */ - - -/**********************************************************************/ -/**********************************************************************/ - -extern void __init_scan_cookie(register struct scan_cookie *sc, - register FILE *fp) attribute_hidden; -extern int __scan_getc(register struct scan_cookie *sc) attribute_hidden; -extern void __scan_ungetc(register struct scan_cookie *sc) attribute_hidden; - -#ifdef __UCLIBC_HAS_FLOATS__ -extern int __scan_strtold(long double *ld, struct scan_cookie *sc); -#endif /* __UCLIBC_HAS_FLOATS__ */ - -extern int __psfs_parse_spec(psfs_t *psfs) attribute_hidden; -extern int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc) attribute_hidden; - -/**********************************************************************/ -#ifdef L___scan_cookie - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Remove dependence on decpt_str and fake_decpt in stub locale mode. -#endif -#ifndef __UCLIBC_HAS_LOCALE__ -static const char decpt_str[] = "."; -#endif - -void attribute_hidden __init_scan_cookie(register struct scan_cookie *sc, - register FILE *fp) -{ - sc->fp = fp; - sc->nread = 0; - sc->ungot_flag = 0; - sc->app_ungot = ((fp->__modeflags & __FLAG_UNGOT) ? fp->__ungot[1] : 0); -#ifdef __UCLIBC_HAS_WCHAR__ - sc->ungot_wflag = 0; /* vfwscanf */ - sc->mb_fail = 0; -#endif /* __UCLIBC_HAS_WCHAR__ */ - -#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ - if (*(sc->grouping = __UCLIBC_CURLOCALE_DATA.grouping)) { - sc->thousands_sep = __UCLIBC_CURLOCALE_DATA.thousands_sep; - sc->tslen = __UCLIBC_CURLOCALE_DATA.thousands_sep_len; -#ifdef __UCLIBC_HAS_WCHAR__ - sc->thousands_sep_wc = __UCLIBC_CURLOCALE_DATA.thousands_sep_wc; -#endif /* __UCLIBC_HAS_WCHAR__ */ - } -#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ - -#ifdef __UCLIBC_HAS_FLOATS__ -#ifdef __UCLIBC_HAS_LOCALE__ - sc->decpt = __UCLIBC_CURLOCALE_DATA.decimal_point; - sc->decpt_len = __UCLIBC_CURLOCALE_DATA.decimal_point_len; -#else /* __UCLIBC_HAS_LOCALE__ */ - sc->fake_decpt = sc->decpt = decpt_str; - sc->decpt_len = 1; -#endif /* __UCLIBC_HAS_LOCALE__ */ -#ifdef __UCLIBC_HAS_WCHAR__ -#ifdef __UCLIBC_HAS_LOCALE__ - sc->decpt_wc = __UCLIBC_CURLOCALE_DATA.decimal_point_wc; -#else - sc->decpt_wc = '.'; -#endif -#endif /* __UCLIBC_HAS_WCHAR__ */ -#endif /* __UCLIBC_HAS_FLOATS__ */ - -} - -int attribute_hidden __scan_getc(register struct scan_cookie *sc) -{ - int c; - -#ifdef __UCLIBC_HAS_WCHAR__ - assert(!sc->mb_fail); -#endif /* __UCLIBC_HAS_WCHAR__ */ - - sc->cc = EOF; - - if (--sc->width < 0) { - sc->ungot_flag |= 2; - return -1; - } - - if (sc->ungot_flag == 0) { -#if !defined(__STDIO_BUFFERS) && !defined(__UCLIBC_HAS_WCHAR__) - if (!__STDIO_STREAM_IS_FAKE_VSSCANF_NB(sc->fp)) { - c = GETC(sc); - } else { - __FILE_vsscanf *fv = (__FILE_vsscanf *)(sc->fp); - if (fv->bufpos < fv->bufread) { - c = *fv->bufpos++; - } else { - c = EOF; - sc->fp->__modeflags |= __FLAG_EOF; - } - } - if (c == EOF) { - sc->ungot_flag |= 2; - return -1; - } -#else - if ((c = GETC(sc)) == EOF) { - sc->ungot_flag |= 2; - return -1; - } -#endif - sc->ungot_char = c; - } else { - assert(sc->ungot_flag == 1); - sc->ungot_flag = 0; - } - - ++sc->nread; - return sc->cc = sc->ungot_char; -} - -void attribute_hidden __scan_ungetc(register struct scan_cookie *sc) -{ - ++sc->width; - if (sc->ungot_flag == 2) { /* last was EOF */ - sc->ungot_flag = 0; - sc->cc = sc->ungot_char; - } else if (sc->ungot_flag == 0) { - sc->ungot_flag = 1; - --sc->nread; - } else { - assert(0); - } -} - -#endif -/**********************************************************************/ -#ifdef L___psfs_parse_spec - -#ifdef SPEC_FLAGS -static const unsigned char spec_flags[] = SPEC_FLAGS; -#endif /* SPEC_FLAGS */ -static const unsigned char spec_chars[] = SPEC_CHARS; -static const unsigned char qual_chars[] = QUAL_CHARS; -static const unsigned char spec_ranges[] = SPEC_RANGES; -static const unsigned short spec_allowed[] = SPEC_ALLOWED_FLAGS; - -int attribute_hidden __psfs_parse_spec(register psfs_t *psfs) -{ - const unsigned char *p; - const unsigned char *fmt0 = psfs->fmt; - int i; -#ifdef SPEC_FLAGS - int j; -#endif -#if defined(NL_ARGMAX) && (NL_ARGMAX > 0) - unsigned char fail = 0; - - i = 0; /* Do this here to avoid a warning. */ - - if (!__isdigit_char(*psfs->fmt)) { /* Not a positional arg. */ - fail = 1; - goto DO_FLAGS; - } - - /* parse the positional arg (or width) value */ - do { - if (i <= ((INT_MAX - 9)/10)) { - i = (i * 10) + (*psfs->fmt++ - '0'); - } - } while (__isdigit_char(*psfs->fmt)); - - if (*psfs->fmt != '$') { /* This is a max field width. */ - if (psfs->num_pos_args >= 0) { /* Already saw a pos arg! */ - goto ERROR_EINVAL; - } - psfs->max_width = i; - psfs->num_pos_args = -2; - goto DO_QUALIFIER; - } - ++psfs->fmt; /* Advance past '$'. */ -#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */ - -#if defined(SPEC_FLAGS) || (defined(NL_ARGMAX) && (NL_ARGMAX > 0)) - DO_FLAGS: -#endif /* defined(SPEC_FLAGS) || (defined(NL_ARGMAX) && (NL_ARGMAX > 0)) */ -#ifdef SPEC_FLAGS - p = spec_flags; - j = FLAG_SURPRESS; - do { - if (*p == *psfs->fmt) { - ++psfs->fmt; - psfs->flags |= j; - goto DO_FLAGS; - } - j += j; - } while (*++p); - - if (psfs->flags & FLAG_SURPRESS) { /* Suppress assignment. */ - psfs->store = 0; - goto DO_WIDTH; - } -#else /* SPEC_FLAGS */ - if (*psfs->fmt == '*') { /* Suppress assignment. */ - ++psfs->fmt; - psfs->store = 0; - goto DO_WIDTH; - } -#endif /* SPEC_FLAGS */ - - -#if defined(NL_ARGMAX) && (NL_ARGMAX > 0) - if (fail) { - /* Must be a non-positional arg */ - if (psfs->num_pos_args >= 0) { /* Already saw a pos arg! */ - goto ERROR_EINVAL; - } - psfs->num_pos_args = -2; - } else { - if ((psfs->num_pos_args == -2) || (((unsigned int)(--i)) >= NL_ARGMAX)) { - /* Already saw a non-pos arg or (0-based) num too large. */ - goto ERROR_EINVAL; - } - psfs->cur_pos_arg = i; - } -#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */ - - DO_WIDTH: - for (i = 0 ; __isdigit_char(*psfs->fmt) ; ) { - if (i <= ((INT_MAX - 9)/10)) { - i = (i * 10) + (*psfs->fmt++ - '0'); - psfs->max_width = i; - } - } - -#if defined(NL_ARGMAX) && (NL_ARGMAX > 0) - DO_QUALIFIER: -#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */ - p = qual_chars; - do { - if (*psfs->fmt == *p) { - ++psfs->fmt; - break; - } - } while (*++p); - if ((p - qual_chars < 2) && (*psfs->fmt == *p)) { - p += ((sizeof(qual_chars)-2) / 2); - ++psfs->fmt; - } - psfs->dataargtype = ((int)(p[(sizeof(qual_chars)-2) / 2])) << 8; - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning CONSIDER: Should we validate that psfs->max_width > 0 in __psfs_parse_spec()? It would avoid whitespace consumption... -#warning CONSIDER: Should INT_MAX be a valid width (%c/%C)? See __psfs_parse_spec(). -#endif /* __UCLIBC_MJN3_ONLY__ */ - - p = spec_chars; - do { - if (*psfs->fmt == *p) { - int p_m_spec_chars = p - spec_chars; - -#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ -#error implement gnu a flag - if ((*p == 'a') - && ((psfs->fmt[1] == '[') || ((psfs->fmt[1]|0x20) == 's')) - ) { /* Assumes ascii for 's' and 'S' test. */ - psfs->flags |= FLAG_MALLOC; - ++psfs->fmt; - ++p; - continue; /* The related conversions follow 'a'. */ - } -#endif /* __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ */ - - for (p = spec_ranges; p_m_spec_chars > *p ; ++p) {} - if (((psfs->dataargtype >> 8) | psfs->flags) - & ~spec_allowed[(int)(p - spec_ranges)] - ) { - goto ERROR_EINVAL; - } - - if ((p_m_spec_chars >= CONV_c) - && (psfs->dataargtype & PA_FLAG_LONG)) { - p_m_spec_chars -= 3; /* lc -> C, ls -> S, l[ -> ?? */ - } - - psfs->conv_num = p_m_spec_chars; - return psfs->fmt - fmt0; - } - if (!*++p) { - ERROR_EINVAL: - __set_errno(EINVAL); - return -1; - } - } while(1); - - assert(0); -} - -#endif -/**********************************************************************/ -#if defined(L_vfscanf) || defined(L_vfwscanf) - -#ifdef __UCLIBC_HAS_WCHAR__ -#ifdef L_vfscanf -static int sc_getc(register struct scan_cookie *sc) -{ - return (getc_unlocked)(sc->fp); /* Disable the macro. */ -} - -static int scan_getwc(register struct scan_cookie *sc) -{ - size_t r; - int width; - wchar_t wc[1]; - char b[1]; - - if (--sc->width < 0) { - sc->ungot_flag |= 2; - return -1; - } - - width = sc->width; /* Preserve width. */ - sc->width = INT_MAX; /* MB_CUR_MAX can invoke a function. */ - - assert(!sc->mb_fail); - - r = (size_t)(-3); - while (__scan_getc(sc) >= 0) { - *b = sc->cc; - - r = mbrtowc(wc, b, 1, &sc->mbstate); - if (((ssize_t) r) >= 0) { /* Successful completion of a wc. */ - sc->wc = *wc; - goto SUCCESS; - } else if (r == ((size_t) -2)) { - /* Potentially valid but incomplete. */ - continue; - } - break; - } - - if (r == ((size_t)(-3))) { /* EOF or ERROR on first read */ - sc->wc = WEOF; - r = (size_t)(-1); - } else { - /* If we reach here, either r == ((size_t)-1) and - * mbrtowc set errno to EILSEQ, or r == ((size_t)-2) - * and stream is in an error state or at EOF with a - * partially complete wchar. */ - __set_errno(EILSEQ); /* In case of incomplete conversion. */ - sc->mb_fail = 1; - } - - SUCCESS: - sc->width = width; /* Restore width. */ - - return (int)((ssize_t) r); -} - -#endif /* L_vfscanf */ - -#ifdef L_vfwscanf - -/* This gets called by __scan_getc. __scan_getc is called by vfwscanf - * when the next wide char is expected to be valid ascii (digits). - */ -static int sc_getc(register struct scan_cookie *sc) -{ - wint_t wc; - - if (__STDIO_STREAM_IS_FAKE_VSWSCANF(sc->fp)) { - if (sc->fp->__bufpos < sc->fp->__bufend) { - wc = *((wchar_t *)(sc->fp->__bufpos)); - sc->fp->__bufpos += sizeof(wchar_t); - } else { - sc->fp->__modeflags |= __FLAG_EOF; - return EOF; - } - } else if ((wc = fgetwc_unlocked(sc->fp)) == WEOF) { - return EOF; - } - - sc->ungot_wflag = 1; - sc->ungot_wchar = wc; - sc->ungot_wchar_width = sc->fp->__ungot_width[0]; - -#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ - if (wc == sc->thousands_sep_wc) { - wc = ','; - } else -#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ -#ifdef __UCLIBC_HAS_FLOATS__ - if (wc == sc->decpt_wc) { - wc = '.'; - } else -#endif /* __UCLIBC_HAS_FLOATS__ */ - if (!__isascii(wc)) { - wc = '?'; - } - sc->wc = sc->ungot_char = wc; - - return (int) wc; -} - -static int scan_getwc(register struct scan_cookie *sc) -{ - wint_t wc; - - sc->wc = WEOF; - - if (--sc->width < 0) { - sc->ungot_flag |= 2; - return -1; - } - - if (sc->ungot_flag == 0) { - if (__STDIO_STREAM_IS_FAKE_VSWSCANF(sc->fp)) { - if (sc->fp->__bufpos < sc->fp->__bufend) { - wc = *((wchar_t *)(sc->fp->__bufpos)); - sc->fp->__bufpos += sizeof(wchar_t); - } else { - sc->ungot_flag |= 2; - return -1; - } - } else if ((wc = fgetwc_unlocked(sc->fp)) == WEOF) { - sc->ungot_flag |= 2; - return -1; - } - sc->ungot_wflag = 1; - sc->ungot_char = wc; - sc->ungot_wchar_width = sc->fp->__ungot_width[0]; - } else { - assert(sc->ungot_flag == 1); - sc->ungot_flag = 0; - } - - ++sc->nread; - sc->wc = sc->ungot_char; - - return 0; -} - - -#endif /* L_vfwscanf */ -#endif /* __UCLIBC_HAS_WCHAR__ */ - -static __inline void kill_scan_cookie(register struct scan_cookie *sc) -{ -#ifdef L_vfscanf - - if (sc->ungot_flag & 1) { -#if !defined(__STDIO_BUFFERS) && !defined(__UCLIBC_HAS_WCHAR__) - if (!__STDIO_STREAM_IS_FAKE_VSSCANF_NB(sc->fp)) { - ungetc(sc->ungot_char, sc->fp); - } -#else - ungetc(sc->ungot_char, sc->fp); -#endif - /* Deal with distiction between user and scanf ungots. */ - if (sc->nread == 0) { /* Only one char was read... app ungot? */ - sc->fp->__ungot[1] = sc->app_ungot; /* restore ungot state. */ - } else { - sc->fp->__ungot[1] = 0; - } - } - -#else - - if ((sc->ungot_flag & 1) && (sc->ungot_wflag & 1) - && !__STDIO_STREAM_IS_FAKE_VSWSCANF(sc->fp) - && (sc->fp->__state.__mask == 0) - ) { - ungetwc(sc->ungot_char, sc->fp); - /* Deal with distiction between user and scanf ungots. */ - if (sc->nread == 0) { /* Only one char was read... app ungot? */ - sc->fp->__ungot[1] = sc->app_ungot; /* restore ungot state. */ - } else { - sc->fp->__ungot[1] = 0; - } - sc->fp->__ungot_width[1] = sc->ungot_wchar_width; - } - -#endif -} - -#ifdef L_vfwscanf -#ifdef __UCLIBC_HAS_FLOATS__ -static const char fake_decpt_str[] = "."; -#endif -#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ -static const char fake_thousands_sep_str[] = ","; -#endif -#endif /* L_vfwscanf */ - - -int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) -{ - const Wuchar *fmt; - unsigned char *b; - - -#ifdef L_vfwscanf - wchar_t wbuf[1]; - wchar_t *wb; -#endif /* L_vfwscanf */ - -#if defined(__UCLIBC_HAS_LOCALE__) && !defined(L_vfwscanf) || !defined(L_vfscanf) - mbstate_t mbstate; -#endif - - struct scan_cookie sc; - psfs_t psfs; - - int i; - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Fix MAX_DIGITS. We do not do binary, so...! -#endif -#define MAX_DIGITS 65 /* Allow one leading 0. */ - unsigned char buf[MAX_DIGITS+2]; -#ifdef L_vfscanf - unsigned char scanset[UCHAR_MAX + 1]; - unsigned char invert; /* Careful! Meaning changes. */ -#endif /* L_vfscanf */ - unsigned char fail; - unsigned char zero_conversions = 1; - __STDIO_AUTO_THREADLOCK_VAR; - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Make checking of the format string in C locale an option. -#endif - /* To support old programs, don't check mb validity if in C locale. */ -#if defined(__UCLIBC_HAS_LOCALE__) && !defined(L_vfwscanf) - /* ANSI/ISO C99 requires format string to be a valid multibyte string - * beginning and ending in its initial shift state. */ - if (((__UCLIBC_CURLOCALE_DATA).encoding) != __ctype_encoding_7_bit) { - const char *p = format; - mbstate.__mask = 0; /* Initialize the mbstate. */ - if (mbsrtowcs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) { - __set_errno(EINVAL); /* Format string is invalid. */ - return 0; - } - } -#endif /* defined(__UCLIBC_HAS_LOCALE__) && !defined(L_vfwscanf) */ - -#if defined(NL_ARGMAX) && (NL_ARGMAX > 0) - psfs.num_pos_args = -1; /* Must start at -1. */ - /* Initialize positional arg ptrs to NULL. */ - memset(psfs.pos_args, 0, sizeof(psfs.pos_args)); -#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */ - - __STDIO_AUTO_THREADLOCK(fp); - - __STDIO_STREAM_VALIDATE(fp); - - __init_scan_cookie(&sc,fp); -#ifdef __UCLIBC_HAS_WCHAR__ - sc.sc_getc = sc_getc; - sc.ungot_wchar_width = sc.fp->__ungot_width[1]; - -#ifdef L_vfwscanf - -#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ - if (*sc.grouping) { - sc.thousands_sep = fake_thousands_sep_str; - sc.tslen = 1; - } -#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ - -#ifdef __UCLIBC_HAS_FLOATS__ - sc.fake_decpt = fake_decpt_str; -#endif /* __UCLIBC_HAS_FLOATS__ */ - -#else /* L_vfwscanf */ - -#ifdef __UCLIBC_HAS_FLOATS__ - sc.fake_decpt = sc.decpt; -#endif /* __UCLIBC_HAS_FLOATS__ */ - -#endif /* L_vfwscanf */ - -#endif /* __UCLIBC_HAS_WCHAR__ */ - psfs.cnt = 0; - - /* Note: If we ever wanted to support non-nice codesets, we - * would really need to do a mb->wc conversion here in the - * vfscanf case. Related changes would have to be made in - * the code that follows... basicly wherever fmt appears. */ - for (fmt = (const Wuchar *) format ; *fmt ; /* ++fmt */) { - - psfs.store = 1; - psfs.flags = 0; -#ifndef NDEBUG - psfs.cur_ptr = NULL; /* Debugging aid. */ -#endif /* NDEBUG */ - - - sc.ungot_flag &= 1; /* Clear (possible fake) EOF. */ - sc.width = psfs.max_width = INT_MAX; - - /* Note: According to the standards, vfscanf does use isspace - * here. So, if we did a mb->wc conversion, we would have to do - * something like - * ((((__uwchar_t)wc) < UCHAR_MAX) && isspace(wc)) - * because wc might not be in the allowed domain. */ - if (ISSPACE(*fmt)) { - do { - ++fmt; - } while (ISSPACE(*fmt)); - --fmt; - psfs.conv_num = CONV_whitespace; - goto DO_WHITESPACE; - } - - if (*fmt == '%') { /* Conversion specification. */ - if (*++fmt == '%') { /* Remember, '%' eats whitespace too. */ - /* Note: The standard says no conversion occurs. - * So do not reset zero_conversions flag. */ - psfs.conv_num = CONV_percent; - goto DO_CONVERSION; - } - - -#ifdef L_vfscanf - psfs.fmt = fmt; -#else /* L_vfscanf */ - { - const __uwchar_t *wf = fmt; - psfs.fmt = b = buf; - - while (*wf && __isascii(*wf) && (b < buf + sizeof(buf) - 1)) { - *b++ = *wf++; - } -#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ -#error this is wrong... we need to ched in __psfs_parse_spec instead since this checks last char in buffer and conversion my have stopped before it. - if ((*b == 'a') && ((*wf == '[') || ((*wf|0x20) == 's'))) { - goto DONE; /* Spec was excessively long. */ - } -#endif /* __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ */ - *b = 0; - if (b == buf) { /* Bad conversion specifier! */ - goto DONE; - } - } -#endif /* L_vfscanf */ - if ((i = __psfs_parse_spec(&psfs)) < 0) { /* Bad conversion specifier! */ - goto DONE; - } - fmt += i; - - if (psfs.store) { -#if defined(NL_ARGMAX) && (NL_ARGMAX > 0) - if (psfs.num_pos_args == -2) { - psfs.cur_ptr = va_arg(arg, void *); - } else { - while (psfs.cur_pos_arg > psfs.num_pos_args) { - psfs.pos_args[++psfs.num_pos_args] = va_arg(arg, void *); - } - psfs.cur_ptr = psfs.pos_args[psfs.cur_pos_arg]; - } -#else /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */ - psfs.cur_ptr = va_arg(arg, void *); -#endif /* defined(NL_ARGMAX) && (NL_ARGMAX > 0) */ - } - - DO_CONVERSION: - /* First, consume white-space if not n, c, [, C, or l[. */ - if ((((1L << CONV_n)|(1L << CONV_C)|(1L << CONV_c) - |(1L << CONV_LEFTBRACKET)|(1L << CONV_leftbracket)) - & (1L << psfs.conv_num)) == 0 - ) { - DO_WHITESPACE: - while ((__scan_getc(&sc) >= 0) -#ifdef L_vfscanf - && isspace(sc.cc) -#else /* L_vfscanf */ - && iswspace(sc.wc) -#endif /* L_vfscanf */ - ) {} - __scan_ungetc(&sc); - if (psfs.conv_num == CONV_whitespace) { - goto NEXT_FMT; - } - } - - sc.width = psfs.max_width; /* Now limit the max width. */ - - if (sc.width == 0) { /* 0 width is forbidden. */ - goto DONE; - } - - - if (psfs.conv_num == CONV_percent) { - goto MATCH_CHAR; - } - - if (psfs.conv_num == CONV_n) { -#ifdef __UCLIBC_MJN3_ONLY__ -#warning CONSIDER: Should %n count as a conversion as far as EOF return value? -#endif -/* zero_conversions = 0; */ - if (psfs.store) { - _store_inttype(psfs.cur_ptr, psfs.dataargtype, - (uintmax_t) sc.nread); - } - goto NEXT_FMT; - } - - if (psfs.conv_num <= CONV_A) { /* pointer, integer, or float spec */ - int r = __psfs_do_numeric(&psfs, &sc); -#ifndef L_vfscanf - if (sc.ungot_wflag == 1) { /* fix up '?', '.', and ',' hacks */ - sc.cc = sc.ungot_char = sc.ungot_wchar; - } -#endif - if (r != -1) { /* Either success or a matching failure. */ - zero_conversions = 0; - } - if (r < 0) { - goto DONE; - } - goto NEXT_FMT; - } - - /* Do string conversions here since they are not common code. */ - - -#ifdef L_vfscanf - - if -#ifdef __UCLIBC_HAS_WCHAR__ - (psfs.conv_num >= CONV_LEFTBRACKET) -#else /* __UCLIBC_HAS_WCHAR__ */ - (psfs.conv_num >= CONV_c) -#endif /* __UCLIBC_HAS_WCHAR__ */ - { - b = (psfs.store ? ((unsigned char *) psfs.cur_ptr) : buf); - fail = 1; - - - if (psfs.conv_num == CONV_c) { - if (sc.width == INT_MAX) { - sc.width = 1; - } - - while (__scan_getc(&sc) >= 0) { - zero_conversions = 0; - *b = sc.cc; - b += psfs.store; - } - __scan_ungetc(&sc); - if (sc.width > 0) { /* Failed to read all required. */ - goto DONE; - } - psfs.cnt += psfs.store; - goto NEXT_FMT; - } - - if (psfs.conv_num == CONV_s) { - /* Yes, believe it or not, a %s conversion can store nuls. */ - while ((__scan_getc(&sc) >= 0) && !isspace(sc.cc)) { - zero_conversions = 0; - *b = sc.cc; - b += psfs.store; - fail = 0; - } - } else { -#ifdef __UCLIBC_HAS_WCHAR__ - assert((psfs.conv_num == CONV_LEFTBRACKET) || \ - (psfs.conv_num == CONV_leftbracket)); -#else /* __UCLIBC_HAS_WCHAR__ */ - assert((psfs.conv_num == CONV_leftbracket)); -#endif /* __UCLIBC_HAS_WCHAR__ */ - - invert = 0; - - if (*++fmt == '^') { - ++fmt; - invert = 1; - } - memset(scanset, invert, sizeof(scanset)); - invert = 1-invert; - - if (*fmt == ']') { - scanset[(int)(']')] = invert; - ++fmt; - } - - while (*fmt != ']') { - if (!*fmt) { /* No closing ']'. */ - goto DONE; - } - if ((*fmt == '-') && (fmt[1] != ']') - && (fmt[-1] < fmt[1]) /* sorted? */ - ) { /* range */ - ++fmt; - i = fmt[-2]; - /* Note: scanset[i] should already have been done - * in the previous iteration. */ - do { - scanset[++i] = invert; - } while (i < *fmt); - /* Safe to fall through, and a bit smaller. */ - } - /* literal char */ - scanset[(int) *fmt] = invert; - ++fmt; - } - -#ifdef __UCLIBC_HAS_WCHAR__ - if (psfs.conv_num == CONV_LEFTBRACKET) { - goto DO_LEFTBRACKET; - } -#endif /* __UCLIBC_HAS_WCHAR__ */ - - - while (__scan_getc(&sc) >= 0) { - zero_conversions = 0; - if (!scanset[sc.cc]) { - break; - } - *b = sc.cc; - b += psfs.store; - fail = 0; - } - } - /* Common tail for processing of %s and %[. */ - - __scan_ungetc(&sc); - if (fail) { /* nothing stored! */ - goto DONE; - } - *b = 0; /* Nul-terminate string. */ - psfs.cnt += psfs.store; - goto NEXT_FMT; - } - -#ifdef __UCLIBC_HAS_WCHAR__ - DO_LEFTBRACKET: /* Need to do common wide init. */ - if (psfs.conv_num >= CONV_C) { - wchar_t wbuf[1]; - wchar_t *wb; - - sc.mbstate.__mask = 0; - - wb = (psfs.store ? ((wchar_t *) psfs.cur_ptr) : wbuf); - fail = 1; - - if (psfs.conv_num == CONV_C) { - if (sc.width == INT_MAX) { - sc.width = 1; - } - - while (scan_getwc(&sc) >= 0) { - zero_conversions = 0; - assert(sc.width >= 0); - *wb = sc.wc; - wb += psfs.store; - } - - __scan_ungetc(&sc); - if (sc.width > 0) { /* Failed to read all required. */ - goto DONE; - } - psfs.cnt += psfs.store; - goto NEXT_FMT; - } - - - if (psfs.conv_num == CONV_S) { - /* Yes, believe it or not, a %s conversion can store nuls. */ - while (scan_getwc(&sc) >= 0) { - zero_conversions = 0; - if ((((__uwchar_t)(sc.wc)) <= UCHAR_MAX) && isspace(sc.wc)) { - break; - } - *wb = sc.wc; - wb += psfs.store; - fail = 0; - } - } else { - assert(psfs.conv_num == CONV_LEFTBRACKET); - - while (scan_getwc(&sc) >= 0) { - zero_conversions = 0; - if (((__uwchar_t) sc.wc) <= UCHAR_MAX) { - if (!scanset[sc.wc]) { - break; - } - } else if (invert) { - break; - } - *wb = sc.wc; - wb += psfs.store; - fail = 0; - } - } - /* Common tail for processing of %ls and %l[. */ - - __scan_ungetc(&sc); - if (fail || sc.mb_fail) { /* Nothing stored or mb error. */ - goto DONE; - } - *wb = 0; /* Nul-terminate string. */ - psfs.cnt += psfs.store; - goto NEXT_FMT; - - } - -#endif /* __UCLIBC_HAS_WCHAR__ */ -#else /* L_vfscanf */ - - if (psfs.conv_num >= CONV_C) { - b = buf; - wb = wbuf; - if (psfs.conv_num >= CONV_c) { - mbstate.__mask = 0; /* Initialize the mbstate. */ - if (psfs.store) { - b = (unsigned char *) psfs.cur_ptr; - } - } else { - if (psfs.store) { - wb = (wchar_t *) psfs.cur_ptr; - } - } - fail = 1; - - - if ((psfs.conv_num == CONV_C) || (psfs.conv_num == CONV_c)) { - if (sc.width == INT_MAX) { - sc.width = 1; - } - - while (scan_getwc(&sc) >= 0) { - zero_conversions = 0; - if (psfs.conv_num == CONV_C) { - *wb = sc.wc; - wb += psfs.store; - } else { - i = wcrtomb(b, sc.wc, &mbstate); - if (i < 0) { /* Conversion failure. */ - goto DONE_DO_UNGET; - } - if (psfs.store) { - b += i; - } - } - } - __scan_ungetc(&sc); - if (sc.width > 0) { /* Failed to read all required. */ - goto DONE; - } - psfs.cnt += psfs.store; - goto NEXT_FMT; - } - - if ((psfs.conv_num == CONV_S) || (psfs.conv_num == CONV_s)) { - /* Yes, believe it or not, a %s conversion can store nuls. */ - while (scan_getwc(&sc) >= 0) { - zero_conversions = 0; - if (iswspace(sc.wc)) { - break; - } - if (psfs.conv_num == CONV_S) { - *wb = sc.wc; - wb += psfs.store; - } else { - i = wcrtomb(b, sc.wc, &mbstate); - if (i < 0) { /* Conversion failure. */ - goto DONE_DO_UNGET; - } - if (psfs.store) { - b += i; - } - } - fail = 0; - } - } else { - const wchar_t *sss; - const wchar_t *ssp; - unsigned char invert = 0; - - assert((psfs.conv_num == CONV_LEFTBRACKET) - || (psfs.conv_num == CONV_leftbracket)); - - if (*++fmt == '^') { - ++fmt; - invert = 1; - } - sss = (const wchar_t *) fmt; - if (*fmt == ']') { - ++fmt; - } - while (*fmt != ']') { - if (!*fmt) { /* No closing ']'. */ - goto DONE; - } - if ((*fmt == '-') && (fmt[1] != ']') - && (fmt[-1] < fmt[1]) /* sorted? */ - ) { /* range */ - ++fmt; - } - ++fmt; - } - /* Ok... a valid scanset spec. */ - - while (scan_getwc(&sc) >= 0) { - zero_conversions = 0; - ssp = sss; - do { /* We know sss < fmt. */ - if (*ssp == '-') { /* possible range... */ - /* Note: We accept a-c-e (ordered) as - * equivalent to a-e. */ - if (ssp > sss) { - if ((++ssp < (const wchar_t *) fmt) - && (ssp[-2] < *ssp) /* sorted? */ - ) { /* yes */ - if ((sc.wc >= ssp[-2]) - && (sc.wc <= *ssp)) { - break; - } - continue; /* not in range */ - } - --ssp; /* oops... '-' at end, so back up */ - } - /* false alarm... a literal '-' */ - } - if (sc.wc == *ssp) { /* Matched literal char. */ - break; - } - } while (++ssp < (const wchar_t *) fmt); - - if ((ssp == (const wchar_t *) fmt) ^ invert) { - /* no match and not inverting - * or match and inverting */ - break; - } - if (psfs.conv_num == CONV_LEFTBRACKET) { - *wb = sc.wc; - wb += psfs.store; - } else { - i = wcrtomb(b, sc.wc, &mbstate); - if (i < 0) { /* Conversion failure. */ - goto DONE_DO_UNGET; - } - if (psfs.store) { - b += i; - } - } - fail = 0; - } - } - /* Common tail for processing of %s and %[. */ - - __scan_ungetc(&sc); - if (fail) { /* nothing stored! */ - goto DONE; - } - *wb = 0; /* Nul-terminate string. */ - *b = 0; - psfs.cnt += psfs.store; - goto NEXT_FMT; - } - -#endif /* L_vfscanf */ - - assert(0); - goto DONE; - } /* conversion specification */ - - MATCH_CHAR: - if (__scan_getc(&sc) != *fmt) { -#ifdef L_vfwscanf - DONE_DO_UNGET: -#endif /* L_vfwscanf */ - __scan_ungetc(&sc); - goto DONE; - } - - NEXT_FMT: - ++fmt; - if (__FERROR_UNLOCKED(fp)) { - break; - } - } - - DONE: - if (__FERROR_UNLOCKED(fp) || (*fmt && zero_conversions && __FEOF_UNLOCKED(fp))) { - psfs.cnt = EOF; /* Yes, vfwscanf also returns EOF. */ - } - - kill_scan_cookie(&sc); - - __STDIO_STREAM_VALIDATE(fp); - - __STDIO_AUTO_THREADUNLOCK(fp); - - return psfs.cnt; -} -libc_hidden_def(VFSCANF) -#endif -/**********************************************************************/ -#ifdef L___psfs_do_numeric - -static const unsigned char spec_base[] = SPEC_BASE; -static const unsigned char nil_string[] = "(nil)"; - -int attribute_hidden __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc) -{ - unsigned char *b; - const unsigned char *p; - -#ifdef __UCLIBC_HAS_FLOATS__ - int exp_adjust = 0; -#endif -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Fix MAX_DIGITS. We do not do binary, so...! -#warning TODO: Fix buf! -#endif -#define MAX_DIGITS 65 /* Allow one leading 0. */ - unsigned char buf[MAX_DIGITS+2+ 100]; - unsigned char usflag, base; - unsigned char nonzero = 0; - unsigned char seendigit = 0; - - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning CONSIDER: What should be returned for an invalid conversion specifier? -#endif -#ifndef __UCLIBC_HAS_FLOATS__ - if (psfs->conv_num > CONV_i) { /* floating point */ - goto DONE; - } -#endif - - base = spec_base[psfs->conv_num - CONV_p]; - usflag = (psfs->conv_num <= CONV_u); /* (1)0 if (un)signed */ - b = buf; - - - if (psfs->conv_num == CONV_p) { /* Pointer */ - p = nil_string; - do { - if ((__scan_getc(sc) < 0) || (*p != sc->cc)) { - __scan_ungetc(sc); - if (p > nil_string) { - /* We matched at least the '(' so even if we - * are at eof, we can not match a pointer. */ - return -2; /* Matching failure */ - } - break; - } - if (!*++p) { /* Matched (nil), so no unget necessary. */ - if (psfs->store) { - ++psfs->cnt; - _store_inttype(psfs->cur_ptr, psfs->dataargtype, - (uintmax_t) NULL); - } - return 0; - } - } while (1); - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning CONSIDER: Should we require a 0x prefix and disallow +/- for pointer %p? -#endif /* __UCLIBC_MJN3_ONLY__ */ - } - - __scan_getc(sc); - if (sc->cc < 0) { - return -1; /* Input failure (nothing read yet). */ - } - - if ((sc->cc == '+') || (sc->cc == '-')) { /* Handle leading sign.*/ - *b++ = sc->cc; - __scan_getc(sc); - } - - if ((base & 0xef) == 0) { /* 0xef is ~16, so 16 or 0. */ - if (sc->cc == '0') { /* Possibly set base and handle prefix. */ - __scan_getc(sc); - if ((sc->cc|0x20) == 'x') { /* Assumes ascii.. x or X. */ - if (__scan_getc(sc) < 0) { - /* Either EOF or error (including wc outside char range). - * If EOF or error, this is a matching failure (we read 0x). - * If wc outside char range, this is also a matching failure. - * Hence, we do an unget (although not really necessary here - * and fail. */ - goto DONE_DO_UNGET; /* matching failure */ - } - base = 16; /* Base 16 for sure now. */ -#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ - /* The prefix is required for hexadecimal floats. */ - *b++ = '0'; - *b++ = 'x'; -#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ - } else { /* oops... back up */ - __scan_ungetc(sc); - sc->cc = '0'; /* NASTY HACK! */ - - base = (base >> 1) + 8; /* 0->8, 16->16. no 'if' */ -#ifdef __UCLIBC_HAS_FLOATS__ - if (psfs->conv_num > CONV_i) { /* floating point */ - base = 10; - } -#endif - } - } else if (!base) { - base = 10; - } - } - - /***************** digit grouping **********************/ -#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ - - if ((psfs->flags & FLAG_THOUSANDS) && (base == 10) - && *(p = sc->grouping) - ) { - - int nblk1, nblk2, nbmax, lastblock, pass, i; - - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning CONSIDER: Should we initalize the grouping blocks in __init_scan_cookie()? -#endif /* __UCLIBC_MJN3_ONLY__ */ - nbmax = nblk2 = nblk1 = *p; - if (*++p) { - nblk2 = *p; - if (nbmax < nblk2) { - nbmax = nblk2; - } - assert(!p[1]); - } - - /* Note: for printf, if 0 and \' flags appear then - * grouping is done before 0-padding. Should we - * strip leading 0's first? Or add a 0 flag? */ - - /* For vfwscanf, sc_getc translates, so the value of sc->cc is - * either EOF or a char. */ - - if (!__isdigit_char_or_EOF(sc->cc)) { /* No starting digit! */ -#ifdef __UCLIBC_HAS_FLOATS__ - if (psfs->conv_num > CONV_i) { /* floating point */ - goto NO_STARTING_DIGIT; - } -#endif - goto DONE_DO_UNGET; - } - - if (sc->cc == '0') { - seendigit = 1; - *b++ = '0'; /* Store the first 0. */ -#ifdef __UCLIBC_MJN3_ONLY__ -#warning CONSIDER: Should leading 0s be skipped before digit grouping? (printf 0 pad) -#endif /* __UCLIBC_MJN3_ONLY__ */ -#if 0 - do { /* But ignore all subsequent 0s. */ - __scan_getc(sc); - } while (sc->cc == '0'); -#endif - } - pass = 0; - lastblock = 0; - do { - i = 0; - while (__isdigit_char_or_EOF(sc->cc)) { - seendigit = 1; - if (i == nbmax) { /* too many digits for a block */ -#ifdef __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__ - if (!pass) { /* treat as nongrouped */ - if (nonzero) { - goto DO_NO_GROUP; - } - goto DO_TRIM_LEADING_ZEROS; - } -#endif - if (nbmax > nblk1) { - goto DONE_DO_UNGET; /* matching failure */ - } - goto DONE_GROUPING_DO_UNGET; /* nbmax == nblk1 */ - } - ++i; - - if (nonzero || (sc->cc != '0')) { - if (b < buf + MAX_DIGITS) { - *b++ = sc->cc; - nonzero = 1; -#ifdef __UCLIBC_HAS_FLOATS__ - } else { - ++exp_adjust; -#endif - } - } - - __scan_getc(sc); - } - - if (i) { /* we saw digits digits */ - if ((i == nblk2) || ((i < nblk2) && !pass)) { - /* (possible) outer grp */ - p = sc->thousands_sep; - if (*p == sc->cc) { /* first byte matches... */ - /* so check if grouping mb char */ - /* Since 1st matched, either match or fail now - * unless EOF (yuk) */ - __scan_getc(sc); - MBG_LOOP: - if (!*++p) { /* is a grouping mb char */ - lastblock = i; - ++pass; - continue; - } - if (*p == sc->cc) { - __scan_getc(sc); - goto MBG_LOOP; - } - /* bad grouping mb char! */ - __scan_ungetc(sc); - if ((sc->cc >= 0) || (p > sc->thousands_sep + 1)) { -#ifdef __UCLIBC_HAS_FLOATS__ - /* We failed to match a thousep mb char, and - * we've read too much to recover. But if - * this is a floating point conversion and - * the initial portion of the decpt mb char - * matches, then we may still be able to - * recover. */ - int k = p - sc->thousands_sep - 1; - - if ((psfs->conv_num > CONV_i) /* float conversion */ - && (!pass || (i == nblk1)) /* possible last */ - && !memcmp(sc->thousands_sep, sc->fake_decpt, k) - /* and prefix matched, so could be decpt */ - ) { - __scan_getc(sc); - p = sc->fake_decpt + k; - do { - if (!*++p) { - strcpy(b, sc->decpt); - b += sc->decpt_len; - goto GOT_DECPT; - } - if (*p != sc->cc) { - __scan_ungetc(sc); - break; /* failed */ - } - __scan_getc(sc); - } while (1); - } -#endif /* __UCLIBC_HAS_FLOATS__ */ - goto DONE; - } - /* was EOF and 1st, so recoverable. */ - } - } - if ((i == nblk1) || ((i < nblk1) && !pass)) { - /* got an inner group */ - goto DONE_GROUPING_DO_UNGET; - } - goto DONE_DO_UNGET; /* Matching failure. */ - } /* i != 0 */ - - assert(pass); - - goto DONE_DO_UNGET; - } while (1); - - assert(0); /* Should never get here. */ - } - -#endif /***************** digit grouping **********************/ - - /* Not grouping so first trim all but one leading 0. */ -#ifdef __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__ - DO_TRIM_LEADING_ZEROS: -#endif /* __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__ */ - if (sc->cc == '0') { - seendigit = 1; - *b++ = '0'; /* Store the first 0. */ - do { /* But ignore all subsequent 0s. */ - __scan_getc(sc); - } while (sc->cc == '0'); - } - -#ifdef __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__ - DO_NO_GROUP: -#endif /* __UCLIBC_HAS_SCANF_LENIENT_DIGIT_GROUPING__ */ - /* At this point, we're ready to start reading digits. */ - -#define valid_digit(cc,base) (isxdigit(cc) && ((base == 16) || (cc - '0' < base))) - - while (valid_digit(sc->cc,base)) { /* Now for significant digits.*/ - if (b - buf < MAX_DIGITS) { - nonzero = seendigit = 1; /* Set nonzero too 0s trimmed above. */ - *b++ = sc->cc; -#ifdef __UCLIBC_HAS_FLOATS__ - } else { - ++exp_adjust; -#endif - } - __scan_getc(sc); - } - -#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ - DONE_GROUPING_DO_UNGET: -#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ - if (psfs->conv_num <= CONV_i) { /* integer conversion */ - __scan_ungetc(sc); - *b = 0; /* null-terminate */ - if (!seendigit) { - goto DONE; /* No digits! */ - } - if (psfs->store) { - if (*buf == '-') { - usflag = 0; - } - ++psfs->cnt; - _store_inttype(psfs->cur_ptr, psfs->dataargtype, - (uintmax_t) STRTOUIM(buf, NULL, base, 1-usflag)); - } - return 0; - } - -#ifdef __UCLIBC_HAS_FLOATS__ - - /* At this point, we have everything left of the decimal point or exponent. */ -#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ - NO_STARTING_DIGIT: -#endif - p = sc->fake_decpt; - do { - if (!*p) { - strcpy(b, sc->decpt); - b += sc->decpt_len; - break; - } - if (*p != sc->cc) { - if (p > sc->fake_decpt) { - goto DONE_DO_UNGET; /* matching failure (read some of decpt) */ - } - goto DO_DIGIT_CHECK; - } - ++p; - __scan_getc(sc); - } while (1); - -#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ - GOT_DECPT: -#endif - if (!nonzero) { - if (sc->cc == '0') { - assert(exp_adjust == 0); - *b++ = '0'; - ++exp_adjust; - seendigit = 1; - do { - --exp_adjust; - __scan_getc(sc); - } while (sc->cc == '0'); - } - } - - while (valid_digit(sc->cc,base)) { /* Process fractional digits.*/ - if (b - buf < MAX_DIGITS) { - seendigit = 1; - *b++ = sc->cc; - } - __scan_getc(sc); - } - - DO_DIGIT_CHECK: - /* Hmm... no decimal point. */ - if (!seendigit) { - static const unsigned char nan_inf_str[] = "an\0nfinity"; - - if (base == 16) { /* We had a prefix, but no digits! */ - goto DONE_DO_UNGET; /* matching failure */ - } - - /* Avoid tolower problems for INFINITY in the tr_TR locale. (yuk)*/ -#undef TOLOWER -#define TOLOWER(C) ((C)|0x20) - - switch (TOLOWER(sc->cc)) { - case 'i': - p = nan_inf_str + 3; - break; - case 'n': - p = nan_inf_str; - break; - default: - /* No digits and not inf or nan. */ - goto DONE_DO_UNGET; - } - - *b++ = sc->cc; - - do { - __scan_getc(sc); - if (TOLOWER(sc->cc) == *p) { - *b++ = sc->cc; - ++p; - continue; - } - if (!*p || (p == nan_inf_str + 5)) { /* match nan/infinity or inf */ - goto GOT_FLOAT; - } - /* Unrecoverable. Even if on 1st char, we had no digits. */ - goto DONE_DO_UNGET; - } while (1); - } - - /* If we get here, we had some digits. */ - - if ( -#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ - ((base == 16) && (((sc->cc)|0x20) == 'p')) || -#endif - (((sc->cc)|0x20) == 'e') - ) { /* Process an exponent. */ - *b++ = sc->cc; - - __scan_getc(sc); - if (sc->cc < 0) { - goto DONE_DO_UNGET; /* matching failure.. no exponent digits */ - } - - if ((sc->cc == '+') || (sc->cc == '-')) { /* Signed exponent? */ - *b++ = sc->cc; - __scan_getc(sc); - } - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Fix MAX_EXP_DIGITS! -#endif -#define MAX_EXP_DIGITS 20 - assert(seendigit); - seendigit = 0; - nonzero = 0; - - if (sc->cc == '0') { - seendigit = 1; - *b++ = '0'; - do { - __scan_getc(sc); - } while (sc->cc == '0'); - } - - while (__isdigit_char_or_EOF(sc->cc)) { /* Exponent digits (base 10).*/ - if (seendigit < MAX_EXP_DIGITS) { - ++seendigit; - *b++ = sc->cc; - } - __scan_getc(sc); - } - - if (!seendigit) { /* No digits. Unrecoverable. */ - goto DONE_DO_UNGET; - } - } - - - GOT_FLOAT: - *b = 0; - { - __fpmax_t x; - char *e; - x = __strtofpmax(buf, &e, exp_adjust); - assert(!*e); - if (psfs->store) { - if (psfs->dataargtype & PA_FLAG_LONG_LONG) { - *((long double *)psfs->cur_ptr) = (long double) x; - } else if (psfs->dataargtype & PA_FLAG_LONG) { - *((double *)psfs->cur_ptr) = (double) x; - } else { - *((float *)psfs->cur_ptr) = (float) x; - } - ++psfs->cnt; - } - __scan_ungetc(sc); - return 0; - } -#endif /* __UCLIBC_HAS_FLOATS__ */ - - DONE_DO_UNGET: - __scan_ungetc(sc); - DONE: - return -2; /* Matching failure. */ - -} -#endif -/**********************************************************************/ diff --git a/libc/stdio/vfprintf.c b/libc/stdio/vfprintf.c deleted file mode 100644 index a32960f2e..000000000 --- a/libc/stdio/vfprintf.c +++ /dev/null @@ -1,1951 +0,0 @@ -/* Copyright (C) 2002-2004 Manuel Novoa III - * My stdio library for linux and (soon) elks. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* This code needs a lot of clean up. Some of that is on hold until uClibc - * gets a better configuration system (on Erik's todo list). - * The other cleanup will take place during the implementation/integration of - * the wide char (un)formatted i/o functions which I'm currently working on. - */ - -/* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! - * - * This code is currently under development. Also, I plan to port - * it to elks which is a 16-bit environment with a fairly limited - * compiler. Therefore, please refrain from modifying this code - * and, instead, pass any bug-fixes, etc. to me. Thanks. Manuel - * - * ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! */ - - -/* April 1, 2002 - * Initialize thread locks for fake files in vsnprintf and vdprintf. - * reported by Erik Andersen (andersen@codepoet.com) - * Fix an arg promotion handling bug in _do_one_spec for %c. - * reported by Ilguiz Latypov - * - * May 10, 2002 - * Remove __isdigit and use new ctype.h version. - * Add conditional setting of QUAL_CHARS for size_t and ptrdiff_t. - * - * Aug 16, 2002 - * Fix two problems that showed up with the python 2.2.1 tests; one - * involving %o and one involving %f. - * - * Oct 28, 2002 - * Fix a problem in vasprintf (reported by vodz a while back) when built - * without custom stream support. In that case, it is necessary to do - * a va_copy. - * Make sure each va_copy has a matching va_end, as required by C99. - * - * Nov 4, 2002 - * Add locale-specific grouping support for integer decimal conversion. - * Add locale-specific decimal point support for floating point conversion. - * Note: grouping will have to wait for _dtostr() rewrite. - * Add printf wchar support for %lc (%C) and %ls (%S). - * Require printf format strings to be valid multibyte strings beginning and - * ending in their initial shift state, as per the stds. - * - * Nov 21, 2002 - * Add *wprintf functions. Currently they don't support floating point - * conversions. That will wait until the rewrite of _dtostr. - * - * Aug 1, 2003 - * Optional hexadecimal float notation support for %a/%A. - * Floating point output now works for *wprintf. - * Support for glibc locale-specific digit grouping for floats. - * Misc bug fixes. - * - * Aug 31, 2003 - * Fix precision bug for %g conversion specifier when using %f style. - * - * Sep 5, 2003 - * Implement *s*scanf for the non-buffered stdio case with old_vfprintf. - * - * Sep 23, 2003 - * vfprintf was not always checking for narrow stream orientation. - */ - -/* TODO: - * - * Should we validate that *printf format strings are valid multibyte - * strings in the current locale? ANSI/ISO C99 seems to imply this - * and Plauger's printf implementation in his Standard C Library book - * treats this as an error. - */ - -#define _ISOC99_SOURCE /* for ULLONG primarily... */ -#include -#include "_stdio.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define __PRINTF_INFO_NO_BITFIELD -#include - -#ifdef __UCLIBC_HAS_THREADS__ -#include -#include -#endif /* __UCLIBC_HAS_THREADS__ */ - -#ifdef __UCLIBC_HAS_WCHAR__ -#include -#endif /* __UCLIBC_HAS_WCHAR__ */ - -#include -#include - -libc_hidden_proto(memcpy) -libc_hidden_proto(memset) -libc_hidden_proto(strlen) -libc_hidden_proto(strnlen) -libc_hidden_proto(__glibc_strerror_r) -libc_hidden_proto(fputs_unlocked) -libc_hidden_proto(abort) -#ifdef __UCLIBC_HAS_WCHAR__ -libc_hidden_proto(wcslen) -libc_hidden_proto(wcsnlen) -libc_hidden_proto(mbsrtowcs) -libc_hidden_proto(wcsrtombs) -libc_hidden_proto(btowc) -libc_hidden_proto(wcrtomb) -libc_hidden_proto(fputws) -#endif - -/* Some older or broken gcc toolchains define LONG_LONG_MAX but not - * LLONG_MAX. Since LLONG_MAX is part of the standard, that's what - * we use. So complain if we do not have it but should. - */ -#if !defined(LLONG_MAX) && defined(LONG_LONG_MAX) -#error Apparently, LONG_LONG_MAX is defined but LLONG_MAX is not. You need to fix your toolchain headers to support the standard macros for (unsigned) long long. -#endif - -/**********************************************************************/ -/* These provide some control over printf's feature set */ - -/* This is undefined below depeding on uClibc's configuration. */ -#define __STDIO_PRINTF_FLOAT 1 - -/* Now controlled by uClibc_stdio.h. */ -/* #define __UCLIBC_HAS_PRINTF_M_SPEC__ */ - - -/**********************************************************************/ - -#if defined(__UCLIBC__) && !defined(__UCLIBC_HAS_FLOATS__) -#undef __STDIO_PRINTF_FLOAT -#endif - -#ifdef __BCC__ -#undef __STDIO_PRINTF_FLOAT -#endif - -#ifdef __STDIO_PRINTF_FLOAT -#include -#include -#else /* __STDIO_PRINTF_FLOAT */ -#undef L__fpmaxtostr -#endif /* __STDIO_PRINTF_FLOAT */ - - -#undef __STDIO_HAS_VSNPRINTF -#if defined(__STDIO_BUFFERS) || defined(__USE_OLD_VFPRINTF__) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) -#define __STDIO_HAS_VSNPRINTF 1 -#endif - -/**********************************************************************/ - -/* Now controlled by uClibc_stdio.h. */ -/* #define __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ - -/* TODO -- move these to a configuration section? */ -#define MAX_FIELD_WIDTH 4095 - -#ifdef __UCLIBC_MJN3_ONLY__ -#ifdef L_register_printf_function -/* emit only once */ -#warning WISHLIST: Make MAX_USER_SPEC configurable? -#warning WISHLIST: Make MAX_ARGS_PER_SPEC configurable? -#endif -#endif /* __UCLIBC_MJN3_ONLY__ */ - -#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ - -#define MAX_USER_SPEC 10 -#define MAX_ARGS_PER_SPEC 5 - -#else /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ - -#undef MAX_USER_SPEC -#define MAX_ARGS_PER_SPEC 1 - -#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ - -#if MAX_ARGS_PER_SPEC < 1 -#error MAX_ARGS_PER_SPEC < 1! -#undef MAX_ARGS_PER_SPEC -#define MAX_ARGS_PER_SPEC 1 -#endif - -#if defined(NL_ARGMAX) && (NL_ARGMAX < 9) -#error NL_ARGMAX < 9! -#endif - -#if defined(NL_ARGMAX) && (NL_ARGMAX >= (MAX_ARGS_PER_SPEC + 2)) -#define MAX_ARGS NL_ARGMAX -#else -/* N for spec itself, plus 1 each for width and precision */ -#define MAX_ARGS (MAX_ARGS_PER_SPEC + 2) -#endif - -/**********************************************************************/ - -#define __PA_FLAG_INTMASK \ - (__PA_FLAG_CHAR|PA_FLAG_SHORT|__PA_FLAG_INT|PA_FLAG_LONG|PA_FLAG_LONG_LONG) - -#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ -extern printf_function _custom_printf_handler[MAX_USER_SPEC] attribute_hidden; -extern printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC] attribute_hidden; -extern char *_custom_printf_spec attribute_hidden; -#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ - -/**********************************************************************/ - -#define SPEC_FLAGS " +0-#'I" -enum { - FLAG_SPACE = 0x01, - FLAG_PLUS = 0x02, /* must be 2 * FLAG_SPACE */ - FLAG_ZERO = 0x04, - FLAG_MINUS = 0x08, /* must be 2 * FLAG_ZERO */ - FLAG_HASH = 0x10, - FLAG_THOUSANDS = 0x20, - FLAG_I18N = 0x40, /* only works for d, i, u */ - FLAG_WIDESTREAM = 0x80 -}; - -/**********************************************************************/ - -/* float layout 01234567890123456789 TODO: B?*/ -#define SPEC_CHARS "npxXoudifFeEgGaACScs" -enum { - CONV_n = 0, - CONV_p, - CONV_x, CONV_X, CONV_o, CONV_u, CONV_d, CONV_i, - CONV_f, CONV_F, CONV_e, CONV_E, CONV_g, CONV_G, CONV_a, CONV_A, - CONV_C, CONV_S, CONV_c, CONV_s, -#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ - CONV_m, -#endif - CONV_custom0 /* must be last */ -}; - -/* p x X o u d i */ -#define SPEC_BASE { 16, 16, 16, 8, 10, 10, 10 } - -#define SPEC_RANGES { CONV_n, CONV_p, CONV_i, CONV_A, \ - CONV_C, CONV_S, CONV_c, CONV_s, CONV_custom0 } - -#define SPEC_OR_MASK { \ - /* n */ (PA_FLAG_PTR|PA_INT), \ - /* p */ PA_POINTER, \ - /* oxXudi */ PA_INT, \ - /* fFeEgGaA */ PA_DOUBLE, \ - /* C */ PA_WCHAR, \ - /* S */ PA_WSTRING, \ - /* c */ PA_CHAR, \ - /* s */ PA_STRING, \ -} - -#define SPEC_AND_MASK { \ - /* n */ (PA_FLAG_PTR|__PA_INTMASK), \ - /* p */ PA_POINTER, \ - /* oxXudi */ (__PA_INTMASK), \ - /* fFeEgGaA */ (PA_FLAG_LONG_DOUBLE|PA_DOUBLE), \ - /* C */ (PA_WCHAR), \ - /* S */ (PA_WSTRING), \ - /* c */ (PA_CHAR), \ - /* s */ (PA_STRING), \ -} - -/**********************************************************************/ -/* - * In order to ease translation to what arginfo and _print_info._flags expect, - * we map: 0:int 1:char 2:longlong 4:long 8:short - * and then _flags |= (((q << 7) + q) & 0x701) and argtype |= (_flags & 0x701) - */ - -/* TODO -- Fix the table below to take into account stdint.h. */ -/* #ifndef LLONG_MAX */ -/* #error fix QUAL_CHARS for no long long! Affects 'L', 'j', 'q', 'll'. */ -/* #else */ -/* #if LLONG_MAX != INTMAX_MAX */ -/* #error fix QUAL_CHARS intmax_t entry 'j'! */ -/* #endif */ -/* #endif */ - -#ifdef PDS -#error PDS already defined! -#endif -#ifdef SS -#error SS already defined! -#endif -#ifdef IMS -#error IMS already defined! -#endif - -#if PTRDIFF_MAX == INT_MAX -#define PDS 0 -#elif PTRDIFF_MAX == LONG_MAX -#define PDS 4 -#elif defined(LLONG_MAX) && (PTRDIFF_MAX == LLONG_MAX) -#define PDS 8 -#else -#error fix QUAL_CHARS ptrdiff_t entry 't'! -#endif - -#if SIZE_MAX == UINT_MAX -#define SS 0 -#elif SIZE_MAX == ULONG_MAX -#define SS 4 -#elif defined(LLONG_MAX) && (SIZE_MAX == ULLONG_MAX) -#define SS 8 -#else -#error fix QUAL_CHARS size_t entries 'z', 'Z'! -#endif - -#if INTMAX_MAX == INT_MAX -#define IMS 0 -#elif INTMAX_MAX == LONG_MAX -#define IMS 4 -#elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX) -#define IMS 8 -#else -#error fix QUAL_CHARS intmax_t entry 'j'! -#endif - -#define QUAL_CHARS { \ - /* j:(u)intmax_t z:(s)size_t t:ptrdiff_t \0:int */ \ - /* q:long_long Z:(s)size_t */ \ - 'h', 'l', 'L', 'j', 'z', 't', 'q', 'Z', 0, \ - 2, 4, 8, IMS, SS, PDS, 8, SS, 0, /* TODO -- fix!!! */\ - 1, 8 \ -} - -/**********************************************************************/ - -#ifdef __STDIO_VA_ARG_PTR -#ifdef __BCC__ -#define __va_arg_ptr(ap,type) (((type *)(ap += sizeof(type))) - 1) -#endif - -#if 1 -#ifdef __GNUC__ -/* TODO -- need other than for 386 as well! */ - -#ifndef __va_rounded_size -#define __va_rounded_size(TYPE) \ - (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int)) -#endif -#define __va_arg_ptr(AP, TYPE) \ - (AP = (va_list) ((char *) (AP) + __va_rounded_size (TYPE)), \ - ((void *) ((char *) (AP) - __va_rounded_size (TYPE)))) -#endif -#endif -#endif /* __STDIO_VA_ARG_PTR */ - -#ifdef __va_arg_ptr -#define GET_VA_ARG(AP,F,TYPE,ARGS) (*(AP) = __va_arg_ptr(ARGS,TYPE)) -#define GET_ARG_VALUE(AP,F,TYPE) (*((TYPE *)(*(AP)))) -#else -typedef union { - wchar_t wc; - unsigned int u; - unsigned long ul; -#ifdef ULLONG_MAX - unsigned long long ull; -#endif -#ifdef __STDIO_PRINTF_FLOAT - double d; - long double ld; -#endif /* __STDIO_PRINTF_FLOAT */ - void *p; -} argvalue_t; - -#define GET_VA_ARG(AU,F,TYPE,ARGS) (AU->F = va_arg(ARGS,TYPE)) -#define GET_ARG_VALUE(AU,F,TYPE) ((TYPE)((AU)->F)) -#endif - -typedef struct { - const char *fmtpos; /* TODO: move below struct?? */ - struct printf_info info; -#ifdef NL_ARGMAX - int maxposarg; /* > 0 if args are positional, 0 if not, -1 if unknown */ -#endif /* NL_ARGMAX */ - int num_data_args; /* TODO: use sentinal??? */ - unsigned int conv_num; - unsigned char argnumber[4]; /* width | prec | 1st data | unused */ - int argtype[MAX_ARGS]; - va_list arg; -#ifdef __va_arg_ptr - void *argptr[MAX_ARGS]; -#else -/* if defined(NL_ARGMAX) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__) */ - /* While this is wasteful of space in the case where pos args aren't - * enabled, it is also needed to support custom printf handlers. */ - argvalue_t argvalue[MAX_ARGS]; -#endif -} ppfs_t; /* parse printf format state */ - -/**********************************************************************/ - -/* TODO: fix printf to return 0 and set errno if format error. Standard says - only returns -1 if sets error indicator for the stream. */ - -#ifdef __STDIO_PRINTF_FLOAT -typedef size_t (__fp_outfunc_t)(FILE *fp, intptr_t type, intptr_t len, - intptr_t buf); - -extern ssize_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, - __fp_outfunc_t fp_outfunc) attribute_hidden; -#endif - -extern int _ppfs_init(ppfs_t *ppfs, const char *fmt0) attribute_hidden; /* validates */ -extern void _ppfs_prepargs(ppfs_t *ppfs, va_list arg) attribute_hidden; /* sets posargptrs */ -extern void _ppfs_setargs(ppfs_t *ppfs) attribute_hidden; /* sets argptrs for current spec */ -extern int _ppfs_parsespec(ppfs_t *ppfs) attribute_hidden; /* parses specifier */ - -extern void _store_inttype(void *dest, int desttype, uintmax_t val) attribute_hidden; -extern uintmax_t _load_inttype(int desttype, const void *src, int uflag) attribute_hidden; - -/**********************************************************************/ -#ifdef L_parse_printf_format - -/* NOTE: This function differs from the glibc version in that parsing stops - * upon encountering an invalid conversion specifier. Since this is the way - * my printf functions work, I think it makes sense to do it that way here. - * Unfortunately, since glibc sets the return type as size_t, we have no way - * of returning that the template is illegal, other than returning 0. - */ - -size_t parse_printf_format(register const char *template, - size_t n, register int *argtypes) -{ - ppfs_t ppfs; - size_t i; - size_t count = 0; - - if (_ppfs_init(&ppfs, template) >= 0) { -#ifdef NL_ARGMAX - if (ppfs.maxposarg > 0) { /* Using positional args. */ - count = ppfs.maxposarg; - if (n > count) { - n = count; - } - for (i = 0 ; i < n ; i++) { - *argtypes++ = ppfs.argtype[i]; - } - } else { /* Not using positional args. */ -#endif /* NL_ARGMAX */ - while (*template) { - if ((*template == '%') && (*++template != '%')) { - ppfs.fmtpos = template; - _ppfs_parsespec(&ppfs); /* Can't fail. */ - template = ppfs.fmtpos; /* Update to one past spec end. */ - if (ppfs.info.width == INT_MIN) { - ++count; - if (n > 0) { - *argtypes++ = PA_INT; - --n; - } - } - if (ppfs.info.prec == INT_MIN) { - ++count; - if (n > 0) { - *argtypes++ = PA_INT; - --n; - } - } - for (i = 0 ; i < ppfs.num_data_args ; i++) { - if ((ppfs.argtype[i]) != __PA_NOARG) { - ++count; - if (n > 0) { - *argtypes++ = ppfs.argtype[i]; - --n; - } - } - } - } else { - ++template; - } - } -#ifdef NL_ARGMAX - } -#endif /* NL_ARGMAX */ - } - - return count; -} - -#endif -/**********************************************************************/ -#ifdef L__ppfs_init - -#ifdef __UCLIBC_HAS_LOCALE__ -libc_hidden_proto(__global_locale) -#endif - -int attribute_hidden _ppfs_init(register ppfs_t *ppfs, const char *fmt0) -{ - int r; - - /* First, zero out everything... argnumber[], argtype[], argptr[] */ - memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */ -#ifdef NL_ARGMAX - --ppfs->maxposarg; /* set to -1 */ -#endif /* NL_ARGMAX */ - ppfs->fmtpos = fmt0; -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Make checking of the format string in C locale an option. -#endif -#ifdef __UCLIBC_HAS_LOCALE__ - /* To support old programs, don't check mb validity if in C locale. */ - if (((__UCLIBC_CURLOCALE_DATA).encoding) != __ctype_encoding_7_bit) { - /* ANSI/ISO C99 requires format string to be a valid multibyte string - * beginning and ending in its initial shift state. */ - static const char invalid_mbs[] = "Invalid multibyte format string."; - mbstate_t mbstate; - const char *p; - mbstate.__mask = 0; /* Initialize the mbstate. */ - p = fmt0; - if (mbsrtowcs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) { - ppfs->fmtpos = invalid_mbs; - return -1; - } - } -#endif /* __UCLIBC_HAS_LOCALE__ */ - /* now set all argtypes to no-arg */ - { -#if 1 - /* TODO - use memset here since already "paid for"? */ - register int *p = ppfs->argtype; - - r = MAX_ARGS; - do { - *p++ = __PA_NOARG; - } while (--r); -#else - /* TODO -- get rid of this?? */ - register char *p = (char *) ((MAX_ARGS-1) * sizeof(int)); - - do { - *((int *)(((char *)ppfs) + ((int)p) + offsetof(ppfs_t,argtype))) = __PA_NOARG; - p -= sizeof(int); - } while (p); -#endif - } - - /* - * Run through the entire format string to validate it and initialize - * the positional arg numbers (if any). - */ - { - register const char *fmt = fmt0; - - while (*fmt) { - if ((*fmt == '%') && (*++fmt != '%')) { - ppfs->fmtpos = fmt; /* back up to the '%' */ - if ((r = _ppfs_parsespec(ppfs)) < 0) { - return -1; - } - fmt = ppfs->fmtpos; /* update to one past end of spec */ - } else { - ++fmt; - } - } - ppfs->fmtpos = fmt0; /* rewind */ - } - -#ifdef NL_MAX_ARG - /* If we have positional args, make sure we know all the types. */ - { - register int *p = ppfs->argtype; - r = ppfs->maxposarg; - while (--r >= 0) { - if ( *p == __PA_NOARG ) { /* missing arg type!!! */ - return -1; - } - ++p; - } - } -#endif /* NL_MAX_ARG */ - - return 0; -} -#endif -/**********************************************************************/ -#ifdef L__ppfs_prepargs -void attribute_hidden _ppfs_prepargs(register ppfs_t *ppfs, va_list arg) -{ - int i; - - va_copy(ppfs->arg, arg); - -#ifdef NL_ARGMAX - if ((i = ppfs->maxposarg) > 0) { /* init for positional args */ - ppfs->num_data_args = i; - ppfs->info.width = ppfs->info.prec = ppfs->maxposarg = 0; - _ppfs_setargs(ppfs); - ppfs->maxposarg = i; - } -#endif /* NL_ARGMAX */ -} -#endif -/**********************************************************************/ -#ifdef L__ppfs_setargs - -void attribute_hidden _ppfs_setargs(register ppfs_t *ppfs) -{ -#ifdef __va_arg_ptr - register void **p = ppfs->argptr; -#else - register argvalue_t *p = ppfs->argvalue; -#endif - int i; - -#ifdef NL_ARGMAX - if (ppfs->maxposarg == 0) { /* initing for or no pos args */ -#endif /* NL_ARGMAX */ - if (ppfs->info.width == INT_MIN) { - ppfs->info.width = -#ifdef __va_arg_ptr - *(int *) -#endif - GET_VA_ARG(p,u,unsigned int,ppfs->arg); - } - if (ppfs->info.prec == INT_MIN) { - ppfs->info.prec = -#ifdef __va_arg_ptr - *(int *) -#endif - GET_VA_ARG(p,u,unsigned int,ppfs->arg); - } - i = 0; - while (i < ppfs->num_data_args) { - switch(ppfs->argtype[i++]) { - case (PA_INT|PA_FLAG_LONG_LONG): -#ifdef ULLONG_MAX - GET_VA_ARG(p,ull,unsigned long long,ppfs->arg); - break; -#endif - case (PA_INT|PA_FLAG_LONG): -#if ULONG_MAX != UINT_MAX - GET_VA_ARG(p,ul,unsigned long,ppfs->arg); - break; -#endif - case PA_CHAR: /* TODO - be careful */ - /* ... users could use above and really want below!! */ - case (PA_INT|__PA_FLAG_CHAR):/* TODO -- translate this!!! */ - case (PA_INT|PA_FLAG_SHORT): - case PA_INT: - GET_VA_ARG(p,u,unsigned int,ppfs->arg); - break; - case PA_WCHAR: /* TODO -- assume int? */ - /* we're assuming wchar_t is at least an int */ - GET_VA_ARG(p,wc,wchar_t,ppfs->arg); - break; -#ifdef __STDIO_PRINTF_FLOAT - /* PA_FLOAT */ - case PA_DOUBLE: - GET_VA_ARG(p,d,double,ppfs->arg); - break; - case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE): - GET_VA_ARG(p,ld,long double,ppfs->arg); - break; -#else /* __STDIO_PRINTF_FLOAT */ - case PA_DOUBLE: - case (PA_DOUBLE|PA_FLAG_LONG_DOUBLE): - assert(0); - continue; -#endif /* __STDIO_PRINTF_FLOAT */ - default: - /* TODO -- really need to ensure this can't happen */ - assert(ppfs->argtype[i-1] & PA_FLAG_PTR); - case PA_POINTER: - case PA_STRING: - case PA_WSTRING: - GET_VA_ARG(p,p,void *,ppfs->arg); - break; - case __PA_NOARG: - continue; - } - ++p; - } -#ifdef NL_ARGMAX - } else { - if (ppfs->info.width == INT_MIN) { - ppfs->info.width - = (int) GET_ARG_VALUE(p + ppfs->argnumber[0] - 1,u,unsigned int); - } - if (ppfs->info.prec == INT_MIN) { - ppfs->info.prec - = (int) GET_ARG_VALUE(p + ppfs->argnumber[1] - 1,u,unsigned int); - } - } -#endif /* NL_ARGMAX */ - - /* Now we know the width and precision. */ - if (ppfs->info.width < 0) { - ppfs->info.width = -ppfs->info.width; - PRINT_INFO_SET_FLAG(&(ppfs->info),left); - PRINT_INFO_CLR_FLAG(&(ppfs->info),space); - ppfs->info.pad = ' '; - } -#if 0 - /* NOTE -- keep neg for now so float knows! */ - if (ppfs->info.prec < 0) { /* spec says treat as omitted. */ - /* so use default prec... 1 for everything but floats and strings. */ - ppfs->info.prec = 1; - } -#endif -} -#endif -/**********************************************************************/ -#ifdef L__ppfs_parsespec - -#ifdef __UCLIBC_HAS_XLOCALE__ -libc_hidden_proto(__ctype_b_loc) -#else -libc_hidden_proto(__ctype_b) -#endif - -/* Notes: argtype differs from glibc for the following: - * mine glibc - * lc PA_WCHAR PA_CHAR the standard says %lc means %C - * ls PA_WSTRING PA_STRING the standard says %ls means %S - * {*}n {*}|PA_FLAG_PTR PA_FLAG_PTR size of n can be qualified - */ - -/* TODO: store positions of positional args */ - -/* TODO -- WARNING -- assumes aligned on integer boundaries!!! */ - -/* TODO -- disable if not using positional args!!! */ -#define _OVERLAPPING_DIFFERENT_ARGS - -/* TODO -- rethink this -- perhaps we should set to largest type??? */ - -#ifdef _OVERLAPPING_DIFFERENT_ARGS - -#define PROMOTED_SIZE_OF(X) ((sizeof(X) + sizeof(int) - 1) / sizeof(X)) - -static const short int type_codes[] = { - __PA_NOARG, /* must be first entry */ - PA_POINTER, - PA_STRING, - PA_WSTRING, - PA_CHAR, - PA_INT|PA_FLAG_SHORT, - PA_INT, - PA_INT|PA_FLAG_LONG, - PA_INT|PA_FLAG_LONG_LONG, - PA_WCHAR, -#ifdef __STDIO_PRINTF_FLOAT - /* PA_FLOAT, */ - PA_DOUBLE, - PA_DOUBLE|PA_FLAG_LONG_DOUBLE, -#endif /* __STDIO_PRINTF_FLOAT */ -}; - -static const unsigned char type_sizes[] = { - /* unknown type consumes no arg */ - 0, /* must be first entry */ - PROMOTED_SIZE_OF(void *), - PROMOTED_SIZE_OF(char *), - PROMOTED_SIZE_OF(wchar_t *), - PROMOTED_SIZE_OF(char), - PROMOTED_SIZE_OF(short), - PROMOTED_SIZE_OF(int), - PROMOTED_SIZE_OF(long), -#ifdef ULLONG_MAX - PROMOTED_SIZE_OF(long long), -#else - PROMOTED_SIZE_OF(long), /* TODO -- is this correct? (above too) */ -#endif - PROMOTED_SIZE_OF(wchar_t), -#ifdef __STDIO_PRINTF_FLOAT - /* PROMOTED_SIZE_OF(float), */ - PROMOTED_SIZE_OF(double), - PROMOTED_SIZE_OF(long double), -#endif /* __STDIO_PRINTF_FLOAT */ -}; - -static int _promoted_size(int argtype) -{ - register const short int *p; - - /* note -- since any unrecognized type is treated as a pointer */ - p = type_codes + sizeof(type_codes)/sizeof(type_codes[0]); - do { - if (*--p == argtype) { - break; - } - } while (p > type_codes); - - return type_sizes[(int)(p - type_codes)]; -} - -static int _is_equal_or_bigger_arg(int curtype, int newtype) -{ - /* Quick test */ - if (newtype == __PA_NOARG) { - return 0; - } - if ((curtype == __PA_NOARG) || (curtype == newtype)) { - return 1; - } - /* Ok... slot is already filled and types are different in name. */ - /* So, compare promoted sizes of curtype and newtype args. */ - return _promoted_size(curtype) <= _promoted_size(newtype); -} - -#else - -#define _is_equal_or_bigger_arg(C,N) (((C) == __PA_NOARG) || ((C) == (N))) - -#endif - -#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ -/* TODO - do this differently? */ -static char _bss_custom_printf_spec[MAX_USER_SPEC]; /* 0-init'd for us. */ - -attribute_hidden char *_custom_printf_spec = _bss_custom_printf_spec; -attribute_hidden printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC]; -attribute_hidden printf_function _custom_printf_handler[MAX_USER_SPEC]; -#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ - -int attribute_hidden _ppfs_parsespec(ppfs_t *ppfs) -{ - register const char *fmt; - register const char *p; - int preci; - int width; - int flags; - int dataargtype; - int i; - int dpoint; -#ifdef NL_ARGMAX - int maxposarg; -#endif /* NL_ARGMAX */ - int p_m_spec_chars; - int n; - int argtype[MAX_ARGS_PER_SPEC+2]; - int argnumber[3]; /* width, precision, 1st data arg */ - static const char spec_flags[] = SPEC_FLAGS; - static const char spec_chars[] = SPEC_CHARS;/* TODO: b? */ - static const char spec_ranges[] = SPEC_RANGES; - static const short spec_or_mask[] = SPEC_OR_MASK; - static const short spec_and_mask[] = SPEC_AND_MASK; - static const char qual_chars[] = QUAL_CHARS; -#ifdef __UCLIBC_HAS_WCHAR__ - char buf[32]; -#endif /* __UCLIBC_HAS_WCHAR__ */ - - /* WIDE note: we can test against '%' here since we don't allow */ - /* WIDE note: other mappings of '%' in the wide char set. */ - preci = -1; - argnumber[0] = 0; - argnumber[1] = 0; - argtype[0] = __PA_NOARG; - argtype[1] = __PA_NOARG; -#ifdef NL_ARGMAX - maxposarg = ppfs->maxposarg; -#endif /* NL_ARGMAX */ - -#ifdef __UCLIBC_HAS_WCHAR__ - /* This is somewhat lame, but saves a lot of code. If we're dealing with - * a wide stream, that means the format is a wchar string. So, copy it - * char-by-char into a normal char buffer for processing. Make the buffer - * (buf) big enough so that any reasonable format specifier will fit. - * While there a legal specifiers that won't, the all involve duplicate - * flags or outrageous field widths/precisions. */ - width = dpoint = 0; - if ((flags = ppfs->info._flags & FLAG_WIDESTREAM) == 0) { - fmt = ppfs->fmtpos; - } else { - fmt = buf + 1; - i = 0; - do { - if ((buf[i] = (char) (((wchar_t *) ppfs->fmtpos)[i-1])) - != (((wchar_t *) ppfs->fmtpos)[i-1]) - ) { - return -1; - } - } while (buf[i++]); - buf[sizeof(buf)-1] = 0; - } -#else /* __UCLIBC_HAS_WCHAR__ */ - width = flags = dpoint = 0; - fmt = ppfs->fmtpos; -#endif /* __UCLIBC_HAS_WCHAR__ */ - - assert(fmt[-1] == '%'); - assert(fmt[0] != '%'); - - /* Process arg pos and/or flags and/or width and/or precision. */ - width_precision: - p = fmt; - if (*fmt == '*') { - argtype[-dpoint] = PA_INT; - ++fmt; - } - i = 0; - while (isdigit(*fmt)) { - if (i < MAX_FIELD_WIDTH) { /* Avoid overflow. */ - i = (i * 10) + (*fmt - '0'); - } - ++fmt; - } - if (p[-1] == '%') { /* Check for a position. */ - - /* TODO: if val not in range, then error */ - -#ifdef NL_ARGMAX - if ((*fmt == '$') && (i > 0)) {/* Positional spec. */ - ++fmt; - if (maxposarg == 0) { - return -1; - } - if ((argnumber[2] = i) > maxposarg) { - maxposarg = i; - } - /* Now fall through to check flags. */ - } else { - if (maxposarg > 0) { -#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Support prec and width for %m when positional args used - /* Actually, positional arg processing will fail in general - * for specifiers that don't require an arg. */ -#endif /* __UCLIBC_MJN3_ONLY__ */ - if (*fmt == 'm') { - goto PREC_WIDTH; - } -#endif /* __UCLIBC_HAS_PRINTF_M_SPEC__ */ - return -1; - } - maxposarg = 0; /* Possible redundant store, but cuts size. */ - - if ((fmt > p) && (*p != '0')) { - goto PREC_WIDTH; - } - - fmt = p; /* Back up for possible '0's flag. */ - /* Now fall through to check flags. */ - } -#else /* NL_ARGMAX */ - if (*fmt == '$') { /* Positional spec. */ - return -1; - } - - if ((fmt > p) && (*p != '0')) { - goto PREC_WIDTH; - } - - fmt = p; /* Back up for possible '0's flag. */ - /* Now fall through to check flags. */ -#endif /* NL_ARGMAX */ - - restart_flags: /* Process flags. */ - i = 1; - p = spec_flags; - - do { - if (*fmt == *p++) { - ++fmt; - flags |= i; - goto restart_flags; - } - i += i; /* Better than i <<= 1 for bcc */ - } while (*p); - i = 0; - - /* If '+' then ignore ' ', and if '-' then ignore '0'. */ - /* Note: Need to ignore '0' when prec is an arg with val < 0, */ - /* but that test needs to wait until the arg is retrieved. */ - flags &= ~((flags & (FLAG_PLUS|FLAG_MINUS)) >> 1); - /* Note: Ignore '0' when prec is specified < 0 too (in printf). */ - - if (fmt[-1] != '%') { /* If we've done anything, loop for width. */ - goto width_precision; - } - } - PREC_WIDTH: - if (*p == '*') { /* Prec or width takes an arg. */ -#ifdef NL_ARGMAX - if (maxposarg) { - if ((*fmt++ != '$') || (i <= 0)) { - /* Using pos args and no $ or invalid arg number. */ - return -1; - } - argnumber[-dpoint] = i; - } else -#endif /* NL_ARGMAX */ - if (++p != fmt) { - /* Not using pos args but digits followed *. */ - return -1; - } - i = INT_MIN; - } - - if (!dpoint) { - width = i; - if (*fmt == '.') { - ++fmt; - dpoint = -1; /* To use as default precison. */ - goto width_precision; - } - } else { - preci = i; - } - - /* Process qualifier. */ - p = qual_chars; - do { - if (*fmt == *p) { - ++fmt; - break; - } - } while (*++p); - if ((p - qual_chars < 2) && (*fmt == *p)) { - p += ((sizeof(qual_chars)-2) / 2); - ++fmt; - } - dataargtype = ((int)(p[(sizeof(qual_chars)-2) / 2])) << 8; - - /* Process conversion specifier. */ - if (!*fmt) { - return -1; - } - - p = spec_chars; - - do { - if (*fmt == *p) { - p_m_spec_chars = p - spec_chars; - - if ((p_m_spec_chars >= CONV_c) - && (dataargtype & PA_FLAG_LONG)) { - p_m_spec_chars -= 2; /* lc -> C and ls -> S */ - } - - ppfs->conv_num = p_m_spec_chars; - p = spec_ranges-1; - while (p_m_spec_chars > *++p) {} - - i = p - spec_ranges; - argtype[2] = (dataargtype | spec_or_mask[i]) & spec_and_mask[i]; - p = spec_chars; - break; - } - } while(*++p); - - ppfs->info.spec = *fmt; - ppfs->info.prec = preci; - ppfs->info.width = width; - ppfs->info.pad = ((flags & FLAG_ZERO) ? '0' : ' '); - ppfs->info._flags = (flags & ~FLAG_ZERO) | (dataargtype & __PA_INTMASK); - ppfs->num_data_args = 1; - - if (!*p) { -#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ - if (*fmt == 'm') { - ppfs->conv_num = CONV_m; - ppfs->num_data_args = 0; - goto DONE; - } -#endif -#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ - - /* Handle custom arg -- WARNING -- overwrites p!!! */ - ppfs->conv_num = CONV_custom0; - p = _custom_printf_spec; - do { - if (*p == *fmt) { - if ((ppfs->num_data_args - = ((*_custom_printf_arginfo[(int)(p-_custom_printf_spec)]) - (&(ppfs->info), MAX_ARGS_PER_SPEC, argtype+2))) - > MAX_ARGS_PER_SPEC) { - break; /* Error -- too many args! */ - } - goto DONE; - } - } while (++p < (_custom_printf_spec + MAX_USER_SPEC)); -#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ - /* Otherwise error. */ - return -1; - } - -#if defined(__UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__) || defined(__UCLIBC_HAS_PRINTF_M_SPEC__) - DONE: -#endif - -#ifdef NL_ARGMAX - if (maxposarg > 0) { - i = 0; - do { - /* Update maxposarg and check that NL_ARGMAX is not exceeded. */ - n = ((i <= 2) - ? (ppfs->argnumber[i] = argnumber[i]) - : argnumber[2] + (i-2)); - if (n > maxposarg) { - if ((maxposarg = n) > NL_ARGMAX) { - return -1; - } - } - --n; - /* Record argtype with largest size (current, new). */ - if (_is_equal_or_bigger_arg(ppfs->argtype[n], argtype[i])) { - ppfs->argtype[n] = argtype[i]; - } - } while (++i < ppfs->num_data_args + 2); - } else { -#endif /* NL_ARGMAX */ - ppfs->argnumber[2] = 1; - memcpy(ppfs->argtype, argtype + 2, ppfs->num_data_args * sizeof(int)); -#ifdef NL_ARGMAX - } - - ppfs->maxposarg = maxposarg; -#endif /* NL_ARGMAX */ - -#ifdef __UCLIBC_HAS_WCHAR__ - if ((flags = ppfs->info._flags & FLAG_WIDESTREAM) == 0) { - ppfs->fmtpos = ++fmt; - } else { - ppfs->fmtpos = (const char *) (((const wchar_t *)(ppfs->fmtpos)) - + (fmt - buf) ); - } -#else /* __UCLIBC_HAS_WCHAR__ */ - ppfs->fmtpos = ++fmt; -#endif /* __UCLIBC_HAS_WCHAR__ */ - - return ppfs->num_data_args + 2; -} - -#endif -/**********************************************************************/ -#ifdef L_register_printf_function - -#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ - -int register_printf_function(int spec, printf_function handler, - printf_arginfo_function arginfo) -{ - register char *r; - register char *p; - - if (spec && (arginfo != NULL)) { /* TODO -- check if spec is valid char */ - r = NULL; - p = _custom_printf_spec + MAX_USER_SPEC; - do { - --p; - if (!*p) { - r = p; - } -#ifdef __BCC__ - else /* bcc generates less code with fall-through */ -#endif - if (*p == spec) { - r = p; - p = _custom_printf_spec; - } - } while (p > _custom_printf_spec); - - if (r) { - if (handler) { - *r = spec; - _custom_printf_handler[(int)(r - p)] = handler; - _custom_printf_arginfo[(int)(r - p)] = arginfo; - } else { - *r = 0; - } - return 0; - } - /* TODO -- if asked to unregister a non-existent spec, return what? */ - } - return -1; -} - -#endif - -#endif -/**********************************************************************/ -#if defined(L_vfprintf) || defined(L_vfwprintf) - -/* We only support ascii digits (or their USC equivalent codes) in - * precision and width settings in *printf (wide) format strings. - * In other words, we don't currently support glibc's 'I' flag. - * We do accept it, but it is currently ignored. */ - -static size_t _charpad(FILE * __restrict stream, int padchar, size_t numpad); - -#ifdef L_vfprintf - -#define VFPRINTF vfprintf -#define FMT_TYPE char -#define OUTNSTR _outnstr -#define STRLEN strlen -#define _PPFS_init _ppfs_init -#define OUTPUT(F,S) fputs_unlocked(S,F) -/* #define _outnstr(stream, string, len) __stdio_fwrite(string, len, stream) */ -#define _outnstr(stream, string, len) ((len > 0) ? __stdio_fwrite(string, len, stream) : 0) -#define FP_OUT _fp_out_narrow - -#ifdef __STDIO_PRINTF_FLOAT - -static size_t _fp_out_narrow(FILE *fp, intptr_t type, intptr_t len, intptr_t buf) -{ - size_t r = 0; - - if (type & 0x80) { /* Some type of padding needed. */ - int buflen = strlen((const char *) buf); - if ((len -= buflen) > 0) { - if ((r = _charpad(fp, (type & 0x7f), len)) != len) { - return r; - } - } - len = buflen; - } - return r + OUTNSTR(fp, (const char *) buf, len); -} - -#endif /* __STDIO_PRINTF_FLOAT */ - -#else /* L_vfprintf */ - -#define VFPRINTF vfwprintf -#define FMT_TYPE wchar_t -#define OUTNSTR _outnwcs -#define STRLEN wcslen -#define _PPFS_init _ppwfs_init -#define OUTPUT(F,S) fputws(S,F) -#define _outnwcs(stream, wstring, len) _wstdio_fwrite(wstring, len, stream) -#define FP_OUT _fp_out_wide - -#ifdef __UCLIBC_HAS_LOCALE__ -libc_hidden_proto(__global_locale) -#endif - -static size_t _outnstr(FILE *stream, const char *s, size_t wclen) -{ - /* NOTE!!! len here is the number of wchars we want to generate!!! */ - wchar_t wbuf[64]; - mbstate_t mbstate; - size_t todo, r, n; - - mbstate.__mask = 0; - todo = wclen; - - while (todo) { - r = mbsrtowcs(wbuf, &s, - ((todo <= sizeof(wbuf)/sizeof(wbuf[0])) - ? todo - : sizeof(wbuf)/sizeof(wbuf[0])), - &mbstate); - assert(((ssize_t)r) > 0); - n = _outnwcs(stream, wbuf, r); - todo -= n; - if (n != r) { - break; - } - } - - return wclen - todo; -} - -#ifdef __STDIO_PRINTF_FLOAT - -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Move defines from _fpmaxtostr. Put them in a common header. -#endif - -/* The following defines are from _fpmaxtostr.*/ -#define DIGITS_PER_BLOCK 9 -#define NUM_DIGIT_BLOCKS ((DECIMAL_DIG+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK) -#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK ) - -static size_t _fp_out_wide(FILE *fp, intptr_t type, intptr_t len, intptr_t buf) -{ - wchar_t wbuf[BUF_SIZE]; - const char *s = (const char *) buf; - size_t r = 0; - int i; - - if (type & 0x80) { /* Some type of padding needed */ - int buflen = strlen(s); - if ((len -= buflen) > 0) { - if ((r = _charpad(fp, (type & 0x7f), len)) != len) { - return r; - } - } - len = buflen; - } - - if (len > 0) { - i = 0; - do { -#ifdef __LOCALE_C_ONLY - wbuf[i] = s[i]; -#else /* __LOCALE_C_ONLY */ - -#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ - if (s[i] == ',') { - wbuf[i] = __UCLIBC_CURLOCALE_DATA.thousands_sep_wc; - } else -#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ - if (s[i] == '.') { - wbuf[i] = __UCLIBC_CURLOCALE_DATA.decimal_point_wc; - } else { - wbuf[i] = s[i]; - } -#endif /* __LOCALE_C_ONLY */ - - } while (++i < len); - - r += OUTNSTR(fp, wbuf, len); - } - - return r; -} - -#endif /* __STDIO_PRINTF_FLOAT */ - -static int _ppwfs_init(register ppfs_t *ppfs, const wchar_t *fmt0) -{ - static const wchar_t invalid_wcs[] = L"Invalid wide format string."; - int r; - - /* First, zero out everything... argnumber[], argtype[], argptr[] */ - memset(ppfs, 0, sizeof(ppfs_t)); /* TODO: nonportable???? */ -#ifdef NL_ARGMAX - --ppfs->maxposarg; /* set to -1 */ -#endif /* NL_ARGMAX */ - ppfs->fmtpos = (const char *) fmt0; - ppfs->info._flags = FLAG_WIDESTREAM; - - { - mbstate_t mbstate; - const wchar_t *p; - mbstate.__mask = 0; /* Initialize the mbstate. */ - p = fmt0; - if (wcsrtombs(NULL, &p, SIZE_MAX, &mbstate) == ((size_t)(-1))) { - ppfs->fmtpos = (const char *) invalid_wcs; - return -1; - } - } - - /* now set all argtypes to no-arg */ - { -#if 1 - /* TODO - use memset here since already "paid for"? */ - register int *p = ppfs->argtype; - - r = MAX_ARGS; - do { - *p++ = __PA_NOARG; - } while (--r); -#else - /* TODO -- get rid of this?? */ - register char *p = (char *) ((MAX_ARGS-1) * sizeof(int)); - - do { - *((int *)(((char *)ppfs) + ((int)p) + offsetof(ppfs_t,argtype))) = __PA_NOARG; - p -= sizeof(int); - } while (p); -#endif - } - - /* - * Run through the entire format string to validate it and initialize - * the positional arg numbers (if any). - */ - { - register const wchar_t *fmt = fmt0; - - while (*fmt) { - if ((*fmt == '%') && (*++fmt != '%')) { - ppfs->fmtpos = (const char *) fmt; /* back up to the '%' */ - if ((r = _ppfs_parsespec(ppfs)) < 0) { - return -1; - } - fmt = (const wchar_t *) ppfs->fmtpos; /* update to one past end of spec */ - } else { - ++fmt; - } - } - ppfs->fmtpos = (const char *) fmt0; /* rewind */ - } - -#ifdef NL_ARGMAX - /* If we have positional args, make sure we know all the types. */ - { - register int *p = ppfs->argtype; - r = ppfs->maxposarg; - while (--r >= 0) { - if ( *p == __PA_NOARG ) { /* missing arg type!!! */ - return -1; - } - ++p; - } - } -#endif /* NL_ARGMAX */ - - return 0; -} - -#endif /* L_vfprintf */ - -static size_t _charpad(FILE * __restrict stream, int padchar, size_t numpad) -{ - size_t todo = numpad; - - /* TODO -- Use a buffer to cut down on function calls... */ - FMT_TYPE pad[1]; - - *pad = padchar; - while (todo && (OUTNSTR(stream, pad, 1) == 1)) { - --todo; - } - - return numpad - todo; -} - -/* TODO -- Dynamically allocate work space to accomodate stack-poor archs? */ -static int _do_one_spec(FILE * __restrict stream, - register ppfs_t *ppfs, int *count) -{ - static const char spec_base[] = SPEC_BASE; -#ifdef L_vfprintf - static const char prefix[] = "+\0-\0 \0000x\0000X"; - /* 0 2 4 6 9 11*/ -#else /* L_vfprintf */ - static const wchar_t prefix[] = L"+\0-\0 \0000x\0000X"; -#endif /* L_vfprintf */ - enum { - PREFIX_PLUS = 0, - PREFIX_MINUS = 2, - PREFIX_SPACE = 4, - PREFIX_LWR_X = 6, - PREFIX_UPR_X = 9, - PREFIX_NONE = 11 - }; - -#ifdef __va_arg_ptr - const void * const *argptr; -#else - const void * argptr[MAX_ARGS_PER_SPEC]; -#endif - int *argtype; -#ifdef __UCLIBC_HAS_WCHAR__ - const wchar_t *ws = NULL; - mbstate_t mbstate; -#endif /* __UCLIBC_HAS_WCHAR__ */ - size_t slen; -#ifdef L_vfprintf -#define SLEN slen -#else - size_t SLEN; - wchar_t wbuf[2]; -#endif - int base; - int numpad; - int alphacase; - int numfill = 0; /* TODO: fix */ - int prefix_num = PREFIX_NONE; - char padchar = ' '; -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Determine appropriate buf size. -#endif /* __UCLIBC_MJN3_ONLY__ */ - /* TODO: buf needs to be big enough for any possible error return strings - * and also for any locale-grouped long long integer strings generated. - * This should be large enough for any of the current archs/locales, but - * eventually this should be handled robustly. */ - char buf[128]; - -#ifdef NDEBUG - _ppfs_parsespec(ppfs); -#else - if (_ppfs_parsespec(ppfs) < 0) { /* TODO: just for debugging */ - abort(); - } -#endif - _ppfs_setargs(ppfs); - - argtype = ppfs->argtype + ppfs->argnumber[2] - 1; - /* Deal with the argptr vs argvalue issue. */ -#ifdef __va_arg_ptr - argptr = (const void * const *) ppfs->argptr; -#ifdef NL_ARGMAX - if (ppfs->maxposarg > 0) { /* Using positional args... */ - argptr += ppfs->argnumber[2] - 1; - } -#endif /* NL_ARGMAX */ -#else - /* Need to build a local copy... */ - { - register argvalue_t *p = ppfs->argvalue; - int i; -#ifdef NL_ARGMAX - if (ppfs->maxposarg > 0) { /* Using positional args... */ - p += ppfs->argnumber[2] - 1; - } -#endif /* NL_ARGMAX */ - for (i = 0 ; i < ppfs->num_data_args ; i++ ) { - argptr[i] = (void *) p++; - } - } -#endif - { - register char *s = NULL; /* TODO: Should s be unsigned char * ? */ - - if (ppfs->conv_num == CONV_n) { - _store_inttype(*(void **)*argptr, - ppfs->info._flags & __PA_INTMASK, - (intmax_t) (*count)); - return 0; - } - if (ppfs->conv_num <= CONV_i) { /* pointer or (un)signed int */ - alphacase = __UIM_LOWER; - -#ifdef __UCLIBC_MJN3_ONLY__ -#ifdef L_vfprintf -#warning CONSIDER: Should we ignore these flags if stub locale? What about custom specs? -#endif -#endif /* __UCLIBC_MJN3_ONLY__ */ - if ((base = spec_base[(int)(ppfs->conv_num - CONV_p)]) == 10) { - if (PRINT_INFO_FLAG_VAL(&(ppfs->info),group)) { - alphacase = __UIM_GROUP; - } - if (PRINT_INFO_FLAG_VAL(&(ppfs->info),i18n)) { - alphacase |= 0x80; - } - } - - if (ppfs->conv_num <= CONV_u) { /* pointer or unsigned int */ - if (ppfs->conv_num == CONV_X) { - alphacase = __UIM_UPPER; - } - if (ppfs->conv_num == CONV_p) { /* pointer */ - prefix_num = PREFIX_LWR_X; - } else { /* unsigned int */ - } - } else { /* signed int */ - base = -base; - } - if (ppfs->info.prec < 0) { /* Ignore '0' flag if prec specified. */ - padchar = ppfs->info.pad; - } -#ifdef __UCLIBC_MJN3_ONLY__ -#ifdef L_vfprintf -#warning CONSIDER: If using outdigits and/or grouping, how should we interpret precision? -#endif -#endif /* __UCLIBC_MJN3_ONLY__ */ - s = _uintmaxtostr(buf + sizeof(buf) - 1, - (uintmax_t) - _load_inttype(*argtype & __PA_INTMASK, - *argptr, base), base, alphacase); - if (ppfs->conv_num > CONV_u) { /* signed int */ - if (*s == '-') { - PRINT_INFO_SET_FLAG(&(ppfs->info),showsign); - ++s; /* handle '-' in the prefix string */ - prefix_num = PREFIX_MINUS; - } else if (PRINT_INFO_FLAG_VAL(&(ppfs->info),showsign)) { - prefix_num = PREFIX_PLUS; - } else if (PRINT_INFO_FLAG_VAL(&(ppfs->info),space)) { - prefix_num = PREFIX_SPACE; - } - } - slen = (char *)(buf + sizeof(buf) - 1) - s; -#ifdef L_vfwprintf - { - const char *q = s; - mbstate.__mask = 0; /* Initialize the mbstate. */ - SLEN = mbsrtowcs(NULL, &q, 0, &mbstate); - } -#endif - numfill = ((ppfs->info.prec < 0) ? 1 : ppfs->info.prec); - if (PRINT_INFO_FLAG_VAL(&(ppfs->info),alt)) { - if (ppfs->conv_num <= CONV_x) { /* x or p */ - prefix_num = PREFIX_LWR_X; - } - if (ppfs->conv_num == CONV_X) { - prefix_num = PREFIX_UPR_X; - } - if ((ppfs->conv_num == CONV_o) && (numfill <= SLEN)) { - numfill = ((*s == '0') ? 1 : SLEN + 1); - } - } - if (*s == '0') { - if (prefix_num >= PREFIX_LWR_X) { - prefix_num = PREFIX_NONE; - } - if (ppfs->conv_num == CONV_p) {/* null pointer */ - s = "(nil)"; -#ifdef L_vfwprintf - SLEN = -#endif - slen = 5; - numfill = 0; - } else if (numfill == 0) { /* if precision 0, no output */ -#ifdef L_vfwprintf - SLEN = -#endif - slen = 0; - } - } - numfill = ((numfill > SLEN) ? numfill - SLEN : 0); - } else if (ppfs->conv_num <= CONV_A) { /* floating point */ -#ifdef __STDIO_PRINTF_FLOAT - ssize_t nf; - nf = _fpmaxtostr(stream, - (__fpmax_t) - (PRINT_INFO_FLAG_VAL(&(ppfs->info),is_long_double) - ? *(long double *) *argptr - : (long double) (* (double *) *argptr)), - &ppfs->info, FP_OUT ); - if (nf < 0) { - return -1; - } - *count += nf; - - return 0; -#else /* __STDIO_PRINTF_FLOAT */ - return -1; /* TODO -- try to continue? */ -#endif /* __STDIO_PRINTF_FLOAT */ - } else if (ppfs->conv_num <= CONV_S) { /* wide char or string */ -#ifdef L_vfprintf - -#ifdef __UCLIBC_HAS_WCHAR__ - mbstate.__mask = 0; /* Initialize the mbstate. */ - if (ppfs->conv_num == CONV_S) { /* wide string */ - if (!(ws = *((const wchar_t **) *argptr))) { - goto NULL_STRING; - } - /* We use an awful uClibc-specific hack here, passing - * (char*) &ws as the conversion destination. This signals - * uClibc's wcsrtombs that we want a "restricted" length - * such that the mbs fits in a buffer of the specified - * size with no partial conversions. */ - if ((slen = wcsrtombs((char *) &ws, &ws, /* Use awful hack! */ - ((ppfs->info.prec >= 0) - ? ppfs->info.prec - : SIZE_MAX), &mbstate)) - == ((size_t)-1) - ) { - return -1; /* EILSEQ */ - } - } else { /* wide char */ - s = buf; - slen = wcrtomb(s, (*((const wchar_t *) *argptr)), &mbstate); - if (slen == ((size_t)-1)) { - return -1; /* EILSEQ */ - } - s[slen] = 0; /* TODO - Is this necessary? */ - } -#else /* __UCLIBC_HAS_WCHAR__ */ - return -1; -#endif /* __UCLIBC_HAS_WCHAR__ */ - } else if (ppfs->conv_num <= CONV_s) { /* char or string */ - if (ppfs->conv_num == CONV_s) { /* string */ - s = *((char **) (*argptr)); - if (s) { -#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ - SET_STRING_LEN: -#endif - slen = strnlen(s, ((ppfs->info.prec >= 0) - ? ppfs->info.prec : SIZE_MAX)); - } else { -#ifdef __UCLIBC_HAS_WCHAR__ - NULL_STRING: -#endif - s = "(null)"; - slen = 6; - } - } else { /* char */ - s = buf; - *s = (unsigned char)(*((const int *) *argptr)); - s[1] = 0; - slen = 1; - } - -#else /* L_vfprintf */ - - if (ppfs->conv_num == CONV_S) { /* wide string */ - ws = *((wchar_t **) (*argptr)); - if (!ws) { - goto NULL_STRING; - } - SLEN = wcsnlen(ws, ((ppfs->info.prec >= 0) - ? ppfs->info.prec : SIZE_MAX)); - } else { /* wide char */ - *wbuf = (wchar_t)(*((const wint_t *) *argptr)); - CHAR_CASE: - ws = wbuf; - wbuf[1] = 0; - SLEN = 1; - } - - } else if (ppfs->conv_num <= CONV_s) { /* char or string */ - - if (ppfs->conv_num == CONV_s) { /* string */ -#ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: Fix %s for vfwprintf... output upto illegal sequence? -#endif /* __UCLIBC_MJN3_ONLY__ */ - s = *((char **) (*argptr)); - if (s) { -#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ - SET_STRING_LEN: -#endif - /* We use an awful uClibc-specific hack here, passing - * (wchar_t*) &mbstate as the conversion destination. - * This signals uClibc's mbsrtowcs that we want a - * "restricted" length such that the mbs fits in a buffer - * of the specified size with no partial conversions. */ - { - const char *q = s; - mbstate.__mask = 0; /* Initialize the mbstate. */ - SLEN = mbsrtowcs((wchar_t *) &mbstate, &q, - ((ppfs->info.prec >= 0) - ? ppfs->info.prec : SIZE_MAX), - &mbstate); - } - if (SLEN == ((size_t)(-1))) { - return -1; /* EILSEQ */ - } - } else { - NULL_STRING: - s = "(null)"; - SLEN = slen = 6; - } - } else { /* char */ - *wbuf = btowc( (unsigned char)(*((const int *) *argptr)) ); - goto CHAR_CASE; - } - -#endif /* L_vfprintf */ - -#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ - } else if (ppfs->conv_num == CONV_m) { - s = __glibc_strerror_r(errno, buf, sizeof(buf)); - goto SET_STRING_LEN; -#endif - } else { -#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ - assert(ppfs->conv_num == CONV_custom0); - - s = _custom_printf_spec; - do { - if (*s == ppfs->info.spec) { - int rv; - /* TODO -- check return value for sanity? */ - rv = (*_custom_printf_handler - [(int)(s-_custom_printf_spec)]) - (stream, &ppfs->info, argptr); - if (rv < 0) { - return -1; - } - *count += rv; - return 0; - } - } while (++s < (_custom_printf_spec + MAX_USER_SPEC)); -#endif /* __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ */ - assert(0); - return -1; - } - -#ifdef __UCLIBC_MJN3_ONLY__ -#ifdef L_vfprintf -#warning CONSIDER: If using outdigits and/or grouping, how should we pad? -#endif -#endif /* __UCLIBC_MJN3_ONLY__ */ - { - size_t t; - - t = SLEN + numfill; - if (prefix_num != PREFIX_NONE) { - t += ((prefix_num < PREFIX_LWR_X) ? 1 : 2); - } - numpad = ((ppfs->info.width > t) ? (ppfs->info.width - t) : 0); - *count += t + numpad; - } - if (padchar == '0') { /* TODO: check this */ - numfill += numpad; - numpad = 0; - } - - /* Now handle the output itself. */ - if (!PRINT_INFO_FLAG_VAL(&(ppfs->info),left)) { - if (_charpad(stream, ' ', numpad) != numpad) { - return -1; - } - numpad = 0; - } - OUTPUT(stream, prefix + prefix_num); - if (_charpad(stream, '0', numfill) != numfill) { - return -1; - } - -#ifdef L_vfprintf - -#ifdef __UCLIBC_HAS_WCHAR__ - if (!ws) { - assert(s); - if (_outnstr(stream, s, slen) != slen) { - return -1; - } - } else { /* wide string */ - size_t t; - mbstate.__mask = 0; /* Initialize the mbstate. */ - while (slen) { - t = (slen <= sizeof(buf)) ? slen : sizeof(buf); - t = wcsrtombs(buf, &ws, t, &mbstate); - assert (t != ((size_t)(-1))); - if (_outnstr(stream, buf, t) != t) { - return -1; - } - slen -= t; - } - } -#else /* __UCLIBC_HAS_WCHAR__ */ - if (_outnstr(stream, s, slen) != slen) { - return -1; - } -#endif /* __UCLIBC_HAS_WCHAR__ */ - -#else /* L_vfprintf */ - - if (!ws) { - assert(s); - if (_outnstr(stream, s, SLEN) != SLEN) { - return -1; - } - } else { - if (_outnwcs(stream, ws, SLEN) != SLEN) { - return -1; - } - } - -#endif /* L_vfprintf */ - if (_charpad(stream, ' ', numpad) != numpad) { - return -1; - } - } - - return 0; -} - -libc_hidden_proto(fprintf) - -libc_hidden_proto(VFPRINTF) -int VFPRINTF (FILE * __restrict stream, - register const FMT_TYPE * __restrict format, - va_list arg) -{ - ppfs_t ppfs; - int count, r; - register const FMT_TYPE *s; - __STDIO_AUTO_THREADLOCK_VAR; - - __STDIO_AUTO_THREADLOCK(stream); - - count = 0; - s = format; - - if -#ifdef L_vfprintf - (!__STDIO_STREAM_IS_NARROW_WRITING(stream) - && __STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_NARROW)) -#else - (!__STDIO_STREAM_IS_WIDE_WRITING(stream) - && __STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_WIDE)) -#endif - { - count = -1; - } else if (_PPFS_init(&ppfs, format) < 0) { /* Bad format string. */ - OUTNSTR(stream, (const FMT_TYPE *) ppfs.fmtpos, - STRLEN((const FMT_TYPE *)(ppfs.fmtpos))); -#if defined(L_vfprintf) && !defined(NDEBUG) - fprintf(stderr,"\nIMbS: \"%s\"\n\n", format); -#endif - count = -1; - } else { - _ppfs_prepargs(&ppfs, arg); /* This did a va_copy!!! */ - - do { - while (*format && (*format != '%')) { - ++format; - } - - if (format-s) { /* output any literal text in format string */ - if ( (r = OUTNSTR(stream, s, format-s)) != (format-s)) { - count = -1; - break; - } - count += r; - } - - if (!*format) { /* we're done */ - break; - } - - if (format[1] != '%') { /* if we get here, *format == '%' */ - /* TODO: _do_one_spec needs to know what the output funcs are!!! */ - ppfs.fmtpos = (const char *)(++format); - /* TODO: check -- should only fail on stream error */ - if ( (r = _do_one_spec(stream, &ppfs, &count)) < 0) { - count = -1; - break; - } - s = format = (const FMT_TYPE *) ppfs.fmtpos; - } else { /* %% means literal %, so start new string */ - s = ++format; - ++format; - } - } while (1); - - va_end(ppfs.arg); /* Need to clean up after va_copy! */ - } - -/* #if defined(L_vfprintf) && defined(__UCLIBC_HAS_WCHAR__) */ -/* DONE: */ -/* #endif */ - - __STDIO_AUTO_THREADUNLOCK(stream); - - return count; -} -libc_hidden_def(VFPRINTF) -#endif -/**********************************************************************/ -- cgit v1.2.3