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