1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1989-2011 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <glenn.s.fowler@gmail.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21
22 /*
23 * locale command
24 */
25
26 static const char usage[] =
27 "[-?\n@(#)$Id: locale (AT&T Research) 2009-10-21 $\n]"
28 USAGE_LICENSE
29 "[+NAME?locale - get locale-specific information]"
30 "[+DESCRIPTION?\blocale\b writes information about the current locale to"
31 " the standard output. If no options or operands are specified then the"
32 " environment for each locale category is summarized. If operands are"
33 " spcified then information is written about each operand: a locale"
34 " category name operand (\fcategories\f) selects all keywords in that"
35 " category; other operands name keywords within a category. Keyword"
36 " names are unique across all categories, so there is no ambiguity."
37 " \acategory=value\a operands set the corresponding locale \acategory\a"
38 " to \avalue\a by a call to \bsetlocale\b(3). A \avalue\a of \b-\b is"
39 " interpreted as \bNULL\b, allowing \acategory\a to be queried. The"
40 " \bTEST\b category converts the \avalue\a locale name to a canonical"
41 " form and lists the converted name on the standard output.]"
42 "[+?If the \b--all\b or \b--undefined\b options are specified and no operands"
43 " are specified then the names of all defined locales are listed on the"
44 " standard output. A \adefined\a locale name should work as an argument"
45 " to \bsetlocale\b(3). An \aundefined\a name is valid but not supported"
46 " on the local system. Locale names have the general form"
47 " \alanguage\a_\aterritory\a.\acharset\a@\aattribute\a[,\aattribute\a]]*"
48 " At least one of \alanguage\a or \aterritory\a is always specified, the"
49 " other parts are optional. The \b--abbreviated\b, \b--qualified\b,"
50 " \b--verbose\b and \b--local\b options determine the locale name"
51 " listing style. The default is \b--abbreviated\b. If multiple styles"
52 " are specified then the names are listed in columns in this order:"
53 " \babbreviated\b, \bqualified\b, \bverbose\b and \blocal\b.]"
54
55 "[a:all?List all defined locale names on the standard output.]"
56 "[b:abbreviated?List abbreviated locale names: \alanguage\a and \aterritory\a"
57 " as the two character ISO codes; non-default \acharset\a and"
58 " \aattributes\a. This is the default.]"
59 "[c:category?List the category names for each operand on the standard output.]"
60 "[e:element?The operands are interpreted as collation elements. Each element"
61 " name is listed on the standard output followed by a \btab\b character"
62 " and the space separated list of \bstrxfrm\b(3) collation weights.]"
63 "[i:indent?Indent keyword output lines for readability.]"
64 "[k:keyword?List the keyword name for each operand on the standard output.]"
65 "[l:local?List the locale names returned by the local system \bsetlocale\b(3)."
66 " NOTE: these names may contain embedded space.]"
67 "[m:charmaps?List the names of available charmaps.]"
68 "[q:qualified?List qualified locale names: \alanguage\a and \aterritory\a"
69 " as the two character ISO codes; default and non-default \acharset\a and"
70 " \aattributes\a.]"
71 "[t:composite?List the composite value of LC_ALL on the standard output.]"
72 "[u:undefined?List all undefined locale names on the standard output.]"
73 "[v:verbose?List verbose locale names: \alanguage\a and \aterritory\a"
74 " as the long English strings; non-default \acharset\a and"
75 " \aattributes\a.]"
76 "[x:transform?The operands are interpreted as strings. Each string is listed"
77 " on the standard output followed by a \btab\b character and the"
78 " space separated list of \bstrxfrm\b(3) collation weights.]"
79
80 "\n"
81 "\n[ name | name=value ... ]\n"
82 "\n"
83
84 "[+SEE ALSO?\blocaleconv\b(3), \bnl_langinfo\b(3), \bsetlocale\b(3)]"
85 ;
86
87 #include <ast.h>
88 #include <cdt.h>
89 #include <ctype.h>
90 #include <error.h>
91 #include <lc.h>
92 #include <tm.h>
93 #include <regex.h>
94 #include <proc.h>
95
96 #include "FEATURE/locales"
97
98 #define TEST (AST_LC_COUNT+1)
99
100 #if _hdr_nl_types && _hdr_langinfo
101
102 #include <nl_types.h>
103 #include <langinfo.h>
104
105 #else
106
107 #undef _hdr_nl_types
108 #undef _hdr_langinfo
109
110 #endif
111
112 typedef struct Keyword_s
113 {
114 const char* name;
115 int index;
116 int type;
117 int elements;
118 long offset;
119 Dtlink_t link;
120 } Keyword_t;
121
122 #define LC_category (LC_user<<1)
123 #define LC_indent (LC_user<<2)
124 #define LC_keyword (LC_user<<3)
125 #define LC_proper (LC_user<<4)
126 #define LC_quote (LC_user<<5)
127 #define LC_recursive (LC_user<<6)
128 #define LC_upper (LC_user<<7)
129
130 #define C 1
131 #define I 4
132 #define N 3
133 #define S 0
134 #define X 5
135
136 #define CV_collate 1
137
138 #define CV_charset 1
139 #define CV_mb_cur_max 2
140 #define CV_mb_cur_min 3
141
142 #ifdef NOEXPR
143 #define CV_noexpr NOEXPR
144 #else
145 #define CV_noexpr (-1)
146 #endif
147 #ifdef NOSTR
148 #define CV_nostr NOSTR
149 #else
150 #define CV_nostr (-1)
151 #endif
152 #ifdef YESEXPR
153 #define CV_yesexpr YESEXPR
154 #else
155 #define CV_yesexpr (-1)
156 #endif
157 #ifdef YESSTR
158 #define CV_yesstr YESSTR
159 #else
160 #define CV_yesstr (-1)
161 #endif
162
163 #if _mem_credit_sign_lconv
164 #define CV_credit_sign offsetof(struct lconv,credit_sign)
165 #else
166 #define CV_credit_sign (-1)
167 #endif
168 #if _mem_currency_symbol_lconv
169 #define CV_currency_symbol offsetof(struct lconv,currency_symbol)
170 #else
171 #define CV_currency_symbol (-1)
172 #endif
173 #if _mem_debit_sign_lconv
174 #define CV_debit_sign offsetof(struct lconv,debit_sign)
175 #else
176 #define CV_debit_sign (-1)
177 #endif
178 #if _mem_frac_digits_lconv
179 #define CV_frac_digits offsetof(struct lconv,frac_digits)
180 #else
181 #define CV_frac_digits (-1)
182 #endif
183 #if _mem_int_curr_symbol_lconv
184 #define CV_int_curr_symbol offsetof(struct lconv,int_curr_symbol)
185 #else
186 #define CV_int_curr_symbol (-1)
187 #endif
188 #if _mem_int_frac_digits_lconv
189 #define CV_int_frac_digits offsetof(struct lconv,int_frac_digits)
190 #else
191 #define CV_int_frac_digits (-1)
192 #endif
193 #if _mem_left_parenthesis_lconv
194 #define CV_left_parenthesis offsetof(struct lconv,left_parenthesis)
195 #else
196 #define CV_left_parenthesis (-1)
197 #endif
198 #if _mem_mon_decimal_point_lconv
199 #define CV_mon_decimal_point offsetof(struct lconv,mon_decimal_point)
200 #else
201 #define CV_mon_decimal_point (-1)
202 #endif
203 #if _mem_mon_grouping_lconv
204 #define CV_mon_grouping offsetof(struct lconv,mon_grouping)
205 #else
206 #define CV_mon_grouping (-1)
207 #endif
208 #if _mem_mon_thousands_sep_lconv
209 #define CV_mon_thousands_sep offsetof(struct lconv,mon_thousands_sep)
210 #else
211 #define CV_mon_thousands_sep (-1)
212 #endif
213 #if _mem_n_cs_precedes_lconv
214 #define CV_n_cs_precedes offsetof(struct lconv,n_cs_precedes)
215 #else
216 #define CV_n_cs_precedes (-1)
217 #endif
218 #if _mem_n_sep_by_space_lconv
219 #define CV_n_sep_by_space offsetof(struct lconv,n_sep_by_space)
220 #else
221 #define CV_n_sep_by_space (-1)
222 #endif
223 #if _mem_n_sign_posn_lconv
224 #define CV_n_sign_posn offsetof(struct lconv,n_sign_posn)
225 #else
226 #define CV_n_sign_posn (-1)
227 #endif
228 #if _mem_negative_sign_lconv
229 #define CV_negative_sign offsetof(struct lconv,negative_sign)
230 #else
231 #define CV_negative_sign (-1)
232 #endif
233 #if _mem_p_cs_precedes_lconv
234 #define CV_p_cs_precedes offsetof(struct lconv,p_cs_precedes)
235 #else
236 #define CV_p_cs_precedes (-1)
237 #endif
238 #if _mem_p_sep_by_space_lconv
239 #define CV_p_sep_by_space offsetof(struct lconv,p_sep_by_space)
240 #else
241 #define CV_p_sep_by_space (-1)
242 #endif
243 #if _mem_p_sign_posn_lconv
244 #define CV_p_sign_posn offsetof(struct lconv,p_sign_posn)
245 #else
246 #define CV_p_sign_posn (-1)
247 #endif
248 #if _mem_positive_sign_lconv
249 #define CV_positive_sign offsetof(struct lconv,positive_sign)
250 #else
251 #define CV_positive_sign (-1)
252 #endif
253 #if _mem_right_parenthesis_lconv
254 #define CV_right_parenthesis offsetof(struct lconv,right_parenthesis)
255 #else
256 #define CV_right_parenthesis (-1)
257 #endif
258
259 #if _mem_decimal_point_lconv
260 #define CV_decimal_point offsetof(struct lconv,decimal_point)
261 #else
262 #define CV_decimal_point (-1)
263 #endif
264 #if _mem_grouping_lconv
265 #define CV_grouping offsetof(struct lconv,grouping)
266 #else
267 #define CV_grouping (-1)
268 #endif
269 #if _mem_thousands_sep_lconv
270 #define CV_thousands_sep offsetof(struct lconv,thousands_sep)
271 #else
272 #define CV_thousands_sep (-1)
273 #endif
274
275 #if _num__NL_ADDRESS_POSTAL_FMT
276 #define CV_postal_fmt _NL_ADDRESS_POSTAL_FMT
277 #else
278 #define CV_postal_fmt (-1)
279 #endif
280 #if _num__NL_ADDRESS_COUNTRY_NAME
281 #define CV_country_name _NL_ADDRESS_COUNTRY_NAME
282 #else
283 #define CV_country_name (-1)
284 #endif
285 #if _num__NL_ADDRESS_COUNTRY_POST
286 #define CV_country_post _NL_ADDRESS_COUNTRY_POST
287 #else
288 #define CV_country_post (-1)
289 #endif
290 #if _num__NL_ADDRESS_COUNTRY_AB2
291 #define CV_country_ab2 _NL_ADDRESS_COUNTRY_AB2
292 #else
293 #define CV_country_ab2 (-1)
294 #endif
295 #if _num__NL_ADDRESS_COUNTRY_AB3
296 #define CV_country_ab3 _NL_ADDRESS_COUNTRY_AB3
297 #else
298 #define CV_country_ab3 (-1)
299 #endif
300 #if _num__NL_ADDRESS_COUNTRY_NUM
301 #define CV_country_num _NL_ADDRESS_COUNTRY_NUM
302 #else
303 #define CV_country_num (-1)
304 #endif
305 #if _num__NL_ADDRESS_COUNTRY_CAR
306 #define CV_country_car _NL_ADDRESS_COUNTRY_CAR
307 #else
308 #define CV_country_car (-1)
309 #endif
310 #if _num__NL_ADDRESS_COUNTRY_ISBN
311 #define CV_country_isbn _NL_ADDRESS_COUNTRY_ISBN
312 #else
313 #define CV_country_isbn (-1)
314 #endif
315 #if _num__NL_ADDRESS_LANG_NAME
316 #define CV_lang_name _NL_ADDRESS_LANG_NAME
317 #else
318 #define CV_lang_name (-1)
319 #endif
320 #if _num__NL_ADDRESS_LANG_AB
321 #define CV_lang_ab _NL_ADDRESS_LANG_AB
322 #else
323 #define CV_lang_ab (-1)
324 #endif
325 #if _num__NL_ADDRESS_LANG_TERM
326 #define CV_lang_term _NL_ADDRESS_LANG_TERM
327 #else
328 #define CV_lang_term (-1)
329 #endif
330 #if _num__NL_ADDRESS_LANG_LIB
331 #define CV_lang_lib _NL_ADDRESS_LANG_LIB
332 #else
333 #define CV_lang_lib (-1)
334 #endif
335
336 #if _num__NL_IDENTIFICATION_TITLE
337 #define CV_title _NL_IDENTIFICATION_TITLE
338 #else
339 #define CV_title (-1)
340 #endif
341 #if _num__NL_IDENTIFICATION_SOURCE
342 #define CV_source _NL_IDENTIFICATION_SOURCE
343 #else
344 #define CV_source (-1)
345 #endif
346 #if _num__NL_IDENTIFICATION_ADDRESS
347 #define CV_address _NL_IDENTIFICATION_ADDRESS
348 #else
349 #define CV_address (-1)
350 #endif
351 #if _num__NL_IDENTIFICATION_CONTACT
352 #define CV_contact _NL_IDENTIFICATION_CONTACT
353 #else
354 #define CV_contact (-1)
355 #endif
356 #if _num__NL_IDENTIFICATION_EMAIL
357 #define CV_email _NL_IDENTIFICATION_EMAIL
358 #else
359 #define CV_email (-1)
360 #endif
361 #if _num__NL_IDENTIFICATION_TEL
362 #define CV_tel _NL_IDENTIFICATION_TEL
363 #else
364 #define CV_tel (-1)
365 #endif
366 #if _num__NL_IDENTIFICATION_FAX
367 #define CV_fax _NL_IDENTIFICATION_FAX
368 #else
369 #define CV_fax (-1)
370 #endif
371 #if _num__NL_IDENTIFICATION_LANGUAGE
372 #define CV_language _NL_IDENTIFICATION_LANGUAGE
373 #define T_language N
374 #else
375 #define CV_language (-2)
376 #define T_language X
377 #endif
378 #if _num__NL_IDENTIFICATION_TERRITORY
379 #define CV_territory _NL_IDENTIFICATION_TERRITORY
380 #define T_territory N
381 #else
382 #define CV_territory (-3)
383 #define T_territory X
384 #endif
385 #if _num__NL_IDENTIFICATION_ATTRIBUTES
386 #define CV_attributes _NL_IDENTIFICATION_ATTRIBUTES
387 #define T_attributes N
388 #else
389 #define CV_attributes (-4)
390 #define T_attributes X
391 #endif
392 #if _num__NL_IDENTIFICATION_REVISION
393 #define CV_revision _NL_IDENTIFICATION_REVISION
394 #else
395 #define CV_revision (-1)
396 #endif
397 #if _num__NL_IDENTIFICATION_DATE
398 #define CV_date _NL_IDENTIFICATION_DATE
399 #else
400 #define CV_date (-1)
401 #endif
402
403 #if _num__NL_MEASUREMENT_MEASUREMENT
404 #define CV_measurement _NL_MEASUREMENT_MEASUREMENT
405 #else
406 #define CV_measurement (-1)
407 #endif
408
409 #if _num__NL_NAME_NAME_FMT
410 #define CV_name_fmt _NL_NAME_NAME_FMT
411 #else
412 #define CV_name_fmt (-1)
413 #endif
414 #if _num__NL_NAME_NAME_MISS
415 #define CV_name_miss _NL_NAME_NAME_MISS
416 #else
417 #define CV_name_miss (-1)
418 #endif
419 #if _num__NL_NAME_NAME_MR
420 #define CV_name_mr _NL_NAME_NAME_MR
421 #else
422 #define CV_name_mr (-1)
423 #endif
424 #if _num__NL_NAME_NAME_MRS
425 #define CV_name_mrs _NL_NAME_NAME_MRS
426 #else
427 #define CV_name_mrs (-1)
428 #endif
429 #if _num__NL_NAME_NAME_MS
430 #define CV_name_ms _NL_NAME_NAME_MS
431 #else
432 #define CV_name_ms (-1)
433 #endif
434
435 #if _num__NL_PAPER_HEIGHT
436 #define CV_height _NL_PAPER_HEIGHT
437 #else
438 #define CV_height (-1)
439 #endif
440 #if _num__NL_PAPER_WIDTH
441 #define CV_width _NL_PAPER_WIDTH
442 #else
443 #define CV_width (-1)
444 #endif
445
446 #if _num__NL_TELEPHONE_TEL_INT_FMT
447 #define CV_tel_int_fmt _NL_TELEPHONE_TEL_INT_FMT
448 #else
449 #define CV_tel_int_fmt (-1)
450 #endif
451 #if _num__NL_TELEPHONE_TEL_DOM_FMT
452 #define CV_tel_dom_fmt _NL_TELEPHONE_TEL_DOM_FMT
453 #else
454 #define CV_tel_dom_fmt (-1)
455 #endif
456 #if _num__NL_TELEPHONE_INT_SELECT
457 #define CV_int_select _NL_TELEPHONE_INT_SELECT
458 #else
459 #define CV_int_select (-1)
460 #endif
461 #if _num__NL_TELEPHONE_INT_PREFIX
462 #define CV_int_prefix _NL_TELEPHONE_INT_PREFIX
463 #else
464 #define CV_int_prefix (-1)
465 #endif
466
467 #ifndef MB_CUR_MIN
468 #define MB_CUR_MIN 1
469 #endif
470
471 static const char defer[] = "/usr/bin/locale";
472
473 static Keyword_t keywords[] =
474 {
475 {"TEST", TEST, S,1,0},
476 {"postal_fmt", AST_LC_ADDRESS, N,1,CV_postal_fmt},
477 {"country_name", AST_LC_ADDRESS, N,1,CV_country_name},
478 {"country_post", AST_LC_ADDRESS, N,1,CV_country_post},
479 {"country_ab2", AST_LC_ADDRESS, N,1,CV_country_ab2},
480 {"country_ab3", AST_LC_ADDRESS, N,1,CV_country_ab3},
481 {"country_num", AST_LC_ADDRESS, N,1,CV_country_num},
482 {"country_car", AST_LC_ADDRESS, N,1,CV_country_car},
483 {"country_isbn", AST_LC_ADDRESS, N,1,CV_country_isbn},
484 {"lang_name", AST_LC_ADDRESS, N,1,CV_lang_name},
485 {"lang_ab", AST_LC_ADDRESS, N,1,CV_lang_ab},
486 {"lang_term", AST_LC_ADDRESS, N,1,CV_lang_term},
487 {"lang_lib", AST_LC_ADDRESS, N,1,CV_lang_lib},
488 {"collate", AST_LC_COLLATE, S,1,CV_collate},
489 {"charset", AST_LC_CTYPE, S,1,CV_charset},
490 {"mb_cur_max", AST_LC_CTYPE, I,1,CV_mb_cur_max},
491 {"mb_cur_min", AST_LC_CTYPE, I,1,CV_mb_cur_min},
492 {"title", AST_LC_IDENTIFICATION,N,1,CV_title},
493 {"source", AST_LC_IDENTIFICATION,N,1,CV_source},
494 {"address", AST_LC_IDENTIFICATION,N,1,CV_address},
495 {"contact", AST_LC_IDENTIFICATION,N,1,CV_contact},
496 {"email", AST_LC_IDENTIFICATION,N,1,CV_email},
497 {"tel", AST_LC_IDENTIFICATION,N,1,CV_tel},
498 {"fax", AST_LC_IDENTIFICATION,N,1,CV_fax},
499 {"language", AST_LC_IDENTIFICATION,T_language,1,CV_language},
500 {"territory", AST_LC_IDENTIFICATION,T_territory,1,CV_territory},
501 {"attributes", AST_LC_IDENTIFICATION,T_attributes,1,CV_attributes},
502 {"revision", AST_LC_IDENTIFICATION,N,1,CV_revision},
503 {"date", AST_LC_IDENTIFICATION,N,1,CV_date},
504 {"measurement", AST_LC_MEASUREMENT,N,1,CV_measurement},
505 {"yesexpr", AST_LC_MESSAGES, N,1,CV_yesexpr},
506 {"noexpr", AST_LC_MESSAGES, N,1,CV_noexpr},
507 {"yesstr", AST_LC_MESSAGES, N,1,CV_yesstr},
508 {"nostr", AST_LC_MESSAGES, N,1,CV_nostr},
509 {"credit_sign", AST_LC_MONETARY, S,1,CV_credit_sign},
510 {"currency_symbol", AST_LC_MONETARY, S,1,CV_currency_symbol},
511 {"debit_sign", AST_LC_MONETARY, S,1,CV_debit_sign},
512 {"frac_digits", AST_LC_MONETARY, C,1,CV_frac_digits},
513 {"int_curr_symbol", AST_LC_MONETARY, S,1,CV_int_curr_symbol},
514 {"int_frac_digits", AST_LC_MONETARY, C,1,CV_int_frac_digits},
515 {"left_parenthesis", AST_LC_MONETARY, S,1,CV_left_parenthesis},
516 {"mon_decimal_point", AST_LC_MONETARY, S,1,CV_mon_decimal_point},
517 {"mon_grouping", AST_LC_MONETARY, S,1,CV_mon_grouping},
518 {"mon_thousands_sep", AST_LC_MONETARY, S,1,CV_mon_thousands_sep},
519 {"n_cs_precedes", AST_LC_MONETARY, C,1,CV_n_cs_precedes},
520 {"n_sep_by_space", AST_LC_MONETARY, C,1,CV_n_sep_by_space},
521 {"n_sign_posn", AST_LC_MONETARY, C,1,CV_n_sign_posn},
522 {"negative_sign", AST_LC_MONETARY, S,1,CV_negative_sign},
523 {"p_cs_precedes", AST_LC_MONETARY, C,1,CV_p_cs_precedes},
524 {"p_sep_by_space", AST_LC_MONETARY, C,1,CV_p_sep_by_space},
525 {"p_sign_posn", AST_LC_MONETARY, C,1,CV_p_sign_posn},
526 {"positive_sign", AST_LC_MONETARY, S,1,CV_positive_sign},
527 {"right_parenthesis", AST_LC_MONETARY, S,1,CV_right_parenthesis},
528 {"name_fmt", AST_LC_NAME, N,1,CV_name_fmt},
529 {"name_miss", AST_LC_NAME, N,1,CV_name_miss},
530 {"name_mr", AST_LC_NAME, N,1,CV_name_mr},
531 {"name_mrs", AST_LC_NAME, N,1,CV_name_mrs},
532 {"name_ms", AST_LC_NAME, N,1,CV_name_ms},
533 {"decimal_point", AST_LC_NUMERIC, S,1,CV_decimal_point},
534 {"grouping", AST_LC_NUMERIC, S,1,CV_grouping},
535 {"thousands_sep", AST_LC_NUMERIC, S,1,CV_thousands_sep},
536 {"height", AST_LC_PAPER, N,1,CV_height},
537 {"width", AST_LC_PAPER, N,1,CV_width},
538 {"tel_int_fmt", AST_LC_TELEPHONE, N,1,CV_tel_int_fmt},
539 {"tel_dom_fmt", AST_LC_TELEPHONE, N,1,CV_tel_dom_fmt},
540 {"int_select", AST_LC_TELEPHONE, N,1,CV_int_select},
541 {"int_prefix", AST_LC_TELEPHONE, N,1,CV_int_prefix},
542 {"abday", AST_LC_TIME, S,7,TM_DAY_ABBREV*sizeof(char*)},
543 {"abmon", AST_LC_TIME, S,12,TM_MONTH_ABBREV*sizeof(char*)},
544 {"alt_digits", AST_LC_TIME, S,10,TM_DIGITS*sizeof(char*)},
545 {"am_pm", AST_LC_TIME, S,2,TM_MERIDIAN*sizeof(char*)},
546 {"d_fmt", AST_LC_TIME, S,1,TM_DATE*sizeof(char*)},
547 {"d_t_fmt", AST_LC_TIME, S,1,TM_DEFAULT*sizeof(char*)},
548 {"day", AST_LC_TIME, S,7,TM_DAY*sizeof(char*)},
549 {"era", AST_LC_TIME, S,1,TM_ERA*sizeof(char*)},
550 {"era_d_fmt", AST_LC_TIME, S,1,TM_ERA_DATE*sizeof(char*)},
551 {"era_d_t_fmt", AST_LC_TIME, S,1,TM_ERA_DEFAULT*sizeof(char*)},
552 {"era_t_fmt", AST_LC_TIME, S,1,TM_ERA_TIME*sizeof(char*)},
553 {"era_year", AST_LC_TIME, S,1,TM_ERA_YEAR*sizeof(char*)},
554 {"m_d_old", AST_LC_TIME, S,1,TM_DISTANT*sizeof(char*)},
555 {"m_d_recent", AST_LC_TIME, S,1,TM_RECENT*sizeof(char*)},
556 {"mon", AST_LC_TIME, S,12,TM_MONTH*sizeof(char*)},
557 {"t_fmt", AST_LC_TIME, S,1,TM_TIME*sizeof(char*)},
558 {"t_fmt_ampm", AST_LC_TIME, S,1,TM_MERIDIAN_TIME*sizeof(char*)},
559 };
560
561 static struct State_s
562 {
563 Lc_category_t* categories;
564 struct lconv* conv;
565 Dt_t* dict;
566 Dtdisc_t disc;
567 int all;
568 int output;
569 int sep;
570 } state;
571
572 /*
573 * list the locale name(s) for lc according to flags
574 */
575
576 static void
list_locale(Sfio_t * sp,Keyword_t * key,Lc_t * lc,unsigned int flags)577 list_locale(Sfio_t* sp, Keyword_t* key, Lc_t* lc, unsigned int flags)
578 {
579 register int i;
580 int n;
581 char* fmt;
582 char* sep;
583 char buf[256];
584
585 static unsigned long types[] = { LC_abbreviated, LC_qualified, LC_verbose, LC_local };
586
587 n = 0;
588 for (i = 0; i < elementsof(types); i++)
589 if (flags & types[i])
590 n++;
591 if (!n)
592 {
593 n++;
594 flags |= LC_abbreviated;
595 }
596 n = n == 1;
597 if (key)
598 {
599 if (flags & LC_indent)
600 {
601 sfputc(sp, '\t');
602 n = 0;
603 }
604 if (flags & (LC_category|LC_keyword))
605 {
606 sfprintf(sp, "%s=", key->name);
607 n = 0;
608 }
609 }
610 fmt = n ? "%s%s" : "%s\"%s\"";
611 sep = "";
612 for (i = 0; i < elementsof(types); i++)
613 if (flags & types[i])
614 {
615 lccanon(lc, types[i], buf, sizeof(buf));
616 if (n && streq(buf, "-"))
617 return;
618 sfprintf(sp, fmt, sep, buf);
619 sep = ";";
620 }
621 sfputc(sp, '\n');
622 }
623
624 /*
625 * print the numeric value i for key
626 */
627
628 static void
number(Sfio_t * sp,register Keyword_t * key,int i,unsigned int flags)629 number(Sfio_t* sp, register Keyword_t* key, int i, unsigned int flags)
630 {
631 state.output = 1;
632 if (flags & LC_indent)
633 sfputc(sp, '\t');
634 if (flags & LC_keyword)
635 sfprintf(sp, "%s=%d\n", key->name, i);
636 else
637 sfprintf(sp, "%d\n", i);
638 }
639
640 /*
641 * print one string value, possibly quoted and converted to upper case
642 */
643
644 static void
value(Sfio_t * sp,register const char * s,register unsigned int flags)645 value(Sfio_t* sp, register const char* s, register unsigned int flags)
646 {
647 register int c;
648 register int u;
649
650 state.output = 1;
651 if ((flags & (LC_quote|LC_proper|LC_upper)) == LC_quote)
652 {
653 sfprintf(sp, "%s", fmtquote(s, "\"", "\"", strlen(s), FMT_ALWAYS));
654 return;
655 }
656 if (flags & LC_quote)
657 sfputc(sp, '"');
658 if (flags & LC_upper)
659 while (c = *s++)
660 {
661 if (islower(c))
662 c = toupper(c);
663 sfputc(sp, c);
664 }
665 else if (flags & LC_proper)
666 {
667 u = 1;
668 while (c = *s++)
669 {
670 if (!isalnum(c))
671 u = 1;
672 else
673 {
674 if (u && islower(c))
675 c = toupper(c);
676 u = 0;
677 }
678 sfputc(sp, c);
679 }
680 }
681 else
682 sfprintf(sp, "%s", s);
683 if (flags & LC_quote)
684 sfputc(sp, '"');
685 }
686
687 /*
688 * print the string value(s) v[n] for key
689 */
690
691 static void
string(Sfio_t * sp,register Keyword_t * key,char ** v,int n,unsigned int flags)692 string(Sfio_t* sp, register Keyword_t* key, char** v, int n, unsigned int flags)
693 {
694 char** e;
695 register const Lc_attribute_list_t* a;
696
697 if (flags & LC_indent)
698 sfputc(sp, '\t');
699 if (flags & LC_keyword)
700 sfprintf(sp, "%s=", key->name);
701 if ((flags & LC_keyword) || n != 1)
702 flags |= LC_quote;
703 if (n)
704 {
705 value(sp, *v, flags);
706 flags |= LC_quote;
707 e = v + n - 1;
708 while (v++ < e)
709 {
710 sfputc(sp, ';');
711 value(sp, *v, flags);
712 }
713 }
714 else if (v && (a = *((Lc_attribute_list_t**)v)))
715 {
716 value(sp, a->attribute->name, flags);
717 while (a = a->next)
718 {
719 sfputc(sp, ';');
720 value(sp, a->attribute->name, flags);
721 }
722 }
723 sfputc(sp, '\n');
724 }
725
726 /*
727 * extact and list info for key with base info at data
728 */
729
730 static void
extract(Sfio_t * sp,register Keyword_t * key,void * data,unsigned int flags)731 extract(Sfio_t* sp, register Keyword_t* key, void* data, unsigned int flags)
732 {
733 register int i;
734 char* s;
735 char** v;
736 Lc_t* lc;
737
738 switch (key->type)
739 {
740 case C:
741 if (key->offset >= 0)
742 i = *((char*)data + key->offset);
743 else
744 i = CHAR_MAX;
745 if (i == CHAR_MAX)
746 i = -1;
747 number(sp, key, i, flags);
748 break;
749 case I:
750 if (key->offset >= 0)
751 i = *(int*)((char*)data + key->offset);
752 else
753 i = -1;
754 number(sp, key, i, flags);
755 break;
756 case N:
757 #if _lib_nl_langinfo
758 if (key->offset >= 0)
759 {
760 s = nl_langinfo(key->offset);
761
762 #if _num__NL_PAPER_HEIGHT
763
764 /*
765 * redhat decided to change the nl_langinfo()
766 * return value after umpteen years of stability
767 * to optionally return an int for some numeric
768 * values -- botch botch botch
769 */
770
771 if (((unsigned int)s) < 8196)
772 {
773 static char xxx[32];
774
775 sfsprintf(xxx, sizeof(xxx), "%d", (unsigned int)s);
776 s = xxx;
777 }
778 #endif
779 }
780 else
781 #endif
782 s = "";
783 string(sp, key, &s, 1, flags);
784 break;
785 case S:
786 if (key->offset >= 0)
787 {
788 v = (char**)((char*)data + key->offset);
789 i = key->elements;
790 }
791 else
792 {
793 s = "";
794 v = &s;
795 i = 1;
796 }
797 string(sp, key, v, i, flags);
798 break;
799 case X:
800 lc = (Lc_t*)lcinfo(state.categories[key->index].external)->lc;
801 i = 1;
802 if (key->offset == CV_language && lc->language)
803 {
804 s = (char*)lc->language->name;
805 flags |= LC_proper;
806 }
807 else if (key->offset == CV_territory && lc->territory)
808 {
809 s = (char*)lc->territory->name;
810 flags |= LC_proper;
811 }
812 else if (key->offset == CV_attributes && lc->attributes)
813 {
814 s = (char*)lc->attributes;
815 i = 0;
816 }
817 else
818 s = "";
819 string(sp, key, &s, i, flags);
820 break;
821 }
822 }
823
824 /*
825 * list LC_ALL info
826 */
827
828 static void
list_all(Sfio_t * sp,register Lc_t * lc,unsigned long flags)829 list_all(Sfio_t* sp, register Lc_t* lc, unsigned long flags)
830 {
831 if (!lc)
832 lc = (Lc_t*)lcinfo(LC_CTYPE)->lc;
833 if (!state.sep)
834 state.sep = 1;
835 else
836 sfputc(sp, '\n');
837 if (flags & LC_category)
838 sfprintf(sp, "LC_ALL\n");
839 if (flags & LC_indent)
840 sfputc(sp, '\t');
841 if (flags & LC_keyword)
842 {
843 flags |= LC_quote;
844 sfprintf(sp, "locale=");
845 }
846 value(sp, lc->name, flags);
847 sfputc(sp, '\n');
848 }
849
850 static int scan(Sfio_t*, Keyword_t*, unsigned long);
851
852 /*
853 * list info for key
854 */
855
856 static void
list_keyword(Sfio_t * sp,register Keyword_t * key,char * value,unsigned int flags)857 list_keyword(Sfio_t* sp, register Keyword_t* key, char* value, unsigned int flags)
858 {
859 register int i;
860 register int j;
861 register int n;
862 register unsigned int f;
863 char* s;
864
865 if ((flags & LC_category) && key->index != AST_LC_ALL)
866 sfprintf(sp, "%s\n", state.categories[key->index].name);
867 switch (key->index)
868 {
869 case AST_LC_COLLATE:
870 s = mbcoll() ? "strcoll" : "strcmp";
871 string(sp, key, &s, 1, flags);
872 break;
873 case AST_LC_CTYPE:
874 switch (key->offset)
875 {
876 case CV_charset:
877 s = (char*)lcinfo(LC_CTYPE)->lc->charset->code;
878 string(sp, key, &s, 1, flags|LC_upper);
879 break;
880 case CV_mb_cur_max:
881 number(sp, key, ast.mb_cur_max, flags);
882 break;
883 case CV_mb_cur_min:
884 number(sp, key, MB_CUR_MIN, flags);
885 break;
886 }
887 break;
888 case AST_LC_MONETARY:
889 case AST_LC_NUMERIC:
890 if (!state.conv)
891 state.conv = localeconv();
892 extract(sp, key, state.conv, flags);
893 break;
894 case AST_LC_ADDRESS:
895 case AST_LC_IDENTIFICATION:
896 case AST_LC_MEASUREMENT:
897 case AST_LC_MESSAGES:
898 case AST_LC_NAME:
899 case AST_LC_PAPER:
900 case AST_LC_TELEPHONE:
901 case AST_LC_TIME:
902 extract(sp, key, tmlocale(), flags);
903 break;
904 case AST_LC_ALL:
905 if (value)
906 {
907 if (streq(value, "-"))
908 value = 0;
909 if (!setlocale(key->offset, value))
910 error(1, "%s: invalid locale", value);
911 state.conv = 0;
912 }
913 else
914 {
915 if (key->type == AST_LC_ALL)
916 {
917 state.all = 1;
918 if ((flags & (LC_defined|LC_recursive)) == LC_defined)
919 {
920 scan(sp, key, flags|LC_recursive);
921 break;
922 }
923 i = 1;
924 n = AST_LC_COUNT - 1;
925 }
926 else
927 i = n = key->type;
928 if (state.all)
929 list_all(sp, NiL, flags);
930 for (; i <= n; i++)
931 {
932 f = flags;
933 for (j = 0; j < elementsof(keywords); j++)
934 if (keywords[j].index == i)
935 {
936 list_keyword(sp, &keywords[j], NiL, f);
937 f &= ~LC_category;
938 }
939 }
940 }
941 break;
942 case TEST:
943 state.output = 1;
944 if (!value)
945 error(2, "%s: value expected", key->name);
946 else
947 {
948 if (streq(value, "-"))
949 value = 0;
950 list_locale(sfstdout, key, lcmake(value), flags);
951 }
952 break;
953 }
954 }
955
956 /*
957 * scan all locales matching flags
958 */
959
960 static int
scan(Sfio_t * sp,Keyword_t * key,unsigned long flags)961 scan(Sfio_t* sp, Keyword_t* key, unsigned long flags)
962 {
963 register Lc_t* lc = 0;
964
965 while (lc = lcscan(lc))
966 {
967 switch (flags & (LC_defined|LC_undefined))
968 {
969 case LC_defined:
970 if (!lc->index && !setlocale(LC_MONETARY, lc->name))
971 continue;
972 break;
973 case LC_undefined:
974 if (lc->index || setlocale(LC_MONETARY, lc->name))
975 continue;
976 break;
977 }
978 if (!key)
979 list_locale(sp, NiL, lc, flags);
980 else if (setlocale(LC_ALL, lc->name))
981 list_keyword(sp, key, NiL, flags&~LC_quote);
982 }
983 return 0;
984 }
985
986 int
main(int argc,char ** argv)987 main(int argc, char** argv)
988 {
989 register char* name;
990 register char* s;
991 register int i;
992 register unsigned int flags;
993 int collate;
994 int composite;
995 int transform;
996 int j;
997 char* value;
998 char** oargv;
999 Keyword_t* key;
1000 char buf[64];
1001 char col[1024];
1002 char dip[64];
1003
1004 error_info.id = "locale";
1005 oargv = argv;
1006 flags = 0;
1007 collate = 0;
1008 composite = 0;
1009 transform = 0;
1010 for (;;)
1011 {
1012 switch (optget(argv, usage))
1013 {
1014 case 'a':
1015 flags |= LC_defined;
1016 continue;
1017 case 'b':
1018 flags |= LC_abbreviated;
1019 continue;
1020 case 'c':
1021 flags |= LC_category;
1022 continue;
1023 case 'e':
1024 collate = 1;
1025 continue;
1026 case 'i':
1027 flags |= LC_indent;
1028 continue;
1029 case 'k':
1030 flags |= LC_keyword;
1031 continue;
1032 case 'l':
1033 flags |= LC_local;
1034 continue;
1035 case 'm':
1036 return execv(defer, argv);
1037 case 'q':
1038 flags |= LC_qualified;
1039 continue;
1040 case 't':
1041 composite = 1;
1042 continue;
1043 case 'u':
1044 flags |= LC_undefined;
1045 continue;
1046 case 'v':
1047 flags |= LC_verbose;
1048 continue;
1049 case 'x':
1050 transform = 1;
1051 continue;
1052 case '?':
1053 error(ERROR_USAGE|4, "%s", opt_info.arg);
1054 break;
1055 case ':':
1056 error(2, "%s", opt_info.arg);
1057 break;
1058 }
1059 break;
1060 }
1061 if (error_info.errors)
1062 error(ERROR_USAGE|4, "%s", optusage(NiL));
1063 argv += opt_info.index;
1064 if (collate)
1065 {
1066 while (name = *argv++)
1067 {
1068 sfprintf(sfstdout, "%s", name);
1069 sfsprintf(col, sizeof(col), ".%s.]", name);
1070 if ((i = regcollate(col, NiL, buf, sizeof(buf), NiL)) < 0)
1071 {
1072 sfprintf(sfstdout, "\tERROR\n");
1073 continue;
1074 }
1075 if (!(collate = mbxfrm(col, buf, sizeof(col))))
1076 {
1077 if (i > 1)
1078 {
1079 sfprintf(sfstdout, "\tINVALID\n");
1080 continue;
1081 }
1082 collate = i;
1083 memcpy(col, buf, i);
1084 }
1085 else if (i > 1)
1086 {
1087 buf[1] = 0;
1088 if (mbxfrm(dip, buf, sizeof(dip)) < collate)
1089 {
1090 sfprintf(sfstdout, "\tUNDEFINED\n");
1091 continue;
1092 }
1093 }
1094 for (i = 0, j = '\t'; i < collate; i++, j = ' ')
1095 sfprintf(sfstdout, "%c%02x", j, ((unsigned char*)col)[i]);
1096 sfputc(sfstdout, '\n');
1097 }
1098 return error_info.errors != 0;
1099 }
1100 if (composite)
1101 {
1102 sfprintf(sfstdout, "%s\n", setlocale(LC_ALL, NiL));
1103 return error_info.errors != 0;
1104 }
1105 if (transform)
1106 {
1107 while (name = *argv++)
1108 {
1109 sfprintf(sfstdout, "%s", name);
1110 collate = mbxfrm(col, name, sizeof(col));
1111 for (i = 0, j = '\t'; i < collate; i++, j = ' ')
1112 sfprintf(sfstdout, "%c%02x", j, ((unsigned char*)col)[i]);
1113 sfputc(sfstdout, '\n');
1114 }
1115 return error_info.errors != 0;
1116 }
1117 state.categories = lccategories();
1118 if (!*argv)
1119 {
1120 if (flags & (LC_undefined|LC_defined))
1121 {
1122 if (!(flags & (LC_abbreviated|LC_qualified|LC_local|LC_verbose)))
1123 flags |= LC_abbreviated;
1124 return scan(sfstdout, NiL, flags);
1125 }
1126 if (!flags)
1127 {
1128 name = "LANG";
1129 sfprintf(sfstdout, "%s=", name);
1130 if (!(s = getenv("LANG")))
1131 s = "POSIX";
1132 sfprintf(sfstdout, "%s\n", fmtquote(s, "'", NiL, strlen(s), 0));
1133 value = getenv(state.categories[AST_LC_ALL].name);
1134 for (i = 1; i < AST_LC_COUNT; i++)
1135 {
1136 s = setlocale(state.categories[i].external, NiL);
1137 sfprintf(sfstdout, "%s=%s\n", state.categories[i].name, fmtquote(s, "\"", "\"", strlen(s), (value || !getenv(state.categories[i].name)) ? FMT_ALWAYS : 0));
1138 }
1139 sfprintf(sfstdout, "%s=", state.categories[0].name);
1140 if (value)
1141 {
1142 s = setlocale(state.categories[0].external, NiL);
1143 sfprintf(sfstdout, "%s", fmtquote(s, "\"", "\"", strlen(s), 0));
1144 }
1145 sfputc(sfstdout, '\n');
1146 return 0;
1147 }
1148 }
1149 state.disc.key = offsetof(Keyword_t, name);
1150 state.disc.size = -1;
1151 state.disc.link = offsetof(Keyword_t, link);
1152 if (!(state.dict = dtopen(&state.disc, Dtoset)))
1153 error(3, "out of space [dictionary]");
1154 for (i = 0; i < elementsof(keywords); i++)
1155 dtinsert(state.dict, keywords + i);
1156 for (i = 0; i < AST_LC_COUNT; i++)
1157 {
1158 if (!(key = newof(0, Keyword_t, 1, 0)))
1159 error(3, "out of space [keyword]");
1160 key->name = state.categories[i].name;
1161 key->index = AST_LC_ALL;
1162 key->type = state.categories[i].internal;
1163 key->offset = state.categories[i].external;
1164 dtinsert(state.dict, key);
1165 }
1166 while (name = *argv++)
1167 {
1168 if (value = strchr(name, '='))
1169 *value++ = 0;
1170 if (!(key = (Keyword_t*)dtmatch(state.dict, name)))
1171 {
1172 if (name[0] == 'L' && name[1] == 'C' && name[2] == '_')
1173 error(2, "%s: unknown category", name);
1174 else if (oargv[0][0] != '/')
1175 {
1176 char* cmd[3];
1177
1178 cmd[0] = (char*)defer;
1179 cmd[1] = name;
1180 cmd[2] = 0;
1181 procrun(defer, cmd, 0);
1182 state.output = 1;
1183 }
1184 else
1185 error(2, "%s: unknown keyword", name);
1186 }
1187 else
1188 list_keyword(sfstdout, key, value, flags);
1189 }
1190 if (!error_info.errors && !state.output)
1191 list_keyword(sfstdout, (Keyword_t*)dtmatch(state.dict, state.categories[0].name), NiL, flags);
1192 return error_info.errors != 0;
1193 }
1194