123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- /* Query the name of the current global locale.
- Copyright (C) 2019-2023 Free Software Foundation, Inc.
- This file is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as
- published by the Free Software Foundation; either version 2.1 of the
- License, or (at your option) any later version.
- This file is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>. */
- /* Written by Bruno Haible <bruno@clisp.org>, 2019. */
- #include <config.h>
- /* Specification. */
- #include "setlocale_null.h"
- #include <errno.h>
- #include <locale.h>
- #include <stdlib.h>
- #include <string.h>
- #if defined _WIN32 && !defined __CYGWIN__
- # include <wchar.h>
- #endif
- #if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE)
- # if defined _WIN32 && !defined __CYGWIN__
- # define WIN32_LEAN_AND_MEAN /* avoid including junk */
- # include <windows.h>
- # elif HAVE_PTHREAD_API
- # include <pthread.h>
- # if HAVE_THREADS_H && HAVE_WEAK_SYMBOLS
- # include <threads.h>
- # pragma weak thrd_exit
- # define c11_threads_in_use() (thrd_exit != NULL)
- # else
- # define c11_threads_in_use() 0
- # endif
- # elif HAVE_THREADS_H
- # include <threads.h>
- # endif
- #endif
- /* Use the system's setlocale() function, not the gnulib override, here. */
- #undef setlocale
- static const char *
- setlocale_null_androidfix (int category)
- {
- const char *result = setlocale (category, NULL);
- #ifdef __ANDROID__
- if (result == NULL)
- switch (category)
- {
- case LC_CTYPE:
- case LC_NUMERIC:
- case LC_TIME:
- case LC_COLLATE:
- case LC_MONETARY:
- case LC_MESSAGES:
- case LC_ALL:
- case LC_PAPER:
- case LC_NAME:
- case LC_ADDRESS:
- case LC_TELEPHONE:
- case LC_MEASUREMENT:
- result = "C";
- break;
- default:
- break;
- }
- #endif
- return result;
- }
- static int
- setlocale_null_unlocked (int category, char *buf, size_t bufsize)
- {
- #if defined _WIN32 && !defined __CYGWIN__ && defined _MSC_VER
- /* On native Windows, nowadays, the setlocale() implementation is based
- on _wsetlocale() and uses malloc() for the result. We are better off
- using _wsetlocale() directly. */
- const wchar_t *result = _wsetlocale (category, NULL);
- if (result == NULL)
- {
- /* CATEGORY is invalid. */
- if (bufsize > 0)
- /* Return an empty string in BUF.
- This is a convenience for callers that don't want to write explicit
- code for handling EINVAL. */
- buf[0] = '\0';
- return EINVAL;
- }
- else
- {
- size_t length = wcslen (result);
- if (length < bufsize)
- {
- size_t i;
- /* Convert wchar_t[] -> char[], assuming plain ASCII. */
- for (i = 0; i <= length; i++)
- buf[i] = result[i];
- return 0;
- }
- else
- {
- if (bufsize > 0)
- {
- /* Return a truncated result in BUF.
- This is a convenience for callers that don't want to write
- explicit code for handling ERANGE. */
- size_t i;
- /* Convert wchar_t[] -> char[], assuming plain ASCII. */
- for (i = 0; i < bufsize; i++)
- buf[i] = result[i];
- buf[bufsize - 1] = '\0';
- }
- return ERANGE;
- }
- }
- #else
- const char *result = setlocale_null_androidfix (category);
- if (result == NULL)
- {
- /* CATEGORY is invalid. */
- if (bufsize > 0)
- /* Return an empty string in BUF.
- This is a convenience for callers that don't want to write explicit
- code for handling EINVAL. */
- buf[0] = '\0';
- return EINVAL;
- }
- else
- {
- size_t length = strlen (result);
- if (length < bufsize)
- {
- memcpy (buf, result, length + 1);
- return 0;
- }
- else
- {
- if (bufsize > 0)
- {
- /* Return a truncated result in BUF.
- This is a convenience for callers that don't want to write
- explicit code for handling ERANGE. */
- memcpy (buf, result, bufsize - 1);
- buf[bufsize - 1] = '\0';
- }
- return ERANGE;
- }
- }
- #endif
- }
- #if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE) /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin */
- /* Use a lock, so that no two threads can invoke setlocale_null_unlocked
- at the same time. */
- /* Prohibit renaming this symbol. */
- # undef gl_get_setlocale_null_lock
- # if defined _WIN32 && !defined __CYGWIN__
- extern __declspec(dllimport) CRITICAL_SECTION *gl_get_setlocale_null_lock (void);
- static int
- setlocale_null_with_lock (int category, char *buf, size_t bufsize)
- {
- CRITICAL_SECTION *lock = gl_get_setlocale_null_lock ();
- int ret;
- EnterCriticalSection (lock);
- ret = setlocale_null_unlocked (category, buf, bufsize);
- LeaveCriticalSection (lock);
- return ret;
- }
- # elif HAVE_PTHREAD_API /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin */
- extern
- # if defined _WIN32 || defined __CYGWIN__
- __declspec(dllimport)
- # endif
- pthread_mutex_t *gl_get_setlocale_null_lock (void);
- # if HAVE_WEAK_SYMBOLS /* musl libc, FreeBSD, NetBSD, OpenBSD, Haiku */
- /* Avoid the need to link with '-lpthread'. */
- # pragma weak pthread_mutex_lock
- # pragma weak pthread_mutex_unlock
- /* Determine whether libpthread is in use. */
- # pragma weak pthread_mutexattr_gettype
- /* See the comments in lock.h. */
- # define pthread_in_use() \
- (pthread_mutexattr_gettype != NULL || c11_threads_in_use ())
- # else
- # define pthread_in_use() 1
- # endif
- static int
- setlocale_null_with_lock (int category, char *buf, size_t bufsize)
- {
- if (pthread_in_use())
- {
- pthread_mutex_t *lock = gl_get_setlocale_null_lock ();
- int ret;
- if (pthread_mutex_lock (lock))
- abort ();
- ret = setlocale_null_unlocked (category, buf, bufsize);
- if (pthread_mutex_unlock (lock))
- abort ();
- return ret;
- }
- else
- return setlocale_null_unlocked (category, buf, bufsize);
- }
- # elif HAVE_THREADS_H
- extern mtx_t *gl_get_setlocale_null_lock (void);
- static int
- setlocale_null_with_lock (int category, char *buf, size_t bufsize)
- {
- mtx_t *lock = gl_get_setlocale_null_lock ();
- int ret;
- if (mtx_lock (lock) != thrd_success)
- abort ();
- ret = setlocale_null_unlocked (category, buf, bufsize);
- if (mtx_unlock (lock) != thrd_success)
- abort ();
- return ret;
- }
- # endif
- #endif
- int
- setlocale_null_r (int category, char *buf, size_t bufsize)
- {
- #if SETLOCALE_NULL_ALL_MTSAFE
- # if SETLOCALE_NULL_ONE_MTSAFE
- return setlocale_null_unlocked (category, buf, bufsize);
- # else
- if (category == LC_ALL)
- return setlocale_null_unlocked (category, buf, bufsize);
- else
- return setlocale_null_with_lock (category, buf, bufsize);
- # endif
- #else
- # if SETLOCALE_NULL_ONE_MTSAFE
- if (category == LC_ALL)
- return setlocale_null_with_lock (category, buf, bufsize);
- else
- return setlocale_null_unlocked (category, buf, bufsize);
- # else
- return setlocale_null_with_lock (category, buf, bufsize);
- # endif
- #endif
- }
- const char *
- setlocale_null (int category)
- {
- #if SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE
- return setlocale_null_androidfix (category);
- #else
- /* This call must be multithread-safe. To achieve this without using
- thread-local storage:
- 1. We use a specific static buffer for each possible CATEGORY
- argument. So that different threads can call setlocale_mtsafe
- with different CATEGORY arguments, without interfering.
- 2. We use a simple strcpy or memcpy to fill this static buffer.
- Filling it through, for example, strcpy + strcat would not be
- guaranteed to leave the buffer's contents intact if another thread
- is currently accessing it. If necessary, the contents is first
- assembled in a stack-allocated buffer. */
- if (category == LC_ALL)
- {
- # if SETLOCALE_NULL_ALL_MTSAFE
- return setlocale_null_androidfix (LC_ALL);
- # else
- char buf[SETLOCALE_NULL_ALL_MAX];
- static char resultbuf[SETLOCALE_NULL_ALL_MAX];
- if (setlocale_null_r (LC_ALL, buf, sizeof (buf)))
- return "C";
- strcpy (resultbuf, buf);
- return resultbuf;
- # endif
- }
- else
- {
- # if SETLOCALE_NULL_ONE_MTSAFE
- return setlocale_null_androidfix (category);
- # else
- enum
- {
- LC_CTYPE_INDEX,
- LC_NUMERIC_INDEX,
- LC_TIME_INDEX,
- LC_COLLATE_INDEX,
- LC_MONETARY_INDEX,
- LC_MESSAGES_INDEX,
- # ifdef LC_PAPER
- LC_PAPER_INDEX,
- # endif
- # ifdef LC_NAME
- LC_NAME_INDEX,
- # endif
- # ifdef LC_ADDRESS
- LC_ADDRESS_INDEX,
- # endif
- # ifdef LC_TELEPHONE
- LC_TELEPHONE_INDEX,
- # endif
- # ifdef LC_MEASUREMENT
- LC_MEASUREMENT_INDEX,
- # endif
- # ifdef LC_IDENTIFICATION
- LC_IDENTIFICATION_INDEX,
- # endif
- LC_INDICES_COUNT
- }
- i;
- char buf[SETLOCALE_NULL_MAX];
- static char resultbuf[LC_INDICES_COUNT][SETLOCALE_NULL_MAX];
- int err;
- err = setlocale_null_r (category, buf, sizeof (buf));
- if (err == EINVAL)
- return NULL;
- if (err)
- return "C";
- switch (category)
- {
- case LC_CTYPE: i = LC_CTYPE_INDEX; break;
- case LC_NUMERIC: i = LC_NUMERIC_INDEX; break;
- case LC_TIME: i = LC_TIME_INDEX; break;
- case LC_COLLATE: i = LC_COLLATE_INDEX; break;
- case LC_MONETARY: i = LC_MONETARY_INDEX; break;
- case LC_MESSAGES: i = LC_MESSAGES_INDEX; break;
- # ifdef LC_PAPER
- case LC_PAPER: i = LC_PAPER_INDEX; break;
- # endif
- # ifdef LC_NAME
- case LC_NAME: i = LC_NAME_INDEX; break;
- # endif
- # ifdef LC_ADDRESS
- case LC_ADDRESS: i = LC_ADDRESS_INDEX; break;
- # endif
- # ifdef LC_TELEPHONE
- case LC_TELEPHONE: i = LC_TELEPHONE_INDEX; break;
- # endif
- # ifdef LC_MEASUREMENT
- case LC_MEASUREMENT: i = LC_MEASUREMENT_INDEX; break;
- # endif
- # ifdef LC_IDENTIFICATION
- case LC_IDENTIFICATION: i = LC_IDENTIFICATION_INDEX; break;
- # endif
- default:
- /* If you get here, a #ifdef LC_xxx is missing. */
- abort ();
- }
- strcpy (resultbuf[i], buf);
- return resultbuf[i];
- # endif
- }
- #endif
- }
|