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