Coverage Report

Created: 2025-12-31 08:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}