diff options
Diffstat (limited to 'libc')
34 files changed, 6903 insertions, 2166 deletions
diff --git a/libc/inet/rpc/rcmd.c b/libc/inet/rpc/rcmd.c index b8138497c..618a6f1b2 100644 --- a/libc/inet/rpc/rcmd.c +++ b/libc/inet/rpc/rcmd.c @@ -38,11 +38,6 @@ static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94"; #define __FORCE_GLIBC #include <features.h> -#ifdef __UCLIBC_HAS_THREADS__ -#undef __UCLIBC_HAS_THREADS__ -#warning FIXME I am not reentrant yet... -#endif - #define __USE_GNU #include <ctype.h> #include <stdio.h> @@ -62,10 +57,10 @@ static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94"; #include <netinet/in.h> #include <arpa/inet.h> - - -/* hmm. uClibc seems to have that, but it doesn't work for some reason */ -#define getc_unlocked getc +#ifdef __UCLIBC_HAS_THREADS__ +#undef __UCLIBC_HAS_THREADS__ +#warning FIXME I am not reentrant yet... +#endif /* some forward declarations */ diff --git a/libc/misc/Makefile b/libc/misc/Makefile index 46de813da..037e2154d 100644 --- a/libc/misc/Makefile +++ b/libc/misc/Makefile @@ -26,8 +26,8 @@ include $(TOPDIR)Rules.mak DIRS = assert ctype dirent file fnmatch glob internals \ - mntent syslog time utmp locale sysvipc statfs \ - error ttyent gnu search + mntent syslog time utmp sysvipc statfs \ + error ttyent gnu search intl locale ifeq ($(strip $(UCLIBC_HAS_REGEX)),y) DIRS += regex endif diff --git a/libc/misc/assert/__assert.c b/libc/misc/assert/__assert.c index efffff1de..26bcc6516 100644 --- a/libc/misc/assert/__assert.c +++ b/libc/misc/assert/__assert.c @@ -37,15 +37,21 @@ #include <assert.h> #undef assert + +#define ASSERT_SHOW_PROGNAME 1 + +#ifdef ASSERT_SHOW_PROGNAME +extern const char *__progname; +#endif + #if 1 void __assert(const char *assertion, const char * filename, int linenumber, register const char * function) { fprintf(stderr, -#if 0 - /* TODO: support program_name like glibc? */ - "%s: %s: %d: %s: Assertion `%s' failed.\n", program_name, +#ifdef ASSERT_SHOW_PROGNAME + "%s: %s: %d: %s: Assertion `%s' failed.\n", __progname, #else "%s: %d: %s: Assertion `%s' failed.\n", #endif @@ -66,8 +72,8 @@ void __assert(const char *assertion, const char * filename, char buf[__BUFLEN_INT10TOSTR]; _stdio_fdout(STDERR_FILENO, -#if 0 - program_name, /* TODO: support program_name like glibc? */ +#ifdef ASSERT_SHOW_PROGNAME + __progname, ": ", #endif filename, diff --git a/libc/misc/ctype/Makefile b/libc/misc/ctype/Makefile index 1d7c24535..2f1dd65f0 100644 --- a/libc/misc/ctype/Makefile +++ b/libc/misc/ctype/Makefile @@ -25,17 +25,26 @@ TOPDIR=../../../ include $(TOPDIR)Rules.mak MSRC=ctype.c -MOBJ= isalnum.o isalpha.o isascii.o iscntrl.o isdigit.o isgraph.o \ - islower.o isprint.o ispunct.o isspace.o isupper.o isxdigit.o \ - isxlower.o isxupper.o toascii.o tolower.o toupper.o isblank.o \ - __isctype_loc.o +MOBJ= isalnum.o isalpha.o isascii.o iscntrl.o isdigit.o \ + isgraph.o islower.o isprint.o ispunct.o isspace.o \ + isupper.o isxdigit.o toascii.o tolower.o toupper.o \ + isblank.o isctype.o isxlower.o isxupper.o \ + __C_ctype_b.o __C_ctype_tolower.o __C_ctype_toupper.o \ + __ctype_b_loc.o __ctype_tolower_loc.o __ctype_toupper_loc.o \ + __ctype_assert.o -CSRC=junk.c -COBJS=$(patsubst %.c,%.o, $(CSRC)) +MOBJx= isalnum_l.o isalpha_l.o isascii_l.o iscntrl_l.o isdigit_l.o \ + isgraph_l.o islower_l.o isprint_l.o ispunct_l.o isspace_l.o \ + isupper_l.o isxdigit_l.o toascii_l.o tolower_l.o toupper_l.o \ + isblank_l.o # isxlower_l.o isxupper_l.o +OBJS=$(MOBJ) -OBJS=$(MOBJ) $(COBJS) -all: $(MOBJ) $(LIBC) +ifeq ($(UCLIBC_HAS_XLOCALE),y) + OBJS += $(MOBJx) +endif + +all: $(OBJS) $(LIBC) $(LIBC): ar-target @@ -46,8 +55,8 @@ $(MOBJ): $(MSRC) $(CC) $(CFLAGS) -DL_$* $< -c -o $*.o $(STRIPTOOL) -x -R .note -R .comment $*.o -$(COBJS): %.o : %.c - $(CC) $(CFLAGS) -c $< -o $@ +$(MOBJx): $(MSRC) + $(CC) $(CFLAGS) -DL_$* -D__UCLIBC_DO_XLOCALE $< -c -o $*.o $(STRIPTOOL) -x -R .note -R .comment $*.o $(OBJS): Makefile diff --git a/libc/misc/ctype/ctype.c b/libc/misc/ctype/ctype.c index dedd5c00a..0eb97140d 100644 --- a/libc/misc/ctype/ctype.c +++ b/libc/misc/ctype/ctype.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002 Manuel Novoa III +/* Copyright (C) 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 @@ -31,299 +31,1026 @@ #include <ctype.h> #include <stdio.h> +#include <stdlib.h> #include <limits.h> +#include <stdint.h> #include <assert.h> #include <locale.h> +#ifdef __UCLIBC_HAS_XLOCALE__ +#include <xlocale.h> +#endif /* __UCLIBC_HAS_XLOCALE__ */ + /**********************************************************************/ -extern int __isctype_loc(int c, int ct); +#ifdef __UCLIBC_HAS_CTYPE_SIGNED__ -/* Some macros used throughout the file. */ -#define U ((unsigned char)c) -/* #define LCT (__cur_locale->ctype) */ -#define LCT (&__global_locale) +#if EOF >= CHAR_MIN +#define CTYPE_DOMAIN_CHECK(C) \ + (((unsigned int)((C) - CHAR_MIN)) <= (UCHAR_MAX - CHAR_MIN)) +#else +#define CTYPE_DOMAIN_CHECK(C) \ + ((((unsigned int)((C) - CHAR_MIN)) <= (UCHAR_MAX - CHAR_MIN)) || ((C) == EOF)) +#endif -/**********************************************************************/ +#else /* __UCLIBC_HAS_CTYPE_SIGNED__ */ -#ifndef __PASTE -#define __PASTE(X,Y) X ## Y +#if EOF == -1 +#define CTYPE_DOMAIN_CHECK(C) \ + (((unsigned int)((C) - EOF)) <= (UCHAR_MAX - EOF)) +#else +#define CTYPE_DOMAIN_CHECK(C) \ + ((((unsigned int)(C)) <= UCHAR_MAX) || ((C) == EOF)) #endif -#define C_MACRO(X) __PASTE(__C_,X)(c) - -#define CT_MACRO(X) __PASTE(__ctype_,X)(c) +#endif /* __UCLIBC_HAS_CTYPE_SIGNED__ */ /**********************************************************************/ +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_isspace +/* emit only once */ +#warning CONSIDER: Should we assert when debugging and __UCLIBC_HAS_CTYPE_CHECKED? +#warning TODO: Fix asserts in to{upper|lower}{_l}. +#warning TODO: Optimize the isx*() funcs. +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#undef CTYPE_NAME +#undef ISCTYPE +#undef CTYPE_ALIAS +#ifdef __UCLIBC_DO_XLOCALE +#define CTYPE_NAME(X) __is ## X ## _l +#define ISCTYPE(C,F) __isctype_l( C, F, locale_arg) +#define CTYPE_ALIAS(NAME) weak_alias( __is ## NAME ## _l , is ## NAME ## _l) +#else +#define CTYPE_NAME(X) is ## X +#define ISCTYPE(C,F) __isctype( C, F ) +#define CTYPE_ALIAS(NAME) +#endif +#undef PASTE2 +#define PASTE2(X,Y) X ## Y -#ifndef __CTYPE_HAS_8_BIT_LOCALES -#define IS_FUNC_BODY(NAME) \ -int NAME (int c) \ -{ \ - return C_MACRO(NAME); \ -} +#undef CTYPE_BODY + +#if defined(__UCLIBC_HAS_CTYPE_ENFORCED__) +/* Make sure assert is active for to*() funcs below. */ +#undef NDEBUG +#include <assert.h> + +extern void __isctype_assert(int c, int mask) __attribute__ ((__noreturn__)); + +#define CTYPE_BODY(NAME,C,MASK) \ + if (CTYPE_DOMAIN_CHECK(C)) { \ + return ISCTYPE(C, MASK); \ + } \ + __isctype_assert(C, MASK); + +#elif defined(__UCLIBC_HAS_CTYPE_CHECKED__) + +#define CTYPE_BODY(NAME,C,MASK) \ + return CTYPE_DOMAIN_CHECK(C) \ + ? ISCTYPE(C, MASK) \ + : 0; + +#elif defined(__UCLIBC_HAS_CTYPE_UNSAFE__) + +#define CTYPE_BODY(NAME,C,MASK) \ + return ISCTYPE(C, MASK); + + +#else /* No checking done. */ + +#error Unknown type of ctype checking! + +#endif -#else -/* It may be worth defining __isctype_loc over the whole range of char. */ -/* #define IS_FUNC_BODY(NAME) \ */ -/* int NAME (int c) \ */ -/* { \ */ -/* return __isctype_loc(c, __PASTE(_CTYPE_,NAME)); \ */ -/* } */ #define IS_FUNC_BODY(NAME) \ -int NAME (int c) \ +int CTYPE_NAME(NAME) (int c __LOCALE_PARAM ) \ { \ - if (((unsigned int) c) <= 0x7f) { \ - return C_MACRO(NAME); \ - } \ - return __isctype_loc(c, __PASTE(_CTYPE_,NAME)); \ + CTYPE_BODY(NAME,c,PASTE2(_IS,NAME)) \ +} \ +CTYPE_ALIAS(NAME) + + +/**********************************************************************/ +#ifdef L___ctype_assert +#ifdef __UCLIBC_HAS_CTYPE_ENFORCED__ + +extern const char *__progname; + +void __isctype_assert(int c, int mask) +{ + fprintf(stderr, "%s: __is*{_l}(%d,%#x {locale})\n", __progname, c, mask); + abort(); } -#endif /* __CTYPE_HAS_8_BIT_LOCALES */ +#endif +#endif +/**********************************************************************/ +#if defined(L_isalnum) || defined(L_isalnum_l) + +IS_FUNC_BODY(alnum); + +#endif +/**********************************************************************/ +#if defined(L_isalpha) || defined(L_isalpha_l) +IS_FUNC_BODY(alpha); + +#endif /**********************************************************************/ -#ifdef L_isalnum +#if defined(L_isblank) || defined(L_isblank_l) -IS_FUNC_BODY(isalnum); +IS_FUNC_BODY(blank); #endif /**********************************************************************/ -#ifdef L_isalpha +#if defined(L_iscntrl) || defined(L_iscntrl_l) -IS_FUNC_BODY(isalpha); +IS_FUNC_BODY(cntrl); #endif /**********************************************************************/ -#ifdef L_isblank +#if defined(L_isdigit) || defined(L_isdigit_l) -/* Warning!!! This is correct for all the currently supported 8-bit locales. - * If any are added though, this will need to be verified. */ +/* 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 -int isblank(int c) +int CTYPE_NAME(digit) (int C __LOCALE_PARAM) { - return __isblank(c); +#if defined(__UCLIBC_HAS_CTYPE_ENFORCED__) + if (CTYPE_DOMAIN_CHECK(C)) { + return __isdigit_char_or_EOF(C); /* C is (unsigned) char or EOF. */ + } + __isctype_assert(C, _ISdigit); +#else + return __isdigit_int(C); /* C could be invalid. */ +#endif } +CTYPE_ALIAS(digit) + #endif /**********************************************************************/ -#ifdef L_iscntrl +#if defined(L_isgraph) || defined(L_isgraph_l) -IS_FUNC_BODY(iscntrl); +IS_FUNC_BODY(graph); #endif /**********************************************************************/ -#ifdef L_isdigit +#if defined(L_islower) || defined(L_islower_l) -int isdigit(int c) -{ - return __isdigit(c); -} +IS_FUNC_BODY(lower); #endif /**********************************************************************/ -#ifdef L_isgraph +#if defined(L_isprint) || defined(L_isprint_l) -IS_FUNC_BODY(isgraph); +IS_FUNC_BODY(print); #endif /**********************************************************************/ -#ifdef L_islower +#if defined(L_ispunct) || defined(L_ispunct_l) -IS_FUNC_BODY(islower); +IS_FUNC_BODY(punct); #endif /**********************************************************************/ -#ifdef L_isprint +#if defined(L_isspace) || defined(L_isspace_l) -IS_FUNC_BODY(isprint); +IS_FUNC_BODY(space); #endif /**********************************************************************/ -#ifdef L_ispunct +#if defined(L_isupper) || defined(L_isupper_l) -IS_FUNC_BODY(ispunct); +IS_FUNC_BODY(upper); #endif /**********************************************************************/ -#ifdef L_isspace +#if defined(L_isxdigit) || defined(L_isxdigit_l) -/* Warning!!! This is correct for all the currently supported 8-bit locales. - * If any are added though, this will need to be verified. */ +IS_FUNC_BODY(xdigit); -int isspace(int c) +#endif +/**********************************************************************/ +#ifdef L_tolower + +int tolower(int c) { - return __isspace(c); +#if defined(__UCLIBC_HAS_CTYPE_ENFORCED__) + assert(CTYPE_DOMAIN_CHECK(c)); +#endif + return __UCLIBC_CTYPE_IN_TO_DOMAIN(c) ? (__UCLIBC_CTYPE_TOLOWER)[c] : c; } #endif /**********************************************************************/ -#ifdef L_isupper +#ifdef L_tolower_l + +#undef tolower_l -IS_FUNC_BODY(isupper); +int tolower_l(int c, __locale_t l) +{ +#if defined(__UCLIBC_HAS_CTYPE_ENFORCED__) + assert(CTYPE_DOMAIN_CHECK(c)); +#endif + return __UCLIBC_CTYPE_IN_TO_DOMAIN(c) ? l->__ctype_tolower[c] : c; +} #endif /**********************************************************************/ -#ifdef L_isxdigit +#ifdef L_toupper -int isxdigit(int c) +int toupper(int c) { - return __isxdigit(c); +#if defined(__UCLIBC_HAS_CTYPE_ENFORCED__) + assert(CTYPE_DOMAIN_CHECK(c)); +#endif + return __UCLIBC_CTYPE_IN_TO_DOMAIN(c) ? (__UCLIBC_CTYPE_TOUPPER)[c] : c; } #endif /**********************************************************************/ -#ifdef L_tolower +#ifdef L_toupper_l -#ifdef __CTYPE_HAS_8_BIT_LOCALES +#undef toupper_l -int tolower(int c) +int toupper_l(int c, __locale_t l) { - return ((((unsigned int) c) <= 0x7f) - || (LCT->encoding != __ctype_encoding_8_bit)) - ? __C_tolower(c) - : ( __isctype_loc(c, _CTYPE_isupper) - ? (unsigned char) - ( U - LCT->tbl8uplow[ ((int) - (LCT->idx8uplow[(U & 0x7f) - >> Cuplow_IDX_SHIFT]) - << Cuplow_IDX_SHIFT) - + (U & ((1 << Cuplow_IDX_SHIFT) - 1)) ]) - : c ); +#if defined(__UCLIBC_HAS_CTYPE_ENFORCED__) + assert(CTYPE_DOMAIN_CHECK(c)); +#endif + return __UCLIBC_CTYPE_IN_TO_DOMAIN(c) ? l->__ctype_toupper[c] : c; } -#else /* __CTYPE_HAS_8_BIT_LOCALES */ +#endif +/**********************************************************************/ +#if defined(L_isascii) || defined(L_isascii_l) -int tolower(int c) +int __XL(isascii)(int c) { - return __C_tolower(c); + return __isascii(c); /* locale-independent */ } -#endif /* __CTYPE_HAS_8_BIT_LOCALES */ - #endif /**********************************************************************/ -#ifdef L_toupper - -#ifdef __CTYPE_HAS_8_BIT_LOCALES +#if defined(L_toascii) || defined(L_toascii_l) -int toupper(int c) +int __XL(toascii)(int c) { - return ((((unsigned int) c) <= 0x7f) - || (LCT->encoding != __ctype_encoding_8_bit)) - ? __C_toupper(c) - : ( __isctype_loc(c, _CTYPE_islower) - ? (unsigned char) - ( U + LCT->tbl8uplow[ ((int) - (LCT->idx8uplow[(U & 0x7f) - >> Cuplow_IDX_SHIFT]) - << Cuplow_IDX_SHIFT) - + (U & ((1 << Cuplow_IDX_SHIFT) - 1)) ]) - : c ); + return __toascii(c); /* locale-independent */ } -#else /* __CTYPE_HAS_8_BIT_LOCALES */ +#endif +/**********************************************************************/ +/* old uClibc extensions */ +/**********************************************************************/ +#ifdef L_isxlower -int toupper(int c) +int isxlower(int C) { - return __C_toupper(c); +#if defined(__UCLIBC_HAS_CTYPE_ENFORCED__) + assert(CTYPE_DOMAIN_CHECK(C)); + return (__isctype(C, (_ISxdigit|_ISupper)) == _ISxdigit); +#elif defined(__UCLIBC_HAS_CTYPE_CHECKED__) + return CTYPE_DOMAIN_CHECK(C) + ? (__isctype(C, (_ISxdigit|_ISupper)) == _ISxdigit) + : 0; +#elif defined(__UCLIBC_HAS_CTYPE_UNSAFE__) + return (__isctype(C, (_ISxdigit|_ISupper)) == _ISxdigit); +#else /* No checking done. */ +#error Unknown type of ctype checking! +#endif } -#endif /* __CTYPE_HAS_8_BIT_LOCALES */ +#endif +/**********************************************************************/ +#ifdef L_isxupper + +int isxupper(int C) +{ +#if defined(__UCLIBC_HAS_CTYPE_ENFORCED__) + assert(CTYPE_DOMAIN_CHECK(C)); + return (__isctype(C, (_ISxdigit|_ISlower)) == _ISxdigit); +#elif defined(__UCLIBC_HAS_CTYPE_CHECKED__) + return CTYPE_DOMAIN_CHECK(C) + ? (__isctype(C, (_ISxdigit|_ISlower)) == _ISxdigit) + : 0; +#elif defined(__UCLIBC_HAS_CTYPE_UNSAFE__) + return (__isctype(C, (_ISxdigit|_ISlower)) == _ISxdigit); +#else /* No checking done. */ +#error Unknown type of ctype checking! +#endif +} #endif /**********************************************************************/ -#ifdef L_isascii +/* glibc extensions */ +/**********************************************************************/ +#ifdef L_isctype -int isascii(int c) +int isctype(int c, int mask) { - return __isascii(c); + CTYPE_BODY(NAME,c,mask) } #endif /**********************************************************************/ -#ifdef L_toascii +#if L___ctype_b_loc + +#ifdef __UCLIBC_HAS_XLOCALE__ -int toascii(int c) +const uint16_t **__ctype_b_loc(void) { - return __toascii(c); + return &(__UCLIBC_CURLOCALE_DATA).__ctype_b; } #endif + +#endif /**********************************************************************/ -#ifdef L_isxlower +#if L___ctype_tolower_loc + +#ifdef __UCLIBC_HAS_XLOCALE__ -int isxlower(int c) +const __ctype_touplow_t **__ctype_tolower_loc(void) { - return __isxlower(c); + return &(__UCLIBC_CURLOCALE_DATA).__ctype_tolower; } #endif + +#endif /**********************************************************************/ -#ifdef L_isxupper +#if L___ctype_toupper_loc + +#ifdef __UCLIBC_HAS_XLOCALE__ -int isxupper(int c) +const __ctype_touplow_t **__ctype_toupper_loc(void) { - return __isxupper(c); + return &(__UCLIBC_CURLOCALE_DATA).__ctype_toupper; } #endif + +#endif /**********************************************************************/ -#ifdef L___isctype_loc -#ifdef __CTYPE_HAS_8_BIT_LOCALES +#ifdef L___C_ctype_b + +const uint16_t __C_ctype_b_data[] = { +#ifdef __UCLIBC_HAS_CTYPE_SIGNED__ + /* -128 M-^@ */ 0, + /* -127 M-^A */ 0, + /* -126 M-^B */ 0, + /* -125 M-^C */ 0, + /* -124 M-^D */ 0, + /* -123 M-^E */ 0, + /* -122 M-^F */ 0, + /* -121 M-^G */ 0, + /* -120 M-^H */ 0, + /* -119 M-^I */ 0, + /* -118 M-^J */ 0, + /* -117 M-^K */ 0, + /* -116 M-^L */ 0, + /* -115 M-^M */ 0, + /* -114 M-^N */ 0, + /* -113 M-^O */ 0, + /* -112 M-^P */ 0, + /* -111 M-^Q */ 0, + /* -110 M-^R */ 0, + /* -109 M-^S */ 0, + /* -108 M-^T */ 0, + /* -107 M-^U */ 0, + /* -106 M-^V */ 0, + /* -105 M-^W */ 0, + /* -104 M-^X */ 0, + /* -103 M-^Y */ 0, + /* -102 M-^Z */ 0, + /* -101 M-^[ */ 0, + /* -100 M-^\ */ 0, + /* -99 M-^] */ 0, + /* -98 M-^^ */ 0, + /* -97 M-^_ */ 0, + /* -96 M- */ 0, + /* -95 M-! */ 0, + /* -94 M-" */ 0, + /* -93 M-# */ 0, + /* -92 M-$ */ 0, + /* -91 M-% */ 0, + /* -90 M-& */ 0, + /* -89 M-' */ 0, + /* -88 M-( */ 0, + /* -87 M-) */ 0, + /* -86 M-* */ 0, + /* -85 M-+ */ 0, + /* -84 M-, */ 0, + /* -83 M-- */ 0, + /* -82 M-. */ 0, + /* -81 M-/ */ 0, + /* -80 M-0 */ 0, + /* -79 M-1 */ 0, + /* -78 M-2 */ 0, + /* -77 M-3 */ 0, + /* -76 M-4 */ 0, + /* -75 M-5 */ 0, + /* -74 M-6 */ 0, + /* -73 M-7 */ 0, + /* -72 M-8 */ 0, + /* -71 M-9 */ 0, + /* -70 M-: */ 0, + /* -69 M-; */ 0, + /* -68 M-< */ 0, + /* -67 M-= */ 0, + /* -66 M-> */ 0, + /* -65 M-? */ 0, + /* -64 M-@ */ 0, + /* -63 M-A */ 0, + /* -62 M-B */ 0, + /* -61 M-C */ 0, + /* -60 M-D */ 0, + /* -59 M-E */ 0, + /* -58 M-F */ 0, + /* -57 M-G */ 0, + /* -56 M-H */ 0, + /* -55 M-I */ 0, + /* -54 M-J */ 0, + /* -53 M-K */ 0, + /* -52 M-L */ 0, + /* -51 M-M */ 0, + /* -50 M-N */ 0, + /* -49 M-O */ 0, + /* -48 M-P */ 0, + /* -47 M-Q */ 0, + /* -46 M-R */ 0, + /* -45 M-S */ 0, + /* -44 M-T */ 0, + /* -43 M-U */ 0, + /* -42 M-V */ 0, + /* -41 M-W */ 0, + /* -40 M-X */ 0, + /* -39 M-Y */ 0, + /* -38 M-Z */ 0, + /* -37 M-[ */ 0, + /* -36 M-\ */ 0, + /* -35 M-] */ 0, + /* -34 M-^ */ 0, + /* -33 M-_ */ 0, + /* -32 M-` */ 0, + /* -31 M-a */ 0, + /* -30 M-b */ 0, + /* -29 M-c */ 0, + /* -28 M-d */ 0, + /* -27 M-e */ 0, + /* -26 M-f */ 0, + /* -25 M-g */ 0, + /* -24 M-h */ 0, + /* -23 M-i */ 0, + /* -22 M-j */ 0, + /* -21 M-k */ 0, + /* -20 M-l */ 0, + /* -19 M-m */ 0, + /* -18 M-n */ 0, + /* -17 M-o */ 0, + /* -16 M-p */ 0, + /* -15 M-q */ 0, + /* -14 M-r */ 0, + /* -13 M-s */ 0, + /* -12 M-t */ 0, + /* -11 M-u */ 0, + /* -10 M-v */ 0, + /* -9 M-w */ 0, + /* -8 M-x */ 0, + /* -7 M-y */ 0, + /* -6 M-z */ 0, + /* -5 M-{ */ 0, + /* -4 M-| */ 0, + /* -3 M-} */ 0, + /* -2 M-~ */ 0, +#endif /* __UCLIBC_HAS_CTYPE_SIGNED__*/ + /* -1 M-^? */ 0, + /* 0 ^@ */ _IScntrl, + /* 1 ^A */ _IScntrl, + /* 2 ^B */ _IScntrl, + /* 3 ^C */ _IScntrl, + /* 4 ^D */ _IScntrl, + /* 5 ^E */ _IScntrl, + /* 6 ^F */ _IScntrl, + /* 7 ^G */ _IScntrl, + /* 8 ^H */ _IScntrl, + /* 9 ^I */ _ISspace|_ISblank|_IScntrl, + /* 10 ^J */ _ISspace|_IScntrl, + /* 11 ^K */ _ISspace|_IScntrl, + /* 12 ^L */ _ISspace|_IScntrl, + /* 13 ^M */ _ISspace|_IScntrl, + /* 14 ^N */ _IScntrl, + /* 15 ^O */ _IScntrl, + /* 16 ^P */ _IScntrl, + /* 17 ^Q */ _IScntrl, + /* 18 ^R */ _IScntrl, + /* 19 ^S */ _IScntrl, + /* 20 ^T */ _IScntrl, + /* 21 ^U */ _IScntrl, + /* 22 ^V */ _IScntrl, + /* 23 ^W */ _IScntrl, + /* 24 ^X */ _IScntrl, + /* 25 ^Y */ _IScntrl, + /* 26 ^Z */ _IScntrl, + /* 27 ^[ */ _IScntrl, + /* 28 ^\ */ _IScntrl, + /* 29 ^] */ _IScntrl, + /* 30 ^^ */ _IScntrl, + /* 31 ^_ */ _IScntrl, + /* 32 */ _ISspace|_ISprint|_ISblank, + /* 33 ! */ _ISprint|_ISgraph|_ISpunct, + /* 34 " */ _ISprint|_ISgraph|_ISpunct, + /* 35 # */ _ISprint|_ISgraph|_ISpunct, + /* 36 $ */ _ISprint|_ISgraph|_ISpunct, + /* 37 % */ _ISprint|_ISgraph|_ISpunct, + /* 38 & */ _ISprint|_ISgraph|_ISpunct, + /* 39 ' */ _ISprint|_ISgraph|_ISpunct, + /* 40 ( */ _ISprint|_ISgraph|_ISpunct, + /* 41 ) */ _ISprint|_ISgraph|_ISpunct, + /* 42 * */ _ISprint|_ISgraph|_ISpunct, + /* 43 + */ _ISprint|_ISgraph|_ISpunct, + /* 44 , */ _ISprint|_ISgraph|_ISpunct, + /* 45 - */ _ISprint|_ISgraph|_ISpunct, + /* 46 . */ _ISprint|_ISgraph|_ISpunct, + /* 47 / */ _ISprint|_ISgraph|_ISpunct, + /* 48 0 */ _ISdigit|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 49 1 */ _ISdigit|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 50 2 */ _ISdigit|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 51 3 */ _ISdigit|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 52 4 */ _ISdigit|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 53 5 */ _ISdigit|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 54 6 */ _ISdigit|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 55 7 */ _ISdigit|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 56 8 */ _ISdigit|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 57 9 */ _ISdigit|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 58 : */ _ISprint|_ISgraph|_ISpunct, + /* 59 ; */ _ISprint|_ISgraph|_ISpunct, + /* 60 < */ _ISprint|_ISgraph|_ISpunct, + /* 61 = */ _ISprint|_ISgraph|_ISpunct, + /* 62 > */ _ISprint|_ISgraph|_ISpunct, + /* 63 ? */ _ISprint|_ISgraph|_ISpunct, + /* 64 @ */ _ISprint|_ISgraph|_ISpunct, + /* 65 A */ _ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 66 B */ _ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 67 C */ _ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 68 D */ _ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 69 E */ _ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 70 F */ _ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 71 G */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 72 H */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 73 I */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 74 J */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 75 K */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 76 L */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 77 M */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 78 N */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 79 O */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 80 P */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 81 Q */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 82 R */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 83 S */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 84 T */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 85 U */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 86 V */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 87 W */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 88 X */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 89 Y */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 90 Z */ _ISupper|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 91 [ */ _ISprint|_ISgraph|_ISpunct, + /* 92 \ */ _ISprint|_ISgraph|_ISpunct, + /* 93 ] */ _ISprint|_ISgraph|_ISpunct, + /* 94 ^ */ _ISprint|_ISgraph|_ISpunct, + /* 95 _ */ _ISprint|_ISgraph|_ISpunct, + /* 96 ` */ _ISprint|_ISgraph|_ISpunct, + /* 97 a */ _ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 98 b */ _ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 99 c */ _ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 100 d */ _ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 101 e */ _ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 102 f */ _ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph|_ISalnum, + /* 103 g */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 104 h */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 105 i */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 106 j */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 107 k */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 108 l */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 109 m */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 110 n */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 111 o */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 112 p */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 113 q */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 114 r */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 115 s */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 116 t */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 117 u */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 118 v */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 119 w */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 120 x */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 121 y */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 122 z */ _ISlower|_ISalpha|_ISprint|_ISgraph|_ISalnum, + /* 123 { */ _ISprint|_ISgraph|_ISpunct, + /* 124 | */ _ISprint|_ISgraph|_ISpunct, + /* 125 } */ _ISprint|_ISgraph|_ISpunct, + /* 126 ~ */ _ISprint|_ISgraph|_ISpunct, + /* 127 ^? */ _IScntrl, + /* 128 M-^@ */ 0, + /* 129 M-^A */ 0, + /* 130 M-^B */ 0, + /* 131 M-^C */ 0, + /* 132 M-^D */ 0, + /* 133 M-^E */ 0, + /* 134 M-^F */ 0, + /* 135 M-^G */ 0, + /* 136 M-^H */ 0, + /* 137 M-^I */ 0, + /* 138 M-^J */ 0, + /* 139 M-^K */ 0, + /* 140 M-^L */ 0, + /* 141 M-^M */ 0, + /* 142 M-^N */ 0, + /* 143 M-^O */ 0, + /* 144 M-^P */ 0, + /* 145 M-^Q */ 0, + /* 146 M-^R */ 0, + /* 147 M-^S */ 0, + /* 148 M-^T */ 0, + /* 149 M-^U */ 0, + /* 150 M-^V */ 0, + /* 151 M-^W */ 0, + /* 152 M-^X */ 0, + /* 153 M-^Y */ 0, + /* 154 M-^Z */ 0, + /* 155 M-^[ */ 0, + /* 156 M-^\ */ 0, + /* 157 M-^] */ 0, + /* 158 M-^^ */ 0, + /* 159 M-^_ */ 0, + /* 160 M- */ 0, + /* 161 M-! */ 0, + /* 162 M-" */ 0, + /* 163 M-# */ 0, + /* 164 M-$ */ 0, + /* 165 M-% */ 0, + /* 166 M-& */ 0, + /* 167 M-' */ 0, + /* 168 M-( */ 0, + /* 169 M-) */ 0, + /* 170 M-* */ 0, + /* 171 M-+ */ 0, + /* 172 M-, */ 0, + /* 173 M-- */ 0, + /* 174 M-. */ 0, + /* 175 M-/ */ 0, + /* 176 M-0 */ 0, + /* 177 M-1 */ 0, + /* 178 M-2 */ 0, + /* 179 M-3 */ 0, + /* 180 M-4 */ 0, + /* 181 M-5 */ 0, + /* 182 M-6 */ 0, + /* 183 M-7 */ 0, + /* 184 M-8 */ 0, + /* 185 M-9 */ 0, + /* 186 M-: */ 0, + /* 187 M-; */ 0, + /* 188 M-< */ 0, + /* 189 M-= */ 0, + /* 190 M-> */ 0, + /* 191 M-? */ 0, + /* 192 M-@ */ 0, + /* 193 M-A */ 0, + /* 194 M-B */ 0, + /* 195 M-C */ 0, + /* 196 M-D */ 0, + /* 197 M-E */ 0, + /* 198 M-F */ 0, + /* 199 M-G */ 0, + /* 200 M-H */ 0, + /* 201 M-I */ 0, + /* 202 M-J */ 0, + /* 203 M-K */ 0, + /* 204 M-L */ 0, + /* 205 M-M */ 0, + /* 206 M-N */ 0, + /* 207 M-O */ 0, + /* 208 M-P */ 0, + /* 209 M-Q */ 0, + /* 210 M-R */ 0, + /* 211 M-S */ 0, + /* 212 M-T */ 0, + /* 213 M-U */ 0, + /* 214 M-V */ 0, + /* 215 M-W */ 0, + /* 216 M-X */ 0, + /* 217 M-Y */ 0, + /* 218 M-Z */ 0, + /* 219 M-[ */ 0, + /* 220 M-\ */ 0, + /* 221 M-] */ 0, + /* 222 M-^ */ 0, + /* 223 M-_ */ 0, + /* 224 M-` */ 0, + /* 225 M-a */ 0, + /* 226 M-b */ 0, + /* 227 M-c */ 0, + /* 228 M-d */ 0, + /* 229 M-e */ 0, + /* 230 M-f */ 0, + /* 231 M-g */ 0, + /* 232 M-h */ 0, + /* 233 M-i */ 0, + /* 234 M-j */ 0, + /* 235 M-k */ 0, + /* 236 M-l */ 0, + /* 237 M-m */ 0, + /* 238 M-n */ 0, + /* 239 M-o */ 0, + /* 240 M-p */ 0, + /* 241 M-q */ 0, + /* 242 M-r */ 0, + /* 243 M-s */ 0, + /* 244 M-t */ 0, + /* 245 M-u */ 0, + /* 246 M-v */ 0, + /* 247 M-w */ 0, + /* 248 M-x */ 0, + /* 249 M-y */ 0, + /* 250 M-z */ 0, + /* 251 M-{ */ 0, + /* 252 M-| */ 0, + /* 253 M-} */ 0, + /* 254 M-~ */ 0, + /* 255 M-^? */ 0 +}; + +const uint16_t *__C_ctype_b = __C_ctype_b_data + 1 +#ifdef __UCLIBC_HAS_CTYPE_SIGNED__ + + 127 +#endif + ; + +#ifndef __UCLIBC_HAS_XLOCALE__ + +const uint16_t *__ctype_b = __C_ctype_b_data + 1 +#ifdef __UCLIBC_HAS_CTYPE_SIGNED__ + + 127 +#endif + ; -/* This internal routine is similar to iswctype(), but it doesn't - * work for any non-standard types, itdoesn't work for "xdigit"s, - * and it doesn't work for chars between 0 and 0x7f (although that - * may change). */ +#endif -static const char ctype_range[] = { - __CTYPE_RANGES +#endif +/**********************************************************************/ +#ifdef L___C_ctype_tolower + +const __ctype_touplow_t __C_ctype_tolower_data[] = { +#ifdef __UCLIBC_HAS_CTYPE_SIGNED__ + -128, -127, -126, -125, + -124, -123, -122, -121, + -120, -119, -118, -117, + -116, -115, -114, -113, + -112, -111, -110, -109, + -108, -107, -106, -105, + -104, -103, -102, -101, + -100, -99, -98, -97, + -96, -95, -94, -93, + -92, -91, -90, -89, + -88, -87, -86, -85, + -84, -83, -82, -81, + -80, -79, -78, -77, + -76, -75, -74, -73, + -72, -71, -70, -69, + -68, -67, -66, -65, + -64, -63, -62, -61, + -60, -59, -58, -57, + -56, -55, -54, -53, + -52, -51, -50, -49, + -48, -47, -46, -45, + -44, -43, -42, -41, + -40, -39, -38, -37, + -36, -35, -34, -33, + -32, -31, -30, -29, + -28, -27, -26, -25, + -24, -23, -22, -21, + -20, -19, -18, -17, + -16, -15, -14, -13, + -12, -11, -10, -9, + -8, -7, -6, -5, + -4, -3, -2, -1, +#endif /* __UCLIBC_HAS_CTYPE_SIGNED__*/ + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 12, 13, 14, 15, + 16, 17, 18, 19, + 20, 21, 22, 23, + 24, 25, 26, 27, + 28, 29, 30, 31, + 32, 33, 34, 35, + 36, 37, 38, 39, + 40, 41, 42, 43, + 44, 45, 46, 47, + 48, 49, 50, 51, + 52, 53, 54, 55, + 56, 57, 58, 59, + 60, 61, 62, 63, + 64, 97 /* a */, 98 /* b */, 99 /* c */, + 100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */, + 104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */, + 108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */, + 112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */, + 116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */, + 120 /* x */, 121 /* y */, 122 /* z */, 91, + 92, 93, 94, 95, + 96, 97, 98, 99, + 100, 101, 102, 103, + 104, 105, 106, 107, + 108, 109, 110, 111, + 112, 113, 114, 115, + 116, 117, 118, 119, + 120, 121, 122, 123, + 124, 125, 126, 127, + 128, 129, 130, 131, + 132, 133, 134, 135, + 136, 137, 138, 139, + 140, 141, 142, 143, + 144, 145, 146, 147, + 148, 149, 150, 151, + 152, 153, 154, 155, + 156, 157, 158, 159, + 160, 161, 162, 163, + 164, 165, 166, 167, + 168, 169, 170, 171, + 172, 173, 174, 175, + 176, 177, 178, 179, + 180, 181, 182, 183, + 184, 185, 186, 187, + 188, 189, 190, 191, + 192, 193, 194, 195, + 196, 197, 198, 199, + 200, 201, 202, 203, + 204, 205, 206, 207, + 208, 209, 210, 211, + 212, 213, 214, 215, + 216, 217, 218, 219, + 220, 221, 222, 223, + 224, 225, 226, 227, + 228, 229, 230, 231, + 232, 233, 234, 235, + 236, 237, 238, 239, + 240, 241, 242, 243, + 244, 245, 246, 247, + 248, 249, 250, 251, + 252, 253, 254, 255 }; -int __isctype_loc(int c, int ct) -{ - unsigned char d; +const __ctype_touplow_t *__C_ctype_tolower = __C_ctype_tolower_data +#ifdef __UCLIBC_HAS_CTYPE_SIGNED__ + + 128 +#endif + ; - assert(((unsigned int)ct) < _CTYPE_isxdigit); - assert(((unsigned int)c) > 0x7f); +#ifndef __UCLIBC_HAS_XLOCALE__ -#if (CHAR_MIN == 0) /* We don't have signed chars... */ - if ((LCT->encoding != __ctype_encoding_8_bit) - || (((unsigned int) c) > UCHAR_MAX) - ) { - return 0; - } -#else - /* Allow non-EOF negative char values for glibc compatiblity. */ - if ((LCT->encoding != __ctype_encoding_8_bit) || (c == EOF) - || ( ((unsigned int)(c - CHAR_MIN)) > (UCHAR_MAX - CHAR_MIN)) - ) { - return 0; - } +const __ctype_touplow_t *__ctype_tolower = __C_ctype_tolower_data +#ifdef __UCLIBC_HAS_CTYPE_SIGNED__ + + 128 #endif + ; - /* TODO - test assumptions??? 8-bit chars -- or ensure in generator. */ +#endif -#define Cctype_TBL_MASK ((1 << Cctype_IDX_SHIFT) - 1) -#define Cctype_IDX_OFFSET (128 >> Cctype_IDX_SHIFT) +#endif +/**********************************************************************/ +#ifdef L___C_ctype_toupper + +const __ctype_touplow_t __C_ctype_toupper_data[] = { +#ifdef __UCLIBC_HAS_CTYPE_SIGNED__ + -128, -127, -126, -125, + -124, -123, -122, -121, + -120, -119, -118, -117, + -116, -115, -114, -113, + -112, -111, -110, -109, + -108, -107, -106, -105, + -104, -103, -102, -101, + -100, -99, -98, -97, + -96, -95, -94, -93, + -92, -91, -90, -89, + -88, -87, -86, -85, + -84, -83, -82, -81, + -80, -79, -78, -77, + -76, -75, -74, -73, + -72, -71, -70, -69, + -68, -67, -66, -65, + -64, -63, -62, -61, + -60, -59, -58, -57, + -56, -55, -54, -53, + -52, -51, -50, -49, + -48, -47, -46, -45, + -44, -43, -42, -41, + -40, -39, -38, -37, + -36, -35, -34, -33, + -32, -31, -30, -29, + -28, -27, -26, -25, + -24, -23, -22, -21, + -20, -19, -18, -17, + -16, -15, -14, -13, + -12, -11, -10, -9, + -8, -7, -6, -5, + -4, -3, -2, -1, +#endif /* __UCLIBC_HAS_CTYPE_SIGNED__*/ + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 12, 13, 14, 15, + 16, 17, 18, 19, + 20, 21, 22, 23, + 24, 25, 26, 27, + 28, 29, 30, 31, + 32, 33, 34, 35, + 36, 37, 38, 39, + 40, 41, 42, 43, + 44, 45, 46, 47, + 48, 49, 50, 51, + 52, 53, 54, 55, + 56, 57, 58, 59, + 60, 61, 62, 63, + 64, 65, 66, 67, + 68, 69, 70, 71, + 72, 73, 74, 75, + 76, 77, 78, 79, + 80, 81, 82, 83, + 84, 85, 86, 87, + 88, 89, 90, 91, + 92, 93, 94, 95, + 96, 65 /* A */, 66 /* B */, 67 /* C */, + 68 /* D */, 69 /* E */, 70 /* F */, 71 /* G */, + 72 /* H */, 73 /* I */, 74 /* J */, 75 /* K */, + 76 /* L */, 77 /* M */, 78 /* N */, 79 /* O */, + 80 /* P */, 81 /* Q */, 82 /* R */, 83 /* S */, + 84 /* T */, 85 /* U */, 86 /* V */, 87 /* W */, + 88 /* X */, 89 /* Y */, 90 /* Z */, 123, + 124, 125, 126, 127, + 128, 129, 130, 131, + 132, 133, 134, 135, + 136, 137, 138, 139, + 140, 141, 142, 143, + 144, 145, 146, 147, + 148, 149, 150, 151, + 152, 153, 154, 155, + 156, 157, 158, 159, + 160, 161, 162, 163, + 164, 165, 166, 167, + 168, 169, 170, 171, + 172, 173, 174, 175, + 176, 177, 178, 179, + 180, 181, 182, 183, + 184, 185, 186, 187, + 188, 189, 190, 191, + 192, 193, 194, 195, + 196, 197, 198, 199, + 200, 201, 202, 203, + 204, 205, 206, 207, + 208, 209, 210, 211, + 212, 213, 214, 215, + 216, 217, 218, 219, + 220, 221, 222, 223, + 224, 225, 226, 227, + 228, 229, 230, 231, + 232, 233, 234, 235, + 236, 237, 238, 239, + 240, 241, 242, 243, + 244, 245, 246, 247, + 248, 249, 250, 251, + 252, 253, 254, 255 +}; + +const __ctype_touplow_t *__C_ctype_toupper = __C_ctype_toupper_data +#ifdef __UCLIBC_HAS_CTYPE_SIGNED__ + + 128 +#endif + ; + +#ifndef __UCLIBC_HAS_XLOCALE__ + +const __ctype_touplow_t *__ctype_toupper = __C_ctype_toupper_data +#ifdef __UCLIBC_HAS_CTYPE_SIGNED__ + + 128 +#endif + ; - c &= 0x7f; -#ifdef Cctype_PACKED - d = LCT->tbl8ctype[ ((int)(LCT->idx8ctype[(U >> Cctype_IDX_SHIFT) ]) - << (Cctype_IDX_SHIFT - 1)) - + ((U & Cctype_TBL_MASK) >> 1)]; - d = (U & 1) ? (d >> 4) : (d & 0xf); -#else - d = LCT->tbl8ctype[ ((int)(LCT->idx8ctype[(U >> Cctype_IDX_SHIFT) ]) - << Cctype_IDX_SHIFT) - + (U & Cctype_TBL_MASK) ]; #endif - return ( ((unsigned char)(d - ctype_range[2*ct])) <= ctype_range[2*ct+1] ); -} -#endif /* __CTYPE_HAS_8_BIT_LOCALES */ #endif /**********************************************************************/ diff --git a/libc/misc/intl/Makefile b/libc/misc/intl/Makefile new file mode 100644 index 000000000..75fbb321f --- /dev/null +++ b/libc/misc/intl/Makefile @@ -0,0 +1,50 @@ +# Makefile for uClibc +# +# Copyright (C) 2000 by Lineo, inc. +# Copyright (C) 2000,2001 Erik Andersen <andersen@uclibc.org> +# +# This program 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 program 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 program; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Derived in part from the Linux-8086 C library, the GNU C Library, and several +# other sundry sources. Files within this library are copyright by their +# respective copyright holders. + +TOPDIR=../../../ +include $(TOPDIR)Rules.mak + +MSRC= intl.c +MOBJ= gettext.o ngettext.o dgettext.o dcgettext.o dngettext.o dcngettext.o \ + textdomain.o bindtextdomain.o bind_textdomain_codeset.o + +OBJS=$(MOBJ) + +all: $(OBJS) $(LIBC) + +$(LIBC): ar-target + +ar-target: $(OBJS) + $(AR) $(ARFLAGS) $(LIBC) $(OBJS) + +$(MOBJ): $(MSRC) + $(CC) $(CFLAGS) -DL_$* $< -c -o $*.o + $(STRIPTOOL) -x -R .note -R .comment $*.o + +$(COBJS): %.o : %.c + $(CC) $(CFLAGS) -c $< -o $@ + $(STRIPTOOL) -x -R .note -R .comment $*.o + +clean: + rm -f *.[oa] *~ core + diff --git a/libc/misc/intl/intl.c b/libc/misc/intl/intl.c new file mode 100644 index 000000000..183ffcc92 --- /dev/null +++ b/libc/misc/intl/intl.c @@ -0,0 +1,149 @@ +/* Copyright (C) 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. + */ + +/* + * Supply some weaks for gettext and friends. Used by strerror*(). + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#undef __OPTIMIZE__ +#include <libintl.h> + +/**********************************************************************/ +#ifdef L_gettext + +char *weak_function gettext(const char *msgid) +{ + return (char *) msgid; +} + +#endif +/**********************************************************************/ +#ifdef L_dgettext + +char *__uClibc_dgettext(const char *domainname, + const char *msgid) +{ + return (char *) msgid; +} + +weak_alias (__uClibc_dgettext, __dgettext) +weak_alias (__uClibc_dgettext, dgettext) + +#endif +/**********************************************************************/ +#ifdef L_dcgettext + +char * __uClibc_dcgettext(const char *domainname, + const char *msgid, int category) +{ + return (char *) msgid; +} + +weak_alias (__uClibc_dcgettext, __dcgettext) +weak_alias (__uClibc_dcgettext, dcgettext) + +#endif +/**********************************************************************/ +#ifdef L_ngettext + +char *weak_function ngettext(const char *msgid1, const char *msgid2, + unsigned long int n) +{ + return (char *) ((n == 1) ? msgid1 : msgid2); +} + +#endif +/**********************************************************************/ +#ifdef L_dngettext + +char *weak_function dngettext(const char *domainname, const char *msgid1, + const char *msgid2, unsigned long int n) +{ + return (char *) ((n == 1) ? msgid1 : msgid2); +} + +#endif +/**********************************************************************/ +#ifdef L_dcngettext + +char *weak_function dcngettext(const char *domainname, const char *msgid1, + const char *msgid2, unsigned long int n, + int category) +{ + return (char *) ((n == 1) ? msgid1 : msgid2); +} + +#endif +/**********************************************************************/ +#ifdef L_textdomain + +char *weak_function textdomain(const char *domainname) +{ + static const char default_str[] = "messages"; + + if (domainname && *domainname && strcmp(domainname, default_str)) { + __set_errno(EINVAL); + return NULL; + } + return (char *) default_str; +} + +#endif +/**********************************************************************/ +#ifdef L_bindtextdomain + +char *weak_function bindtextdomain(const char *domainname, const char *dirname) +{ + static const char dir[] = "/"; + + if (!domainname || !*domainname + || (dirname +#if 1 + && ((dirname[0] != '/') || dirname[1]) +#else + && strcmp(dirname, dir) +#endif + ) + ) { + __set_errno(EINVAL); + return NULL; + } + + return (char *) dir; +} + +#endif +/**********************************************************************/ +#ifdef L_bind_textdomain_codeset + +/* Specify the character encoding in which the messages from the + DOMAINNAME message catalog will be returned. */ +char *weak_function bind_textdomain_codeset(const char *domainname, + const char *codeset) +{ + if (!domainname || !*domainname || codeset) { + __set_errno(EINVAL); + } + return NULL; +} + +#endif +/**********************************************************************/ diff --git a/libc/misc/locale/Makefile b/libc/misc/locale/Makefile index 29c8cd5a0..8ad041e69 100644 --- a/libc/misc/locale/Makefile +++ b/libc/misc/locale/Makefile @@ -26,14 +26,29 @@ include $(TOPDIR)Rules.mak MSRC= locale.c MOBJ= setlocale.o localeconv.o _locale_init.o nl_langinfo.o +MOBJx= -OBJS= $(MOBJ) +ifeq ($(UCLIBC_HAS_LOCALE),y) + MOBJ += newlocale.o __locale_mbrtowc_l.o +endif + +ifeq ($(UCLIBC_HAS_XLOCALE),y) + MOBJx += nl_langinfo_l.o duplocale.o freelocale.o uselocale.o __curlocale.o +endif + +OBJS= $(MOBJ) $(MOBJx) ifeq ($(UCLIBC_HAS_LOCALE),y) - OBJS += locale_data.o + OBJS += $(COBJS) locale_data.o endif -all: data $(OBJS) $(LIBC) +DATA= +ifeq ($(UCLIBC_HAS_LOCALE),y) + DATA += locale_data.o +endif + +all: $(DATA) $(OBJS) $(LIBC) + $(LIBC): ar-target @@ -44,12 +59,14 @@ $(MOBJ): $(MSRC) $(CC) $(CFLAGS) -DL_$* $< -c -o $*.o $(STRIPTOOL) -x -R .note -R .comment $*.o +$(MOBJx): $(MSRC) + $(CC) $(CFLAGS) -DL_$* -D__UCLIBC_DO_XLOCALE $< -c -o $*.o + $(STRIPTOOL) -x -R .note -R .comment $*.o + $(OBJS): Makefile -data: -ifeq ($(UCLIBC_HAS_LOCALE),y) - make -C $(TOPDIR)/extra/locale -endif +# locale_data.o: +# make -C $(TOPDIR)/extra/locale clean: rm -f *.[oa] *~ core diff --git a/libc/misc/locale/locale.c b/libc/misc/locale/locale.c index 9c162a980..071a8df71 100644 --- a/libc/misc/locale/locale.c +++ b/libc/misc/locale/locale.c @@ -36,28 +36,94 @@ */ #define _GNU_SOURCE -#include <locale.h> + +#define __CTYPE_HAS_8_BIT_LOCALES 1 + + #include <string.h> #include <stdlib.h> #include <stddef.h> #include <limits.h> #include <stdint.h> #include <assert.h> +#include <errno.h> +#include <ctype.h> + +#undef __LOCALE_C_ONLY +#ifndef __UCLIBC_HAS_LOCALE__ +#define __LOCALE_C_ONLY +#endif /* __UCLIBC_HAS_LOCALE__ */ + + +#ifdef __LOCALE_C_ONLY + +#include <locale.h> + +#else /* __LOCALE_C_ONLY */ + +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_setlocale +#warning TODO: Fix the __CTYPE_HAS_8_BIT_LOCALES define at the top of the file. +#warning TODO: Fix __WCHAR_ENABLED. +#endif +#endif -#ifndef __LOCALE_C_ONLY +/* Need to include this before locale.h and xlocale.h! */ +#include <bits/uClibc_locale.h> -#define CUR_LOCALE_SPEC (__global_locale.cur_locale) #undef CODESET_LIST #define CODESET_LIST (__locale_mmap->codeset_list) +#ifdef __UCLIBC_HAS_XLOCALE__ +#include <xlocale.h> +#include <locale.h> +#else /* __UCLIBC_HAS_XLOCALE__ */ +/* We need this internally... */ +#define __UCLIBC_HAS_XLOCALE__ 1 +#include <xlocale.h> +#include <locale.h> +#undef __UCLIBC_HAS_XLOCALE__ +#endif /* __UCLIBC_HAS_XLOCALE__ */ + +#include <wchar.h> + +#define LOCALE_NAMES (__locale_mmap->locale_names5) +#define LOCALES (__locale_mmap->locales) +#define LOCALE_AT_MODIFIERS (__locale_mmap->locale_at_modifiers) +#define CATEGORY_NAMES (__locale_mmap->lc_names) + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning REMINDER: redo the MAX_LOCALE_STR stuff... +#endif +#define MAX_LOCALE_STR 256 /* TODO: Only sufficient for current case. */ +#define MAX_LOCALE_CATEGORY_STR 32 /* TODO: Only sufficient for current case. */ +/* Note: Best if MAX_LOCALE_CATEGORY_STR is a power of 2. */ + +extern int _locale_set_l(const unsigned char *p, __locale_t base); +extern void _locale_init_l(__locale_t base); + #endif /* __LOCALE_C_ONLY */ +#undef LOCALE_STRING_SIZE +#define LOCALE_SELECTOR_SIZE (2 * __LC_ALL + 2) + +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_setlocale +#warning TODO: Create a C locale selector string. +#endif +#endif +#define C_LOCALE_SELECTOR "\x23\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" + + +#include <langinfo.h> +#include <nl_types.h> + /**********************************************************************/ #ifdef L_setlocale #ifdef __LOCALE_C_ONLY -link_warning(setlocale,"the 'setlocale' function supports only C|POSIX locales") +link_warning(setlocale,"REMINDER: The 'setlocale' function supports only C|POSIX locales.") static const char C_string[] = "C"; @@ -74,273 +140,125 @@ char *setlocale(int category, register const char *locale) #else /* ---------------------------------------------- __LOCALE_C_ONLY */ -#if !defined(NUM_LOCALES) || (NUM_LOCALES <= 1) -#error locales enabled, but not data other than for C locale! +#ifdef __UCLIBC_HAS_THREADS__ +link_warning(setlocale,"REMINDER: The 'setlocale' function is _not_ threadsafe except for simple queries.") #endif -#define LOCALE_NAMES (__locale_mmap->locale_names5) -#define LOCALES (__locale_mmap->locales) -#define LOCALE_AT_MODIFIERS (__locale_mmap->locale_at_modifiers) -#define CATEGORY_NAMES (__locale_mmap->lc_names) +#if !defined(__LOCALE_DATA_NUM_LOCALES) || (__LOCALE_DATA_NUM_LOCALES <= 1) +#error locales enabled, but not data other than for C locale! +#endif +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Move posix and utf8 strings. +#endif static const char posix[] = "POSIX"; static const char utf8[] = "UTF-8"; #ifdef __UCLIBC_MJN3_ONLY__ -#warning REMINDER: redo the MAX_LOCALE_STR stuff... +#warning TODO: Fix dimensions of hr_locale. #endif -#define MAX_LOCALE_STR 256 /* TODO: Only sufficient for current case. */ - -static char hr_locale[MAX_LOCALE_STR]; +/* Individual category strings start at hr_locale + category * MAX_LOCALE_CATEGORY. + * This holds for LC_ALL as well. + */ +static char hr_locale[(MAX_LOCALE_CATEGORY_STR * LC_ALL) + MAX_LOCALE_STR]; -static __inline char *human_readable_locale(int category, const unsigned char *s) +static void update_hr_locale(const unsigned char *spec) { const unsigned char *loc; + const unsigned char *s; char *n; - int i; - - ++s; - - if (category == LC_ALL) { - for (i = 0 ; i < LC_ALL-1 ; i += 2) { - if ((s[i] != s[i+2]) || (s[i+1] != s[i+3])) { - goto SKIP; - } - } - /* All categories the same, so simplify string by using a single - * category. */ - category = LC_CTYPE; - } - - SKIP: - i = (category == LC_ALL) ? 0 : category; - s += 2*i; - n = hr_locale; + int i, category, done; + done = category = 0; do { - if ((*s != 0xff) || (s[1] != 0xff)) { - loc = LOCALES + WIDTH_LOCALES * ((((int)(*s & 0x7f)) << 7) + (s[1] & 0x7f)); - if (category == LC_ALL) { - n = stpcpy(n, CATEGORY_NAMES + (int) CATEGORY_NAMES[i]); - *n++ = '='; - } - if (*loc == 0) { - *n++ = 'C'; - *n = 0; - } else { - char at = 0; - memcpy(n, LOCALE_NAMES + 5*((*loc)-1), 5); - if (n[2] != '_') { - at = n[2]; - n[2] = '_'; - } - n += 5; - *n++ = '.'; - if (loc[2] == 2) { - n = stpcpy(n, utf8); - } else if (loc[2] >= 3) { - n = stpcpy(n, CODESET_LIST + (int)(CODESET_LIST[loc[2] - 3])); - } - if (at) { - const char *q; - *n++ = '@'; - q = LOCALE_AT_MODIFIERS; - do { - if (q[1] == at) { - n = stpcpy(n, q+2); - break; - } - q += 2 + *q; - } while (*q); + s = spec + 1; + n = hr_locale + category * MAX_LOCALE_CATEGORY_STR; + + if (category == LC_ALL) { + done = 1; + for (i = 0 ; i < LC_ALL-1 ; i += 2) { + if ((s[i] != s[i+2]) || (s[i+1] != s[i+3])) { + goto SKIP; } } - *n++ = ';'; + /* All categories the same, so simplify string by using a single + * category. */ + category = LC_CTYPE; } - s += 2; - } while (++i < category); - *--n = 0; /* Remove trailing ';' and nul-terminate. */ - assert(n-hr_locale < MAX_LOCALE_STR); - return hr_locale; -} - -static int find_locale(int category, const char *p, unsigned char *new_locale) -{ - int i; - const unsigned char *s; - uint16_t n; - unsigned char lang_cult, codeset; - -#if defined(LOCALE_AT_MODIFIERS_LENGTH) && 1 - /* Support standard locale handling for @-modifiers. */ + SKIP: + i = (category == LC_ALL) ? 0 : category; + s += 2*i; -#ifdef __UCLIBC_MJN3_ONLY__ -#warning REMINDER: fix buf size in find_locale -#endif - char buf[18]; /* TODO: 7+{max codeset name length} */ - const char *q; - - if ((q = strchr(p,'@')) != NULL) { - if ((((size_t)((q-p)-5)) > (sizeof(buf) - 5)) || (p[2] != '_')) { - return 0; - } - /* locale name at least 5 chars long and 3rd char is '_' */ - s = LOCALE_AT_MODIFIERS; do { - if (!strcmp(s+2, q+1)) { - break; - } - s += 2 + *s; /* TODO - fix this throughout */ - } while (*s); - if (!*s) { - return 0; - } - assert(q - p < sizeof(buf)); - memcpy(buf, p, q-p); - buf[q-p] = 0; - buf[2] = s[1]; - p = buf; - } -#endif - - lang_cult = codeset = 0; /* Assume C and default codeset. */ - if (((*p == 'C') && !p[1]) || !strcmp(p, posix)) { - goto FIND_LOCALE; - } - - if ((strlen(p) > 5) && (p[5] == '.')) { /* Codeset in locale name? */ - /* TODO: maybe CODESET_LIST + *s ??? */ - /* 7bit is 1, UTF-8 is 2, 8-bit is >= 3 */ - codeset = 2; - if (strcmp(utf8,p+6) != 0) {/* TODO - fix! */ - s = CODESET_LIST; - do { - ++codeset; /* Increment codeset first. */ - if (!strcmp(CODESET_LIST+*s, p+6)) { - goto FIND_LANG_CULT; + if ((*s != 0xff) || (s[1] != 0xff)) { + loc = LOCALES + + __LOCALE_DATA_WIDTH_LOCALES * ((((int)(*s & 0x7f)) << 7) + + (s[1] & 0x7f)); + if (category == LC_ALL) { + n = stpcpy(n, CATEGORY_NAMES + (int) CATEGORY_NAMES[i]); + *n++ = '='; } - } while (*++s); - return 0; /* No matching codeset! */ - } - } - - FIND_LANG_CULT: /* Find language_culture number. */ - s = LOCALE_NAMES; - do { /* TODO -- do a binary search? */ - /* TODO -- fix gen_mmap!*/ - ++lang_cult; /* Increment first since C/POSIX is 0. */ - if (!strncmp(s,p,5)) { /* Found a matching locale name; */ - goto FIND_LOCALE; - } - s += 5; - } while (lang_cult < NUM_LOCALE_NAMES); - return 0; /* No matching language_culture! */ - - FIND_LOCALE: /* Find locale row matching name and codeset */ - s = LOCALES; - n = 0; - do { /* TODO -- do a binary search? */ - if ((lang_cult == *s) && ((codeset == s[1]) || (codeset == s[2]))) { - i = ((category == LC_ALL) ? 0 : category); - s = new_locale + 2*i; - do { - /* Encode current locale row number. */ - *((unsigned char *) ++s) = (n >> 7) | 0x80; - *((unsigned char *) ++s) = (n & 0x7f) | 0x80; - } while (++i < category); - - return i; /* Return non-zero */ - } - s += WIDTH_LOCALES; - ++n; - } while (n <= NUM_LOCALES); /* We started at 1!!! */ - - return 0; /* Unsupported locale. */ -} - -static unsigned char *composite_locale(int category, const char *locale, unsigned char *new_locale) -{ - char buf[MAX_LOCALE_STR]; - char *t; - char *e; - int c; - - if (!strchr(locale,'=')) { - if (!find_locale(category, locale, new_locale)) { - return NULL; - } - return new_locale; - } - - if (strlen(locale) >= sizeof(buf)) { - return NULL; - } - stpcpy(buf, locale); - - t = strtok_r(buf, "=", &e); /* This can't fail because of strchr test above. */ - do { - for (c = 0 ; c < LC_ALL ; c++) { /* Find the category... */ - if (!strcmp(CATEGORY_NAMES + (int) CATEGORY_NAMES[c], t)) { - break; - } - } - t = strtok_r(NULL, ";", &e); - if ((category == LC_ALL) || (c == category)) { - if (!t || !find_locale(c, t, new_locale)) { - return NULL; + if (*loc == 0) { + *n++ = 'C'; + *n = 0; + } else { + char at = 0; + memcpy(n, LOCALE_NAMES + 5*((*loc)-1), 5); + if (n[2] != '_') { + at = n[2]; + n[2] = '_'; + } + n += 5; + *n++ = '.'; + if (loc[2] == 2) { + n = stpcpy(n, utf8); + } else if (loc[2] >= 3) { + n = stpcpy(n, CODESET_LIST + (int)(CODESET_LIST[loc[2] - 3])); + } + if (at) { + const char *q; + *n++ = '@'; + q = LOCALE_AT_MODIFIERS; + do { + if (q[1] == at) { + n = stpcpy(n, q+2); + break; + } + q += 2 + *q; + } while (*q); + } + } + *n++ = ';'; } - } - } while ((t = strtok_r(NULL, "=", &e)) != NULL); + s += 2; + } while (++i < category); + *--n = 0; /* Remove trailing ';' and nul-terminate. */ - return new_locale; + ++category; + } while (!done); } char *setlocale(int category, const char *locale) { - const unsigned char *p; - int i; - unsigned char new_locale[LOCALE_STRING_SIZE]; - if (((unsigned int)(category)) > LC_ALL) { - /* TODO - set errno? SUSv3 doesn't say too. */ +#if 0 + __set_errno(EINVAL); /* glibc sets errno -- SUSv3 doesn't say. */ +#endif return NULL; /* Illegal/unsupported category. */ } - if (locale != NULL) { /* Not just a query... */ - stpcpy(new_locale, CUR_LOCALE_SPEC); /* Start with current. */ - - if (!*locale) { /* locale == "", so check environment. */ - i = ((category == LC_ALL) ? 0 : category); - do { - /* Note: SUSv3 doesn't define a fallback mechanism here. So, - * if LC_ALL is invalid, we do _not_ continue trying the other - * environment vars. */ - if (!(p = getenv("LC_ALL"))) { - if (!(p = getenv(CATEGORY_NAMES + CATEGORY_NAMES[i]))) { - if (!(p = getenv("LANG"))) { - p = posix; - } - } - } - - /* The user set something... is it valid? */ - /* Note: Since we don't support user-supplied locales and - * alternate paths, we don't need to worry about special - * handling for suid/sgid apps. */ - if (!find_locale(i, p, new_locale)) { - return NULL; - } - } while (++i < category); - } else if (!composite_locale(category, locale, new_locale)) { + if (locale != NULL) { /* Not just a query... */ + if (!__newlocale((category == LC_ALL) ? LC_ALL_MASK : (1 << category), + locale, __global_locale) + ) { /* Failed! */ return NULL; } - - /* TODO: Ok, everything checks out, so install the new locale. */ - _locale_set(new_locale); + update_hr_locale(__global_locale->cur_locale); } /* Either a query or a successful set, so return current locale string. */ - return human_readable_locale(category, CUR_LOCALE_SPEC); + return hr_locale + (category * MAX_LOCALE_CATEGORY_STR); } #endif /* __LOCALE_C_ONLY */ @@ -355,7 +273,7 @@ char *setlocale(int category, const char *locale) #ifdef __LOCALE_C_ONLY -link_warning(localeconv,"the 'localeconv' function is hardwired for C/POSIX locale only") +link_warning(localeconv,"REMINDER: The 'localeconv' function is hardwired for C/POSIX locale only.") static struct lconv the_lconv; @@ -387,7 +305,7 @@ static struct lconv the_lconv; struct lconv *localeconv(void) { register char *p = (char *) &the_lconv; - register char **q = (char **) &__global_locale.decimal_point; + register char **q = (char **) &(__UCLIBC_CURLOCALE_DATA).decimal_point; do { *((char **)p) = *q; @@ -408,16 +326,22 @@ struct lconv *localeconv(void) #endif /**********************************************************************/ -#ifdef L__locale_init +#if defined(L__locale_init) && !defined(__LOCALE_C_ONLY) -#ifndef __LOCALE_C_ONLY +static __uclibc_locale_t __global_locale_data; -#define C_LOCALE_SELECTOR "\x23\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80" -#define LOCALE_INIT_FAILED "locale init failed!\n" +__locale_t __global_locale = &__global_locale_data; -#define CUR_LOCALE_SPEC (__global_locale.cur_locale) +#ifdef __UCLIBC_HAS_XLOCALE__ +__locale_t __curlocale_var = &__global_locale_data; +#endif -__locale_t __global_locale; +/*----------------------------------------------------------------------*/ +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Move utf8 and ascii strings. +#endif +static const char utf8[] = "UTF-8"; +static const char ascii[] = "ASCII"; typedef struct { uint16_t num_base; @@ -461,9 +385,8 @@ typedef struct { uint16_t multistart_offset; } coldata_der_t; -static int init_cur_collate(int der_num) +static int init_cur_collate(int der_num, __collate_t *cur_collate) { - __collate_t *cur_collate = &__global_locale.collate; const uint16_t *__locale_collate_tbl = __locale_mmap->collate_data; coldata_header_t *cdh; coldata_base_t *cdb; @@ -485,9 +408,16 @@ static int init_cur_collate(int der_num) cdh = (coldata_header_t *) __locale_collate_tbl; +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Should we assert here? +#endif +#if 0 if (der_num >= cdh->num_der) { return 0; } +#else + assert((der_num < cdh->num_der)); +#endif cdd = (coldata_der_t *)(__locale_collate_tbl + (sizeof(coldata_header_t) @@ -539,14 +469,17 @@ static int init_cur_collate(int der_num) cur_collate->MAX_WEIGHTS = cdh->MAX_WEIGHTS; #ifdef __UCLIBC_MJN3_ONLY__ -#warning if calloc fails, this is WRONG. there is also a memory leak here at the moment -#warning fix the +1 by increasing max_col_index? +#warning CONSIDER: Fix the +1 by increasing max_col_index? +#warning CONSIDER: Since this collate info is dependent only on LC_COLLATE ll_cc and not on codeset, we could just globally allocate this for each in a table #endif - cur_collate->index2weight = calloc(2*cur_collate->max_col_index+2, sizeof(uint16_t)); + + cur_collate->index2weight = calloc(2*cur_collate->max_col_index+2, + sizeof(uint16_t)); if (!cur_collate->index2weight) { return 0; } - cur_collate->index2ruleidx = cur_collate->index2weight + cur_collate->max_col_index + 1; + cur_collate->index2ruleidx = cur_collate->index2weight + + cur_collate->max_col_index + 1; memcpy(cur_collate->index2weight, cur_collate->index2weight_tbl, cur_collate->num_col_base * sizeof(uint16_t)); @@ -602,61 +535,10 @@ static int init_cur_collate(int der_num) return 1; } -void _locale_init(void) -{ - /* TODO: mmap the locale file */ - - /* TODO - ??? */ - memset(CUR_LOCALE_SPEC, 0, LOCALE_STRING_SIZE); - CUR_LOCALE_SPEC[0] = '#'; - - memcpy(__global_locale.category_item_count, - __locale_mmap->lc_common_item_offsets_LEN, - LC_ALL); - - ++__global_locale.category_item_count[0]; /* Increment for codeset entry. */ - __global_locale.category_offsets[0] = offsetof(__locale_t, outdigit0_mb); - __global_locale.category_offsets[1] = offsetof(__locale_t, decimal_point); - __global_locale.category_offsets[2] = offsetof(__locale_t, int_curr_symbol); - __global_locale.category_offsets[3] = offsetof(__locale_t, abday_1); -/* __global_locale.category_offsets[4] = offsetof(__locale_t, collate???); */ - __global_locale.category_offsets[5] = offsetof(__locale_t, yesexpr); - -#ifdef __CTYPE_HAS_8_BIT_LOCALES - __global_locale.tbl8ctype - = (const unsigned char *) &__locale_mmap->tbl8ctype; - __global_locale.tbl8uplow - = (const unsigned char *) &__locale_mmap->tbl8uplow; -#ifdef __WCHAR_ENABLED - __global_locale.tbl8c2wc - = (const uint16_t *) &__locale_mmap->tbl8c2wc; - __global_locale.tbl8wc2c - = (const unsigned char *) &__locale_mmap->tbl8wc2c; - /* translit */ -#endif /* __WCHAR_ENABLED */ -#endif /* __CTYPE_HAS_8_BIT_LOCALES */ -#ifdef __WCHAR_ENABLED - __global_locale.tblwctype - = (const unsigned char *) &__locale_mmap->tblwctype; - __global_locale.tblwuplow - = (const unsigned char *) &__locale_mmap->tblwuplow; - __global_locale.tblwuplow_diff - = (const uint16_t *) &__locale_mmap->tblwuplow_diff; -/* __global_locale.tblwcomb */ -/* = (const unsigned char *) &__locale_mmap->tblwcomb; */ - /* width?? */ -#endif /* __WCHAR_ENABLED */ - - _locale_set(C_LOCALE_SELECTOR); -} - -static const char ascii[] = "ASCII"; -static const char utf8[] = "UTF-8"; - -void _locale_set(const unsigned char *p) +int _locale_set_l(const unsigned char *p, __locale_t base) { const char **x; - unsigned char *s = CUR_LOCALE_SPEC + 1; + unsigned char *s = base->cur_locale + 1; const size_t *stp; const unsigned char *r; const uint16_t *io; @@ -667,12 +549,30 @@ void _locale_set(const unsigned char *p) int len; int c; int i = 0; + __collate_t newcol; ++p; + + newcol.index2weight = NULL; + if ((p[2*LC_COLLATE] != s[2*LC_COLLATE]) + || (p[2*LC_COLLATE + 1] != s[2*LC_COLLATE + 1]) + ) { + row = (((int)(*p & 0x7f)) << 7) + (p[1] & 0x7f); + assert(row < __LOCALE_DATA_NUM_LOCALES); + if (!init_cur_collate(__locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES + * row + 3 + i ], + &newcol) + ) { + return 0; /* calloc failed. */ + } + free(base->collate.index2weight); + memcpy(&base->collate, &newcol, sizeof(__collate_t)); + } + do { if ((*p != *s) || (p[1] != s[1])) { row = (((int)(*p & 0x7f)) << 7) + (p[1] & 0x7f); - assert(row < NUM_LOCALES); + assert(row < __LOCALE_DATA_NUM_LOCALES); *s = *p; s[1] = p[1]; @@ -680,10 +580,13 @@ void _locale_set(const unsigned char *p) if ((i != LC_COLLATE) && ((len = __locale_mmap->lc_common_item_offsets_LEN[i]) != 0) ) { - crow = __locale_mmap->locales[ WIDTH_LOCALES * row + 3 + i ] + crow = __locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES * row + + 3 + i ] * len; - x = (const char **)(((char *) &__global_locale) - + __global_locale.category_offsets[i]); + + x = (const char **)(((char *) base) + + base->category_offsets[i]); + stp = __locale_mmap->lc_common_tbl_offsets + 4*i; r = (const unsigned char *)( ((char *)__locale_mmap) + *stp ); io = (const uint16_t *)( ((char *)__locale_mmap) + *++stp ); @@ -694,67 +597,176 @@ void _locale_set(const unsigned char *p) } } if (i == LC_CTYPE) { - c = __locale_mmap->locales[ WIDTH_LOCALES * row + 2 ]; /* codeset */ + c = __locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES * row + + 2 ]; /* codeset */ if (c <= 2) { if (c == 2) { - __global_locale.codeset = utf8; - __global_locale.encoding = __ctype_encoding_utf8; + base->codeset = utf8; + base->encoding = __ctype_encoding_utf8; /* TODO - fix for bcc */ - __global_locale.mb_cur_max = 6; + base->mb_cur_max = 6; } else { assert(c==1); - __global_locale.codeset = ascii; - __global_locale.encoding = __ctype_encoding_7_bit; - __global_locale.mb_cur_max = 1; + base->codeset = ascii; + base->encoding = __ctype_encoding_7_bit; + base->mb_cur_max = 1; } } else { - const codeset_8_bit_t *c8b; + const __codeset_8_bit_t *c8b; r = CODESET_LIST; - __global_locale.codeset = r + r[c -= 3]; - __global_locale.encoding = __ctype_encoding_8_bit; + base->codeset = r + r[c -= 3]; + base->encoding = __ctype_encoding_8_bit; #ifdef __UCLIBC_MJN3_ONLY__ -#warning REMINDER: update 8 bit mb_cur_max when trasnlit implemented! +#warning REMINDER: update 8 bit mb_cur_max when translit implemented! #endif /* TODO - update when translit implemented! */ - __global_locale.mb_cur_max = 1; + base->mb_cur_max = 1; c8b = __locale_mmap->codeset_8_bit + c; #ifdef __CTYPE_HAS_8_BIT_LOCALES - __global_locale.idx8ctype = c8b->idx8ctype; - __global_locale.idx8uplow = c8b->idx8uplow; -#ifdef __WCHAR_ENABLED - __global_locale.idx8c2wc = c8b->idx8c2wc; - __global_locale.idx8wc2c = c8b->idx8wc2c; + base->idx8ctype = c8b->idx8ctype; + base->idx8uplow = c8b->idx8uplow; +#ifdef __UCLIBC_HAS_WCHAR__ + base->idx8c2wc = c8b->idx8c2wc; + base->idx8wc2c = c8b->idx8wc2c; /* translit */ -#endif /* __WCHAR_ENABLED */ +#endif /* __UCLIBC_HAS_WCHAR__ */ #endif /* __CTYPE_HAS_8_BIT_LOCALES */ } #ifdef __UCLIBC_MJN3_ONLY__ -#warning might want to just put this in the locale_mmap object +#warning TODO: Put the outdigit string length in the locale_mmap object. #endif - d = __global_locale.outdigit_length; - x = &__global_locale.outdigit0_mb; + d = base->outdigit_length; + x = &base->outdigit0_mb; for (c = 0 ; c < 10 ; c++) { ((unsigned char *)d)[c] = strlen(x[c]); assert(d[c] > 0); } - } else if (i == LC_COLLATE) { - init_cur_collate(__locale_mmap->locales[ WIDTH_LOCALES * row + 3 + i ]); + } else if (i == LC_NUMERIC) { + assert(LC_NUMERIC > LC_CTYPE); /* Need ctype initialized. */ + + base->decimal_point_len + = __locale_mbrtowc_l(&base->decimal_point_wc, + base->decimal_point, base); + assert(base->decimal_point_len > 0); + assert(base->decimal_point[base->decimal_point_len] == 0); + + if (*base->grouping) { + base->thousands_sep_len + = __locale_mbrtowc_l(&base->thousands_sep_wc, + base->thousands_sep, base); + assert(base->thousands_sep_len > 0); + assert(base->thousands_sep[base->thousands_sep_len] == 0); + } + +/* } else if (i == LC_COLLATE) { */ +/* init_cur_collate(__locale_mmap->locales[ __LOCALE_DATA_WIDTH_LOCALES */ +/* * row + 3 + i ], */ +/* &base->collate); */ } } ++i; p += 2; s += 2; } while (i < LC_ALL); + + return 1; } -#endif /* __LOCALE_C_ONLY */ +static const uint16_t __code2flag[16] = { + 0, /* unclassified = 0 */ + _ISprint|_ISgraph|_ISalnum|_ISalpha, /* alpha_nonupper_nonlower */ + _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, /* alpha_lower */ + _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower|_ISupper, /* alpha_upper_lower */ + _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, /* alpha_upper */ + _ISprint|_ISgraph|_ISalnum|_ISdigit, /* digit */ + _ISprint|_ISgraph|_ISpunct, /* punct */ + _ISprint|_ISgraph, /* graph */ + _ISprint|_ISspace, /* print_space_nonblank */ + _ISprint|_ISspace|_ISblank, /* print_space_blank */ + _ISspace, /* space_nonblank_noncntrl */ + _ISspace|_ISblank, /* space_blank_noncntrl */ + _IScntrl|_ISspace, /* cntrl_space_nonblank */ + _IScntrl|_ISspace|_ISblank, /* cntrl_space_blank */ + _IScntrl /* cntrl_nonspace */ +}; + +void _locale_init_l(__locale_t base) +{ + memset(base->cur_locale, 0, LOCALE_SELECTOR_SIZE); + base->cur_locale[0] = '#'; + + memcpy(base->category_item_count, + __locale_mmap->lc_common_item_offsets_LEN, + LC_ALL); + + ++base->category_item_count[0]; /* Increment for codeset entry. */ + base->category_offsets[0] = offsetof(__uclibc_locale_t, outdigit0_mb); + base->category_offsets[1] = offsetof(__uclibc_locale_t, decimal_point); + base->category_offsets[2] = offsetof(__uclibc_locale_t, int_curr_symbol); + base->category_offsets[3] = offsetof(__uclibc_locale_t, abday_1); +/* base->category_offsets[4] = offsetof(__uclibc_locale_t, collate???); */ + base->category_offsets[5] = offsetof(__uclibc_locale_t, yesexpr); +#ifdef __CTYPE_HAS_8_BIT_LOCALES + base->tbl8ctype + = (const unsigned char *) &__locale_mmap->tbl8ctype; + base->tbl8uplow + = (const unsigned char *) &__locale_mmap->tbl8uplow; +#ifdef __UCLIBC_HAS_WCHAR__ + base->tbl8c2wc + = (const uint16_t *) &__locale_mmap->tbl8c2wc; + base->tbl8wc2c + = (const unsigned char *) &__locale_mmap->tbl8wc2c; + /* translit */ +#endif /* __UCLIBC_HAS_WCHAR__ */ +#endif /* __CTYPE_HAS_8_BIT_LOCALES */ +#ifdef __UCLIBC_HAS_WCHAR__ + base->tblwctype + = (const unsigned char *) &__locale_mmap->tblwctype; + base->tblwuplow + = (const unsigned char *) &__locale_mmap->tblwuplow; + base->tblwuplow_diff + = (const uint16_t *) &__locale_mmap->tblwuplow_diff; +/* base->tblwcomb */ +/* = (const unsigned char *) &__locale_mmap->tblwcomb; */ + /* width?? */ +#endif /* __UCLIBC_HAS_WCHAR__ */ + + + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning wrong for now, but always set ctype arrays to global C version #endif -/**********************************************************************/ -#ifdef L_nl_langinfo +#ifdef __UCLIBC_HAS_XLOCALE__ + base->__ctype_b = __C_ctype_b; + base->__ctype_tolower = __C_ctype_tolower; + base->__ctype_toupper = __C_ctype_toupper; +#else /* __UCLIBC_HAS_XLOCALE__ */ + __ctype_b = __C_ctype_b; + __ctype_tolower = __C_ctype_tolower; + __ctype_toupper = __C_ctype_toupper; +#endif /* __UCLIBC_HAS_XLOCALE__ */ -#include <langinfo.h> -#include <nl_types.h> +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Initialize code2flag correctly based on locale_mmap. +#endif + base->code2flag = __code2flag; + + + _locale_set_l(C_LOCALE_SELECTOR, base); +} + +void _locale_init(void) +{ + /* TODO: mmap the locale file */ + + /* TODO - ??? */ + _locale_init_l(__global_locale); +} + +#endif +/**********************************************************************/ +#if defined(L_nl_langinfo) || defined(L_nl_langinfo_l) #ifdef __LOCALE_C_ONLY @@ -853,22 +865,435 @@ char *nl_langinfo(nl_item item) #else /* __LOCALE_C_ONLY */ -static const char empty[] = ""; +#if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) char *nl_langinfo(nl_item item) { + return nl_langinfo_l(item, __UCLIBC_CURLOCALE); +} + +#else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ + +static const char empty[] = ""; + +char *__XL(nl_langinfo)(nl_item item __LOCALE_PARAM ) +{ unsigned int c = _NL_ITEM_CATEGORY(item); unsigned int i = _NL_ITEM_INDEX(item); - if ((c < LC_ALL) && (i < __global_locale.category_item_count[c])) { - return ((char **)(((char *) &__global_locale) - + __global_locale.category_offsets[c]))[i]; - + if ((c < LC_ALL) && (i < __LOCALE_PTR->category_item_count[c])) { + return ((char **)(((char *) __LOCALE_PTR) + + __LOCALE_PTR->category_offsets[c]))[i]; } + return (char *) empty; } +#endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ + #endif /* __LOCALE_C_ONLY */ #endif /**********************************************************************/ +#ifdef L_newlocale + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Move posix and utf8 strings. +#endif +static const char posix[] = "POSIX"; +static const char utf8[] = "UTF-8"; + +static int find_locale(int category_mask, const char *p, + unsigned char *new_locale) +{ + int i; + const unsigned char *s; + uint16_t n; + unsigned char lang_cult, codeset; + +#if defined(__LOCALE_DATA_AT_MODIFIERS_LENGTH) && 1 + /* Support standard locale handling for @-modifiers. */ + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning REMINDER: Fix buf size in find_locale. +#endif + char buf[18]; /* TODO: 7+{max codeset name length} */ + const char *q; + + if ((q = strchr(p,'@')) != NULL) { + if ((((size_t)((q-p)-5)) > (sizeof(buf) - 5)) || (p[2] != '_')) { + return 0; + } + /* locale name at least 5 chars long and 3rd char is '_' */ + s = LOCALE_AT_MODIFIERS; + do { + if (!strcmp(s+2, q+1)) { + break; + } + s += 2 + *s; /* TODO - fix this throughout */ + } while (*s); + if (!*s) { + return 0; + } + assert(q - p < sizeof(buf)); + memcpy(buf, p, q-p); + buf[q-p] = 0; + buf[2] = s[1]; + p = buf; + } +#endif + + lang_cult = codeset = 0; /* Assume C and default codeset. */ + if (((*p == 'C') && !p[1]) || !strcmp(p, posix)) { + goto FIND_LOCALE; + } + + if ((strlen(p) > 5) && (p[5] == '.')) { /* Codeset in locale name? */ + /* TODO: maybe CODESET_LIST + *s ??? */ + /* 7bit is 1, UTF-8 is 2, 8-bit is >= 3 */ + codeset = 2; + if (strcmp(utf8,p+6) != 0) {/* TODO - fix! */ + s = CODESET_LIST; + do { + ++codeset; /* Increment codeset first. */ + if (!strcmp(CODESET_LIST+*s, p+6)) { + goto FIND_LANG_CULT; + } + } while (*++s); + return 0; /* No matching codeset! */ + } + } + + FIND_LANG_CULT: /* Find language_culture number. */ + s = LOCALE_NAMES; + do { /* TODO -- do a binary search? */ + /* TODO -- fix gen_mmap!*/ + ++lang_cult; /* Increment first since C/POSIX is 0. */ + if (!strncmp(s,p,5)) { /* Found a matching locale name; */ + goto FIND_LOCALE; + } + s += 5; + } while (lang_cult < __LOCALE_DATA_NUM_LOCALE_NAMES); + return 0; /* No matching language_culture! */ + + FIND_LOCALE: /* Find locale row matching name and codeset */ + s = LOCALES; + n = 0; + do { /* TODO -- do a binary search? */ + if ((lang_cult == *s) && ((codeset == s[1]) || (codeset == s[2]))) { + i = 1; + s = new_locale + 1; + do { + if (category_mask & i) { + /* Encode current locale row number. */ + ((unsigned char *) s)[0] = (n >> 7) | 0x80; + ((unsigned char *) s)[1] = (n & 0x7f) | 0x80; + } + s += 2; + i += i; + } while (i < (1 << LC_ALL)); + + return i; /* Return non-zero */ + } + s += __LOCALE_DATA_WIDTH_LOCALES; + ++n; + } while (n <= __LOCALE_DATA_NUM_LOCALES); /* We started at 1!!! */ + + return 0; /* Unsupported locale. */ +} + +static unsigned char *composite_locale(int category_mask, const char *locale, + unsigned char *new_locale) +{ + char buf[MAX_LOCALE_STR]; + char *t; + char *e; + int c; + int component_mask; + + if (!strchr(locale,'=')) { + if (!find_locale(category_mask, locale, new_locale)) { + return NULL; + } + return new_locale; + } + + if (strlen(locale) >= sizeof(buf)) { + return NULL; + } + stpcpy(buf, locale); + + component_mask = 0; + t = strtok_r(buf, "=", &e); /* This can't fail because of strchr test above. */ + do { + c = 0; + while (strcmp(CATEGORY_NAMES + (int) CATEGORY_NAMES[c], t)) { + if (++c == LC_ALL) { /* Unknown category name! */ + return NULL; + } + } + t = strtok_r(NULL, ";", &e); + c = (1 << c); + if (component_mask & c) { /* Multiple components for one category. */ + return NULL; + } + component_mask |= c; + if ((category_mask & c) && (!t || !find_locale(c, t, new_locale))) { + return NULL; + } + } while ((t = strtok_r(NULL, "=", &e)) != NULL); + + if (category_mask & ~component_mask) { /* Category component(s) missing. */ + return NULL; + } + + return new_locale; +} + +__locale_t __newlocale(int category_mask, const char *locale, __locale_t base) +{ + const unsigned char *p; + int i, j, k; + unsigned char new_selector[LOCALE_SELECTOR_SIZE]; + + if (!locale || (((unsigned int)(category_mask)) > LC_ALL_MASK)) { + INVALID: + __set_errno(EINVAL); + return NULL; /* No locale or illegal/unsupported category. */ + } + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Rename cur_locale to locale_selector. +#endif + strcpy((char *) new_selector, + (base ? (char *) base->cur_locale : C_LOCALE_SELECTOR)); + + if (!*locale) { /* locale == "", so check environment. */ +#ifndef __UCLIBC_HAS_THREADS__ + static /* If no threads, then envstr can be static. */ +#endif /* __UCLIBC_HAS_THREADS__ */ + const char *envstr[4] = { "LC_ALL", NULL, "LANG", posix }; + + i = 1; + k = 0; + do { + if (category_mask & i) { + /* Note: SUSv3 doesn't define a fallback mechanism here. + * So, if LC_ALL is invalid, we do _not_ continue trying + * the other environment vars. */ + envstr[1] = CATEGORY_NAMES + CATEGORY_NAMES[k]; + j = 0; + do { + p = envstr[j]; + } while ((++j < 4) && (!(p = getenv(p)) || !*p)); + + + /* The user set something... is it valid? */ + /* Note: Since we don't support user-supplied locales and + * alternate paths, we don't need to worry about special + * handling for suid/sgid apps. */ + if (!find_locale(i, p, new_selector)) { + goto INVALID; + } + } + i += i; + } while (++k < LC_ALL); + } else if (!composite_locale(category_mask, locale, new_selector)) { + goto INVALID; + } + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Do a compatible codeset check! +#endif + + /* If we get here, the new selector corresponds to a valid locale. */ + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Probably want a _locale_new func to allow for caching of locales. +#endif +#if 0 + if (base) { + _locale_set_l(new_selector, base); + } else { + base = _locale_new(new_selector); + } +#else + if (!base) { + if ((base = malloc(sizeof(__uclibc_locale_t))) == NULL) { + return base; + } + _locale_init_l(base); + } + + _locale_set_l(new_selector, base); +#endif + + return base; +} + +weak_alias(__newlocale, newlocale) + +#endif +/**********************************************************************/ +#ifdef L_duplocale + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning REMINDER: When we allocate ctype tables, remember to dup them. +#endif + +__locale_t duplocale(__locale_t dataset) +{ + __locale_t r; + uint16_t * i2w; + + assert(dataset != LC_GLOBAL_LOCALE); + + if ((r = malloc(sizeof(__uclibc_locale_t))) != NULL) { + if ((i2w = calloc(2*dataset->collate.max_col_index+2, + sizeof(uint16_t))) + != NULL + ) { + memcpy(r, dataset, sizeof(__uclibc_locale_t)); + r->collate.index2weight = i2w; + } else { + free(r); + r = NULL; + } + } + return r; +} + +#endif +/**********************************************************************/ +#ifdef L_freelocale + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning REMINDER: When we allocate ctype tables, remember to free them. +#endif + +void freelocale(__locale_t dataset) +{ + assert(dataset != __global_locale); + assert(dataset != LC_GLOBAL_LOCALE); + + free(dataset->collate.index2weight); /* Free collation data. */ + free(dataset); /* Free locale */ +} + +#endif +/**********************************************************************/ +#ifdef L_uselocale + +__locale_t uselocale(__locale_t dataset) +{ + __locale_t old; + + if (!dataset) { + old = __UCLIBC_CURLOCALE; + } else { + if (dataset == LC_GLOBAL_LOCALE) { + dataset = __global_locale; + } +#ifdef __UCLIBC_HAS_THREADS__ + old = __curlocale_set(dataset); +#else + old = __curlocale_var; + __curlocale_var = dataset; +#endif + } + + if (old == __global_locale) { + return LC_GLOBAL_LOCALE; + } + return old; +} + +#endif +/**********************************************************************/ +#ifdef L___curlocale + +#ifdef __UCLIBC_HAS_THREADS__ + +__locale_t weak_const_function __curlocale(void) +{ + return __curlocale_var; /* This is overriden by the thread version. */ +} + +__locale_t weak_function __curlocale_set(__locale_t newloc) +{ + assert(newloc != LC_GLOBAL_LOCALE); + + __locale_t oldloc = __curlocale_var; + __curlocale_var = newloc; + return oldloc; +} + +#endif + +#endif +/**********************************************************************/ +#ifdef L___locale_mbrtowc_l + +/* NOTE: This returns an int... not size_t. Also, it is not a general + * routine. It is actually a very stripped-down version of mbrtowc + * that takes a __locale_t arg. This is used by strcoll and strxfrm. + * It is also used above to generate wchar_t versions of the decimal point + * and thousands seperator. */ + + +#ifndef __CTYPE_HAS_UTF_8_LOCALES +#warning __CTYPE_HAS_UTF_8_LOCALES not set! +#endif +#ifndef __CTYPE_HAS_8_BIT_LOCALES +#warning __CTYPE_HAS_8_BIT_LOCALES not set! +#endif + +#define Cc2wc_IDX_SHIFT __LOCALE_DATA_Cc2wc_IDX_SHIFT +#define Cc2wc_ROW_LEN __LOCALE_DATA_Cc2wc_ROW_LEN + +extern size_t _wchar_utf8sntowcs(wchar_t *__restrict pwc, size_t wn, + const char **__restrict src, size_t n, + mbstate_t *ps, int allow_continuation); + +int __locale_mbrtowc_l(wchar_t *__restrict dst, + const char *__restrict src, + __locale_t loc ) +{ +#ifdef __CTYPE_HAS_UTF_8_LOCALES + if (loc->encoding == __ctype_encoding_utf8) { + mbstate_t ps; + const char *p = src; + size_t r; + ps.mask = 0; + r = _wchar_utf8sntowcs(dst, 1, &p, SIZE_MAX, &ps, 1); + return (r == 1) ? (p-src) : r; /* Need to return 0 if nul char. */ + } +#endif + +#ifdef __CTYPE_HAS_8_BIT_LOCALES + assert((loc->encoding == __ctype_encoding_7_bit) || (loc->encoding == __ctype_encoding_8_bit)); +#else + assert(loc->encoding == __ctype_encoding_7_bit); +#endif + + if ((*dst = ((unsigned char)(*src))) < 0x80) { /* ASCII... */ + return (*src != 0); + } + +#ifdef __CTYPE_HAS_8_BIT_LOCALES + if (loc->encoding == __ctype_encoding_8_bit) { + wchar_t wc = *dst - 0x80; + *dst = __LOCALE_PTR->tbl8c2wc[ + (__LOCALE_PTR->idx8c2wc[wc >> Cc2wc_IDX_SHIFT] + << Cc2wc_IDX_SHIFT) + (wc & (Cc2wc_ROW_LEN - 1))]; + if (*dst) { + return 1; + } + } +#endif + + return -1; +} + +#endif +/**********************************************************************/ diff --git a/libc/misc/time/Makefile b/libc/misc/time/Makefile index b5714e511..302214baf 100644 --- a/libc/misc/time/Makefile +++ b/libc/misc/time/Makefile @@ -28,15 +28,22 @@ MSRC= time.c MOBJ= asctime.o asctime_r.o clock.o ctime.o ctime_r.o gmtime.o gmtime_r.o \ localtime.o localtime_r.o mktime.o strftime.o strptime.o tzset.o \ _time_t2tm.o __time_tm.o _time_mktime.o dysize.o timegm.o +MOBJx= ifeq ($(UCLIBC_HAS_FLOATS),y) MOBJ += difftime.o endif +ifeq ($(UCLIBC_HAS_XLOCALE),y) + MOBJx += strftime_l.o strptime_l.o +endif + +ifeq ($(UCLIBC_HAS_WCHAR),y) +endif CSRC= adjtime.c ftime.c COBJS=$(patsubst %.c,%.o, $(CSRC)) -OBJS=$(COBJS) $(MOBJ) +OBJS=$(COBJS) $(MOBJ) $(MOBJx) all: $(OBJS) $(LIBC) @@ -49,6 +56,10 @@ $(MOBJ): $(MSRC) $(CC) $(CFLAGS) -DL_$* $< -c -o $*.o $(STRIPTOOL) -x -R .note -R .comment $*.o +$(MOBJx): $(MSRC) + $(CC) $(CFLAGS) -DL_$* -D__UCLIBC_DO_XLOCALE $< -c -o $*.o + $(STRIPTOOL) -x -R .note -R .comment $*.o + $(COBJS): %.o : %.c $(CC) $(CFLAGS) -c $< -o $@ $(STRIPTOOL) -x -R .note -R .comment $*.o diff --git a/libc/misc/time/time.c b/libc/misc/time/time.c index 8d3cbc3b4..0ae91f69a 100644 --- a/libc/misc/time/time.c +++ b/libc/misc/time/time.c @@ -95,9 +95,32 @@ * * Nov 26, 2002 Fix bug in setting daylight and timezone when no (valid) TZ. * Bug reported by Arne Bernin <arne@alamut.de> in regards to freeswan. + * + * July 27, 2003 Adjust the struct tm extension field support. + * Change __tm_tzone back to a ptr and add the __tm_tzname[] buffer for + * __tm_tzone to point to. This gets around complaints from g++. + * Who knows... it might even fix the PPC timezone init problem. + * + * July 29, 2003 Fix a bug in mktime behavior when tm_isdst was -1. + * Bug reported by "Sid Wade" <sid@vivato.net> in regards to busybox. + * + * NOTE: uClibc mktime behavior is different than glibc's when + * the struct tm has tm_isdst == -1 and also had fields outside of + * the normal ranges. + * + * Apparently, glibc examines (at least) tm_sec and guesses the app's + * intention of assuming increasing or decreasing time when entering an + * ambiguous time period at the dst<->st boundaries. + * + * The uClibc behavior is to always normalize the struct tm and then + * try to determing the dst setting. + * + * As long as tm_isdst != -1 or the time specifiec by struct tm is + * unambiguous (not falling in the dst<->st transition region) both + * uClibc and glibc should produce the same result for mktime. + * */ - #define _GNU_SOURCE #define _STDIO_UTILITY #include <stdio.h> @@ -112,6 +135,10 @@ #include <langinfo.h> #include <locale.h> +#ifdef __UCLIBC_HAS_XLOCALE__ +#include <xlocale.h> +#endif + #ifndef __isleap #define __isleap(y) ( !((y) % 4) && ( ((y) % 100) || !((y) % 400) ) ) #endif @@ -121,27 +148,26 @@ #endif /**********************************************************************/ - /* The era code is currently unfinished. */ /* #define ENABLE_ERA_CODE */ -#define __TIME_TZ_FILE -/* #define __TIME_TZ_FILE_ONCE */ - -#define __TIME_TZ_OPT_SPEED - #define TZ_BUFLEN (2*TZNAME_MAX + 56) -#ifdef __TIME_TZ_FILE +#ifdef __UCLIBC_HAS_TZ_FILE__ + #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include "paths.h" /* ":<tzname>+hh:mm:ss<tzname>+hh:mm:ss,Mmm.w.d/hh:mm:ss,Mmm.w.d/hh:mm:ss" + nul */ /* 1 + 2*(1+TZNAME_MAX+1 + 9 + 7 + 9) + 1 = 2*TZNAME_MAX + 56 */ -#else /* __TIME_TZ_FILE */ -#undef __TIME_TZ_FILE_ONCE -#endif /* __TIME_TZ_FILE */ + +#else /* __UCLIBC_HAS_TZ_FILE__ */ + +/* Probably no longer needed. */ +#undef __UCLIBC_HAS_TZ_FILE_READ_MANY__ + +#endif /* __UCLIBC_HAS_TZ_FILE__ */ /**********************************************************************/ @@ -592,7 +618,8 @@ struct tm *localtime_r(register const time_t *__restrict timer, result->tm_isdst = dst; #ifdef __UCLIBC_HAS_TM_EXTENSIONS__ result->tm_gmtoff = - _time_tzinfo[dst].gmt_offset; - strcpy( (char *)(result->tm_zone), _time_tzinfo[dst].tzname); + result->tm_zone = result->__tm_tzname; + strcpy(result->__tm_tzname, _time_tzinfo[dst].tzname); #endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */ } while ((++dst < 2) && (result->tm_isdst = tm_isdst(result)) != 0); @@ -617,7 +644,18 @@ time_t mktime(struct tm *timeptr) #endif /**********************************************************************/ -#ifdef L_strftime +#if defined(L_strftime) || defined(L_strftime_l) + +#if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) + +size_t strftime(char *__restrict s, size_t maxsize, + const char *__restrict format, + const struct tm *__restrict timeptr) +{ + return strftime_l(s, maxsize, format, timeptr, __UCLIBC_CURLOCALE); +} + +#else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ #define NO_E_MOD 0x80 #define NO_O_MOD 0x40 @@ -811,9 +849,13 @@ static int load_field(int k, const struct tm *__restrict timeptr) #define MAX_PUSH 4 -size_t strftime(char *__restrict s, size_t maxsize, - const char *__restrict format, - const struct tm *__restrict timeptr) +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Check multibyte format string validity. +#endif + +size_t __XL(strftime)(char *__restrict s, size_t maxsize, + const char *__restrict format, + const struct tm *__restrict timeptr __LOCALE_PARAM ) { long tzo; register const char *p; @@ -882,16 +924,19 @@ size_t strftime(char *__restrict s, size_t maxsize, + (code & 7); #ifdef ENABLE_ERA_CODE if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */ - && (*(o = nl_langinfo(_NL_ITEM(LC_TIME, - (int)(((unsigned char *)p)[4])) + && (*(o = __XL(nl_langinfo)(_NL_ITEM(LC_TIME, + (int)(((unsigned char *)p)[4])) + __LOCALE_ARG ))) ) { p = o; goto LOOP; } #endif - p = nl_langinfo(_NL_ITEM(LC_TIME, - (int)(*((unsigned char *)p)))); + p = __XL(nl_langinfo)(_NL_ITEM(LC_TIME, + (int)(*((unsigned char *)p))) + __LOCALE_ARG + ); goto LOOP; } @@ -1037,7 +1082,7 @@ size_t strftime(char *__restrict s, size_t maxsize, if ((code & MASK_SPEC) == STRING_SPEC) { o_count = SIZE_MAX; field_val += spec[STRINGS_NL_ITEM_START + (code & 0xf)]; - o = nl_langinfo(_NL_ITEM(LC_TIME, field_val)); + o = __XL(nl_langinfo)(_NL_ITEM(LC_TIME, field_val) __LOCALE_ARG ); } else { o_count = ((i >> 1) & 3) + 1; o = buf + o_count; @@ -1061,9 +1106,31 @@ size_t strftime(char *__restrict s, size_t maxsize, goto LOOP; } +#endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ + #endif /**********************************************************************/ -#ifdef L_strptime +#if defined(L_strptime) || defined(L_strptime_l) + +#if defined(L_strptime) || defined(L_strptime_l) +#define ISDIGIT(C) __isdigit_char((C)) +#endif + +#ifdef __UCLIBC_DO_XLOCALE +#define ISSPACE(C) isspace_l((C), locale_arg) +#else +#define ISSPACE(C) isspace((C)) +#endif + +#if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) + +char *strptime(const char *__restrict buf, const char *__restrict format, + struct tm *__restrict tm) +{ + return strptime_l(buf, format, tm, __UCLIBC_CURLOCALE); +} + +#else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ /* TODO: * 1) %l and %k are space-padded, so "%l" by itself fails while " %l" succeeds. @@ -1207,8 +1274,8 @@ static const unsigned char spec[] = { #define MAX_PUSH 4 -char *strptime(const char *__restrict buf, const char *__restrict format, - struct tm *__restrict tm) +char *__XL(strptime)(const char *__restrict buf, const char *__restrict format, + struct tm *__restrict tm __LOCALE_PARAM) { register const char *p; char *o; @@ -1273,16 +1340,18 @@ char *strptime(const char *__restrict buf, const char *__restrict format, + (code & 7); #ifdef ENABLE_ERA_CODE if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */ - && (*(o = nl_langinfo(_NL_ITEM(LC_TIME, - (int)(((unsigned char *)p)[4])) - ))) + && (*(o = __XL(nl_langinfo)(_NL_ITEM(LC_TIME, + (int)(((unsigned char *)p)[4])) + __LOCALE_ARG + ))) ) { p = o; goto LOOP; } #endif - p = nl_langinfo(_NL_ITEM(LC_TIME, - (int)(*((unsigned char *)p)))); + p = __XL(nl_langinfo)(_NL_ITEM(LC_TIME, + (int)(*((unsigned char *)p))) + __LOCALE_ARG ); goto LOOP; } @@ -1295,9 +1364,9 @@ char *strptime(const char *__restrict buf, const char *__restrict format, /* Go backwards to check full names before abreviations. */ do { --j; - o = nl_langinfo(i+j); - if (!strncasecmp(buf,o,strlen(o)) && *o) { /* Found a match. */ - do { + o = __XL(nl_langinfo)(i+j __LOCALE_ARG); + if (!__XL(strncasecmp)(buf,o,strlen(o) __LOCALE_ARG) && *o) { + do { /* Found a match. */ ++buf; } while (*++o); if (!code) { /* am/pm */ @@ -1322,11 +1391,11 @@ char *strptime(const char *__restrict buf, const char *__restrict format, o = (char *) buf; i = errno; __set_errno(0); - if (!isspace(*buf)) { /* Signal an error if whitespace. */ + if (!ISSPACE(*buf)) { /* Signal an error if whitespace. */ #ifdef TIME_T_IS_UNSIGNED - t = strtoul(buf, &o, 10); + t = __XL(strtoul)(buf, &o, 10 __LOCALE_ARG); #else - t = strtol(buf, &o, 10); + t = __XL(strtol)(buf, &o, 10 __LOCALE_ARG); #endif } if ((o == buf) || errno) { /* Not a number or overflow. */ @@ -1356,7 +1425,7 @@ char *strptime(const char *__restrict buf, const char *__restrict format, j = ((j==1) ? 366 : 9999); } i = -1; - while (isdigit(*buf)) { + while (ISDIGIT(*buf)) { if (i < 0) { i = 0; } @@ -1401,9 +1470,9 @@ char *strptime(const char *__restrict buf, const char *__restrict format, } } goto LOOP; - } else if (isspace(*p)) { + } else if (ISSPACE(*p)) { ++p; - while (isspace(*buf)) { + while (ISSPACE(*buf)) { ++buf; } goto LOOP; @@ -1413,6 +1482,8 @@ char *strptime(const char *__restrict buf, const char *__restrict format, return NULL; } +#endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ + #endif /**********************************************************************/ #ifdef L_time @@ -1478,10 +1549,10 @@ static const char *getoffset(register const char *e, long *pn) f = -1; do { ++s; - if (isdigit(*e)) { + if (__isdigit_char(*e)) { f = *e++ - '0'; } - if (isdigit(*e)) { + if (__isdigit_char(*e)) { f = 10 * f + (*e++ - '0'); } if (((unsigned int)f) >= *s) { @@ -1507,7 +1578,7 @@ static const char *getnumber(register const char *e, int *pn) int f; f = 0; - while (n && isdigit(*e)) { + while (n && __isdigit_char(*e)) { f = 10 * f + (*e++ - '0'); --n; } @@ -1519,7 +1590,7 @@ static const char *getnumber(register const char *e, int *pn) n = 3; f = 0; - while (n && isdigit(*e)) { + while (n && __isdigit_char(*e)) { f = 10 * f + (*e++ - '0'); --n; } @@ -1529,11 +1600,16 @@ static const char *getnumber(register const char *e, int *pn) #endif /* __BCC__ */ } -#ifdef __TIME_TZ_FILE -#ifdef __TIME_TZ_FILE_ONCE +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Should we preserve errno from open/read/close errors re TZ file? +#endif + +#ifdef __UCLIBC_HAS_TZ_FILE__ + +#ifndef __UCLIBC_HAS_TZ_FILE_READ_MANY__ static int TZ_file_read; /* Let BSS initialization set this to 0. */ -#endif /* __TIME_TZ_FILE_ONCE */ +#endif /* __UCLIBC_HAS_TZ_FILE_READ_MANY__ */ static char *read_TZ_file(char *buf) { @@ -1542,7 +1618,7 @@ static char *read_TZ_file(char *buf) size_t todo; char *p = NULL; - if ((fd = open("/etc/TZ", O_RDONLY)) >= 0) { + if ((fd = open(__UCLIBC_TZ_FILE_PATH__, O_RDONLY)) >= 0) { todo = TZ_BUFLEN; p = buf; do { @@ -1559,9 +1635,9 @@ static char *read_TZ_file(char *buf) if ((p > buf) && (p[-1] == '\n')) { /* Must end with newline. */ p[-1] = 0; p = buf; -#ifdef __TIME_TZ_FILE_ONCE +#ifndef __UCLIBC_HAS_TZ_FILE_READ_MANY__ ++TZ_file_read; -#endif /* __TIME_TZ_FILE_ONCE */ +#endif /* __UCLIBC_HAS_TZ_FILE_READ_MANY__ */ } else { ERROR: p = NULL; @@ -1571,7 +1647,7 @@ static char *read_TZ_file(char *buf) return p; } -#endif /* __TIME_TZ_FILE */ +#endif /* __UCLIBC_HAS_TZ_FILE__ */ void tzset(void) { @@ -1582,18 +1658,18 @@ void tzset(void) rule_struct new_rules[2]; int n, count, f; char c; -#ifdef __TIME_TZ_FILE +#ifdef __UCLIBC_HAS_TZ_FILE__ char buf[TZ_BUFLEN]; -#endif /* __TIME_TZ_FILE */ -#ifdef __TIME_TZ_OPT_SPEED +#endif /* __UCLIBC_HAS_TZ_FILE__ */ +#ifdef __UCLIBC_HAS_TZ_CACHING__ static char oldval[TZ_BUFLEN]; /* BSS-zero'd. */ -#endif /* __TIME_TZ_OPT_SPEED */ +#endif /* __UCLIBC_HAS_TZ_CACHING__ */ TZLOCK; e = getenv(TZ); /* TZ env var always takes precedence. */ -#ifdef __TIME_TZ_FILE_ONCE +#if defined(__UCLIBC_HAS_TZ_FILE__) && !defined(__UCLIBC_HAS_TZ_FILE_READ_MANY__) /* Put this inside the lock to prevent the possiblity of two different * timezones being used in a threaded app. */ @@ -1602,7 +1678,7 @@ void tzset(void) } else if (TZ_file_read > 0) { goto FAST_DONE; } -#endif /* __TIME_TZ_FILE_ONCE */ +#endif /* defined(__UCLIBC_HAS_TZ_FILE__) && !defined(__UCLIBC_HAS_TZ_FILE_READ_MANY__) */ /* Warning!!! Since uClibc doesn't do lib locking, the following is * potentially unsafe in a multi-threaded program since it is remotely @@ -1610,14 +1686,14 @@ void tzset(void) * the string being parsed. So, don't do that... */ if ((!e /* TZ env var not set... */ -#ifdef __TIME_TZ_FILE +#ifdef __UCLIBC_HAS_TZ_FILE__ && !(e = read_TZ_file(buf)) /* and no file or invalid file */ -#endif /* __TIME_TZ_FILE */ +#endif /* __UCLIBC_HAS_TZ_FILE__ */ ) || !*e) { /* or set to empty string. */ ILLEGAL: /* TODO: Clean up the following... */ -#ifdef __TIME_TZ_OPT_SPEED +#ifdef __UCLIBC_HAS_TZ_CACHING__ *oldval = 0; /* Set oldval to an empty string. */ -#endif /* __TIME_TZ_OPT_SPEED */ +#endif /* __UCLIBC_HAS_TZ_CACHING__ */ s = _time_tzinfo[0].tzname; *s = 'U'; *++s = 'T'; @@ -1632,7 +1708,7 @@ void tzset(void) ++e; } -#ifdef __TIME_TZ_OPT_SPEED +#ifdef __UCLIBC_HAS_TZ_CACHING__ if (strcmp(e, oldval) == 0) { /* Same string as last time... */ goto FAST_DONE; /* So nothing to do. */ } @@ -1640,7 +1716,7 @@ void tzset(void) * it is too long, but it that case it will be illegal and will be reset * to the empty string anyway. */ strncpy(oldval, e, TZ_BUFLEN); -#endif /* __TIME_TZ_OPT_SPEED */ +#endif /* __UCLIBC_HAS_TZ_CACHING__ */ count = 0; new_rules[1].tzname[0] = 0; @@ -1675,7 +1751,7 @@ void tzset(void) /* Get offset */ s = (char *) e; if ((*e != '-') && (*e != '+')) { - if (count && !isdigit(*e)) { + if (count && !__isdigit_char(*e)) { off -= 3600; /* Default to 1 hour ahead of std. */ goto SKIP_OFFSET; } @@ -1938,8 +2014,9 @@ struct tm *_time_t2tm(const time_t *__restrict timer, p[4] = 0; /* result[8] .. tm_isdst */ #ifdef __UCLIBC_HAS_TM_EXTENSIONS__ result->tm_gmtoff = 0; + result->tm_zone = result->__tm_tzname; { - register char *s = (char *) result->tm_zone; + register char *s = result->__tm_tzname; *s = 'U'; *++s = 'T'; *++s = 'C'; @@ -1977,12 +2054,16 @@ time_t _time_mktime(struct tm *timeptr, int store_on_success) /* 0:sec 1:min 2:hour 3:mday 4:mon 5:year 6:wday 7:yday 8:isdst */ register int *p = (int *) &x; register const unsigned char *s; - int d; + int d, default_dst; tzset(); memcpy(p, timeptr, sizeof(struct tm)); + if ((default_dst = p[8]) < 0) { + default_dst = 1; /* Assume advancing */ + } + d = 400; p[5] = (p[5] - ((p[6] = p[5]/d) * d)) + (p[7] = p[4]/12); if ((p[4] -= 12 * p[7]) < 0) { @@ -2007,49 +2088,69 @@ time_t _time_mktime(struct tm *timeptr, int store_on_success) --d; } + TZLOCK; + #ifdef __BCC__ d = p[5] - 1; days = -719163L + ((long)d)*365 + ((d/4) - (d/100) + (d/400) + p[3] + p[7]); secs = p[0] + 60*( p[1] + 60*((long)(p[2])) ) - + _time_tzinfo[timeptr->tm_isdst > 0].gmt_offset; + + _time_tzinfo[default_dst].gmt_offset; + DST_CORRECT: if (secs < 0) { secs += 120009600L; days -= 1389; } if ( ((unsigned long)(days + secs/86400L)) > 49710L) { - return -1; + t = ((time_t)(-1)); + goto DONE; } secs += (days * 86400L); #else - TZLOCK; d = p[5] - 1; d = -719163L + d*365 + (d/4) - (d/100) + (d/400); secs = p[0] - + _time_tzinfo[timeptr->tm_isdst > 0].gmt_offset + + _time_tzinfo[default_dst].gmt_offset + 60*( p[1] + 60*(p[2] + 24*(((146073L * ((long long)(p[6])) + d) + p[3]) + p[7]))); - TZUNLOCK; + + DST_CORRECT: if (((unsigned long long)(secs - LONG_MIN)) > (((unsigned long long)LONG_MAX) - LONG_MIN) ) { - return -1; + t = ((time_t)(-1)); + goto DONE; } #endif + d = ((struct tm *)p)->tm_isdst; t = secs; localtime_r(&t, (struct tm *)p); - if (t < 0) { - return -1; + if (t == ((time_t)(-1))) { /* Remember, time_t can be unsigned. */ + goto DONE; + } + + if ((d < 0) && (((struct tm *)p)->tm_isdst != default_dst)) { +#ifdef __BCC__ + secs -= (days * 86400L); +#endif + secs += (_time_tzinfo[1-default_dst].gmt_offset + - _time_tzinfo[default_dst].gmt_offset); + goto DST_CORRECT; } + if (store_on_success) { memcpy(timeptr, p, sizeof(struct tm)); } + + DONE: + TZUNLOCK; + return t; } diff --git a/libc/misc/wchar/Makefile b/libc/misc/wchar/Makefile index acc852195..4a3e2fc7a 100644 --- a/libc/misc/wchar/Makefile +++ b/libc/misc/wchar/Makefile @@ -42,7 +42,6 @@ MOBJ2= fwide.o \ # getwc (fgetwc alias) getwc_unlocked (fgetwc_unlocked alias) # putwc (fputwc alias) putwc_unlocked (fputwc_unlocked alias) -# fwscanf wscanf swscanf vfwscanf vwscanf vswscanf # wcsftime OBJS=$(MOBJ1) $(MOBJ2) diff --git a/libc/misc/wchar/wchar.c b/libc/misc/wchar/wchar.c index ff3e42a84..aa0c2735a 100644 --- a/libc/misc/wchar/wchar.c +++ b/libc/misc/wchar/wchar.c @@ -86,6 +86,9 @@ * Add a couple of ugly hacks to support *wprintf. * Add a mini iconv() and iconv implementation (requires locale support). * + * Aug 1, 2003 + * Bug fix for mbrtowc. + * * Manuel */ @@ -101,13 +104,39 @@ #include <assert.h> #include <locale.h> #include <wchar.h> +#include <bits/uClibc_uwchar.h> +/**********************************************************************/ #ifdef __UCLIBC_HAS_LOCALE__ -#define ENCODING (__global_locale.encoding) +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_iswspace +/* generates one warning */ +#warning TODO: Fix Cc2wc* and Cwc2c* defines! +#endif +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#define ENCODING ((__UCLIBC_CURLOCALE_DATA).encoding) + +#define Cc2wc_IDX_SHIFT __LOCALE_DATA_Cc2wc_IDX_SHIFT +#define Cc2wc_ROW_LEN __LOCALE_DATA_Cc2wc_ROW_LEN +#define Cwc2c_DOMAIN_MAX __LOCALE_DATA_Cwc2c_DOMAIN_MAX +#define Cwc2c_TI_SHIFT __LOCALE_DATA_Cwc2c_TI_SHIFT +#define Cwc2c_TT_SHIFT __LOCALE_DATA_Cwc2c_TT_SHIFT +#define Cwc2c_TI_LEN __LOCALE_DATA_Cwc2c_TI_LEN + #ifndef __CTYPE_HAS_UTF_8_LOCALES #warning __CTYPE_HAS_UTF_8_LOCALES not set! #endif -#else + +#else /* __UCLIBC_HAS_LOCALE__ */ + +#ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_btowc +/* emit only once */ +#warning fix preprocessor logic testing locale settings +#endif +#endif + #define ENCODING (__ctype_encoding_7_bit) #ifdef __CTYPE_HAS_8_BIT_LOCALES #error __CTYPE_HAS_8_BIT_LOCALES is defined! @@ -117,7 +146,9 @@ #endif #undef L__wchar_utf8sntowcs #undef L__wchar_wcsntoutf8s -#endif + +#endif /* __UCLIBC_HAS_LOCALE__ */ +/**********************************************************************/ #if WCHAR_MAX > 0xffffUL #define UTF_8_MAX_LEN 6 @@ -266,11 +297,18 @@ size_t mbrtowc(wchar_t *__restrict pwc, const char *__restrict s, #ifdef __CTYPE_HAS_UTF_8_LOCALES /* Need to do this here since mbsrtowcs doesn't allow incompletes. */ if (ENCODING == __ctype_encoding_utf8) { + if (!pwc) { + pwc = wcbuf; + } r = _wchar_utf8sntowcs(pwc, 1, &p, n, ps, 1); return (r == 1) ? (p-s) : r; /* Need to return 0 if nul char. */ } #endif +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: This adds a trailing nul! +#endif /* __UCLIBC_MJN3_ONLY__ */ + r = __mbsnrtowcs(wcbuf, &p, SIZE_MAX, 1, ps); if (((ssize_t) r) >= 0) { @@ -291,7 +329,10 @@ size_t mbrtowc(wchar_t *__restrict pwc, const char *__restrict s, size_t wcrtomb(register char *__restrict s, wchar_t wc, mbstate_t *__restrict ps) { - wchar_t wcbuf[2]; +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Should wcsnrtombs nul-terminate unconditionally? Check glibc. +#endif /* __UCLIBC_MJN3_ONLY__ */ + wchar_t wcbuf[1]; const wchar_t *pwc; size_t r; char buf[MB_LEN_MAX]; @@ -303,9 +344,8 @@ size_t wcrtomb(register char *__restrict s, wchar_t wc, pwc = wcbuf; wcbuf[0] = wc; - wcbuf[1] = 0; - r = __wcsnrtombs(s, &pwc, SIZE_MAX, MB_LEN_MAX, ps); + r = __wcsnrtombs(s, &pwc, 1, MB_LEN_MAX, ps); return (r != 0) ? r : 1; } @@ -418,7 +458,7 @@ size_t _wchar_utf8sntowcs(wchar_t *__restrict pwc, size_t wn, if ((wc = ((unsigned char) *s++)) >= 0x80) { /* Not ASCII... */ mask = 0x40; #ifdef __UCLIBC_MJN3_ONLY__ -#warning fix range for 16 bit wides +#warning TODO: Fix range for 16 bit wchar_t case. #endif if ( ((unsigned char)(s[-1] - 0xc0)) < (0xfe - 0xc0) ) { goto START; @@ -495,7 +535,6 @@ size_t _wchar_utf8sntowcs(wchar_t *__restrict pwc, size_t wn, COMPLETE: *pwc = wc; pwc += incr; - } #ifdef DECODER while (--count); @@ -684,8 +723,8 @@ size_t __mbsnrtowcs(wchar_t *__restrict dst, const char **__restrict src, while (count) { if ((wc = ((unsigned char)(*s))) >= 0x80) { /* Non-ASCII... */ wc -= 0x80; - wc = __global_locale.tbl8c2wc[ - (__global_locale.idx8c2wc[wc >> Cc2wc_IDX_SHIFT] + wc = __UCLIBC_CURLOCALE_DATA.tbl8c2wc[ + (__UCLIBC_CURLOCALE_DATA.idx8c2wc[wc >> Cc2wc_IDX_SHIFT] << Cc2wc_IDX_SHIFT) + (wc & (Cc2wc_ROW_LEN - 1))]; if (!wc) { goto BAD; @@ -797,12 +836,12 @@ size_t __wcsnrtombs(char *__restrict dst, const wchar_t **__restrict src, } else { u = 0; if (wc <= Cwc2c_DOMAIN_MAX) { - u = __global_locale.idx8wc2c[wc >> (Cwc2c_TI_SHIFT + u = __UCLIBC_CURLOCALE_DATA.idx8wc2c[wc >> (Cwc2c_TI_SHIFT + Cwc2c_TT_SHIFT)]; - u = __global_locale.tbl8wc2c[(u << Cwc2c_TI_SHIFT) + u = __UCLIBC_CURLOCALE_DATA.tbl8wc2c[(u << Cwc2c_TI_SHIFT) + ((wc >> Cwc2c_TT_SHIFT) & ((1 << Cwc2c_TI_SHIFT)-1))]; - u = __global_locale.tbl8wc2c[Cwc2c_TI_LEN + u = __UCLIBC_CURLOCALE_DATA.tbl8wc2c[Cwc2c_TI_LEN + (u << Cwc2c_TT_SHIFT) + (wc & ((1 << Cwc2c_TT_SHIFT)-1))]; } @@ -859,7 +898,8 @@ size_t __wcsnrtombs(char *__restrict dst, const wchar_t **__restrict src, #ifdef L_wcswidth #ifdef __UCLIBC_MJN3_ONLY__ -#warning if we start doing translit, wcwidth and wcswidth will need updating. +#warning REMINDER: If we start doing translit, wcwidth and wcswidth will need updating. +#warning TODO: Update wcwidth to match latest by Kuhn. #endif #if defined(__UCLIBC_HAS_LOCALE__) && \ @@ -1163,7 +1203,7 @@ enum { * */ -const unsigned char codesets[] = +const unsigned char __iconv_codesets[] = "\x0a\xe0""WCHAR_T\x00" /* superset of UCS-4 but platform-endian */ #if __BYTE_ORDER == __BIG_ENDIAN "\x08\xec""UCS-4\x00" /* always BE */ @@ -1201,7 +1241,7 @@ static int find_codeset(const char *name) const unsigned char *s; int codeset; - for (s = codesets ; *s ; s += *s) { + for (s = __iconv_codesets ; *s ; s += *s) { if (!strcasecmp(s+2, name)) { return s[1]; } @@ -1212,10 +1252,10 @@ static int find_codeset(const char *name) /* TODO: maybe CODESET_LIST + *s ??? */ /* 7bit is 1, UTF-8 is 2, 8-bit is >= 3 */ codeset = 2; - s = CODESET_LIST; + s = __LOCALE_DATA_CODESET_LIST; do { ++codeset; /* Increment codeset first. */ - if (!strcasecmp(CODESET_LIST+*s, name)) { + if (!strcasecmp(__LOCALE_DATA_CODESET_LIST+*s, name)) { return codeset; } } while (*++s); @@ -1223,7 +1263,7 @@ static int find_codeset(const char *name) return 0; /* No matching codeset! */ } -iconv_t iconv_open(const char *tocode, const char *fromcode) +iconv_t weak_function iconv_open(const char *tocode, const char *fromcode) { register _UC_iconv_t *px; int tocodeset, fromcodeset; @@ -1244,16 +1284,17 @@ iconv_t iconv_open(const char *tocode, const char *fromcode) return (iconv_t)(-1); } -int iconv_close(iconv_t cd) +int weak_function iconv_close(iconv_t cd) { free(cd); return 0; } -size_t iconv(iconv_t cd, char **__restrict inbuf, - size_t *__restrict inbytesleft, - char **__restrict outbuf, size_t *__restrict outbytesleft) +size_t weak_function iconv(iconv_t cd, char **__restrict inbuf, + size_t *__restrict inbytesleft, + char **__restrict outbuf, + size_t *__restrict outbytesleft) { _UC_iconv_t *px = (_UC_iconv_t *) cd; size_t nrcount, r; @@ -1362,9 +1403,9 @@ size_t iconv(iconv_t cd, char **__restrict inbuf, return (size_t)(-1); } #ifdef __UCLIBC_MJN3_ONLY__ -#warning optimize this +#warning TODO: optimize this. #endif - if (p != NULL) { /* incomplet char case */ + if (p != NULL) { /* incomplete char case */ goto INVALID; } p = *inbuf + 1; /* nul */ @@ -1374,10 +1415,10 @@ size_t iconv(iconv_t cd, char **__restrict inbuf, if (px->fromcodeset == IC_ASCII) { /* US-ASCII codeset */ goto ILLEGAL; } else { /* some other 8-bit ascii-extension codeset */ - const codeset_8_bit_t *c8b + const __codeset_8_bit_t *c8b = __locale_mmap->codeset_8_bit + px->fromcodeset - 3; wc -= 0x80; - wc = __global_locale.tbl8c2wc[ + wc = __UCLIBC_CURLOCALE_DATA.tbl8c2wc[ (c8b->idx8c2wc[wc >> Cc2wc_IDX_SHIFT] << Cc2wc_IDX_SHIFT) + (wc & (Cc2wc_ROW_LEN - 1))]; if (!wc) { @@ -1439,7 +1480,7 @@ size_t iconv(iconv_t cd, char **__restrict inbuf, r = _wchar_wcsntoutf8s(*outbuf, *outbytesleft, &pw, 1); if (r != (size_t)(-1)) { #ifdef __UCLIBC_MJN3_ONLY__ -#warning what happens for a nul? +#warning TODO: What happens for a nul? #endif if (r == 0) { if (wc != 0) { @@ -1458,14 +1499,14 @@ size_t iconv(iconv_t cd, char **__restrict inbuf, **outbuf = wc; } else { if ((px->tocodeset != 0x01) && (wc <= Cwc2c_DOMAIN_MAX)) { - const codeset_8_bit_t *c8b + const __codeset_8_bit_t *c8b = __locale_mmap->codeset_8_bit + px->tocodeset - 3; __uwchar_t u; u = c8b->idx8wc2c[wc >> (Cwc2c_TI_SHIFT + Cwc2c_TT_SHIFT)]; - u = __global_locale.tbl8wc2c[(u << Cwc2c_TI_SHIFT) + u = __UCLIBC_CURLOCALE_DATA.tbl8wc2c[(u << Cwc2c_TI_SHIFT) + ((wc >> Cwc2c_TT_SHIFT) & ((1 << Cwc2c_TI_SHIFT)-1))]; - wc = __global_locale.tbl8wc2c[Cwc2c_TI_LEN + wc = __UCLIBC_CURLOCALE_DATA.tbl8wc2c[Cwc2c_TI_LEN + (u << Cwc2c_TT_SHIFT) + (wc & ((1 << Cwc2c_TT_SHIFT)-1))]; if (wc) { @@ -1497,7 +1538,7 @@ size_t iconv(iconv_t cd, char **__restrict inbuf, #include <stdarg.h> #include <libgen.h> -extern const unsigned char codesets[]; +extern const unsigned char __iconv_codesets[]; #define IBUF BUFSIZ #define OBUF BUFSIZ @@ -1572,12 +1613,12 @@ int main(int argc, char **argv) if (opts[5]) { /* -l */ fprintf(stderr, "Recognized codesets:\n"); - for (s = codesets ; *s ; s += *s) { + for (s = __iconv_codesets ; *s ; s += *s) { fprintf(stderr," %s\n", s+2); } - s = CODESET_LIST; + s = __LOCALE_DATA_CODESET_LIST; do { - fprintf(stderr," %s\n", CODESET_LIST+ (unsigned char)(*s)); + fprintf(stderr," %s\n", __LOCALE_DATA_CODESET_LIST+ (unsigned char)(*s)); } while (*++s); return EXIT_SUCCESS; diff --git a/libc/misc/wchar/wstdio.c b/libc/misc/wchar/wstdio.c index dfeb35c30..1069ee938 100644 --- a/libc/misc/wchar/wstdio.c +++ b/libc/misc/wchar/wstdio.c @@ -171,7 +171,6 @@ UNLOCKED(wint_t,fgetwc,(register FILE *stream),(stream)) size_t r; unsigned char c[1]; unsigned char sbuf[1]; - unsigned char ungot_width; /* Support ftell after wscanf ungetwc. */ wi = WEOF; /* Prepare for failure. */ @@ -183,8 +182,18 @@ UNLOCKED(wint_t,fgetwc,(register FILE *stream),(stream)) stream->modeflags |= __FLAG_WIDE; if (stream->modeflags & __MASK_UNGOT) {/* Any ungetwc()s? */ - assert( (stream->modeflags & (__FLAG_READING|__FLAG_ERROR)) - == __FLAG_READING); + + assert(stream->modeflags & __FLAG_READING); + +/* assert( (stream->modeflags & (__FLAG_READING|__FLAG_ERROR)) */ +/* == __FLAG_READING); */ + + if ((((stream->modeflags & __MASK_UNGOT) > 1) || stream->ungot[1])) { + stream->ungot_width[0] = 0; /* Application ungot... */ + } else { + stream->ungot_width[0] = stream->ungot_width[1]; /* scanf ungot */ + } + wi = stream->ungot[(--stream->modeflags) & __MASK_UNGOT]; stream->ungot[1] = 0; goto DONE; @@ -196,7 +205,9 @@ UNLOCKED(wint_t,fgetwc,(register FILE *stream),(stream)) ++stream->bufend; } - ungot_width = 0; + if (stream->state.mask == 0) { /* If last was a complete char */ + stream->ungot_width[0] = 0; /* then reset the width. */ + } LOOP: if ((n = stream->bufread - stream->bufpos) == 0) { @@ -204,12 +215,12 @@ UNLOCKED(wint_t,fgetwc,(register FILE *stream),(stream)) } r = mbrtowc(wc, stream->bufpos, n, &stream->state); - if (((ssize_t) r) >= 0) { /* Single byte... */ + if (((ssize_t) r) >= 0) { /* Success... */ if (r == 0) { /* Nul wide char... means 0 byte for us so */ ++r; /* increment r and handle below as single. */ } stream->bufpos += r; - stream->ungot_width[0] = ungot_width + r; + stream->ungot_width[0] += r; wi = *wc; goto DONE; } @@ -217,7 +228,7 @@ UNLOCKED(wint_t,fgetwc,(register FILE *stream),(stream)) if (r == ((size_t) -2)) { /* Potentially valid but incomplete and no more buffered. */ stream->bufpos += n; /* Update bufpos for stream. */ - ungot_width += n; + stream->ungot_width[0] += n; FILL_BUFFER: if (_stdio_fread(c, (size_t) 1, stream) > 0) { assert(stream->bufpos == stream->bufstart + 1); @@ -371,7 +382,8 @@ UNLOCKED(int,fputws,(const wchar_t *__restrict ws, #ifdef L_ungetwc /* * Note: This is the application-callable ungetwc. If wscanf calls this, it - * should also set stream->ungot[1] to 0 if this is the only ungot. + * should also set stream->ungot[1] to 0 if this is the only ungot, as well + * as reset stream->ungot_width[1] for use by _stdio_adjpos(). */ /* Reentrant. */ @@ -389,8 +401,7 @@ wint_t ungetwc(wint_t c, register FILE *stream) } stream->modeflags |= __FLAG_WIDE; - /* If can't read or there's been an error, or c == EOF, or ungot slots - * already filled, then return EOF */ + /* If can't read or c == WEOF or ungot slots already filled, then fail. */ if ((stream->modeflags & (__MASK_UNGOT2|__FLAG_WRITEONLY #ifndef __STDIO_AUTO_RW_TRANSITION @@ -406,14 +417,18 @@ wint_t ungetwc(wint_t c, register FILE *stream) /* ungot_width */ #ifdef __STDIO_BUFFERS - /* TODO: shouldn't allow writing??? */ +#ifdef __STDIO_AUTO_RW_TRANSITION if (stream->modeflags & __FLAG_WRITING) { fflush_unlocked(stream); /* Commit any write-buffered chars. */ } +#endif /* __STDIO_AUTO_RW_TRANSITION */ #endif /* __STDIO_BUFFERS */ /* Clear EOF and WRITING flags, and set READING FLAG */ stream->modeflags &= ~(__FLAG_EOF|__FLAG_WRITING); +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Is setting the reading flag after an ungetwc necessary? +#endif /* __UCLIBC_MJN3_ONLY__ */ stream->modeflags |= __FLAG_READING; stream->ungot[1] = 1; /* Flag as app ungetc call; wscanf fixes up. */ stream->ungot[(stream->modeflags++) & __MASK_UNGOT] = c; diff --git a/libc/misc/wctype/Makefile b/libc/misc/wctype/Makefile index 875ccef56..44734945c 100644 --- a/libc/misc/wctype/Makefile +++ b/libc/misc/wctype/Makefile @@ -25,12 +25,22 @@ TOPDIR=../../../ include $(TOPDIR)Rules.mak 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 towlower.o \ - towupper.o iswblank.o wctype.o iswctype.o wctrans.o towctrans.o +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 \ + wctype.o iswctype.o towlower.o towupper.o + +MOBJx= iswalnum_l.o iswalpha_l.o iswcntrl_l.o iswdigit_l.o iswgraph_l.o \ + iswlower_l.o iswprint_l.o iswpunct_l.o iswspace_l.o iswupper_l.o \ + iswxdigit_l.o iswblank_l.o \ + wctype_l.o iswctype_l.o wctrans_l.o towctrans_l.o towlower_l.o towupper_l.o OBJS=$(MOBJ) +ifeq ($(UCLIBC_HAS_XLOCALE),y) + OBJS += $(MOBJx) +endif + all: $(OBJS) $(LIBC) $(LIBC): ar-target @@ -42,6 +52,10 @@ $(MOBJ): $(MSRC) $(CC) $(CFLAGS) -DL_$* $< -c -o $*.o $(STRIPTOOL) -x -R .note -R .comment $*.o +$(MOBJx): $(MSRC) + $(CC) $(CFLAGS) -DL_$* -D__UCLIBC_DO_XLOCALE $< -c -o $*.o + $(STRIPTOOL) -x -R .note -R .comment $*.o + clean: rm -f *.[oa] *~ core diff --git a/libc/misc/wctype/wctype.c b/libc/misc/wctype/wctype.c index 68b9e4579..e0dde3a1f 100644 --- a/libc/misc/wctype/wctype.c +++ b/libc/misc/wctype/wctype.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002 Manuel Novoa III +/* 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 @@ -35,149 +35,309 @@ #include <errno.h> #include <locale.h> #include <ctype.h> +#include <stdint.h> +#include <bits/uClibc_uwchar.h> + +#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 <xlocale.h> + +extern int __iswctype_l (wint_t __wc, wctype_t __desc, __locale_t __locale); + __THROW; +extern wint_t __towlower_l (wint_t __wc, __locale_t __locale) __THROW; + +extern wint_t __towupper_l (wint_t __wc, __locale_t __locale) __THROW; +extern wint_t __towctrans_l (wint_t __wc, wctrans_t __desc, + __locale_t __locale) __THROW; + +#endif /* __UCLIBC_HAS_XLOCALE__ */ /* 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 */ +/* #define SMALL_UPLOW */ -#ifndef __LOCALE_C_ONLY -#define __WCTYPE_WITH_LOCALE +/**********************************************************************/ +#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 -/**********************************************************************/ -#ifndef __PASTE -#define __PASTE(X,Y) X ## Y +/* 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 C_MACRO(X) __PASTE(__C_,X)(wc) +#define ENCODING ((__UCLIBC_CURLOCALE_DATA).encoding) -#define CT_MACRO(X) __PASTE(__ctype_,X)(wc) +#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) /**********************************************************************/ -/* TODO: fix this! */ -#ifdef __WCTYPE_WITH_LOCALE +#undef __PASTE2 +#undef __PASTE3 +#define __PASTE2(X,Y) X ## Y +#define __PASTE3(X,Y,Z) X ## Y ## Z -#define WCctype (__global_locale.tblwctype) -#define WCuplow (__global_locale.tblwuplow) -#define WCcmob (__global_locale.tblwcomb) -#define WCuplow_diff (__global_locale.tblwuplow_diff) +#ifdef __UCLIBC_DO_XLOCALE -#define ENCODING (__global_locale.encoding) +extern int __iswctype_l (wint_t __wc, wctype_t __desc, __locale_t __locale) + __THROW; #define ISW_FUNC_BODY(NAME) \ -int NAME (wint_t wc) \ +int __PASTE3(__isw,NAME,_l) (wint_t wc, __locale_t l) \ { \ - return iswctype(wc, __PASTE(_CTYPE_,NAME)); \ -} + return __iswctype_l(wc, __PASTE2(_CTYPE_is,NAME), l); \ +} \ +weak_alias(__PASTE3(__isw,NAME,_l), __PASTE3(isw,NAME,_l)) + +#else /* __UCLIBC_DO_XLOCALE */ -#else /* __WCTYPE_WITH_LOCALE */ +extern int __iswctype (wint_t __wc, wctype_t __desc) __THROW; #define ISW_FUNC_BODY(NAME) \ -int NAME (wint_t wc) \ +int __PASTE2(isw,NAME) (wint_t wc) \ { \ - return C_MACRO(NAME); \ + return __iswctype(wc, __PASTE2(_CTYPE_is,NAME)); \ } -#endif /* __WCTYPE_WITH_LOCALE */ - +#endif /* __UCLIBC_DO_XLOCALE */ /**********************************************************************/ -#ifdef L_iswalnum +#if defined(L_iswalnum) || defined(L_iswalnum_l) -ISW_FUNC_BODY(iswalnum); +ISW_FUNC_BODY(alnum); #endif /**********************************************************************/ -#ifdef L_iswalpha +#if defined(L_iswalpha) || defined(L_iswalpha_l) -ISW_FUNC_BODY(iswalpha); +ISW_FUNC_BODY(alpha); #endif /**********************************************************************/ -#ifdef L_iswblank +#if defined(L_iswblank) || defined(L_iswblank_l) -ISW_FUNC_BODY(iswblank); +ISW_FUNC_BODY(blank); #endif /**********************************************************************/ -#ifdef L_iswcntrl +#if defined(L_iswcntrl) || defined(L_iswcntrl_l) -ISW_FUNC_BODY(iswcntrl); +ISW_FUNC_BODY(cntrl); #endif /**********************************************************************/ -#ifdef L_iswdigit +#if defined(L_iswdigit) || defined(L_iswdigit_l) -int iswdigit(wint_t wc) -{ - return __C_iswdigit(wc); -} +ISW_FUNC_BODY(digit); #endif /**********************************************************************/ -#ifdef L_iswgraph +#if defined(L_iswgraph) || defined(L_iswgraph_l) -ISW_FUNC_BODY(iswgraph); +ISW_FUNC_BODY(graph); #endif /**********************************************************************/ -#ifdef L_iswlower +#if defined(L_iswlower) || defined(L_iswlower_l) -ISW_FUNC_BODY(iswlower); +ISW_FUNC_BODY(lower); #endif /**********************************************************************/ -#ifdef L_iswprint +#if defined(L_iswprint) || defined(L_iswprint_l) -ISW_FUNC_BODY(iswprint); +ISW_FUNC_BODY(print); #endif /**********************************************************************/ -#ifdef L_iswpunct +#if defined(L_iswpunct) || defined(L_iswpunct_l) -ISW_FUNC_BODY(iswpunct); +ISW_FUNC_BODY(punct); #endif /**********************************************************************/ -#ifdef L_iswspace +#if defined(L_iswspace) || defined(L_iswspace_l) -ISW_FUNC_BODY(iswspace); +ISW_FUNC_BODY(space); #endif /**********************************************************************/ -#ifdef L_iswupper +#if defined(L_iswupper) || defined(L_iswupper_l) -ISW_FUNC_BODY(iswupper); +ISW_FUNC_BODY(upper); #endif /**********************************************************************/ -#ifdef L_iswxdigit +#if defined(L_iswxdigit) || defined(L_iswxdigit_l) -int iswxdigit(wint_t wc) -{ - return __C_iswxdigit(wc); -} +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 -#ifdef __WCTYPE_WITH_LOCALE +wint_t towlower(wint_t wc) +{ + return __C_towlower(wc); +} + +#else /* __LOCALE_C_ONLY */ #ifdef SMALL_UPLOW +#if defined(L_towlower) && defined(__UCLIBC_HAS_XLOCALE__) + wint_t towlower(wint_t wc) { - return towctrans(wc, _CTYPE_tolower); + return __towctrans_l(wc, _CTYPE_tolower, __UCLIBC_CURLOCALE); } -#else +#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; @@ -202,34 +362,79 @@ wint_t towlower(wint_t wc) return wc; } -#endif +#endif /* defined(L_towlower) && defined(__UCLIBC_HAS_XLOCALE__) */ -#else /* __WCTYPE_WITH_LOCALE */ +#endif /* SMALL_UPLOW */ -wint_t towlower(wint_t wc) -{ - return __C_towlower(wc); -} +#ifdef L_towlower_l +weak_alias(__towlower_l, towlower_l) +#endif /* L_towlower_l */ -#endif /* __WCTYPE_WITH_LOCALE */ +#endif /* __LOCALE_C_ONLY */ #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 -#ifdef __WCTYPE_WITH_LOCALE +wint_t towupper(wint_t wc) +{ + return __C_towupper(wc); +} + +#else /* __LOCALE_C_ONLY */ #ifdef SMALL_UPLOW +#if defined(L_towupper) && defined(__UCLIBC_HAS_XLOCALE__) + wint_t towupper(wint_t wc) { - return towctrans(wc, _CTYPE_toupper); + return __towctrans_l(wc, _CTYPE_toupper, __UCLIBC_CURLOCALE); } -#else +#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; @@ -254,16 +459,15 @@ wint_t towupper(wint_t wc) return wc; } -#endif +#endif /* defined(L_towupper) && defined(__UCLIBC_HAS_XLOCALE__) */ -#else /* __WCTYPE_WITH_LOCALE */ +#endif /* SMALL_UPLOW */ -wint_t towupper(wint_t wc) -{ - return __C_towupper(wc); -} +#ifdef L_towupper_l +weak_alias(__towupper_l, towupper_l) +#endif /* L_towupper_l */ -#endif /* __WCTYPE_WITH_LOCALE */ +#endif /* __LOCALE_C_ONLY */ #endif /**********************************************************************/ @@ -293,33 +497,102 @@ wctype_t wctype(const char *property) #endif /**********************************************************************/ -#ifdef L_iswctype +#ifdef L_wctype_l #ifdef __UCLIBC_MJN3_ONLY__ -#warning duh... replace the range-based classification with table lookup! -#endif +#warning REMINDER: Currently wctype_l simply calls wctype. +#endif /* __UCLIBC_MJN3_ONLY__ */ -#ifdef __WCTYPE_WITH_LOCALE +wctype_t __wctype_l (const char *property, __locale_t locale) +{ + return wctype(property); +} + +weak_alias(__wctype_l, 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__ -#warning TODO: need to fix locale ctype table lookup stuff +#ifdef L_iswctype +#warning CONSIDER: Change to bit shift? would need to sync with wctype.h #endif -#if 0 -extern const char ctype_range[]; -#else -static const char ctype_range[] = { - __CTYPE_RANGES +#endif /* __UCLIBC_MJN3_ONLY__ */ + + +#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 + +#endif /* defined(L_iswctype_L) || defined(__LOCALE_C_ONLY) */ + +#ifdef __LOCALE_C_ONLY + +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 /* __LOCALE_C_ONLY */ #ifdef __UCLIBC_MJN3_ONLY__ -#warning TODO: need to handle combining class! +#ifdef L_iswctype +#warning CONSIDER: Handle combining class? #endif +#endif /* __UCLIBC_MJN3_ONLY__ */ -#define WCctype_TI_MASK ((1 << WCctype_TI_SHIFT) - 1) -#define WCctype_II_MASK ((1 << WCctype_II_SHIFT) - 1) +#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); +} -int iswctype(wint_t wc, wctype_t desc) +#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; @@ -348,122 +621,100 @@ int iswctype(wint_t wc, wctype_t desc) 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; } -#else +#endif /* defined(L_iswctype) && defined(__UCLIBC_HAS_XLOCALE__) */ -static const unsigned char WCctype[] = { - __CTYPE_cntrl_nonspace | (__CTYPE_cntrl_nonspace << 4), - __CTYPE_cntrl_nonspace | (__CTYPE_cntrl_nonspace << 4), - __CTYPE_cntrl_nonspace | (__CTYPE_cntrl_nonspace << 4), - __CTYPE_cntrl_nonspace | (__CTYPE_cntrl_nonspace << 4), - __CTYPE_cntrl_nonspace | (__CTYPE_cntrl_space_blank << 4), - __CTYPE_cntrl_space_nonblank | (__CTYPE_cntrl_space_nonblank << 4), - __CTYPE_cntrl_space_nonblank | (__CTYPE_cntrl_space_nonblank << 4), - __CTYPE_cntrl_nonspace | (__CTYPE_cntrl_nonspace << 4), - __CTYPE_cntrl_nonspace | (__CTYPE_cntrl_nonspace << 4), - __CTYPE_cntrl_nonspace | (__CTYPE_cntrl_nonspace << 4), - __CTYPE_cntrl_nonspace | (__CTYPE_cntrl_nonspace << 4), - __CTYPE_cntrl_nonspace | (__CTYPE_cntrl_nonspace << 4), - __CTYPE_cntrl_nonspace | (__CTYPE_cntrl_nonspace << 4), - __CTYPE_cntrl_nonspace | (__CTYPE_cntrl_nonspace << 4), - __CTYPE_cntrl_nonspace | (__CTYPE_cntrl_nonspace << 4), - __CTYPE_cntrl_nonspace | (__CTYPE_cntrl_nonspace << 4), - __CTYPE_print_space_blank | (__CTYPE_punct << 4), - __CTYPE_punct | (__CTYPE_punct << 4), - __CTYPE_punct | (__CTYPE_punct << 4), - __CTYPE_punct | (__CTYPE_punct << 4), - __CTYPE_punct | (__CTYPE_punct << 4), - __CTYPE_punct | (__CTYPE_punct << 4), - __CTYPE_punct | (__CTYPE_punct << 4), - __CTYPE_punct | (__CTYPE_punct << 4), - __CTYPE_digit | (__CTYPE_digit << 4), - __CTYPE_digit | (__CTYPE_digit << 4), - __CTYPE_digit | (__CTYPE_digit << 4), - __CTYPE_digit | (__CTYPE_digit << 4), - __CTYPE_digit | (__CTYPE_digit << 4), - __CTYPE_punct | (__CTYPE_punct << 4), - __CTYPE_punct | (__CTYPE_punct << 4), - __CTYPE_punct | (__CTYPE_punct << 4), - __CTYPE_punct | (__CTYPE_alpha_upper << 4), - __CTYPE_alpha_upper | (__CTYPE_alpha_upper << 4), - __CTYPE_alpha_upper | (__CTYPE_alpha_upper << 4), - __CTYPE_alpha_upper | (__CTYPE_alpha_upper << 4), - __CTYPE_alpha_upper | (__CTYPE_alpha_upper << 4), - __CTYPE_alpha_upper | (__CTYPE_alpha_upper << 4), - __CTYPE_alpha_upper | (__CTYPE_alpha_upper << 4), - __CTYPE_alpha_upper | (__CTYPE_alpha_upper << 4), - __CTYPE_alpha_upper | (__CTYPE_alpha_upper << 4), - __CTYPE_alpha_upper | (__CTYPE_alpha_upper << 4), - __CTYPE_alpha_upper | (__CTYPE_alpha_upper << 4), - __CTYPE_alpha_upper | (__CTYPE_alpha_upper << 4), - __CTYPE_alpha_upper | (__CTYPE_alpha_upper << 4), - __CTYPE_alpha_upper | (__CTYPE_punct << 4), - __CTYPE_punct | (__CTYPE_punct << 4), - __CTYPE_punct | (__CTYPE_punct << 4), - __CTYPE_punct | (__CTYPE_alpha_lower << 4), - __CTYPE_alpha_lower | (__CTYPE_alpha_lower << 4), - __CTYPE_alpha_lower | (__CTYPE_alpha_lower << 4), - __CTYPE_alpha_lower | (__CTYPE_alpha_lower << 4), - __CTYPE_alpha_lower | (__CTYPE_alpha_lower << 4), - __CTYPE_alpha_lower | (__CTYPE_alpha_lower << 4), - __CTYPE_alpha_lower | (__CTYPE_alpha_lower << 4), - __CTYPE_alpha_lower | (__CTYPE_alpha_lower << 4), - __CTYPE_alpha_lower | (__CTYPE_alpha_lower << 4), - __CTYPE_alpha_lower | (__CTYPE_alpha_lower << 4), - __CTYPE_alpha_lower | (__CTYPE_alpha_lower << 4), - __CTYPE_alpha_lower | (__CTYPE_alpha_lower << 4), - __CTYPE_alpha_lower | (__CTYPE_alpha_lower << 4), - __CTYPE_alpha_lower | (__CTYPE_punct << 4), - __CTYPE_punct | (__CTYPE_punct << 4), - __CTYPE_punct | (__CTYPE_cntrl_nonspace << 4), -}; +#ifdef L_iswctype_l +weak_alias(__iswctype_l, iswctype_l) +#endif /* L_iswctype_l */ -static const char ctype_range[] = { - __CTYPE_RANGES -}; +#endif /* __LOCALE_C_ONLY */ -int iswctype(wint_t wc, wctype_t desc) -{ - unsigned char d = __CTYPE_unclassified; +#ifdef L_iswctype +weak_alias(__iswctype, iswctype) +#endif /* L_iswctype */ - if (((__uwchar_t) wc) <= 0x7f) { - if (desc < _CTYPE_iswxdigit) { - d = WCctype[wc >> 1]; - d = (wc & 1) ? (d >> 4) : (d & 0xf); +#endif +/**********************************************************************/ +#if defined(L_towctrans) || defined(L_towctrans_l) - return ( ((unsigned char)(d - ctype_range[2*desc])) - <= ctype_range[2*desc + 1] ) - && ((desc != _CTYPE_iswblank) || (d & 1)); - } +#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 - if (desc == _CTYPE_iswxdigit) { - return __C_isxdigit(((char) wc)); +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 0; + return wc; } -#endif +#else /* __LOCALE_C_ONLY */ -#endif -/**********************************************************************/ #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__) -#ifdef __WCTYPE_WITH_LOCALE +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) +wint_t TOWCTRANS(wint_t wc, wctrans_t desc) { unsigned int sc, n, i; __uwchar_t u = wc; @@ -499,6 +750,9 @@ wint_t towctrans(wint_t wc, wctrans_t desc) } 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)) @@ -516,9 +770,9 @@ wint_t towctrans(wint_t wc, wctrans_t desc) return wc; } -#else +#else /* SMALL_UPLOW */ -wint_t towctrans(wint_t wc, wctrans_t desc) +wint_t TOWCTRANS(wint_t wc, wctrans_t desc) { if (ENCODING == __ctype_encoding_7_bit) { if ((((__uwchar_t) wc) > 0x7f) @@ -532,12 +786,15 @@ wint_t towctrans(wint_t wc, wctrans_t desc) } if (desc == _CTYPE_tolower) { - return towlower(wc); + return TOWLOWER(wc, __UCLIBC_CURLOCALE); } else if (((unsigned int)(desc - _CTYPE_toupper)) <= (_CTYPE_totitle - _CTYPE_toupper) ) { - wc = towupper(wc); + 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)) @@ -553,28 +810,15 @@ wint_t towctrans(wint_t wc, wctrans_t desc) return wc; } -#endif +#endif /* SMALL_UPLOW */ -#else /* __WCTYPE_WITH_LOCALE */ +#endif /* defined(L_towctrans) && defined(__UCLIBC_HAS_XLOCALE__) */ -/* Minimal support for C/POSIX locale. */ +#ifdef L_towctrans_l +weak_alias(__towctrans_l, towctrans_l) +#endif /* L_towctrans_l */ -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; -} - -#endif /* __WCTYPE_WITH_LOCALE */ +#endif /* __LOCALE_C_ONLY */ #endif /**********************************************************************/ @@ -603,3 +847,18 @@ wctrans_t wctrans(const char *property) #endif /**********************************************************************/ +#ifdef L_wctrans_l + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning REMINDER: Currently wctrans_l simply calls wctrans. +#endif /* __UCLIBC_MJN3_ONLY__ */ + +wctrans_t __wctrans_l(const char *property, __locale_t locale) +{ + return wctrans(property); +} + +weak_alias(__wctrans_l, wctrans_l) + +#endif +/**********************************************************************/ diff --git a/libc/stdio/Makefile b/libc/stdio/Makefile index 9f8af9e4c..271fe7207 100644 --- a/libc/stdio/Makefile +++ b/libc/stdio/Makefile @@ -51,10 +51,15 @@ MSRC2= printf.c MOBJ2= vsnprintf.o vdprintf.o vasprintf.o vprintf.o vsprintf.o \ fprintf.o snprintf.o dprintf.o asprintf.o printf.o sprintf.o +MSRC3=scanf.c +MOBJ3=scanf.o sscanf.o fscanf.o vscanf.o vsscanf.o vfscanf.o \ + __scan_cookie.o __psfs_parse_spec.o __psfs_do_numeric.o + ifeq ($(UCLIBC_HAS_WCHAR),y) MOBJ += _wstdio_fwrite.o MOBJ2 += fwprintf.o wprintf.o swprintf.o vwprintf.o vswprintf.o \ vfwprintf.o + MOBJ3 += wscanf.o swscanf.o fwscanf.o vwscanf.o vswscanf.o vfwscanf.o endif ifneq ($(USE_OLD_VFPRINTF),y) @@ -63,16 +68,11 @@ ifneq ($(USE_OLD_VFPRINTF),y) _store_inttype.o _load_inttype.o \ register_printf_function.o parse_printf_format.o endif -# _do_one_spec.o - ifeq ($(UCLIBC_HAS_FLOATS),y) - MOBJ2 += _dtostr.o + MOBJ2 += _fpmaxtostr.o endif -MSRC3=scanf.c -MOBJ3=scanf.o sscanf.o fscanf.o vscanf.o vsscanf.o vfscanf.o - CSRC=popen.c tmpfile.c tmpnam.c tmpnam_r.c tempnam.c ifeq ($(USE_OLD_VFPRINTF),y) CSRC += old_vfprintf.c diff --git a/libc/stdio/old_vfprintf.c b/libc/stdio/old_vfprintf.c index 9c8970912..77e593490 100644 --- a/libc/stdio/old_vfprintf.c +++ b/libc/stdio/old_vfprintf.c @@ -117,7 +117,8 @@ * strerror and the corresponding string table which together are about 3.8k. */ -#define WANT_GNU_ERRNO 0 +/* Now controlled by uClibc_stdio.h and set below. */ +/* #define WANT_GNU_ERRNO 0 */ /**************************************************************************/ @@ -143,6 +144,12 @@ /* #define __isdigit(c) (((unsigned int)(c - '0')) < 10) */ +#ifdef __STDIO_PRINTF_M_SUPPORT +#define WANT_GNU_ERRNO 1 +#else +#define WANT_GNU_ERRNO 0 +#endif + #if defined(__UCLIBC_HAS_FLOATS__) extern size_t _dtostr(FILE * fp, long double x, struct printf_info *info); #endif diff --git a/libc/stdio/printf.c b/libc/stdio/printf.c index 945d3c38d..da0444510 100644 --- a/libc/stdio/printf.c +++ b/libc/stdio/printf.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002 Manuel Novoa III +/* Copyright (C) 2002, 2003 Manuel Novoa III * My stdio library for linux and (soon) elks. * * This library is free software; you can redistribute it and/or @@ -63,6 +63,12 @@ * 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. */ /* TODO: @@ -90,6 +96,7 @@ #include <assert.h> #include <stdint.h> #include <errno.h> +#include <locale.h> #define __PRINTF_INFO_NO_BITFIELD #include <printf.h> @@ -103,11 +110,22 @@ #include <wchar.h> #endif /* __UCLIBC_HAS_WCHAR__ */ -/**********************************************************************/ +/* 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 */ -#define __STDIO_PRINTF_FLOAT -#define __STDIO_PRINTF_M_SUPPORT + +/* This is undefined below depeding on uClibc's configuration. */ +#define __STDIO_PRINTF_FLOAT 1 + +/* Now controlled by uClibc_stdio.h. */ +/* #define __STDIO_PRINTF_M_SUPPORT */ /**********************************************************************/ @@ -120,28 +138,59 @@ #undef __STDIO_PRINTF_FLOAT #endif -#ifndef __STDIO_PRINTF_FLOAT -#undef L__dtostr -#endif +#ifdef __STDIO_PRINTF_FLOAT +#include <float.h> +#include <bits/uClibc_fpmax.h> +#else /* __STDIO_PRINTF_FLOAT */ +#undef L__fpmaxtostr +#endif /* __STDIO_PRINTF_FLOAT */ /**********************************************************************/ -#define __STDIO_GLIBC_CUSTOM_PRINTF +/* Now controlled by uClibc_stdio.h. */ +/* #define __STDIO_GLIBC_CUSTOM_PRINTF */ /* TODO -- move these to a configuration section? */ #define MAX_FIELD_WIDTH 4095 -#define MAX_USER_SPEC 10 -#define MAX_POS_ARGS 10 -/* TODO - fix the defs below */ -#define MAX_ARGS_PER_SPEC (MAX_POS_ARGS-2) +#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 __STDIO_GLIBC_CUSTOM_PRINTF + +#define MAX_USER_SPEC 10 +#define MAX_ARGS_PER_SPEC 5 + +#else /* __STDIO_GLIBC_CUSTOM_PRINTF */ -#if MAX_ARGS_PER_SPEC + 2 > MAX_POS_ARGS -#define MAX_ARGS MAX_ARGS_PER_SPEC + 2 +#undef MAX_USER_SPEC +#define MAX_ARGS_PER_SPEC 1 + +#endif /* __STDIO_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 -#define MAX_ARGS MAX_POS_ARGS +/* N for spec itself, plus 1 each for width and precision */ +#define MAX_ARGS (MAX_ARGS_PER_SPEC + 2) #endif + /**********************************************************************/ /* Deal with pre-C99 compilers. */ @@ -154,7 +203,7 @@ * to ensure we get the right behavior? Either that or fall back * on the portable (but costly in size) method of using a va_list *. * That means a pointer derefs in the va_arg() invocations... */ -#warning neither va_copy or __va_copy is defined. using a simple copy instead... +#warning Neither va_copy (C99/SUSv3) or __va_copy is defined. Using a simple copy instead. But you should really check that this is appropriate... /* the glibc manual suggests that this will usually suffice when __va_copy doesn't exist. */ #define va_copy(A,B) A = B @@ -167,9 +216,11 @@ #define __PA_FLAG_INTMASK \ (__PA_FLAG_CHAR|PA_FLAG_SHORT|__PA_FLAG_INT|PA_FLAG_LONG|PA_FLAG_LONG_LONG) +#ifdef __STDIO_GLIBC_CUSTOM_PRINTF extern printf_function _custom_printf_handler[MAX_USER_SPEC]; extern printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC]; extern char *_custom_printf_spec; +#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */ /**********************************************************************/ @@ -282,7 +333,7 @@ enum { #elif defined(LLONG_MAX) && (INTMAX_MAX == LLONG_MAX) #define IMS 8 #else -#error fix QUAL_CHARS ptrdiff_t entry 't'! +#error fix QUAL_CHARS intmax_t entry 'j'! #endif #define QUAL_CHARS { \ @@ -340,7 +391,9 @@ typedef union { 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 */ @@ -349,6 +402,7 @@ typedef struct { #ifdef __va_arg_ptr void *argptr[MAX_ARGS]; #else +/* if defined(NL_ARGMAX) || defined(__STDIO_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]; @@ -361,7 +415,11 @@ typedef struct { only returns -1 if sets error indicator for the stream. */ #ifdef __STDIO_PRINTF_FLOAT -extern size_t _dtostr(FILE * fp, long double x, struct printf_info *info); +typedef void (__fp_outfunc_t)(FILE *fp, intptr_t type, intptr_t len, + intptr_t buf); + +extern size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, + __fp_outfunc_t fp_outfunc); #endif extern int _ppfs_init(ppfs_t *ppfs, const char *fmt0); /* validates */ @@ -390,6 +448,7 @@ size_t parse_printf_format(register const char *template, 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) { @@ -399,6 +458,7 @@ size_t parse_printf_format(register const char *template, *argtypes++ = ppfs.argtype[i]; } } else { /* Not using positional args. */ +#endif /* NL_ARGMAX */ while (*template) { if ((*template == '%') && (*++template != '%')) { ppfs.fmtpos = template; @@ -431,7 +491,9 @@ size_t parse_printf_format(register const char *template, ++template; } } +#ifdef NL_ARGMAX } +#endif /* NL_ARGMAX */ } return count; @@ -443,17 +505,23 @@ size_t parse_printf_format(register const char *template, int _ppfs_init(register ppfs_t *ppfs, const char *fmt0) { -#ifdef __UCLIBC_HAS_WCHAR__ - static const char invalid_mbs[] = "Invalid multibyte format string."; -#endif /* __UCLIBC_HAS_WCHAR__ */ 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_HAS_WCHAR__ - { +#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. */ @@ -463,7 +531,7 @@ int _ppfs_init(register ppfs_t *ppfs, const char *fmt0) return -1; } } -#endif /* __UCLIBC_HAS_WCHAR__ */ +#endif /* __UCLIBC_HAS_LOCALE__ */ /* now set all argtypes to no-arg */ { #if 1 @@ -506,6 +574,7 @@ int _ppfs_init(register ppfs_t *ppfs, const char *fmt0) 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; @@ -517,6 +586,7 @@ int _ppfs_init(register ppfs_t *ppfs, const char *fmt0) ++p; } } +#endif /* NL_MAX_ARG */ return 0; } @@ -529,12 +599,14 @@ void _ppfs_prepargs(register ppfs_t *ppfs, va_list arg) 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 /**********************************************************************/ @@ -549,7 +621,9 @@ void _ppfs_setargs(register ppfs_t *ppfs) #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 @@ -615,6 +689,7 @@ void _ppfs_setargs(register ppfs_t *ppfs) } ++p; } +#ifdef NL_ARGMAX } else { if (ppfs->info.width == INT_MIN) { ppfs->info.width @@ -625,6 +700,7 @@ void _ppfs_setargs(register ppfs_t *ppfs) = (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) { @@ -741,12 +817,14 @@ static int _is_equal_or_bigger_arg(int curtype, int newtype) #endif +#ifdef __STDIO_GLIBC_CUSTOM_PRINTF /* TODO - do this differently? */ static char _bss_custom_printf_spec[MAX_USER_SPEC]; /* 0-init'd for us. */ char *_custom_printf_spec = _bss_custom_printf_spec; printf_arginfo_function *_custom_printf_arginfo[MAX_USER_SPEC]; printf_function _custom_printf_handler[MAX_USER_SPEC]; +#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */ extern int _ppfs_parsespec(ppfs_t *ppfs) { @@ -758,12 +836,13 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) 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 */ - unsigned int conv_num; /* This does not need to be initialized. */ static const char spec_flags[] = SPEC_FLAGS; static const char spec_chars[] = SPEC_CHARS;/* TODO: b? */ static const char spec_ranges[] = SPEC_RANGES; @@ -781,7 +860,10 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) 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 @@ -820,7 +902,7 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) ++fmt; } i = 0; - while (__isdigit(*fmt)) { + while (isdigit(*fmt)) { if (i < MAX_FIELD_WIDTH) { /* Avoid overflow. */ i = (i * 10) + (*fmt - '0'); } @@ -830,6 +912,7 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) /* TODO: if val not in range, then error */ +#ifdef NL_ARGMAX if ((*fmt == '$') && (i > 0)) {/* Positional spec. */ ++fmt; if (maxposarg == 0) { @@ -846,7 +929,7 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) #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 +#endif /* __UCLIBC_MJN3_ONLY__ */ if (*fmt == 'm') { goto PREC_WIDTH; } @@ -862,6 +945,18 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) 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; @@ -889,13 +984,16 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) } 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 if (++p != fmt) { + } else +#endif /* NL_ARGMAX */ + if (++p != fmt) { /* Not using pos args but digits followed *. */ return -1; } @@ -943,7 +1041,7 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) p_m_spec_chars -= 2; /* lc -> C and ls -> S */ } - conv_num = p_m_spec_chars; + ppfs->conv_num = p_m_spec_chars; p = spec_ranges-1; while (p_m_spec_chars > *++p) {} @@ -962,18 +1060,17 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) ppfs->num_data_args = 1; if (!*p) { -#ifdef __STDIO_GLIBC_CUSTOM_PRINTF - /* TODO -- gnu %m support build option. */ #ifdef __STDIO_PRINTF_M_SUPPORT if (*fmt == 'm') { - conv_num = CONV_m; + ppfs->conv_num = CONV_m; ppfs->num_data_args = 0; goto DONE; } #endif +#ifdef __STDIO_GLIBC_CUSTOM_PRINTF /* Handle custom arg -- WARNING -- overwrites p!!! */ - conv_num = CONV_custom0; + ppfs->conv_num = CONV_custom0; p = _custom_printf_spec; do { if (*p == *fmt) { @@ -991,10 +1088,11 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) return -1; } -#ifdef __STDIO_GLIBC_CUSTOM_PRINTF +#if defined(__STDIO_GLIBC_CUSTOM_PRINTF) || defined(__STDIO_PRINTF_M_SUPPORT) DONE: #endif +#ifdef NL_ARGMAX if (maxposarg > 0) { i = 0; do { @@ -1014,12 +1112,14 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) } } 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; - ppfs->conv_num = conv_num; +#endif /* NL_ARGMAX */ #ifdef __UCLIBC_HAS_WCHAR__ if ((flags = ppfs->info._flags & FLAG_WIDESTREAM) == 0) { @@ -1039,6 +1139,8 @@ extern int _ppfs_parsespec(ppfs_t *ppfs) /**********************************************************************/ #ifdef L_register_printf_function +#ifdef __STDIO_GLIBC_CUSTOM_PRINTF + int register_printf_function(int spec, printf_function handler, printf_arginfo_function arginfo) { @@ -1076,10 +1178,17 @@ int register_printf_function(int spec, printf_function handler, } return -1; } + +#endif + #endif /**********************************************************************/ #ifdef L_vsnprintf +#ifdef __UCLIBC_MJN3_ONLY__ +#warning WISHLIST: Implement vsnprintf for non-buffered and no custom stream case. +#endif /* __UCLIBC_MJN3_ONLY__ */ + #ifdef __STDIO_BUFFERS int vsnprintf(char *__restrict buf, size_t size, const char * __restrict format, va_list arg) @@ -1206,7 +1315,7 @@ int vsnprintf(char *__restrict buf, size_t size, } #else /* __STDIO_GLIBC_CUSTOM_STREAMS */ -#warning skipping vsnprintf since no buffering and no custom streams! +#warning Skipping vsnprintf since no buffering and no custom streams! #endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ #endif /* __STDIO_BUFFERS */ #endif @@ -1258,7 +1367,7 @@ int vdprintf(int filedes, const char * __restrict format, va_list arg) #ifdef L_vasprintf #if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) -#warning skipping vasprintf since no buffering and no custom streams! +#warning Skipping vasprintf since no buffering and no custom streams! #else int vasprintf(char **__restrict buf, const char * __restrict format, @@ -1312,7 +1421,7 @@ int vprintf(const char * __restrict format, va_list arg) #ifdef L_vsprintf #if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) -#warning skipping vsprintf since no buffering and no custom streams! +#warning Skipping vsprintf since no buffering and no custom streams! #else int vsprintf(char *__restrict buf, const char * __restrict format, @@ -1343,7 +1452,7 @@ int fprintf(FILE * __restrict stream, const char * __restrict format, ...) #ifdef L_snprintf #if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) -#warning skipping snprintf since no buffering and no custom streams! +#warning Skipping snprintf since no buffering and no custom streams! #else int snprintf(char *__restrict buf, size_t size, @@ -1380,7 +1489,7 @@ int dprintf(int filedes, const char * __restrict format, ...) #ifdef L_asprintf #if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) -#warning skipping asprintf and __asprintf since no buffering and no custom streams! +#warning Skipping asprintf and __asprintf since no buffering and no custom streams! #else weak_alias(__asprintf,asprintf) @@ -1417,7 +1526,7 @@ int printf(const char * __restrict format, ...) #ifdef L_sprintf #if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) -#warning skipping sprintf since no buffering and no custom streams! +#warning Skipping sprintf since no buffering and no custom streams! #else int sprintf(char *__restrict buf, const char * __restrict format, ...) @@ -1492,7 +1601,7 @@ int vswprintf(wchar_t *__restrict buf, size_t size, return rv; } #else /* __STDIO_BUFFERS */ -#warning skipping vswprintf since no buffering! +#warning Skipping vswprintf since no buffering! #endif /* __STDIO_BUFFERS */ #endif /**********************************************************************/ @@ -1512,7 +1621,7 @@ int swprintf(wchar_t *__restrict buf, size_t size, } #else /* __STDIO_BUFFERS */ -#warning skipping vsWprintf since no buffering! +#warning Skipping vsWprintf since no buffering! #endif /* __STDIO_BUFFERS */ #endif /**********************************************************************/ @@ -1553,174 +1662,249 @@ int wprintf(const wchar_t * __restrict format, ...) } #endif /**********************************************************************/ -#ifdef L__dtostr -/* - * Copyright (C) 2000, 2001 Manuel Novoa III +#ifdef L__fpmaxtostr + +/* Copyright (C) 2000, 2001, 2003 Manuel Novoa III + * + * Function: * - * Function: size_t _dtostr(FILE *fp, long double x, struct printf_info *info) + * size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, + * __fp_outfunc_t fp_outfunc); * - * This was written for uClibc to provide floating point support for - * the printf functions. It handles +/- infinity and nan on i386. + * This is derived from the old _dtostr, whic I wrote for uClibc to provide + * floating point support for the printf functions. It handles +/- infinity, + * nan, and signed 0 assuming you have ieee arithmetic. It also now handles + * digit grouping (for the uClibc supported locales) and hexadecimal float + * notation. Finally, via the fp_outfunc parameter, it now supports wide + * output. * * Notes: * - * At most MAX_DIGITS significant digits are kept. Any trailing digits + * At most DECIMAL_DIG significant digits are kept. Any trailing digits * are treated as 0 as they are really just the results of rounding noise * anyway. If you want to do better, use an arbitary precision arithmetic * package. ;-) * - * It should also be fairly portable, as not assumptions are made about the - * bit-layout of doubles. - * - * It should be too difficult to convert this to handle long doubles on i386. - * For information, see the comments below. + * It should also be fairly portable, as no assumptions are made about the + * bit-layout of doubles. Of course, that does make it less efficient than + * it could be. * - * TODO: - * long double and/or float version? (note: for float can trim code some). - * - * Decrease the size. This is really much bigger than I'd like. */ /*****************************************************************************/ /* Don't change anything that follows unless you know what you're doing. */ /*****************************************************************************/ - -/* - * Configuration for the scaling power table. Ignoring denormals, you - * should have 2**EXP_TABLE_SIZE >= LDBL_MAX_EXP >= 2**(EXP_TABLE_SIZE-1). - * The minimum for standard C is 6. For IEEE 8bit doubles, 9 suffices. - * For long doubles on i386, use 13. +/* Fairly portable nan check. Bitwise for i386 generated larger code. + * If you have a better version, comment this out. */ -#define EXP_TABLE_SIZE 13 +#define isnan(x) ((x) != (x)) -/* - * Set this to the maximum number of digits you want converted. - * Conversion is done in blocks of DIGITS_PER_BLOCK (9 by default) digits. - * (20) 17 digits suffices to uniquely determine a (long) double on i386. +/* Without seminumerical functions to examine the sign bit, this is + * about the best we can do to test for '-0'. */ -#define MAX_DIGITS 20 +#define zeroisnegative(x) ((1./(x)) < 0) -/* - * Set this to the smallest integer type capable of storing a pointer. - */ -#define INT_OR_PTR int +/*****************************************************************************/ +/* Don't change anything that follows peroid!!! ;-) */ +/*****************************************************************************/ +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ +#if FLT_RADIX != 2 +#error FLT_RADIX != 2 is not currently supported +#endif +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ -/* - * This is really only used to check for infinities. The macro produces - * smaller code for i386 and, since this is tested before any floating point - * calculations, it doesn't appear to suffer from the excess precision problem - * caused by the FPU that strtod had. If it causes problems, call the function - * and compile zoicheck.c with -ffloat-store. - */ -#define _zero_or_inf_check(x) ( x == (x/4) ) +#define NUM_HEX_DIGITS ((FPMAX_MANT_DIG + 3)/ 4) -/* - * Fairly portable nan check. Bitwise for i386 generated larger code. - * If you have a better version, comment this out. +/* WARNING: Adjust _fp_out_wide() below if this changes! */ +/* With 32 bit ints, we can get 9 decimal digits per block. */ +#define DIGITS_PER_BLOCK 9 +#define HEX_DIGITS_PER_BLOCK 8 + +/* Maximum number of subcases to output double is... + * 0 - sign + * 1 - padding and initial digit + * 2 - digits left of the radix + * 3 - 0s left of the radix or radix + * 4 - radix or digits right of the radix + * 5 - 0s right of the radix + * 6 - exponent + * 7 - trailing space padding + * although not all cases may occur. */ -#define isnan(x) (x != x) +#define MAX_CALLS 8 /*****************************************************************************/ -/* Don't change anything that follows peroid!!! ;-) */ -/*****************************************************************************/ -#include <float.h> +#define NUM_DIGIT_BLOCKS ((DECIMAL_DIG+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK) +#define NUM_HEX_DIGIT_BLOCKS \ + ((NUM_HEX_DIGITS+HEX_DIGITS_PER_BLOCK-1)/HEX_DIGITS_PER_BLOCK) -/*****************************************************************************/ +/* WARNING: Adjust _fp_out_wide() below if this changes! */ -/* - * Set things up for the scaling power table. - */ +/* extra space for '-', '.', 'e+###', and nul */ +#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK ) -#if EXP_TABLE_SIZE < 6 -#error EXP_TABLE_SIZE should be at least 6 to comply with standards -#endif +/*****************************************************************************/ -#define EXP_TABLE_MAX (1U<<(EXP_TABLE_SIZE-1)) +static const char fmt[] = "inf\0INF\0nan\0NAN\0.\0,"; -/* - * Only bother checking if this is too small. - */ +#define INF_OFFSET 0 /* must be 1st */ +#define NAN_OFFSET 8 /* must be 2nd.. see hex sign handling */ +#define DECPT_OFFSET 16 +#define THOUSEP_OFFSET 18 -#if LDBL_MAX_10_EXP/2 > EXP_TABLE_MAX -#error larger EXP_TABLE_SIZE needed -#endif +#define EMPTY_STRING_OFFSET 3 -/* - * With 32 bit ints, we can get 9 digits per block. - */ -#define DIGITS_PER_BLOCK 9 +/*****************************************************************************/ +#if FPMAX_MAX_10_EXP < -FPMAX_MIN_10_EXP +#error scaling code can not handle FPMAX_MAX_10_EXP < -FPMAX_MIN_10_EXP +#endif -#if INT_MAX >= 2147483647L -#define DIGIT_BLOCK_TYPE int -#define DB_FMT "%.*d" -#elif LONG_MAX >= 2147483647L -#define DIGIT_BLOCK_TYPE long -#define DB_FMT "%.*ld" -#else -#warning need at least 32 bit longs +static const __fpmax_t exp10_table[] = +{ + 1e1L, 1e2L, 1e4L, 1e8L, 1e16L, 1e32L, /* floats */ +#if FPMAX_MAX_10_EXP < 32 +#error unsupported FPMAX_MAX_10_EXP (< 32). ANSI/ISO C requires >= 37. +#endif +#if FPMAX_MAX_10_EXP >= 64 + 1e64L, #endif +#if FPMAX_MAX_10_EXP >= 128 + 1e128L, +#endif +#if FPMAX_MAX_10_EXP >= 256 + 1e256L, +#endif +#if FPMAX_MAX_10_EXP >= 512 + 1e512L, +#endif +#if FPMAX_MAX_10_EXP >= 1024 + 1e1024L, +#endif +#if FPMAX_MAX_10_EXP >= 2048 + 1e2048L, +#endif +#if FPMAX_MAX_10_EXP >= 4096 + 1e4096L +#endif +#if FPMAX_MAX_10_EXP >= 8192 +#error unsupported FPMAX_MAX_10_EXP. please increase table +#endif +}; -/* Maximum number of calls to fnprintf to output double. */ -#define MAX_CALLS 8 +#define EXP10_TABLE_SIZE (sizeof(exp10_table)/sizeof(exp10_table[0])) +#define EXP10_TABLE_MAX (1U<<(EXP10_TABLE_SIZE-1)) /*****************************************************************************/ +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ -#define NUM_DIGIT_BLOCKS ((MAX_DIGITS+DIGITS_PER_BLOCK-1)/DIGITS_PER_BLOCK) +#if FLT_RADIX != 2 +#error FLT_RADIX != 2 is not currently supported +#endif -/* extra space for '-', '.', 'e+###', and nul */ -#define BUF_SIZE ( 3 + NUM_DIGIT_BLOCKS * DIGITS_PER_BLOCK ) -/*****************************************************************************/ +#if FPMAX_MAX_EXP < -FPMAX_MIN_EXP +#error scaling code can not handle FPMAX_MAX_EXP < -FPMAX_MIN_EXP +#endif -static const char *fmts[] = { - "%0*d", "%.*s", ".", "inf", "INF", "nan", "NAN", "%*s" +static const __fpmax_t exp16_table[] = { + 0x1.0p4L, 0x1.0p8L, 0x1.0p16L, 0x1.0p32L, 0x1.0p64L, +#if FPMAX_MAX_EXP >= 128 + 0x1.0p128L, +#endif +#if FPMAX_MAX_EXP >= 256 + 0x1.0p256L, +#endif +#if FPMAX_MAX_EXP >= 512 + 0x1.0p512L, +#endif +#if FPMAX_MAX_EXP >= 1024 + 0x1.0p1024L, +#endif +#if FPMAX_MAX_EXP >= 2048 + 0x1.0p2048L, +#endif +#if FPMAX_MAX_EXP >= 4096 + 0x1.0p4096L, +#endif +#if FPMAX_MAX_EXP >= 8192 + 0x1.0p8192L, +#endif +#if FPMAX_MAX_EXP >= 16384 + 0x1.0p16384L +#endif +#if FPMAX_MAX_EXP >= 32768 +#error unsupported FPMAX_MAX_EXP. please increase table +#endif }; -/*****************************************************************************/ -#include <locale.h> +#define EXP16_TABLE_SIZE (sizeof(exp16_table)/sizeof(exp16_table[0])) +#define EXP16_TABLE_MAX (1U<<(EXP16_TABLE_SIZE-1)) -#ifdef __UCLIBC_MJN3_ONLY__ -#warning REMINDER: implement grouping for floating point -#endif +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ +/*****************************************************************************/ -#ifndef __LOCALE_C_ONLY -#define CUR_LOCALE (__global_locale) -#endif /* __LOCALE_C_ONLY */ +#define FPO_ZERO_PAD (0x80 | '0') +#define FPO_STR_WIDTH (0x80 | ' '); +#define FPO_STR_PREC 'p' -size_t _dtostr(FILE * fp, long double x, struct printf_info *info) +size_t _fpmaxtostr(FILE * fp, __fpmax_t x, struct printf_info *info, + __fp_outfunc_t fp_outfunc) { - long double exp_table[EXP_TABLE_SIZE]; - long double p10; - DIGIT_BLOCK_TYPE digit_block; /* int of at least 32 bits */ +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + __fpmax_t lower_bnd; + __fpmax_t upper_bnd = 1e9; +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + uint_fast32_t digit_block; +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + uint_fast32_t base = 10; + const __fpmax_t *power_table; + int dpb = DIGITS_PER_BLOCK; + int ndb = NUM_DIGIT_BLOCKS; + int nd = DECIMAL_DIG; + int sufficient_precision = 0; +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + int num_groups = 0; + int initial_group; /* This does not need to be initialized. */ + int tslen; /* This does not need to be initialized. */ + int nblk2; /* This does not need to be initialized. */ + const char *ts; /* This does not need to be initialized. */ +#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ int i, j; int round, o_exp; int exp, exp_neg; int width, preci; + int cnt; char *s; char *e; + intptr_t pc_fwi[3*MAX_CALLS]; + intptr_t *ppc; + intptr_t *ppc_last; +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: The size of exp_buf[] should really be determined by the float constants. +#endif /* __UCLIBC_MJN3_ONLY__ */ + char exp_buf[16]; char buf[BUF_SIZE]; - INT_OR_PTR pc_fwi[2*MAX_CALLS]; - INT_OR_PTR *ppc; - char exp_buf[8]; - char drvr[8]; - char *pdrvr; - int npc; - int cnt; - char sign_str[2]; + char sign_str[6]; /* Last 2 are for 1st digit + nul. */ char o_mode; char mode; - /* check that INT_OR_PTR is sufficiently large */ - assert( sizeof(INT_OR_PTR) == sizeof(char *) ); width = info->width; preci = info->prec; mode = info->spec; - if (mode == 'a') { - mode = 'g'; /* TODO -- fix */ - } - if (mode == 'A') { - mode = 'G'; /* TODO -- fix */ + + *exp_buf = 'e'; + if ((mode|0x20) == 'a') { +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + *exp_buf = 'p'; + if (preci < 0) { + preci = NUM_HEX_DIGITS; + sufficient_precision = 1; + } +#else + mode += ('g' - 'a'); +#endif } if (preci < 0) { @@ -1733,134 +1917,196 @@ size_t _dtostr(FILE * fp, long double x, struct printf_info *info) } else if (PRINT_INFO_FLAG_VAL(info,space)) { *sign_str = ' '; } -/* *sign_str = flag[FLAG_PLUS]; */ + *(sign_str+1) = 0; - if (isnan(x)) { /* nan check */ - pdrvr = drvr + 1; - *pdrvr++ = 5 + (mode < 'a'); - pc_fwi[2] = 3; - info->pad = ' '; -/* flag[FLAG_0_PAD] = 0; */ - goto EXIT_SPECIAL; + pc_fwi[5] = INF_OFFSET; + if (isnan(x)) { /* First, check for nan. */ + pc_fwi[5] = NAN_OFFSET; + goto INF_NAN; } - if (x == 0) { /* handle 0 now to avoid false positive */ + if (x == 0) { /* Handle 0 now to avoid false positive. */ +#if 1 + if (zeroisnegative(x)) { /* Handle 'signed' zero. */ + *sign_str = '-'; + } +#endif exp = -1; goto GENERATE_DIGITS; } - if (x < 0) { /* convert negatives to positives */ + if (x < 0) { /* Convert negatives to positives. */ *sign_str = '-'; x = -x; } - if (_zero_or_inf_check(x)) { /* must be inf since zero handled above */ - pdrvr = drvr + 1; - *pdrvr++ = 3 + + (mode < 'a'); - pc_fwi[2] = 3; + if (__FPMAX_ZERO_OR_INF_CHECK(x)) { /* Inf since zero handled above. */ + INF_NAN: info->pad = ' '; -/* flag[FLAG_0_PAD] = 0; */ + ppc = pc_fwi + 6; + pc_fwi[3] = FPO_STR_PREC; + pc_fwi[4] = 3; + if (mode < 'a') { + pc_fwi[5] += 4; + } + pc_fwi[5] = (intptr_t)(fmt + pc_fwi[5]); goto EXIT_SPECIAL; } - /* need to build the scaling table */ - for (i = 0, p10 = 10 ; i < EXP_TABLE_SIZE ; i++) { - exp_table[i] = p10; - p10 *= p10; +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Clean up defines when hexadecimal float notation is unsupported. +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + + if ((mode|0x20) == 'a') { + lower_bnd = 0x1.0p31L; + upper_bnd = 0x1.0p32L; + power_table = exp16_table; + exp = HEX_DIGITS_PER_BLOCK - 1; + i = EXP16_TABLE_SIZE; + j = EXP16_TABLE_MAX; + dpb = HEX_DIGITS_PER_BLOCK; + ndb = NUM_HEX_DIGIT_BLOCKS; + nd = NUM_HEX_DIGITS; + base = 16; + } else { + lower_bnd = 1e8; +/* upper_bnd = 1e9; */ + power_table = exp10_table; + exp = DIGITS_PER_BLOCK - 1; + i = EXP10_TABLE_SIZE; + j = EXP10_TABLE_MAX; +/* dpb = DIGITS_PER_BLOCK; */ +/* ndb = NUM_DIGIT_BLOCKS; */ +/* base = 10; */ } + + +#else /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + +#define lower_bnd 1e8 +#define upper_bnd 1e9 +#define power_table exp10_table +#define dpb DIGITS_PER_BLOCK +#define base 10 +#define ndb NUM_DIGIT_BLOCKS +#define nd DECIMAL_DIG + + exp = DIGITS_PER_BLOCK - 1; + i = EXP10_TABLE_SIZE; + j = EXP10_TABLE_MAX; + +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + exp_neg = 0; - if (x < 1e8) { /* do we need to scale up or down? */ + if (x < lower_bnd) { /* Do we need to scale up or down? */ exp_neg = 1; } - exp = DIGITS_PER_BLOCK - 1; - - i = EXP_TABLE_SIZE; - j = EXP_TABLE_MAX; - while ( i-- ) { /* scale x such that 1e8 <= x < 1e9 */ + do { + --i; if (exp_neg) { - if (x * exp_table[i] < 1e9) { - x *= exp_table[i]; + if (x * power_table[i] < upper_bnd) { + x *= power_table[i]; exp -= j; } } else { - if (x / exp_table[i] >= 1e8) { - x /= exp_table[i]; + if (x / power_table[i] >= lower_bnd) { + x /= power_table[i]; exp += j; } } j >>= 1; - } - if (x >= 1e9) { /* handle bad rounding case */ - x /= 10; + } while (i); + if (x >= upper_bnd) { /* Handle bad rounding case. */ + x /= power_table[0]; ++exp; } - assert(x < 1e9); + assert(x < upper_bnd); GENERATE_DIGITS: - s = buf + 2; /* leave space for '\0' and '0' */ -#if 1 -#define ONE_E_NINE 1000000000L -#else -#define ONE_E_NINE 1e9 -#endif - for (i = 0 ; i < NUM_DIGIT_BLOCKS ; ++i ) { - digit_block = (DIGIT_BLOCK_TYPE) x; - x = (x - digit_block) * ONE_E_NINE; - s += sprintf(s, DB_FMT, DIGITS_PER_BLOCK, digit_block); - } + s = buf + 2; /* Leave space for '\0' and '0'. */ + i = 0; + do { + digit_block = (uint_fast32_t) x; + assert(digit_block < upper_bnd); +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Can rounding be a problem? +#endif /* __UCLIBC_MJN3_ONLY__ */ + x = (x - digit_block) * upper_bnd; + s += dpb; + j = 0; + do { + s[- ++j] = '0' + (digit_block % base); + digit_block /= base; + } while (j < dpb); + } while (++i < ndb); /*************************************************************************/ - *exp_buf = 'e'; if (mode < 'a') { - *exp_buf = 'E'; + *exp_buf -= ('a' - 'A'); /* e->E and p->P */ mode += ('a' - 'A'); } o_mode = mode; - - round = preci; - - if ((mode == 'g') && (round > 0)){ - --round; + if ((mode == 'g') && (preci > 0)){ + --preci; } + round = preci; if (mode == 'f') { round += exp; if (round < -1) { - memset(buf, '0', MAX_DIGITS); + memset(buf, '0', DECIMAL_DIG); /* OK, since 'f' -> decimal case. */ exp = -1; round = -1; } } s = buf; - *s++ = 0; /* terminator for rounding and 0-triming */ - *s = '0'; /* space to round */ + *s++ = 0; /* Terminator for rounding and 0-triming. */ + *s = '0'; /* Space to round. */ i = 0; - e = s + MAX_DIGITS + 1; - if (round < MAX_DIGITS) { + e = s + nd + 1; + if (round < nd) { e = s + round + 2; - if (*e >= '5') { + if (*e >= '0' + (base/2)) { /* NOTE: We always round away from 0! */ i = 1; } } - do { /* handle rounding and trim trailing 0s */ - *--e += i; /* add the carry */ - } while ((*e == '0') || (*e > '9')); + do { /* Handle rounding and trim trailing 0s. */ + *--e += i; /* Add the carry. */ + } while ((*e == '0') || (*e > '0' - 1 + base)); + +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + if ((mode|0x20) == 'a') { + char *q; + + for (q = e ; *q ; --q) { + if (*q > '9') { + *q += (*exp_buf - ('p' - 'a') - '9' - 1); + } + } + + if (e > s) { + exp *= 4; /* Change from base 16 to base 2. */ + } + } +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ o_exp = exp; - if (e <= s) { /* we carried into extra digit */ + if (e <= s) { /* We carried into an extra digit. */ ++o_exp; - e = s; /* needed if all 0s */ + e = s; /* Needed if all 0s. */ } else { ++s; } - *++e = 0; /* ending nul char */ + *++e = 0; /* Terminating nul char. */ if ((mode == 'g') && ((o_exp >= -4) && (o_exp <= round))) { mode = 'f'; @@ -1871,34 +2117,73 @@ size_t _dtostr(FILE * fp, long double x, struct printf_info *info) o_exp = 0; } - if (o_exp < 0) { - *--s = '0'; /* fake the first digit */ + if (o_exp < 0) { /* Exponent is < 0, so */ + *--s = '0'; /* fake the first 0 digit. */ } - pdrvr = drvr+1; - ppc = pc_fwi+2; - - *pdrvr++ = 0; - *ppc++ = 1; - *ppc++ = (INT_OR_PTR)(*s++ - '0'); + pc_fwi[3] = FPO_ZERO_PAD; + pc_fwi[4] = 1; + pc_fwi[5] = (intptr_t)(sign_str + 4); + sign_str[4] = *s++; + sign_str[5] = 0; + ppc = pc_fwi + 6; - i = e - s; /* total digits */ + i = e - s; /* Total digits is 'i'. */ if (o_exp >= 0) { +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + + const char *p; + + if (PRINT_INFO_FLAG_VAL(info,group) + && *(p = __UCLIBC_CURLOCALE_DATA.grouping) + ) { + int nblk1; + + nblk2 = nblk1 = *p; + if (*++p) { + nblk2 = *p; + assert(!*++p); + } + + if (o_exp >= nblk1) { + num_groups = (o_exp - nblk1) / nblk2 + 1; + initial_group = (o_exp - nblk1) % nblk2; + +#ifdef __UCLIBC_HAS_WCHAR__ + if (PRINT_INFO_FLAG_VAL(info,wide)) { + /* _fp_out_wide() will fix this up. */ + ts = fmt + THOUSEP_OFFSET; + tslen = 1; + } else { +#endif /* __UCLIBC_HAS_WCHAR__ */ + ts = __UCLIBC_CURLOCALE_DATA.thousands_sep; + tslen = __UCLIBC_CURLOCALE_DATA.thousands_sep_len; +#ifdef __UCLIBC_HAS_WCHAR__ + } +#endif /* __UCLIBC_HAS_WCHAR__ */ + + width -= num_groups * tslen; + } + } + + +#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ + ppc[0] = FPO_STR_PREC; + ppc[2] = (intptr_t)(s); if (o_exp >= i) { /* all digit(s) left of decimal */ - *pdrvr++ = 1; - *ppc++ = i; - *ppc++ = (INT_OR_PTR)(s); + ppc[1] = i; + ppc += 3; o_exp -= i; i = 0; if (o_exp>0) { /* have 0s left of decimal */ - *pdrvr++ = 0; - *ppc++ = o_exp; - *ppc++ = 0; + ppc[0] = FPO_ZERO_PAD; + ppc[1] = o_exp; + ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); + ppc += 3; } } else if (o_exp > 0) { /* decimal between digits */ - *pdrvr++ = 1; - *ppc++ = o_exp; - *ppc++ = (INT_OR_PTR)(s); + ppc[1] = o_exp; + ppc += 3; s += o_exp; i -= o_exp; } @@ -1906,105 +2191,221 @@ size_t _dtostr(FILE * fp, long double x, struct printf_info *info) } if (PRINT_INFO_FLAG_VAL(info,alt) -/* flag[FLAG_HASH] */ - || (i) || ((o_mode != 'g') && (preci > 0))) { + || (i) + || ((o_mode != 'g') +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + && (o_mode != 'a') +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + && (preci > 0)) + ) { + ppc[0] = FPO_STR_PREC; #ifdef __LOCALE_C_ONLY - *pdrvr++ = 2; /* need decimal */ - *ppc++ = 1; /* needed for width calc */ - ppc++; + ppc[1] = 1; + ppc[2] = (intptr_t)(fmt + DECPT_OFFSET); #else /* __LOCALE_C_ONLY */ - *pdrvr++ = 1; - *ppc++ = strlen(CUR_LOCALE.decimal_point); - *ppc++ = (INT_OR_PTR)(CUR_LOCALE.decimal_point); +#ifdef __UCLIBC_HAS_WCHAR__ + if (PRINT_INFO_FLAG_VAL(info,wide)) { + /* _fp_out_wide() will fix this up. */ + ppc[1] = 1; + ppc[2] = (intptr_t)(fmt + DECPT_OFFSET); + } else { +#endif /* __UCLIBC_HAS_WCHAR__ */ + ppc[1] = __UCLIBC_CURLOCALE_DATA.decimal_point_len; + ppc[2] = (intptr_t)(__UCLIBC_CURLOCALE_DATA.decimal_point); +#ifdef __UCLIBC_HAS_WCHAR__ + } +#endif /* __UCLIBC_HAS_WCHAR__ */ #endif /* __LOCALE_C_ONLY */ + ppc += 3; } - if (++o_exp < 0) { /* have 0s right of decimal */ - *pdrvr++ = 0; - *ppc++ = -o_exp; - *ppc++ = 0; + if (++o_exp < 0) { /* Have 0s right of decimal. */ + ppc[0] = FPO_ZERO_PAD; + ppc[1] = -o_exp; + ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); + ppc += 3; } - if (i) { /* have digit(s) right of decimal */ - *pdrvr++ = 1; - *ppc++ = i; - *ppc++ = (INT_OR_PTR)(s); + if (i) { /* Have digit(s) right of decimal. */ + ppc[0] = FPO_STR_PREC; + ppc[1] = i; + ppc[2] = (intptr_t)(s); + ppc += 3; } - if (o_mode != 'g') { + if (((o_mode != 'g') || PRINT_INFO_FLAG_VAL(info,alt)) +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + && !sufficient_precision +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + ) { i -= o_exp; - if (i < preci) { /* have 0s right of digits */ + if (i < preci) { /* Have 0s right of digits. */ i = preci - i; - *pdrvr++ = 0; - *ppc++ = i; - *ppc++ = 0; + ppc[0] = FPO_ZERO_PAD; + ppc[1] = i; + ppc[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); + ppc += 3; } } - /* build exponent string */ + /* Build exponent string. */ if (mode != 'f') { - *pdrvr++ = 1; - *ppc++ = sprintf(exp_buf,"%c%+.2d", *exp_buf, exp); - *ppc++ = (INT_OR_PTR) exp_buf; + char *p = exp_buf + sizeof(exp_buf); + char exp_char = *exp_buf; + char exp_sign = '+'; +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + int min_exp_dig_plus_2 = ((o_mode != 'a') ? (2+2) : (2+1)); +#else /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ +#define min_exp_dig_plus_2 (2+2) +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + + if (exp < 0) { + exp_sign = '-'; + exp = -exp; + } + + *--p = 0; /* nul-terminate */ + j = 2; /* Count exp_char and exp_sign. */ + do { + *--p = '0' + (exp % 10); + exp /= 10; + } while ((++j < min_exp_dig_plus_2) || exp); /* char+sign+mindigits */ + *--p = exp_sign; + *--p = exp_char; + + ppc[0] = FPO_STR_PREC; + ppc[1] = j; + ppc[2] = (intptr_t)(p); + ppc += 3; } EXIT_SPECIAL: - npc = pdrvr - drvr; - ppc = pc_fwi + 2; - for (i=1 ; i< npc ; i++) { - width -= *(ppc++); - ppc++; - } - i = 0; - if (*sign_str) { - i = 1; - } - width -= i; - if (width <= 0) { - width = 0; - } else { - if (PRINT_INFO_FLAG_VAL(info,left)) { /* padding on right */ -/* flag[FLAG_MINUS_LJUSTIFY] */ - ++npc; - *pdrvr++ = 7; - *ppc = width; - *++ppc = (INT_OR_PTR)(""); - width = 0; + ppc_last = ppc; + ppc = pc_fwi + 4; /* Need width fields starting with second. */ + do { + width -= *ppc; + ppc += 3; + } while (ppc < ppc_last); + + ppc = pc_fwi; + ppc[0] = FPO_STR_WIDTH; + ppc[1] = i = ((*sign_str) != 0); + ppc[2] = (intptr_t) sign_str; + +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ + if (((mode|0x20) == 'a') && (pc_fwi[3] >= 16)) { /* Hex sign handling. */ + /* Hex and not inf or nan, so prefix with 0x. */ + char *h = sign_str + i; + *h = '0'; + *++h = 'x' - 'p' + *exp_buf; + *++h = 0; + ppc[1] = (i += 2); + } +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + + if ((width -= i) > 0) { + if (PRINT_INFO_FLAG_VAL(info,left)) { /* Left-justified. */ + ppc_last[0] = FPO_STR_WIDTH; + ppc_last[1] = width; + ppc_last[2] = (intptr_t)(fmt + EMPTY_STRING_OFFSET); + ppc_last += 3; } else if (info->pad == '0') { /* 0 padding */ -/* (flag[FLAG_0_PAD] == '0') */ - pc_fwi[2] += width; - width = 0; + ppc[4] += width; /* Pad second field. */ + } else { + ppc[1] += width; /* Pad first (sign) field. */ } } - *drvr = 7; - ppc = pc_fwi; - *ppc++ = width + i; - *ppc = (INT_OR_PTR) sign_str; - pdrvr = drvr; - ppc = pc_fwi; cnt = 0; - for (i=0 ; i<npc ; i++) { -#if 1 - fprintf(fp, fmts[(int)(*pdrvr++)], (INT_OR_PTR)(*(ppc)), - (INT_OR_PTR)(*(ppc+1))); -#else - j = fprintf(fp, fmts[(int)(*pdrvr++)], (INT_OR_PTR)(*(ppc)), - (INT_OR_PTR)(*(ppc+1))); - assert(j == *ppc); -#endif -/* if (size > *ppc) { */ -/* size -= *ppc; */ -/* } */ - cnt += *ppc; /* to avoid problems if j == -1 */ - ppc += 2; - } + + do { +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ + + if ((ppc == pc_fwi + 6) && num_groups) { + const char *gp = (const char *) ppc[2]; + int len = ppc[1]; + int blk = initial_group; + + cnt += num_groups * tslen; /* Adjust count now for sep chars. */ + +/* printf("\n"); */ + do { + if (!blk) { /* Initial group could be 0 digits long! */ + blk = nblk2; + } else if (len >= blk) { /* Enough digits for a group. */ +/* printf("norm: len=%d blk=%d \"%.*s\"\n", len, blk, blk, gp); */ + fp_outfunc(fp, *ppc, blk, (intptr_t) gp); + assert(gp); + if (*gp) { + gp += blk; + } + len -= blk; + } else { /* Transition to 0s. */ +/* printf("trans: len=%d blk=%d \"%.*s\"\n", len, blk, len, gp); */ + if (len) { +/* printf("len\n"); */ + fp_outfunc(fp, *ppc, len, (intptr_t) gp); + gp += len; + } + + if (ppc[3] == FPO_ZERO_PAD) { /* Need to group 0s */ +/* printf("zeropad\n"); */ + cnt += ppc[1]; + ppc += 3; + gp = (const char *) ppc[2]; + blk -= len; /* blk > len, so blk still > 0. */ + len = ppc[1]; + continue; /* Don't decrement num_groups here. */ + } else { + assert(num_groups == 0); + break; + } + } + + if (num_groups <= 0) { + break; + } + --num_groups; + + fp_outfunc(fp, FPO_STR_PREC, tslen, (intptr_t) ts); + blk = nblk2; + +/* printf("num_groups=%d blk=%d\n", num_groups, blk); */ + + } while (1); + } else + +#endif /* __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ */ + + fp_outfunc(fp, *ppc, ppc[1], ppc[2]); /* NOTE: Remember 'else' above! */ + + cnt += ppc[1]; + ppc += 3; + } while (ppc < ppc_last); return cnt; } + #endif /**********************************************************************/ #ifdef L__store_inttype -/* TODO -- right now, assumes intmax_t is either long or long long */ + +/* Right now, we assume intmax_t is either long or long long */ + +#ifdef INTMAX_MAX + +#ifdef LLONG_MAX + +#if INTMAX_MAX > LLONG_MAX +#error INTMAX_MAX > LLONG_MAX! The printf code needs to be updated! +#endif + +#elif INTMAX_MAX > LONG_MAX + +#error No LLONG_MAX and INTMAX_MAX > LONG_MAX! The printf code needs to be updated! + +#endif /* LLONG_MAX */ + +#endif /* INTMAX_MAX */ /* We assume int may be short or long, but short and long are different. */ @@ -2107,6 +2508,7 @@ extern uintmax_t _load_inttype(int desttype, register const void *src, * In other words, we don't currently support glibc's 'I' flag. * We do accept it, but it is currently ignored. */ +static void _charpad(FILE * __restrict stream, int padchar, size_t numpad); #ifdef L_vfprintf @@ -2117,6 +2519,23 @@ extern uintmax_t _load_inttype(int desttype, register const void *src, #define _PPFS_init _ppfs_init #define OUTPUT(F,S) fputs(S,F) #define _outnstr(stream, string, len) _stdio_fwrite(string, len, stream) +#define FP_OUT _fp_out_narrow + +#ifdef __STDIO_PRINTF_FLOAT + +static void _fp_out_narrow(FILE *fp, intptr_t type, intptr_t len, intptr_t buf) +{ + if (type & 0x80) { /* Some type of padding needed. */ + int buflen = strlen((const char *) buf); + if ((len -= buflen) > 0) { + _charpad(fp, (type & 0x7f), len); + } + len = buflen; + } + OUTNSTR(fp, (const char *) buf, len); +} + +#endif /* __STDIO_PRINTF_FLOAT */ #else /* L_vfprintf */ @@ -2127,6 +2546,7 @@ extern uintmax_t _load_inttype(int desttype, register const void *src, #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 static void _outnstr(FILE *stream, const char *s, size_t wclen) { @@ -2139,13 +2559,69 @@ static void _outnstr(FILE *stream, const char *s, size_t wclen) todo = wclen; while (todo) { - r = mbsrtowcs(wbuf, &s, sizeof(wbuf)/sizeof(wbuf[0]), &mbstate); + r = mbsrtowcs(wbuf, &s, + ((todo <= sizeof(wbuf)/sizeof(wbuf[0])) + ? todo + : sizeof(wbuf)/sizeof(wbuf[0])), + &mbstate); assert(((ssize_t)r) > 0); _outnwcs(stream, wbuf, r); todo -= r; } } +#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 void _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; + int i; + + if (type & 0x80) { /* Some type of padding needed */ + int buflen = strlen(s); + if ((len -= buflen) > 0) { + _charpad(fp, (type & 0x7f), len); + } + 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); + + OUTNSTR(fp, wbuf, len); + } +} + +#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."; @@ -2153,7 +2629,9 @@ static int _ppwfs_init(register ppfs_t *ppfs, const wchar_t *fmt0) /* 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; @@ -2210,6 +2688,7 @@ static int _ppwfs_init(register ppfs_t *ppfs, const wchar_t *fmt0) 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; @@ -2221,6 +2700,7 @@ static int _ppwfs_init(register ppfs_t *ppfs, const wchar_t *fmt0) ++p; } } +#endif /* NL_ARGMAX */ return 0; } @@ -2283,8 +2763,8 @@ static int _do_one_spec(FILE * __restrict stream, int prefix_num = PREFIX_NONE; char padchar = ' '; #ifdef __UCLIBC_MJN3_ONLY__ -#warning REMINDER: buf size -#endif +#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 @@ -2304,24 +2784,28 @@ static int _do_one_spec(FILE * __restrict stream, /* 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; /* TODO: Should s be unsigned char * ? */ + register char *s = NULL; /* TODO: Should s be unsigned char * ? */ if (ppfs->conv_num == CONV_n) { _store_inttype(*(void **)*argptr, @@ -2331,7 +2815,12 @@ static int _do_one_spec(FILE * __restrict stream, } if (ppfs->conv_num <= CONV_i) { /* pointer or (un)signed int */ alphacase = __UIM_LOWER; -#ifndef __LOCALE_C_ONLY + +#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; @@ -2340,7 +2829,7 @@ static int _do_one_spec(FILE * __restrict stream, alphacase |= 0x80; } } -#endif /* __LOCALE_C_ONLY */ + if (ppfs->conv_num <= CONV_u) { /* pointer or unsigned int */ if (ppfs->conv_num == CONV_X) { alphacase = __UIM_UPPER; @@ -2356,8 +2845,10 @@ static int _do_one_spec(FILE * __restrict stream, padchar = ppfs->info.pad; } #ifdef __UCLIBC_MJN3_ONLY__ -#warning if using outdigits and/or grouping, how should we interpret precision? +#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, @@ -2413,23 +2904,18 @@ static int _do_one_spec(FILE * __restrict stream, } numfill = ((numfill > SLEN) ? numfill - SLEN : 0); } else if (ppfs->conv_num <= CONV_A) { /* floating point */ -#ifdef L_vfwprintf -#ifdef __UCLIBC_MJN3_ONLY__ -#warning fix dtostr -#endif - return -1; -#else /* L_vfwprintf */ #ifdef __STDIO_PRINTF_FLOAT - *count += _dtostr(stream, - (PRINT_INFO_FLAG_VAL(&(ppfs->info),is_long_double) - ? *(long double *) *argptr - : (long double) (* (double *) *argptr)), - &ppfs->info); + *count += + _fpmaxtostr(stream, + (__fpmax_t) + (PRINT_INFO_FLAG_VAL(&(ppfs->info),is_long_double) + ? *(long double *) *argptr + : (long double) (* (double *) *argptr)), + &ppfs->info, FP_OUT ); return 0; #else /* __STDIO_PRINTF_FLOAT */ return -1; /* TODO -- try to continue? */ #endif /* __STDIO_PRINTF_FLOAT */ -#endif /* L_vfwprintf */ } else if (ppfs->conv_num <= CONV_S) { /* wide char or string */ #ifdef L_vfprintf @@ -2467,7 +2953,9 @@ static int _do_one_spec(FILE * __restrict stream, if (ppfs->conv_num == CONV_s) { /* string */ s = *((char **) (*argptr)); if (s) { +#ifdef __STDIO_PRINTF_M_SUPPORT SET_STRING_LEN: +#endif slen = strnlen(s, ((ppfs->info.prec >= 0) ? ppfs->info.prec : SIZE_MAX)); } else { @@ -2505,11 +2993,13 @@ static int _do_one_spec(FILE * __restrict stream, if (ppfs->conv_num == CONV_s) { /* string */ #ifdef __UCLIBC_MJN3_ONLY__ -#warning Fix %s for vfwprintf... output upto illegal sequence? -#endif +#warning TODO: Fix %s for vfwprintf... output upto illegal sequence? +#endif /* __UCLIBC_MJN3_ONLY__ */ s = *((char **) (*argptr)); if (s) { +#ifdef __STDIO_PRINTF_M_SUPPORT 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 @@ -2544,6 +3034,7 @@ static int _do_one_spec(FILE * __restrict stream, goto SET_STRING_LEN; #endif } else { +#ifdef __STDIO_GLIBC_CUSTOM_PRINTF assert(ppfs->conv_num == CONV_custom0); s = _custom_printf_spec; @@ -2561,13 +3052,16 @@ static int _do_one_spec(FILE * __restrict stream, return 0; } } while (++s < (_custom_printf_spec + MAX_USER_SPEC)); +#endif /* __STDIO_GLIBC_CUSTOM_PRINTF */ assert(0); return -1; } #ifdef __UCLIBC_MJN3_ONLY__ -#warning if using outdigits and/or grouping, how should we pad? +#ifdef L_vfprintf +#warning CONSIDER: If using outdigits and/or grouping, how should we pad? #endif +#endif /* __UCLIBC_MJN3_ONLY__ */ { size_t t; @@ -2595,6 +3089,7 @@ static int _do_one_spec(FILE * __restrict stream, #ifdef __UCLIBC_HAS_WCHAR__ if (!ws) { + assert(s); _outnstr(stream, s, slen); } else { /* wide string */ size_t t; @@ -2606,7 +3101,6 @@ static int _do_one_spec(FILE * __restrict stream, _outnstr(stream, buf, t); slen -= t; } - ws = NULL; /* Reset ws. */ } #else /* __UCLIBC_HAS_WCHAR__ */ _outnstr(stream, s, slen); @@ -2615,10 +3109,10 @@ static int _do_one_spec(FILE * __restrict stream, #else /* L_vfprintf */ if (!ws) { + assert(s); _outnstr(stream, s, SLEN); } else { _outnwcs(stream, ws, SLEN); - ws = NULL; /* Reset ws. */ } #endif /* L_vfprintf */ @@ -2644,6 +3138,9 @@ int VFPRINTF (FILE * __restrict stream, 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!!! */ diff --git a/libc/stdio/scanf.c b/libc/stdio/scanf.c index 9ac3d3c9c..48e9344c0 100644 --- a/libc/stdio/scanf.c +++ b/libc/stdio/scanf.c @@ -1,130 +1,178 @@ - -/* - * Modified by Manuel Novoa III Mar 13, 2001 - * - * The vfscanf routine was completely rewritten to add features and remove - * bugs. The function __strtold, based on my strtod code in stdlib, was - * added to provide floating point support for the scanf functions. - * - * So far they pass the test cases from glibc-2.1.3, except in two instances. - * In one case, the test appears to be broken. The other case is something - * I need to research further. This version of scanf assumes it can only - * peek one character ahead. Apparently, glibc looks further. The difference - * can be seen when parsing a floating point value in the character - * sequence "100ergs". glibc is able to back up before the 'e' and return - * a value of 100, whereas this scanf reports a bad match with the stream - * pointer at 'r'. A similar situation can also happen when parsing hex - * values prefixed by 0x or 0X; a failure would occur for "0xg". In order to - * fix this, I need to rework the "ungetc" machinery in stdio.c again. - * I do have one reference though, that seems to imply scanf has a single - * character of lookahead. - * - * May 20, 2001 +/* Copyright (C) 2002, 2003 Manuel Novoa III * - * Quote from ANSI/ISO C99 standard: + * 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. * - * fscanf pushes back at most one input character onto the input stream. - * Therefore, some sequences that are acceptable to strtod, strtol, etc., - * are unacceptable to fscanf. + * 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. * - * So uClibc's *scanf functions conform to the standard, and glibc's - * implementation doesn't for the "100ergs" case mentioned above. - * - * Sep 6, 2002 - * Patch from Tero_Lyytikäinen <tero@paravant.fi> to fix bug in matchchar case. - * - * May 15, 2003 - * Hopefully fix handling of 0 bytes with %s, %c, and %[ specifiers. - * - * July 17, 2003 - * Bug fix from Peter Kjellerstedt <peter.kjellerstedt@axis.com>. vfscanf was - * not setting the FILE bufread member to flag the end of the buffer. - * Also, do not set bufgetc member if getc macro support is disabled. + * 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. */ + #define _ISOC99_SOURCE /* for LLONG_MAX primarily... */ #define _GNU_SOURCE #define _STDIO_UTILITY +#include <features.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <ctype.h> #include <string.h> #include <stdarg.h> +#include <stdint.h> +#include <errno.h> +#include <printf.h> + +#ifdef __UCLIBC_HAS_WCHAR__ +#include <bits/uClibc_uwchar.h> +#include <wchar.h> +#include <wctype.h> +#endif /* __UCLIBC_HAS_WCHAR__ */ + +#include <langinfo.h> +#include <locale.h> + +#include <assert.h> +#include <limits.h> #ifdef __STDIO_THREADSAFE #include <stdio_ext.h> #include <pthread.h> #endif /* __STDIO_THREADSAFE */ -#ifdef L_scanf -#ifdef __STDC__ -int scanf(const char *fmt, ...) +#ifdef __UCLIBC_HAS_FLOATS__ +#include <float.h> +#include <bits/uClibc_fpmax.h> +#endif /* __UCLIBC_HAS_FLOATS__ */ + +#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 + +extern void _store_inttype(void *dest, int desttype, uintmax_t val); + +#ifdef LLONG_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 /* LLONG_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 /* LLONG_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 -int scanf(fmt, va_alist) -__const char *fmt; -va_dcl +#define __isdigit_char_or_EOF(C) __isdigit_int((C)) #endif + +/**********************************************************************/ +#ifdef L_fscanf + +int fscanf(FILE * __restrict stream, const char * __restrict format, ...) { - va_list ptr; + va_list arg; int rv; - va_start(ptr, fmt); - rv = vfscanf(stdin, fmt, ptr); - va_end(ptr); + va_start(arg, format); + rv = vfscanf(stream, format, arg); + va_end(arg); + return rv; } -#endif -#ifdef L_sscanf -#if !defined(__STDIO_BUFFERS) && !defined(__STDIO_GLIBC_CUSTOM_STREAMS) -#warning skipping sscanf since no buffering and no custom streams! -#else +#endif +/**********************************************************************/ +#ifdef L_scanf -int sscanf(const char *sp, const char *fmt, ...) +int scanf(const char * __restrict format, ...) { - va_list ptr; + va_list arg; int rv; - va_start(ptr, fmt); - rv = vsscanf(sp, fmt, ptr); - va_end(ptr); + va_start(arg, format); + rv = vfscanf(stdin, format, arg); + va_end(arg); + return rv; } #endif -#endif +/**********************************************************************/ +#ifdef L_sscanf -#ifdef L_fscanf -#ifdef __STDC__ -int fscanf(FILE * fp, const char *fmt, ...) -#else -int fscanf(fp, fmt, va_alist) -FILE *fp; -__const char *fmt; -va_dcl -#endif +#if defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) + +int sscanf(const char * __restrict str, const char * __restrict format, ...) { - va_list ptr; + va_list arg; int rv; - va_start(ptr, fmt); - rv = vfscanf(fp, fmt, ptr); - va_end(ptr); + va_start(arg, format); + rv = vsscanf(str, format, arg); + va_end(arg); + return rv; } -#endif +#else /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */ +#warning Skipping sscanf since no buffering and no custom streams! +#endif /* defined(__STDIO_BUFFERS) || defined(__STDIO_GLIBC_CUSTOM_STREAMS) */ + +#endif +/**********************************************************************/ #ifdef L_vscanf -int vscanf(fmt, ap) -__const char *fmt; -va_list ap; + +int vscanf(const char * __restrict format, va_list arg) { - return vfscanf(stdin, fmt, ap); + return vfscanf(stdin, format, arg); } -#endif +#endif +/**********************************************************************/ #ifdef L_vsscanf + +#ifdef __UCLIBC_MJN3_ONLY__ +#warning WISHLIST: Implement vsscanf for non-buffered and no custom stream case. +#endif /* __UCLIBC_MJN3_ONLY__ */ + #ifdef __STDIO_BUFFERS int vsscanf(__const char *sp, __const char *fmt, va_list ap) { @@ -165,598 +213,1866 @@ int vsscanf(__const char *sp, __const char *fmt, va_list ap) return rv; } #else /* __STDIO_GLIBC_CUSTOM_STREAMS */ -#warning skipping vsscanf since no buffering and no custom streams! +#warning Skipping vsscanf since no buffering and no custom streams! #endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ #endif /* __STDIO_BUFFERS */ + #endif +/**********************************************************************/ +#ifdef L_fwscanf -#ifdef L_vfscanf +int fwscanf(FILE * __restrict stream, const wchar_t * __restrict format, ...) +{ + va_list arg; + int rv; -#include <assert.h> -#include <ctype.h> -#include <limits.h> + va_start(arg, format); + rv = vfwscanf(stream, format, arg); + va_end(arg); + + return rv; +} + +#endif +/**********************************************************************/ +#ifdef L_wscanf -static int valid_digit(char c, char base) +int wscanf(const wchar_t * __restrict format, ...) { - if (base == 16) { - return isxdigit(c); - } else { - return (__isdigit(c) && (c < '0' + base)); - } + va_list arg; + int rv; + + va_start(arg, format); + rv = vfwscanf(stdin, format, arg); + va_end(arg); + + return rv; } -extern unsigned long -_stdlib_strto_l(register const char * __restrict str, - char ** __restrict endptr, int base, int sflag); -#ifdef LLONG_MAX -extern unsigned long long -_stdlib_strto_ll(register const char * __restrict str, - char ** __restrict endptr, int base, int sflag); +#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.filedes = -3; /* FAKE STREAM TO SUPPORT *wscanf! */ + f.modeflags = (__FLAG_WIDE|__FLAG_READONLY|__FLAG_READING); + f.bufpos = (char *) str; + f.bufend = (char *)(str + wcslen(str)); + f.ungot_width[0] = 0; +#ifdef __STDIO_THREADSAFE + f.user_locking = 0; + __stdio_init_mutex(&f.lock); +#endif + + + return vfwscanf(&f, format, arg); +} +#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; - int width_flag; - int ungot_char; - int ungot_flag; - int app_ungot; + +#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__ */ + }; -static const char qual[] = "hl" /* "jtz" */ "Lq"; -/* char = -2, short = -1, int = 0, long = 1, long long = 2 */ -static const char qsz[] = { -1, 1, 2, 2 }; +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); +extern int __scan_getc(register struct scan_cookie *sc); +extern void __scan_ungetc(register struct scan_cookie *sc); #ifdef __UCLIBC_HAS_FLOATS__ -static int __strtold(long double *ld, struct scan_cookie *sc); - /*01234567890123456 */ -static const char spec[] = "%n[csoupxXidfeEgG"; -#else -static const char spec[] = "%n[csoupxXid"; +extern int __scan_strtold(long double *ld, struct scan_cookie *sc); +#endif /* __UCLIBC_HAS_FLOATS__ */ + +extern int __psfs_parse_spec(psfs_t *psfs); +extern int __psfs_do_numeric(psfs_t *psfs, struct scan_cookie *sc); + +/**********************************************************************/ +#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 -/* radix[i] <-> spec[i+5] o u p x X i d */ -static const char radix[] = { 8, 10, 16, 16, 16, 0, 10 }; -static void init_scan_cookie(register struct scan_cookie *sc, - register FILE *fp) +void __init_scan_cookie(register struct scan_cookie *sc, + register FILE *fp) { sc->fp = fp; sc->nread = 0; - sc->width_flag = 0; sc->ungot_flag = 0; sc->app_ungot = ((fp->modeflags & __MASK_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__ */ -/* TODO -- what about literal '\0' chars in a file??? */ +} -static int scan_getc_nw(register struct scan_cookie *sc) +int __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) { - sc->ungot_char = getc(sc->fp); + if ((c = GETC(sc)) == EOF) { + sc->ungot_flag |= 2; + return -1; + } + sc->ungot_char = c; } else { + assert(sc->ungot_flag == 1); sc->ungot_flag = 0; } - if (sc->ungot_char > 0) { - ++sc->nread; + + ++sc->nread; + return sc->cc = sc->ungot_char; +} + +void __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); } - sc->width_flag = 0; - return sc->ungot_char; } -static int scan_getc(register struct scan_cookie *sc) +#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 __psfs_parse_spec(register psfs_t *psfs) { - if (sc->ungot_flag == 0) { - sc->ungot_char = getc(sc->fp); + 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; } - sc->width_flag = 1; +#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(sc->fp); +} + +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 = 1; + sc->ungot_flag |= 2; return -1; } - sc->ungot_flag = 0; - if (sc->ungot_char > 0) { - ++sc->nread; + + width = sc->width; /* Preserve width. */ + sc->width = INT_MAX; /* MB_CUR_MAX can invoke a function. */ + + r = (size_t)(-1); + 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; } - return sc->ungot_char; + + /* 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); } -static void scan_ungetc(register struct scan_cookie *sc) +#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) { - if (sc->ungot_flag != 0) { - assert(sc->width < 0); - return; + wint_t wc; + + if (sc->fp->filedes == -3) { + 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; } - if (sc->width_flag) { - ++sc->width; + + 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->ungot_flag = 1; - if (sc->ungot_char > 0) { /* not EOF or EOS */ - --sc->nread; + 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 (sc->fp->filedes == -3) { + 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; } -static void kill_scan_cookie(register struct scan_cookie *sc) + +#endif /* L_vfwscanf */ +#endif /* __UCLIBC_HAS_WCHAR__ */ + +static __inline void kill_scan_cookie(register struct scan_cookie *sc) { - if (sc->ungot_flag) { - ungetc(sc->ungot_char,sc->fp); +#ifdef L_vfscanf + + if (sc->ungot_flag & 1) { + ungetc(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; + } + } + +#else + + if ((sc->ungot_wflag & 1) && (sc->fp->filedes != -3) && (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 } -int vfscanf(FILE *fp, const char *format, va_list ap) -{ -#define STRTO_L_(s,e,b,sf) _stdlib_strto_ll(s,e,b,sf) -#define MAX_DIGITS 64 -#define UV_TYPE unsigned long long -#define V_TYPE long long +#ifdef L_vfwscanf #ifdef __UCLIBC_HAS_FLOATS__ - long double ld; +static const char fake_decpt_str[] = "."; +#endif +#ifdef __UCLIBC_HAS_GLIBC_DIGIT_GROUPING__ +static const char fake_thousands_sep_str[] = ","; #endif - UV_TYPE uv; +#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 */ + +#ifdef __UCLIBC_HAS_WCHAR__ + mbstate_t mbstate; +#endif /* __UCLIBC_HAS_WCHAR__ */ + struct scan_cookie sc; - register unsigned const char *fmt; - const char *p; - register unsigned char *b; - void *vp; - int cc, i, cnt; - signed char lval; - unsigned char store, usflag, base, invert, r0, r1; + psfs_t psfs; + + int i; + +#warning fix MAX_DIGITS. we do not do binary, so...! +#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; + +#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) { + mbstate.mask = 0; /* Initialize the mbstate. */ + const char *p = format; + 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_THREADLOCK(fp); - init_scan_cookie(&sc,fp); + __init_scan_cookie(&sc,fp); +#ifdef __UCLIBC_HAS_WCHAR__ + sc.sc_getc = sc_getc; + sc.ungot_wchar_width = sc.fp->ungot_width[1]; - fmt = (unsigned const char *) format; - cnt = 0; +#ifdef L_vfwscanf - while (*fmt) { - store = 1; - lval = 0; - sc.width = INT_MAX; - if (*fmt == '%') { /* Conversion specification. */ - ++fmt; - if (*fmt == '*') { /* Suppress assignment. */ - store = 0; +#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. */ + psfs.conv_num = CONV_percent; + goto DO_CONVERSION; } - for (i = 0 ; __isdigit(*fmt) ; sc.width = i) { - i = (i * 10) + (*fmt++ - '0'); /* Get specified width. */ + + +#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; + } } - for (i = 0 ; i < sizeof(qual) ; i++) { /* Optional qualifier. */ - if (qual[i] == *fmt) { - ++fmt; - lval += qsz[i]; - if ((i < 2) && (qual[i] == *fmt)) { /* Double h or l. */ - ++fmt; - lval += qsz[i]; +#endif /* L_vfscanf */ + if ((i = __psfs_parse_spec(&psfs)) < 0) { /* Bad conversion specifier! */ + goto DONE; + } + fmt += i; + +#if defined(NL_ARGMAX) && (NL_ARGMAX > 0) + if (psfs.store) { + 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 *); } - break; + 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) { + if (psfs.store) { + _store_inttype(psfs.cur_ptr, psfs.dataargtype, + (uintmax_t) sc.nread); } + goto NEXT_FMT; } - for (p = spec ; *p ; p++) { /* Process format specifier. */ - if (*fmt != *p) continue; - if (p-spec < 1) { /* % - match a '%'*/ - goto matchchar; + + if (psfs.conv_num <= CONV_A) { /* pointer, integer, or float spec */ +#ifdef L_vfscanf + if (__psfs_do_numeric(&psfs, &sc) < 0) { /* Num conv failed! */ + goto DONE; } - if (p-spec < 2) { /* n - store number of chars read */ - *(va_arg(ap, int *)) = sc.nread; - scan_getc_nw(&sc); - goto nextfmt; + goto NEXT_FMT; +#else + int r = __psfs_do_numeric(&psfs, &sc); + if (sc.ungot_wflag == 1) { /* fix up '?', '.', and ',' hacks */ + sc.cc = sc.ungot_char = sc.ungot_wchar; + } + if (r < 0) { + goto DONE; } - if (p-spec > 3) { /* skip white space if not c or [ */ - do { - i = scan_getc_nw(&sc); - } while (__isspace(i)); - scan_ungetc(&sc); + goto NEXT_FMT; +#endif + } + + /* 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) { + *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 (p-spec < 5) { /* [,c,s - string conversions */ + + 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)) { + *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 (*p == 'c') { + + if (*++fmt == '^') { + ++fmt; invert = 1; - if (sc.width == INT_MAX) { - sc.width = 1; - } - } - for (i=0 ; i<= UCHAR_MAX ; i++) { - scanset[i] = ((*p == 's') ? (__isspace(i) == 0) : 0); + } + memset(scanset, invert, sizeof(scanset)); + invert = 1-invert; + + if (*fmt == ']') { + scanset[(int)(']')] = invert; + ++fmt; } - if (*p == '[') { /* need to build a scanset */ - if (*++fmt == '^') { - invert = 1; - ++fmt; - } - if (*fmt == ']') { - scanset[(int)']'] = 1; - ++fmt; + + while (*fmt != ']') { + if (!*fmt) { /* No closing ']'. */ + goto DONE; } - r0 = 0; - while (*fmt && *fmt !=']') { /* build scanset */ - if ((*fmt == '-') && r0 && (fmt[1] != ']')) { - /* range */ - ++fmt; - if (*fmt < r0) { - r1 = r0; - r0 = *fmt; - } else { - r1 = *fmt; - } - for (i=r0 ; i<= r1 ; i++) { - scanset[i] = 1; - } - r0 = 0; - } else { - r0 = *fmt; - scanset[r0] = 1; - } + 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. */ } - if (!*fmt) { /* format string exhausted! */ - goto done; - } + /* literal char */ + scanset[(int) *fmt] = invert; + ++fmt; } - /* ok -- back to common work */ - if (sc.width <= 0) { - goto done; + +#ifdef __UCLIBC_HAS_WCHAR__ + if (psfs.conv_num == CONV_LEFTBRACKET) { + goto DO_LEFTBRACKET; } - if (store) { - b = va_arg(ap, unsigned char *); - } else { - b = buf; +#endif /* __UCLIBC_HAS_WCHAR__ */ + + + while (__scan_getc(&sc) >= 0) { + if (!scanset[sc.cc]) { + break; + } + *b = sc.cc; + b += psfs.store; + fail = 0; } - cc = scan_getc(&sc); - if (cc < 0) { - scan_ungetc(&sc); - goto done; /* return EOF if cnt == 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; } - if (*p == 'c') { - goto c_spec; + + while (scan_getwc(&sc) >= 0) { + assert(sc.width >= 0); + *wb = sc.wc; + wb += psfs.store; } - i = 0; - while ((cc>=0) && (scanset[cc] != invert)) { - c_spec: - i = 1; /* yes, we stored something */ - *b = cc; - b += store; - cc = scan_getc(&sc); + + __scan_ungetc(&sc); + if (sc.width > 0) { /* Failed to read all required. */ + goto DONE; } - if (i==0) { - scan_ungetc(&sc); - goto done; /* return cnt */ + 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) + && ((((__uwchar_t)(sc.wc)) > UCHAR_MAX) + || !isspace(sc.wc)) + ) { + *wb = sc.wc; + wb += psfs.store; + fail = 0; } - if (*p != 'c') { /* nul-terminate the stored string */ - *b = 0; + } else { + assert(psfs.conv_num == CONV_LEFTBRACKET); + + while (scan_getwc(&sc) >= 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; } - cnt += store; - goto nextfmt; } - if (p-spec < 12) { /* o,u,p,x,X,i,d - (un)signed integer */ - if (*p == 'p') { - /* assume pointer same size as int or long. */ - lval = (sizeof(char *) == sizeof(long)); + /* 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; } - usflag = ((p-spec) < 10); /* (1)0 if (un)signed */ - base = radix[(int)(p-spec) - 5]; - b = buf; - if (sc.width <= 0) { - goto done; + } else { + if (psfs.store) { + wb = (wchar_t *) psfs.cur_ptr; } - cc = scan_getc(&sc); - if ((cc == '+') || (cc == '-')) { /* Handle leading sign.*/ - *b++ = cc; - cc = scan_getc(&sc); + } + fail = 1; + + + if ((psfs.conv_num == CONV_C) || (psfs.conv_num == CONV_c)) { + if (sc.width == INT_MAX) { + sc.width = 1; } - if (cc == '0') { /* Possibly set base and handle prefix. */ - if ((base == 0) || (base == 16)) { - cc = scan_getc(&sc); - if ((cc == 'x') || (cc == 'X')) { - /* We're committed to base 16 now. */ - base = 16; - cc = scan_getc(&sc); - } else { /* oops... back up */ - scan_ungetc(&sc); - cc = '0'; - if (base == 0) { - base = 8; - } + + while (scan_getwc(&sc) >= 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; } } } - if (base == 0) { /* Default to base 10 */ - base = 10; + __scan_ungetc(&sc); + if (sc.width > 0) { /* Failed to read all required. */ + goto DONE; } - /* At this point, we're ready to start reading digits. */ - if (cc == '0') { - *b++ = cc; /* Store first leading 0 */ - do { /* but ignore others. */ - cc = scan_getc(&sc); - } while (cc == '0'); - } - while (valid_digit(cc,base)) { /* Now for nonzero digits.*/ - if (b - buf < MAX_DIGITS) { - *b++ = cc; + 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) && !iswspace(sc.wc)) { + 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; + } } - cc = scan_getc(&sc); + 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; } - *b = 0; /* null-terminate */ - if ((b == buf) || (*--b == '+') || (*b == '-')) { - scan_ungetc(&sc); - goto done; /* No digits! */ + sss = (const wchar_t *) fmt; + if (*fmt == ']') { + ++fmt; } - if (store) { - if (*buf == '-') { - usflag = 0; + while (*fmt != ']') { + if (!*fmt) { /* No closing ']'. */ + goto DONE; } - uv = STRTO_L_(buf, NULL, base, 1-usflag); - vp = va_arg(ap, void *); - switch (lval) { - case 2: /* If no long long, treat as long . */ - *((unsigned long long *)vp) = uv; - break; - case 1: -#if ULONG_MAX == UINT_MAX - case 0: /* int and long int are the same */ -#endif - if (usflag) { - if (uv > ULONG_MAX) { - uv = ULONG_MAX; - } - } else if (((V_TYPE)uv) > LONG_MAX) { - uv = LONG_MAX; - } else if (((V_TYPE)uv) < LONG_MIN) { - uv = (UV_TYPE) LONG_MIN; - } - *((unsigned long *)vp) = (unsigned long)uv; - break; -#if ULONG_MAX != UINT_MAX - case 0: /* int and long int are different */ - if (usflag) { - if (uv > UINT_MAX) { - uv = UINT_MAX; - } - } else if (((V_TYPE)uv) > INT_MAX) { - uv = INT_MAX; - } else if (((V_TYPE)uv) < INT_MIN) { - uv = (UV_TYPE) INT_MIN; - } - *((unsigned int *)vp) = (unsigned int)uv; - break; -#endif - case (signed char)(-1): - if (usflag) { - if (uv > USHRT_MAX) { - uv = USHRT_MAX; - } - } else if (((V_TYPE)uv) > SHRT_MAX) { - uv = SHRT_MAX; - } else if (((V_TYPE)uv) < SHRT_MIN) { - uv = (UV_TYPE) SHRT_MIN; - } - *((unsigned short *)vp) = (unsigned short)uv; - break; - case (signed char)(-2): - if (usflag) { - if (uv > UCHAR_MAX) { - uv = UCHAR_MAX; + if ((*fmt == '-') && (fmt[1] != ']') + && (fmt[-1] < fmt[1]) /* sorted? */ + ) { /* range */ + ++fmt; + } + ++fmt; + } + /* Ok... a valid scanset spec. */ + + while (scan_getwc(&sc) >= 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 */ } - } else if (((V_TYPE)uv) > CHAR_MAX) { - uv = CHAR_MAX; - } else if (((V_TYPE)uv) < CHAR_MIN) { - uv = (UV_TYPE) CHAR_MIN; + --ssp; /* oops... '-' at end, so back up */ } - *((unsigned char *)vp) = (unsigned char) uv; + /* false alarm... a literal '-' */ + } + if (sc.wc == *ssp) { /* Matched literal char. */ break; - default: - assert(0); + } + } while (++ssp < (const wchar_t *) fmt); + + if ((ssp == (const wchar_t *) fmt) ^ invert) { + /* no match and not inverting + * or match and inverting */ + break; } - ++cnt; - } - goto nextfmt; - } -#ifdef __UCLIBC_HAS_FLOATS__ - else { /* floating point */ - if (sc.width <= 0) { - goto done; - } - if (__strtold(&ld, &sc)) { /* Success! */ - if (store) { - vp = va_arg(ap, void *); - switch (lval) { - case 2: - *((long double *)vp) = ld; - break; - case 1: - *((double *)vp) = (double) ld; - break; - case 0: - *((float *)vp) = (float) ld; - break; - default: /* Illegal qualifier! */ - assert(0); - goto done; + 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; } - ++cnt; } - goto nextfmt; + fail = 0; } } -#else - assert(0); -#endif - goto done; - } - /* Unrecognized specifier! */ - goto RETURN_cnt; - } if (__isspace(*fmt)) { /* Consume all whitespace. */ - do { - i = scan_getc_nw(&sc); - } while (__isspace(i)); - } else { /* Match the current fmt char. */ - matchchar: - if (scan_getc_nw(&sc) != *fmt) { - scan_ungetc(&sc); - goto done; + /* 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; } - scan_getc_nw(&sc); + +#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; } - nextfmt: - scan_ungetc(&sc); + + NEXT_FMT: ++fmt; } - done: /* end of scan */ - kill_scan_cookie(&sc); - - if ((sc.ungot_char <= 0) && (cnt == 0) && (*fmt)) { - cnt = EOF; + DONE: + if ((psfs.cnt == 0) && (*fmt) && __FEOF_OR_FERROR(fp)) { + psfs.cnt = EOF; /* Yes, vfwscanf also returns EOF. */ } - RETURN_cnt: + kill_scan_cookie(&sc); + +/* RETURN_cnt: */ __STDIO_THREADUNLOCK(fp); - return (cnt); + return psfs.cnt; } +#endif +/**********************************************************************/ +#ifdef L___psfs_do_numeric -/*****************************************************************************/ +static const unsigned char spec_base[] = SPEC_BASE; +static const unsigned char nil_string[] = "(nil)"; + +int __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 +#warning fix MAX_DIGITS. we do not do binary, so...! +#define MAX_DIGITS 65 /* Allow one leading 0. */ +#warning fix buf! + unsigned char buf[MAX_DIGITS+2+ 100]; + unsigned char usflag, base; + unsigned char nonzero = 0; + unsigned char seendigit = 0; + -#include <float.h> +#ifndef __UCLIBC_HAS_FLOATS__ + if (psfs->conv_num > CONV_i) { /* floating point */ + goto DONE; + } +#endif -#define MAX_SIG_DIGITS 20 -#define MAX_IGNORED_DIGITS 2000 -#define MAX_ALLOWED_EXP (MAX_SIG_DIGITS + MAX_IGNORED_DIGITS + LDBL_MAX_10_EXP) + base = spec_base[psfs->conv_num - CONV_p]; + usflag = (psfs->conv_num <= CONV_u); /* (1)0 if (un)signed */ + b = buf; -#if LDBL_DIG > MAX_SIG_DIGITS -#error need to adjust MAX_SIG_DIGITS -#endif -#include <limits.h> -#if MAX_ALLOWED_EXP > INT_MAX -#error size assumption violated for MAX_ALLOWED_EXP + 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) { /* failed */ + /* We matched at least the '(' so even if we + * are at eof, we can not match a pointer. */ + goto DONE; + } + 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 == '+') || (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) +#ifdef __UCLIBC_HAS_WCHAR__ + && !sc->ungot_wflag /* wc outside char range */ +#endif /* __UCLIBC_HAS_WCHAR__ */ + ) { + /* Note! 'x' at end of file|field is special. + * While this looks like I'm 'unget'ing twice, + * EOF and end of field are handled specially + * by the scan_* funcs. */ + __scan_ungetc(sc); + goto DO_NO_0X; + } + 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 */ + DO_NO_0X: + __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; + } + } -int __strtold(long double *ld, struct scan_cookie *sc) -{ - long double number; - long double p10; - int exponent_power; - int exponent_temp; - int negative; - int num_digits; - int since_decimal; - int c; + /***************** 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; - c = scan_getc(sc); /* Decrements width. */ - - negative = 0; - switch(c) { /* Handle optional sign. */ - case '-': negative = 1; /* Fall through to get next char. */ - case '+': c = scan_getc(sc); - } - - number = 0.; - num_digits = -1; - exponent_power = 0; - since_decimal = INT_MIN; - - LOOP: - while (__isdigit(c)) { /* Process string of digits. */ - ++since_decimal; - if (num_digits < 0) { /* First time through? */ - ++num_digits; /* We've now seen a digit. */ - } - if (num_digits || (c != '0')) { /* had/have nonzero */ - ++num_digits; - if (num_digits <= MAX_SIG_DIGITS) { /* Is digit significant? */ - number = number * 10. + (c - '0'); + +#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); } - c = scan_getc(sc); - } - - if ((c == '.') && (since_decimal < 0)) { /* If no previous decimal pt, */ - since_decimal = 0; /* save position of decimal point */ - c = scan_getc(sc); /* and process rest of digits */ - goto LOOP; - } - - if (num_digits<0) { /* Must have at least one digit. */ - goto FAIL; - } - - if (num_digits > MAX_SIG_DIGITS) { /* Adjust exp for skipped digits. */ - exponent_power += num_digits - MAX_SIG_DIGITS; - } - - if (since_decimal >= 0) { /* Adjust exponent for decimal point. */ - exponent_power -= since_decimal; - } - - if (negative) { /* Correct for sign. */ - number = -number; - negative = 0; /* Reset for exponent processing below. */ - } - - /* Process an exponent string. */ - if (c == 'e' || c == 'E') { - c = scan_getc(sc); - switch(c) { /* Handle optional sign. */ - case '-': negative = 1; /* Fall through to get next char. */ - case '+': c = scan_getc(sc); - } - - num_digits = 0; - exponent_temp = 0; - while (__isdigit(c)) { /* Process string of digits. */ - if (exponent_temp < MAX_ALLOWED_EXP) { /* overflow check */ - exponent_temp = exponent_temp * 10 + (c - '0'); + + /* 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; } - c = scan_getc(sc); - ++num_digits; +#endif + goto DONE_DO_UNGET; } - if (num_digits == 0) { /* Were there no exp digits? */ - goto FAIL; - } /* else */ - if (negative) { - exponent_power -= exponent_temp; - } else { - exponent_power += exponent_temp; + 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; + } + 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 + } + } - if (number != 0.) { - /* Now scale the result. */ - exponent_temp = exponent_power; - p10 = 10.; + __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 (exponent_temp < 0) { - exponent_temp = -exponent_temp; + 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; + } + if (i > nblk1) { + /* An inner group if we can back up a bit. */ + if ((i - nblk1) <= (sc->ungot_flag ^ 1)) { + assert(sc->cc < 0); + --b; + goto DO_RECOVER_GROUP; + } + } + + /* (0 < i < nblk1) && (pass > 0) so prev group char + * So we have an unrecoverable situation. */ + goto DONE_DO_UNGET; + } /* i != 0 */ + + assert(pass); + + /* No next group. Can we back up past grouping mb char? */ + if ((pass == 1) || (nblk1 == nblk2)) { + if (!i && (sc->tslen == 1) && (sc->cc < 0)) { + /* No digits, grouping mb char is len 1, and EOF*/ + DO_RECOVER_GROUP: + if (sc->ungot_flag & 2) { + __scan_ungetc(sc); + } + goto DONE_GROUPING_DO_UNGET; + } + } + 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); + } - while (exponent_temp) { - if (exponent_temp & 1) { - if (exponent_power < 0) { - number /= p10; - } else { - number *= p10; +#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) { + if ((sc->cc >= 0) && (p > sc->fake_decpt + 1)) { + goto DONE_DO_UNGET; /* failed */ } + + __scan_ungetc(sc); + } - exponent_temp >>= 1; - p10 *= p10; + 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'); } } - *ld = number; - return 1; - FAIL: - scan_ungetc(sc); - return 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; + } + + /* 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) { /* EOF... recoverable */ + --b; + goto GOT_FLOAT; + } + + if ((sc->cc == '+') || (sc->cc == '-')) { /* Signed exponent? */ + *b++ = sc->cc; + __scan_getc(sc); + } + +#warning fix MAX_EXP_DIGITS! +#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 -1; + +} #endif +/**********************************************************************/ diff --git a/libc/stdio/stdio.c b/libc/stdio/stdio.c index 0d4708d97..40ac693a4 100644 --- a/libc/stdio/stdio.c +++ b/libc/stdio/stdio.c @@ -352,7 +352,7 @@ UNLOCKED(int,fileno,(register FILE *stream),(stream)) FILE *fdopen(int filedes, const char *mode) { - register char *cur_mode; /* TODO -- replace by intptr_t?? */ + register char *cur_mode; /* TODO -- use intptr_t?? (also fopencookie) */ return (((int)(cur_mode = (char *) fcntl(filedes, F_GETFL))) != -1) ? _stdio_fopen(cur_mode, mode, NULL, filedes) @@ -491,7 +491,10 @@ static ssize_t fmo_read(register void *cookie, char *buf, size_t bufsize) { size_t count = COOKIE->len - COOKIE->pos; - /* Note: bufsize < SSIZE_MAX because of _stdio_READ. */ + /* Note: 0 < bufsize < SSIZE_MAX because of _stdio_READ. */ + if (!count) { /* EOF! */ + return 0; + } if (bufsize > count) { bufsize = count; @@ -838,8 +841,6 @@ FILE *open_memstream(char **__restrict bufloc, size_t *__restrict sizeloc) * When compiled without large file support, the offset pointer for the * cookie_seek function is off_t * and not off64_t * as for glibc. */ -/* TODO: rewrite _stdio_fopen() to avoid the fopencookie() kludge. */ - /* Currently no real reentrancy issues other than a possible double close(). */ #ifndef __BCC__ @@ -848,12 +849,14 @@ FILE *fopencookie(void * __restrict cookie, const char * __restrict mode, cookie_io_functions_t io_functions) { FILE *stream; - int fd; - if ((stream = _stdio_fopen("/dev/null", mode, NULL, -1)) != NULL) { - fd = stream->filedes; + /* Fake an fdopen guaranteed to pass the _stdio_fopen basic agreement + * check without an fcntl call. */ + if ((stream = _stdio_fopen(((char *)(INT_MAX-1)), + mode, NULL, INT_MAX)) /* TODO: use intptr_t? */ + != NULL + ) { stream->filedes = -1; - close(fd); stream->gcs = io_functions; stream->cookie = cookie; } @@ -886,10 +889,13 @@ FILE *_fopencookie(void * __restrict cookie, const char * __restrict mode, { register FILE *stream; - if ((stream = _stdio_fopen("/dev/null", mode, NULL, -1)) != NULL) { - int fd = stream->filedes; + /* Fake an fdopen guaranteed to pass the _stdio_fopen basic agreement + * check without an fcntl call. */ + if ((stream = _stdio_fopen(((char *)(INT_MAX-1)), + mode, NULL, INT_MAX)) /* TODO: use intptr_t? */ + != NULL + ) { stream->filedes = -1; - close(fd); stream->gcs.read = io_functions->read; stream->gcs.write = io_functions->write; stream->gcs.seek = io_functions->seek; @@ -1019,7 +1025,7 @@ void __fpurge(register FILE * __restrict stream) /* Not reentrant. */ #ifdef __STDIO_WIDE -#warning unlike the glibc version, this __fpending returns bytes in buffer for wide streams too! +#warning Unlike the glibc version, this __fpending returns bytes in buffer for wide streams too! link_warning(__fpending, "This version of __fpending returns bytes remaining in buffer for both narrow and wide streams. glibc's version returns wide chars in buffer for the wide stream case.") @@ -1220,12 +1226,17 @@ FILE *_stdio_fsfopen(const char * __restrict filename, /* stdio internal functions */ /**********************************************************************/ #ifdef L__stdio_adjpos -/* - * ANSI/ISO p. 370: The file positioning indicator is unspecified after - * a successful call to ungetwc. + +/* According to the ANSI/ISO C99 definition of ungetwc() + * For a text or binary stream, the value of its file position indicator + * after a successful call to the ungetwc function is unspecified until + * all pushedback wide characters are read or discarded. * Note however, that this applies only to _user_ calls to ungetwc. We - * need to allow for internal calls by scanf. So we store the byte count - * of the first ungot wide char in ungot0_bytes. If it is 0 (user case) + * need to allow for internal calls by scanf. + + + * So we store the byte count + * of the first ungot wide char in ungot_width. If it is 0 (user case) * then the file position is treated as unknown. */ @@ -1236,16 +1247,23 @@ int _stdio_adjpos(register FILE * __restrict stream, register __offmax_t *pos) __offmax_t r; int cor = stream->modeflags & __MASK_UNGOT; /* handle ungots */ + assert(cor <= 2); + #ifdef __STDIO_WIDE /* Assumed narrow stream so correct if wide. */ if (cor && (stream->modeflags & __FLAG_WIDE)) { - cor = cor - 1 + stream->ungot_width[0]; - if (((stream->modeflags & __MASK_UNGOT) > 1) || stream->ungot[1]) { + if ((((stream->modeflags & __MASK_UNGOT) > 1) || stream->ungot[1])) { return -1; /* App did ungetwc, so position is indeterminate. */ } - assert(stream->ungot_width[0] > 0); + if (stream->modeflags & __MASK_UNGOT) { + cor = cor - 1 + stream->ungot_width[1]; + } + if (stream->state.mask > 0) { /* Incomplete character (possible bad) */ + cor -= stream->ungot_width[0]; + } } #endif /* __STDIO_WIDE */ + #ifdef __STDIO_BUFFERS if (stream->modeflags & __FLAG_WRITING) { cor -= (stream->bufpos - stream->bufstart); /* pending writes */ @@ -1415,9 +1433,14 @@ size_t _stdio_fread(unsigned char *buffer, size_t bytes, register FILE *stream) *p++ = *stream->bufpos++; } - if ((bytes > 0) && (stream->filedes != -2)) { + if (bytes > 0) { ssize_t len; + if (stream->filedes == -2) { + stream->modeflags |= __FLAG_EOF; + goto DONE; + } + /* The buffer is exhausted, but we still need chars. */ stream->bufpos = stream->bufread = stream->bufstart; @@ -1449,6 +1472,7 @@ size_t _stdio_fread(unsigned char *buffer, size_t bytes, register FILE *stream) } #endif + DONE: __stdio_validate_FILE(stream); /* debugging only */ return (p - (unsigned char *)buffer); } @@ -2086,7 +2110,9 @@ int fclose(register FILE *stream) /* At this point, any dangling refs to the stream are the result of * a programming bug... so free the unlocked stream. */ if (stream->modeflags & __FLAG_FREEFILE) { +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS stream->cookie = NULL; /* To aid debugging... */ +#endif free(stream); } @@ -2118,7 +2144,9 @@ int fclose(register FILE *stream) /* At this point, any dangling refs to the stream are the result of * a programming bug... so free the unlocked stream. */ if (stream->modeflags & __FLAG_FREEFILE) { +#ifdef __STDIO_GLIBC_CUSTOM_STREAMS stream->cookie = NULL; /* To aid debugging... */ +#endif free(stream); } @@ -2213,7 +2241,7 @@ int fflush_unlocked(register FILE *stream) rv = -1; /* Not all chars written. */ } #ifdef __UCLIBC_MJN3_ONLY__ -#warning add option to test for undefined behavior of fflush +#warning WISHLIST: Add option to test for undefined behavior of fflush. #endif /* __UCLIBC_MJN3_ONLY__ */ #if 0 } else if (stream->modeflags & (__FLAG_READING|__FLAG_READONLY)) { @@ -2242,7 +2270,7 @@ int fflush_unlocked(register FILE *stream) #endif #ifdef __UCLIBC_MJN3_ONLY__ -#warning add option to test for undefined behavior of fflush +#warning WISHLIST: Add option to test for undefined behavior of fflush. #endif /* __UCLIBC_MJN3_ONLY__ */ #if 0 return ((stream != NULL) @@ -2317,16 +2345,18 @@ FILE *_stdio_fopen(const char * __restrict filename, open_mode |= O_RDWR; } -#if defined(__STDIO_GNU_FEATURE) || defined(__STDIO_FOPEN_LARGEFILE_MODE) - while (*mode) { /* ignore everything else except ... */ +#if defined(__STDIO_FOPEN_EXCLUSIVE_MODE) || defined(__STDIO_FOPEN_LARGEFILE_MODE) + for ( ; *mode ; ++mode) { /* ignore everything else except ... */ #ifdef __STDIO_FOPEN_EXCLUSIVE_MODE - if (*mode++ == 'x') { /* open exclusive -- glibc extension */ + if (*mode == 'x') { /* open exclusive -- glibc extension */ open_mode |= O_EXCL; + continue; } #endif /* __STDIO_FOPEN_EXCLUSIVE_MODE */ #ifdef __STDIO_FOPEN_LARGEFILE_MODE - if (*mode++ == 'F') { /* open large file */ + if (*mode == 'F') { /* open large file */ open_mode |= O_LARGEFILE; + continue; } #endif /* __STDIO_FOPEN_LARGEFILE_MODE */ } @@ -2371,6 +2401,7 @@ FILE *_stdio_fopen(const char * __restrict filename, * leave it set (glibc compat). */ int i = (open_mode & (O_ACCMODE|O_LARGEFILE)) + 1; + /* NOTE: fopencookie needs changing if the basic check changes! */ if (((i & (((int) filename) + 1)) != i) /* Check basic agreement. */ || (((open_mode & O_APPEND) && !(((int) filename) & O_APPEND) @@ -2445,6 +2476,9 @@ FILE *_stdio_fopen(const char * __restrict filename, stream->gcs.close = _cs_close; #endif /* __STDIO_GLIBC_CUSTOM_STREAMS */ +#ifdef __STDIO_WIDE + stream->ungot_width[0] = 0; +#endif /* __STDIO_WIDE */ #ifdef __STDIO_MBSTATE __INIT_MBSTATE(&(stream->state)); #endif /* __STDIO_MBSTATE */ @@ -2797,6 +2831,7 @@ UNLOCKED(int,fputs, /**********************************************************************/ #ifdef L_getc #undef getc +#undef getc_unlocked /* Reentrancy handled by UNLOCKED() macro. */ @@ -2855,6 +2890,7 @@ char *gets(char *s) /* WARNING!!! UNSAFE FUNCTION!!! */ /**********************************************************************/ #ifdef L_putc #undef putc +#undef putc_unlocked /* Reentrancy handled by UNLOCKED() macro. */ @@ -2931,8 +2967,7 @@ int ungetc(int c, register FILE *stream) stream->modeflags |= __FLAG_NARROW; #endif /* __STDIO_WIDE */ - /* If can't read or there's been an error, or c == EOF, or ungot slots - * already filled, then return EOF */ + /* If can't read or c == EOF or ungot slots already filled, then fail. */ if ((stream->modeflags & (__MASK_UNGOT2|__FLAG_WRITEONLY #ifndef __STDIO_AUTO_RW_TRANSITION @@ -2946,14 +2981,18 @@ int ungetc(int c, register FILE *stream) } #ifdef __STDIO_BUFFERS - /* TODO: shouldn't allow writing??? */ +#ifdef __STDIO_AUTO_RW_TRANSITION if (stream->modeflags & __FLAG_WRITING) { fflush_unlocked(stream); /* Commit any write-buffered chars. */ } +#endif /* __STDIO_AUTO_RW_TRANSITION */ #endif /* __STDIO_BUFFERS */ /* Clear EOF and WRITING flags, and set READING FLAG */ stream->modeflags &= ~(__FLAG_EOF|__FLAG_WRITING); +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Is setting the reading flag after an ungetc necessary? +#endif /* __UCLIBC_MJN3_ONLY__ */ stream->modeflags |= __FLAG_READING; stream->ungot[1] = 1; /* Flag as app ungetc call; scanf fixes up. */ stream->ungot[(stream->modeflags++) & __MASK_UNGOT] = c; @@ -3047,22 +3086,35 @@ UNLOCKED(size_t,fwrite, int fgetpos64(FILE * __restrict stream, register fpos64_t * __restrict pos) { -#ifdef __STDIO_MBSTATE - int retval; + int retval = -1; - __STDIO_THREADLOCK(stream); +#ifdef __STDIO_WIDE - retval = ((pos != NULL) && ((pos->__pos = ftello64(stream)) >= 0)) - ? (__COPY_MBSTATE(&(pos->__mbstate), &(stream->state)), 0) - : (__set_errno(EINVAL), -1); + if (pos == NULL) { + __set_errno(EINVAL); + } else { + __STDIO_THREADLOCK(stream); - __STDIO_THREADUNLOCK(stream); + if ((pos->__pos = ftello64(stream)) >= 0) { + __COPY_MBSTATE(&(pos->__mbstate), &(stream->state)); + pos->mblen_pending = stream->ungot_width[0]; + retval = 0; + } + + __STDIO_THREADUNLOCK(stream); + } + +#else /* __STDIO_WIDE */ + + if (pos == NULL) { + __set_errno(EINVAL); + } else if ((pos->__pos = ftello64(stream)) >= 0) { + retval = 0; + } + +#endif /* __STDIO_WIDE */ return retval; -#else - return ((pos != NULL) && ((pos->__pos = ftello64(stream)) >= 0)) - ? 0 : (__set_errno(EINVAL), -1); -#endif } #ifndef L_fgetpos64 @@ -3137,10 +3189,19 @@ int fseeko64(register FILE *stream, __off64_t offset, int whence) stream->modeflags &= ~(__FLAG_READING|__FLAG_WRITING|__FLAG_EOF|__MASK_UNGOT); +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: How do we deal with fseek to an ftell position for incomplete or error wide? Right now, we clear all multibyte state info. If we do not clear, then fix rewind() to do so if fseek() succeeds. +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#ifdef __STDIO_WIDE + /* TODO: don't clear state if don't move? */ + stream->ungot_width[0] = 0; +#endif /* __STDIO_WIDE */ #ifdef __STDIO_MBSTATE /* TODO: don't clear state if don't move? */ __INIT_MBSTATE(&(stream->state)); #endif /* __STDIO_MBSTATE */ + __stdio_validate_FILE(stream); /* debugging only */ retval = 0; @@ -3178,7 +3239,7 @@ int fsetpos64(FILE *stream, register const fpos64_t *pos) __set_errno(EINVAL); return EOF; } -#ifdef __STDIO_MBSTATE +#ifdef __STDIO_WIDE { int retval; @@ -3186,15 +3247,19 @@ int fsetpos64(FILE *stream, register const fpos64_t *pos) if ((retval = fseeko64(stream, pos->__pos, SEEK_SET)) == 0) { __COPY_MBSTATE(&(stream->state), &(pos->__mbstate)); +#ifdef __UCLIBC_MJN3_ONLY__ +#warning CONSIDER: Moving mblen_pending into some mbstate field might be useful. But we would need to modify all the mb<->wc funcs. +#endif /* __UCLIBC_MJN3_ONLY__ */ + stream->ungot_width[0] = pos->mblen_pending; } __STDIO_THREADUNLOCK(stream); return retval; } -#else /* __STDIO_MBSTATE */ +#else /* __STDIO_WIDE */ return fseeko64(stream, pos->__pos, SEEK_SET); -#endif /* __STDIO_MBSTATE */ +#endif /* __STDIO_WIDE */ } #ifndef L_fsetpos64 @@ -3257,10 +3322,6 @@ void rewind(register FILE *stream) __CLEARERR(stream); /* Clear errors first and then seek */ fseek(stream, 0L, SEEK_SET); /* in case there is an error seeking. */ -#ifdef __STDIO_MBSTATE - /* TODO: Is it correct to re-init the stream's state? I think so... */ - __INIT_MBSTATE(&(stream->state)); -#endif /* __STDIO_MBSTATE */ __STDIO_THREADUNLOCK(stream); } @@ -3371,15 +3432,8 @@ void _stdio_fdout(int fd, ...) #define INTERNAL_DIV_MOD #endif -#ifdef __UCLIBC_MJN3_ONLY__ -#warning REMINDER: move _uintmaxtostr to locale.c??? -#endif #include <locale.h> -#ifndef __LOCALE_C_ONLY -#define CUR_LOCALE (__global_locale) -#endif /* __LOCALE_C_ONLY */ - char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval, int base, __UIM_CASE alphacase) { @@ -3410,14 +3464,10 @@ char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval, grouping = -1; outdigit = 0x80 & alphacase; alphacase ^= outdigit; -#ifdef __UCLIBC_MJN3_ONLY_ -#warning implement outdigit... need digit lengths! (put it in locale struct) -#endif if (alphacase == __UIM_GROUP) { assert(base == 10); - if (*(g = CUR_LOCALE.grouping) - && ((gslen = strlen(CUR_LOCALE.thousands_sep)) > 0) - ) { + if (*(g = __UCLIBC_CURLOCALE_DATA.grouping)) { + gslen = strlen(__UCLIBC_CURLOCALE_DATA.thousands_sep); grouping = *g; } } @@ -3430,15 +3480,15 @@ char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval, #ifndef __LOCALE_C_ONLY if (!grouping) { /* Finished a group. */ #ifdef __UCLIBC_MJN3_ONLY__ -#warning REMINDER: decide about memcpy in _uintmaxtostr -#endif +#warning TODO: Decide about memcpy in _uintmaxtostr. +#endif /* __UCLIBC_MJN3_ONLY__ */ #if 0 bufend -= gslen; - memcpy(bufend, CUR_LOCALE.thousands_sep, gslen); + memcpy(bufend, __UCLIBC_CURLOCALE_DATA.thousands_sep, gslen); #else grouping = gslen; do { - *--bufend = CUR_LOCALE.thousands_sep[--grouping]; + *--bufend = __UCLIBC_CURLOCALE_DATA.thousands_sep[--grouping]; } while (grouping); #endif if (g[1] != 0) { /* g[1] == 0 means repeat last grouping. */ @@ -3456,9 +3506,9 @@ char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval, #ifndef __LOCALE_C_ONLY if (outdigit) { - outdigit = CUR_LOCALE.outdigit_length[digit]; + outdigit = __UCLIBC_CURLOCALE_DATA.outdigit_length[digit]; do { - *--bufend = (&CUR_LOCALE.outdigit0_mb)[digit][--outdigit]; + *--bufend = (&__UCLIBC_CURLOCALE_DATA.outdigit0_mb)[digit][--outdigit]; } while (outdigit); outdigit = 1; } else @@ -3483,15 +3533,15 @@ char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval, #ifndef __LOCALE_C_ONLY if (!grouping) { /* Finished a group. */ #ifdef __UCLIBC_MJN3_ONLY__ -#warning REMINDER: decide about memcpy in _uintmaxtostr -#endif +#warning TODO: Decide about memcpy in _uintmaxtostr +#endif /* __UCLIBC_MJN3_ONLY__ */ #if 0 bufend -= gslen; - memcpy(bufend, CUR_LOCALE.thousands_sep, gslen); + memcpy(bufend, __UCLIBC_CURLOCALE_DATA.thousands_sep, gslen); #else grouping = gslen; do { - *--bufend = CUR_LOCALE.thousands_sep[--grouping]; + *--bufend = __UCLIBC_CURLOCALE_DATA.thousands_sep[--grouping]; } while (grouping); #endif if (g[1] != 0) { /* g[1] == 0 means repeat last grouping. */ @@ -3513,9 +3563,9 @@ char *_uintmaxtostr(register char * __restrict bufend, uintmax_t uval, #ifndef __LOCALE_C_ONLY if (outdigit) { - outdigit = CUR_LOCALE.outdigit_length[digit]; + outdigit = __UCLIBC_CURLOCALE_DATA.outdigit_length[digit]; do { - *--bufend = (&CUR_LOCALE.outdigit0_mb)[digit][--outdigit]; + *--bufend = (&__UCLIBC_CURLOCALE_DATA.outdigit0_mb)[digit][--outdigit]; } while (outdigit); outdigit = 1; } else @@ -3548,7 +3598,7 @@ size_t _wstdio_fwrite(const wchar_t *__restrict ws, size_t n, char buf[64]; const wchar_t *pw; -#ifdef __STDIO_BUFFERS +#if defined(__STDIO_WIDE) && defined(__STDIO_BUFFERS) if (stream->filedes == -3) { /* Special case to support {v}swprintf. */ count = ((wchar_t *)(stream->bufend)) - ((wchar_t *)(stream->bufpos)); if (count > n) { @@ -3560,7 +3610,7 @@ size_t _wstdio_fwrite(const wchar_t *__restrict ws, size_t n, } return n; } -#endif +#endif /* defined(__STDIO_WIDE) && defined(__STDIO_BUFFERS) */ if (stream->modeflags & __FLAG_NARROW) { stream->modeflags |= __FLAG_ERROR; diff --git a/libc/stdlib/Makefile b/libc/stdlib/Makefile index 59a5a6e0c..01765c1f6 100644 --- a/libc/stdlib/Makefile +++ b/libc/stdlib/Makefile @@ -38,31 +38,57 @@ MOBJ = abs.o labs.o atoi.o atol.o strtol.o strtoul.o _stdlib_strto_l.o \ qsort.o bsearch.o \ llabs.o atoll.o strtoll.o strtoull.o _stdlib_strto_ll.o # (aliases) strtoq.o strtouq.o +ifeq ($(UCLIBC_HAS_XLOCALE),y) + +MOBJx = +MOBJx += strtol_l.o strtoul_l.o _stdlib_strto_l_l.o \ + strtoll_l.o strtoull_l.o _stdlib_strto_ll_l.o +endif + +MSRC1 = strtod.c +MOBJ1 = +MOBJ1x = + ifeq ($(UCLIBC_HAS_FLOATS),y) MOBJ += atof.o + MOBJ1 += strtod.o strtof.o strtold.o __strtofpmax.o __fp_range_check.o +ifeq ($(UCLIBC_HAS_XLOCALE),y) + MOBJ1x += strtod_l.o strtof_l.o strtold_l.o __strtofpmax_l.o +endif +ifeq ($(UCLIBC_HAS_WCHAR),y) + MOBJ1 += wcstod.o wcstof.o wcstold.o __wcstofpmax.o +ifeq ($(UCLIBC_HAS_XLOCALE),y) + MOBJ1x += wcstod_l.o wcstof_l.o wcstold_l.o __wcstofpmax_l.o +endif +endif endif ifeq ($(UCLIBC_HAS_WCHAR),y) MOBJ += mblen.o mbtowc.o wctomb.o mbstowcs.o wcstombs.o \ _stdlib_mb_cur_max.o _stdlib_wcsto_l.o _stdlib_wcsto_ll.o \ wcstol.o wcstoul.o wcstoll.o wcstoull.o +ifeq ($(UCLIBC_HAS_XLOCALE),y) + MOBJx += _stdlib_wcsto_l_l.o _stdlib_wcsto_ll_l.o \ + wcstol_l.o wcstoul_l.o wcstoll_l.o wcstoull_l.o +endif +endif # (aliases) wcstoq.o wcstouq.o # wcstod wcstof wcstold -endif -MSRC2=atexit.c -MOBJ2=atexit.o on_exit.o __exit_handler.o exit.o +MSRC2 = atexit.c +MOBJ2 = atexit.o on_exit.o __exit_handler.o exit.o + CSRC = abort.c getenv.c mkdtemp.c mktemp.c realpath.c mkstemp.c mkstemp64.c \ rand.c random.c random_r.c setenv.c system.c div.c ldiv.c getpt.c \ ptsname.c grantpt.c unlockpt.c gcvt.c drand48-iter.c jrand48.c \ jrand48_r.c lrand48.c lrand48_r.c mrand48.c mrand48_r.c nrand48.c \ nrand48_r.c rand_r.c srand48.c srand48_r.c calloc.c valloc.c ifeq ($(UCLIBC_HAS_FLOATS),y) - CSRC += strtod.c strtof.c strtold.c drand48.c drand48_r.c erand48.c erand48_r.c + CSRC += drand48.c drand48_r.c erand48.c erand48_r.c endif COBJS=$(patsubst %.c,%.o, $(CSRC)) -OBJS=$(MOBJ) $(MOBJ2) $(COBJS) +OBJS=$(MOBJ) $(MOBJx) $(MOBJ1) $(MOBJ1x) $(MOBJ2) $(COBJS) all: $(OBJS) $(LIBC) @@ -75,6 +101,18 @@ $(MOBJ): $(MSRC) $(CC) $(CFLAGS) -DL_$* $< -c -o $*.o $(STRIPTOOL) -x -R .note -R .comment $*.o +$(MOBJx): $(MSRC) + $(CC) $(CFLAGS) -DL_$* -D__UCLIBC_DO_XLOCALE $< -c -o $*.o + $(STRIPTOOL) -x -R .note -R .comment $*.o + +$(MOBJ1): $(MSRC1) + $(CC) $(CFLAGS) -DL_$* $< -c -o $*.o + $(STRIPTOOL) -x -R .note -R .comment $*.o + +$(MOBJ1x): $(MSRC1) + $(CC) $(CFLAGS) -DL_$* -D__UCLIBC_DO_XLOCALE $< -c -o $*.o + $(STRIPTOOL) -x -R .note -R .comment $*.o + $(MOBJ2): $(MSRC2) $(CC) $(CFLAGS) -DL_$* $< -c -o $*.o $(STRIPTOOL) -x -R .note -R .comment $*.o diff --git a/libc/stdlib/stdlib.c b/libc/stdlib/stdlib.c index 122289c2c..68b1af8b7 100644 --- a/libc/stdlib/stdlib.c +++ b/libc/stdlib/stdlib.c @@ -56,15 +56,24 @@ #define strtoull __ignore_strtoull #define wcstoll __ignore_wcstoll #define wcstoull __ignore_wcstoull +#define strtoll_l __ignore_strtoll_l +#define strtoull_l __ignore_strtoull_l +#define wcstoll_l __ignore_wcstoll_l +#define wcstoull_l __ignore_wcstoull_l #endif #include <stdlib.h> +#include <locale.h> #ifdef __UCLIBC_HAS_WCHAR__ -#include <locale.h> #include <wchar.h> #include <wctype.h> +#include <bits/uClibc_uwchar.h> + +#ifdef __UCLIBC_HAS_XLOCALE__ +#include <xlocale.h> +#endif /* __UCLIBC_HAS_XLOCALE__ */ /* TODO: clean up the following... */ @@ -75,15 +84,23 @@ #endif #ifdef __UCLIBC_HAS_LOCALE__ -#define ENCODING (__global_locale.encoding) + +#define ENCODING ((__UCLIBC_CURLOCALE_DATA).encoding) #ifndef __CTYPE_HAS_UTF_8_LOCALES +#ifdef L_mblen +/* emit only once */ #warning __CTYPE_HAS_UTF_8_LOCALES not set! #endif -#else +#endif + +#else /* __UCLIBC_HAS_LOCALE__ */ + #ifdef __UCLIBC_MJN3_ONLY__ +#ifdef L_mblen +/* emit only once */ #warning devel checks #endif -#define ENCODING (__ctype_encoding_7_bit) +#endif #ifdef __CTYPE_HAS_8_BIT_LOCALES #error __CTYPE_HAS_8_BIT_LOCALES is defined! #endif @@ -92,7 +109,7 @@ #endif #endif -#endif +#endif /* __UCLIBC_HAS_LOCALE__ */ #if UINT_MAX == ULONG_MAX #undef atoi @@ -105,9 +122,44 @@ #undef strtoull #undef wcstoll #undef wcstoull +#undef strtoll_l +#undef strtoull_l +#undef wcstoll_l +#undef wcstoull_l #endif /* __UCLIBC_HAS_WCHAR__ */ /**********************************************************************/ +#ifdef __UCLIBC_HAS_XLOCALE__ + +extern unsigned long +_stdlib_strto_l_l(register const char * __restrict str, + char ** __restrict endptr, int base, int sflag, + __locale_t locale_arg); + +#if defined(ULLONG_MAX) +extern unsigned long long +_stdlib_strto_ll_l(register const char * __restrict str, + char ** __restrict endptr, int base, int sflag, + __locale_t locale_arg); +#endif + +#ifdef __UCLIBC_HAS_WCHAR__ +extern unsigned long +_stdlib_wcsto_l_l(register const wchar_t * __restrict str, + wchar_t ** __restrict endptr, int base, int sflag, + __locale_t locale_arg); + +#if defined(ULLONG_MAX) +extern unsigned long long +_stdlib_wcsto_ll_l(register const wchar_t * __restrict str, + wchar_t ** __restrict endptr, int base, int sflag, + __locale_t locale_arg); +#endif +#endif /* __UCLIBC_HAS_WCHAR__ */ + +#endif /* __UCLIBC_HAS_XLOCALE__ */ + + extern unsigned long _stdlib_strto_l(register const char * __restrict str, @@ -130,7 +182,6 @@ _stdlib_wcsto_ll(register const wchar_t * __restrict str, wchar_t ** __restrict endptr, int base, int sflag); #endif #endif /* __UCLIBC_HAS_WCHAR__ */ - /**********************************************************************/ #ifdef L_atof @@ -235,73 +286,82 @@ long long atoll(const char *nptr) #endif /**********************************************************************/ -#ifdef L_strtol +#if defined(L_strtol) || defined(L_strtol_l) -#if ULONG_MAX == UINTMAX_MAX +#if (ULONG_MAX == UINTMAX_MAX) && !defined(L_strtol_l) strong_alias(strtol,strtoimax) #endif #if defined(ULLONG_MAX) && (ULLONG_MAX == ULONG_MAX) -strong_alias(strtol,strtoll) +strong_alias(__XL(strtol),__XL(strtoll)) #endif -long strtol(const char * __restrict str, char ** __restrict endptr, int base) +long __XL(strtol)(const char * __restrict str, char ** __restrict endptr, + int base __LOCALE_PARAM ) { - return _stdlib_strto_l(str, endptr, base, 1); + return __XL(_stdlib_strto_l)(str, endptr, base, 1 __LOCALE_ARG ); } #endif /**********************************************************************/ -#ifdef L_strtoll +#if defined(L_strtoll) || defined(L_strtoll_l) #if defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) +#if !defined(L_strtoll_l) #if (ULLONG_MAX == UINTMAX_MAX) strong_alias(strtoll,strtoimax) #endif strong_alias(strtoll,strtoq) +#endif -long long strtoll(const char * __restrict str, - char ** __restrict endptr, int base) +long long __XL(strtoll)(const char * __restrict str, + char ** __restrict endptr, int base + __LOCALE_PARAM ) { - return (long long) _stdlib_strto_ll(str, endptr, base, 1); + return (long long) __XL(_stdlib_strto_ll)(str, endptr, base, 1 + __LOCALE_ARG ); } #endif /* defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) */ #endif /**********************************************************************/ -#ifdef L_strtoul +#if defined(L_strtoul) || defined(L_strtoul_l) -#if ULONG_MAX == UINTMAX_MAX +#if (ULONG_MAX == UINTMAX_MAX) && !defined(L_strtoul_l) strong_alias(strtoul,strtoumax) #endif #if defined(ULLONG_MAX) && (ULLONG_MAX == ULONG_MAX) -strong_alias(strtoul,strtoull) +strong_alias(__XL(strtoul),__XL(strtoull)) #endif -unsigned long strtoul(const char * __restrict str, - char ** __restrict endptr, int base) +unsigned long __XL(strtoul)(const char * __restrict str, + char ** __restrict endptr, int base + __LOCALE_PARAM ) { - return _stdlib_strto_l(str, endptr, base, 0); + return __XL(_stdlib_strto_l)(str, endptr, base, 0 __LOCALE_ARG ); } #endif /**********************************************************************/ -#ifdef L_strtoull +#if defined(L_strtoull) || defined(L_strtoull_l) #if defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) +#if !defined(L_strtoull_l) #if (ULLONG_MAX == UINTMAX_MAX) strong_alias(strtoull,strtoumax) #endif strong_alias(strtoull,strtouq) +#endif -unsigned long long strtoull(const char * __restrict str, - char ** __restrict endptr, int base) +unsigned long long __XL(strtoull)(const char * __restrict str, + char ** __restrict endptr, int base + __LOCALE_PARAM ) { - return _stdlib_strto_ll(str, endptr, base, 0); + return __XL(_stdlib_strto_ll)(str, endptr, base, 0 __LOCALE_ARG ); } #endif /* defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) */ @@ -327,26 +387,54 @@ unsigned long long strtoull(const char * __restrict str, #endif /**********************************************************************/ -#ifdef L__stdlib_wcsto_l +#if defined(L__stdlib_wcsto_l) || defined(L__stdlib_wcsto_l_l) #define L__stdlib_strto_l #endif -#ifdef L__stdlib_strto_l +#if defined(L__stdlib_strto_l) || defined(L__stdlib_strto_l_l) + +#if defined(L__stdlib_wcsto_l) || defined(L__stdlib_wcsto_l_l) -#ifdef L__stdlib_wcsto_l #define _stdlib_strto_l _stdlib_wcsto_l +#define _stdlib_strto_l_l _stdlib_wcsto_l_l #define Wchar wchar_t -#define ISSPACE iswspace +#define Wuchar __uwchar_t +#ifdef __UCLIBC_DO_XLOCALE +#define ISSPACE(C) iswspace_l((C), locale_arg) #else +#define ISSPACE(C) iswspace((C)) +#endif + +#else /* defined(L__stdlib_wcsto_l) || defined(L__stdlib_wcsto_l_l) */ + #define Wchar char -#define ISSPACE isspace +#define Wuchar unsigned char +#ifdef __UCLIBC_DO_XLOCALE +#define ISSPACE(C) isspace_l((C), locale_arg) +#else +#define ISSPACE(C) isspace((C)) #endif +#endif /* defined(L__stdlib_wcsto_l) || defined(L__stdlib_wcsto_l_l) */ + +#if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) + +unsigned long _stdlib_strto_l(register const Wchar * __restrict str, + Wchar ** __restrict endptr, int base, + int sflag) +{ + return _stdlib_strto_l_l(str, endptr, base, sflag, __UCLIBC_CURLOCALE); +} + + +#else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ + /* This is the main work fuction which handles both strtol (sflag = 1) and * strtoul (sflag = 0). */ -unsigned long _stdlib_strto_l(register const Wchar * __restrict str, - Wchar ** __restrict endptr, int base, int sflag) +unsigned long __XL(_stdlib_strto_l)(register const Wchar * __restrict str, + Wchar ** __restrict endptr, int base, + int sflag __LOCALE_PARAM ) { unsigned long number, cutoff; #if _STRTO_ENDPTR @@ -361,7 +449,7 @@ unsigned long _stdlib_strto_l(register const Wchar * __restrict str, SET_FAIL(str); - while (ISSPACE(*str)) { /* Skip leading whitespace. */ + while (ISSPACE(*str)) { /* Skip leading whitespace. */ ++str; } @@ -394,7 +482,7 @@ unsigned long _stdlib_strto_l(register const Wchar * __restrict str, cutoff_digit = ULONG_MAX % base; cutoff = ULONG_MAX / base; do { - digit = (((unsigned char)(*str - '0')) <= 9) + digit = (((Wuchar)(*str - '0')) <= 9) ? (*str - '0') : ((*str >= 'A') ? (((0x20|(*str)) - 'a' + 10)) /* WARNING: assumes ascii. */ @@ -436,32 +524,61 @@ unsigned long _stdlib_strto_l(register const Wchar * __restrict str, return negative ? (unsigned long)(-((long)number)) : number; } +#endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ + + #endif /**********************************************************************/ -#ifdef L__stdlib_wcsto_ll +#if defined(L__stdlib_wcsto_ll) || defined(L__stdlib_wcsto_ll_l) #define L__stdlib_strto_ll #endif -#ifdef L__stdlib_strto_ll +#if defined(L__stdlib_strto_ll) || defined(L__stdlib_strto_ll_l) #if defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) -#ifdef L__stdlib_wcsto_ll +#if defined(L__stdlib_wcsto_ll) || defined(L__stdlib_wcsto_ll_l) #define _stdlib_strto_ll _stdlib_wcsto_ll +#define _stdlib_strto_ll_l _stdlib_wcsto_ll_l #define Wchar wchar_t -#define ISSPACE iswspace +#define Wuchar __uwchar_t +#ifdef __UCLIBC_DO_XLOCALE +#define ISSPACE(C) iswspace_l((C), locale_arg) #else +#define ISSPACE(C) iswspace((C)) +#endif + +#else /* defined(L__stdlib_wcsto_ll) || defined(L__stdlib_wcsto_ll_l) */ + #define Wchar char -#define ISSPACE isspace +#define Wuchar unsigned char +#ifdef __UCLIBC_DO_XLOCALE +#define ISSPACE(C) isspace_l((C), locale_arg) +#else +#define ISSPACE(C) isspace((C)) #endif -/* This is the main work fuction which handles both strtoll (sflag = 1) and - * strtoull (sflag = 0). */ +#endif /* defined(L__stdlib_wcsto_ll) || defined(L__stdlib_wcsto_ll_l) */ + +#if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) unsigned long long _stdlib_strto_ll(register const Wchar * __restrict str, Wchar ** __restrict endptr, int base, int sflag) { + return _stdlib_strto_ll_l(str, endptr, base, sflag, __UCLIBC_CURLOCALE); +} + + +#else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ + +/* This is the main work fuction which handles both strtoll (sflag = 1) and + * strtoull (sflag = 0). */ + +unsigned long long __XL(_stdlib_strto_ll)(register const Wchar * __restrict str, + Wchar ** __restrict endptr, int base, + int sflag __LOCALE_PARAM ) +{ unsigned long long number; #if _STRTO_ENDPTR const Wchar *fail_char; @@ -507,7 +624,7 @@ unsigned long long _stdlib_strto_ll(register const Wchar * __restrict str, if (((unsigned)(base - 2)) < 35) { /* Legal base. */ do { - digit = (((unsigned char)(*str - '0')) <= 9) + digit = (((Wuchar)(*str - '0')) <= 9) ? (*str - '0') : ((*str >= 'A') ? (((0x20|(*str)) - 'a' + 10)) /* WARNING: assumes ascii. */ @@ -560,6 +677,8 @@ unsigned long long _stdlib_strto_ll(register const Wchar * __restrict str, return negative ? (unsigned long long)(-((long long)number)) : number; } +#endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ + #endif /* defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) */ #endif @@ -715,7 +834,7 @@ void ssort (void *base, size_t _stdlib_mb_cur_max(void) { #ifdef __CTYPE_HAS_UTF_8_LOCALES - return __global_locale.mb_cur_max; + return __UCLIBC_CURLOCALE_DATA.mb_cur_max; #else #ifdef __CTYPE_HAS_8_BIT_LOCALES #ifdef __UCLIBC_MJN3_ONLY__ @@ -824,73 +943,82 @@ size_t wcstombs(char * __restrict s, const wchar_t * __restrict pwcs, size_t n) #endif /**********************************************************************/ -#ifdef L_wcstol +#if defined(L_wcstol) || defined(L_wcstol_l) -#if ULONG_MAX == UINTMAX_MAX +#if (ULONG_MAX == UINTMAX_MAX) && !defined(L_wcstol_l) strong_alias(wcstol,wcstoimax) #endif #if defined(ULLONG_MAX) && (ULLONG_MAX == ULONG_MAX) -strong_alias(wcstol,wcstoll) +strong_alias(__XL(wcstol),__XL(wcstoll)) #endif -long wcstol(const wchar_t * __restrict str, wchar_t ** __restrict endptr, int base) +long __XL(wcstol)(const wchar_t * __restrict str, + wchar_t ** __restrict endptr, int base __LOCALE_PARAM ) { - return _stdlib_wcsto_l(str, endptr, base, 1); + return __XL(_stdlib_wcsto_l)(str, endptr, base, 1 __LOCALE_ARG ); } #endif /**********************************************************************/ -#ifdef L_wcstoll +#if defined(L_wcstoll) || defined(L_wcstoll_l) #if defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) +#if !defined(L_wcstoll_l) #if (ULLONG_MAX == UINTMAX_MAX) strong_alias(wcstoll,wcstoimax) #endif strong_alias(wcstoll,wcstoq) +#endif -long long wcstoll(const wchar_t * __restrict str, - wchar_t ** __restrict endptr, int base) +long long __XL(wcstoll)(const wchar_t * __restrict str, + wchar_t ** __restrict endptr, int base + __LOCALE_PARAM ) { - return (long long) _stdlib_wcsto_ll(str, endptr, base, 1); + return (long long) __XL(_stdlib_wcsto_ll)(str, endptr, base, 1 + __LOCALE_ARG ); } #endif /* defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) */ #endif /**********************************************************************/ -#ifdef L_wcstoul +#if defined(L_wcstoul) || defined(L_wcstoul_l) -#if ULONG_MAX == UINTMAX_MAX +#if (ULONG_MAX == UINTMAX_MAX) && !defined(L_wcstoul_l) strong_alias(wcstoul,wcstoumax) #endif #if defined(ULLONG_MAX) && (ULLONG_MAX == ULONG_MAX) -strong_alias(wcstoul,wcstoull) +strong_alias(__XL(wcstoul),__XL(wcstoull)) #endif -unsigned long wcstoul(const wchar_t * __restrict str, - wchar_t ** __restrict endptr, int base) +unsigned long __XL(wcstoul)(const wchar_t * __restrict str, + wchar_t ** __restrict endptr, int base + __LOCALE_PARAM ) { - return _stdlib_wcsto_l(str, endptr, base, 0); + return __XL(_stdlib_wcsto_l)(str, endptr, base, 0 __LOCALE_ARG ); } #endif /**********************************************************************/ -#ifdef L_wcstoull +#if defined(L_wcstoull) || defined(L_wcstoull_l) #if defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) +#if !defined(L_wcstoull_l) #if (ULLONG_MAX == UINTMAX_MAX) strong_alias(wcstoull,wcstoumax) #endif strong_alias(wcstoull,wcstouq) +#endif -unsigned long long wcstoull(const wchar_t * __restrict str, - wchar_t ** __restrict endptr, int base) +unsigned long long __XL(wcstoull)(const wchar_t * __restrict str, + wchar_t ** __restrict endptr, int base + __LOCALE_PARAM ) { - return _stdlib_wcsto_ll(str, endptr, base, 0); + return __XL(_stdlib_wcsto_ll)(str, endptr, base, 0 __LOCALE_ARG ); } #endif /* defined(ULLONG_MAX) && (LLONG_MAX > LONG_MAX) */ diff --git a/libc/stdlib/strtod.c b/libc/stdlib/strtod.c index 7359d5cf9..c4d95bab1 100644 --- a/libc/stdlib/strtod.c +++ b/libc/stdlib/strtod.c @@ -1,263 +1,618 @@ -/* - * Copyright (C) 2000 Manuel Novoa III +/* Copyright (C) 2000, 2003 Manuel Novoa III * - * Notes: + * 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. * - * The primary objective of this implementation was minimal size while - * providing robustness and resonable accuracy. + * 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. + */ + + +/* Notes: + * + * The primary objective of this implementation was minimal size and + * portablility, while providing robustness and resonable accuracy. * * This implementation depends on IEEE floating point behavior and expects * to be able to generate +/- infinity as a result. * * There are a number of compile-time options below. + */ + +/* July 27, 2003 + * + * General cleanup and some minor size optimizations. + * Change implementation to support __strtofpmax() rather than strtod(). + * Now all the strto{floating pt}() funcs are implemented in terms of + * of the internal __strtofpmax() function. + * Support "nan", "inf", and "infinity" strings (case-insensitive). + * Support hexadecimal floating point notation. + * Support wchar variants. + * Support xlocale variants. + * + * TODO: * + * Consider accumulating blocks of digits in longs to save floating pt mults. + * This would likely be much better on anything that only supported floats + * where DECIMAL_DIG == 9. Actually, if floats have FLT_MAX_10_EXP == 38, + * we could calculate almost all the exponent multipliers (p_base) in + * long arithmetic as well. */ -/*****************************************************************************/ -/* OPTIONS */ -/*****************************************************************************/ +/**********************************************************************/ +/* OPTIONS */ +/**********************************************************************/ -/* Set if we want to scale with a O(log2(exp)) multiplications. */ -#define _STRTOD_LOG_SCALING 1 +/* Defined if we want to recognize "nan", "inf", and "infinity". (C99) */ +#define _STRTOD_NAN_INF_STRINGS 1 -/* Set if we want strtod to set errno appropriately. */ -/* NOTE: Implies all options below and pulls in _zero_or_inf_check. */ -#define _STRTOD_ERRNO 0 +/* Defined if we want support hexadecimal floating point notation. (C99) */ +/* Note! Now controlled by uClibc configuration. See below. */ +#define _STRTOD_HEXADECIMAL_FLOATS 1 -/* Set if we want support for the endptr arg. */ +/* Defined if we want to scale with a O(log2(exp)) multiplications. + * This is generally a good thing to do unless you are really tight + * on space and do not expect to convert values of large magnitude. */ + +#define _STRTOD_LOG_SCALING 1 + +/* WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! + * + * Clearing any of the options below this point is not advised (or tested). + * + * WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! */ + +/* Defined if we want strtod to set errno appropriately. */ +/* NOTE: Implies all options below. */ +#define _STRTOD_ERRNO 1 + +/* Defined if we want support for the endptr arg. */ /* Implied by _STRTOD_ERRNO. */ -#define _STRTOD_ENDPTR 1 +#define _STRTOD_ENDPTR 1 -/* Set if we want to prevent overflow in accumulating the exponent. */ -#define _STRTOD_RESTRICT_EXP 1 +/* Defined if we want to prevent overflow in accumulating the exponent. */ +/* Implied by _STRTOD_ERRNO. */ +#define _STRTOD_RESTRICT_EXP 1 -/* Set if we want to process mantissa digits more intelligently. */ +/* Defined if we want to process mantissa digits more intelligently. */ /* Implied by _STRTOD_ERRNO. */ #define _STRTOD_RESTRICT_DIGITS 1 -/* Set if we want to skip scaling 0 for the exponent. */ +/* Defined if we want to skip scaling 0 for the exponent. */ /* Implied by _STRTOD_ERRNO. */ -#define _STRTOD_ZERO_CHECK 0 +#define _STRTOD_ZERO_CHECK 1 -/*****************************************************************************/ -/* Don't change anything that follows. */ -/*****************************************************************************/ +/**********************************************************************/ +/* Don't change anything that follows. */ +/**********************************************************************/ -#if _STRTOD_ERRNO +#ifdef _STRTOD_ERRNO #undef _STRTOD_ENDPTR #undef _STRTOD_RESTRICT_EXP #undef _STRTOD_RESTRICT_DIGITS #undef _STRTOD_ZERO_CHECK -#define _STRTOD_ENDPTR 1 -#define _STRTOD_RESTRICT_EXP 1 +#define _STRTOD_ENDPTR 1 +#define _STRTOD_RESTRICT_EXP 1 #define _STRTOD_RESTRICT_DIGITS 1 -#define _STRTOD_ZERO_CHECK 1 +#define _STRTOD_ZERO_CHECK 1 #endif -/*****************************************************************************/ +/**********************************************************************/ +#define _ISOC99_SOURCE 1 +#define _GNU_SOURCE #include <stdlib.h> - +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> #include <float.h> +#include <bits/uClibc_fpmax.h> + +#include <locale.h> -#if _STRTOD_RESTRICT_DIGITS -#define MAX_SIG_DIGITS 20 -#define EXP_DENORM_ADJUST MAX_SIG_DIGITS -#define MAX_ALLOWED_EXP (MAX_SIG_DIGITS + EXP_DENORM_ADJUST - DBL_MIN_10_EXP) +#ifdef __UCLIBC_HAS_WCHAR__ + +#include <wchar.h> +#include <wctype.h> +#include <bits/uClibc_uwchar.h> -#if DBL_DIG > MAX_SIG_DIGITS -#error need to adjust MAX_SIG_DIGITS #endif -#include <limits.h> +#ifdef __UCLIBC_HAS_XLOCALE__ +#include <xlocale.h> +#endif /* __UCLIBC_HAS_XLOCALE__ */ + + + +/* Handle _STRTOD_HEXADECIMAL_FLOATS via uClibc config now. */ +#undef _STRTOD_HEXADECIMAL_FLOATS +#ifdef __UCLIBC_HAS_HEXADECIMAL_FLOATS__ +#define _STRTOD_HEXADECIMAL_FLOATS 1 +#endif /* __UCLIBC_HAS_HEXADECIMAL_FLOATS__ */ + +/**********************************************************************/ + +#undef _STRTOD_FPMAX + +#if FPMAX_TYPE == 3 + +#define NEED_STRTOLD_WRAPPER +#define NEED_STRTOD_WRAPPER +#define NEED_STRTOF_WRAPPER + +#elif FPMAX_TYPE == 2 + +#define NEED_STRTOD_WRAPPER +#define NEED_STRTOF_WRAPPER + +#elif FPMAX_TYPE == 1 + +#define NEED_STRTOF_WRAPPER + +#else + +#error unknown FPMAX_TYPE! + +#endif + +extern void __fp_range_check(__fpmax_t y, __fpmax_t x); + +/**********************************************************************/ + +#ifdef _STRTOD_RESTRICT_DIGITS +#define EXP_DENORM_ADJUST DECIMAL_DIG +#define MAX_ALLOWED_EXP (DECIMAL_DIG + EXP_DENORM_ADJUST - FPMAX_MIN_10_EXP) + #if MAX_ALLOWED_EXP > INT_MAX #error size assumption violated for MAX_ALLOWED_EXP #endif #else /* We want some excess if we're not restricting mantissa digits. */ -#define MAX_ALLOWED_EXP ((20 - DBL_MIN_10_EXP) * 2) +#define MAX_ALLOWED_EXP ((20 - FPMAX_MIN_10_EXP) * 2) #endif -#include <ctype.h> -/* Note: For i386 the macro resulted in smaller code than the function call. */ -#if 1 -#undef isdigit -#define isdigit(x) ( (x >= '0') && (x <= '9') ) + +#if defined(_STRTOD_RESTRICT_DIGITS) || defined(_STRTOD_ENDPTR) || defined(_STRTOD_HEXADECIMAL_FLOATS) +#undef _STRTOD_NEED_NUM_DIGITS +#define _STRTOD_NEED_NUM_DIGITS 1 #endif -#if _STRTOD_ERRNO -#include <errno.h> -extern int _zero_or_inf_check(double x); +/**********************************************************************/ +#if defined(L___strtofpmax) || defined(L___strtofpmax_l) || defined(L___wcstofpmax) || defined(L___wcstofpmax_l) + +#if defined(L___wcstofpmax) || defined(L___wcstofpmax_l) + +#define __strtofpmax __wcstofpmax +#define __strtofpmax_l __wcstofpmax_l + +#define Wchar wchar_t +#ifdef __UCLIBC_DO_XLOCALE +#define ISSPACE(C) iswspace_l((C), locale_arg) +#else +#define ISSPACE(C) iswspace((C)) +#endif + +#else /* defined(L___wcstofpmax) || defined(L___wcstofpmax) */ + +#define Wchar char +#ifdef __UCLIBC_DO_XLOCALE +#define ISSPACE(C) isspace_l((C), locale_arg) +#else +#define ISSPACE(C) isspace((C)) #endif -double strtod(const char *str, char **endptr) +#endif /* defined(L___wcstofpmax) || defined(L___wcstofpmax) */ + + +#if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) + +__fpmax_t __strtofpmax(const Wchar *str, Wchar **endptr, int exponent_power) { - double number; -#if _STRTOD_LOG_SCALING - double p10; + return __strtofpmax_l(str, endptr, exponent_power, __UCLIBC_CURLOCALE); +} + +#else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ + +__fpmax_t __XL(__strtofpmax)(const Wchar *str, Wchar **endptr, int exponent_power + __LOCALE_PARAM ) +{ + __fpmax_t number; + __fpmax_t p_base = 10; /* Adjusted to 16 in the hex case. */ + Wchar *pos0; +#ifdef _STRTOD_ENDPTR + Wchar *pos1; #endif - char *pos0; -#if _STRTOD_ENDPTR - char *pos1; + Wchar *pos = (Wchar *) str; + int exponent_temp; + int negative; /* A flag for the number, a multiplier for the exponent. */ +#ifdef _STRTOD_NEED_NUM_DIGITS + int num_digits; +#endif +#ifdef __UCLIBC_HAS_LOCALE__ + const char *decpt = __LOCALE_PTR->decimal_point; +#if defined(L___wcstofpmax) || defined(L___wcstofpmax) + wchar_t decpt_wc = __LOCALE_PTR->decimal_point; +#else + int decpt_len = __LOCALE_PTR->decimal_point_len; #endif - char *pos = (char *) str; - int exponent_power; - int exponent_temp; - int negative; -#if _STRTOD_RESTRICT_DIGITS || _STRTOD_ENDPTR - int num_digits; #endif - while (isspace(*pos)) { /* skip leading whitespace */ - ++pos; - } +#ifdef _STRTOD_HEXADECIMAL_FLOATS + Wchar expchar = 'e'; + Wchar *poshex = NULL; + __uint16_t is_mask = _ISdigit; +#define EXPCHAR expchar +#define IS_X_DIGIT(C) __isctype((C), is_mask) +#else /* _STRTOD_HEXADECIMAL_FLOATS */ +#define EXPCHAR 'e' +#define IS_X_DIGIT(C) isdigit((C)) +#endif /* _STRTOD_HEXADECIMAL_FLOATS */ + + while (ISSPACE(*pos)) { /* Skip leading whitespace. */ + ++pos; + } + + negative = 0; + switch(*pos) { /* Handle optional sign. */ + case '-': negative = 1; /* Fall through to increment position. */ + case '+': ++pos; + } - negative = 0; - switch(*pos) { /* handle optional sign */ - case '-': negative = 1; /* fall through to increment position */ - case '+': ++pos; - } +#ifdef _STRTOD_HEXADECIMAL_FLOATS + if ((*pos == '0') && (((pos[1])|0x20) == 'x')) { + poshex = ++pos; /* Save position of 'x' in case no digits */ + ++pos; /* and advance past it. */ + is_mask = _ISxdigit; /* Used by IS_X_DIGIT. */ + expchar = 'p'; /* Adjust exponent char. */ + p_base = 16; /* Adjust base multiplier. */ + } +#endif - number = 0.; -#if _STRTOD_RESTRICT_DIGITS || _STRTOD_ENDPTR - num_digits = -1; + number = 0.; +#ifdef _STRTOD_NEED_NUM_DIGITS + num_digits = -1; #endif - exponent_power = 0; - pos0 = NULL; +/* exponent_power = 0; */ + pos0 = NULL; LOOP: - while (isdigit(*pos)) { /* process string of digits */ -#if _STRTOD_RESTRICT_DIGITS - if (num_digits < 0) { /* first time through? */ - ++num_digits; /* we've now seen a digit */ + while (IS_X_DIGIT(*pos)) { /* Process string of (hex) digits. */ +#ifdef _STRTOD_RESTRICT_DIGITS + if (num_digits < 0) { /* First time through? */ + ++num_digits; /* We've now seen a digit. */ + } + if (num_digits || (*pos != '0')) { /* Had/have nonzero. */ + ++num_digits; + if (num_digits <= DECIMAL_DIG) { /* Is digit significant? */ +#ifdef _STRTOD_HEXADECIMAL_FLOATS + number = number * p_base + + (isdigit(*pos) + ? (*pos - '0') + : (((*pos)|0x20) - ('a' - 10))); +#else /* _STRTOD_HEXADECIMAL_FLOATS */ + number = number * p_base + (*pos - '0'); +#endif /* _STRTOD_HEXADECIMAL_FLOATS */ + } + } +#else /* _STRTOD_RESTRICT_DIGITS */ +#ifdef _STRTOD_NEED_NUM_DIGITS + ++num_digits; +#endif +#ifdef _STRTOD_HEXADECIMAL_FLOATS + number = number * p_base + + (isdigit(*pos) + ? (*pos - '0') + : (((*pos)|0x20) - ('a' - 10))); +#else /* _STRTOD_HEXADECIMAL_FLOATS */ + number = number * p_base + (*pos - '0'); +#endif /* _STRTOD_HEXADECIMAL_FLOATS */ +#endif /* _STRTOD_RESTRICT_DIGITS */ + ++pos; } - if (num_digits || (*pos != '0')) { /* had/have nonzero */ - ++num_digits; - if (num_digits <= MAX_SIG_DIGITS) { /* is digit significant */ - number = number * 10. + (*pos - '0'); - } + +#ifdef __UCLIBC_HAS_LOCALE__ +#if defined(L___wcstofpmax) || defined(L___wcstofpmax) + if (!pos0 && (*pos == decpt_wc)) { /* First decimal point? */ + pos0 = ++pos; + goto LOOP; } #else -#if _STRTOD_ENDPTR - ++num_digits; + if (!pos0 && !memcmp(pos, decpt, decpt_len)) { /* First decimal point? */ + pos0 = (pos += decpt_len); + goto LOOP; + } #endif - number = number * 10. + (*pos - '0'); +#else /* __UCLIBC_HAS_LOCALE__ */ + if ((*pos == '.') && !pos0) { /* First decimal point? */ + pos0 = ++pos; /* Save position of decimal point */ + goto LOOP; /* and process rest of digits. */ + } +#endif /* __UCLIBC_HAS_LOCALE__ */ + +#ifdef _STRTOD_NEED_NUM_DIGITS + if (num_digits<0) { /* Must have at least one digit. */ +#ifdef _STRTOD_HEXADECIMAL_FLOATS + if (poshex) { /* Back up to '0' in '0x' prefix. */ + pos = poshex; + goto DONE; + } +#endif /* _STRTOD_HEXADECIMAL_FLOATS */ + +#ifdef _STRTOD_NAN_INF_STRINGS + if (!pos0) { /* No decimal point, so check for inf/nan. */ + /* Note: nan is the first string so 'number = i/0.;' works. */ + static const char nan_inf_str[] = "\05nan\0\012infinity\0\05inf\0"; + int i = 0; + +#ifdef __UCLIBC_HAS_LOCALE__ + /* Avoid tolower problems for INFINITY in the tr_TR locale. (yuk)*/ +#undef _tolower +#define _tolower(C) ((C)|0x20) +#endif /* __UCLIBC_HAS_LOCALE__ */ + + do { + /* Unfortunately, we have no memcasecmp(). */ + int j = 0; + while (_tolower(pos[j]) == nan_inf_str[i+1+j]) { + ++j; + if (!nan_inf_str[i+1+j]) { + number = i / 0.; + if (negative) { /* Correct for sign. */ + number = -number; + } + pos += nan_inf_str[i] - 2; + goto DONE; + } + } + i += nan_inf_str[i]; + } while (nan_inf_str[i]); + } + +#endif /* STRTOD_NAN_INF_STRINGS */ +#ifdef _STRTOD_ENDPTR + pos = (Wchar *) str; #endif - ++pos; - } - - if ((*pos == '.') && !pos0) { /* is this the first decimal point? */ - pos0 = ++pos; /* save position of decimal point */ - goto LOOP; /* and process rest of digits */ - } + goto DONE; + } +#endif /* _STRTOD_NEED_NUM_DIGITS */ -#if _STRTOD_ENDPTR - if (num_digits<0) { /* must have at least one digit */ - pos = (char *) str; - goto DONE; - } +#ifdef _STRTOD_RESTRICT_DIGITS + if (num_digits > DECIMAL_DIG) { /* Adjust exponent for skipped digits. */ + exponent_power += num_digits - DECIMAL_DIG; + } #endif -#if _STRTOD_RESTRICT_DIGITS - if (num_digits > MAX_SIG_DIGITS) { /* adjust exponent for skipped digits */ - exponent_power += num_digits - MAX_SIG_DIGITS; - } -#endif + if (pos0) { + exponent_power += pos0 - pos; /* Adjust exponent for decimal point. */ + } + +#ifdef _STRTOD_HEXADECIMAL_FLOATS + if (poshex) { + exponent_power *= 4; /* Above is 2**4, but below is 2. */ + p_base = 2; + } +#endif /* _STRTOD_HEXADECIMAL_FLOATS */ - if (pos0) { - exponent_power += pos0 - pos; /* adjust exponent for decimal point */ - } + if (negative) { /* Correct for sign. */ + number = -number; + } - if (negative) { /* correct for sign */ - number = -number; - negative = 0; /* reset for exponent processing below */ - } + /* process an exponent string */ + if (((*pos)|0x20) == EXPCHAR) { +#ifdef _STRTOD_ENDPTR + pos1 = pos; +#endif + negative = 1; + switch(*++pos) { /* Handle optional sign. */ + case '-': negative = -1; /* Fall through to increment pos. */ + case '+': ++pos; + } + + pos0 = pos; + exponent_temp = 0; + while (isdigit(*pos)) { /* Process string of digits. */ +#ifdef _STRTOD_RESTRICT_EXP + if (exponent_temp < MAX_ALLOWED_EXP) { /* Avoid overflow. */ + exponent_temp = exponent_temp * 10 + (*pos - '0'); + } +#else + exponent_temp = exponent_temp * 10 + (*pos - '0'); +#endif + ++pos; + } - /* process an exponent string */ - if (*pos == 'e' || *pos == 'E') { -#if _STRTOD_ENDPTR - pos1 = pos; +#ifdef _STRTOD_ENDPTR + if (pos == pos0) { /* No digits? */ + pos = pos1; /* Back up to {e|E}/{p|P}. */ + } /* else */ #endif - switch(*++pos) { /* handle optional sign */ - case '-': negative = 1; /* fall through to increment pos */ - case '+': ++pos; + + exponent_power += negative * exponent_temp; } - pos0 = pos; - exponent_temp = 0; - while (isdigit(*pos)) { /* process string of digits */ -#if _STRTOD_RESTRICT_EXP - if (exponent_temp < MAX_ALLOWED_EXP) { /* overflow check */ - exponent_temp = exponent_temp * 10 + (*pos - '0'); - } -#else - exponent_temp = exponent_temp * 10 + (*pos - '0'); +#ifdef _STRTOD_ZERO_CHECK + if (number == 0.) { + goto DONE; + } #endif - ++pos; + + /* scale the result */ +#ifdef _STRTOD_LOG_SCALING + exponent_temp = exponent_power; + + if (exponent_temp < 0) { + exponent_temp = -exponent_temp; } -#if _STRTOD_ENDPTR - if (pos == pos0) { /* were there no digits? */ - pos = pos1; /* back up to e|E */ - } /* else */ + while (exponent_temp) { + if (exponent_temp & 1) { + if (exponent_power < 0) { + /* Warning... caluclating a factor for the exponent and + * then dividing could easily be faster. But doing so + * might cause problems when dealing with denormals. */ + number /= p_base; + } else { + number *= p_base; + } + } + exponent_temp >>= 1; + p_base *= p_base; + } + +#else /* _STRTOD_LOG_SCALING */ + while (exponent_power) { + if (exponent_power < 0) { + number /= p_base; + exponent_power++; + } else { + number *= p_base; + exponent_power--; + } + } +#endif /* _STRTOD_LOG_SCALING */ + +#ifdef _STRTOD_ERRNO + if (__FPMAX_ZERO_OR_INF_CHECK(number)) { + __set_errno(ERANGE); + } #endif - if (negative) { - exponent_power -= exponent_temp; - } else { - exponent_power += exponent_temp; + + DONE: +#ifdef _STRTOD_ENDPTR + if (endptr) { + *endptr = pos; } - } - -#if _STRTOD_ZERO_CHECK - if (number == 0.) { - goto DONE; - } -#endif - - /* scale the result */ -#if _STRTOD_LOG_SCALING - exponent_temp = exponent_power; - p10 = 10.; - - if (exponent_temp < 0) { - exponent_temp = -exponent_temp; - } - - while (exponent_temp) { - if (exponent_temp & 1) { - if (exponent_power < 0) { - number /= p10; - } else { - number *= p10; - } +#endif + + return number; +} + +#endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ + +#endif +/**********************************************************************/ +#ifdef L___fp_range_check +#if defined(NEED_STRTOF_WRAPPER) || defined(NEED_STRTOD_WRAPPER) + +extern void __fp_range_check(__fpmax_t y, __fpmax_t x) +{ + if (__FPMAX_ZERO_OR_INF_CHECK(y) /* y is 0 or +/- infinity */ + && (y != 0) /* y is not 0 (could have x>0, y==0 if underflow) */ + && !__FPMAX_ZERO_OR_INF_CHECK(x) /* x is not 0 or +/- infinity */ + ) { + __set_errno(ERANGE); /* Then x is not in y's range. */ } - exponent_temp >>= 1; - p10 *= p10; - } +} + +#endif +#endif +/**********************************************************************/ +#if defined(L_strtof) || defined(L_strtof_l) || defined(L_wcstof) || defined(L_wcstof_l) +#if defined(NEED_STRTOF_WRAPPER) + +#if defined(L_wcstof) || defined(L_wcstof_l) +#define strtof wcstof +#define strtof_l wcstof_l +#define __strtofpmax __wcstofpmax +#define __strtofpmax_l __wcstofpmax_l +#define Wchar wchar_t #else - while (exponent_power) { - if (exponent_power < 0) { - number /= 10.; - exponent_power++; - } else { - number *= 10.; - exponent_power--; - } - } +#define Wchar char #endif -#if _STRTOD_ERRNO - if (_zero_or_inf_check(number)) { - __set_errno(ERANGE); - } + +float __XL(strtof)(const Wchar *str, Wchar **endptr __LOCALE_PARAM ) +{ +#if FPMAX_TYPE == 1 + return __XL(__strtofpmax)(str, endptr, 0 __LOCALE_ARG ); +#else + __fpmax_t x; + float y; + + x = __XL(__strtofpmax)(str, endptr, 0 __LOCALE_ARG ); + y = (float) x; + + __fp_range_check(y, x); + + return y; #endif +} - DONE: -#if _STRTOD_ENDPTR - if (endptr) { - *endptr = pos; - } #endif +#endif +/**********************************************************************/ +#if defined(L_strtod) || defined(L_strtod_l) || defined(L_wcstod) || defined(L_wcstod_l) +#if defined(NEED_STRTOD_WRAPPER) + +#if defined(L_wcstod) || defined(L_wcstod_l) +#define strtod wcstod +#define strtod_l wcstod_l +#define __strtofpmax __wcstofpmax +#define __strtofpmax_l __wcstofpmax_l +#define Wchar wchar_t +#else +#define Wchar char +#endif + +double __XL(strtod)(const Wchar *str, Wchar **endptr __LOCALE_PARAM ) +{ +#if FPMAX_TYPE == 2 + return __XL(__strtofpmax)(str, endptr, 0 __LOCALE_ARG ); +#else + __fpmax_t x; + double y; + + x = __XL(__strtofpmax)(str, endptr, 0 __LOCALE_ARG ); + y = (double) x; + + __fp_range_check(y, x); - return number; + return y; +#endif } + +#endif +#endif +/**********************************************************************/ +#if defined(L_strtold) || defined(L_strtold_l) || defined(L_wcstold) || defined(L_wcstold_l) +#if defined(NEED_STRTOLD_WRAPPER) + +#if defined(L_wcstold) || defined(L_wcstold_l) +#define strtold wcstold +#define strtold_l wcstold_l +#define __strtofpmax __wcstofpmax +#define __strtofpmax_l __wcstofpmax_l +#define Wchar wchar_t +#else +#define Wchar char +#endif + +long double __XL(strtold)(const Wchar *str, Wchar **endptr __LOCALE_PARAM ) +{ +#if FPMAX_TYPE == 3 + return __XL(__strtofpmax)(str, endptr, 0 __LOCALE_ARG ); +#else + __fpmax_t x; + long double y; + + x = __XL(__strtofpmax)(str, endptr, 0 __LOCALE_ARG ); + y = (long double) x; + + __fp_range_check(y, x); + + return y; +#endif +} + +#endif +#endif +/**********************************************************************/ diff --git a/libc/string/Makefile b/libc/string/Makefile index 5ec0924d8..1b12924d5 100644 --- a/libc/string/Makefile +++ b/libc/string/Makefile @@ -24,8 +24,8 @@ TOPDIR=../../ include $(TOPDIR)Rules.mak -MSRCW= wstring.c -MOBJW= basename.o bcopy.o bzero.o dirname.o ffs.o memccpy.o memchr.o memcmp.o \ +MSRC= wstring.c +MOBJ= basename.o bcopy.o bzero.o dirname.o ffs.o memccpy.o memchr.o memcmp.o \ memcpy.o memmove.o mempcpy.o memrchr.o memset.o rawmemchr.o stpcpy.o \ stpncpy.o strcasecmp.o strcasestr.o strcat.o strchrnul.o strchr.o \ strcmp.o strcpy.o strcspn.o strdup.o strlen.o strncasecmp.o strncat.o \ @@ -35,17 +35,27 @@ MOBJW= basename.o bcopy.o bzero.o dirname.o ffs.o memccpy.o memchr.o memcmp.o \ _string_syssigmsgs.o sys_siglist.o strsignal.o psignal.o \ __xpg_basename.o strlcat.o strlcpy.o sys_errlist.o memmem.o -MOBJW2= wcscasecmp.o wcscat.o wcschrnul.o wcschr.o wcscmp.o wcscpy.o wcscspn.o \ +MOBJW= wcscasecmp.o wcscat.o wcschrnul.o wcschr.o wcscmp.o wcscpy.o wcscspn.o \ wcsdup.o wcslen.o wcsncasecmp.o wcsncat.o wcsncmp.o wcsncpy.o \ wcsnlen.o wcspbrk.o wcsrchr.o wcsspn.o wcsstr.o wcstok.o wmemchr.o \ wmemcmp.o wmemcpy.o wmemmove.o wmempcpy.o wmemset.o wcpcpy.o wcpncpy.o \ __wcslcpy.o \ wcsxfrm.o strxfrm.o # wcscoll strcoll.o -OBJS=$(MOBJ) $(MOBJ1) $(MOBJ2) $(MOBJW) +MOBJx= +MOBJWx= + +ifeq ($(UCLIBC_HAS_XLOCALE),y) + MOBJx += strcasecmp_l.o strncasecmp_l.o + MOBJWx += wcscasecmp_l.o wcsncasecmp_l.o wcsxfrm_l.o strxfrm_l.o +endif + +#ffsl ffsll + +OBJS=$(MOBJ) $(MOBJx) ifeq ($(UCLIBC_HAS_WCHAR),y) - OBJS += $(MOBJW2) + OBJS += $(MOBJW) $(MOBJWx) endif all: $(OBJS) $(LIBC) @@ -55,14 +65,22 @@ $(LIBC): ar-target ar-target: $(OBJS) $(AR) $(ARFLAGS) $(LIBC) $(OBJS) -$(MOBJW): $(MSRCW) +$(MOBJ): $(MSRC) $(CC) $(CFLAGS) -DL_$* $< -c -o $*.o $(STRIPTOOL) -x -R .note -R .comment $*.o -$(MOBJW2): $(MSRCW) +$(MOBJx): $(MSRC) + $(CC) $(CFLAGS) -DL_$* -D__UCLIBC_DO_XLOCALE $< -c -o $*.o + $(STRIPTOOL) -x -R .note -R .comment $*.o + +$(MOBJW): $(MSRC) $(CC) $(CFLAGS) -DWANT_WIDE -DL_$* $< -c -o $*.o $(STRIPTOOL) -x -R .note -R .comment $*.o +$(MOBJWx): $(MSRC) + $(CC) $(CFLAGS) -DWANT_WIDE -DL_$* -D__UCLIBC_DO_XLOCALE $< -c -o $*.o + $(STRIPTOOL) -x -R .note -R .comment $*.o + $(COBJS): %.o : %.c $(CC) $(CFLAGS) -c $< -o $@ $(STRIPTOOL) -x -R .note -R .comment $*.o diff --git a/libc/string/wstring.c b/libc/string/wstring.c index 343e82b6c..1343bf98b 100644 --- a/libc/string/wstring.c +++ b/libc/string/wstring.c @@ -49,6 +49,7 @@ #ifdef WANT_WIDE #include <wchar.h> #include <wctype.h> +#include <bits/uClibc_uwchar.h> #define Wvoid wchar_t #define Wchar wchar_t @@ -77,12 +78,22 @@ typedef unsigned char __string_uchar_t; #define _SYS_NERR 126 #endif +#ifdef __UCLIBC_HAS_ERRNO_MESSAGES__ #define _SYS_ERRMSG_MAXLEN 50 +#else /* __UCLIBC_HAS_ERRNO_MESSAGES__ */ +#define _SYS_ERRMSG_MAXLEN 0 +#endif /* __UCLIBC_HAS_ERRNO_MESSAGES__ */ + extern const char _string_syserrmsgs[]; #define _SYS_NSIG 32 + +#ifdef __UCLIBC_HAS_SIGNUM_MESSAGES__ #define _SYS_SIGMSG_MAXLEN 25 +#else /* __UCLIBC_HAS_SIGNUM_MESSAGES__ */ +#define _SYS_SIGMSG_MAXLEN 0 +#endif /* __UCLIBC_HAS_SIGNUM_MESSAGES__ */ extern const char _string_syssigmsgs[]; @@ -93,14 +104,14 @@ extern const char _string_syssigmsgs[]; #define _STRERROR_BUFSIZE _SYS_ERRMSG_MAXLEN #endif -#if _SYS_SIGMSG_MAXLEN < __UIM_BUFLEN_INT + 14 -#define _STRSIGNAL_BUFSIZE (__UIM_BUFLEN_INT + 14) +#if _SYS_SIGMSG_MAXLEN < __UIM_BUFLEN_INT + 15 +#define _STRSIGNAL_BUFSIZE (__UIM_BUFLEN_INT + 15) #else #define _STRSIGNAL_BUFSIZE _SYS_SIGMSG_MAXLEN #endif /**********************************************************************/ -#ifdef L__string_syserrmsgs +#if defined(L__string_syserrmsgs) && defined(__UCLIBC_HAS_ERRNO_MESSAGES__) const char _string_syserrmsgs[] = { /* 0: 0, 8 */ "Success\0" @@ -238,7 +249,7 @@ const char _string_syserrmsgs[] = { #endif /**********************************************************************/ -#ifdef L_sys_errlist +#if defined(L_sys_errlist) && defined(__UCLIBC_HAS_SYS_ERRLIST__) link_warning(_sys_errlist, "sys_nerr and sys_errlist are obsolete and uClibc support for them (in at least some configurations) will probably be unavailable in the near future.") @@ -1070,54 +1081,104 @@ int ffs(int i) #endif /**********************************************************************/ -#ifdef L_wcscasecmp -#define L_strcasecmp -#define Wstrcasecmp wcscasecmp +#if defined(L_strcasecmp) || defined(L_strcasecmp_l) || defined(L_wcscasecmp) || defined(L_wcscasecmp_l) + +#if defined(L_wcscasecmp) || defined(L_wcscasecmp_l) + +#define strcasecmp wcscasecmp +#define strcasecmp_l wcscasecmp_l +#ifdef __UCLIBC_DO_XLOCALE +#define TOLOWER(C) towlower_l((C), locale_arg) +#else +#define TOLOWER(C) towlower((C)) +#endif + +#else /* defined(L_wcscasecmp) || defined(L_wcscasecmp_l) */ + +#ifdef __UCLIBC_DO_XLOCALE +#define TOLOWER(C) tolower_l((C), locale_arg) #else -#define Wstrcasecmp strcasecmp +#define TOLOWER(C) tolower((C)) #endif -#ifdef L_strcasecmp +#endif /* defined(L_wcscasecmp) || defined(L_wcscasecmp_l) */ + + +#if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) + +int strcasecmp(register const Wchar *s1, register const Wchar *s2) +{ + return strcasecmp_l(s1, s2, __UCLIBC_CURLOCALE); +} + +#else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ -int Wstrcasecmp(register const Wchar *s1, register const Wchar *s2) +int __XL(strcasecmp)(register const Wchar *s1, register const Wchar *s2 + __LOCALE_PARAM ) { #ifdef WANT_WIDE - while ((*s1 == *s2) || (towlower(*s1) == towlower(*s2))) { + while ((*s1 == *s2) || (TOLOWER(*s1) == TOLOWER(*s2))) { if (!*s1++) { return 0; } ++s2; } - return (((Wuchar)towlower(*s1)) < ((Wuchar)towlower(*s2))) ? -1 : 1; + return (((Wuchar)TOLOWER(*s1)) < ((Wuchar)TOLOWER(*s2))) ? -1 : 1; /* TODO -- should wide cmp funcs do wchar or Wuchar compares? */ #else int r = 0; while ( ((s1 == s2) || - !(r = ((int)( tolower(*((Wuchar *)s1)))) - - tolower(*((Wuchar *)s2)))) + !(r = ((int)( TOLOWER(*((Wuchar *)s1)))) + - TOLOWER(*((Wuchar *)s2)))) && (++s2, *s1++)); return r; #endif } +#endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ + #endif /**********************************************************************/ -#ifdef L_wcsncasecmp -#define L_strncasecmp -#define Wstrncasecmp wcsncasecmp +#if defined(L_strncasecmp) || defined(L_strncasecmp_l) || defined(L_wcsncasecmp) || defined(L_wcsncasecmp_l) + +#if defined(L_wcsncasecmp) || defined(L_wcsncasecmp_l) + +#define strncasecmp wcsncasecmp +#define strncasecmp_l wcsncasecmp_l +#ifdef __UCLIBC_DO_XLOCALE +#define TOLOWER(C) towlower_l((C), locale_arg) +#else +#define TOLOWER(C) towlower((C)) +#endif + +#else /* defined(L_wcsncasecmp) || defined(L_wcsncasecmp_l) */ + +#ifdef __UCLIBC_DO_XLOCALE +#define TOLOWER(C) tolower_l((C), locale_arg) #else -#define Wstrncasecmp strncasecmp +#define TOLOWER(C) tolower((C)) #endif -#ifdef L_strncasecmp +#endif /* defined(L_wcsncasecmp) || defined(L_wcsncasecmp_l) */ + + +#if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) + +int strncasecmp(register const Wchar *s1, register const Wchar *s2, size_t n) +{ + return strncasecmp_l(s1, s2, n, __UCLIBC_CURLOCALE); +} + +#else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ -int Wstrncasecmp(register const Wchar *s1, register const Wchar *s2, size_t n) +int __XL(strncasecmp)(register const Wchar *s1, register const Wchar *s2, + size_t n __LOCALE_PARAM ) { #ifdef WANT_WIDE - while (n && ((*s1 == *s2) || (towlower(*s1) == towlower(*s2)))) { + while (n && ((*s1 == *s2) || (TOLOWER(*s1) == TOLOWER(*s2)))) { if (!*s1++) { return 0; } @@ -1127,19 +1188,22 @@ int Wstrncasecmp(register const Wchar *s1, register const Wchar *s2, size_t n) return (n == 0) ? 0 - : ((((Wuchar)towlower(*s1)) < ((Wuchar)towlower(*s2))) ? -1 : 1); + : ((((Wuchar)TOLOWER(*s1)) < ((Wuchar)TOLOWER(*s2))) ? -1 : 1); /* TODO -- should wide cmp funcs do wchar or Wuchar compares? */ #else int r = 0; while ( n && ((s1 == s2) || - !(r = ((int)( tolower(*((unsigned char *)s1)))) - - tolower(*((unsigned char *)s2)))) + !(r = ((int)( TOLOWER(*((unsigned char *)s1)))) + - TOLOWER(*((unsigned char *)s2)))) && (--n, ++s2, *s1++)); return r; #endif } + +#endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ + #endif /**********************************************************************/ #ifdef L_wcsnlen @@ -1220,7 +1284,7 @@ Wchar *Wstrdup(register const Wchar *s1) char *strerror(int errnum) { - static char buf[_SYS_ERRMSG_MAXLEN]; + static char buf[_STRERROR_BUFSIZE]; _susv3_strerror_r(errnum, buf, sizeof(buf)); @@ -1233,6 +1297,7 @@ char *strerror(int errnum) /**********************************************************************/ #ifdef L__susv3_strerror_r +#ifdef __UCLIBC_HAS_ERRNO_MESSAGES__ #if defined(__alpha__) || defined(__mips__) || defined(__sparc__) static const unsigned char estridx[] = { @@ -1372,19 +1437,20 @@ static const unsigned char estridx[] = { #endif - int _susv3_strerror_r(int errnum, char *strerrbuf, size_t buflen) { register char *s; int i, retval; - char buf[_SYS_ERRMSG_MAXLEN]; - static const char unknown[14] = { + char buf[_STRERROR_BUFSIZE]; + static const char unknown[] = { 'U', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 'e', 'r', 'r', 'o', 'r', ' ' }; retval = EINVAL; +#ifdef __UCLIBC_HAS_ERRNO_MESSAGES__ + #if defined(__alpha__) || defined(__mips__) || defined(__sparc__) /* Need to translate errno to string index. */ for (i = 0 ; i < sizeof(estridx)/sizeof(estridx[0]) ; i++) { @@ -1420,6 +1486,8 @@ int _susv3_strerror_r(int errnum, char *strerrbuf, size_t buflen) } } +#endif /* __UCLIBC_HAS_ERRNO_MESSAGES__ */ + s = _int10tostr(buf+sizeof(buf)-1, errnum) - sizeof(unknown); memcpy(s, unknown, sizeof(unknown)); @@ -1445,6 +1513,45 @@ int _susv3_strerror_r(int errnum, char *strerrbuf, size_t buflen) return retval; } +#else /* __UCLIBC_HAS_ERRNO_MESSAGES__ */ + +int _susv3_strerror_r(int errnum, char *strerrbuf, size_t buflen) +{ + register char *s; + int i, retval; + char buf[_STRERROR_BUFSIZE]; + static const char unknown[] = { + 'U', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 'e', 'r', 'r', 'o', 'r', ' ' + }; + + s = _int10tostr(buf+sizeof(buf)-1, errnum) - sizeof(unknown); + memcpy(s, unknown, sizeof(unknown)); + + if (!strerrbuf) { /* SUSv3 */ + buflen = 0; + } + + retval = EINVAL; + + i = buf + sizeof(buf) - s; + + if (i > buflen) { + i = buflen; + retval = ERANGE; + } + + if (i) { + memcpy(strerrbuf, s, i); + strerrbuf[i-1] = 0; /* In case buf was too small. */ + } + + __set_errno(retval); + + return retval; +} + +#endif /* __UCLIBC_HAS_ERRNO_MESSAGES__ */ + #endif /**********************************************************************/ /* GNU extension functions. */ @@ -1993,7 +2100,7 @@ size_t Wstrlcpy(register Wchar *__restrict dst, #endif /**********************************************************************/ -#ifdef L__string_syssigmsgs +#if defined(L__string_syssigmsgs) && defined(__UCLIBC_HAS_SIGNUM_MESSAGES__) const char _string_syssigmsgs[] = { /* 0: 0, 1 */ "\0" @@ -2033,7 +2140,7 @@ const char _string_syssigmsgs[] = { #endif /**********************************************************************/ -#ifdef L_sys_siglist +#if defined(L_sys_siglist) && defined(__UCLIBC_HAS_SYS_SIGLIST__) const char *const sys_siglist[_NSIG] = { NULL, @@ -2108,12 +2215,14 @@ const char *const sys_siglist[_NSIG] = { /* TODO: make a threadsafe version? */ +#ifdef __UCLIBC_HAS_SIGNUM_MESSAGES__ + char *strsignal(int signum) { register char *s; int i; static char buf[_STRSIGNAL_BUFSIZE]; - static const char unknown[15] = { + static const char unknown[] = { 'U', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 's', 'i', 'g', 'n', 'a', 'l', ' ' }; @@ -2139,6 +2248,22 @@ char *strsignal(int signum) return s; } +#else /* __UCLIBC_HAS_SIGNUM_MESSAGES__ */ + +char *strsignal(int signum) +{ + static char buf[_STRSIGNAL_BUFSIZE]; + static const char unknown[] = { + 'U', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 's', 'i', 'g', 'n', 'a', 'l', ' ' + }; + + return (char *) memcpy(_int10tostr(buf+sizeof(buf)-1, signum) + - sizeof(unknown), + unknown, sizeof(unknown)); +} + +#endif /* __UCLIBC_HAS_SIGNUM_MESSAGES__ */ + #endif /**********************************************************************/ #ifdef L_psignal @@ -2171,6 +2296,7 @@ void psignal(int signum, register const char *message) #endif /**********************************************************************/ #ifndef __LOCALE_C_ONLY +#if defined(L_strxfrm) || defined(L_strxfrm_l) || defined(L_wcsxfrm) || defined(L_wcsxfrm_l) #ifdef L_strxfrm #ifndef WANT_WIDE @@ -2179,11 +2305,16 @@ void psignal(int signum, register const char *message) #ifdef L_wcsxfrm #error L_wcsxfrm already defined for L_strxfrm #endif +#endif /* L_strxfrm */ -#define wcscoll strcoll -#define L_wcsxfrm -#undef WANT_WIDE +#if defined(L_strxfrm) || defined(L_strxfrm_l) +#define wcscoll strcoll +#define wcscoll_l strcoll_l +#define wcsxfrm strxfrm +#define wcsxfrm_l strxfrm_l + +#undef WANT_WIDE #undef Wvoid #undef Wchar #undef Wuchar @@ -2191,13 +2322,28 @@ void psignal(int signum, register const char *message) #define Wchar char -#endif /* L_strxfrm */ +#endif /* defined(L_strxfrm) || defined(L_strxfrm_l) */ + +#if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) + +int wcscoll (const Wchar *s0, const Wchar *s1) +{ + return wcscoll_l(s0, s1, __UCLIBC_CURLOCALE ); +} +size_t wcsxfrm(Wchar *__restrict ws1, const Wchar *__restrict ws2, size_t n) +{ + return wcsxfrm_l(ws1, ws2, n, __UCLIBC_CURLOCALE ); +} +#else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ -#ifdef L_wcsxfrm -#define CUR_COLLATE (&__global_locale.collate) +#if 0 +#define CUR_COLLATE (&__UCLIBC_CURLOCALE_DATA.collate) +#else +#define CUR_COLLATE (& __LOCALE_PTR->collate) +#endif #define MAX_PENDING 8 @@ -2246,7 +2392,7 @@ typedef struct { #define TRACE(X) ((void)0) #endif -static int lookup(wchar_t wc) +static int lookup(wchar_t wc __LOCALE_PARAM ) { unsigned int sc, n, i0, i1; @@ -2276,7 +2422,7 @@ static void init_col_state(col_state_t *cs, const Wchar *wcs) cs->bbe = cs->back_buf + (cs->bb_size -1); } -static void next_weight(col_state_t *cs, int pass) +static void next_weight(col_state_t *cs, int pass __LOCALE_PARAM ) { int r, w, ru, ri, popping_backup_stack; ssize_t n; @@ -2285,12 +2431,10 @@ static void next_weight(col_state_t *cs, int pass) #define WC (*cs->s) #define N (1) #else /* WANT_WIDE */ - mbstate_t mbstate; wchar_t WC; size_t n0, nx; #define N n0 - mbstate.mask = 0; #endif /* WANT_WIDE */ do { @@ -2343,15 +2487,15 @@ static void next_weight(col_state_t *cs, int pass) BACK_LOOP: #ifdef WANT_WIDE n = 1; - cs->colitem = r = lookup(*cs->s); + cs->colitem = r = lookup(*cs->s __LOCALE_ARG ); #else /* WANT_WIDE */ - n = n0 = mbrtowc(&WC, cs->s, SIZE_MAX, &mbstate); + n = n0 = __locale_mbrtowc_l(&WC, cs->s, __LOCALE_PTR); if (n < 0) { __set_errno(EILSEQ); cs->weight = 0; return; } - cs->colitem = r = lookup(WC); + cs->colitem = r = lookup(WC __LOCALE_ARG ); #endif /* WANT_WIDE */ TRACE((" r=%d WC=%#lx\n", r, (unsigned long)(WC))); @@ -2370,7 +2514,7 @@ static void next_weight(col_state_t *cs, int pass) } #ifdef WANT_WIDE /* the lookup check here is safe since we're assured that *p is a valid colidx */ - if (!cs->s[n] || (lookup(cs->s[n]) != *p)) { + if (!cs->s[n] || (lookup(cs->s[n] __LOCALE_ARG ) != *p)) { do {} while (*p++); break; } @@ -2378,19 +2522,19 @@ static void next_weight(col_state_t *cs, int pass) ++n; #else /* WANT_WIDE */ if (cs->s[n]) { - nx = mbrtowc(&WC, cs->s + n, SIZE_MAX, &mbstate); + nx = __locale_mbrtowc_l(&WC, cs->s + n, __LOCALE_PTR); if (nx < 0) { __set_errno(EILSEQ); cs->weight = 0; return; } } - if (!cs->s[n] || (lookup(WC) != *p)) { + if (!cs->s[n] || (lookup(WC __LOCALE_ARG ) != *p)) { do {} while (*p++); break; } ++p; - n += nx; + n += nx; /* Only gets here if cs->s[n] != 0, so nx is set. */ #endif /* WANT_WIDE */ } while (1); } while (1); @@ -2628,7 +2772,7 @@ static void next_weight(col_state_t *cs, int pass) } while (1); } -int wcscoll (const Wchar *s0, const Wchar *s1) +int __XL(wcscoll) (const Wchar *s0, const Wchar *s1 __LOCALE_PARAM ) { col_state_t ws[2]; int pass; @@ -2647,8 +2791,8 @@ int wcscoll (const Wchar *s0, const Wchar *s1) init_col_state(ws+1, s1); do { /* loop through the strings */ /* for each string, get the next weight */ - next_weight(ws, pass); - next_weight(ws+1, pass); + next_weight(ws, pass __LOCALE_ARG ); + next_weight(ws+1, pass __LOCALE_ARG ); TRACE(("w0=%lu w1=%lu\n", (unsigned long) ws[0].weight, (unsigned long) ws[1].weight)); @@ -2664,7 +2808,8 @@ int wcscoll (const Wchar *s0, const Wchar *s1) #ifdef WANT_WIDE -size_t wcsxfrm(wchar_t *__restrict ws1, const wchar_t *__restrict ws2, size_t n) +size_t __XL(wcsxfrm)(wchar_t *__restrict ws1, const wchar_t *__restrict ws2, + size_t n __LOCALE_PARAM ) { col_state_t cs; size_t count; @@ -2682,7 +2827,7 @@ size_t wcsxfrm(wchar_t *__restrict ws1, const wchar_t *__restrict ws2, size_t n) do { /* loop through the weights levels */ init_col_state(&cs, ws2); do { /* loop through the string */ - next_weight(&cs, pass); + next_weight(&cs, pass __LOCALE_ARG ); TRACE(("weight=%lu (%#lx)\n", (unsigned long) cs.weight, (unsigned long) cs.weight)); if (count < n) { ws1[count] = cs.weight +1; @@ -2743,7 +2888,8 @@ static size_t store(unsigned char *s, size_t count, size_t n, __uwchar_t weight) return r; } -size_t strxfrm(char *__restrict ws1, const char *__restrict ws2, size_t n) +size_t __XL(strxfrm)(char *__restrict ws1, const char *__restrict ws2, size_t n + __LOCALE_PARAM ) { col_state_t cs; size_t count, inc; @@ -2761,7 +2907,7 @@ size_t strxfrm(char *__restrict ws1, const char *__restrict ws2, size_t n) do { /* loop through the weights levels */ init_col_state(&cs, ws2); do { /* loop through the string */ - next_weight(&cs, pass); + next_weight(&cs, pass __LOCALE_ARG ); TRACE(("weight=%lu (%#lx)\n", (unsigned long) cs.weight, (unsigned long) cs.weight)); inc = store((unsigned char *)ws1, count, n, cs.weight + 1); count += inc; @@ -2780,11 +2926,11 @@ size_t strxfrm(char *__restrict ws1, const char *__restrict ws2, size_t n) return count-1; } - #endif /* WANT_WIDE */ -#endif /* wcscoll */ +#endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */ + +#endif /* defined(L_strxfrm) || defined(L_strxfrm_l) || defined(L_wcsxfrm) || defined(L_wcsxfrm_l) */ #endif /* __LOCALE_C_ONLY */ /**********************************************************************/ - diff --git a/libc/sysdeps/linux/common/bits/uClibc_ctype.h b/libc/sysdeps/linux/common/bits/uClibc_ctype.h index 7ff62e376..294997b74 100644 --- a/libc/sysdeps/linux/common/bits/uClibc_ctype.h +++ b/libc/sysdeps/linux/common/bits/uClibc_ctype.h @@ -33,6 +33,8 @@ #ifndef _BITS_CTYPE_H #define _BITS_CTYPE_H +#warning uClibc_ctype.h is deprecated + /* 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 diff --git a/libc/sysdeps/linux/common/bits/uClibc_fpmax.h b/libc/sysdeps/linux/common/bits/uClibc_fpmax.h new file mode 100644 index 000000000..08d47129c --- /dev/null +++ b/libc/sysdeps/linux/common/bits/uClibc_fpmax.h @@ -0,0 +1,132 @@ +/* Copyright (C) 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. + */ + +/* Define a maximal floating point type, and the associated constants + * that are defined for the floating point types in float.h. + * + * This is to support archs that are missing long double, or even double. + */ + +#ifndef _UCLIBC_FPMAX_H +#define _UCLIBC_FPMAX_H + +#ifndef _ISOC99_SOURCE +#define _ISOC99_SOURCE 1 +#endif + +#include <features.h> +#include <float.h> + +#ifdef __UCLIBC_HAS_FLOATS__ + +#if defined(LDBL_MANT_DIG) + +typedef long double __fpmax_t; +#define FPMAX_TYPE 3 + +#define FPMAX_MANT_DIG LDBL_MANT_DIG +#define FPMAX_DIG LDBL_DIG +#define FPMAX_EPSILON LDBL_EPSILON +#define FPMAX_MIN_EXP LDBL_MIN_EXP +#define FPMAX_MIN LDBL_MIN +#define FPMAX_MIN_10_EXP LDBL_MIN_10_EXP +#define FPMAX_MAX_EXP LDBL_MAX_EXP +#define FPMAX_MAX LDBL_MAX +#define FPMAX_MAX_10_EXP LDBL_MAX_10_EXP + +#elif defined(DBL_MANT_DIG) + +typedef double __fpmax_t; +#define FPMAX_TYPE 2 + +#define FPMAX_MANT_DIG DBL_MANT_DIG +#define FPMAX_DIG DBL_DIG +#define FPMAX_EPSILON DBL_EPSILON +#define FPMAX_MIN_EXP DBL_MIN_EXP +#define FPMAX_MIN DBL_MIN +#define FPMAX_MIN_10_EXP DBL_MIN_10_EXP +#define FPMAX_MAX_EXP DBL_MAX_EXP +#define FPMAX_MAX DBL_MAX +#define FPMAX_MAX_10_EXP DBL_MAX_10_EXP + +#elif defined(FLT_MANT_DIG) + +typedef float __fpmax_t; +#define FPMAX_TYPE 1 + +#define FPMAX_MANT_DIG FLT_MANT_DIG +#define FPMAX_DIG FLT_DIG +#define FPMAX_EPSILON FLT_EPSILON +#define FPMAX_MIN_EXP FLT_MIN_EXP +#define FPMAX_MIN FLT_MIN +#define FPMAX_MIN_10_EXP FLT_MIN_10_EXP +#define FPMAX_MAX_EXP FLT_MAX_EXP +#define FPMAX_MAX FLT_MAX +#define FPMAX_MAX_10_EXP FLT_MAX_10_EXP + +#else +#error unable to determine appropriate type for __fpmax_t! +#endif + +#ifndef DECIMAL_DIG + +#ifdef L___strtofpmax +/* Emit warning only once. */ +#warning DECIMAL_DIG is not defined! If you are using gcc, it may not be defining __STDC_VERSION__ as it should. +#endif +#if !defined(FLT_RADIX) || (FLT_RADIX != 2) +#error unable to compensate for missing DECIMAL_DIG! +#endif + +/* ceil (1 + #mantissa * log10 (FLT_RADIX)) */ +#define DECIMAL_DIG (1 + (((FPMAX_MANT_DIG * 100) + 331) / 332)) + +#endif /* DECIMAL_DIG */ + +extern __fpmax_t __strtofpmax(const char *str, char **endptr, int exp_adjust); + +#ifdef __UCLIBC_HAS_XLOCALE__ +extern __fpmax_t __strtofpmax_l(const char *str, char **endptr, int exp_adjust, + __locale_t locale_arg); +#endif + +#ifdef __UCLIBC_HAS_WCHAR__ +extern __fpmax_t __wcstofpmax(const wchar_t *wcs, wchar_t **endptr, + int exp_adjust); + +#ifdef __UCLIBC_HAS_XLOCALE__ +extern __fpmax_t __wcstofpmax_l(const wchar_t *wcs, wchar_t **endptr, + int exp_adjust, __locale_t locale_arg); +#endif +#endif /* __UCLIBC_HAS_WCHAR__ */ + +/* The following checks in an __fpmax_t is either 0 or +/- infinity. + * + * WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! + * + * This only works if __fpmax_t is the actual maximal floating point type used + * in intermediate calculations. Otherwise, excess precision in the + * intermediate values can cause the test to fail. + * + * WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! + */ + +#define __FPMAX_ZERO_OR_INF_CHECK(x) ((x) == ((x)/4) ) + +#endif /* __UCLIBC_HAS_FLOATS__ */ + +#endif /* _UCLIBC_FPMAX_H */ diff --git a/libc/sysdeps/linux/common/bits/uClibc_locale.h b/libc/sysdeps/linux/common/bits/uClibc_locale.h index 4e89188b8..56819ba62 100644 --- a/libc/sysdeps/linux/common/bits/uClibc_locale.h +++ b/libc/sysdeps/linux/common/bits/uClibc_locale.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2002 Manuel Novoa III +/* 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 @@ -32,18 +32,17 @@ /**********************************************************************/ /* uClibc compatibilty stuff */ -#ifdef __UCLIBC_HAS_WCHAR__ -#define __WCHAR_ENABLED -#endif - - #ifdef __UCLIBC_HAS_LOCALE__ #undef __LOCALE_C_ONLY #else /* __UCLIBC_HAS_LOCALE__ */ -#define __LOCALE_C_ONLY +#define __LOCALE_C_ONLY + +#define __XL(N) N +#define __LOCALE_PARAM +#define __LOCALE_ARG #endif /* __UCLIBC_HAS_LOCALE__ */ @@ -63,43 +62,18 @@ #define __LC_ALL 6 /**********************************************************************/ -#if defined(_LIBC) && defined(__WCHAR_ENABLED) - -/* TODO: This really needs to be somewhere else... */ -#include <limits.h> -#include <stdint.h> - -#if WCHAR_MIN == 0 -typedef wchar_t __uwchar_t; -#elif WCHAR_MAX <= USHRT_MAX -typedef unsigned short __uwchar_t; -#elif WCHAR_MAX <= UINT_MAX -typedef unsigned int __uwchar_t; -#elif WCHAR_MAX <= ULONG_MAX -typedef unsigned long __uwchar_t; -#elif defined(ULLONG_MAX) && (WCHAR_MAX <= ULLONG_MAX) -typedef unsigned long long __uwchar_t; -#elif WCHAR_MAX <= UINT_MAX -typedef uintmax_t __uwchar_t; -#else -#error Can not determine an appropriate type for __uwchar_t! -#endif - -#endif - -/**********************************************************************/ -#if defined(_LIBC) && !defined(__LOCALE_C_ONLY) +/* #if defined(_LIBC) && !defined(__LOCALE_C_ONLY) */ +#ifndef __LOCALE_C_ONLY #include <stddef.h> #include <stdint.h> -#include <bits/uClibc_locale_data.h> +#include <bits/uClibc_touplow.h> +#include <bits/uClibc_locale_data.h> extern void _locale_set(const unsigned char *p); extern void _locale_init(void); -/* TODO: assumes 8-bit chars!!! */ - enum { __ctype_encoding_7_bit, /* C/POSIX */ __ctype_encoding_utf8, /* UTF-8 */ @@ -164,6 +138,12 @@ typedef struct { /* static unsigned char cur_locale[LOCALE_STRING_SIZE]; */ typedef struct { +#ifdef __UCLIBC_HAS_XLOCALE__ + const __uint16_t *__ctype_b; + const __ctype_touplow_t *__ctype_tolower; + const __ctype_touplow_t *__ctype_toupper; +#endif + /* int tables_loaded; */ /* unsigned char lctypes[LOCALE_STRING_SIZE]; */ unsigned char cur_locale[LOCALE_STRING_SIZE]; @@ -185,21 +165,30 @@ typedef struct { const unsigned char *tbl8ctype; const unsigned char *idx8uplow; const unsigned char *tbl8uplow; -#ifdef __WCHAR_ENABLED +#ifdef __UCLIBC_HAS_WCHAR__ const unsigned char *idx8c2wc; const uint16_t *tbl8c2wc; /* char > 0x7f to wide char */ const unsigned char *idx8wc2c; const unsigned char *tbl8wc2c; /* translit */ -#endif /* __WCHAR_ENABLED */ +#endif /* __UCLIBC_HAS_WCHAR__ */ #endif /* __CTYPE_HAS_8_BIT_LOCALES */ -#ifdef __WCHAR_ENABLED +#ifdef __UCLIBC_HAS_WCHAR__ + + const uint16_t *code2flag; + const unsigned char *tblwctype; const unsigned char *tblwuplow; /* const unsigned char *tblwcomb; */ const int16_t *tblwuplow_diff; /* yes... signed */ /* width?? */ -#endif /* __WCHAR_ENABLED */ + + wchar_t decimal_point_wc; + wchar_t thousands_sep_wc; + int decimal_point_len; + int thousands_sep_len; + +#endif /* __UCLIBC_HAS_WCHAR__ */ /* ctype */ const char *outdigit0_mb; @@ -313,10 +302,68 @@ typedef struct { /* collate is at the end */ __collate_t collate; -} __locale_t; +} __uclibc_locale_t; + +typedef __uclibc_locale_t *__locale_t; extern __locale_t __global_locale; + +extern int __locale_mbrtowc_l(wchar_t *__restrict dst, + const char *__restrict src, + __locale_t loc ); + +#ifdef L_setlocale +/* so we only get the warning once... */ +#warning need thread version of CUR_LOCALE! +#endif +/**********************************************************************/ +#ifdef __UCLIBC_HAS_XLOCALE__ + +extern __locale_t __curlocale_var; + +#ifdef __UCLIBC_HAS_THREADS__ + +extern __locale_t __curlocale(void) __THROW __attribute__ ((__const__)); +extern __locale_t __curlocale_set(__locale_t new); +#define __UCLIBC_CURLOCALE (__curlocale()) +#define __UCLIBC_CURLOCALE_DATA (*__curlocale()) + +#else /* __UCLIBC_HAS_THREADS__ */ + +#define __UCLIBC_CURLOCALE (__curlocale_var) +#define __UCLIBC_CURLOCALE_DATA (*__curlocale_var) + +#endif /* __UCLIBC_HAS_THREADS__ */ + +#elif defined(__UCLIBC_HAS_LOCALE__) + +#define __UCLIBC_CURLOCALE (__global_locale) +#define __UCLIBC_CURLOCALE_DATA (*__global_locale) + +#endif +/**********************************************************************/ +#if defined(__UCLIBC_HAS_XLOCALE__) && defined(__UCLIBC_DO_XLOCALE) + +#define __XL(N) N ## _l +#define __LOCALE_PARAM , __locale_t locale_arg +#define __LOCALE_ARG , locale_arg +#define __LOCALE_PTR locale_arg + +#else /* defined(__UCLIBC_HAS_XLOCALE__) && defined(__STDLIB_DO_XLOCALE) */ + +#define __XL(N) N +#define __LOCALE_PARAM +#define __LOCALE_ARG +#define __LOCALE_PTR __UCLIBC_CURLOCALE + +#endif /* defined(__UCLIBC_HAS_XLOCALE__) && defined(__STDLIB_DO_XLOCALE) */ +/**********************************************************************/ + +extern __locale_t __newlocale(int category_mask, const char *locale, __locale_t base) + __THROW; + #endif /* defined(_LIBC) && !defined(__LOCALE_C_ONLY) */ +/**********************************************************************/ #endif /* _UCLIBC_LOCALE_H */ diff --git a/libc/sysdeps/linux/common/bits/uClibc_stdio.h b/libc/sysdeps/linux/common/bits/uClibc_stdio.h index 59156eeea..6c0cff7ed 100644 --- a/libc/sysdeps/linux/common/bits/uClibc_stdio.h +++ b/libc/sysdeps/linux/common/bits/uClibc_stdio.h @@ -44,6 +44,79 @@ #define __STDIO_WIDE #endif +#define __STDIO_BUFFERS +/* ANSI/ISO mandate at least 256. */ +#if defined(__UCLIBC_HAS_STDIO_BUFSIZ_NONE__) +/* Fake this because some apps use stdio.h BUFSIZ. */ +#define _STDIO_BUFSIZ 256 +#undef __STDIO_BUFFERS +#elif defined(__UCLIBC_HAS_STDIO_BUFSIZ_256__) +#define _STDIO_BUFSIZ 256 +#elif defined(__UCLIBC_HAS_STDIO_BUFSIZ_512__) +#define _STDIO_BUFSIZ 512 +#elif defined(__UCLIBC_HAS_STDIO_BUFSIZ_1024__) +#define _STDIO_BUFSIZ 1024 +#elif defined(__UCLIBC_HAS_STDIO_BUFSIZ_2048__) +#define _STDIO_BUFSIZ 2048 +#elif defined(__UCLIBC_HAS_STDIO_BUFSIZ_4096__) +#define _STDIO_BUFSIZ 4096 +#elif defined(__UCLIBC_HAS_STDIO_BUFSIZ_8192__) +#define _STDIO_BUFSIZ 8192 +#else +#error config seems to be out of sync regarding bufsiz options +#endif + +#ifdef __UCLIBC_HAS_STDIO_GETC_MACRO__ +#define __STDIO_GETC_MACRO +#endif + +#ifdef __UCLIBC_HAS_STDIO_PUTC_MACRO__ +#define __STDIO_PUTC_MACRO +#endif + +#ifdef __UCLIBC_HAS_STDIO_AUTO_RW_TRANSITION__ +#define __STDIO_AUTO_RW_TRANSITION +#endif + +#ifdef __UCLIBC_HAS_FOPEN_LARGEFILE_MODE__ +#define __STDIO_FOPEN_LARGEFILE_MODE +#endif + +#ifdef __UCLIBC_HAS_FOPEN_LARGEFILE_MODE__ +#define __STDIO_FOPEN_EXCLUSIVE_MODE +#endif + +#ifdef __UCLIBC_HAS_PRINTF_M_SPEC__ +#define __STDIO_PRINTF_M_SUPPORT +#endif + +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ +#define __STDIO_GLIBC_CUSTOM_STREAMS +#endif + +#ifdef __UCLIBC_HAS_STDIO_BUFSIZ_NONE__ +#define _STDIO_BUILTIN_BUF_SIZE 0 +#else /* __UCLIBC_HAS_STDIO_BUFSIZ_NONE__ */ +#if defined(__UCLIBC_HAS_STDIO_BUILTIN_BUFFER_NONE__) +#define _STDIO_BUILTIN_BUF_SIZE 0 +#elif defined(__UCLIBC_HAS_STDIO_BUILTIN_BUFFER_4__) +#define _STDIO_BUILTIN_BUF_SIZE 4 +#elif defined(__UCLIBC_HAS_STDIO_BUILTIN_BUFFER_8__) +#define _STDIO_BUILTIN_BUF_SIZE 8 +#else +#error config seems to be out of sync regarding builtin buffer size +#endif +#endif /* __UCLIBC_HAS_STDIO_BUFSIZ_NONE__ */ + +#ifdef __UCLIBC_HAS_GLIBC_CUSTOM_PRINTF__ +#define __STDIO_GLIBC_CUSTOM_PRINTF +#endif + + +/* Currently unimplemented/untested */ +/* #define __STDIO_FLEXIBLE_SETVBUF */ + + /* Make sure defines related to large files are consistent. */ #ifdef _LIBC @@ -81,32 +154,23 @@ #include <wchar.h> #endif -#define __STDIO_BUFFERS -#define __STDIO_GETC_MACRO -#define __STDIO_PUTC_MACRO /* For uClibc, these are currently handled above. */ -/* #define __STDIO_LARGE_FILES */ -/* #define __STDIO_THREADSAFE */ - +/* #define __STDIO_BUFFERS */ +/* #define __STDIO_GETC_MACRO */ +/* #define __STDIO_PUTC_MACRO */ +/* #define __STDIO_LARGE_FILES */ +/* #define __STDIO_THREADSAFE */ +/* ANSI/ISO mandate at least 256. */ +/* #define _STDIO_BUFSIZ 256 */ +/* #define __STDIO_AUTO_RW_TRANSITION */ +/* #define __STDIO_FOPEN_EXCLUSIVE_MODE */ +/* #define __STDIO_PRINTF_M_SPEC */ +/* #define __STDIO_GLIBC_CUSTOM_STREAMS */ /* L mode extension for fopen. */ -#define __STDIO_FOPEN_LARGEFILE_MODE - +/* #define __STDIO_FOPEN_LARGEFILE_MODE */ /* size of builtin buf -- only tested with 0 */ -#define _STDIO_BUILTIN_BUF_SIZE 0 - -/* TODO - enable features based on __STDIO_GLIBC_FEATURES */ - -/* #define __STDIO_GLIBC_FEATURES */ -#define __STDIO_AUTO_RW_TRANSITION -#define __STDIO_FOPEN_EXCLUSIVE_MODE -#define __STDIO_PRINTF_M_SPEC -#define __STDIO_GLIBC_CUSTOM_STREAMS - - -/* ANSI/ISO mandate at least 256. */ -#define _STDIO_BUFSIZ 256 - +/* #define _STDIO_BUILTIN_BUF_SIZE 0 */ /* Currently unimplemented/untested */ /* #define __STDIO_FLEXIBLE_SETVBUF */ @@ -173,7 +237,10 @@ typedef struct { __off_t __pos; #ifdef __STDIO_MBSTATE - __mbstate_t __mbstate; + __mbstate_t __mbstate; +#endif +#ifdef __STDIO_WIDE + int mblen_pending; #endif } __stdio_fpos_t; @@ -181,7 +248,10 @@ typedef struct { typedef struct { __off64_t __pos; #ifdef __STDIO_MBSTATE - __mbstate_t __mbstate; + __mbstate_t __mbstate; +#endif +#ifdef __STDIO_WIDE + int mblen_pending; #endif } __stdio_fpos64_t; #endif @@ -234,6 +304,7 @@ typedef _IO_cookie_io_functions_t cookie_io_functions_t; * 0 1 one user (unused ungot is 1) or one scanf (unused ungot is 0) * 1 0 must be scanf[0] and user[1] * 1 1 illegal -- could be used to signal safe for setbuf + * but if used, need to fix _stdio_adjpos at least! */ #ifdef __UCLIBC__ @@ -244,8 +315,8 @@ struct __stdio_file_struct { unsigned short modeflags; /* There could be a hole here, but modeflags is used most.*/ #ifdef __STDIO_WIDE - /* TOOD - ungot_width could be combined with ungot. But what about hole? */ - unsigned char ungot_width[2]; + /* TODO - ungot_width could be combined with ungot. But what about hole? */ + unsigned char ungot_width[2]; /* 0 is current (building) char, 1 is scanf */ wchar_t ungot[2]; #else /* __STDIO_WIDE */ unsigned char ungot[2]; diff --git a/libc/sysdeps/linux/common/bits/uClibc_touplow.h b/libc/sysdeps/linux/common/bits/uClibc_touplow.h new file mode 100644 index 000000000..75d508546 --- /dev/null +++ b/libc/sysdeps/linux/common/bits/uClibc_touplow.h @@ -0,0 +1,44 @@ +/* Copyright (C) 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! */ + +#ifndef _UCLIBC_TOUPLOW_H +#define _UCLIBC_TOUPLOW_H + +#include <features.h> +#include <bits/types.h> + +/* glibc uses the equivalent of - typedef __int32_t __ctype_touplow_t; */ + +#ifdef __UCLIBC_HAS_CTYPE_SIGNED__ +typedef __int16_t __ctype_touplow_t; +#else /* __UCLIBC_HAS_CTYPE_SIGNED__ */ +typedef unsigned char __ctype_touplow_t; +#endif /* __UCLIBC_HAS_CTYPE_SIGNED__ */ + +#endif /* _UCLIBC_TOUPLOW_H */ + diff --git a/libc/sysdeps/linux/common/bits/uClibc_uwchar.h b/libc/sysdeps/linux/common/bits/uClibc_uwchar.h new file mode 100644 index 000000000..206191276 --- /dev/null +++ b/libc/sysdeps/linux/common/bits/uClibc_uwchar.h @@ -0,0 +1,56 @@ +/* Copyright (C) 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 an internal unsigned int type __uwchar_t just large enough + * to hold a wchar_t. + */ + +#ifndef _UCLIBC_UWCHAR_H +#define _UCLIBC_UWCHAR_H + +#include <limits.h> +#include <stdint.h> + +#if WCHAR_MIN == 0 +typedef wchar_t __uwchar_t; +#elif WCHAR_MAX <= USHRT_MAX +typedef unsigned short __uwchar_t; +#elif WCHAR_MAX <= UINT_MAX +typedef unsigned int __uwchar_t; +#elif WCHAR_MAX <= ULONG_MAX +typedef unsigned long __uwchar_t; +#elif defined(ULLONG_MAX) && (WCHAR_MAX <= ULLONG_MAX) +typedef unsigned long long __uwchar_t; +#elif WCHAR_MAX <= UINTMAX_MAX +typedef uintmax_t __uwchar_t; +#else +#error Can not determine an appropriate type for __uwchar_t! +#endif + +#endif /* _UCLIBC_UWCHAR_H */ diff --git a/libc/sysdeps/linux/common/bits/xopen_lim.h b/libc/sysdeps/linux/common/bits/xopen_lim.h index 90cb54123..c2363ab04 100644 --- a/libc/sysdeps/linux/common/bits/xopen_lim.h +++ b/libc/sysdeps/linux/common/bits/xopen_lim.h @@ -68,7 +68,13 @@ /* Maximum value of `digit' in calls to the `printf' and `scanf' functions. Posix dictates this should be a minimum of 9 */ +#if !defined(__UCLIBC_PRINTF_SCANF_POSITIONAL_ARGS__) || (__UCLIBC_PRINTF_SCANF_POSITIONAL_ARGS__ <= 1) +#undef NL_ARGMAX +#elif __UCLIBC_PRINTF_SCANF_POSITIONAL_ARGS__ < 9 #define NL_ARGMAX 9 +#else +#define NL_ARGMAX __UCLIBC_PRINTF_SCANF_POSITIONAL_ARGS__ +#endif /* Maximum number of bytes in a `LANG' name. We have no limit. */ #define NL_LANGMAX _POSIX2_LINE_MAX diff --git a/libc/unistd/getopt.c b/libc/unistd/getopt.c index 311118853..504f3764c 100644 --- a/libc/unistd/getopt.c +++ b/libc/unistd/getopt.c @@ -31,6 +31,12 @@ #include <stdlib.h> #include <libintl.h> +#ifdef __UCLIBC_MJN3_ONLY__ +#warning TODO: Enable gettext awareness. +#endif /* __UCLIBC_MJN3_ONLY__ */ + +#undef _ +#define _(X) X /* Treat '-W foo' the same as the long option '--foo', * disabled for the moment since it costs about 2k... */ |