Do the Windows oldnames workaround through the C++ GNULIB_NAMESPACE.
[gnulib.git] / lib / setlocale_null.c
blobabe55b5a3a315feca99c7a888bd61142c29d4fac
1 /* Query the name of the current global locale.
2 Copyright (C) 2019-2020 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. */
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>
28 #if defined _WIN32 && !defined __CYGWIN__
29 # include <wchar.h>
30 #endif
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 */
36 # include <windows.h>
38 # elif HAVE_PTHREAD_API
40 # include <pthread.h>
41 # if HAVE_THREADS_H && HAVE_WEAK_SYMBOLS
42 # include <threads.h>
43 # pragma weak thrd_exit
44 # define c11_threads_in_use() (thrd_exit != NULL)
45 # else
46 # define c11_threads_in_use() 0
47 # endif
49 # elif HAVE_THREADS_H
51 # include <threads.h>
53 # endif
54 #endif
56 /* Use the system's setlocale() function, not the gnulib override, here. */
57 #undef setlocale
59 static const char *
60 setlocale_null_androidfix (int category)
62 const char *result = setlocale (category, NULL);
64 #ifdef __ANDROID__
65 if (result == NULL)
66 switch (category)
68 case LC_CTYPE:
69 case LC_NUMERIC:
70 case LC_TIME:
71 case LC_COLLATE:
72 case LC_MONETARY:
73 case LC_MESSAGES:
74 case LC_ALL:
75 case LC_PAPER:
76 case LC_NAME:
77 case LC_ADDRESS:
78 case LC_TELEPHONE:
79 case LC_MEASUREMENT:
80 result = "C";
81 break;
82 default:
83 break;
85 #endif
87 return result;
90 static int
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);
99 if (result == NULL)
101 /* CATEGORY is invalid. */
102 if (bufsize > 0)
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. */
106 buf[0] = '\0';
107 return EINVAL;
109 else
111 size_t length = wcslen (result);
112 if (length < bufsize)
114 size_t i;
116 /* Convert wchar_t[] -> char[], assuming plain ASCII. */
117 for (i = 0; i <= length; i++)
118 buf[i] = result[i];
120 return 0;
122 else
124 if (bufsize > 0)
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. */
129 size_t i;
131 /* Convert wchar_t[] -> char[], assuming plain ASCII. */
132 for (i = 0; i < bufsize; i++)
133 buf[i] = result[i];
134 buf[bufsize - 1] = '\0';
136 return ERANGE;
139 #else
140 const char *result = setlocale_null_androidfix (category);
142 if (result == NULL)
144 /* CATEGORY is invalid. */
145 if (bufsize > 0)
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. */
149 buf[0] = '\0';
150 return EINVAL;
152 else
154 size_t length = strlen (result);
155 if (length < bufsize)
157 memcpy (buf, result, length + 1);
158 return 0;
160 else
162 if (bufsize > 0)
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';
170 return ERANGE;
173 #endif
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
179 at the same time. */
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);
188 static int
189 setlocale_null_with_lock (int category, char *buf, size_t bufsize)
191 CRITICAL_SECTION *lock = gl_get_setlocale_null_lock ();
192 int ret;
194 EnterCriticalSection (lock);
195 ret = setlocale_null_unlocked (category, buf, bufsize);
196 LeaveCriticalSection (lock);
198 return ret;
201 # elif HAVE_PTHREAD_API /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin */
203 extern
204 # if defined _WIN32 || defined __CYGWIN__
205 __declspec(dllimport)
206 # endif
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 ())
221 # else
222 # define pthread_in_use() 1
223 # endif
225 static int
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 ();
231 int ret;
233 if (pthread_mutex_lock (lock))
234 abort ();
235 ret = setlocale_null_unlocked (category, buf, bufsize);
236 if (pthread_mutex_unlock (lock))
237 abort ();
239 return ret;
241 else
242 return setlocale_null_unlocked (category, buf, bufsize);
245 # elif HAVE_THREADS_H
247 extern mtx_t *gl_get_setlocale_null_lock (void);
249 static int
250 setlocale_null_with_lock (int category, char *buf, size_t bufsize)
252 mtx_t *lock = gl_get_setlocale_null_lock ();
253 int ret;
255 if (mtx_lock (lock) != thrd_success)
256 abort ();
257 ret = setlocale_null_unlocked (category, buf, bufsize);
258 if (mtx_unlock (lock) != thrd_success)
259 abort ();
261 return ret;
264 # endif
266 #endif
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);
276 # else
278 if (category == LC_ALL)
279 return setlocale_null_unlocked (category, buf, bufsize);
280 else
281 return setlocale_null_with_lock (category, buf, bufsize);
283 # endif
284 #else
285 # if SETLOCALE_NULL_ONE_MTSAFE
287 if (category == LC_ALL)
288 return setlocale_null_with_lock (category, buf, bufsize);
289 else
290 return setlocale_null_unlocked (category, buf, bufsize);
292 # else
294 return setlocale_null_with_lock (category, buf, bufsize);
296 # endif
297 #endif
300 const char *
301 setlocale_null (int category)
303 #if SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE
304 return setlocale_null_androidfix (category);
305 #else
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);
321 # else
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)))
326 return "C";
327 strcpy (resultbuf, buf);
328 return resultbuf;
329 # endif
331 else
333 # if SETLOCALE_NULL_ONE_MTSAFE
334 return setlocale_null_androidfix (category);
335 # else
336 enum
338 LC_CTYPE_INDEX,
339 LC_NUMERIC_INDEX,
340 LC_TIME_INDEX,
341 LC_COLLATE_INDEX,
342 LC_MONETARY_INDEX,
343 LC_MESSAGES_INDEX,
344 # ifdef LC_PAPER
345 LC_PAPER_INDEX,
346 # endif
347 # ifdef LC_NAME
348 LC_NAME_INDEX,
349 # endif
350 # ifdef LC_ADDRESS
351 LC_ADDRESS_INDEX,
352 # endif
353 # ifdef LC_TELEPHONE
354 LC_TELEPHONE_INDEX,
355 # endif
356 # ifdef LC_MEASUREMENT
357 LC_MEASUREMENT_INDEX,
358 # endif
359 # ifdef LC_IDENTIFICATION
360 LC_IDENTIFICATION_INDEX,
361 # endif
362 LC_INDICES_COUNT
365 char buf[SETLOCALE_NULL_MAX];
366 static char resultbuf[LC_INDICES_COUNT][SETLOCALE_NULL_MAX];
367 int err;
369 err = setlocale_null_r (category, buf, sizeof (buf));
370 if (err == EINVAL)
371 return NULL;
372 if (err)
373 return "C";
375 switch (category)
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;
383 # ifdef LC_PAPER
384 case LC_PAPER: i = LC_PAPER_INDEX; break;
385 # endif
386 # ifdef LC_NAME
387 case LC_NAME: i = LC_NAME_INDEX; break;
388 # endif
389 # ifdef LC_ADDRESS
390 case LC_ADDRESS: i = LC_ADDRESS_INDEX; break;
391 # endif
392 # ifdef LC_TELEPHONE
393 case LC_TELEPHONE: i = LC_TELEPHONE_INDEX; break;
394 # endif
395 # ifdef LC_MEASUREMENT
396 case LC_MEASUREMENT: i = LC_MEASUREMENT_INDEX; break;
397 # endif
398 # ifdef LC_IDENTIFICATION
399 case LC_IDENTIFICATION: i = LC_IDENTIFICATION_INDEX; break;
400 # endif
401 default:
402 /* If you get here, a #ifdef LC_xxx is missing. */
403 abort ();
406 strcpy (resultbuf[i], buf);
407 return resultbuf[i];
408 # endif
410 #endif