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