1 /* $OpenBSD: uselocale.c,v 1.9 2024/02/05 06:48:04 anton Exp $ */
2 /*
3 * Copyright (c) 2017, 2022 Ingo Schwarze <schwarze@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <ctype.h>
19 #include <err.h>
20 #include <errno.h>
21 #include <langinfo.h>
22 #include <locale.h>
23 #include <pthread.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <wchar.h>
27 #include <wctype.h>
28
29 /* Keep in sync with /usr/src/lib/libc/locale/rune.h. */
30 #define _LOCALE_NONE (locale_t)0
31 #define _LOCALE_C (locale_t)1
32 #define _LOCALE_UTF8 (locale_t)2
33 #define _LOCALE_BAD (locale_t)3
34
35 /* Options for switch_thread() below. */
36 #define SWITCH_SIGNAL 1 /* Call pthread_cond_signal(3). */
37 #define SWITCH_WAIT 2 /* Call pthread_cond_timedwait(3). */
38
39 /* Options for TESTFUNC(). */
40 #define TOPT_ERR (1 << 0)
41
42 /*
43 * Generate one test function for a specific interface.
44 * Fn = function name
45 * Ft = function return type
46 * FUNCPARA = function parameter list with types and names
47 * FUNCARGS = function argument list, names only, no types
48 * Af = format string to print the arguments
49 * Rf = format string to print the return value
50 * Op = options for the test function, see above
51 * line = source code line number in this test file
52 * ee = expected error number
53 * er = expected return value
54 * ar = actual return value
55 * errno = actual error number (global)
56 */
57 #define TESTFUNC(Fn, Ft, Af, Rf, Op) \
58 static void \
59 _test_##Fn(int line, int ee, Ft er, FUNCPARA) \
60 { \
61 Ft ar; \
62 errno = 0; \
63 ar = Fn(FUNCARGS); \
64 if (ar != er) \
65 errx(1, "[%d] %s(" Af ")=" Rf " [exp: " Rf "]", \
66 line, #Fn, FUNCARGS, ar, er); \
67 if (Op & TOPT_ERR && errno != ee) \
68 errx(1, "[%d] %s(" Af ") errno=%d [exp: %d]", \
69 line, #Fn, FUNCARGS, errno, ee); \
70 }
71
72 #define STRTESTFUNC(Fn, Af) \
73 static void \
74 _test_##Fn(int line, int ee, const char *er, FUNCPARA) \
75 { \
76 const char *ar; \
77 errno = 0; \
78 ar = Fn(FUNCARGS); \
79 if (er == NULL) \
80 er = "NULL"; \
81 if (ar == NULL) \
82 ar = "NULL"; \
83 if (strcmp((const char *)er, (const char *)ar) != 0) \
84 errx(1, "[%d] %s(" Af ")=%s [exp: %s]", \
85 line, #Fn, FUNCARGS, ar, er); \
86 }
87
88 /*
89 * Test functions for all tested interfaces.
90 */
91 #define FUNCPARA int mask, const char *locname
92 #define FUNCARGS mask, locname, _LOCALE_NONE
93 TESTFUNC(newlocale, locale_t, "%d, %s, %p", "%p", TOPT_ERR)
94
95 #define FUNCPARA locale_t locale
96 #define FUNCARGS locale
97 TESTFUNC(duplocale, locale_t, "%p", "%p", TOPT_ERR)
98 TESTFUNC(uselocale, locale_t, "%p", "%p", TOPT_ERR)
99
100 #define FUNCPARA int category, char *locname
101 #define FUNCARGS category, locname
102 STRTESTFUNC(setlocale, "%d, %s")
103
104 #define FUNCPARA nl_item item
105 #define FUNCARGS item
106 STRTESTFUNC(nl_langinfo, "%ld")
107
108 #define FUNCPARA nl_item item, locale_t locale
109 #define FUNCARGS item, locale
110 STRTESTFUNC(nl_langinfo_l, "%ld, %p")
111
112 #define FUNCPARA int c
113 #define FUNCARGS c
114 TESTFUNC(isalpha, int, "0x%.2x", "%d", 0)
115 TESTFUNC(tolower, int, "0x%.2x", "0x%.2x", 0)
116
117 #define FUNCPARA int c, locale_t locale
118 #define FUNCARGS c, locale
119 TESTFUNC(isalpha_l, int, "0x%.2x, %p", "%d", 0)
120 TESTFUNC(tolower_l, int, "0x%.2x, %p", "0x%.2x", 0)
121
122 #define FUNCPARA wint_t wc
123 #define FUNCARGS wc
124 TESTFUNC(iswalpha, int, "U+%.4X", "%d", 0)
125 TESTFUNC(towupper, wint_t, "U+%.4X", "U+%.4X", 0)
126
127 #define FUNCPARA wint_t wc, locale_t locale
128 #define FUNCARGS wc, locale
129 TESTFUNC(iswalpha_l, int, "U+%.4X, %p", "%d", 0)
130 TESTFUNC(towupper_l, wint_t, "U+%.4X, %p", "U+%.4X", 0)
131
132 #define FUNCPARA wint_t wc, wctype_t charclass
133 #define FUNCARGS wc, charclass
134 TESTFUNC(iswctype, int, "U+%.4X, %p", "%d", 0)
135
136 #define FUNCPARA wint_t wc, wctype_t charclass, locale_t locale
137 #define FUNCARGS wc, charclass, locale
138 TESTFUNC(iswctype_l, int, "U+%.4X, %p, %p", "%d", 0)
139
140 #define FUNCPARA wint_t wc, wctrans_t charmap
141 #define FUNCARGS wc, charmap
142 TESTFUNC(towctrans, wint_t, "U+%.4X, %p", "U+%.4X", 0)
143
144 #define FUNCPARA wint_t wc, wctrans_t charmap, locale_t locale
145 #define FUNCARGS wc, charmap, locale
146 TESTFUNC(towctrans_l, wint_t, "U+%.4X, %p, %p", "U+%.4X", 0)
147
148 #define FUNCPARA const wchar_t *s1, const wchar_t *s2
149 #define FUNCARGS s1, s2
150 TESTFUNC(wcscasecmp, int, "%ls, %ls", "%d", 0)
151
152 #define FUNCPARA const wchar_t *s1, const wchar_t *s2, locale_t locale
153 #define FUNCARGS s1, s2, locale
154 TESTFUNC(wcscasecmp_l, int, "%ls, %ls, %p", "%d", 0)
155
156 #define FUNCPARA const wchar_t *s1, const wchar_t *s2, size_t len
157 #define FUNCARGS s1, s2, len
158 TESTFUNC(wcsncasecmp, int, "%ls, %ls, %zu", "%d", 0)
159
160 #define FUNCPARA const wchar_t *s1, const wchar_t *s2, size_t len, \
161 locale_t locale
162 #define FUNCARGS s1, s2, len, locale
163 TESTFUNC(wcsncasecmp_l, int, "%ls, %ls, %zu, %p", "%d", 0)
164
165 static void
_test_MB_CUR_MAX(int line,int ee,size_t ar)166 _test_MB_CUR_MAX(int line, int ee, size_t ar)
167 {
168 if (MB_CUR_MAX != ar)
169 errx(1, "[%d] MB_CUR_MAX=%zd [exp: %zd]",
170 line, MB_CUR_MAX, ar);
171 }
172
173 /*
174 * Test macros:
175 * TEST_R(funcname, er, arguments) if you expect errno == 0.
176 * TEST_ER(funcname, ee, er, arguments) otherwise.
177 */
178 #define TEST_R(Fn, ...) _test_##Fn(__LINE__, 0, __VA_ARGS__)
179 #define TEST_ER(Fn, ...) _test_##Fn(__LINE__, __VA_ARGS__)
180
181 static pthread_mutex_t mtx;
182 static pthread_mutexattr_t mtxattr;
183 static pthread_cond_t cond;
184
185 /*
186 * SWITCH_SIGNAL wakes the other thread.
187 * SWITCH_WAIT goes to sleep.
188 * Both can be combined.
189 * The step argument is used for error reporting only.
190 */
191 static void
switch_thread(int step,int flags)192 switch_thread(int step, int flags)
193 {
194 struct timespec t;
195 int irc;
196
197 if (flags & SWITCH_SIGNAL) {
198 if ((irc = pthread_cond_signal(&cond)) != 0)
199 errc(1, irc, "pthread_cond_signal(%d)", step);
200 }
201 if (flags & SWITCH_WAIT) {
202 if ((irc = pthread_mutex_trylock(&mtx)) != 0)
203 errc(1, irc, "pthread_mutex_trylock(%d)", step);
204 t.tv_sec = time(NULL) + 2;
205 t.tv_nsec = 0;
206 if ((irc = pthread_cond_timedwait(&cond, &mtx, &t)) != 0)
207 errc(1, irc, "pthread_cond_timedwait(%d)", step);
208 if ((irc = pthread_mutex_unlock(&mtx)) != 0)
209 errc(1, irc, "pthread_mutex_unlock(%d)", step);
210 }
211 }
212
213 static void *
child_func(void * arg)214 child_func(void *arg)
215 {
216 const wchar_t s1[] = { 0x00C7, 0x00E0, 0x0000 };
217 const wchar_t s2[] = { 0x00E7, 0x00C0, 0x0000 };
218 const wchar_t s3[] = { 0x00C9, 0x0074, 0x00C9, 0x0000 };
219 const wchar_t s4[] = { 0x00E9, 0x0054, 0x00CC, 0x0000 };
220 wctype_t wctyg, wctyu, wctyc;
221 wctrans_t wctrg, wctru, wctrc;
222 char *sego, *segc, *selo, *selc;
223
224 /* Test invalid newlocale(3) arguments. */
225 TEST_ER(newlocale, EINVAL, _LOCALE_NONE, LC_CTYPE_MASK, NULL);
226 TEST_R(MB_CUR_MAX, 1);
227 TEST_ER(newlocale, EINVAL, _LOCALE_NONE, LC_ALL_MASK + 1, "C.UTF-8");
228 TEST_R(MB_CUR_MAX, 1);
229 TEST_ER(newlocale, ENOENT, _LOCALE_NONE, LC_COLLATE_MASK, "C.INV");
230 TEST_R(MB_CUR_MAX, 1);
231 setenv("LC_TIME", "C.INV", 1);
232 TEST_ER(newlocale, ENOENT, _LOCALE_NONE, LC_TIME_MASK, "");
233 unsetenv("LC_TIME");
234 TEST_R(MB_CUR_MAX, 1);
235 setenv("LC_CTYPE", "C.INV", 1);
236 TEST_ER(newlocale, ENOENT, _LOCALE_NONE, LC_CTYPE_MASK, "");
237 TEST_R(MB_CUR_MAX, 1);
238
239 /* Test duplocale(3). */
240 TEST_ER(duplocale, EINVAL, _LOCALE_NONE, _LOCALE_UTF8);
241 TEST_R(duplocale, _LOCALE_C, _LOCALE_C);
242 TEST_R(duplocale, _LOCALE_C, LC_GLOBAL_LOCALE);
243
244 /* Test premature UTF-8 uselocale(3). */
245 TEST_ER(uselocale, EINVAL, _LOCALE_NONE, _LOCALE_UTF8);
246 TEST_R(MB_CUR_MAX, 1);
247 TEST_R(uselocale, LC_GLOBAL_LOCALE, _LOCALE_NONE);
248
249 /* Test UTF-8 initialization. */
250 setenv("LC_CTYPE", "C.UTF-8", 1);
251 TEST_R(newlocale, _LOCALE_UTF8, LC_CTYPE_MASK, "");
252 unsetenv("LC_CTYPE");
253 TEST_R(MB_CUR_MAX, 1);
254 TEST_R(duplocale, _LOCALE_UTF8, _LOCALE_UTF8);
255
256 /* Test invalid uselocale(3) argument. */
257 TEST_ER(uselocale, EINVAL, _LOCALE_NONE, _LOCALE_BAD);
258 TEST_R(MB_CUR_MAX, 1);
259 TEST_R(uselocale, LC_GLOBAL_LOCALE, _LOCALE_NONE);
260 TEST_R(nl_langinfo, "US-ASCII", CODESET);
261 TEST_R(nl_langinfo_l, "UTF-8", CODESET, _LOCALE_UTF8);
262 TEST_R(iswalpha, 0, 0x00E9);
263 TEST_R(iswalpha_l, 1, 0x00E9, _LOCALE_UTF8);
264 TEST_R(towupper, 0x00E9, 0x00E9);
265 TEST_R(towupper_l, 0x00C9, 0x00E9, _LOCALE_UTF8);
266 TEST_R(wcscasecmp, *s1 - *s2, s1, s2);
267 TEST_R(wcscasecmp_l, 0, s1, s2, _LOCALE_UTF8);
268
269 /* Test switching the thread locale. */
270 TEST_R(uselocale, LC_GLOBAL_LOCALE, _LOCALE_UTF8);
271 TEST_R(MB_CUR_MAX, 4);
272 TEST_R(uselocale, _LOCALE_UTF8, _LOCALE_NONE);
273 TEST_R(nl_langinfo, "UTF-8", CODESET);
274 TEST_R(nl_langinfo_l, "UTF-8", CODESET, _LOCALE_UTF8);
275 TEST_R(nl_langinfo_l, "US-ASCII", CODESET, _LOCALE_C);
276 TEST_R(isalpha, _CTYPE_L, 0x65); /* e */
277 TEST_R(isalpha_l, _CTYPE_L, 0x65, _LOCALE_UTF8);
278 TEST_R(isalpha_l, _CTYPE_L, 0x65, _LOCALE_C);
279 TEST_R(isalpha_l, _CTYPE_L, 0x65, _LOCALE_C);
280 TEST_R(isalpha, 0, 0x30); /* 0 */
281 TEST_R(isalpha_l, 0, 0x30, _LOCALE_UTF8);
282 TEST_R(isalpha_l, 0, 0x30, _LOCALE_C);
283 TEST_R(tolower, 0x61, 0x41); /* A */
284 TEST_R(tolower_l, 0x61, 0x41, _LOCALE_UTF8);
285 TEST_R(tolower_l, 0x61, 0x41, _LOCALE_C);
286 TEST_R(tolower, 0x40, 0x40); /* @ */
287 TEST_R(tolower_l, 0x40, 0x40, _LOCALE_UTF8);
288 TEST_R(tolower_l, 0x40, 0x40, _LOCALE_C);
289 TEST_R(iswalpha, 1, 0x00E9); /* e accent aigu */
290 TEST_R(iswalpha_l, 1, 0x00E9, _LOCALE_UTF8);
291 TEST_R(iswalpha_l, 0, 0x00E9, _LOCALE_C);
292 TEST_R(iswalpha, 1, 0x0153); /* ligature oe */
293 TEST_R(iswalpha_l, 1, 0x0153, _LOCALE_UTF8);
294 TEST_R(iswalpha_l, 0, 0x0153, _LOCALE_C);
295 TEST_R(iswalpha, 0, 0x2200); /* for all */
296 TEST_R(iswalpha_l, 0, 0x2200, _LOCALE_UTF8);
297 TEST_R(iswalpha_l, 0, 0x2200, _LOCALE_C);
298 TEST_R(towupper, 0x00C9, 0x00E9);
299 TEST_R(towupper_l, 0x00C9, 0x00E9, _LOCALE_UTF8);
300 TEST_R(towupper_l, 0x00E9, 0x00E9, _LOCALE_C);
301 TEST_R(towupper, 0x0152, 0x0153);
302 TEST_R(towupper_l, 0x0152, 0x0153, _LOCALE_UTF8);
303 TEST_R(towupper_l, 0x0153, 0x0153, _LOCALE_C);
304 TEST_R(towupper, 0x2205, 0x2205);
305 TEST_R(towupper_l, 0x2205, 0x2205, _LOCALE_UTF8);
306 TEST_R(towupper_l, 0x2205, 0x2205, _LOCALE_C);
307 wctyg = wctype("upper");
308 if (wctyg == NULL)
309 errx(1, "wctype(upper) == NULL");
310 wctyu = wctype_l("upper", _LOCALE_UTF8);
311 if (wctyu == NULL)
312 errx(1, "wctype_l(upper, UTF-8) == NULL");
313 if (wctyg != wctyu)
314 errx(1, "wctype global != UTF-8");
315 wctyc = wctype_l("upper", _LOCALE_C);
316 if (wctyc == NULL)
317 errx(1, "wctype_l(upper, C) == NULL");
318 TEST_R(iswctype, 1, 0x00D0, wctyg); /* Eth */
319 TEST_R(iswctype_l, 1, 0x00D0, wctyu, _LOCALE_UTF8);
320 TEST_R(iswctype_l, 0, 0x00D0, wctyc, _LOCALE_C);
321 TEST_R(iswctype, 1, 0x0393, wctyg); /* Gamma */
322 TEST_R(iswctype_l, 1, 0x0393, wctyu, _LOCALE_UTF8);
323 TEST_R(iswctype_l, 0, 0x0393, wctyc, _LOCALE_C);
324 TEST_R(iswctype, 0, 0x2205, wctyg); /* empty set */
325 TEST_R(iswctype_l, 0, 0x2205, wctyu, _LOCALE_UTF8);
326 TEST_R(iswctype_l, 0, 0x2205, wctyc, _LOCALE_C);
327 wctrg = wctrans("tolower");
328 if (wctrg == NULL)
329 errx(1, "wctrans(tolower) == NULL");
330 wctru = wctrans_l("tolower", _LOCALE_UTF8);
331 if (wctru == NULL)
332 errx(1, "wctrans(tolower, UTF-8) == NULL");
333 if (wctrg != wctru)
334 errx(1, "wctrans global != UTF-8");
335 wctrc = wctrans_l("tolower", _LOCALE_C);
336 if (wctrc == NULL)
337 errx(1, "wctrans(tolower, C) == NULL");
338 TEST_R(towctrans, 0x00FE, 0x00DE, wctrg); /* Thorn */
339 TEST_R(towctrans_l, 0x00FE, 0x00DE, wctru, _LOCALE_UTF8);
340 TEST_R(towctrans_l, 0x00DE, 0x00DE, wctrc, _LOCALE_C);
341 TEST_R(towctrans, 0x03C6, 0x03A6, wctrg); /* Phi */
342 TEST_R(towctrans_l, 0x03C6, 0x03A6, wctru, _LOCALE_UTF8);
343 TEST_R(towctrans_l, 0x03A6, 0x03A6, wctrc, _LOCALE_C);
344 TEST_R(towctrans, 0x2207, 0x2207, wctrg); /* Nabla */
345 TEST_R(towctrans_l, 0x2207, 0x2207, wctru, _LOCALE_UTF8);
346 TEST_R(towctrans_l, 0x2207, 0x2207, wctrc, _LOCALE_C);
347 TEST_R(wcscasecmp, 0, s1, s2);
348 TEST_R(wcscasecmp_l, 0, s1, s2, _LOCALE_UTF8);
349 TEST_R(wcscasecmp_l, *s1 - *s2, s1, s2, _LOCALE_C);
350 TEST_R(wcsncasecmp, 0, s3, s4, 2);
351 TEST_R(wcsncasecmp_l, 0, s3, s4, 2, _LOCALE_UTF8);
352 TEST_R(wcsncasecmp_l, *s3 - *s4, s3, s4, 2, _LOCALE_C);
353
354 /* Test non-ctype newlocale(3). */
355 TEST_R(newlocale, _LOCALE_C, LC_MESSAGES_MASK, "en_US.UTF-8");
356
357 /* Test strerror(3). */
358 sego = strerror(EPERM);
359 segc = strdup(sego);
360 selo = strerror_l(ENOENT, _LOCALE_C);
361 selc = strdup(selo);
362 if (strcmp(sego, segc) != 0)
363 errx(1, "child: strerror_l clobbered strerror");
364 free(segc);
365 sego = strerror(ESRCH);
366 if (strcmp(selo, selc) != 0)
367 errx(1, "child: strerror clobbered strerror_l");
368
369 /* Temporarily switch to the main thread. */
370 switch_thread(2, SWITCH_SIGNAL | SWITCH_WAIT);
371 if (strcmp(selo, selc) != 0)
372 errx(1, "child: main clobbered strerror_l");
373 free(selc);
374
375 /* Check that the C locale works even while all is set to UTF-8. */
376 TEST_R(nl_langinfo_l, "US-ASCII", CODESET, _LOCALE_C);
377 TEST_R(iswalpha_l, 0, 0x00E9, _LOCALE_C);
378 TEST_R(towupper_l, 0x00E9, 0x00E9, _LOCALE_C);
379 TEST_R(wcscasecmp_l, *s1 - *s2, s1, s2, _LOCALE_C);
380
381 /* Test displaying the global locale while a local one is set. */
382 TEST_R(setlocale, "C/C.UTF-8/C/C/C/C", LC_ALL, NULL);
383
384 /* Test switching the thread locale back. */
385 TEST_R(MB_CUR_MAX, 4);
386 TEST_R(duplocale, _LOCALE_UTF8, LC_GLOBAL_LOCALE);
387 TEST_R(uselocale, _LOCALE_UTF8, _LOCALE_C);
388 TEST_R(MB_CUR_MAX, 1);
389 TEST_R(uselocale, _LOCALE_C, _LOCALE_NONE);
390
391 /* Check that UTF-8 works even with a C thread locale. */
392 TEST_R(nl_langinfo, "US-ASCII", CODESET);
393 TEST_R(nl_langinfo_l, "UTF-8", CODESET, _LOCALE_UTF8);
394 TEST_R(iswalpha, 0, 0x0153);
395 TEST_R(iswalpha_l, 1, 0x0153, _LOCALE_UTF8);
396 TEST_R(towupper, 0x0153, 0x0153);
397 TEST_R(towupper_l, 0x0152, 0x0153, _LOCALE_UTF8);
398 TEST_R(wcsncasecmp, *s3 - *s4, s3, s4, 2);
399 TEST_R(wcsncasecmp_l, 0, s3, s4, 2, _LOCALE_UTF8);
400
401 /* Test switching back to the global locale. */
402 TEST_R(uselocale, _LOCALE_C, LC_GLOBAL_LOCALE);
403 TEST_R(MB_CUR_MAX, 4);
404 TEST_R(uselocale, LC_GLOBAL_LOCALE, _LOCALE_NONE);
405
406 /* Check that the global locale takes effect even in a thread. */
407 TEST_R(nl_langinfo, "UTF-8", CODESET);
408 TEST_R(iswalpha, 1, 0x0153);
409 TEST_R(towupper, 0x0152, 0x0153);
410 TEST_R(wcscasecmp, 0, s1, s2);
411
412 /* Hand control back to the main thread. */
413 switch_thread(4, SWITCH_SIGNAL);
414 return NULL;
415 }
416
417 int
main(void)418 main(void)
419 {
420 pthread_t child_thread;
421 char *sego, *segc, *selo, *selc;
422 int irc;
423
424 /* Initialize environment. */
425 unsetenv("LC_ALL");
426 unsetenv("LC_COLLATE");
427 unsetenv("LC_CTYPE");
428 unsetenv("LC_MONETARY");
429 unsetenv("LC_NUMERIC");
430 unsetenv("LC_TIME");
431 unsetenv("LC_MESSAGES");
432 unsetenv("LANG");
433
434 if ((irc = pthread_mutexattr_init(&mtxattr)) != 0)
435 errc(1, irc, "pthread_mutexattr_init");
436 if ((irc = pthread_mutexattr_settype(&mtxattr,
437 PTHREAD_MUTEX_STRICT_NP)) != 0)
438 errc(1, irc, "pthread_mutexattr_settype");
439 if ((irc = pthread_mutex_init(&mtx, &mtxattr)) != 0)
440 errc(1, irc, "pthread_mutex_init");
441 if ((irc = pthread_cond_init(&cond, NULL)) != 0)
442 errc(1, irc, "pthread_cond_init");
443
444 /* First let the child do some tests. */
445 if ((irc = pthread_create(&child_thread, NULL, child_func, NULL)) != 0)
446 errc(1, irc, "pthread_create");
447 switch_thread(1, SWITCH_WAIT);
448
449 /* Check that the global locale is undisturbed. */
450 TEST_R(setlocale, "C", LC_ALL, NULL);
451 TEST_R(MB_CUR_MAX, 1);
452
453 /* Check that *_l(3) works without any locale installed. */
454 TEST_R(nl_langinfo_l, "UTF-8", CODESET, _LOCALE_UTF8);
455 TEST_R(iswalpha_l, 1, 0x00E9, _LOCALE_UTF8);
456 TEST_R(towupper_l, 0x00C9, 0x00E9, _LOCALE_UTF8);
457
458 /* Test setting the globale locale. */
459 TEST_R(setlocale, "C.UTF-8", LC_CTYPE, "C.UTF-8");
460 TEST_R(MB_CUR_MAX, 4);
461 TEST_R(uselocale, LC_GLOBAL_LOCALE, _LOCALE_NONE);
462
463 /* Test strerror(3). */
464 sego = strerror(EINTR);
465 segc = strdup(sego);
466 selo = strerror_l(EIO, _LOCALE_C);
467 selc = strdup(selo);
468 if (strcmp(sego, segc) != 0)
469 errx(1, "main: strerror_l clobbered strerror");
470 free(segc);
471 sego = strerror(ENXIO);
472 if (strcmp(selo, selc) != 0)
473 errx(1, "main: strerror clobbered strerror_l");
474 free(selc);
475
476 /* Let the child do some more tests, then clean up. */
477 switch_thread(3, SWITCH_SIGNAL);
478 if ((irc = pthread_join(child_thread, NULL)) != 0)
479 errc(1, irc, "pthread_join");
480 return 0;
481 }
482