(root)/
gettext-0.22.4/
gettext-tools/
libgettextpo/
setlocale_null.c
       1  /* Query the name of the current global locale.
       2     Copyright (C) 2019-2023 Free Software Foundation, Inc.
       3  
       4     This file is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU Lesser General Public License as
       6     published by the Free Software Foundation; either version 2.1 of the
       7     License, or (at your option) any later version.
       8  
       9     This file is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by Bruno Haible <bruno@clisp.org>, 2019.  */
      18  
      19  #include <config.h>
      20  
      21  /* Specification.  */
      22  #include "setlocale_null.h"
      23  
      24  #include <errno.h>
      25  #include <locale.h>
      26  #include <stdlib.h>
      27  #include <string.h>
      28  #if defined _WIN32 && !defined __CYGWIN__
      29  # include <wchar.h>
      30  #endif
      31  
      32  #if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE)
      33  
      34  # if AVOID_ANY_THREADS
      35  
      36  /* The option '--disable-threads' explicitly requests no locking.  */
      37  
      38  # elif defined _WIN32 && !defined __CYGWIN__
      39  
      40  #  define WIN32_LEAN_AND_MEAN  /* avoid including junk */
      41  #  include <windows.h>
      42  
      43  # elif HAVE_PTHREAD_API
      44  
      45  #  include <pthread.h>
      46  #  if HAVE_THREADS_H && HAVE_WEAK_SYMBOLS
      47  #   include <threads.h>
      48  #   pragma weak thrd_exit
      49  #   define c11_threads_in_use() (thrd_exit != NULL)
      50  #  else
      51  #   define c11_threads_in_use() 0
      52  #  endif
      53  
      54  # elif HAVE_THREADS_H
      55  
      56  #  include <threads.h>
      57  
      58  # endif
      59  
      60  #endif
      61  
      62  /* Use the system's setlocale() function, not the gnulib override, here.  */
      63  #undef setlocale
      64  
      65  static const char *
      66  setlocale_null_androidfix (int category)
      67  {
      68    const char *result = setlocale (category, NULL);
      69  
      70  #ifdef __ANDROID__
      71    if (result == NULL)
      72      switch (category)
      73        {
      74        case LC_CTYPE:
      75        case LC_NUMERIC:
      76        case LC_TIME:
      77        case LC_COLLATE:
      78        case LC_MONETARY:
      79        case LC_MESSAGES:
      80        case LC_ALL:
      81        case LC_PAPER:
      82        case LC_NAME:
      83        case LC_ADDRESS:
      84        case LC_TELEPHONE:
      85        case LC_MEASUREMENT:
      86          result = "C";
      87          break;
      88        default:
      89          break;
      90        }
      91  #endif
      92  
      93    return result;
      94  }
      95  
      96  static int
      97  setlocale_null_unlocked (int category, char *buf, size_t bufsize)
      98  {
      99  #if defined _WIN32 && !defined __CYGWIN__ && defined _MSC_VER
     100    /* On native Windows, nowadays, the setlocale() implementation is based
     101       on _wsetlocale() and uses malloc() for the result.  We are better off
     102       using _wsetlocale() directly.  */
     103    const wchar_t *result = _wsetlocale (category, NULL);
     104  
     105    if (result == NULL)
     106      {
     107        /* CATEGORY is invalid.  */
     108        if (bufsize > 0)
     109          /* Return an empty string in BUF.
     110             This is a convenience for callers that don't want to write explicit
     111             code for handling EINVAL.  */
     112          buf[0] = '\0';
     113        return EINVAL;
     114      }
     115    else
     116      {
     117        size_t length = wcslen (result);
     118        if (length < bufsize)
     119          {
     120            size_t i;
     121  
     122            /* Convert wchar_t[] -> char[], assuming plain ASCII.  */
     123            for (i = 0; i <= length; i++)
     124              buf[i] = result[i];
     125  
     126            return 0;
     127          }
     128        else
     129          {
     130            if (bufsize > 0)
     131              {
     132                /* Return a truncated result in BUF.
     133                   This is a convenience for callers that don't want to write
     134                   explicit code for handling ERANGE.  */
     135                size_t i;
     136  
     137                /* Convert wchar_t[] -> char[], assuming plain ASCII.  */
     138                for (i = 0; i < bufsize; i++)
     139                  buf[i] = result[i];
     140                buf[bufsize - 1] = '\0';
     141              }
     142            return ERANGE;
     143          }
     144      }
     145  #else
     146    const char *result = setlocale_null_androidfix (category);
     147  
     148    if (result == NULL)
     149      {
     150        /* CATEGORY is invalid.  */
     151        if (bufsize > 0)
     152          /* Return an empty string in BUF.
     153             This is a convenience for callers that don't want to write explicit
     154             code for handling EINVAL.  */
     155          buf[0] = '\0';
     156        return EINVAL;
     157      }
     158    else
     159      {
     160        size_t length = strlen (result);
     161        if (length < bufsize)
     162          {
     163            memcpy (buf, result, length + 1);
     164            return 0;
     165          }
     166        else
     167          {
     168            if (bufsize > 0)
     169              {
     170                /* Return a truncated result in BUF.
     171                   This is a convenience for callers that don't want to write
     172                   explicit code for handling ERANGE.  */
     173                memcpy (buf, result, bufsize - 1);
     174                buf[bufsize - 1] = '\0';
     175              }
     176            return ERANGE;
     177          }
     178      }
     179  #endif
     180  }
     181  
     182  #if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE) /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin < 3.4.6 */
     183  
     184  /* Use a lock, so that no two threads can invoke setlocale_null_unlocked
     185     at the same time.  */
     186  
     187  /* Prohibit renaming this symbol.  */
     188  # undef gl_get_setlocale_null_lock
     189  
     190  # if AVOID_ANY_THREADS
     191  
     192  /* The option '--disable-threads' explicitly requests no locking.  */
     193  #  define setlocale_null_with_lock setlocale_null_unlocked
     194  
     195  # elif defined _WIN32 && !defined __CYGWIN__
     196  
     197  extern __declspec(dllimport) CRITICAL_SECTION *gl_get_setlocale_null_lock (void);
     198  
     199  static int
     200  setlocale_null_with_lock (int category, char *buf, size_t bufsize)
     201  {
     202    CRITICAL_SECTION *lock = gl_get_setlocale_null_lock ();
     203    int ret;
     204  
     205    EnterCriticalSection (lock);
     206    ret = setlocale_null_unlocked (category, buf, bufsize);
     207    LeaveCriticalSection (lock);
     208  
     209    return ret;
     210  }
     211  
     212  # elif HAVE_PTHREAD_API /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin < 3.4.6 */
     213  
     214  extern
     215  #  if defined _WIN32 || defined __CYGWIN__
     216    __declspec(dllimport)
     217  #  endif
     218    pthread_mutex_t *gl_get_setlocale_null_lock (void);
     219  
     220  #  if HAVE_WEAK_SYMBOLS /* musl libc, FreeBSD, NetBSD, OpenBSD, Haiku */
     221  
     222      /* Avoid the need to link with '-lpthread'.  */
     223  #   pragma weak pthread_mutex_lock
     224  #   pragma weak pthread_mutex_unlock
     225  
     226      /* Determine whether libpthread is in use.  */
     227  #   pragma weak pthread_mutexattr_gettype
     228      /* See the comments in lock.h.  */
     229  #   define pthread_in_use() \
     230        (pthread_mutexattr_gettype != NULL || c11_threads_in_use ())
     231  
     232  #  else
     233  #   define pthread_in_use() 1
     234  #  endif
     235  
     236  static int
     237  setlocale_null_with_lock (int category, char *buf, size_t bufsize)
     238  {
     239    if (pthread_in_use())
     240      {
     241        pthread_mutex_t *lock = gl_get_setlocale_null_lock ();
     242        int ret;
     243  
     244        if (pthread_mutex_lock (lock))
     245          abort ();
     246        ret = setlocale_null_unlocked (category, buf, bufsize);
     247        if (pthread_mutex_unlock (lock))
     248          abort ();
     249  
     250        return ret;
     251      }
     252    else
     253      return setlocale_null_unlocked (category, buf, bufsize);
     254  }
     255  
     256  # elif HAVE_THREADS_H
     257  
     258  extern mtx_t *gl_get_setlocale_null_lock (void);
     259  
     260  static int
     261  setlocale_null_with_lock (int category, char *buf, size_t bufsize)
     262  {
     263    mtx_t *lock = gl_get_setlocale_null_lock ();
     264    int ret;
     265  
     266    if (mtx_lock (lock) != thrd_success)
     267      abort ();
     268    ret = setlocale_null_unlocked (category, buf, bufsize);
     269    if (mtx_unlock (lock) != thrd_success)
     270      abort ();
     271  
     272    return ret;
     273  }
     274  
     275  # endif
     276  
     277  #endif
     278  
     279  int
     280  setlocale_null_r (int category, char *buf, size_t bufsize)
     281  {
     282  #if SETLOCALE_NULL_ALL_MTSAFE
     283  # if SETLOCALE_NULL_ONE_MTSAFE
     284  
     285    return setlocale_null_unlocked (category, buf, bufsize);
     286  
     287  # else
     288  
     289    if (category == LC_ALL)
     290      return setlocale_null_unlocked (category, buf, bufsize);
     291    else
     292      return setlocale_null_with_lock (category, buf, bufsize);
     293  
     294  # endif
     295  #else
     296  # if SETLOCALE_NULL_ONE_MTSAFE
     297  
     298    if (category == LC_ALL)
     299      return setlocale_null_with_lock (category, buf, bufsize);
     300    else
     301      return setlocale_null_unlocked (category, buf, bufsize);
     302  
     303  # else
     304  
     305    return setlocale_null_with_lock (category, buf, bufsize);
     306  
     307  # endif
     308  #endif
     309  }
     310  
     311  const char *
     312  setlocale_null (int category)
     313  {
     314  #if SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE
     315    return setlocale_null_androidfix (category);
     316  #else
     317  
     318    /* This call must be multithread-safe.  To achieve this without using
     319       thread-local storage:
     320         1. We use a specific static buffer for each possible CATEGORY
     321            argument.  So that different threads can call setlocale_mtsafe
     322            with different CATEGORY arguments, without interfering.
     323         2. We use a simple strcpy or memcpy to fill this static buffer.
     324            Filling it through, for example, strcpy + strcat would not be
     325            guaranteed to leave the buffer's contents intact if another thread
     326            is currently accessing it.  If necessary, the contents is first
     327            assembled in a stack-allocated buffer.  */
     328    if (category == LC_ALL)
     329      {
     330  # if SETLOCALE_NULL_ALL_MTSAFE
     331        return setlocale_null_androidfix (LC_ALL);
     332  # else
     333        char buf[SETLOCALE_NULL_ALL_MAX];
     334        static char resultbuf[SETLOCALE_NULL_ALL_MAX];
     335  
     336        if (setlocale_null_r (LC_ALL, buf, sizeof (buf)))
     337          return "C";
     338        strcpy (resultbuf, buf);
     339        return resultbuf;
     340  # endif
     341      }
     342    else
     343      {
     344  # if SETLOCALE_NULL_ONE_MTSAFE
     345        return setlocale_null_androidfix (category);
     346  # else
     347        enum
     348          {
     349            LC_CTYPE_INDEX,
     350            LC_NUMERIC_INDEX,
     351            LC_TIME_INDEX,
     352            LC_COLLATE_INDEX,
     353            LC_MONETARY_INDEX,
     354            LC_MESSAGES_INDEX,
     355  #  ifdef LC_PAPER
     356            LC_PAPER_INDEX,
     357  #  endif
     358  #  ifdef LC_NAME
     359            LC_NAME_INDEX,
     360  #  endif
     361  #  ifdef LC_ADDRESS
     362            LC_ADDRESS_INDEX,
     363  #  endif
     364  #  ifdef LC_TELEPHONE
     365            LC_TELEPHONE_INDEX,
     366  #  endif
     367  #  ifdef LC_MEASUREMENT
     368            LC_MEASUREMENT_INDEX,
     369  #  endif
     370  #  ifdef LC_IDENTIFICATION
     371            LC_IDENTIFICATION_INDEX,
     372  #  endif
     373            LC_INDICES_COUNT
     374          }
     375          i;
     376        char buf[SETLOCALE_NULL_MAX];
     377        static char resultbuf[LC_INDICES_COUNT][SETLOCALE_NULL_MAX];
     378        int err;
     379  
     380        err = setlocale_null_r (category, buf, sizeof (buf));
     381        if (err == EINVAL)
     382          return NULL;
     383        if (err)
     384          return "C";
     385  
     386        switch (category)
     387          {
     388          case LC_CTYPE:          i = LC_CTYPE_INDEX;          break;
     389          case LC_NUMERIC:        i = LC_NUMERIC_INDEX;        break;
     390          case LC_TIME:           i = LC_TIME_INDEX;           break;
     391          case LC_COLLATE:        i = LC_COLLATE_INDEX;        break;
     392          case LC_MONETARY:       i = LC_MONETARY_INDEX;       break;
     393          case LC_MESSAGES:       i = LC_MESSAGES_INDEX;       break;
     394  #  ifdef LC_PAPER
     395          case LC_PAPER:          i = LC_PAPER_INDEX;          break;
     396  #  endif
     397  #  ifdef LC_NAME
     398          case LC_NAME:           i = LC_NAME_INDEX;           break;
     399  #  endif
     400  #  ifdef LC_ADDRESS
     401          case LC_ADDRESS:        i = LC_ADDRESS_INDEX;        break;
     402  #  endif
     403  #  ifdef LC_TELEPHONE
     404          case LC_TELEPHONE:      i = LC_TELEPHONE_INDEX;      break;
     405  #  endif
     406  #  ifdef LC_MEASUREMENT
     407          case LC_MEASUREMENT:    i = LC_MEASUREMENT_INDEX;    break;
     408  #  endif
     409  #  ifdef LC_IDENTIFICATION
     410          case LC_IDENTIFICATION: i = LC_IDENTIFICATION_INDEX; break;
     411  #  endif
     412          default:
     413            /* If you get here, a #ifdef LC_xxx is missing.  */
     414            abort ();
     415          }
     416  
     417        strcpy (resultbuf[i], buf);
     418        return resultbuf[i];
     419  # endif
     420      }
     421  #endif
     422  }