diff options
Diffstat (limited to '')
| -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 | } | ||
