Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Isabelle/HOL/Computational_Algebra/   (Isabelle Prover Version 2025-1©)  Datei vom 16.11.2025 mit Größe 41 kB image not shown  

 pg_localeconv_r.c  Interaktion und
Portierbarkeitunbekannt

 
/*-------------------------------------------------------------------------
 *
 * 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

[Verzeichnis aufwärts0.15unsichere VerbindungÜbersetzung europäischer Sprachen durch Browser2026-09-27]