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