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 <errno.h>
31 #include <unistd.h>
32 #include "test_common.h"
33 
34 /*
35  * Note that on some platforms, different symbols are used.  For example,
36  * MacOS Mavericks uses "Eu" for Euro symbol, instead of €.  If the locale
37  * data changes, then this program will need to update to reflect that.
38  *
39  * Note also that this file is easiest edited with a UTF-8 capable editor,
40  * as there are embedded UTF-8 symbols in some of the strings.
41  */
42 struct langinfo_test {
43 	nl_item		param;
44 	const char	*value;
45 };
46 
47 struct langinfo_test C_data[] = {
48 	{ CODESET,	"646" },
49 	{ D_T_FMT,	"%a %b %e %H:%M:%S %Y" },
50 	{ D_FMT,	"%m/%d/%y" },
51 	{ T_FMT,	"%H:%M:%S" },
52 	{ T_FMT_AMPM,	"%I:%M:%S %p" },
53 	{ AM_STR,	"AM" },
54 	{ PM_STR,	"PM" },
55 	{ ERA,		"" },
56 	{ ERA_D_FMT,	"" },
57 	{ ERA_D_T_FMT,	"" },
58 	{ ERA_T_FMT,	"" },
59 	{ DAY_1,	"Sunday" },
60 	{ DAY_7,	"Saturday" },
61 	{ ABDAY_1,	"Sun" },
62 	{ ABDAY_7,	"Sat" },
63 	{ MON_1,	"January" },
64 	{ MON_12,	"December" },
65 	{ ABMON_1,	"Jan" },
66 	{ ABMON_12,	"Dec" },
67 	{ RADIXCHAR,	"." },
68 	{ THOUSEP,	"" },
69 	{ YESSTR,	"yes" },
70 	{ NOSTR,	"no" },
71 	{ YESEXPR,	"^[yY]" },
72 	{ NOEXPR,	"^[nN]" },
73 	{ CRNCYSTR,	"" },
74 	{ -1,		NULL }
75 };
76 
77 struct langinfo_test en_us_utf8_data[] = {
78 	{ CODESET,	"UTF-8" },
79 	{ D_T_FMT,	"%B %e, %Y at %I:%M:%S %p %Z" },
80 	{ D_FMT,	"%m/%d/%y" },
81 	{ T_FMT,	"%I:%M:%S %p" },
82 	{ T_FMT_AMPM,	"%I:%M:%S %p" },
83 	{ AM_STR,	"AM" },
84 	{ PM_STR,	"PM" },
85 	{ ERA,		"" },
86 	{ ERA_D_FMT,	"" },
87 	{ ERA_D_T_FMT,	"" },
88 	{ ERA_T_FMT,	"" },
89 	{ DAY_1,	"Sunday" },
90 	{ DAY_7,	"Saturday" },
91 	{ ABDAY_1,	"Sun" },
92 	{ ABDAY_7,	"Sat" },
93 	{ MON_1,	"January" },
94 	{ MON_12,	"December" },
95 	{ ABMON_1,	"Jan" },
96 	{ ABMON_12,	"Dec" },
97 	{ RADIXCHAR,	"." },
98 	{ THOUSEP,	"," },
99 	{ YESSTR,	"yes" },
100 	{ NOSTR,	"no" },
101 	{ YESEXPR,	"^(([yY]([eE][sS])?)|([yY]))" },
102 	{ NOEXPR,	"^(([nN]([oO])?)|([nN]))" },
103 	{ CRNCYSTR,	"-$" },
104 	{ -1,		NULL }
105 };
106 
107 struct langinfo_test en_gb_latin15_data[] = {
108 	{ CODESET,	"ISO8859-15" },
109 	{ D_T_FMT,	"%e %B %Y at %H:%M:%S %Z" },
110 	{ D_FMT,	"%d/%m/%Y" },
111 	{ T_FMT,	"%H:%M:%S" },
112 	{ T_FMT_AMPM,	"%I:%M:%S %p" },
113 	{ AM_STR,	"am" },
114 	{ PM_STR,	"pm" },
115 	{ ERA,		"" },
116 	{ ERA_D_FMT,	"" },
117 	{ ERA_D_T_FMT,	"" },
118 	{ ERA_T_FMT,	"" },
119 	{ DAY_1,	"Sunday" },
120 	{ DAY_7,	"Saturday" },
121 	{ ABDAY_1,	"Sun" },
122 	{ ABDAY_7,	"Sat" },
123 	{ MON_1,	"January" },
124 	{ MON_12,	"December" },
125 	{ ABMON_1,	"Jan" },
126 	{ ABMON_12,	"Dec" },
127 	{ RADIXCHAR,	"." },
128 	{ THOUSEP,	"," },
129 	{ YESSTR,	"yes" },
130 	{ NOSTR,	"no" },
131 	{ YESEXPR,	"^(([yY]([eE][sS])?)|([yY]))" },
132 	{ NOEXPR,	"^(([nN]([oO])?)|([nN]))" },
133 	{ CRNCYSTR,	"-\243" },
134 	{ -1,		NULL }
135 };
136 
137 struct langinfo_test ru_ru_utf8_data[] = {
138 	{ CODESET,	"UTF-8" },
139 	{ D_T_FMT,	"%e %B %Y г., %H:%M:%S %Z"},
140 	{ D_FMT,	"%d.%m.%Y" },
141 	{ T_FMT,	"%H:%M:%S" },
142 	{ T_FMT_AMPM,	"%I:%M:%S %p" },
143 	{ AM_STR,	"ДП" },
144 	{ PM_STR,	"ПП" },
145 	{ ERA,		"" },
146 	{ ERA_D_FMT,	"" },
147 	{ ERA_D_T_FMT,	"" },
148 	{ ERA_T_FMT,	"" },
149 	{ DAY_1,	"воскресенье" },
150 	{ DAY_7,	"суббота" },
151 	{ ABDAY_1,	"вс" },
152 	{ ABDAY_7,	"сб" },
153 	{ MON_1,	"января" },
154 	{ MON_12,	"декабря" },
155 	{ ABMON_1,	"янв." },
156 	{ ABMON_12,	"дек." },
157 	{ RADIXCHAR,	"," },
158 	{ THOUSEP,	" " },
159 	{ YESSTR,	"да" },
160 	{ NOSTR,	"нет" },
161 	{ YESEXPR,	"^(([дД]([аА])?)|([дД])|([yY]([eE][sS])?)|([yY]))" },
162 	{ NOEXPR,	"^(([нН]([еЕ][тТ])?)|([нН])|([nN]([oO])?)|([nN]))" },
163 	{ CRNCYSTR,	"+₽" },
164 	{ -1,		NULL }
165 };
166 
167 struct {
168 	const char *locale;
169 	struct langinfo_test *loctest;
170 } locales[] =  {
171 	{ "C",			C_data },
172 	{ "en_US.UTF-8",	en_us_utf8_data },
173 	{ "en_GB.ISO8859-15",	en_gb_latin15_data },
174 	{ "ru_RU.UTF-8",	ru_ru_utf8_data },
175 	{ NULL, 		NULL }
176 };
177 
178 void
test_nl_langinfo_1(const char * locale,struct langinfo_test * test)179 test_nl_langinfo_1(const char *locale, struct langinfo_test *test)
180 {
181 	char 	tname[128];
182 	char 	*v;
183 	test_t	t;
184 
185 	(void) snprintf(tname, sizeof (tname), "nl_langinfo (locale %s)",
186 	    locale);
187 	t = test_start(tname);
188 
189 	v = setlocale(LC_ALL, locale);
190 	if (v == NULL) {
191 		test_failed(t, "setlocale failed: %s", strerror(errno));
192 	}
193 	if (strcmp(v, locale) != 0) {
194 		test_failed(t, "setlocale got %s instead of %s", v, locale);
195 	}
196 
197 	for (int i = 0; test[i].value != NULL; i++) {
198 		v = nl_langinfo(test[i].param);
199 		test_debugf(t, "%d: expect [%s], got [%s]",
200 		    test[i].param, test[i].value, v);
201 		if (strcmp(v, test[i].value) != 0) {
202 			test_failed(t,
203 			    "param %d wrong, expected [%s], got [%s]",
204 			    test[i].param, test[i].value, v);
205 		}
206 	}
207 	test_passed(t);
208 }
209 
210 void
test_nl_langinfo_l(const char * locale,struct langinfo_test * test)211 test_nl_langinfo_l(const char *locale, struct langinfo_test *test)
212 {
213 	char 		tname[128];
214 	char 		*v;
215 	test_t		t;
216 	locale_t	loc;
217 
218 	(void) snprintf(tname, sizeof (tname), "nl_langinfo_l (locale %s)",
219 	    locale);
220 	t = test_start(tname);
221 
222 	v = setlocale(LC_ALL, "C");
223 	if (v == NULL) {
224 		test_failed(t, "setlocale failed: %s", strerror(errno));
225 	}
226 	if (strcmp(v, "C") != 0) {
227 		test_failed(t, "setlocale got %s instead of %s", v, "C");
228 	}
229 
230 	loc = newlocale(LC_ALL_MASK, locale, NULL);
231 	if (loc == NULL) {
232 		test_failed(t, "newlocale failed: %s", strerror(errno));
233 	}
234 
235 	for (int i = 0; test[i].value != NULL; i++) {
236 		v = nl_langinfo_l(test[i].param, loc);
237 		test_debugf(t, "%d: expect [%s], got [%s]",
238 		    test[i].param, test[i].value, v);
239 		if (strcmp(v, test[i].value) != 0) {
240 			test_failed(t,
241 			    "param %d wrong, expected [%s], got [%s]",
242 			    test[i].param, test[i].value, v);
243 		}
244 	}
245 	test_passed(t);
246 }
247 void
test_nl_langinfo(void)248 test_nl_langinfo(void)
249 {
250 	for (int i = 0; locales[i].locale != NULL; i++) {
251 		test_nl_langinfo_1(locales[i].locale, locales[i].loctest);
252 		test_nl_langinfo_l(locales[i].locale, locales[i].loctest);
253 	}
254 }
255 
256 int
main(int argc,char ** argv)257 main(int argc, char **argv)
258 {
259 	int optc;
260 
261 	while ((optc = getopt(argc, argv, "df")) != EOF) {
262 		switch (optc) {
263 		case 'd':
264 			test_set_debug();
265 			break;
266 		case 'f':
267 			test_set_force();
268 			break;
269 		default:
270 			(void) fprintf(stderr, "Usage: %s [-df]\n", argv[0]);
271 			exit(1);
272 		}
273 	}
274 
275 	test_nl_langinfo();
276 
277 	exit(0);
278 }
279