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