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 }