1#include <iostream> 2#include <sstream> 3#include <map> 4#include <set> 5#include <vector> 6#include <algorithm> 7 8#include <unistd.h> 9#include <langinfo.h> 10#include <locale.h> 11#include <xlocale.h> 12#include <sys/types.h> 13#include <dirent.h> 14 15#define LAST(array) array + (sizeof(array) / sizeof(*array)) 16 17#define LC_SPECIAL (LC_COLLATE+LC_CTYPE+LC_MESSAGES+LC_MONETARY+LC_NUMERIC+LC_TIME) 18 19using namespace std; 20 21enum vtype { 22 V_STR, V_NUM 23}; 24 25template<typename T> string tostr(T val) { 26 ostringstream ss; 27 ss << val; 28 return ss.str(); 29} 30 31string quote(string s) { 32 if (s.length() == 0) { 33 return ""; 34 } 35 36 return '"' + s + '"'; 37} 38 39class keyword { 40 public: 41 virtual string get_category() const { return category; } 42 virtual string get_keyword() const { return kword; } 43 virtual string get_value(bool show_quotes) const { 44 return (show_quotes && t == V_STR) ? quote(value) : value; } 45 46 virtual ~keyword() { } 47 protected: 48 keyword(int category_, string kword, string value, vtype t) 49 : kword(kword), value(value), t(t) { 50 switch(category_) { 51 case LC_COLLATE: 52 category = "LC_COLLATE"; 53 break; 54 case LC_CTYPE: 55 category = "LC_CTYPE"; 56 break; 57 case LC_MESSAGES: 58 category = "LC_MESSAGES"; 59 break; 60 case LC_MONETARY: 61 category = "LC_MONETARY"; 62 break; 63 case LC_NUMERIC: 64 category = "LC_NUMERIC"; 65 break; 66 case LC_TIME: 67 category = "LC_TIME"; 68 break; 69 case LC_SPECIAL: 70 category = "LC_SPECIAL"; 71 break; 72 default: 73 { 74 ostringstream lc; 75 lc << "LC_" << category_; 76 category = lc.str(); 77 } 78 break; 79 } 80 } 81 82 string category, kword, value; 83 vtype t; 84}; 85 86struct keyword_cmp { 87 bool operator()(const keyword *a, const keyword *b) const { 88 return a->get_category() < b->get_category(); 89 } 90}; 91 92class li_keyword : public keyword { 93 public: 94 li_keyword(int category, string kword, int itemnum, vtype t = V_STR) 95 : keyword(category, kword, nl_langinfo(itemnum), t) { } 96}; 97 98class lia_keyword : public keyword { 99 protected: 100 vector<string> values; 101 public: 102 virtual string get_value(bool show_quotes) const { 103 ostringstream ss; 104 vector<string>::const_iterator s(values.begin()), e(values.end()), i(s); 105 106 for(; i < e; ++i) { 107 if (i != s) { 108 ss << ';'; 109 } 110 if (show_quotes && t == V_STR) { 111 ss << quote(*i); 112 } else { 113 ss << *i; 114 } 115 } 116 117 return ss.str(); 118 } 119 120 lia_keyword(int category, string kword, int *s, int *e, vtype t = V_STR) 121 : keyword(category, kword, "", t) { 122 for(; s < e; ++s) { 123 values.push_back(nl_langinfo(*s)); 124 } 125 } 126}; 127 128class lc_keyword : public keyword { 129 public: 130 lc_keyword(int category, string kword, string value, vtype t = V_STR) 131 : keyword(category, kword, value, t) { } 132}; 133 134void usage(char *argv0) { 135 clog << "usage: " << argv0 << "[-a|-m]\n or: " 136 << argv0 << " [-cCk] name..." << endl; 137} 138 139void list_all_valid_locales() { 140 string locale_dir("/usr/share/locale"); 141 bool found_C = false, found_POSIX = false; 142 DIR *d = opendir(locale_dir.c_str()); 143 struct dirent *de; 144 static string expected[] = { "LC_COLLATE", "LC_CTYPE", "LC_MESSAGES", 145 "LC_NUMERIC", "LC_TIME" }; 146 147 for(de = readdir(d); de; de = readdir(d)) { 148 string lname(de->d_name, de->d_namlen); 149 string ldir(locale_dir + "/" + lname); 150 int cnt = 0; 151 DIR *ld = opendir(ldir.c_str()); 152 if (ld) { 153 struct dirent *lde; 154 for(lde = readdir(ld); lde; lde = readdir(ld)) { 155 string fname(lde->d_name, lde->d_namlen); 156 if (LAST(expected) != find(expected, LAST(expected), fname)) { 157 cnt++; 158 } 159 } 160 closedir(ld); 161 162 if (cnt == LAST(expected) - expected) { 163 cout << lname << endl; 164 if (lname == "C") { 165 found_C = true; 166 } 167 if (lname == "POSIX") { 168 found_POSIX = true; 169 } 170 } 171 } 172 } 173 closedir(d); 174 if (!found_C) { 175 cout << "C" << endl; 176 } 177 if (!found_POSIX) { 178 cout << "POSIX" << endl; 179 } 180} 181 182void show_all_unique_codesets() { 183 string locale_dir("/usr/share/locale"); 184 DIR *d = opendir(locale_dir.c_str()); 185 struct dirent *de; 186 static string expected[] = { "LC_COLLATE", "LC_CTYPE", "LC_MESSAGES", 187 "LC_NUMERIC", "LC_TIME" }; 188 set<string> codesets; 189 for(de = readdir(d); de; de = readdir(d)) { 190 string lname(de->d_name, de->d_namlen); 191 string ldir(locale_dir + "/" + lname); 192 int cnt = 0; 193 DIR *ld = opendir(ldir.c_str()); 194 if (ld) { 195 struct dirent *lde; 196 for(lde = readdir(ld); lde; lde = readdir(ld)) { 197 string fname(lde->d_name, lde->d_namlen); 198 if (LAST(expected) != find(expected, LAST(expected), fname)) { 199 cnt++; 200 } 201 } 202 closedir(ld); 203 204 if (cnt == LAST(expected) - expected) { 205 locale_t xloc = newlocale(LC_ALL_MASK, lname.c_str(), NULL); 206 if (xloc) { 207 char *cs = nl_langinfo_l(CODESET, xloc); 208 if (cs && *cs && (codesets.find(cs) == codesets.end())) { 209 cout << cs << endl; 210 codesets.insert(cs); 211 } 212 freelocale(xloc); 213 } 214 } 215 } 216 } 217 closedir(d); 218} 219 220typedef map<string, keyword *> keywords_t; 221keywords_t keywords; 222 223typedef map<string, vector<keyword *> > catorgies_t; 224catorgies_t catoriges; 225 226void add_kw(keyword *k) { 227 keywords.insert(make_pair(k->get_keyword(), k)); 228 catorgies_t::iterator c = catoriges.find(k->get_category()); 229 if (c != catoriges.end()) { 230 c->second.push_back(k); 231 } else { 232 vector<keyword *> v; 233 v.push_back(k); 234 catoriges.insert(make_pair(k->get_category(), v)); 235 } 236} 237 238string grouping(char *g) { 239 ostringstream ss; 240 if (*g == 0) { 241 ss << "0"; 242 } else { 243 ss << static_cast<int>(*g); 244 while(*++g) { 245 ss << ";" << static_cast<int>(*g); 246 } 247 } 248 return ss.str(); 249} 250 251void init_keywords() { 252 struct lconv *lc = localeconv(); 253 if (lc) { 254 add_kw(new lc_keyword(LC_NUMERIC, "decimal_point", lc->decimal_point)); 255 add_kw(new lc_keyword(LC_NUMERIC, "thousands_sep", lc->thousands_sep)); 256 add_kw(new lc_keyword(LC_NUMERIC, "grouping", grouping(lc->grouping))); 257 add_kw(new lc_keyword(LC_MONETARY, "int_curr_symbol", lc->int_curr_symbol)); 258 add_kw(new lc_keyword(LC_MONETARY, "currency_symbol", lc->currency_symbol)); 259 add_kw(new lc_keyword(LC_MONETARY, "mon_decimal_point", lc->mon_decimal_point)); 260 add_kw(new lc_keyword(LC_MONETARY, "mon_thousands_sep", lc->mon_thousands_sep)); 261 add_kw(new lc_keyword(LC_MONETARY, "mon_grouping", grouping(lc->mon_grouping))); 262 add_kw(new lc_keyword(LC_MONETARY, "positive_sign", lc->positive_sign)); 263 add_kw(new lc_keyword(LC_MONETARY, "negative_sign", lc->negative_sign)); 264 add_kw(new lc_keyword(LC_MONETARY, "int_frac_digits", tostr((int)lc->int_frac_digits), V_NUM)); 265 add_kw(new lc_keyword(LC_MONETARY, "frac_digits", tostr((int)lc->frac_digits), V_NUM)); 266 add_kw(new lc_keyword(LC_MONETARY, "p_cs_precedes", tostr((int)lc->p_cs_precedes), V_NUM)); 267 add_kw(new lc_keyword(LC_MONETARY, "p_sep_by_space", tostr((int)lc->p_sep_by_space), V_NUM)); 268 add_kw(new lc_keyword(LC_MONETARY, "n_cs_precedes", tostr((int)lc->n_cs_precedes), V_NUM)); 269 add_kw(new lc_keyword(LC_MONETARY, "n_sep_by_space", tostr((int)lc->n_sep_by_space), V_NUM)); 270 add_kw(new lc_keyword(LC_MONETARY, "p_sign_posn", tostr((int)lc->p_sign_posn), V_NUM)); 271 add_kw(new lc_keyword(LC_MONETARY, "n_sign_posn", tostr((int)lc->n_sign_posn), V_NUM)); 272 add_kw(new lc_keyword(LC_MONETARY, "int_p_cs_precedes", tostr((int)lc->int_p_cs_precedes), V_NUM)); 273 add_kw(new lc_keyword(LC_MONETARY, "int_n_cs_precedes", tostr((int)lc->int_n_cs_precedes), V_NUM)); 274 add_kw(new lc_keyword(LC_MONETARY, "int_p_sep_by_space", tostr((int)lc->int_p_sep_by_space), V_NUM)); 275 add_kw(new lc_keyword(LC_MONETARY, "int_n_sep_by_space", tostr((int)lc->int_n_sep_by_space), V_NUM)); 276 add_kw(new lc_keyword(LC_MONETARY, "int_p_sign_posn", tostr((int)lc->int_p_sign_posn), V_NUM)); 277 add_kw(new lc_keyword(LC_MONETARY, "int_n_sign_posn", tostr((int)lc->int_n_sign_posn), V_NUM)); 278 } 279 280 int abdays[] = {ABDAY_1, ABDAY_2, ABDAY_3, ABDAY_4, ABDAY_5, ABDAY_6, ABDAY_7}; 281 add_kw(new lia_keyword(LC_TIME, "ab_day", abdays, LAST(abdays))); 282 add_kw(new lia_keyword(LC_TIME, "abday", abdays, LAST(abdays))); 283 284 int days[] = {DAY_1, DAY_2, DAY_3, DAY_4, DAY_5, DAY_6, DAY_7}; 285 add_kw(new lia_keyword(LC_TIME, "day", days, LAST(days))); 286 287 int abmons[] = {ABMON_1, ABMON_2, ABMON_3, ABMON_4, ABMON_5, ABMON_6, ABMON_7, ABMON_8, ABMON_9, ABMON_10, ABMON_11, ABMON_12}; 288 add_kw(new lia_keyword(LC_TIME, "abmon", abmons, LAST(abmons))); 289 290 int mons[] = {MON_1, MON_2, MON_3, MON_4, MON_5, MON_6, MON_7, MON_8, MON_9, MON_10, MON_11, MON_12}; 291 add_kw(new lia_keyword(LC_TIME, "mon", mons, LAST(mons))); 292 293 int am_pms[] = {AM_STR, PM_STR}; 294 add_kw(new lia_keyword(LC_TIME, "am_pm", am_pms, LAST(am_pms))); 295 296 add_kw(new li_keyword(LC_TIME, "t_fmt_ampm", T_FMT_AMPM)); 297 add_kw(new li_keyword(LC_TIME, "era", ERA)); 298 add_kw(new li_keyword(LC_TIME, "era_d_fmt", ERA_D_FMT)); 299 add_kw(new li_keyword(LC_TIME, "era_t_fmt", ERA_T_FMT)); 300 add_kw(new li_keyword(LC_TIME, "era_d_t_fmt", ERA_D_T_FMT)); 301 add_kw(new li_keyword(LC_TIME, "alt_digits", ALT_DIGITS)); 302 303 add_kw(new li_keyword(LC_TIME, "d_t_fmt", D_T_FMT)); 304 add_kw(new li_keyword(LC_TIME, "d_fmt", D_FMT)); 305 add_kw(new li_keyword(LC_TIME, "t_fmt", T_FMT)); 306 307 add_kw(new li_keyword(LC_MESSAGES, "yesexpr", YESEXPR)); 308 add_kw(new li_keyword(LC_MESSAGES, "noexpr", NOEXPR)); 309 add_kw(new li_keyword(LC_MESSAGES, "yesstr", YESSTR)); 310 add_kw(new li_keyword(LC_MESSAGES, "nostr", NOSTR)); 311 312 add_kw(new li_keyword(LC_CTYPE, "charmap", CODESET)); 313 add_kw(new lc_keyword(LC_SPECIAL, "categories", "LC_COLLATE LC_CTYPE LC_MESSAGES LC_MONETARY LC_NUMERIC LC_TIME")); 314 315 // add_kw: CRNCYSTR D_MD_ORDER CODESET RADIXCHAR THOUSEP 316} 317 318void show_keyword(string &last_cat, bool sw_categories, bool sw_keywords, 319 keyword *k) { 320 if (sw_categories && last_cat != k->get_category()) { 321 last_cat = k->get_category(); 322 cout << last_cat << endl; 323 } 324 if (sw_keywords) { 325 cout << k->get_keyword() << "="; 326 } 327 cout << k->get_value(sw_keywords) << endl; 328} 329 330int main(int argc, char *argv[]) { 331 int sw; 332 bool sw_all_locales = false, sw_categories = false, sw_keywords = false, 333 sw_charmaps = false; 334 335 while(-1 != (sw = getopt(argc, argv, "ackm"))) { 336 switch(sw) { 337 case 'a': 338 sw_all_locales = true; 339 break; 340 case 'c': 341 sw_categories = true; 342 break; 343 case 'k': 344 sw_keywords = true; 345 break; 346 case 'm': 347 sw_charmaps = true; 348 break; 349 default: 350 usage(argv[0]); 351 exit(1); 352 } 353 } 354 355 if ((sw_all_locales && sw_charmaps) 356 || ((sw_all_locales || sw_charmaps) && (sw_keywords || sw_categories)) 357 ) { 358 usage(argv[0]); 359 exit(1); 360 } 361 362 setlocale(LC_ALL, ""); 363 364 if (!(sw_all_locales || sw_categories || sw_keywords || sw_charmaps) 365 && argc == optind) { 366 char *lang = getenv("LANG"); 367 cout << "LANG=" << quote(lang ? lang : "") << endl; 368 cout << "LC_COLLATE=" << quote(setlocale(LC_COLLATE, NULL)) << endl; 369 cout << "LC_CTYPE=" << quote(setlocale(LC_CTYPE, NULL)) << endl; 370 cout << "LC_MESSAGES=" << quote(setlocale(LC_MESSAGES, NULL)) << endl; 371 cout << "LC_MONETARY=" << quote(setlocale(LC_MONETARY, NULL)) << endl; 372 cout << "LC_NUMERIC=" << quote(setlocale(LC_NUMERIC, NULL)) << endl; 373 cout << "LC_TIME=" << quote(setlocale(LC_TIME, NULL)) << endl; 374 if (getenv("LC_ALL")) { 375 cout << "LC_ALL=" << quote(setlocale(LC_ALL, NULL)) << endl; 376 } else { 377 cout << "LC_ALL=" << endl; 378 } 379 380 return 0; 381 } 382 383 if (sw_all_locales) { 384 list_all_valid_locales(); 385 return 0; 386 } 387 388 if (sw_charmaps) { 389 show_all_unique_codesets(); 390 return 0; 391 } 392 393 init_keywords(); 394 string last_cat(""); 395 int exit_val = 0; 396 for(int i = optind; i < argc; ++i) { 397 keywords_t::iterator ki = keywords.find(argv[i]); 398 if (ki != keywords.end()) { 399 show_keyword(last_cat, sw_categories, sw_keywords, ki->second); 400 } else { 401 catorgies_t::iterator ci = catoriges.find(argv[i]); 402 if (ci != catoriges.end()) { 403 vector<keyword *>::iterator vi(ci->second.begin()), 404 ve(ci->second.end()); 405 for(; vi != ve; ++vi) { 406 show_keyword(last_cat, sw_categories, sw_keywords, *vi); 407 } 408 } else if (argv[i] == string("LC_ALL")) { 409 ki = keywords.begin(); 410 keywords_t::iterator ke = keywords.end(); 411 for(; ki != ke; ++ki) { 412 show_keyword(last_cat, sw_categories, sw_keywords, ki->second); 413 } 414 } else { 415 if (argv[i] == string("LC_CTYPE") 416 || argv[i] == string("LC_COLLATE")) { 417 // It would be nice to print a warning, 418 // but we aren't allowed (locale.ex test#14) 419 if (sw_categories) { 420 cout << argv[i] << endl; 421 } 422 } else { 423 clog << "unknown keyword " 424 << argv[i] << endl; 425 exit_val = 1; 426 } 427 } 428 } 429 } 430 431 return exit_val; 432} 433