diff options
Diffstat (limited to '')
-rw-r--r-- | src/regress/lib/libc/locale/uselocale/uselocale.c | 481 |
1 files changed, 0 insertions, 481 deletions
diff --git a/src/regress/lib/libc/locale/uselocale/uselocale.c b/src/regress/lib/libc/locale/uselocale/uselocale.c deleted file mode 100644 index 4759eff46c..0000000000 --- a/src/regress/lib/libc/locale/uselocale/uselocale.c +++ /dev/null | |||
@@ -1,481 +0,0 @@ | |||
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 | ||
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 | ||
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 * | ||
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 | ||
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 | } | ||