/src/wget/lib/setlocale_null.c
Line | Count | Source |
1 | | /* Query the name of the current global locale. |
2 | | Copyright (C) 2019-2025 Free Software Foundation, Inc. |
3 | | |
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. |
8 | | |
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. |
13 | | |
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/>. */ |
16 | | |
17 | | /* Written by Bruno Haible <bruno@clisp.org>, 2019. */ |
18 | | |
19 | | #include <config.h> |
20 | | |
21 | | /* Specification. */ |
22 | | #include "setlocale_null.h" |
23 | | |
24 | | #include <errno.h> |
25 | | #include <locale.h> |
26 | | #include <stdlib.h> |
27 | | #include <string.h> |
28 | | |
29 | | #if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE) |
30 | | |
31 | | # if AVOID_ANY_THREADS |
32 | | |
33 | | /* The option '--disable-threads' explicitly requests no locking. */ |
34 | | |
35 | | # elif defined _WIN32 && !defined __CYGWIN__ |
36 | | |
37 | | # define WIN32_LEAN_AND_MEAN /* avoid including junk */ |
38 | | # include <windows.h> |
39 | | |
40 | | # elif HAVE_PTHREAD_API |
41 | | |
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 |
50 | | |
51 | | # elif HAVE_THREADS_H |
52 | | |
53 | | # include <threads.h> |
54 | | |
55 | | # endif |
56 | | |
57 | | #endif |
58 | | |
59 | | #if !(SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE) /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin < 3.4.6 */ |
60 | | |
61 | | /* Use a lock, so that no two threads can invoke setlocale_null_r_unlocked |
62 | | at the same time. */ |
63 | | |
64 | | /* Prohibit renaming this symbol. */ |
65 | | # undef gl_get_setlocale_null_lock |
66 | | |
67 | | # if AVOID_ANY_THREADS |
68 | | |
69 | | /* The option '--disable-threads' explicitly requests no locking. */ |
70 | | # define setlocale_null_r_with_lock setlocale_null_r_unlocked |
71 | | |
72 | | # elif defined _WIN32 && !defined __CYGWIN__ |
73 | | |
74 | | extern __declspec(dllimport) CRITICAL_SECTION *gl_get_setlocale_null_lock (void); |
75 | | |
76 | | static int |
77 | | setlocale_null_r_with_lock (int category, char *buf, size_t bufsize) |
78 | | { |
79 | | CRITICAL_SECTION *lock = gl_get_setlocale_null_lock (); |
80 | | |
81 | | EnterCriticalSection (lock); |
82 | | int ret = setlocale_null_r_unlocked (category, buf, bufsize); |
83 | | LeaveCriticalSection (lock); |
84 | | |
85 | | return ret; |
86 | | } |
87 | | |
88 | | # elif HAVE_PTHREAD_API /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin < 3.4.6 */ |
89 | | |
90 | | extern |
91 | | # if defined _WIN32 || defined __CYGWIN__ |
92 | | __declspec(dllimport) |
93 | | # endif |
94 | | pthread_mutex_t *gl_get_setlocale_null_lock (void); |
95 | | |
96 | | # if HAVE_WEAK_SYMBOLS /* musl libc, FreeBSD, NetBSD, OpenBSD, Haiku */ |
97 | | |
98 | | /* Avoid the need to link with '-lpthread'. */ |
99 | | # pragma weak pthread_mutex_lock |
100 | | # pragma weak pthread_mutex_unlock |
101 | | |
102 | | /* Determine whether libpthread is in use. */ |
103 | | # pragma weak pthread_mutexattr_gettype |
104 | | /* See the comments in lock.h. */ |
105 | | # define pthread_in_use() \ |
106 | | (pthread_mutexattr_gettype != NULL || c11_threads_in_use ()) |
107 | | |
108 | | # else |
109 | | # define pthread_in_use() 1 |
110 | | # endif |
111 | | |
112 | | static int |
113 | | setlocale_null_r_with_lock (int category, char *buf, size_t bufsize) |
114 | | { |
115 | | if (pthread_in_use()) |
116 | | { |
117 | | pthread_mutex_t *lock = gl_get_setlocale_null_lock (); |
118 | | |
119 | | if (pthread_mutex_lock (lock)) |
120 | | abort (); |
121 | | int ret = setlocale_null_r_unlocked (category, buf, bufsize); |
122 | | if (pthread_mutex_unlock (lock)) |
123 | | abort (); |
124 | | |
125 | | return ret; |
126 | | } |
127 | | else |
128 | | return setlocale_null_r_unlocked (category, buf, bufsize); |
129 | | } |
130 | | |
131 | | # elif HAVE_THREADS_H |
132 | | |
133 | | extern mtx_t *gl_get_setlocale_null_lock (void); |
134 | | |
135 | | static int |
136 | | setlocale_null_r_with_lock (int category, char *buf, size_t bufsize) |
137 | | { |
138 | | mtx_t *lock = gl_get_setlocale_null_lock (); |
139 | | |
140 | | if (mtx_lock (lock) != thrd_success) |
141 | | abort (); |
142 | | int ret = setlocale_null_r_unlocked (category, buf, bufsize); |
143 | | if (mtx_unlock (lock) != thrd_success) |
144 | | abort (); |
145 | | |
146 | | return ret; |
147 | | } |
148 | | |
149 | | # endif |
150 | | |
151 | | #endif |
152 | | |
153 | | int |
154 | | setlocale_null_r (int category, char *buf, size_t bufsize) |
155 | 36.9k | { |
156 | 36.9k | #if SETLOCALE_NULL_ALL_MTSAFE |
157 | 36.9k | # if SETLOCALE_NULL_ONE_MTSAFE |
158 | | |
159 | 36.9k | return setlocale_null_r_unlocked (category, buf, bufsize); |
160 | | |
161 | | # else |
162 | | |
163 | | if (category == LC_ALL) |
164 | | return setlocale_null_r_unlocked (category, buf, bufsize); |
165 | | else |
166 | | return setlocale_null_r_with_lock (category, buf, bufsize); |
167 | | |
168 | | # endif |
169 | | #else |
170 | | # if SETLOCALE_NULL_ONE_MTSAFE |
171 | | |
172 | | if (category == LC_ALL) |
173 | | return setlocale_null_r_with_lock (category, buf, bufsize); |
174 | | else |
175 | | return setlocale_null_r_unlocked (category, buf, bufsize); |
176 | | |
177 | | # else |
178 | | |
179 | | return setlocale_null_r_with_lock (category, buf, bufsize); |
180 | | |
181 | | # endif |
182 | | #endif |
183 | 36.9k | } |
184 | | |
185 | | const char * |
186 | | setlocale_null (int category) |
187 | 0 | { |
188 | 0 | #if SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE |
189 | 0 | return setlocale_null_unlocked (category); |
190 | | #else |
191 | | |
192 | | /* This call must be multithread-safe. To achieve this without using |
193 | | thread-local storage: |
194 | | 1. We use a specific static buffer for each possible CATEGORY |
195 | | argument. So that different threads can call setlocale_mtsafe |
196 | | with different CATEGORY arguments, without interfering. |
197 | | 2. We use a simple strcpy or memcpy to fill this static buffer. |
198 | | Filling it through, for example, strcpy + strcat would not be |
199 | | guaranteed to leave the buffer's contents intact if another thread |
200 | | is currently accessing it. If necessary, the contents is first |
201 | | assembled in a stack-allocated buffer. */ |
202 | | if (category == LC_ALL) |
203 | | { |
204 | | # if SETLOCALE_NULL_ALL_MTSAFE |
205 | | return setlocale_null_unlocked (LC_ALL); |
206 | | # else |
207 | | char buf[SETLOCALE_NULL_ALL_MAX]; |
208 | | static char resultbuf[SETLOCALE_NULL_ALL_MAX]; |
209 | | |
210 | | if (setlocale_null_r (LC_ALL, buf, sizeof (buf))) |
211 | | return "C"; |
212 | | strcpy (resultbuf, buf); |
213 | | return resultbuf; |
214 | | # endif |
215 | | } |
216 | | else |
217 | | { |
218 | | # if SETLOCALE_NULL_ONE_MTSAFE |
219 | | return setlocale_null_unlocked (category); |
220 | | # else |
221 | | enum |
222 | | { |
223 | | LC_CTYPE_INDEX, |
224 | | LC_NUMERIC_INDEX, |
225 | | LC_TIME_INDEX, |
226 | | LC_COLLATE_INDEX, |
227 | | LC_MONETARY_INDEX, |
228 | | LC_MESSAGES_INDEX, |
229 | | # ifdef LC_PAPER |
230 | | LC_PAPER_INDEX, |
231 | | # endif |
232 | | # ifdef LC_NAME |
233 | | LC_NAME_INDEX, |
234 | | # endif |
235 | | # ifdef LC_ADDRESS |
236 | | LC_ADDRESS_INDEX, |
237 | | # endif |
238 | | # ifdef LC_TELEPHONE |
239 | | LC_TELEPHONE_INDEX, |
240 | | # endif |
241 | | # ifdef LC_MEASUREMENT |
242 | | LC_MEASUREMENT_INDEX, |
243 | | # endif |
244 | | # ifdef LC_IDENTIFICATION |
245 | | LC_IDENTIFICATION_INDEX, |
246 | | # endif |
247 | | LC_INDICES_COUNT |
248 | | } |
249 | | i; |
250 | | char buf[SETLOCALE_NULL_MAX]; |
251 | | static char resultbuf[LC_INDICES_COUNT][SETLOCALE_NULL_MAX]; |
252 | | int err = setlocale_null_r (category, buf, sizeof (buf)); |
253 | | if (err == EINVAL) |
254 | | return NULL; |
255 | | if (err) |
256 | | return "C"; |
257 | | |
258 | | switch (category) |
259 | | { |
260 | | case LC_CTYPE: i = LC_CTYPE_INDEX; break; |
261 | | case LC_NUMERIC: i = LC_NUMERIC_INDEX; break; |
262 | | case LC_TIME: i = LC_TIME_INDEX; break; |
263 | | case LC_COLLATE: i = LC_COLLATE_INDEX; break; |
264 | | case LC_MONETARY: i = LC_MONETARY_INDEX; break; |
265 | | case LC_MESSAGES: i = LC_MESSAGES_INDEX; break; |
266 | | # ifdef LC_PAPER |
267 | | case LC_PAPER: i = LC_PAPER_INDEX; break; |
268 | | # endif |
269 | | # ifdef LC_NAME |
270 | | case LC_NAME: i = LC_NAME_INDEX; break; |
271 | | # endif |
272 | | # ifdef LC_ADDRESS |
273 | | case LC_ADDRESS: i = LC_ADDRESS_INDEX; break; |
274 | | # endif |
275 | | # ifdef LC_TELEPHONE |
276 | | case LC_TELEPHONE: i = LC_TELEPHONE_INDEX; break; |
277 | | # endif |
278 | | # ifdef LC_MEASUREMENT |
279 | | case LC_MEASUREMENT: i = LC_MEASUREMENT_INDEX; break; |
280 | | # endif |
281 | | # ifdef LC_IDENTIFICATION |
282 | | case LC_IDENTIFICATION: i = LC_IDENTIFICATION_INDEX; break; |
283 | | # endif |
284 | | default: |
285 | | /* If you get here, a #ifdef LC_xxx is missing. */ |
286 | | abort (); |
287 | | } |
288 | | |
289 | | strcpy (resultbuf[i], buf); |
290 | | return resultbuf[i]; |
291 | | # endif |
292 | | } |
293 | | #endif |
294 | 0 | } |