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