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 %I:%M:%S %p %Z" },
  80         { D_FMT,        "%m/%e/%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])?))" },
 102         { NOEXPR,       "^(([nN]([oO])?))" },
 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 %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])?))" },
 132         { NOEXPR,       "^(([nN]([oO])?))" },
 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])?))" },
 162         { NOEXPR,       "^(([нН]([еЕ][тТ])?)|([nN]([oO])?))" },
 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
 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
 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
 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
 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 }