From aefd8b5b518b958f64506a6f74aeffb4f47bde8a Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Fri, 22 Jul 2016 22:54:07 +0200 Subject: [PATCH] Implement newlocale, freelocale, duplocale, uselocale Add global const __C_locale for reference purposes. Bump Cygwin API minor number and DLL major version number to 2.6.0. Signed-off by: Corinna Vinschen --- newlib/libc/include/locale.h | 22 ++- newlib/libc/locale/locale.c | 250 +++++++++++++++++++++++++ winsup/cygwin/common.din | 4 + winsup/cygwin/include/cygwin/version.h | 7 +- 4 files changed, 274 insertions(+), 9 deletions(-) diff --git a/newlib/libc/include/locale.h b/newlib/libc/include/locale.h index 40e412451..d60132c35 100644 --- a/newlib/libc/include/locale.h +++ b/newlib/libc/include/locale.h @@ -65,15 +65,25 @@ struct lconv char int_p_sign_posn; }; -#ifndef _REENT_ONLY -char *_EXFUN(setlocale,(int category, const char *locale)); -struct lconv *_EXFUN(localeconv,(void)); -#endif - struct _reent; -char *_EXFUN(_setlocale_r,(struct _reent *, int category, const char *locale)); +char *_EXFUN(_setlocale_r,(struct _reent *, int, const char *)); struct lconv *_EXFUN(_localeconv_r,(struct _reent *)); +locale_t _newlocale_r (struct _reent *, int, const char *, locale_t); +void _freelocale_r (struct _reent *, locale_t); +locale_t _duplocale_r (struct _reent *, locale_t); +locale_t _uselocale_r (struct _reent *, locale_t); + +#ifndef _REENT_ONLY +char *_EXFUN(setlocale,(int, const char *)); +struct lconv *_EXFUN(localeconv,(void)); + +locale_t newlocale (int, const char *, locale_t); +void freelocale (locale_t); +locale_t duplocale (locale_t); +locale_t uselocale (locale_t); +#endif + _END_STD_C #endif /* _LOCALE_H_ */ diff --git a/newlib/libc/locale/locale.c b/newlib/libc/locale/locale.c index 35c5e6c6f..4f2d6d271 100644 --- a/newlib/libc/locale/locale.c +++ b/newlib/libc/locale/locale.c @@ -226,6 +226,34 @@ static char *categories[_LC_LAST] = { */ char __default_locale[ENCODING_LEN + 1] = DEFAULT_LOCALE; +const struct __locale_t __C_locale = +{ + { "C", "C", "C", "C", "C", "C", "C", }, + __ascii_wctomb, + __ascii_mbtowc, + 0, + NULL, +#ifndef __HAVE_LOCALE_INFO__ + "\1", + "ASCII", + "ASCII", +#else + { + { NULL, NULL }, /* LC_ALL */ +#ifdef __CYGWIN__ + { &_C_collate_locale, NULL }, /* LC_COLLATE */ +#else + { NULL, NULL }, /* LC_COLLATE */ +#endif + { &_C_ctype_locale, NULL }, /* LC_CTYPE */ + { &_C_monetary_locale, NULL }, /* LC_MONETARY */ + { &_C_numeric_locale, NULL }, /* LC_NUMERIC */ + { &_C_time_locale, NULL }, /* LC_TIME */ + { &_C_messages_locale, NULL }, /* LC_MESSAGES */ + }, +#endif +}; + struct __locale_t __global_locale = { { "C", "C", DEFAULT_LOCALE, "C", "C", "C", "C", }, @@ -1008,6 +1036,205 @@ _DEFUN (_localeconv_r, (data), return (struct lconv *) &lconv; } +#define LC_VALID_MASK (LC_COLLATE_MASK | LC_CTYPE_MASK | LC_MONETARY_MASK \ + | LC_NUMERIC_MASK | LC_TIME_MASK | LC_MESSAGES_MASK) + +struct __locale_t * +_newlocale_r (struct _reent *p, int category_mask, const char *locale, + struct __locale_t *base) +{ + struct __locale_t tmp_locale, *new_locale; + int i; + + /* Convert LC_ALL_MASK to a mask containing all valid MASK values. + This simplifies the code below. */ + if (category_mask & LC_ALL_MASK) + { + category_mask &= ~LC_ALL_MASK; + category_mask |= LC_VALID_MASK; + } + /* Check for invalid mask values and valid locale ptr. */ + if (category_mask & ~LC_VALID_MASK || !locale) + { + p->_errno = EINVAL; + return NULL; + } + /* If the new locale is supposed to be all default locale, just return + a pointer to the default locale. */ + if ((!base && category_mask == 0) + || (category_mask == LC_VALID_MASK + && (!strcmp (locale, "C") || !strcmp (locale, "POSIX")))) + return (struct __locale_t *) &__C_locale; + /* Start with setting all values to the default locale values. */ + tmp_locale = __C_locale; + /* Fill out category strings. */ + if (!*locale) + { + for (i = 1; i < _LC_LAST; ++i) + if (((1 << i) & category_mask) != 0) + { + const char *env = __get_locale_env (p, i); + if (strlen (env) > ENCODING_LEN) + { + p->_errno = EINVAL; + return NULL; + } + strcpy (tmp_locale.categories[i], env); + } + } + else + { + for (i = 1; i < _LC_LAST; ++i) + if (((1 << i) & category_mask) != 0) + strcpy (tmp_locale.categories[i], locale); + } + /* Now go over all categories and set them. */ + for (i = 1; i < _LC_LAST; ++i) + { + if (((1 << i) & category_mask) != 0) + { + /* Nothing to do for "C"/"POSIX" locale. */ + if (!strcmp (tmp_locale.categories[i], "C") + || !strcmp (tmp_locale.categories[i], "POSIX")) + continue; + /* If the new locale is the old locale, just copy it over. */ + if (base && !strcmp (base->categories[i], tmp_locale.categories[i])) + { + if (i == LC_CTYPE) + { + tmp_locale.wctomb = base->wctomb; + tmp_locale.mbtowc = base->mbtowc; + tmp_locale.cjk_lang = base->cjk_lang; + tmp_locale.ctype_ptr - base->ctype_ptr; + } +#ifdef __HAVE_LOCALE_INFO__ + tmp_locale.lc_cat[i].ptr = base->lc_cat[i].ptr; + /* Mark the value as "has still to be copied". We do this in + two steps to simplify freeing new locale types in case of a + subsequent error. */ + tmp_locale.lc_cat[i].buf = (void *) -1; +#else + if (i == LC_CTYPE) + strcpy (tmp_locale.ctype_codeset, base->ctype_codeset); + else if (i == LC_MESSAGES) + strcpy (tmp_locale.message_codeset, base->message_codeset); +#endif + continue; + } + /* Otherwise load locale data. */ + if (!loadlocale (&tmp_locale, i, tmp_locale.categories[i])) + goto error; + } + } + /* Allocate new locale_t. */ + new_locale = (struct __locale_t *) _calloc_r (p, 1, sizeof *new_locale); + if (!new_locale) + goto error; +#ifdef __HAVE_LOCALE_INFO__ + /* Second step of copying over. At this point we can safely copy. Make + sure to invalidate the copied buffer pointers in base, so a subsequent + freelocale (base) doesn't free the buffers now used in the new locale. */ + for (i = 1; i < _LC_LAST; ++i) + if (tmp_locale.lc_cat[i].buf == (const void *) -1) + { + tmp_locale.lc_cat[i].buf = base->lc_cat[i].buf; + base->lc_cat[i].buf = NULL; + } +#endif + + *new_locale = tmp_locale; + return new_locale; + +error: + /* An error occured while we had already (potentially) allocated memory. + Free memory and return NULL. errno is supposed to be set already. */ +#ifdef __HAVE_LOCALE_INFO__ + for (i = 1; i < _LC_LAST; ++i) + if (tmp_locale.lc_cat[i].buf + && tmp_locale.lc_cat[i].buf != (const void *) -1) + _free_r (p, tmp_locale.lc_cat[i].buf); +#endif + + return NULL; +} + +void +_freelocale_r (struct _reent *p, struct __locale_t *locobj) +{ + /* Sanity check. The "C" locale is static, don't try to free it. */ + if (!locobj || locobj == &__C_locale || locobj == LC_GLOBAL_LOCALE) + return; +#ifdef __HAVE_LOCALE_INFO__ + for (int i = 1; i < _LC_LAST; ++i) + if (locobj->lc_cat[i].buf) + _free_r (p, locobj->lc_cat[i].buf); +#endif + _free_r (p, locobj); +} + +struct __locale_t * +_duplocale_r (struct _reent *p, struct __locale_t *locobj) +{ + struct __locale_t tmp_locale, *new_locale; + int i; + + /* LC_GLOBAL_LOCALE denotes the global locale. */ + if (locobj == LC_GLOBAL_LOCALE) + locobj = __get_global_locale (); + /* The "C" locale is used statically, never copied. */ + else if (locobj == &__C_locale) + return (struct __locale_t *) &__C_locale; + /* Copy locale content. */ + tmp_locale = *locobj; +#ifdef __HAVE_LOCALE_INFO__ + for (i = 1; i < _LC_LAST; ++i) + if (locobj->lc_cat[i].buf) + { + /* If the object is not a "C" locale category, copy it. Just call + loadlocale. It knows what to do to replicate the category. */ + tmp_locale.lc_cat[i].ptr = NULL; + tmp_locale.lc_cat[i].buf = NULL; + if (!loadlocale (&tmp_locale, i, tmp_locale.categories[i])) + goto error; + } +#endif + /* Allocate new locale_t. */ + new_locale = (struct __locale_t *) _calloc_r (p, 1, sizeof *new_locale); + if (!new_locale) + goto error; + + *new_locale = tmp_locale; + return new_locale; + +error: + /* An error occured while we had already (potentially) allocated memory. + Free memory and return NULL. errno is supposed to be set already. */ +#ifdef __HAVE_LOCALE_INFO__ + while (--i > 0) + if (tmp_locale.lc_cat[i].buf) + _free_r (p, tmp_locale.lc_cat[i].buf); +#endif + + return NULL; +} + +struct __locale_t * +_uselocale_r (struct _reent *p, struct __locale_t *newloc) +{ + struct __locale_t *current_locale; + + current_locale = __get_locale_r (p); + if (!current_locale) + current_locale = LC_GLOBAL_LOCALE; + + if (newloc == LC_GLOBAL_LOCALE) + p->_locale = NULL; + else if (newloc) + p->_locale = newloc; + + return current_locale; +} + #ifndef _REENT_ONLY char * @@ -1024,4 +1251,27 @@ _DEFUN_VOID (localeconv) return _localeconv_r (_REENT); } +struct __locale_t * +newlocale (int category_mask, const char *locale, struct __locale_t *base) +{ + return _newlocale_r (_REENT, category_mask, locale, base); +} + +void +freelocale (struct __locale_t *locobj) +{ + _freelocale_r (_REENT, locobj); +} + +struct __locale_t * +duplocale (struct __locale_t *locobj) +{ + return _duplocale_r (_REENT, locobj); +} + +struct __locale_t * +uselocale (struct __locale_t *newloc) +{ + return _uselocale_r (_REENT, newloc); +} #endif diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din index acb3fabb9..0660a38e7 100644 --- a/winsup/cygwin/common.din +++ b/winsup/cygwin/common.din @@ -381,6 +381,7 @@ dreml= remainderl NOSIGFE dup SIGFE dup2 SIGFE dup3 SIGFE +duplocale SIGFE eaccess = euidaccess SIGFE ecvt SIGFE ecvtbuf SIGFE @@ -536,6 +537,7 @@ fread_unlocked SIGFE free SIGFE freeaddrinfo = cygwin_freeaddrinfo SIGFE freeifaddrs SIGFE +freelocale SIGFE fremovexattr SIGFE freopen SIGFE frexp NOSIGFE @@ -900,6 +902,7 @@ nanosleep SIGFE nearbyint NOSIGFE nearbyintf NOSIGFE nearbyintl NOSIGFE +newlocale SIGFE nextafter NOSIGFE nextafterf NOSIGFE nextafterl NOSIGFE @@ -1417,6 +1420,7 @@ unlockpt NOSIGFE unsetenv SIGFE updwtmp SIGFE updwtmpx SIGFE +uselocale SIGFE usleep SIGFE utime SIGFE utimensat SIGFE diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index 1f5bf7212..9ae5983f8 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -10,8 +10,8 @@ details. */ the Cygwin shared library". This version is used to track important changes to the DLL and is mainly informative in nature. */ -#define CYGWIN_VERSION_DLL_MAJOR 2005 -#define CYGWIN_VERSION_DLL_MINOR 3 +#define CYGWIN_VERSION_DLL_MAJOR 2006 +#define CYGWIN_VERSION_DLL_MINOR 0 /* Major numbers before CYGWIN_VERSION_DLL_EPOCH are incompatible. */ @@ -454,12 +454,13 @@ details. */ nexttowardf, nexttowardl, pow10l, powl, remainderl, remquol, roundl, scalbl, scalblnl, scalbnl, sincosl, sinhl, sinl, tanhl, tanl, tgammal, truncl. + 298: newlocale, freelocale, duplocale, uselocale. Note that we forgot to bump the api for ualarm, strtoll, strtoull, sigaltstack, sethostname. */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 297 +#define CYGWIN_VERSION_API_MINOR 298 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible changes are made to the shared