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