#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <langinfo.h>
#include <locale.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>
#define _LOCALE_NONE (locale_t)0
#define _LOCALE_C (locale_t)1
#define _LOCALE_UTF8 (locale_t)2
#define _LOCALE_BAD (locale_t)3
#define SWITCH_SIGNAL 1
#define SWITCH_WAIT 2
#define TOPT_ERR (1 << 0)
#define TESTFUNC(Fn, Ft, Af, Rf, Op) \
static void \
_test_##Fn(int line, int ee, Ft er, FUNCPARA) \
{ \
Ft ar; \
errno = 0; \
ar = Fn(FUNCARGS); \
if (ar != er) \
errx(1, "[%d] %s(" Af ")=" Rf " [exp: " Rf "]", \
line, #Fn, FUNCARGS, ar, er); \
if (Op & TOPT_ERR && errno != ee) \
errx(1, "[%d] %s(" Af ") errno=%d [exp: %d]", \
line, #Fn, FUNCARGS, errno, ee); \
}
#define STRTESTFUNC(Fn, Af) \
static void \
_test_##Fn(int line, int ee, const char *er, FUNCPARA) \
{ \
const char *ar; \
errno = 0; \
ar = Fn(FUNCARGS); \
if (er == NULL) \
er = "NULL"; \
if (ar == NULL) \
ar = "NULL"; \
if (strcmp((const char *)er, (const char *)ar) != 0) \
errx(1, "[%d] %s(" Af ")=%s [exp: %s]", \
line, #Fn, FUNCARGS, ar, er); \
}
#define FUNCPARA int mask, const char *locname
#define FUNCARGS mask, locname, _LOCALE_NONE
TESTFUNC(newlocale, locale_t, "%d, %s, %p", "%p", TOPT_ERR)
#define FUNCPARA locale_t locale
#define FUNCARGS locale
TESTFUNC(duplocale, locale_t, "%p", "%p", TOPT_ERR)
TESTFUNC(uselocale, locale_t, "%p", "%p", TOPT_ERR)
#define FUNCPARA int category, char *locname
#define FUNCARGS category, locname
STRTESTFUNC(setlocale, "%d, %s")
#define FUNCPARA nl_item item
#define FUNCARGS item
STRTESTFUNC(nl_langinfo, "%ld")
#define FUNCPARA nl_item item, locale_t locale
#define FUNCARGS item, locale
STRTESTFUNC(nl_langinfo_l, "%ld, %p")
#define FUNCPARA int c
#define FUNCARGS c
TESTFUNC(isalpha, int, "0x%.2x", "%d", 0)
TESTFUNC(tolower, int, "0x%.2x", "0x%.2x", 0)
#define FUNCPARA int c, locale_t locale
#define FUNCARGS c, locale
TESTFUNC(isalpha_l, int, "0x%.2x, %p", "%d", 0)
TESTFUNC(tolower_l, int, "0x%.2x, %p", "0x%.2x", 0)
#define FUNCPARA wint_t wc
#define FUNCARGS wc
TESTFUNC(iswalpha, int, "U+%.4X", "%d", 0)
TESTFUNC(towupper, wint_t, "U+%.4X", "U+%.4X", 0)
#define FUNCPARA wint_t wc, locale_t locale
#define FUNCARGS wc, locale
TESTFUNC(iswalpha_l, int, "U+%.4X, %p", "%d", 0)
TESTFUNC(towupper_l, wint_t, "U+%.4X, %p", "U+%.4X", 0)
#define FUNCPARA wint_t wc, wctype_t charclass
#define FUNCARGS wc, charclass
TESTFUNC(iswctype, int, "U+%.4X, %p", "%d", 0)
#define FUNCPARA wint_t wc, wctype_t charclass, locale_t locale
#define FUNCARGS wc, charclass, locale
TESTFUNC(iswctype_l, int, "U+%.4X, %p, %p", "%d", 0)
#define FUNCPARA wint_t wc, wctrans_t charmap
#define FUNCARGS wc, charmap
TESTFUNC(towctrans, wint_t, "U+%.4X, %p", "U+%.4X", 0)
#define FUNCPARA wint_t wc, wctrans_t charmap, locale_t locale
#define FUNCARGS wc, charmap, locale
TESTFUNC(towctrans_l, wint_t, "U+%.4X, %p, %p", "U+%.4X", 0)
#define FUNCPARA const wchar_t *s1, const wchar_t *s2
#define FUNCARGS s1, s2
TESTFUNC(wcscasecmp, int, "%ls, %ls", "%d", 0)
#define FUNCPARA const wchar_t *s1, const wchar_t *s2, locale_t locale
#define FUNCARGS s1, s2, locale
TESTFUNC(wcscasecmp_l, int, "%ls, %ls, %p", "%d", 0)
#define FUNCPARA const wchar_t *s1, const wchar_t *s2, size_t len
#define FUNCARGS s1, s2, len
TESTFUNC(wcsncasecmp, int, "%ls, %ls, %zu", "%d", 0)
#define FUNCPARA const wchar_t *s1, const wchar_t *s2, size_t len, \
locale_t locale
#define FUNCARGS s1, s2, len, locale
TESTFUNC(wcsncasecmp_l, int, "%ls, %ls, %zu, %p", "%d", 0)
static void
_test_MB_CUR_MAX(int line, int ee, size_t ar)
{
if (MB_CUR_MAX != ar)
errx(1, "[%d] MB_CUR_MAX=%zd [exp: %zd]",
line, MB_CUR_MAX, ar);
}
#define TEST_R(Fn, ...) _test_##Fn(__LINE__, 0, __VA_ARGS__)
#define TEST_ER(Fn, ...) _test_##Fn(__LINE__, __VA_ARGS__)
static pthread_mutex_t mtx;
static pthread_mutexattr_t mtxattr;
static pthread_cond_t cond;
static void
switch_thread(int step, int flags)
{
struct timespec t;
int irc;
if (flags & SWITCH_SIGNAL) {
if ((irc = pthread_cond_signal(&cond)) != 0)
errc(1, irc, "pthread_cond_signal(%d)", step);
}
if (flags & SWITCH_WAIT) {
if ((irc = pthread_mutex_trylock(&mtx)) != 0)
errc(1, irc, "pthread_mutex_trylock(%d)", step);
t.tv_sec = time(NULL) + 2;
t.tv_nsec = 0;
if ((irc = pthread_cond_timedwait(&cond, &mtx, &t)) != 0)
errc(1, irc, "pthread_cond_timedwait(%d)", step);
if ((irc = pthread_mutex_unlock(&mtx)) != 0)
errc(1, irc, "pthread_mutex_unlock(%d)", step);
}
}
static void *
child_func(void *arg)
{
const wchar_t s1[] = { 0x00C7, 0x00E0, 0x0000 };
const wchar_t s2[] = { 0x00E7, 0x00C0, 0x0000 };
const wchar_t s3[] = { 0x00C9, 0x0074, 0x00C9, 0x0000 };
const wchar_t s4[] = { 0x00E9, 0x0054, 0x00CC, 0x0000 };
wctype_t wctyg, wctyu, wctyc;
wctrans_t wctrg, wctru, wctrc;
char *sego, *segc, *selo, *selc;
TEST_ER(newlocale, EINVAL, _LOCALE_NONE, LC_CTYPE_MASK, NULL);
TEST_R(MB_CUR_MAX, 1);
TEST_ER(newlocale, EINVAL, _LOCALE_NONE, LC_ALL_MASK + 1, "C.UTF-8");
TEST_R(MB_CUR_MAX, 1);
TEST_ER(newlocale, ENOENT, _LOCALE_NONE, LC_COLLATE_MASK, "C.INV");
TEST_R(MB_CUR_MAX, 1);
setenv("LC_TIME", "C.INV", 1);
TEST_ER(newlocale, ENOENT, _LOCALE_NONE, LC_TIME_MASK, "");
unsetenv("LC_TIME");
TEST_R(MB_CUR_MAX, 1);
setenv("LC_CTYPE", "C.INV", 1);
TEST_ER(newlocale, ENOENT, _LOCALE_NONE, LC_CTYPE_MASK, "");
TEST_R(MB_CUR_MAX, 1);
TEST_ER(duplocale, EINVAL, _LOCALE_NONE, _LOCALE_UTF8);
TEST_R(duplocale, _LOCALE_C, _LOCALE_C);
TEST_R(duplocale, _LOCALE_C, LC_GLOBAL_LOCALE);
TEST_ER(uselocale, EINVAL, _LOCALE_NONE, _LOCALE_UTF8);
TEST_R(MB_CUR_MAX, 1);
TEST_R(uselocale, LC_GLOBAL_LOCALE, _LOCALE_NONE);
setenv("LC_CTYPE", "C.UTF-8", 1);
TEST_R(newlocale, _LOCALE_UTF8, LC_CTYPE_MASK, "");
unsetenv("LC_CTYPE");
TEST_R(MB_CUR_MAX, 1);
TEST_R(duplocale, _LOCALE_UTF8, _LOCALE_UTF8);
TEST_ER(uselocale, EINVAL, _LOCALE_NONE, _LOCALE_BAD);
TEST_R(MB_CUR_MAX, 1);
TEST_R(uselocale, LC_GLOBAL_LOCALE, _LOCALE_NONE);
TEST_R(nl_langinfo, "US-ASCII", CODESET);
TEST_R(nl_langinfo_l, "UTF-8", CODESET, _LOCALE_UTF8);
TEST_R(iswalpha, 0, 0x00E9);
TEST_R(iswalpha_l, 1, 0x00E9, _LOCALE_UTF8);
TEST_R(towupper, 0x00E9, 0x00E9);
TEST_R(towupper_l, 0x00C9, 0x00E9, _LOCALE_UTF8);
TEST_R(wcscasecmp, *s1 - *s2, s1, s2);
TEST_R(wcscasecmp_l, 0, s1, s2, _LOCALE_UTF8);
TEST_R(uselocale, LC_GLOBAL_LOCALE, _LOCALE_UTF8);
TEST_R(MB_CUR_MAX, 4);
TEST_R(uselocale, _LOCALE_UTF8, _LOCALE_NONE);
TEST_R(nl_langinfo, "UTF-8", CODESET);
TEST_R(nl_langinfo_l, "UTF-8", CODESET, _LOCALE_UTF8);
TEST_R(nl_langinfo_l, "US-ASCII", CODESET, _LOCALE_C);
TEST_R(isalpha, _CTYPE_L, 0x65);
TEST_R(isalpha_l, _CTYPE_L, 0x65, _LOCALE_UTF8);
TEST_R(isalpha_l, _CTYPE_L, 0x65, _LOCALE_C);
TEST_R(isalpha_l, _CTYPE_L, 0x65, _LOCALE_C);
TEST_R(isalpha, 0, 0x30);
TEST_R(isalpha_l, 0, 0x30, _LOCALE_UTF8);
TEST_R(isalpha_l, 0, 0x30, _LOCALE_C);
TEST_R(tolower, 0x61, 0x41);
TEST_R(tolower_l, 0x61, 0x41, _LOCALE_UTF8);
TEST_R(tolower_l, 0x61, 0x41, _LOCALE_C);
TEST_R(tolower, 0x40, 0x40);
TEST_R(tolower_l, 0x40, 0x40, _LOCALE_UTF8);
TEST_R(tolower_l, 0x40, 0x40, _LOCALE_C);
TEST_R(iswalpha, 1, 0x00E9);
TEST_R(iswalpha_l, 1, 0x00E9, _LOCALE_UTF8);
TEST_R(iswalpha_l, 0, 0x00E9, _LOCALE_C);
TEST_R(iswalpha, 1, 0x0153);
TEST_R(iswalpha_l, 1, 0x0153, _LOCALE_UTF8);
TEST_R(iswalpha_l, 0, 0x0153, _LOCALE_C);
TEST_R(iswalpha, 0, 0x2200);
TEST_R(iswalpha_l, 0, 0x2200, _LOCALE_UTF8);
TEST_R(iswalpha_l, 0, 0x2200, _LOCALE_C);
TEST_R(towupper, 0x00C9, 0x00E9);
TEST_R(towupper_l, 0x00C9, 0x00E9, _LOCALE_UTF8);
TEST_R(towupper_l, 0x00E9, 0x00E9, _LOCALE_C);
TEST_R(towupper, 0x0152, 0x0153);
TEST_R(towupper_l, 0x0152, 0x0153, _LOCALE_UTF8);
TEST_R(towupper_l, 0x0153, 0x0153, _LOCALE_C);
TEST_R(towupper, 0x2205, 0x2205);
TEST_R(towupper_l, 0x2205, 0x2205, _LOCALE_UTF8);
TEST_R(towupper_l, 0x2205, 0x2205, _LOCALE_C);
wctyg = wctype("upper");
if (wctyg == NULL)
errx(1, "wctype(upper) == NULL");
wctyu = wctype_l("upper", _LOCALE_UTF8);
if (wctyu == NULL)
errx(1, "wctype_l(upper, UTF-8) == NULL");
if (wctyg != wctyu)
errx(1, "wctype global != UTF-8");
wctyc = wctype_l("upper", _LOCALE_C);
if (wctyc == NULL)
errx(1, "wctype_l(upper, C) == NULL");
TEST_R(iswctype, 1, 0x00D0, wctyg);
TEST_R(iswctype_l, 1, 0x00D0, wctyu, _LOCALE_UTF8);
TEST_R(iswctype_l, 0, 0x00D0, wctyc, _LOCALE_C);
TEST_R(iswctype, 1, 0x0393, wctyg);
TEST_R(iswctype_l, 1, 0x0393, wctyu, _LOCALE_UTF8);
TEST_R(iswctype_l, 0, 0x0393, wctyc, _LOCALE_C);
TEST_R(iswctype, 0, 0x2205, wctyg);
TEST_R(iswctype_l, 0, 0x2205, wctyu, _LOCALE_UTF8);
TEST_R(iswctype_l, 0, 0x2205, wctyc, _LOCALE_C);
wctrg = wctrans("tolower");
if (wctrg == NULL)
errx(1, "wctrans(tolower) == NULL");
wctru = wctrans_l("tolower", _LOCALE_UTF8);
if (wctru == NULL)
errx(1, "wctrans(tolower, UTF-8) == NULL");
if (wctrg != wctru)
errx(1, "wctrans global != UTF-8");
wctrc = wctrans_l("tolower", _LOCALE_C);
if (wctrc == NULL)
errx(1, "wctrans(tolower, C) == NULL");
TEST_R(towctrans, 0x00FE, 0x00DE, wctrg);
TEST_R(towctrans_l, 0x00FE, 0x00DE, wctru, _LOCALE_UTF8);
TEST_R(towctrans_l, 0x00DE, 0x00DE, wctrc, _LOCALE_C);
TEST_R(towctrans, 0x03C6, 0x03A6, wctrg);
TEST_R(towctrans_l, 0x03C6, 0x03A6, wctru, _LOCALE_UTF8);
TEST_R(towctrans_l, 0x03A6, 0x03A6, wctrc, _LOCALE_C);
TEST_R(towctrans, 0x2207, 0x2207, wctrg);
TEST_R(towctrans_l, 0x2207, 0x2207, wctru, _LOCALE_UTF8);
TEST_R(towctrans_l, 0x2207, 0x2207, wctrc, _LOCALE_C);
TEST_R(wcscasecmp, 0, s1, s2);
TEST_R(wcscasecmp_l, 0, s1, s2, _LOCALE_UTF8);
TEST_R(wcscasecmp_l, *s1 - *s2, s1, s2, _LOCALE_C);
TEST_R(wcsncasecmp, 0, s3, s4, 2);
TEST_R(wcsncasecmp_l, 0, s3, s4, 2, _LOCALE_UTF8);
TEST_R(wcsncasecmp_l, *s3 - *s4, s3, s4, 2, _LOCALE_C);
TEST_R(newlocale, _LOCALE_C, LC_MESSAGES_MASK, "en_US.UTF-8");
sego = strerror(EPERM);
segc = strdup(sego);
selo = strerror_l(ENOENT, _LOCALE_C);
selc = strdup(selo);
if (strcmp(sego, segc) != 0)
errx(1, "child: strerror_l clobbered strerror");
free(segc);
sego = strerror(ESRCH);
if (strcmp(selo, selc) != 0)
errx(1, "child: strerror clobbered strerror_l");
switch_thread(2, SWITCH_SIGNAL | SWITCH_WAIT);
if (strcmp(selo, selc) != 0)
errx(1, "child: main clobbered strerror_l");
free(selc);
TEST_R(nl_langinfo_l, "US-ASCII", CODESET, _LOCALE_C);
TEST_R(iswalpha_l, 0, 0x00E9, _LOCALE_C);
TEST_R(towupper_l, 0x00E9, 0x00E9, _LOCALE_C);
TEST_R(wcscasecmp_l, *s1 - *s2, s1, s2, _LOCALE_C);
TEST_R(setlocale, "C/C.UTF-8/C/C/C/C", LC_ALL, NULL);
TEST_R(MB_CUR_MAX, 4);
TEST_R(duplocale, _LOCALE_UTF8, LC_GLOBAL_LOCALE);
TEST_R(uselocale, _LOCALE_UTF8, _LOCALE_C);
TEST_R(MB_CUR_MAX, 1);
TEST_R(uselocale, _LOCALE_C, _LOCALE_NONE);
TEST_R(nl_langinfo, "US-ASCII", CODESET);
TEST_R(nl_langinfo_l, "UTF-8", CODESET, _LOCALE_UTF8);
TEST_R(iswalpha, 0, 0x0153);
TEST_R(iswalpha_l, 1, 0x0153, _LOCALE_UTF8);
TEST_R(towupper, 0x0153, 0x0153);
TEST_R(towupper_l, 0x0152, 0x0153, _LOCALE_UTF8);
TEST_R(wcsncasecmp, *s3 - *s4, s3, s4, 2);
TEST_R(wcsncasecmp_l, 0, s3, s4, 2, _LOCALE_UTF8);
TEST_R(uselocale, _LOCALE_C, LC_GLOBAL_LOCALE);
TEST_R(MB_CUR_MAX, 4);
TEST_R(uselocale, LC_GLOBAL_LOCALE, _LOCALE_NONE);
TEST_R(nl_langinfo, "UTF-8", CODESET);
TEST_R(iswalpha, 1, 0x0153);
TEST_R(towupper, 0x0152, 0x0153);
TEST_R(wcscasecmp, 0, s1, s2);
switch_thread(4, SWITCH_SIGNAL);
return NULL;
}
int
main(void)
{
pthread_t child_thread;
char *sego, *segc, *selo, *selc;
int irc;
unsetenv("LC_ALL");
unsetenv("LC_COLLATE");
unsetenv("LC_CTYPE");
unsetenv("LC_MONETARY");
unsetenv("LC_NUMERIC");
unsetenv("LC_TIME");
unsetenv("LC_MESSAGES");
unsetenv("LANG");
if ((irc = pthread_mutexattr_init(&mtxattr)) != 0)
errc(1, irc, "pthread_mutexattr_init");
if ((irc = pthread_mutexattr_settype(&mtxattr,
PTHREAD_MUTEX_STRICT_NP)) != 0)
errc(1, irc, "pthread_mutexattr_settype");
if ((irc = pthread_mutex_init(&mtx, &mtxattr)) != 0)
errc(1, irc, "pthread_mutex_init");
if ((irc = pthread_cond_init(&cond, NULL)) != 0)
errc(1, irc, "pthread_cond_init");
if ((irc = pthread_create(&child_thread, NULL, child_func, NULL)) != 0)
errc(1, irc, "pthread_create");
switch_thread(1, SWITCH_WAIT);
TEST_R(setlocale, "C", LC_ALL, NULL);
TEST_R(MB_CUR_MAX, 1);
TEST_R(nl_langinfo_l, "UTF-8", CODESET, _LOCALE_UTF8);
TEST_R(iswalpha_l, 1, 0x00E9, _LOCALE_UTF8);
TEST_R(towupper_l, 0x00C9, 0x00E9, _LOCALE_UTF8);
TEST_R(setlocale, "C.UTF-8", LC_CTYPE, "C.UTF-8");
TEST_R(MB_CUR_MAX, 4);
TEST_R(uselocale, LC_GLOBAL_LOCALE, _LOCALE_NONE);
sego = strerror(EINTR);
segc = strdup(sego);
selo = strerror_l(EIO, _LOCALE_C);
selc = strdup(selo);
if (strcmp(sego, segc) != 0)
errx(1, "main: strerror_l clobbered strerror");
free(segc);
sego = strerror(ENXIO);
if (strcmp(selo, selc) != 0)
errx(1, "main: strerror clobbered strerror_l");
free(selc);
switch_thread(3, SWITCH_SIGNAL);
if ((irc = pthread_join(child_thread, NULL)) != 0)
errc(1, irc, "pthread_join");
return 0;
}