/src/wget/lib/setlocale_null.c
Line | Count | Source (jump to first uncovered line) |
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 | | int ret; |
81 | | |
82 | | EnterCriticalSection (lock); |
83 | | ret = setlocale_null_r_unlocked (category, buf, bufsize); |
84 | | LeaveCriticalSection (lock); |
85 | | |
86 | | return ret; |
87 | | } |
88 | | |
89 | | # elif HAVE_PTHREAD_API /* musl libc, macOS, FreeBSD, NetBSD, OpenBSD, AIX, Haiku, Cygwin < 3.4.6 */ |
90 | | |
91 | | extern |
92 | | # if defined _WIN32 || defined __CYGWIN__ |
93 | | __declspec(dllimport) |
94 | | # endif |
95 | | pthread_mutex_t *gl_get_setlocale_null_lock (void); |
96 | | |
97 | | # if HAVE_WEAK_SYMBOLS /* musl libc, FreeBSD, NetBSD, OpenBSD, Haiku */ |
98 | | |
99 | | /* Avoid the need to link with '-lpthread'. */ |
100 | | # pragma weak pthread_mutex_lock |
101 | | # pragma weak pthread_mutex_unlock |
102 | | |
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 ()) |
108 | | |
109 | | # else |
110 | | # define pthread_in_use() 1 |
111 | | # endif |
112 | | |
113 | | static int |
114 | | setlocale_null_r_with_lock (int category, char *buf, size_t bufsize) |
115 | | { |
116 | | if (pthread_in_use()) |
117 | | { |
118 | | pthread_mutex_t *lock = gl_get_setlocale_null_lock (); |
119 | | int ret; |
120 | | |
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 (); |
126 | | |
127 | | return ret; |
128 | | } |
129 | | else |
130 | | return setlocale_null_r_unlocked (category, buf, bufsize); |
131 | | } |
132 | | |
133 | | # elif HAVE_THREADS_H |
134 | | |
135 | | extern mtx_t *gl_get_setlocale_null_lock (void); |
136 | | |
137 | | static int |
138 | | setlocale_null_r_with_lock (int category, char *buf, size_t bufsize) |
139 | | { |
140 | | mtx_t *lock = gl_get_setlocale_null_lock (); |
141 | | int ret; |
142 | | |
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 (); |
148 | | |
149 | | return ret; |
150 | | } |
151 | | |
152 | | # endif |
153 | | |
154 | | #endif |
155 | | |
156 | | int |
157 | | setlocale_null_r (int category, char *buf, size_t bufsize) |
158 | 0 | { |
159 | 0 | #if SETLOCALE_NULL_ALL_MTSAFE |
160 | 0 | # if SETLOCALE_NULL_ONE_MTSAFE |
161 | |
|
162 | 0 | return setlocale_null_r_unlocked (category, buf, bufsize); |
163 | |
|
164 | | # else |
165 | | |
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); |
170 | | |
171 | | # endif |
172 | | #else |
173 | | # if SETLOCALE_NULL_ONE_MTSAFE |
174 | | |
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); |
179 | | |
180 | | # else |
181 | | |
182 | | return setlocale_null_r_with_lock (category, buf, bufsize); |
183 | | |
184 | | # endif |
185 | | #endif |
186 | 0 | } |
187 | | |
188 | | const char * |
189 | | setlocale_null (int category) |
190 | 0 | { |
191 | 0 | #if SETLOCALE_NULL_ALL_MTSAFE && SETLOCALE_NULL_ONE_MTSAFE |
192 | 0 | return setlocale_null_unlocked (category); |
193 | | #else |
194 | | |
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) |
206 | | { |
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]; |
212 | | |
213 | | if (setlocale_null_r (LC_ALL, buf, sizeof (buf))) |
214 | | return "C"; |
215 | | strcpy (resultbuf, buf); |
216 | | return resultbuf; |
217 | | # endif |
218 | | } |
219 | | else |
220 | | { |
221 | | # if SETLOCALE_NULL_ONE_MTSAFE |
222 | | return setlocale_null_unlocked (category); |
223 | | # else |
224 | | enum |
225 | | { |
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 |
251 | | } |
252 | | i; |
253 | | char buf[SETLOCALE_NULL_MAX]; |
254 | | static char resultbuf[LC_INDICES_COUNT][SETLOCALE_NULL_MAX]; |
255 | | int err; |
256 | | |
257 | | err = setlocale_null_r (category, buf, sizeof (buf)); |
258 | | if (err == EINVAL) |
259 | | return NULL; |
260 | | if (err) |
261 | | return "C"; |
262 | | |
263 | | switch (category) |
264 | | { |
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 (); |
292 | | } |
293 | | |
294 | | strcpy (resultbuf[i], buf); |
295 | | return resultbuf[i]; |
296 | | # endif |
297 | | } |
298 | | #endif |
299 | 0 | } |