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