1 /* Query the name of the current global locale.
2 Copyright (C) 2019-2024 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"
29 #if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE)
31 # if AVOID_ANY_THREADS
33 /* The option '--disable-threads' explicitly requests no locking. */
35 # elif defined _WIN32 && !defined __CYGWIN__
37 # define WIN32_LEAN_AND_MEAN /* avoid including junk */
40 # elif HAVE_PTHREAD_API
43 # if HAVE_THREADS_H && HAVE_WEAK_SYMBOLS
45 # pragma weak thrd_exit
46 # define c11_threads_in_use() (thrd_exit != NULL)
48 # define c11_threads_in_use() 0
59 #if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE) /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin < 3.4.6 */
61 /* Use a lock, so that no two threads can invoke setlocale_null_r_unlocked
64 /* Prohibit renaming this symbol. */
65 # undef gl_get_setlocale_null_lock
67 # if AVOID_ANY_THREADS
69 /* The option '--disable-threads' explicitly requests no locking. */
70 # define setlocale_null_r_with_lock setlocale_null_r_unlocked
72 # elif defined _WIN32 && !defined __CYGWIN__
74 extern __declspec(dllimport
) CRITICAL_SECTION
*gl_get_setlocale_null_lock (void);
77 setlocale_null_r_with_lock (int category
, char *buf
, size_t bufsize
)
79 CRITICAL_SECTION
*lock
= gl_get_setlocale_null_lock ();
82 EnterCriticalSection (lock
);
83 ret
= setlocale_null_r_unlocked (category
, buf
, bufsize
);
84 LeaveCriticalSection (lock
);
89 # elif HAVE_PTHREAD_API /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin < 3.4.6 */
92 # if defined _WIN32 || defined __CYGWIN__
95 pthread_mutex_t
*gl_get_setlocale_null_lock (void);
97 # if HAVE_WEAK_SYMBOLS /* musl libc, FreeBSD, NetBSD, OpenBSD, Haiku */
99 /* Avoid the need to link with '-lpthread'. */
100 # pragma weak pthread_mutex_lock
101 # pragma weak pthread_mutex_unlock
103 /* Determine whether libpthread is in use. */
104 # pragma weak pthread_mutexattr_gettype
105 /* See the comments in lock.h. */
106 # define pthread_in_use() \
107 (pthread_mutexattr_gettype != NULL || c11_threads_in_use ())
110 # define pthread_in_use() 1
114 setlocale_null_r_with_lock (int category
, char *buf
, size_t bufsize
)
116 if (pthread_in_use())
118 pthread_mutex_t
*lock
= gl_get_setlocale_null_lock ();
121 if (pthread_mutex_lock (lock
))
123 ret
= setlocale_null_r_unlocked (category
, buf
, bufsize
);
124 if (pthread_mutex_unlock (lock
))
130 return setlocale_null_r_unlocked (category
, buf
, bufsize
);
133 # elif HAVE_THREADS_H
135 extern mtx_t
*gl_get_setlocale_null_lock (void);
138 setlocale_null_r_with_lock (int category
, char *buf
, size_t bufsize
)
140 mtx_t
*lock
= gl_get_setlocale_null_lock ();
143 if (mtx_lock (lock
) != thrd_success
)
145 ret
= setlocale_null_r_unlocked (category
, buf
, bufsize
);
146 if (mtx_unlock (lock
) != thrd_success
)
157 setlocale_null_r (int category
, char *buf
, size_t bufsize
)
159 #if SETLOCALE_NULL_ALL_MTSAFE
160 # if SETLOCALE_NULL_ONE_MTSAFE
162 return setlocale_null_r_unlocked (category
, buf
, bufsize
);
166 if (category
== LC_ALL
)
167 return setlocale_null_r_unlocked (category
, buf
, bufsize
);
169 return setlocale_null_r_with_lock (category
, buf
, bufsize
);
173 # if SETLOCALE_NULL_ONE_MTSAFE
175 if (category
== LC_ALL
)
176 return setlocale_null_r_with_lock (category
, buf
, bufsize
);
178 return setlocale_null_r_unlocked (category
, buf
, bufsize
);
182 return setlocale_null_r_with_lock (category
, buf
, bufsize
);
189 setlocale_null (int category
)
191 #if SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE
192 return setlocale_null_unlocked (category
);
195 /* This call must be multithread-safe. To achieve this without using
196 thread-local storage:
197 1. We use a specific static buffer for each possible CATEGORY
198 argument. So that different threads can call setlocale_mtsafe
199 with different CATEGORY arguments, without interfering.
200 2. We use a simple strcpy or memcpy to fill this static buffer.
201 Filling it through, for example, strcpy + strcat would not be
202 guaranteed to leave the buffer's contents intact if another thread
203 is currently accessing it. If necessary, the contents is first
204 assembled in a stack-allocated buffer. */
205 if (category
== LC_ALL
)
207 # if SETLOCALE_NULL_ALL_MTSAFE
208 return setlocale_null_unlocked (LC_ALL
);
210 char buf
[SETLOCALE_NULL_ALL_MAX
];
211 static char resultbuf
[SETLOCALE_NULL_ALL_MAX
];
213 if (setlocale_null_r (LC_ALL
, buf
, sizeof (buf
)))
215 strcpy (resultbuf
, buf
);
221 # if SETLOCALE_NULL_ONE_MTSAFE
222 return setlocale_null_unlocked (category
);
244 # ifdef LC_MEASUREMENT
245 LC_MEASUREMENT_INDEX
,
247 # ifdef LC_IDENTIFICATION
248 LC_IDENTIFICATION_INDEX
,
253 char buf
[SETLOCALE_NULL_MAX
];
254 static char resultbuf
[LC_INDICES_COUNT
][SETLOCALE_NULL_MAX
];
257 err
= setlocale_null_r (category
, buf
, sizeof (buf
));
265 case LC_CTYPE
: i
= LC_CTYPE_INDEX
; break;
266 case LC_NUMERIC
: i
= LC_NUMERIC_INDEX
; break;
267 case LC_TIME
: i
= LC_TIME_INDEX
; break;
268 case LC_COLLATE
: i
= LC_COLLATE_INDEX
; break;
269 case LC_MONETARY
: i
= LC_MONETARY_INDEX
; break;
270 case LC_MESSAGES
: i
= LC_MESSAGES_INDEX
; break;
272 case LC_PAPER
: i
= LC_PAPER_INDEX
; break;
275 case LC_NAME
: i
= LC_NAME_INDEX
; break;
278 case LC_ADDRESS
: i
= LC_ADDRESS_INDEX
; break;
281 case LC_TELEPHONE
: i
= LC_TELEPHONE_INDEX
; break;
283 # ifdef LC_MEASUREMENT
284 case LC_MEASUREMENT
: i
= LC_MEASUREMENT_INDEX
; break;
286 # ifdef LC_IDENTIFICATION
287 case LC_IDENTIFICATION
: i
= LC_IDENTIFICATION_INDEX
; break;
290 /* If you get here, a #ifdef LC_xxx is missing. */
294 strcpy (resultbuf
[i
], buf
);