/*-------------------------------------------------------------------------
*
* pg_localeconv_r . c
* Thread - safe implementations of localeconv ( )
*
* Portions Copyright ( c ) 1996 - 2025 , PostgreSQL Global Development Group
* Portions Copyright ( c ) 1994 , Regents of the University of California
*
*
* IDENTIFICATION
* src / port / pg_localeconv_r . c
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#include "c.h"
#if !defined (WIN32)
#include <langinfo.h>
#include <pthread.h>
#endif
#include <limits.h>
#ifdef MON_THOUSANDS_SEP
/*
* One of glibc ' s extended langinfo items detected . Assume that the full set
* is present , which means we can use nl_langinfo_l ( ) instead of localeconv ( ) .
*/
#define TRANSLATE_FROM_LANGINFO
#endif
struct lconv_member_info
{
bool is_string;
int category;
size_t offset;
#ifdef TRANSLATE_FROM_LANGINFO
nl_item item;
#endif
};
/* Some macros to declare the lconv members compactly. */
#ifdef TRANSLATE_FROM_LANGINFO
#define LCONV_M(is_string, category, name, item) \
{ is_string, category, offsetof(struct lconv, name), item }
#else
#define LCONV_M(is_string, category, name, item) \
{ is_string, category, offsetof(struct lconv, name) }
#endif
#define LCONV_S(c, n, i) LCONV_M(true , c, n, i)
#define LCONV_C(c, n, i) LCONV_M(false , c, n, i)
/*
* The work of populating lconv objects is driven by this table . Since we
* tolerate non - matching encodings in LC_NUMERIC and LC_MONETARY , we have to
* call the underlying OS routine multiple times , with the correct locales .
* The first column of this table says which locale category applies to each struct
* member . The second column is the name of the struct member . The third
* column is the name of the nl_item , if translating from nl_langinfo_l ( ) ( it ' s
* always the member name , in upper case ) .
*/
static const struct lconv_member_info table[] = {
/* String fields. */
LCONV_S(LC_NUMERIC, decimal_point, DECIMAL_POINT),
LCONV_S(LC_NUMERIC, thousands_sep, THOUSANDS_SEP),
LCONV_S(LC_NUMERIC, grouping, GROUPING),
LCONV_S(LC_MONETARY, int_curr_symbol, INT_CURR_SYMBOL),
LCONV_S(LC_MONETARY, currency_symbol, CURRENCY_SYMBOL),
LCONV_S(LC_MONETARY, mon_decimal_point, MON_DECIMAL_POINT),
LCONV_S(LC_MONETARY, mon_thousands_sep, MON_THOUSANDS_SEP),
LCONV_S(LC_MONETARY, mon_grouping, MON_GROUPING),
LCONV_S(LC_MONETARY, positive_sign, POSITIVE_SIGN),
LCONV_S(LC_MONETARY, negative_sign, NEGATIVE_SIGN),
/* Character fields. */
LCONV_C(LC_MONETARY, int_frac_digits, INT_FRAC_DIGITS),
LCONV_C(LC_MONETARY, frac_digits, FRAC_DIGITS),
LCONV_C(LC_MONETARY, p_cs_precedes, P_CS_PRECEDES),
LCONV_C(LC_MONETARY, p_sep_by_space, P_SEP_BY_SPACE),
LCONV_C(LC_MONETARY, n_cs_precedes, N_CS_PRECEDES),
LCONV_C(LC_MONETARY, n_sep_by_space, N_SEP_BY_SPACE),
LCONV_C(LC_MONETARY, p_sign_posn, P_SIGN_POSN),
LCONV_C(LC_MONETARY, n_sign_posn, N_SIGN_POSN),
};
static inline char **
lconv_string_member(struct lconv *lconv, int i)
{
return (char **) ((char *) lconv + table[i].offset);
}
static inline char *
lconv_char_member(struct lconv *lconv, int i)
{
return (char *) lconv + table[i].offset;
}
/*
* Free the members of a struct lconv populated by pg_localeconv_r ( ) . The
* struct itself is in storage provided by the caller of pg_localeconv_r ( ) .
*/
void
pg_localeconv_free(struct lconv *lconv)
{
for (int i = 0 ; i < lengthof(table); ++i)
if (table[i].is_string)
free(*lconv_string_member(lconv, i));
}
#ifdef TRANSLATE_FROM_LANGINFO
/*
* Fill in struct lconv members using the equivalent nl_langinfo_l ( ) items .
*/
static int
pg_localeconv_from_langinfo(struct lconv *dst,
locale_t monetary_locale,
locale_t numeric_locale)
{
for (int i = 0 ; i < lengthof(table); ++i)
{
locale_t locale;
locale = table[i].category == LC_NUMERIC ?
numeric_locale : monetary_locale;
if (table[i].is_string)
{
char *string;
string = nl_langinfo_l(table[i].item, locale);
if (!(string = strdup(string)))
{
pg_localeconv_free(dst);
errno = ENOMEM;
return -1 ;
}
*lconv_string_member(dst, i) = string;
}
else
{
*lconv_char_member(dst, i) =
*nl_langinfo_l(table[i].item, locale);
}
}
return 0 ;
}
#else /* not TRANSLATE_FROM_LANGINFO */
/*
* Copy members from a given category . Note that you have to call this twice
* to copy the LC_MONETARY and then LC_NUMERIC members .
*/
static int
pg_localeconv_copy_members(struct lconv *dst,
struct lconv *src,
int category)
{
for (int i = 0 ; i < lengthof(table); ++i)
{
if (table[i].category != category)
continue ;
if (table[i].is_string)
{
char *string;
string = *lconv_string_member(src, i);
if (!(string = strdup(string)))
{
pg_localeconv_free(dst);
errno = ENOMEM;
return -1 ;
}
*lconv_string_member(dst, i) = string;
}
else
{
*lconv_char_member(dst, i) = *lconv_char_member(src, i);
}
}
return 0 ;
}
#endif /* not TRANSLATE_FROM_LANGINFO */
/*
* A thread - safe routine to get a copy of the lconv struct for a given
* LC_NUMERIC and LC_MONETARY . Different approaches are used on different
* OSes , because the standard interface is so multi - threading unfriendly .
*
* 1 . On Windows , there is no uselocale ( ) , but there is a way to put
* setlocale ( ) into a thread - local mode temporarily . Its localeconv ( ) is
* documented as returning a pointer to thread - local storage , so we don ' t have
* to worry about concurrent callers .
*
* 2 . On Glibc , as an extension , all the information required to populate
* struct lconv is also available via nl_langpath_l ( ) , which is thread - safe .
*
* 3 . On macOS and * BSD , there is localeconv_l ( ) , so we can create a temporary
* locale_t to pass in , and the result is a pointer to storage associated with
* the locale_t so we control its lifetime and we don ' t have to worry about
* concurrent calls clobbering it .
*
* 4 . Otherwise , we wrap plain old localeconv ( ) in uselocale ( ) to avoid
* touching the global locale , but the output buffer is allowed by the standard
* to be overwritten by concurrent calls to localeconv ( ) . We protect against
* _ this_ function doing that with a Big Lock , but there isn ' t much we can do
* about code outside our tree that might call localeconv ( ) , given such a poor
* interface .
*
* The POSIX standard explicitly says that it is undefined what happens if
* LC_MONETARY or LC_NUMERIC imply an encoding ( codeset ) different from that
* implied by LC_CTYPE . In practice , all Unix - ish platforms seem to believe
* that localeconv ( ) should return strings that are encoded in the codeset
* implied by the LC_MONETARY or LC_NUMERIC locale name . On Windows , LC_CTYPE
* has to match to get sane results .
*
* To get predictable results on all platforms , we ' ll call the underlying
* routines with LC_ALL set to the appropriate locale for each set of members ,
* and merge the results . Three members of the resulting object are therefore
* guaranteed to be encoded with LC_NUMERIC ' s codeset : " decimal_point " ,
* " thousands_sep " and " grouping " . All other members are encoded with
* LC_MONETARY ' s codeset .
*
* Returns 0 on success . Returns non - zero on failure , and sets errno . On
* success , the caller is responsible for calling pg_localeconv_free ( ) on the
* output struct to free the string members it contains .
*/
int
pg_localeconv_r(const char *lc_monetary,
const char *lc_numeric,
struct lconv *output)
{
#ifdef WIN32
wchar_t *save_lc_ctype = NULL;
wchar_t *save_lc_monetary = NULL;
wchar_t *save_lc_numeric = NULL;
int save_config_thread_locale;
int result = -1 ;
/* Put setlocale() into thread-local mode. */
save_config_thread_locale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
/*
* Capture the current values as wide strings . Otherwise , we might not be
* able to restore them if their names contain non - ASCII characters and
* the intermediate locale changes the expected encoding . We don ' t want
* to leave the caller in an unexpected state by failing to restore , or
* crash the runtime library .
*/
save_lc_ctype = _wsetlocale(LC_CTYPE, NULL);
if (!save_lc_ctype || !(save_lc_ctype = wcsdup(save_lc_ctype)))
goto exit ;
save_lc_monetary = _wsetlocale(LC_MONETARY, NULL);
if (!save_lc_monetary || !(save_lc_monetary = wcsdup(save_lc_monetary)))
goto exit ;
save_lc_numeric = _wsetlocale(LC_NUMERIC, NULL);
if (!save_lc_numeric || !(save_lc_numeric = wcsdup(save_lc_numeric)))
goto exit ;
memset(output, 0 , sizeof (*output));
/* Copy the LC_MONETARY members. */
if (!setlocale(LC_ALL, lc_monetary))
goto exit ;
result = pg_localeconv_copy_members(output, localeconv(), LC_MONETARY);
if (result != 0 )
goto exit ;
/* Copy the LC_NUMERIC members. */
if (!setlocale(LC_ALL, lc_numeric))
goto exit ;
result = pg_localeconv_copy_members(output, localeconv(), LC_NUMERIC);
exit :
/* Restore everything we changed. */
if (save_lc_ctype)
{
_wsetlocale(LC_CTYPE, save_lc_ctype);
free(save_lc_ctype);
}
if (save_lc_monetary)
{
_wsetlocale(LC_MONETARY, save_lc_monetary);
free(save_lc_monetary);
}
if (save_lc_numeric)
{
_wsetlocale(LC_NUMERIC, save_lc_numeric);
free(save_lc_numeric);
}
_configthreadlocale(save_config_thread_locale);
return result;
#else /* !WIN32 */
locale_t monetary_locale;
locale_t numeric_locale;
int result;
/*
* All variations on Unix require locale_t objects for LC_MONETARY and
* LC_NUMERIC . We ' ll set all locale categories , so that we can don ' t have
* to worry about POSIX ' s undefined behavior if LC_CTYPE ' s encoding
* doesn ' t match .
*/
errno = ENOENT;
monetary_locale = newlocale(LC_ALL_MASK, lc_monetary, 0 );
if (monetary_locale == 0 )
return -1 ;
numeric_locale = newlocale(LC_ALL_MASK, lc_numeric, 0 );
if (numeric_locale == 0 )
{
freelocale(monetary_locale);
return -1 ;
}
memset(output, 0 , sizeof (*output));
#if defined (TRANSLATE_FROM_LANGINFO)
/* Copy from non-standard nl_langinfo_l() extended items. */
result = pg_localeconv_from_langinfo(output,
monetary_locale,
numeric_locale);
#elif defined (HAVE_LOCALECONV_L)
/* Copy the LC_MONETARY members from a thread-safe lconv object. */
result = pg_localeconv_copy_members(output,
localeconv_l(monetary_locale),
LC_MONETARY);
if (result == 0 )
{
/* Copy the LC_NUMERIC members from a thread-safe lconv object. */
result = pg_localeconv_copy_members(output,
localeconv_l(numeric_locale),
LC_NUMERIC);
}
#else
/* We have nothing better than standard POSIX facilities. */
{
static pthread_mutex_t big_lock = PTHREAD_MUTEX_INITIALIZER;
locale_t save_locale;
pthread_mutex_lock(&big_lock);
/* Copy the LC_MONETARY members. */
save_locale = uselocale(monetary_locale);
result = pg_localeconv_copy_members(output,
localeconv(),
LC_MONETARY);
if (result == 0 )
{
/* Copy the LC_NUMERIC members. */
uselocale(numeric_locale);
result = pg_localeconv_copy_members(output,
localeconv(),
LC_NUMERIC);
}
pthread_mutex_unlock(&big_lock);
uselocale(save_locale);
}
#endif
freelocale(monetary_locale);
freelocale(numeric_locale);
return result;
#endif /* !WIN32 */
}
Messung V0.5 in Prozent C=94 H=83 G=88
¤ Dauer der Verarbeitung: 0.21 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland