1*2d08521bSGarrett D'Amore /*
2*2d08521bSGarrett D'Amore  * This file and its contents are supplied under the terms of the
3*2d08521bSGarrett D'Amore  * Common Development and Distribution License ("CDDL"), version 1.0.
4*2d08521bSGarrett D'Amore  * You may only use this file in accordance with the terms of version
5*2d08521bSGarrett D'Amore  * 1.0 of the CDDL.
6*2d08521bSGarrett D'Amore  *
7*2d08521bSGarrett D'Amore  * A full copy of the text of the CDDL should have accompanied this
8*2d08521bSGarrett D'Amore  * source.  A copy of the CDDL is also available via the Internet at
9*2d08521bSGarrett D'Amore  * http://www.illumos.org/license/CDDL.
10*2d08521bSGarrett D'Amore  */
11*2d08521bSGarrett D'Amore 
12*2d08521bSGarrett D'Amore /*
13*2d08521bSGarrett D'Amore  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
14*2d08521bSGarrett D'Amore  */
15*2d08521bSGarrett D'Amore 
16*2d08521bSGarrett D'Amore /*
17*2d08521bSGarrett D'Amore  * This program tests that newlocale and uselocale work properly in
18*2d08521bSGarrett D'Amore  * multi-threaded programs.  In order for it to work, it requires that
19*2d08521bSGarrett D'Amore  * some additional locales be installed.
20*2d08521bSGarrett D'Amore  */
21*2d08521bSGarrett D'Amore 
22*2d08521bSGarrett D'Amore #include <stdio.h>
23*2d08521bSGarrett D'Amore #include <stdlib.h>
24*2d08521bSGarrett D'Amore #include <string.h>
25*2d08521bSGarrett D'Amore #include <locale.h>
26*2d08521bSGarrett D'Amore #include <libintl.h>
27*2d08521bSGarrett D'Amore #include <langinfo.h>
28*2d08521bSGarrett D'Amore #include <nl_types.h>
29*2d08521bSGarrett D'Amore #include <err.h>
30*2d08521bSGarrett D'Amore #include <unistd.h>
31*2d08521bSGarrett D'Amore #include <pthread.h>
32*2d08521bSGarrett D'Amore 
33*2d08521bSGarrett D'Amore int debug = 0;
34*2d08521bSGarrett D'Amore 
35*2d08521bSGarrett D'Amore /*
36*2d08521bSGarrett D'Amore  * Note that on some platforms, different symbols are used.  For example,
37*2d08521bSGarrett D'Amore  * MacOS Mavericks uses "Eu" for Euro symbol, instead of €.  If the locale
38*2d08521bSGarrett D'Amore  * data changes, then this program will need to update to reflect that.
39*2d08521bSGarrett D'Amore  */
40*2d08521bSGarrett D'Amore struct ldata {
41*2d08521bSGarrett D'Amore 	const char *locale;
42*2d08521bSGarrett D'Amore 	const char *day1;
43*2d08521bSGarrett D'Amore 	const char *cursym;
44*2d08521bSGarrett D'Amore } ldata[] = {
45*2d08521bSGarrett D'Amore 	{ "C", "Sunday", "" },
46*2d08521bSGarrett D'Amore 	{ "en_US.UTF-8", "Sunday", "$" },
47*2d08521bSGarrett D'Amore 	{ "de_DE.UTF-8", "Sonntag", "€" },
48*2d08521bSGarrett D'Amore 	{ "ru_RU.UTF-8", "воскресенье", "руб." },
49*2d08521bSGarrett D'Amore 	{ "ja_JP.UTF-8", "日曜日", "¥" },
50*2d08521bSGarrett D'Amore };
51*2d08521bSGarrett D'Amore 
52*2d08521bSGarrett D'Amore #define	NUM_LDATA	5
53*2d08521bSGarrett D'Amore #define	NUMTHR	20
54*2d08521bSGarrett D'Amore #define	NUMITR	200
55*2d08521bSGarrett D'Amore 
56*2d08521bSGarrett D'Amore static void
57*2d08521bSGarrett D'Amore test_start(const char *testName, const char *format, ...)
58*2d08521bSGarrett D'Amore {
59*2d08521bSGarrett D'Amore 	va_list args;
60*2d08521bSGarrett D'Amore 
61*2d08521bSGarrett D'Amore 	(void) printf("TEST STARTING %s: ", testName);
62*2d08521bSGarrett D'Amore 
63*2d08521bSGarrett D'Amore 	va_start(args, format);
64*2d08521bSGarrett D'Amore 	(void) vprintf(format, args);
65*2d08521bSGarrett D'Amore 	va_end(args);
66*2d08521bSGarrett D'Amore 	(void) fflush(stdout);
67*2d08521bSGarrett D'Amore }
68*2d08521bSGarrett D'Amore 
69*2d08521bSGarrett D'Amore static void
70*2d08521bSGarrett D'Amore test_failed(const char *testName, const char *format, ...)
71*2d08521bSGarrett D'Amore {
72*2d08521bSGarrett D'Amore 	va_list args;
73*2d08521bSGarrett D'Amore 
74*2d08521bSGarrett D'Amore 	(void) printf("TEST FAILED %s: ", testName);
75*2d08521bSGarrett D'Amore 
76*2d08521bSGarrett D'Amore 	va_start(args, format);
77*2d08521bSGarrett D'Amore 	(void) vprintf(format, args);
78*2d08521bSGarrett D'Amore 	va_end(args);
79*2d08521bSGarrett D'Amore 
80*2d08521bSGarrett D'Amore 	(void) exit(-1);
81*2d08521bSGarrett D'Amore }
82*2d08521bSGarrett D'Amore 
83*2d08521bSGarrett D'Amore static void
84*2d08521bSGarrett D'Amore test_passed(const char *testName)
85*2d08521bSGarrett D'Amore {
86*2d08521bSGarrett D'Amore 	(void) printf("TEST PASS: %s\n", testName);
87*2d08521bSGarrett D'Amore 	(void) fflush(stdout);
88*2d08521bSGarrett D'Amore }
89*2d08521bSGarrett D'Amore 
90*2d08521bSGarrett D'Amore void *
91*2d08521bSGarrett D'Amore testlocale_thr(void *ptr)
92*2d08521bSGarrett D'Amore {
93*2d08521bSGarrett D'Amore 	locale_t	cloc, loc;
94*2d08521bSGarrett D'Amore 	struct lconv	*lc;
95*2d08521bSGarrett D'Amore 	char		*day;
96*2d08521bSGarrett D'Amore 	char		*tname = ptr;
97*2d08521bSGarrett D'Amore 
98*2d08521bSGarrett D'Amore 	for (int i = 0; i < NUMITR; i++) {
99*2d08521bSGarrett D'Amore 		struct ldata *l = &ldata[i % NUM_LDATA];
100*2d08521bSGarrett D'Amore 		cloc = uselocale(NULL);
101*2d08521bSGarrett D'Amore 
102*2d08521bSGarrett D'Amore 		loc = newlocale(LC_ALL_MASK, l->locale, NULL);
103*2d08521bSGarrett D'Amore 		if (loc == NULL) {
104*2d08521bSGarrett D'Amore 			test_failed("newlocale %s failed", l->locale);
105*2d08521bSGarrett D'Amore 		}
106*2d08521bSGarrett D'Amore 		day = nl_langinfo_l(DAY_1, loc);
107*2d08521bSGarrett D'Amore 		if (strcmp(day, l->day1) != 0) {
108*2d08521bSGarrett D'Amore 			test_failed(tname, "newlocale data mismatch (%s != %s)",
109*2d08521bSGarrett D'Amore 			    day, l->day1);
110*2d08521bSGarrett D'Amore 		}
111*2d08521bSGarrett D'Amore 		if (debug)
112*2d08521bSGarrett D'Amore 			(void) printf("DAY1: %s\n", day);
113*2d08521bSGarrett D'Amore 
114*2d08521bSGarrett D'Amore 		day = nl_langinfo(DAY_1);
115*2d08521bSGarrett D'Amore 		if (strcmp(day, "Sunday") != 0) {
116*2d08521bSGarrett D'Amore 			test_failed(tname, "C locale day wrong %s != Sunday",
117*2d08521bSGarrett D'Amore 			    day);
118*2d08521bSGarrett D'Amore 		}
119*2d08521bSGarrett D'Amore 		lc = localeconv();
120*2d08521bSGarrett D'Amore 		if (strcmp(lc->currency_symbol, "") != 0) {
121*2d08521bSGarrett D'Amore 			test_failed(tname, "C cursym mismatch (%s != %s)",
122*2d08521bSGarrett D'Amore 			    lc->currency_symbol, "");
123*2d08521bSGarrett D'Amore 		}
124*2d08521bSGarrett D'Amore 
125*2d08521bSGarrett D'Amore 		/* we sleep a random bit to mix it up */
126*2d08521bSGarrett D'Amore 		(void) usleep(rand() % 10);
127*2d08521bSGarrett D'Amore 
128*2d08521bSGarrett D'Amore 		(void) uselocale(loc);
129*2d08521bSGarrett D'Amore 		day = nl_langinfo(DAY_1);
130*2d08521bSGarrett D'Amore 		if (strcmp(day, l->day1) != 0) {
131*2d08521bSGarrett D'Amore 			test_failed(tname, "uselocale data mismatch (%s != %s)",
132*2d08521bSGarrett D'Amore 			    day, l->day1);
133*2d08521bSGarrett D'Amore 		}
134*2d08521bSGarrett D'Amore 
135*2d08521bSGarrett D'Amore 		lc = localeconv();
136*2d08521bSGarrett D'Amore 		if (strcmp(lc->currency_symbol, l->cursym) != 0) {
137*2d08521bSGarrett D'Amore 			test_failed(tname, "uselocal cursym %s != %s",
138*2d08521bSGarrett D'Amore 			    lc->currency_symbol, l->cursym);
139*2d08521bSGarrett D'Amore 		}
140*2d08521bSGarrett D'Amore 		if (debug)
141*2d08521bSGarrett D'Amore 			(void) printf("CSYM: %s\n", lc->currency_symbol);
142*2d08521bSGarrett D'Amore 
143*2d08521bSGarrett D'Amore 		/* we sleep a random bit to mix it up */
144*2d08521bSGarrett D'Amore 		(void) usleep(rand() % 10);
145*2d08521bSGarrett D'Amore 
146*2d08521bSGarrett D'Amore 		if (uselocale(cloc) != loc) {
147*2d08521bSGarrett D'Amore 			test_failed(tname, "revert old locale mismatch");
148*2d08521bSGarrett D'Amore 		}
149*2d08521bSGarrett D'Amore 		freelocale(loc);
150*2d08521bSGarrett D'Amore 		if (uselocale(LC_GLOBAL_LOCALE) != cloc) {
151*2d08521bSGarrett D'Amore 			test_failed(tname, "revert GLOBAL_LOCALE mismatch");
152*2d08521bSGarrett D'Amore 		}
153*2d08521bSGarrett D'Amore 	}
154*2d08521bSGarrett D'Amore 	return (NULL);
155*2d08521bSGarrett D'Amore }
156*2d08521bSGarrett D'Amore 
157*2d08521bSGarrett D'Amore 
158*2d08521bSGarrett D'Amore void
159*2d08521bSGarrett D'Amore testlocale(void)
160*2d08521bSGarrett D'Amore {
161*2d08521bSGarrett D'Amore 	char		*tname = "newlocale/uselocale";
162*2d08521bSGarrett D'Amore 	pthread_t	tid[NUMTHR];
163*2d08521bSGarrett D'Amore 
164*2d08521bSGarrett D'Amore 	test_start(tname, "running %d threads %d iterations\n", NUMTHR, NUMITR);
165*2d08521bSGarrett D'Amore 
166*2d08521bSGarrett D'Amore 	for (int i = 0; i < NUMTHR; i++) {
167*2d08521bSGarrett D'Amore 		(void) pthread_create(&tid[i], NULL, testlocale_thr, tname);
168*2d08521bSGarrett D'Amore 	}
169*2d08521bSGarrett D'Amore 
170*2d08521bSGarrett D'Amore 	for (int i = 0; i < NUMTHR; i++) {
171*2d08521bSGarrett D'Amore 		(void) pthread_join(tid[i], NULL);
172*2d08521bSGarrett D'Amore 	}
173*2d08521bSGarrett D'Amore 	test_passed(tname);
174*2d08521bSGarrett D'Amore }
175*2d08521bSGarrett D'Amore 
176*2d08521bSGarrett D'Amore int
177*2d08521bSGarrett D'Amore main(int argc, char **argv)
178*2d08521bSGarrett D'Amore {
179*2d08521bSGarrett D'Amore 	int optc;
180*2d08521bSGarrett D'Amore 
181*2d08521bSGarrett D'Amore 	while ((optc = getopt(argc, argv, "d")) != EOF) {
182*2d08521bSGarrett D'Amore 		switch (optc) {
183*2d08521bSGarrett D'Amore 		case 'd':
184*2d08521bSGarrett D'Amore 			debug++;
185*2d08521bSGarrett D'Amore 			break;
186*2d08521bSGarrett D'Amore 		default:
187*2d08521bSGarrett D'Amore 			(void) fprintf(stderr, "Usage: %s [-d]\n", argv[0]);
188*2d08521bSGarrett D'Amore 			exit(1);
189*2d08521bSGarrett D'Amore 		}
190*2d08521bSGarrett D'Amore 	}
191*2d08521bSGarrett D'Amore 
192*2d08521bSGarrett D'Amore 	testlocale();
193*2d08521bSGarrett D'Amore 
194*2d08521bSGarrett D'Amore 	exit(0);
195*2d08521bSGarrett D'Amore }
196