>From c45dbe1d43d5a5aab85f529902f3f57d8149fe7a Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Fri, 18 Sep 2026 11:32:47 -0400 Subject: [PATCH 19/23] implement non-stub localeconv because struct lconv mixes data from LC_NUMERIC and LC_MONETARY, it cannot be a constant structure held with the loaded locale. it has to be expanded from the relevant properties in the different categories to the locale object referenced by locale_t or the global locale. setlocale and newlocale are modified to expand the actual locale data into the target locale object. the mutable global locale structure is pre-initialized with the C locale values, so that no code is needed to prep it, and the static __c_locale and __c_dot_utf8_locale objects which exist to make newlocale failure-free for these locales also hold copies of this data. --- src/internal/locale_impl.h | 2 ++ src/locale/localeconv.c | 51 +++++++++++++++++++++++++++++++++++++- src/locale/newlocale.c | 28 ++++++++++++++++----- src/locale/setlocale.c | 9 +++++++ 4 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/internal/locale_impl.h b/src/internal/locale_impl.h index efe1e7aa..531c74be 100644 --- a/src/internal/locale_impl.h +++ b/src/internal/locale_impl.h @@ -28,6 +28,7 @@ struct __locale_map { struct __locale_struct { const struct __locale_map *cat[6]; + struct lconv *lconv; }; extern hidden volatile int __locale_lock[1]; @@ -37,6 +38,7 @@ extern hidden const struct __locale_struct __c_locale; extern hidden const struct __locale_struct __c_dot_utf8_locale; hidden const struct __locale_map *__get_locale(int, const char *); +hidden void __lconv_expand(locale_t); hidden const char *__loc_lookup(const unsigned char *, const unsigned char *, const int *, const int *); hidden const char *__mo_lookup(const void *, size_t, const char *); hidden int __loc_is_allocated(locale_t); diff --git a/src/locale/localeconv.c b/src/locale/localeconv.c index 4cbb9dc5..08851a76 100644 --- a/src/locale/localeconv.c +++ b/src/locale/localeconv.c @@ -1,5 +1,8 @@ #include #include +#include +#include "locale_impl.h" +#include "ikmlt.h" static const struct lconv posix_lconv = { .decimal_point = ".", @@ -28,7 +31,53 @@ static const struct lconv posix_lconv = { .int_n_sign_posn = CHAR_MAX, }; +static struct lconv global_lconv; + +void __lconv_expand(locale_t loc) +{ + const unsigned char *lconv, *item; + + /* The only way __lconv_expand can be called with loc->lconv null is + * when loc is GLOBAL_LOCALE and this is first setlocale call. */ + struct lconv *lc = loc->lconv; + if (!lc) loc->lconv = lc = &global_lconv; + + *lc = posix_lconv; + + lconv = ikmlt_lookup(LMEMB(loc, LC_NUMERIC, table_root), K_LCONV); + + for (int i=0; i<10; i++) { + if (i==3) lconv = ikmlt_lookup( + LMEMB(loc, LC_MONETARY, table_root), K_LCONV); + if (!lconv) continue; + item = ikmlt_lookup(lconv, i); + if (!item) continue; + *(char **)((char *)loc->lconv+i*sizeof(char *)) = (char *)item; + } + + unsigned char *lconv_bytes = (unsigned char *)loc->lconv + + offsetof(struct lconv, int_frac_digits); + if (lconv && (item = ikmlt_lookup(lconv, -1))) { + for (int i=0; i<14; i++) + lconv_bytes[i] = item[i]>CHAR_MAX ? CHAR_MAX : item[i]; + } + + if ((char)-1 > 0) { + loc->lconv->grouping += strlen(loc->lconv->grouping) + 1; + loc->lconv->mon_grouping += strlen(loc->lconv->mon_grouping) + 1; + } + + if (loc->lconv->decimal_point[0] != ',' || loc->lconv->decimal_point[1]) + loc->lconv->decimal_point = "."; + + return; +} + struct lconv *localeconv(void) { - return (void *)&posix_lconv; + /* Prior to first setlocale, or for uselocale(&default_ctype_locale), + * the locale's lconv pointer may be null. In either case, + * default_lconv accurately reflects the locale. */ + struct lconv *lc = CURRENT_LOCALE->lconv; + return lc ? lc : (struct lconv *)&posix_lconv; } diff --git a/src/locale/newlocale.c b/src/locale/newlocale.c index 10d68f6b..56536ebf 100644 --- a/src/locale/newlocale.c +++ b/src/locale/newlocale.c @@ -11,6 +11,7 @@ static int default_locale_init_done; static struct __locale_struct default_locale, default_ctype_locale; +static struct lconv default_locale_lconv; int __loc_is_allocated(locale_t loc) { @@ -18,6 +19,12 @@ int __loc_is_allocated(locale_t loc) && loc != &default_locale && loc != &default_ctype_locale; } +static void dummy_lconv_expand(locale_t loc) +{ +} + +weak_alias(dummy_lconv_expand, __lconv_expand); + static locale_t do_newlocale(int mask, const char *name, locale_t loc) { struct __locale_struct tmp; @@ -31,15 +38,17 @@ static locale_t do_newlocale(int mask, const char *name, locale_t loc) /* For locales with allocated storage, modify in-place. */ if (__loc_is_allocated(loc)) { - *loc = tmp; + for (int i=0; icat[i] = tmp.cat[i]; + __lconv_expand(loc); return loc; } /* Otherwise, first see if we can use one of the builtin locales. * This makes the common usage case for newlocale, getting a C locale * with predictable behavior, very fast, and more importantly, fail-safe. */ - if (!memcmp(&tmp, C_LOCALE, sizeof tmp)) return C_LOCALE; - if (!memcmp(&tmp, UTF8_LOCALE, sizeof tmp)) return UTF8_LOCALE; + if (!memcmp(&tmp, C_LOCALE, sizeof tmp.cat)) return C_LOCALE; + if (!memcmp(&tmp, UTF8_LOCALE, sizeof tmp.cat)) return UTF8_LOCALE; /* And provide builtins for the initial default locale, and a * variant of the C locale honoring the default locale's encoding. */ @@ -47,14 +56,21 @@ static locale_t do_newlocale(int mask, const char *name, locale_t loc) for (int i=0; ilconv))) { + *loc = tmp; + loc->lconv = (void *)(loc + 1); + __lconv_expand(loc); + } return loc; } diff --git a/src/locale/setlocale.c b/src/locale/setlocale.c index a14b0ea6..e7f3afd7 100644 --- a/src/locale/setlocale.c +++ b/src/locale/setlocale.c @@ -7,6 +7,12 @@ static char buf[LC_ALL*(LOCALE_NAME_MAX+1)]; +static void dummy_lconv_expand(locale_t loc) +{ +} + +weak_alias(dummy_lconv_expand, __lconv_expand); + char *setlocale(int cat, const char *name) { const struct __locale_map *lm; @@ -40,6 +46,7 @@ char *setlocale(int cat, const char *name) } tmp_locale.cat[i] = lm; } + __lconv_expand(&tmp_locale); __global_locale = tmp_locale; } char *s = buf; @@ -67,6 +74,8 @@ char *setlocale(int cat, const char *name) return 0; } __global_locale.cat[cat] = lm; + if (cat==LC_NUMERIC || cat==LC_MONETARY) + __lconv_expand(&__global_locale); } else { lm = __global_locale.cat[cat]; } -- 2.21.0