diff options
-rw-r--r-- | src/regress/lib/libc/locale/uselocale/Makefile | 9 | ||||
-rw-r--r-- | src/regress/lib/libc/locale/uselocale/uselocale.c | 259 |
2 files changed, 268 insertions, 0 deletions
diff --git a/src/regress/lib/libc/locale/uselocale/Makefile b/src/regress/lib/libc/locale/uselocale/Makefile new file mode 100644 index 0000000000..45c3b7e807 --- /dev/null +++ b/src/regress/lib/libc/locale/uselocale/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | # $OpenBSD: Makefile,v 1.1 2017/08/10 14:45:42 schwarze Exp $ | ||
2 | |||
3 | PROG = uselocale | ||
4 | LDFLAGS += -pthread | ||
5 | |||
6 | run-regress-${PROG}: ${PROG} | ||
7 | ./${PROG} | ||
8 | |||
9 | .include <bsd.regress.mk> | ||
diff --git a/src/regress/lib/libc/locale/uselocale/uselocale.c b/src/regress/lib/libc/locale/uselocale/uselocale.c new file mode 100644 index 0000000000..a40f77397a --- /dev/null +++ b/src/regress/lib/libc/locale/uselocale/uselocale.c | |||
@@ -0,0 +1,259 @@ | |||
1 | /* $OpenBSD: uselocale.c,v 1.1 2017/08/10 14:45:42 schwarze Exp $ */ | ||
2 | /* | ||
3 | * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> | ||
4 | * Copyright (c) 2015 Sebastien Marie <semarie@openbsd.org> | ||
5 | * | ||
6 | * Permission to use, copy, modify, and distribute this software for any | ||
7 | * purpose with or without fee is hereby granted, provided that the above | ||
8 | * copyright notice and this permission notice appear in all copies. | ||
9 | * | ||
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES | ||
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR | ||
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
17 | */ | ||
18 | |||
19 | #include <err.h> | ||
20 | #include <errno.h> | ||
21 | #include <locale.h> | ||
22 | #include <pthread.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <string.h> | ||
25 | #include <time.h> | ||
26 | |||
27 | #define _LOCALE_NONE (locale_t)0 | ||
28 | #define _LOCALE_C (locale_t)1 | ||
29 | #define _LOCALE_UTF8 (locale_t)2 | ||
30 | #define _LOCALE_BAD (locale_t)3 | ||
31 | |||
32 | #define SWITCH_SIGNAL 1 | ||
33 | #define SWITCH_WAIT 2 | ||
34 | |||
35 | /* | ||
36 | * test helpers for __LINE__ | ||
37 | */ | ||
38 | #define test_newlocale(_e, _ee, _m, _l) \ | ||
39 | _test_newlocale(_e, _ee, _m, _l, __LINE__) | ||
40 | #define test_duplocale(_e, _ee, _l) _test_duplocale(_e, _ee, _l, __LINE__) | ||
41 | #define test_uselocale(_e, _ee, _l) _test_uselocale(_e, _ee, _l, __LINE__) | ||
42 | #define test_setlocale(_e, _c, _l) _test_setlocale(_e, _c, _l, __LINE__) | ||
43 | #define test_MB_CUR_MAX(_e) _test_MB_CUR_MAX(_e, __LINE__) | ||
44 | |||
45 | |||
46 | static void | ||
47 | _test_newlocale(locale_t expected, int exp_err, | ||
48 | int mask, const char *locname, int line) | ||
49 | { | ||
50 | locale_t result = newlocale(mask, locname, _LOCALE_NONE); | ||
51 | |||
52 | if (result != expected) | ||
53 | errx(1, "[%d] newlocale(%d, \"%s\")=\"%d\" [expected: \"%d\"]", | ||
54 | line, mask, locname, (int)result, (int)expected); | ||
55 | if (errno != exp_err) | ||
56 | errx(1, "[%d] newlocale(%d, \"%s\") errno=\"%d\" [expected:" | ||
57 | " \"%d\"]", line, mask, locname, errno, exp_err); | ||
58 | errno = 0; | ||
59 | } | ||
60 | |||
61 | static void | ||
62 | _test_duplocale(locale_t expected, int exp_err, locale_t oldloc, int line) | ||
63 | { | ||
64 | locale_t result = duplocale(oldloc); | ||
65 | |||
66 | if (result != expected) | ||
67 | errx(1, "[%d] duplocale(%d)=\"%d\" [expected: \"%d\"]", | ||
68 | line, (int)oldloc, (int)result, (int)expected); | ||
69 | if (errno != exp_err) | ||
70 | errx(1, "[%d] duplocale(%d) errno=\"%d\" [expected:" | ||
71 | " \"%d\"]", line, (int)oldloc, errno, exp_err); | ||
72 | errno = 0; | ||
73 | } | ||
74 | |||
75 | static void | ||
76 | _test_uselocale(locale_t expected, int exp_err, locale_t newloc, int line) | ||
77 | { | ||
78 | locale_t result = uselocale(newloc); | ||
79 | |||
80 | if (result != expected) | ||
81 | errx(1, "[%d] uselocale(%d)=\"%d\" [expected: \"%d\"]", | ||
82 | line, (int)newloc, (int)result, (int)expected); | ||
83 | if (errno != exp_err) | ||
84 | errx(1, "[%d] uselocale(%d) errno=\"%d\" [expected:" | ||
85 | " \"%d\"]", line, (int)newloc, errno, exp_err); | ||
86 | errno = 0; | ||
87 | } | ||
88 | |||
89 | static void | ||
90 | _test_setlocale(const char *expected, int category, char *locale, int line) | ||
91 | { | ||
92 | const char *result = setlocale(category, locale); | ||
93 | |||
94 | if (expected == NULL) | ||
95 | expected = "(null)"; | ||
96 | if (result == NULL) | ||
97 | result = "(null)"; | ||
98 | if (strcmp(expected, result) != 0) | ||
99 | errx(1, "[%d] setlocale(%d, \"%s\")=\"%s\" [expected: \"%s\"]", | ||
100 | line, category, locale, result, expected); | ||
101 | errno = 0; | ||
102 | } | ||
103 | |||
104 | static void | ||
105 | _test_MB_CUR_MAX(size_t expected, int line) | ||
106 | { | ||
107 | if (MB_CUR_MAX != expected) | ||
108 | errx(1, "[%d] MB_CUR_MAX=%ld [expected %ld]", | ||
109 | line, MB_CUR_MAX, expected); | ||
110 | } | ||
111 | |||
112 | static void | ||
113 | switch_thread(int step, int flags) | ||
114 | { | ||
115 | static pthread_mutexattr_t ma; | ||
116 | static struct timespec t; | ||
117 | static pthread_cond_t *c; | ||
118 | static pthread_mutex_t *m; | ||
119 | int irc; | ||
120 | |||
121 | if (m == NULL) { | ||
122 | if ((m = malloc(sizeof(*m))) == NULL) | ||
123 | err(1, NULL); | ||
124 | if ((irc = pthread_mutexattr_init(&ma)) != 0) | ||
125 | errc(1, irc, "pthread_mutexattr_init"); | ||
126 | if ((irc = pthread_mutexattr_settype(&ma, | ||
127 | PTHREAD_MUTEX_STRICT_NP)) != 0) | ||
128 | errc(1, irc, "pthread_mutexattr_settype"); | ||
129 | if ((irc = pthread_mutex_init(m, &ma)) != 0) | ||
130 | errc(1, irc, "pthread_mutex_init"); | ||
131 | } | ||
132 | if (c == NULL) { | ||
133 | if ((c = malloc(sizeof(*c))) == NULL) | ||
134 | err(1, NULL); | ||
135 | if ((irc = pthread_cond_init(c, NULL)) != 0) | ||
136 | errc(1, irc, "pthread_cond_init"); | ||
137 | } | ||
138 | if (flags & SWITCH_SIGNAL) { | ||
139 | if ((irc = pthread_cond_signal(c)) != 0) | ||
140 | errc(1, irc, "pthread_cond_signal(%d)", step); | ||
141 | } | ||
142 | if (flags & SWITCH_WAIT) { | ||
143 | if ((irc = pthread_mutex_trylock(m)) != 0) | ||
144 | errc(1, irc, "pthread_mutex_trylock(%d)", step); | ||
145 | t.tv_sec = time(NULL) + 2; | ||
146 | if ((irc = pthread_cond_timedwait(c, m, &t)) != 0) | ||
147 | errc(1, irc, "pthread_cond_timedwait(%d)", step); | ||
148 | if ((irc = pthread_mutex_unlock(m)) != 0) | ||
149 | errc(1, irc, "pthread_mutex_unlock(%d)", step); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | static void * | ||
154 | child_func(void *arg) | ||
155 | { | ||
156 | /* Test invalid newlocale(3) arguments. */ | ||
157 | test_newlocale(_LOCALE_NONE, EINVAL, LC_CTYPE_MASK, NULL); | ||
158 | test_MB_CUR_MAX(1); | ||
159 | test_newlocale(_LOCALE_NONE, EINVAL, LC_ALL_MASK + 1, "C.UTF-8"); | ||
160 | test_MB_CUR_MAX(1); | ||
161 | test_newlocale(_LOCALE_NONE, ENOENT, LC_COLLATE_MASK, "C.INV"); | ||
162 | test_MB_CUR_MAX(1); | ||
163 | setenv("LC_TIME", "C.INV", 1); | ||
164 | test_newlocale(_LOCALE_NONE, ENOENT, LC_TIME_MASK, ""); | ||
165 | unsetenv("LC_TIME"); | ||
166 | test_MB_CUR_MAX(1); | ||
167 | setenv("LC_CTYPE", "C.INV", 1); | ||
168 | test_newlocale(_LOCALE_NONE, ENOENT, LC_CTYPE_MASK, ""); | ||
169 | test_MB_CUR_MAX(1); | ||
170 | |||
171 | /* Test duplocale(3). */ | ||
172 | test_duplocale(_LOCALE_NONE, EINVAL, _LOCALE_UTF8); | ||
173 | test_duplocale(_LOCALE_C, 0, _LOCALE_C); | ||
174 | test_duplocale(_LOCALE_C, 0, LC_GLOBAL_LOCALE); | ||
175 | |||
176 | /* Test premature UTF-8 uselocale(3). */ | ||
177 | test_uselocale(_LOCALE_NONE, EINVAL, _LOCALE_UTF8); | ||
178 | test_MB_CUR_MAX(1); | ||
179 | test_uselocale(LC_GLOBAL_LOCALE, 0, _LOCALE_NONE); | ||
180 | |||
181 | /* Test UTF-8 initialization. */ | ||
182 | setenv("LC_CTYPE", "C.UTF-8", 1); | ||
183 | test_newlocale(_LOCALE_UTF8, 0, LC_CTYPE_MASK, ""); | ||
184 | unsetenv("LC_CTYPE"); | ||
185 | test_MB_CUR_MAX(1); | ||
186 | test_duplocale(_LOCALE_UTF8, 0, _LOCALE_UTF8); | ||
187 | |||
188 | /* Test invalid uselocale(3) argument. */ | ||
189 | test_uselocale(_LOCALE_NONE, EINVAL, _LOCALE_BAD); | ||
190 | test_MB_CUR_MAX(1); | ||
191 | test_uselocale(LC_GLOBAL_LOCALE, 0, _LOCALE_NONE); | ||
192 | |||
193 | /* Test switching the thread locale. */ | ||
194 | test_uselocale(LC_GLOBAL_LOCALE, 0, _LOCALE_UTF8); | ||
195 | test_MB_CUR_MAX(4); | ||
196 | test_uselocale(_LOCALE_UTF8, 0, _LOCALE_NONE); | ||
197 | |||
198 | /* Test non-ctype newlocale(3). */ | ||
199 | test_newlocale(_LOCALE_C, 0, LC_MESSAGES_MASK, "en_US.UTF-8"); | ||
200 | |||
201 | /* Temporarily switch to the main thread. */ | ||
202 | switch_thread(2, SWITCH_SIGNAL | SWITCH_WAIT); | ||
203 | |||
204 | /* Test displaying the global locale while a local one is set. */ | ||
205 | test_setlocale("C/C.UTF-8/C/C/C/C", LC_ALL, NULL); | ||
206 | |||
207 | /* Test switching the thread locale back. */ | ||
208 | test_MB_CUR_MAX(4); | ||
209 | test_duplocale(_LOCALE_UTF8, 0, LC_GLOBAL_LOCALE); | ||
210 | test_uselocale(_LOCALE_UTF8, 0, _LOCALE_C); | ||
211 | test_MB_CUR_MAX(1); | ||
212 | test_uselocale(_LOCALE_C, 0, _LOCALE_NONE); | ||
213 | |||
214 | /* Test switching back to the global locale. */ | ||
215 | test_uselocale(_LOCALE_C, 0, LC_GLOBAL_LOCALE); | ||
216 | test_MB_CUR_MAX(4); | ||
217 | test_uselocale(LC_GLOBAL_LOCALE, 0, _LOCALE_NONE); | ||
218 | |||
219 | /* Hand control back to the main thread. */ | ||
220 | switch_thread(4, SWITCH_SIGNAL); | ||
221 | return NULL; | ||
222 | } | ||
223 | |||
224 | int | ||
225 | main(void) | ||
226 | { | ||
227 | pthread_t child_thread; | ||
228 | int irc; | ||
229 | |||
230 | /* Initialize environment. */ | ||
231 | unsetenv("LC_ALL"); | ||
232 | unsetenv("LC_COLLATE"); | ||
233 | unsetenv("LC_CTYPE"); | ||
234 | unsetenv("LC_MONETARY"); | ||
235 | unsetenv("LC_NUMERIC"); | ||
236 | unsetenv("LC_TIME"); | ||
237 | unsetenv("LC_MESSAGES"); | ||
238 | unsetenv("LANG"); | ||
239 | |||
240 | /* First let the child do some tests. */ | ||
241 | if ((irc = pthread_create(&child_thread, NULL, child_func, NULL)) != 0) | ||
242 | errc(1, irc, "pthread_create"); | ||
243 | switch_thread(1, SWITCH_WAIT); | ||
244 | |||
245 | /* Check that the global locale is undisturbed. */ | ||
246 | test_setlocale("C", LC_ALL, NULL); | ||
247 | test_MB_CUR_MAX(1); | ||
248 | |||
249 | /* Test setting the globale locale. */ | ||
250 | test_setlocale("C.UTF-8", LC_CTYPE, "C.UTF-8"); | ||
251 | test_MB_CUR_MAX(4); | ||
252 | test_uselocale(LC_GLOBAL_LOCALE, 0, _LOCALE_NONE); | ||
253 | |||
254 | /* Let the child do some more tests, then clean up. */ | ||
255 | switch_thread(3, SWITCH_SIGNAL); | ||
256 | if ((irc = pthread_join(child_thread, NULL)) != 0) | ||
257 | errc(1, irc, "pthread_join"); | ||
258 | return 0; | ||
259 | } | ||