1 /* Query the name of the current global locale.
2 Copyright (C) 2019-2021 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3 of the License, or
7 (at your option) any later version.
9 This program 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 General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2019. */
22 #include "setlocale_null.h"
28 #if defined _WIN32 && !defined __CYGWIN__
32 #if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE)
33 # if defined _WIN32 && !defined __CYGWIN__
35 # define WIN32_LEAN_AND_MEAN /* avoid including junk */
38 # elif HAVE_PTHREAD_API
41 # if HAVE_THREADS_H && HAVE_WEAK_SYMBOLS
43 # pragma weak thrd_exit
44 # define c11_threads_in_use() (thrd_exit != NULL)
46 # define c11_threads_in_use() 0
56 /* Use the system's setlocale() function, not the gnulib override, here. */
60 setlocale_null_androidfix (int category)
62 const char *result = setlocale (category, NULL);
91 setlocale_null_unlocked (int category, char *buf, size_t bufsize)
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);
101 /* CATEGORY is invalid. */
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. */
111 size_t length = wcslen (result);
112 if (length < bufsize)
116 /* Convert wchar_t[] -> char[], assuming plain ASCII. */
117 for (i = 0; i <= length; i++)
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. */
131 /* Convert wchar_t[] -> char[], assuming plain ASCII. */
132 for (i = 0; i < bufsize; i++)
134 buf[bufsize - 1] = '\0';
140 const char *result = setlocale_null_androidfix (category);
144 /* CATEGORY is invalid. */
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. */
154 size_t length = strlen (result);
155 if (length < bufsize)
157 memcpy (buf, result, length + 1);
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';
176 #if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE) /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin */
178 /* Use a lock, so that no two threads can invoke setlocale_null_unlocked
181 /* Prohibit renaming this symbol. */
182 # undef gl_get_setlocale_null_lock
184 # if defined _WIN32 && !defined __CYGWIN__
186 extern __declspec(dllimport) CRITICAL_SECTION *gl_get_setlocale_null_lock (void);
189 setlocale_null_with_lock (int category, char *buf, size_t bufsize)
191 CRITICAL_SECTION *lock = gl_get_setlocale_null_lock ();
194 EnterCriticalSection (lock);
195 ret = setlocale_null_unlocked (category, buf, bufsize);
196 LeaveCriticalSection (lock);
201 # elif HAVE_PTHREAD_API /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin */
204 # if defined _WIN32 || defined __CYGWIN__
205 __declspec(dllimport)
207 pthread_mutex_t *gl_get_setlocale_null_lock (void);
209 # if HAVE_WEAK_SYMBOLS /* musl libc, FreeBSD, NetBSD, OpenBSD, Haiku */
211 /* Avoid the need to link with '-lpthread'. */
212 # pragma weak pthread_mutex_lock
213 # pragma weak pthread_mutex_unlock
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 ())
222 # define pthread_in_use() 1
226 setlocale_null_with_lock (int category, char *buf, size_t bufsize)
228 if (pthread_in_use())
230 pthread_mutex_t *lock = gl_get_setlocale_null_lock ();
233 if (pthread_mutex_lock (lock))
235 ret = setlocale_null_unlocked (category, buf, bufsize);
236 if (pthread_mutex_unlock (lock))
242 return setlocale_null_unlocked (category, buf, bufsize);
245 # elif HAVE_THREADS_H
247 extern mtx_t *gl_get_setlocale_null_lock (void);
250 setlocale_null_with_lock (int category, char *buf, size_t bufsize)
252 mtx_t *lock = gl_get_setlocale_null_lock ();
255 if (mtx_lock (lock) != thrd_success)
257 ret = setlocale_null_unlocked (category, buf, bufsize);
258 if (mtx_unlock (lock) != thrd_success)
269 setlocale_null_r (int category, char *buf, size_t bufsize)
271 #if SETLOCALE_NULL_ALL_MTSAFE
272 # if SETLOCALE_NULL_ONE_MTSAFE
274 return setlocale_null_unlocked (category, buf, bufsize);
278 if (category == LC_ALL)
279 return setlocale_null_unlocked (category, buf, bufsize);
281 return setlocale_null_with_lock (category, buf, bufsize);
285 # if SETLOCALE_NULL_ONE_MTSAFE
287 if (category == LC_ALL)
288 return setlocale_null_with_lock (category, buf, bufsize);
290 return setlocale_null_unlocked (category, buf, bufsize);
294 return setlocale_null_with_lock (category, buf, bufsize);
301 setlocale_null (int category)
303 #if SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE
304 return setlocale_null_androidfix (category);
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)
319 # if SETLOCALE_NULL_ALL_MTSAFE
320 return setlocale_null_androidfix (LC_ALL);
322 char buf[SETLOCALE_NULL_ALL_MAX];
323 static char resultbuf[SETLOCALE_NULL_ALL_MAX];
325 if (setlocale_null_r (LC_ALL, buf, sizeof (buf)))
327 strcpy (resultbuf, buf);
333 # if SETLOCALE_NULL_ONE_MTSAFE
334 return setlocale_null_androidfix (category);
356 # ifdef LC_MEASUREMENT
357 LC_MEASUREMENT_INDEX,
359 # ifdef LC_IDENTIFICATION
360 LC_IDENTIFICATION_INDEX,
365 char buf[SETLOCALE_NULL_MAX];
366 static char resultbuf[LC_INDICES_COUNT][SETLOCALE_NULL_MAX];
369 err = setlocale_null_r (category, buf, sizeof (buf));
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;
384 case LC_PAPER: i = LC_PAPER_INDEX; break;
387 case LC_NAME: i = LC_NAME_INDEX; break;
390 case LC_ADDRESS: i = LC_ADDRESS_INDEX; break;
393 case LC_TELEPHONE: i = LC_TELEPHONE_INDEX; break;
395 # ifdef LC_MEASUREMENT
396 case LC_MEASUREMENT: i = LC_MEASUREMENT_INDEX; break;
398 # ifdef LC_IDENTIFICATION
399 case LC_IDENTIFICATION: i = LC_IDENTIFICATION_INDEX; break;
402 /* If you get here, a #ifdef LC_xxx is missing. */
406 strcpy (resultbuf[i], buf);