1 /* Query the name of the current global locale.
2 Copyright (C) 2019-2023 Free Software Foundation, Inc.
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.
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.
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/>. */
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
);