LCOV - differential code coverage report
Current view: top level - src/port - pg_localeconv_r.c (source / functions) Coverage Total Hit UBC CBC
Current: c70b6db34ffeab48beef1fb4ce61bcad3772b8dd vs 06473f5a344df8c9594ead90a609b86f6724cff8 Lines: 83.8 % 37 31 6 31
Current Date: 2025-09-06 07:49:51 +0900 Functions: 100.0 % 5 5 5
Baseline: lcov-20250906-005545-baseline Branches: 81.2 % 16 13 3 13
Baseline Date: 2025-09-05 08:21:35 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(30,360] days: 83.8 % 37 31 6 31
Function coverage date bins:
(30,360] days: 100.0 % 5 5 5
Branch coverage date bins:
(30,360] days: 81.2 % 16 13 3 13

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * pg_localeconv_r.c
                                  4                 :                :  *    Thread-safe implementations of localeconv()
                                  5                 :                :  *
                                  6                 :                :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
                                  7                 :                :  * Portions Copyright (c) 1994, Regents of the University of California
                                  8                 :                :  *
                                  9                 :                :  *
                                 10                 :                :  * IDENTIFICATION
                                 11                 :                :  *    src/port/pg_localeconv_r.c
                                 12                 :                :  *
                                 13                 :                :  *-------------------------------------------------------------------------
                                 14                 :                :  */
                                 15                 :                : 
                                 16                 :                : #include "c.h"
                                 17                 :                : 
                                 18                 :                : #if !defined(WIN32)
                                 19                 :                : #include <langinfo.h>
                                 20                 :                : #include <pthread.h>
                                 21                 :                : #endif
                                 22                 :                : 
                                 23                 :                : #include <limits.h>
                                 24                 :                : 
                                 25                 :                : #ifdef MON_THOUSANDS_SEP
                                 26                 :                : /*
                                 27                 :                :  * One of glibc's extended langinfo items detected.  Assume that the full set
                                 28                 :                :  * is present, which means we can use nl_langinfo_l() instead of localeconv().
                                 29                 :                :  */
                                 30                 :                : #define TRANSLATE_FROM_LANGINFO
                                 31                 :                : #endif
                                 32                 :                : 
                                 33                 :                : struct lconv_member_info
                                 34                 :                : {
                                 35                 :                :     bool        is_string;
                                 36                 :                :     int         category;
                                 37                 :                :     size_t      offset;
                                 38                 :                : #ifdef TRANSLATE_FROM_LANGINFO
                                 39                 :                :     nl_item     item;
                                 40                 :                : #endif
                                 41                 :                : };
                                 42                 :                : 
                                 43                 :                : /* Some macros to declare the lconv members compactly. */
                                 44                 :                : #ifdef TRANSLATE_FROM_LANGINFO
                                 45                 :                : #define LCONV_M(is_string, category, name, item)                        \
                                 46                 :                :     { is_string, category, offsetof(struct lconv, name), item }
                                 47                 :                : #else
                                 48                 :                : #define LCONV_M(is_string, category, name, item)            \
                                 49                 :                :     { is_string, category, offsetof(struct lconv, name) }
                                 50                 :                : #endif
                                 51                 :                : #define LCONV_S(c, n, i) LCONV_M(true,  c, n, i)
                                 52                 :                : #define LCONV_C(c, n, i) LCONV_M(false, c, n, i)
                                 53                 :                : 
                                 54                 :                : /*
                                 55                 :                :  * The work of populating lconv objects is driven by this table.  Since we
                                 56                 :                :  * tolerate non-matching encodings in LC_NUMERIC and LC_MONETARY, we have to
                                 57                 :                :  * call the underlying OS routine multiple times, with the correct locales.
                                 58                 :                :  * The first column of this table says which locale category applies to each struct
                                 59                 :                :  * member.  The second column is the name of the struct member.  The third
                                 60                 :                :  * column is the name of the nl_item, if translating from nl_langinfo_l() (it's
                                 61                 :                :  * always the member name, in upper case).
                                 62                 :                :  */
                                 63                 :                : static const struct lconv_member_info table[] = {
                                 64                 :                :     /* String fields. */
                                 65                 :                :     LCONV_S(LC_NUMERIC, decimal_point, DECIMAL_POINT),
                                 66                 :                :     LCONV_S(LC_NUMERIC, thousands_sep, THOUSANDS_SEP),
                                 67                 :                :     LCONV_S(LC_NUMERIC, grouping, GROUPING),
                                 68                 :                :     LCONV_S(LC_MONETARY, int_curr_symbol, INT_CURR_SYMBOL),
                                 69                 :                :     LCONV_S(LC_MONETARY, currency_symbol, CURRENCY_SYMBOL),
                                 70                 :                :     LCONV_S(LC_MONETARY, mon_decimal_point, MON_DECIMAL_POINT),
                                 71                 :                :     LCONV_S(LC_MONETARY, mon_thousands_sep, MON_THOUSANDS_SEP),
                                 72                 :                :     LCONV_S(LC_MONETARY, mon_grouping, MON_GROUPING),
                                 73                 :                :     LCONV_S(LC_MONETARY, positive_sign, POSITIVE_SIGN),
                                 74                 :                :     LCONV_S(LC_MONETARY, negative_sign, NEGATIVE_SIGN),
                                 75                 :                : 
                                 76                 :                :     /* Character fields. */
                                 77                 :                :     LCONV_C(LC_MONETARY, int_frac_digits, INT_FRAC_DIGITS),
                                 78                 :                :     LCONV_C(LC_MONETARY, frac_digits, FRAC_DIGITS),
                                 79                 :                :     LCONV_C(LC_MONETARY, p_cs_precedes, P_CS_PRECEDES),
                                 80                 :                :     LCONV_C(LC_MONETARY, p_sep_by_space, P_SEP_BY_SPACE),
                                 81                 :                :     LCONV_C(LC_MONETARY, n_cs_precedes, N_CS_PRECEDES),
                                 82                 :                :     LCONV_C(LC_MONETARY, n_sep_by_space, N_SEP_BY_SPACE),
                                 83                 :                :     LCONV_C(LC_MONETARY, p_sign_posn, P_SIGN_POSN),
                                 84                 :                :     LCONV_C(LC_MONETARY, n_sign_posn, N_SIGN_POSN),
                                 85                 :                : };
                                 86                 :                : 
                                 87                 :                : static inline char **
  163 peter@eisentraut.org       88                 :CBC         560 : lconv_string_member(struct lconv *lconv, int i)
                                 89                 :                : {
                                 90                 :            560 :     return (char **) ((char *) lconv + table[i].offset);
                                 91                 :                : }
                                 92                 :                : 
                                 93                 :                : static inline char *
                                 94                 :            224 : lconv_char_member(struct lconv *lconv, int i)
                                 95                 :                : {
                                 96                 :            224 :     return (char *) lconv + table[i].offset;
                                 97                 :                : }
                                 98                 :                : 
                                 99                 :                : /*
                                100                 :                :  * Free the members of a struct lconv populated by pg_localeconv_r().  The
                                101                 :                :  * struct itself is in storage provided by the caller of pg_localeconv_r().
                                102                 :                :  */
                                103                 :                : void
                                104                 :             28 : pg_localeconv_free(struct lconv *lconv)
                                105                 :                : {
                                106         [ +  + ]:            532 :     for (int i = 0; i < lengthof(table); ++i)
                                107         [ +  + ]:            504 :         if (table[i].is_string)
                                108                 :            280 :             free(*lconv_string_member(lconv, i));
                                109                 :             28 : }
                                110                 :                : 
                                111                 :                : #ifdef TRANSLATE_FROM_LANGINFO
                                112                 :                : /*
                                113                 :                :  * Fill in struct lconv members using the equivalent nl_langinfo_l() items.
                                114                 :                :  */
                                115                 :                : static int
                                116                 :             28 : pg_localeconv_from_langinfo(struct lconv *dst,
                                117                 :                :                             locale_t monetary_locale,
                                118                 :                :                             locale_t numeric_locale)
                                119                 :                : {
                                120         [ +  + ]:            532 :     for (int i = 0; i < lengthof(table); ++i)
                                121                 :                :     {
                                122                 :                :         locale_t    locale;
                                123                 :                : 
                                124                 :           1008 :         locale = table[i].category == LC_NUMERIC ?
                                125         [ +  + ]:            504 :             numeric_locale : monetary_locale;
                                126                 :                : 
                                127         [ +  + ]:            504 :         if (table[i].is_string)
                                128                 :                :         {
                                129                 :                :             char       *string;
                                130                 :                : 
                                131                 :            280 :             string = nl_langinfo_l(table[i].item, locale);
                                132         [ -  + ]:            280 :             if (!(string = strdup(string)))
                                133                 :                :             {
  163 peter@eisentraut.org      134                 :UBC           0 :                 pg_localeconv_free(dst);
                                135                 :              0 :                 errno = ENOMEM;
                                136                 :              0 :                 return -1;
                                137                 :                :             }
  163 peter@eisentraut.org      138                 :CBC         280 :             *lconv_string_member(dst, i) = string;
                                139                 :                :         }
                                140                 :                :         else
                                141                 :                :         {
                                142                 :            224 :             *lconv_char_member(dst, i) =
                                143                 :            448 :                 *nl_langinfo_l(table[i].item, locale);
                                144                 :                :         }
                                145                 :                :     }
                                146                 :                : 
                                147                 :             28 :     return 0;
                                148                 :                : }
                                149                 :                : #else                           /* not TRANSLATE_FROM_LANGINFO */
                                150                 :                : /*
                                151                 :                :  * Copy members from a given category.  Note that you have to call this twice
                                152                 :                :  * to copy the LC_MONETARY and then LC_NUMERIC members.
                                153                 :                :  */
                                154                 :                : static int
                                155                 :                : pg_localeconv_copy_members(struct lconv *dst,
                                156                 :                :                            struct lconv *src,
                                157                 :                :                            int category)
                                158                 :                : {
                                159                 :                :     for (int i = 0; i < lengthof(table); ++i)
                                160                 :                :     {
                                161                 :                :         if (table[i].category != category)
                                162                 :                :             continue;
                                163                 :                : 
                                164                 :                :         if (table[i].is_string)
                                165                 :                :         {
                                166                 :                :             char       *string;
                                167                 :                : 
                                168                 :                :             string = *lconv_string_member(src, i);
                                169                 :                :             if (!(string = strdup(string)))
                                170                 :                :             {
                                171                 :                :                 pg_localeconv_free(dst);
                                172                 :                :                 errno = ENOMEM;
                                173                 :                :                 return -1;
                                174                 :                :             }
                                175                 :                :             *lconv_string_member(dst, i) = string;
                                176                 :                :         }
                                177                 :                :         else
                                178                 :                :         {
                                179                 :                :             *lconv_char_member(dst, i) = *lconv_char_member(src, i);
                                180                 :                :         }
                                181                 :                :     }
                                182                 :                : 
                                183                 :                :     return 0;
                                184                 :                : }
                                185                 :                : #endif                          /* not TRANSLATE_FROM_LANGINFO */
                                186                 :                : 
                                187                 :                : /*
                                188                 :                :  * A thread-safe routine to get a copy of the lconv struct for a given
                                189                 :                :  * LC_NUMERIC and LC_MONETARY.  Different approaches are used on different
                                190                 :                :  * OSes, because the standard interface is so multi-threading unfriendly.
                                191                 :                :  *
                                192                 :                :  * 1.  On Windows, there is no uselocale(), but there is a way to put
                                193                 :                :  * setlocale() into a thread-local mode temporarily.  Its localeconv() is
                                194                 :                :  * documented as returning a pointer to thread-local storage, so we don't have
                                195                 :                :  * to worry about concurrent callers.
                                196                 :                :  *
                                197                 :                :  * 2.  On Glibc, as an extension, all the information required to populate
                                198                 :                :  * struct lconv is also available via nl_langpath_l(), which is thread-safe.
                                199                 :                :  *
                                200                 :                :  * 3.  On macOS and *BSD, there is localeconv_l(), so we can create a temporary
                                201                 :                :  * locale_t to pass in, and the result is a pointer to storage associated with
                                202                 :                :  * the locale_t so we control its lifetime and we don't have to worry about
                                203                 :                :  * concurrent calls clobbering it.
                                204                 :                :  *
                                205                 :                :  * 4.  Otherwise, we wrap plain old localeconv() in uselocale() to avoid
                                206                 :                :  * touching the global locale, but the output buffer is allowed by the standard
                                207                 :                :  * to be overwritten by concurrent calls to localeconv().  We protect against
                                208                 :                :  * _this_ function doing that with a Big Lock, but there isn't much we can do
                                209                 :                :  * about code outside our tree that might call localeconv(), given such a poor
                                210                 :                :  * interface.
                                211                 :                :  *
                                212                 :                :  * The POSIX standard explicitly says that it is undefined what happens if
                                213                 :                :  * LC_MONETARY or LC_NUMERIC imply an encoding (codeset) different from that
                                214                 :                :  * implied by LC_CTYPE.  In practice, all Unix-ish platforms seem to believe
                                215                 :                :  * that localeconv() should return strings that are encoded in the codeset
                                216                 :                :  * implied by the LC_MONETARY or LC_NUMERIC locale name.  On Windows, LC_CTYPE
                                217                 :                :  * has to match to get sane results.
                                218                 :                :  *
                                219                 :                :  * To get predictable results on all platforms, we'll call the underlying
                                220                 :                :  * routines with LC_ALL set to the appropriate locale for each set of members,
                                221                 :                :  * and merge the results.  Three members of the resulting object are therefore
                                222                 :                :  * guaranteed to be encoded with LC_NUMERIC's codeset: "decimal_point",
                                223                 :                :  * "thousands_sep" and "grouping".  All other members are encoded with
                                224                 :                :  * LC_MONETARY's codeset.
                                225                 :                :  *
                                226                 :                :  * Returns 0 on success.  Returns non-zero on failure, and sets errno.  On
                                227                 :                :  * success, the caller is responsible for calling pg_localeconv_free() on the
                                228                 :                :  * output struct to free the string members it contains.
                                229                 :                :  */
                                230                 :                : int
                                231                 :             28 : pg_localeconv_r(const char *lc_monetary,
                                232                 :                :                 const char *lc_numeric,
                                233                 :                :                 struct lconv *output)
                                234                 :                : {
                                235                 :                : #ifdef WIN32
                                236                 :                :     wchar_t    *save_lc_ctype = NULL;
                                237                 :                :     wchar_t    *save_lc_monetary = NULL;
                                238                 :                :     wchar_t    *save_lc_numeric = NULL;
                                239                 :                :     int         save_config_thread_locale;
                                240                 :                :     int         result = -1;
                                241                 :                : 
                                242                 :                :     /* Put setlocale() into thread-local mode. */
                                243                 :                :     save_config_thread_locale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
                                244                 :                : 
                                245                 :                :     /*
                                246                 :                :      * Capture the current values as wide strings.  Otherwise, we might not be
                                247                 :                :      * able to restore them if their names contain non-ASCII characters and
                                248                 :                :      * the intermediate locale changes the expected encoding.  We don't want
                                249                 :                :      * to leave the caller in an unexpected state by failing to restore, or
                                250                 :                :      * crash the runtime library.
                                251                 :                :      */
                                252                 :                :     save_lc_ctype = _wsetlocale(LC_CTYPE, NULL);
                                253                 :                :     if (!save_lc_ctype || !(save_lc_ctype = wcsdup(save_lc_ctype)))
                                254                 :                :         goto exit;
                                255                 :                :     save_lc_monetary = _wsetlocale(LC_MONETARY, NULL);
                                256                 :                :     if (!save_lc_monetary || !(save_lc_monetary = wcsdup(save_lc_monetary)))
                                257                 :                :         goto exit;
                                258                 :                :     save_lc_numeric = _wsetlocale(LC_NUMERIC, NULL);
                                259                 :                :     if (!save_lc_numeric || !(save_lc_numeric = wcsdup(save_lc_numeric)))
                                260                 :                :         goto exit;
                                261                 :                : 
                                262                 :                :     memset(output, 0, sizeof(*output));
                                263                 :                : 
                                264                 :                :     /* Copy the LC_MONETARY members. */
                                265                 :                :     if (!setlocale(LC_ALL, lc_monetary))
                                266                 :                :         goto exit;
                                267                 :                :     result = pg_localeconv_copy_members(output, localeconv(), LC_MONETARY);
                                268                 :                :     if (result != 0)
                                269                 :                :         goto exit;
                                270                 :                : 
                                271                 :                :     /* Copy the LC_NUMERIC members. */
                                272                 :                :     if (!setlocale(LC_ALL, lc_numeric))
                                273                 :                :         goto exit;
                                274                 :                :     result = pg_localeconv_copy_members(output, localeconv(), LC_NUMERIC);
                                275                 :                : 
                                276                 :                : exit:
                                277                 :                :     /* Restore everything we changed. */
                                278                 :                :     if (save_lc_ctype)
                                279                 :                :     {
                                280                 :                :         _wsetlocale(LC_CTYPE, save_lc_ctype);
                                281                 :                :         free(save_lc_ctype);
                                282                 :                :     }
                                283                 :                :     if (save_lc_monetary)
                                284                 :                :     {
                                285                 :                :         _wsetlocale(LC_MONETARY, save_lc_monetary);
                                286                 :                :         free(save_lc_monetary);
                                287                 :                :     }
                                288                 :                :     if (save_lc_numeric)
                                289                 :                :     {
                                290                 :                :         _wsetlocale(LC_NUMERIC, save_lc_numeric);
                                291                 :                :         free(save_lc_numeric);
                                292                 :                :     }
                                293                 :                :     _configthreadlocale(save_config_thread_locale);
                                294                 :                : 
                                295                 :                :     return result;
                                296                 :                : 
                                297                 :                : #else                           /* !WIN32 */
                                298                 :                :     locale_t    monetary_locale;
                                299                 :                :     locale_t    numeric_locale;
                                300                 :                :     int         result;
                                301                 :                : 
                                302                 :                :     /*
                                303                 :                :      * All variations on Unix require locale_t objects for LC_MONETARY and
                                304                 :                :      * LC_NUMERIC.  We'll set all locale categories, so that we can don't have
                                305                 :                :      * to worry about POSIX's undefined behavior if LC_CTYPE's encoding
                                306                 :                :      * doesn't match.
                                307                 :                :      */
                                308                 :             28 :     errno = ENOENT;
                                309                 :             28 :     monetary_locale = newlocale(LC_ALL_MASK, lc_monetary, 0);
                                310         [ -  + ]:             28 :     if (monetary_locale == 0)
  163 peter@eisentraut.org      311                 :UBC           0 :         return -1;
  163 peter@eisentraut.org      312                 :CBC          28 :     numeric_locale = newlocale(LC_ALL_MASK, lc_numeric, 0);
                                313         [ -  + ]:             28 :     if (numeric_locale == 0)
                                314                 :                :     {
  163 peter@eisentraut.org      315                 :UBC           0 :         freelocale(monetary_locale);
                                316                 :              0 :         return -1;
                                317                 :                :     }
                                318                 :                : 
  163 peter@eisentraut.org      319                 :CBC          28 :     memset(output, 0, sizeof(*output));
                                320                 :                : #if defined(TRANSLATE_FROM_LANGINFO)
                                321                 :                :     /* Copy from non-standard nl_langinfo_l() extended items. */
                                322                 :             28 :     result = pg_localeconv_from_langinfo(output,
                                323                 :                :                                          monetary_locale,
                                324                 :                :                                          numeric_locale);
                                325                 :                : #elif defined(HAVE_LOCALECONV_L)
                                326                 :                :     /* Copy the LC_MONETARY members from a thread-safe lconv object. */
                                327                 :                :     result = pg_localeconv_copy_members(output,
                                328                 :                :                                         localeconv_l(monetary_locale),
                                329                 :                :                                         LC_MONETARY);
                                330                 :                :     if (result == 0)
                                331                 :                :     {
                                332                 :                :         /* Copy the LC_NUMERIC members from a thread-safe lconv object. */
                                333                 :                :         result = pg_localeconv_copy_members(output,
                                334                 :                :                                             localeconv_l(numeric_locale),
                                335                 :                :                                             LC_NUMERIC);
                                336                 :                :     }
                                337                 :                : #else
                                338                 :                :     /* We have nothing better than standard POSIX facilities. */
                                339                 :                :     {
                                340                 :                :         static pthread_mutex_t big_lock = PTHREAD_MUTEX_INITIALIZER;
                                341                 :                :         locale_t    save_locale;
                                342                 :                : 
                                343                 :                :         pthread_mutex_lock(&big_lock);
                                344                 :                :         /* Copy the LC_MONETARY members. */
                                345                 :                :         save_locale = uselocale(monetary_locale);
                                346                 :                :         result = pg_localeconv_copy_members(output,
                                347                 :                :                                             localeconv(),
                                348                 :                :                                             LC_MONETARY);
                                349                 :                :         if (result == 0)
                                350                 :                :         {
                                351                 :                :             /* Copy the LC_NUMERIC members. */
                                352                 :                :             uselocale(numeric_locale);
                                353                 :                :             result = pg_localeconv_copy_members(output,
                                354                 :                :                                                 localeconv(),
                                355                 :                :                                                 LC_NUMERIC);
                                356                 :                :         }
                                357                 :                :         pthread_mutex_unlock(&big_lock);
                                358                 :                : 
                                359                 :                :         uselocale(save_locale);
                                360                 :                :     }
                                361                 :                : #endif
                                362                 :                : 
                                363                 :             28 :     freelocale(monetary_locale);
                                364                 :             28 :     freelocale(numeric_locale);
                                365                 :                : 
                                366                 :             28 :     return result;
                                367                 :                : #endif                          /* !WIN32 */
                                368                 :                : }
        

Generated by: LCOV version 2.4-beta