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 }