openat: don’t close (-1)
[gnulib.git] / lib / setlocale_null.c
blob5ecf413de01ed9e95b0953b7560e0fb1afccd8e6
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. */
19 #include <config.h>
21 /* Specification. */
22 #include "setlocale_null.h"
24 #include <errno.h>
25 #include <locale.h>
26 #include <stdlib.h>
27 #include <string.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 */
38 # include <windows.h>
40 # elif HAVE_PTHREAD_API
42 # include <pthread.h>
43 # if HAVE_THREADS_H && HAVE_WEAK_SYMBOLS
44 # include <threads.h>
45 # pragma weak thrd_exit
46 # define c11_threads_in_use() (thrd_exit != NULL)
47 # else
48 # define c11_threads_in_use() 0
49 # endif
51 # elif HAVE_THREADS_H
53 # include <threads.h>
55 # endif
57 #endif
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
62 at the same time. */
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);
76 static int
77 setlocale_null_r_with_lock (int category, char *buf, size_t bufsize)
79 CRITICAL_SECTION *lock = gl_get_setlocale_null_lock ();
80 int ret;
82 EnterCriticalSection (lock);
83 ret = setlocale_null_r_unlocked (category, buf, bufsize);
84 LeaveCriticalSection (lock);
86 return ret;
89 # elif HAVE_PTHREAD_API /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin < 3.4.6 */
91 extern
92 # if defined _WIN32 || defined __CYGWIN__
93 __declspec(dllimport)
94 # endif
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 ())
109 # else
110 # define pthread_in_use() 1
111 # endif
113 static int
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 ();
119 int ret;
121 if (pthread_mutex_lock (lock))
122 abort ();
123 ret = setlocale_null_r_unlocked (category, buf, bufsize);
124 if (pthread_mutex_unlock (lock))
125 abort ();
127 return ret;
129 else
130 return setlocale_null_r_unlocked (category, buf, bufsize);
133 # elif HAVE_THREADS_H
135 extern mtx_t *gl_get_setlocale_null_lock (void);
137 static int
138 setlocale_null_r_with_lock (int category, char *buf, size_t bufsize)
140 mtx_t *lock = gl_get_setlocale_null_lock ();
141 int ret;
143 if (mtx_lock (lock) != thrd_success)
144 abort ();
145 ret = setlocale_null_r_unlocked (category, buf, bufsize);
146 if (mtx_unlock (lock) != thrd_success)
147 abort ();
149 return ret;
152 # endif
154 #endif
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);
164 # else
166 if (category == LC_ALL)
167 return setlocale_null_r_unlocked (category, buf, bufsize);
168 else
169 return setlocale_null_r_with_lock (category, buf, bufsize);
171 # endif
172 #else
173 # if SETLOCALE_NULL_ONE_MTSAFE
175 if (category == LC_ALL)
176 return setlocale_null_r_with_lock (category, buf, bufsize);
177 else
178 return setlocale_null_r_unlocked (category, buf, bufsize);
180 # else
182 return setlocale_null_r_with_lock (category, buf, bufsize);
184 # endif
185 #endif
188 const char *
189 setlocale_null (int category)
191 #if SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE
192 return setlocale_null_unlocked (category);
193 #else
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);
209 # else
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)))
214 return "C";
215 strcpy (resultbuf, buf);
216 return resultbuf;
217 # endif
219 else
221 # if SETLOCALE_NULL_ONE_MTSAFE
222 return setlocale_null_unlocked (category);
223 # else
224 enum
226 LC_CTYPE_INDEX,
227 LC_NUMERIC_INDEX,
228 LC_TIME_INDEX,
229 LC_COLLATE_INDEX,
230 LC_MONETARY_INDEX,
231 LC_MESSAGES_INDEX,
232 # ifdef LC_PAPER
233 LC_PAPER_INDEX,
234 # endif
235 # ifdef LC_NAME
236 LC_NAME_INDEX,
237 # endif
238 # ifdef LC_ADDRESS
239 LC_ADDRESS_INDEX,
240 # endif
241 # ifdef LC_TELEPHONE
242 LC_TELEPHONE_INDEX,
243 # endif
244 # ifdef LC_MEASUREMENT
245 LC_MEASUREMENT_INDEX,
246 # endif
247 # ifdef LC_IDENTIFICATION
248 LC_IDENTIFICATION_INDEX,
249 # endif
250 LC_INDICES_COUNT
253 char buf[SETLOCALE_NULL_MAX];
254 static char resultbuf[LC_INDICES_COUNT][SETLOCALE_NULL_MAX];
255 int err;
257 err = setlocale_null_r (category, buf, sizeof (buf));
258 if (err == EINVAL)
259 return NULL;
260 if (err)
261 return "C";
263 switch (category)
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;
271 # ifdef LC_PAPER
272 case LC_PAPER: i = LC_PAPER_INDEX; break;
273 # endif
274 # ifdef LC_NAME
275 case LC_NAME: i = LC_NAME_INDEX; break;
276 # endif
277 # ifdef LC_ADDRESS
278 case LC_ADDRESS: i = LC_ADDRESS_INDEX; break;
279 # endif
280 # ifdef LC_TELEPHONE
281 case LC_TELEPHONE: i = LC_TELEPHONE_INDEX; break;
282 # endif
283 # ifdef LC_MEASUREMENT
284 case LC_MEASUREMENT: i = LC_MEASUREMENT_INDEX; break;
285 # endif
286 # ifdef LC_IDENTIFICATION
287 case LC_IDENTIFICATION: i = LC_IDENTIFICATION_INDEX; break;
288 # endif
289 default:
290 /* If you get here, a #ifdef LC_xxx is missing. */
291 abort ();
294 strcpy (resultbuf[i], buf);
295 return resultbuf[i];
296 # endif
298 #endif