aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2016-07-22 22:54:07 +0200
committerCorinna Vinschen <corinna@vinschen.de>2016-08-12 21:50:08 +0200
commit507858891ec09a4cd598909ef76096830adb8031 (patch)
tree2eb483eb3ab883d1426e7783d8eb322bb66c7952
parent519c0a6c2607edc134114c739585a456bfc61aab (diff)
newlocale, freelocale, duplocale, uselocale
-rw-r--r--newlib/libc/include/locale.h20
-rw-r--r--newlib/libc/locale/locale.c250
-rw-r--r--winsup/cygwin/common.din4
-rw-r--r--winsup/cygwin/include/cygwin/version.h7
4 files changed, 273 insertions, 8 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,14 +65,24 @@ struct lconv
char int_p_sign_posn;
};
+struct _reent;
+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 category, const char *locale));
+char *_EXFUN(setlocale,(int, const char *));
struct lconv *_EXFUN(localeconv,(void));
-#endif
-struct _reent;
-char *_EXFUN(_setlocale_r,(struct _reent *, int category, const char *locale));
-struct lconv *_EXFUN(_localeconv_r,(struct _reent *));
+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
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