>From 66014412556378ff963d4ef71f7c3b1ed2f4bb05 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Fri, 18 Sep 2026 11:32:47 -0400 Subject: [PATCH 17/18] 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/c_locale.c | 13 +++++-- src/locale/default_lconv.h | 24 +++++++++++++ src/locale/global_locale.c | 5 ++- src/locale/localeconv.c | 71 +++++++++++++++++++++++--------------- src/locale/newlocale.c | 18 +++++++--- src/locale/setlocale.c | 9 +++++ 7 files changed, 108 insertions(+), 34 deletions(-) create mode 100644 src/locale/default_lconv.h diff --git a/src/internal/locale_impl.h b/src/internal/locale_impl.h index 9e30afd9..5a6e519c 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/c_locale.c b/src/locale/c_locale.c index 620060fa..18dc1f6b 100644 --- a/src/locale/c_locale.c +++ b/src/locale/c_locale.c @@ -1,11 +1,20 @@ #include "locale_impl.h" #include +#include const struct __locale_map __c_dot_utf8 = { .name = "C.UTF-8" }; -const struct __locale_struct __c_locale = { 0 }; +const struct __locale_struct __c_locale = { + .lconv = { + #include "default_lconv.h" + } +}; + const struct __locale_struct __c_dot_utf8_locale = { - .cat[LC_CTYPE] = &__c_dot_utf8 + .cat[LC_CTYPE] = &__c_dot_utf8, + .lconv = { + #include "default_lconv.h" + } }; diff --git a/src/locale/default_lconv.h b/src/locale/default_lconv.h new file mode 100644 index 00000000..c16b1094 --- /dev/null +++ b/src/locale/default_lconv.h @@ -0,0 +1,24 @@ + .decimal_point = ".", + .thousands_sep = "", + .grouping = "", + .int_curr_symbol = "", + .currency_symbol = "", + .mon_decimal_point = "", + .mon_thousands_sep = "", + .mon_grouping = "", + .positive_sign = "", + .negative_sign = "", + .int_frac_digits = CHAR_MAX, + .frac_digits = CHAR_MAX, + .p_cs_precedes = CHAR_MAX, + .p_sep_by_space = CHAR_MAX, + .n_cs_precedes = CHAR_MAX, + .n_sep_by_space = CHAR_MAX, + .p_sign_posn = CHAR_MAX, + .n_sign_posn = CHAR_MAX, + .int_p_cs_precedes = CHAR_MAX, + .int_p_sep_by_space = CHAR_MAX, + .int_n_cs_precedes = CHAR_MAX, + .int_n_sep_by_space = CHAR_MAX, + .int_p_sign_posn = CHAR_MAX, + .int_n_sign_posn = CHAR_MAX, diff --git a/src/locale/global_locale.c b/src/locale/global_locale.c index 998142fe..cdf5fade 100644 --- a/src/locale/global_locale.c +++ b/src/locale/global_locale.c @@ -1,3 +1,6 @@ +#include #include "locale_impl.h" -struct __locale_struct __global_locale; +struct __locale_struct __global_locale = { .lconv = { +#include "default_lconv.h" +} }; diff --git a/src/locale/localeconv.c b/src/locale/localeconv.c index 4cbb9dc5..727cd058 100644 --- a/src/locale/localeconv.c +++ b/src/locale/localeconv.c @@ -1,34 +1,51 @@ #include #include +#include +#include "locale_impl.h" +#include "ikmlt.h" -static const struct lconv posix_lconv = { - .decimal_point = ".", - .thousands_sep = "", - .grouping = "", - .int_curr_symbol = "", - .currency_symbol = "", - .mon_decimal_point = "", - .mon_thousands_sep = "", - .mon_grouping = "", - .positive_sign = "", - .negative_sign = "", - .int_frac_digits = CHAR_MAX, - .frac_digits = CHAR_MAX, - .p_cs_precedes = CHAR_MAX, - .p_sep_by_space = CHAR_MAX, - .n_cs_precedes = CHAR_MAX, - .n_sep_by_space = CHAR_MAX, - .p_sign_posn = CHAR_MAX, - .n_sign_posn = CHAR_MAX, - .int_p_cs_precedes = CHAR_MAX, - .int_p_sep_by_space = CHAR_MAX, - .int_n_cs_precedes = CHAR_MAX, - .int_n_sep_by_space = CHAR_MAX, - .int_p_sign_posn = CHAR_MAX, - .int_n_sign_posn = CHAR_MAX, -}; +void __lconv_expand(locale_t loc) +{ + const struct __locale_map *l; + const unsigned char *lconv, *item; + unsigned char *lconv_bytes = (unsigned char *)&loc->lconv + + offsetof(struct lconv, int_frac_digits); + + for (int i=0; i<14; i++) lconv_bytes[i] = CHAR_MAX; + for (int i=0; i<10; i++) + *(char **)((char *)&loc->lconv + i*sizeof(char *)) = ""; + + l = loc->cat[LC_NUMERIC]; + lconv = l && l->table_root ? ikmlt_lookup(l->table_root, K_LCONV) : 0; + + for (int i=0; i<10; i++) { + if (i==3) { + l = loc->cat[LC_MONETARY]; + lconv = l && l->table_root ? + ikmlt_lookup(l->table_root, K_LCONV) : 0; + } + if (!lconv) continue; + item = ikmlt_lookup(lconv, i); + if (!item) continue; + *(char **)((char *)&loc->lconv+i*sizeof(char *)) = (char *)item; + } + 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; + return &CURRENT_LOCALE->lconv; } diff --git a/src/locale/newlocale.c b/src/locale/newlocale.c index 9ac3cd38..166ff01d 100644 --- a/src/locale/newlocale.c +++ b/src/locale/newlocale.c @@ -18,6 +18,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; @@ -28,6 +34,7 @@ static locale_t do_newlocale(int mask, const char *name, locale_t loc) if (tmp.cat[i] == LOC_MAP_FAILED) return 0; } + __lconv_expand(&tmp); /* For locales with allocated storage, modify in-place. */ if (__loc_is_allocated(loc)) { @@ -38,8 +45,8 @@ static locale_t do_newlocale(int mask, const char *name, locale_t 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,10 +54,13 @@ static locale_t do_newlocale(int mask, const char *name, locale_t loc) for (int i=0; i