Mercurial > illumos > onarm
comparison usr/src/cmd/locale/locale.c @ 0:c9caec207d52 b86
Initial porting based on b86
author | Koji Uno <koji.uno@sun.com> |
---|---|
date | Tue, 02 Jun 2009 18:56:50 +0900 |
parents | |
children | 1a15d5aaf794 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c9caec207d52 |
---|---|
1 /* | |
2 * CDDL HEADER START | |
3 * | |
4 * The contents of this file are subject to the terms of the | |
5 * Common Development and Distribution License, Version 1.0 only | |
6 * (the "License"). You may not use this file except in compliance | |
7 * with the License. | |
8 * | |
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | |
10 * or http://www.opensolaris.org/os/licensing. | |
11 * See the License for the specific language governing permissions | |
12 * and limitations under the License. | |
13 * | |
14 * When distributing Covered Code, include this CDDL HEADER in each | |
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. | |
16 * If applicable, add the following below this CDDL HEADER, with the | |
17 * fields enclosed by brackets "[]" replaced with your own identifying | |
18 * information: Portions Copyright [yyyy] [name of copyright owner] | |
19 * | |
20 * CDDL HEADER END | |
21 */ | |
22 /* | |
23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. | |
24 * Use is subject to license terms. | |
25 */ | |
26 | |
27 /* | |
28 * locale -- get current locale information | |
29 * | |
30 * Copyright 1991, 1993 by Mortice Kern Systems Inc. All rights reserved. | |
31 * | |
32 */ | |
33 | |
34 #pragma ident "@(#)locale.c 1.13 05/06/08 SMI" | |
35 | |
36 /* | |
37 * locale: get locale-specific information | |
38 * usage: locale [-a|-m] | |
39 * locale [-ck] name ... | |
40 */ | |
41 | |
42 /* | |
43 * New members added in the struct lconv by IEEE Std 1003.1-2001 | |
44 * are always activated in the locale object. | |
45 * See <iso/locale_iso.h>. | |
46 */ | |
47 #define _LCONV_C99 | |
48 | |
49 #include <stdio.h> | |
50 #include <stdlib.h> | |
51 #include <limits.h> | |
52 #include <string.h> | |
53 #include <dirent.h> | |
54 #include <ctype.h> | |
55 #include <stddef.h> | |
56 #include <nl_types.h> | |
57 #include <langinfo.h> | |
58 #include <locale.h> | |
59 #include <sys/types.h> | |
60 #include <sys/stat.h> | |
61 | |
62 #define LC_LOCDEF 999 /* Random number! */ | |
63 | |
64 #define LOCALE_DIR "/usr/lib/locale/" | |
65 #define CHARMAP_DIR "/usr/lib/localedef/src/" | |
66 #define CHARMAP_NAME "charmap.src" | |
67 | |
68 #define GET_LOCALE 0 | |
69 #define GET_CHARMAP 1 | |
70 #define CSSIZE 128 | |
71 | |
72 #ifndef isblank | |
73 #define isblank(c) ((__ctype + 1)[c] & _B) | |
74 #endif | |
75 | |
76 enum types { | |
77 TYPE_STR, /* char * */ | |
78 TYPE_GROUP, /* char *, for mon_grouping, and grouping */ | |
79 TYPE_INT, /* int */ | |
80 TYPE_CHR, /* char, printed as signed integer */ | |
81 TYPE_PCHR, /* char, printed as printable character */ | |
82 TYPE_CTP, /* ctype entry */ | |
83 TYPE_CNVL, /* convert to lower */ | |
84 TYPE_CNVU, /* convert to upper */ | |
85 TYPE_COLLEL /* print the multi-character collating elements */ | |
86 }; | |
87 | |
88 static int print_locale_info(char *keyword, int cflag, int kflag); | |
89 static int print_category(int category, int cflag, int kflag); | |
90 static int print_keyword(char *name, int cflag, int kflag); | |
91 static void usage(void); | |
92 static void print_all_info(int); | |
93 static void print_cur_locale(void); | |
94 static void outstr(char *s); | |
95 static void outchar(int); | |
96 static void prt_ctp(char *); | |
97 static void prt_cnv(char *); | |
98 static void prt_collel(char *); | |
99 static char get_escapechar(void); | |
100 static char get_commentchar(void); | |
101 | |
102 static char *save_loc; | |
103 | |
104 /* | |
105 * yes/no is not in the localeconv structure for xpg style. | |
106 * We dummy up a new structure for purposes of the code below. | |
107 * If YESEXPR is available per XPG4, we use it. | |
108 * Otherwise, use YESSTR, the old method with less functionality from XPG3. | |
109 */ | |
110 struct yesno { | |
111 char *yes_expr; | |
112 char *no_expr; | |
113 char *yes_str; | |
114 char *no_str; | |
115 }; | |
116 | |
117 struct dtconv { | |
118 char *date_time_format; | |
119 char *date_format; | |
120 char *time_format; | |
121 char *time_format_ampm; | |
122 char *am_string; | |
123 char *pm_string; | |
124 char *abbrev_day_names[7]; | |
125 char *day_names[7]; | |
126 char *abbrev_month_names[12]; | |
127 char *month_names[12]; | |
128 char *era; | |
129 char *era_d_fmt; | |
130 char *era_d_t_fmt; | |
131 char *era_t_fmt; | |
132 char *alt_digits; | |
133 }; | |
134 | |
135 struct localedef { | |
136 char *charmap; | |
137 char *code_set_name; | |
138 char escape_char; | |
139 char comment_char; | |
140 int mb_cur_max; | |
141 int mb_cur_min; | |
142 }; | |
143 | |
144 static struct yesno * | |
145 getyesno(void) | |
146 { | |
147 static struct yesno yn; | |
148 static int loaded = 0; | |
149 | |
150 if (loaded) { | |
151 return (&yn); | |
152 /* NOTREACHED */ | |
153 } | |
154 | |
155 yn.yes_expr = strdup(nl_langinfo(YESEXPR)); | |
156 yn.no_expr = strdup(nl_langinfo(NOEXPR)); | |
157 yn.yes_str = strdup(nl_langinfo(YESSTR)); | |
158 yn.no_str = strdup(nl_langinfo(NOSTR)); | |
159 | |
160 loaded = 1; | |
161 return (&yn); | |
162 } | |
163 | |
164 static struct dtconv * | |
165 localedtconv(void) | |
166 { | |
167 static struct dtconv _dtconv; | |
168 static int loaded = 0; | |
169 | |
170 if (loaded) { | |
171 return (&_dtconv); | |
172 /* NOTREACHED */ | |
173 } | |
174 | |
175 _dtconv.date_time_format = strdup(nl_langinfo(D_T_FMT)); | |
176 _dtconv.date_format = strdup(nl_langinfo(D_FMT)); | |
177 _dtconv.time_format = strdup(nl_langinfo(T_FMT)); | |
178 _dtconv.time_format_ampm = strdup(nl_langinfo(T_FMT_AMPM)); | |
179 _dtconv.am_string = strdup(nl_langinfo(AM_STR)); | |
180 _dtconv.pm_string = strdup(nl_langinfo(PM_STR)); | |
181 _dtconv.abbrev_day_names[0] = strdup(nl_langinfo(ABDAY_1)); | |
182 _dtconv.abbrev_day_names[1] = strdup(nl_langinfo(ABDAY_2)); | |
183 _dtconv.abbrev_day_names[2] = strdup(nl_langinfo(ABDAY_3)); | |
184 _dtconv.abbrev_day_names[3] = strdup(nl_langinfo(ABDAY_4)); | |
185 _dtconv.abbrev_day_names[4] = strdup(nl_langinfo(ABDAY_5)); | |
186 _dtconv.abbrev_day_names[5] = strdup(nl_langinfo(ABDAY_6)); | |
187 _dtconv.abbrev_day_names[6] = strdup(nl_langinfo(ABDAY_7)); | |
188 _dtconv.day_names[0] = strdup(nl_langinfo(DAY_1)); | |
189 _dtconv.day_names[1] = strdup(nl_langinfo(DAY_2)); | |
190 _dtconv.day_names[2] = strdup(nl_langinfo(DAY_3)); | |
191 _dtconv.day_names[3] = strdup(nl_langinfo(DAY_4)); | |
192 _dtconv.day_names[4] = strdup(nl_langinfo(DAY_5)); | |
193 _dtconv.day_names[5] = strdup(nl_langinfo(DAY_6)); | |
194 _dtconv.day_names[6] = strdup(nl_langinfo(DAY_7)); | |
195 _dtconv.abbrev_month_names[0] = strdup(nl_langinfo(ABMON_1)); | |
196 _dtconv.abbrev_month_names[1] = strdup(nl_langinfo(ABMON_2)); | |
197 _dtconv.abbrev_month_names[2] = strdup(nl_langinfo(ABMON_3)); | |
198 _dtconv.abbrev_month_names[3] = strdup(nl_langinfo(ABMON_4)); | |
199 _dtconv.abbrev_month_names[4] = strdup(nl_langinfo(ABMON_5)); | |
200 _dtconv.abbrev_month_names[5] = strdup(nl_langinfo(ABMON_6)); | |
201 _dtconv.abbrev_month_names[6] = strdup(nl_langinfo(ABMON_7)); | |
202 _dtconv.abbrev_month_names[7] = strdup(nl_langinfo(ABMON_8)); | |
203 _dtconv.abbrev_month_names[8] = strdup(nl_langinfo(ABMON_9)); | |
204 _dtconv.abbrev_month_names[9] = strdup(nl_langinfo(ABMON_10)); | |
205 _dtconv.abbrev_month_names[10] = strdup(nl_langinfo(ABMON_11)); | |
206 _dtconv.abbrev_month_names[11] = strdup(nl_langinfo(ABMON_12)); | |
207 _dtconv.month_names[0] = strdup(nl_langinfo(MON_1)); | |
208 _dtconv.month_names[1] = strdup(nl_langinfo(MON_2)); | |
209 _dtconv.month_names[2] = strdup(nl_langinfo(MON_3)); | |
210 _dtconv.month_names[3] = strdup(nl_langinfo(MON_4)); | |
211 _dtconv.month_names[4] = strdup(nl_langinfo(MON_5)); | |
212 _dtconv.month_names[5] = strdup(nl_langinfo(MON_6)); | |
213 _dtconv.month_names[6] = strdup(nl_langinfo(MON_7)); | |
214 _dtconv.month_names[7] = strdup(nl_langinfo(MON_8)); | |
215 _dtconv.month_names[8] = strdup(nl_langinfo(MON_9)); | |
216 _dtconv.month_names[9] = strdup(nl_langinfo(MON_10)); | |
217 _dtconv.month_names[10] = strdup(nl_langinfo(MON_11)); | |
218 _dtconv.month_names[11] = strdup(nl_langinfo(MON_12)); | |
219 _dtconv.era = strdup(nl_langinfo(ERA)); | |
220 _dtconv.era_d_fmt = strdup(nl_langinfo(ERA_D_FMT)); | |
221 _dtconv.era_d_t_fmt = strdup(nl_langinfo(ERA_D_T_FMT)); | |
222 _dtconv.era_t_fmt = strdup(nl_langinfo(ERA_T_FMT)); | |
223 _dtconv.alt_digits = strdup(nl_langinfo(ALT_DIGITS)); | |
224 | |
225 loaded = 1; | |
226 return (&_dtconv); | |
227 } | |
228 | |
229 static struct localedef * | |
230 localeldconv(void) | |
231 { | |
232 static struct localedef _locdef; | |
233 static int loaded = 0; | |
234 | |
235 if (loaded) { | |
236 return (&_locdef); | |
237 /* NOTREACHED */ | |
238 } | |
239 | |
240 _locdef.charmap = strdup(nl_langinfo(CODESET)); | |
241 _locdef.code_set_name = strdup(nl_langinfo(CODESET)); | |
242 _locdef.mb_cur_max = MB_CUR_MAX; | |
243 _locdef.mb_cur_min = 1; | |
244 _locdef.escape_char = get_escapechar(); | |
245 _locdef.comment_char = get_commentchar(); | |
246 | |
247 loaded = 1; | |
248 return (&_locdef); | |
249 } | |
250 | |
251 /* | |
252 * The locale_name array also defines a canonical ordering for the categories. | |
253 * The function tocanon() translates the LC_* manifests to their canonical | |
254 * values. | |
255 */ | |
256 static struct locale_name { | |
257 char *name; | |
258 int category; | |
259 } locale_name[] = { | |
260 {"LC_CTYPE", LC_CTYPE}, | |
261 {"LC_NUMERIC", LC_NUMERIC}, | |
262 {"LC_TIME", LC_TIME}, | |
263 {"LC_COLLATE", LC_COLLATE}, | |
264 {"LC_MONETARY", LC_MONETARY}, | |
265 {"LC_MESSAGES", LC_MESSAGES}, | |
266 {"LC_ALL", LC_ALL}, | |
267 NULL | |
268 }; | |
269 | |
270 /* | |
271 * The structure key contains all keywords string name, | |
272 * symbolic name, category, and type (STR INT ...) | |
273 * the type will decide the way the value of the item be printed out | |
274 */ | |
275 static struct key { | |
276 char *name; | |
277 void *(*structure)(void); | |
278 int offset; | |
279 int count; | |
280 int category; | |
281 enum types type; | |
282 } key[] = { | |
283 | |
284 #define SPECIAL 0, 0, 0, | |
285 {"lower", SPECIAL LC_CTYPE, TYPE_CTP}, | |
286 {"upper", SPECIAL LC_CTYPE, TYPE_CTP}, | |
287 {"alpha", SPECIAL LC_CTYPE, TYPE_CTP}, | |
288 {"digit", SPECIAL LC_CTYPE, TYPE_CTP}, | |
289 {"space", SPECIAL LC_CTYPE, TYPE_CTP}, | |
290 {"cntrl", SPECIAL LC_CTYPE, TYPE_CTP}, | |
291 {"punct", SPECIAL LC_CTYPE, TYPE_CTP}, | |
292 {"graph", SPECIAL LC_CTYPE, TYPE_CTP}, | |
293 {"print", SPECIAL LC_CTYPE, TYPE_CTP}, | |
294 {"xdigit", SPECIAL LC_CTYPE, TYPE_CTP}, | |
295 {"blank", SPECIAL LC_CTYPE, TYPE_CTP}, | |
296 | |
297 {"tolower", SPECIAL LC_CTYPE, TYPE_CNVL}, | |
298 {"toupper", SPECIAL LC_CTYPE, TYPE_CNVU}, | |
299 | |
300 {"collating-element", 0, 0, 0, LC_COLLATE, TYPE_COLLEL}, | |
301 {"character-collation", 0, 1, 0, LC_COLLATE, TYPE_COLLEL}, | |
302 | |
303 #define dt(member, count) \ | |
304 (void *(*)(void))localedtconv, \ | |
305 offsetof(struct dtconv, member), \ | |
306 count, \ | |
307 LC_TIME, \ | |
308 TYPE_STR | |
309 {"d_t_fmt", dt(date_time_format, 1)}, | |
310 {"d_fmt", dt(date_format, 1)}, | |
311 {"t_fmt", dt(time_format, 1)}, | |
312 {"t_fmt_ampm", dt(time_format_ampm, 1)}, | |
313 {"am_pm", dt(am_string, 2)}, | |
314 {"day", dt(day_names, 7)}, | |
315 {"abday", dt(abbrev_day_names, 7)}, | |
316 {"mon", dt(month_names, 12)}, | |
317 {"abmon", dt(abbrev_month_names, 12)}, | |
318 {"era", dt(era, 1)}, | |
319 {"era_d_fmt", dt(era_d_fmt, 1)}, | |
320 {"era_d_t_fmt", dt(era_d_t_fmt, 1)}, | |
321 {"era_t_fmt", dt(era_t_fmt, 1)}, | |
322 {"alt_digits", dt(alt_digits, 1)}, | |
323 | |
324 #undef dt | |
325 | |
326 #define lc(member, locale, type) \ | |
327 (void *(*)(void))localeconv, \ | |
328 offsetof(struct lconv, member), \ | |
329 1, \ | |
330 locale, \ | |
331 type | |
332 {"decimal_point", lc(decimal_point, LC_NUMERIC, TYPE_STR) }, | |
333 {"thousands_sep", lc(thousands_sep, LC_NUMERIC, TYPE_STR) }, | |
334 {"grouping", lc(grouping, LC_NUMERIC, TYPE_GROUP)}, | |
335 {"int_curr_symbol", lc(int_curr_symbol, LC_MONETARY, TYPE_STR)}, | |
336 {"currency_symbol", lc(currency_symbol, LC_MONETARY, TYPE_STR)}, | |
337 {"mon_decimal_point", lc(mon_decimal_point, LC_MONETARY, TYPE_STR)}, | |
338 {"mon_thousands_sep", lc(mon_thousands_sep, LC_MONETARY, TYPE_STR)}, | |
339 {"mon_grouping", lc(mon_grouping, LC_MONETARY, TYPE_GROUP)}, | |
340 {"positive_sign", lc(positive_sign, LC_MONETARY, TYPE_STR)}, | |
341 {"negative_sign", lc(negative_sign, LC_MONETARY, TYPE_STR)}, | |
342 | |
343 {"int_frac_digits", lc(int_frac_digits, LC_MONETARY, TYPE_CHR)}, | |
344 {"frac_digits", lc(frac_digits, LC_MONETARY, TYPE_CHR)}, | |
345 {"p_cs_precedes", lc(p_cs_precedes, LC_MONETARY, TYPE_CHR)}, | |
346 {"p_sep_by_space", lc(p_sep_by_space, LC_MONETARY, TYPE_CHR)}, | |
347 {"n_cs_precedes", lc(n_cs_precedes, LC_MONETARY, TYPE_CHR)}, | |
348 {"n_sep_by_space", lc(n_sep_by_space, LC_MONETARY, TYPE_CHR)}, | |
349 {"p_sign_posn", lc(p_sign_posn, LC_MONETARY, TYPE_CHR)}, | |
350 {"n_sign_posn", lc(n_sign_posn, LC_MONETARY, TYPE_CHR)}, | |
351 {"int_p_cs_precedes", lc(int_p_cs_precedes, LC_MONETARY, TYPE_CHR)}, | |
352 {"int_p_sep_by_space", lc(int_p_sep_by_space, LC_MONETARY, TYPE_CHR)}, | |
353 {"int_n_cs_precedes", lc(int_n_cs_precedes, LC_MONETARY, TYPE_CHR)}, | |
354 {"int_n_sep_by_space", lc(int_n_sep_by_space, LC_MONETARY, TYPE_CHR)}, | |
355 {"int_p_sign_posn", lc(int_p_sign_posn, LC_MONETARY, TYPE_CHR)}, | |
356 {"int_n_sign_posn", lc(int_n_sign_posn, LC_MONETARY, TYPE_CHR)}, | |
357 | |
358 #undef lc | |
359 #define lc(member) \ | |
360 (void *(*)(void))getyesno, \ | |
361 offsetof(struct yesno, member), \ | |
362 1, \ | |
363 LC_MESSAGES, \ | |
364 TYPE_STR | |
365 {"yesexpr", lc(yes_expr)}, | |
366 {"noexpr", lc(no_expr)}, | |
367 {"yesstr", lc(yes_str)}, | |
368 {"nostr", lc(no_str)}, | |
369 #undef lc | |
370 | |
371 /* | |
372 * Following keywords have no official method of obtaining them | |
373 */ | |
374 #define ld(member, locale, type) \ | |
375 (void *(*)(void))localeldconv, \ | |
376 offsetof(struct localedef, member), \ | |
377 1, \ | |
378 locale, \ | |
379 type | |
380 {"charmap", ld(charmap, LC_LOCDEF, TYPE_STR)}, | |
381 {"code_set_name", ld(code_set_name, LC_LOCDEF, TYPE_STR)}, | |
382 {"escape_char", ld(escape_char, LC_LOCDEF, TYPE_PCHR)}, | |
383 {"comment_char", ld(comment_char, LC_LOCDEF, TYPE_PCHR)}, | |
384 {"mb_cur_max", ld(mb_cur_max, LC_LOCDEF, TYPE_INT)}, | |
385 {"mb_cur_min", ld(mb_cur_min, LC_LOCDEF, TYPE_INT)}, | |
386 #undef ld | |
387 | |
388 {NULL, NULL, 0, 0} | |
389 }; | |
390 | |
391 static char escapec; | |
392 | |
393 int | |
394 main(int argc, char **argv) | |
395 { | |
396 int c; | |
397 int retval = 0; | |
398 int cflag, kflag, aflag, mflag; | |
399 | |
400 (void) setlocale(LC_ALL, ""); | |
401 #if !defined(TEXT_DOMAIN) | |
402 #define TEXT_DOMAIN "SYS_TEST" | |
403 #endif | |
404 (void) textdomain(TEXT_DOMAIN); | |
405 | |
406 cflag = kflag = aflag = mflag = 0; | |
407 | |
408 while ((c = getopt(argc, argv, "amck")) != EOF) { | |
409 switch (c) { | |
410 case 'a': | |
411 aflag = 1; | |
412 break; | |
413 case 'm': | |
414 mflag = 1; | |
415 break; | |
416 case 'c': | |
417 cflag = 1; | |
418 break; | |
419 case 'k': | |
420 kflag = 1; | |
421 break; | |
422 default: | |
423 usage(); | |
424 /* NOTREACHED */ | |
425 break; | |
426 } | |
427 } | |
428 | |
429 /* -a OR -m OR (-c and/or -k) */ | |
430 if ((aflag && mflag) || ((aflag || mflag) && (cflag || kflag))) { | |
431 usage(); | |
432 /* NOTREACHED */ | |
433 } | |
434 | |
435 escapec = get_escapechar(); | |
436 | |
437 if (aflag) { | |
438 print_all_info(GET_LOCALE); | |
439 /* NOTREACHED */ | |
440 } | |
441 | |
442 if (mflag) { | |
443 print_all_info(GET_CHARMAP); | |
444 /* NOTREACHED */ | |
445 } | |
446 | |
447 if (optind == argc && !cflag && !kflag) { | |
448 print_cur_locale(); | |
449 /* NOTREACHED */ | |
450 } | |
451 if (optind == argc) { | |
452 usage(); | |
453 /* NOTREACHED */ | |
454 } | |
455 | |
456 for (; optind < argc; optind++) { | |
457 retval += print_locale_info(argv[optind], cflag, kflag); | |
458 } | |
459 return (retval); | |
460 } | |
461 | |
462 /* | |
463 * No options or operands. | |
464 * Print out the current locale names from the environment, or implied. | |
465 * Variables directly set in the environment are printed as-is, those | |
466 * implied are printed in quotes. | |
467 * The strings are printed ``appropriately quoted for possible later re-entry | |
468 * to the shell''. We use the routine outstr to do this -- however we | |
469 * want the shell escape character, the backslash, not the locale escape | |
470 * character, so we quietly save and restore the locale escape character. | |
471 */ | |
472 static void | |
473 print_cur_locale(void) | |
474 { | |
475 char *lc_allp; | |
476 char *env, *eff; | |
477 int i; | |
478 | |
479 if ((env = getenv("LANG")) != NULL) { | |
480 (void) printf("LANG=%s\n", env); | |
481 } else { | |
482 (void) printf("LANG=\n"); | |
483 } | |
484 | |
485 lc_allp = getenv("LC_ALL"); | |
486 | |
487 for (i = 0; i < LC_ALL; i++) { | |
488 (void) printf("%s=", locale_name[i].name); | |
489 eff = setlocale(i, NULL); | |
490 if (eff == NULL) { | |
491 eff = ""; | |
492 } | |
493 env = getenv(locale_name[i].name); | |
494 | |
495 if (env == NULL) { | |
496 (void) putchar('"'); | |
497 outstr(eff); | |
498 (void) putchar('"'); | |
499 } else { | |
500 if (strcmp(env, eff) != 0) { | |
501 (void) putchar('"'); | |
502 outstr(eff); | |
503 (void) putchar('"'); | |
504 } else { | |
505 outstr(eff); | |
506 } | |
507 } | |
508 (void) putchar('\n'); | |
509 } | |
510 | |
511 (void) printf("LC_ALL="); | |
512 if (lc_allp != NULL) { | |
513 outstr(lc_allp); | |
514 } | |
515 (void) putchar('\n'); | |
516 exit(0); | |
517 } | |
518 | |
519 static int num_of_loc = 0; | |
520 static int num_of_entries = 0; | |
521 static char **entries = NULL; | |
522 | |
523 static void | |
524 add_loc_entry(char *loc) | |
525 { | |
526 #define _INC_NUM 10 | |
527 char *s; | |
528 | |
529 if (num_of_loc >= num_of_entries) { | |
530 char **tmp; | |
531 num_of_entries += _INC_NUM; | |
532 tmp = realloc(entries, sizeof (char *) * num_of_entries); | |
533 if (tmp == NULL) { | |
534 /* restoring original locale */ | |
535 (void) setlocale(LC_ALL, save_loc); | |
536 (void) fprintf(stderr, | |
537 gettext("locale: cannot allocate buffer")); | |
538 exit(1); | |
539 } | |
540 entries = tmp; | |
541 } | |
542 s = strdup(loc); | |
543 if (s == NULL) { | |
544 /* restoring original locale */ | |
545 (void) setlocale(LC_ALL, save_loc); | |
546 (void) fprintf(stderr, | |
547 gettext("locale: cannot allocate buffer")); | |
548 exit(1); | |
549 } | |
550 entries[num_of_loc] = s; | |
551 | |
552 num_of_loc++; | |
553 } | |
554 | |
555 static int | |
556 loccmp(const char **str1, const char **str2) | |
557 { | |
558 return (strcmp(*str1, *str2)); | |
559 } | |
560 | |
561 static void | |
562 show_loc_entry(void) | |
563 { | |
564 int i; | |
565 | |
566 qsort(entries, num_of_loc, sizeof (char *), | |
567 (int (*)(const void *, const void *))loccmp); | |
568 for (i = 0; i < num_of_loc; i++) { | |
569 (void) printf("%s\n", entries[i]); | |
570 } | |
571 } | |
572 | |
573 static void | |
574 check_loc(char *loc) | |
575 { | |
576 int cat; | |
577 | |
578 /* first, try LC_ALL */ | |
579 if (setlocale(LC_ALL, loc) != NULL) { | |
580 /* succeeded */ | |
581 add_loc_entry(loc); | |
582 return; | |
583 } | |
584 | |
585 /* | |
586 * LC_ALL failed. | |
587 * try each category. | |
588 */ | |
589 for (cat = 0; cat <= _LastCategory; cat++) { | |
590 if (setlocale(cat, loc) != NULL) { | |
591 /* succeeded */ | |
592 add_loc_entry(loc); | |
593 return; | |
594 } | |
595 } | |
596 | |
597 /* loc is not a valid locale */ | |
598 } | |
599 | |
600 /* | |
601 * print_all_info(): Print out all the locales and | |
602 * charmaps supported by the system | |
603 */ | |
604 static void | |
605 print_all_info(int flag) | |
606 { | |
607 struct dirent *direntp; | |
608 DIR *dirp; | |
609 char *filename; /* filename[PATH_MAX] */ | |
610 char *p; | |
611 | |
612 if ((filename = malloc(PATH_MAX)) == NULL) { | |
613 (void) fprintf(stderr, | |
614 gettext("locale: cannot allocate buffer")); | |
615 exit(1); | |
616 } | |
617 | |
618 (void) memset(filename, 0, PATH_MAX); | |
619 | |
620 if (flag == GET_LOCALE) { | |
621 /* save the current locale */ | |
622 save_loc = setlocale(LC_ALL, NULL); | |
623 | |
624 (void) strcpy(filename, LOCALE_DIR); | |
625 add_loc_entry("POSIX"); | |
626 } else { /* CHARMAP */ | |
627 (void) strcpy(filename, CHARMAP_DIR); | |
628 } | |
629 | |
630 if ((dirp = opendir(filename)) == NULL) { | |
631 if (flag == GET_LOCALE) | |
632 exit(0); | |
633 else { /* CHARMAP */ | |
634 (void) fprintf(stderr, gettext( | |
635 "locale: charmap information not available.\n")); | |
636 exit(2); | |
637 } | |
638 } | |
639 | |
640 p = filename + strlen(filename); | |
641 while ((direntp = readdir(dirp)) != NULL) { | |
642 struct stat stbuf; | |
643 | |
644 (void) strcpy(p, direntp->d_name); | |
645 if (stat(filename, &stbuf) < 0) { | |
646 continue; | |
647 } | |
648 | |
649 if (flag == GET_LOCALE) { | |
650 if (S_ISDIR(stbuf.st_mode) && | |
651 (direntp->d_name[0] != '.') && | |
652 /* "POSIX" has already been printed out */ | |
653 strcmp(direntp->d_name, "POSIX") != 0) { | |
654 check_loc(direntp->d_name); | |
655 } | |
656 } else { /* CHARMAP */ | |
657 if (S_ISDIR(stbuf.st_mode) && | |
658 direntp->d_name[0] != '.') { | |
659 struct dirent *direntc; | |
660 DIR *dirc; | |
661 char *charmap; | |
662 char *c; | |
663 | |
664 if ((charmap = malloc(PATH_MAX)) == NULL) { | |
665 (void) fprintf(stderr, | |
666 gettext("locale: cannot allocate buffer")); | |
667 exit(1); | |
668 } | |
669 | |
670 (void) memset(charmap, 0, PATH_MAX); | |
671 | |
672 (void) strcpy(charmap, filename); | |
673 | |
674 if ((dirc = opendir(charmap)) == NULL) { | |
675 exit(0); | |
676 } | |
677 | |
678 c = charmap + strlen(charmap); | |
679 *c++ = '/'; | |
680 while ((direntc = readdir(dirc)) != NULL) { | |
681 struct stat stbuf; | |
682 | |
683 (void) strcpy(c, direntc->d_name); | |
684 if (stat(charmap, &stbuf) < 0) { | |
685 continue; | |
686 } | |
687 | |
688 if (S_ISREG(stbuf.st_mode) && | |
689 (strcmp(direntc->d_name, | |
690 CHARMAP_NAME) == 0) && | |
691 (direntc->d_name[0] != '.')) { | |
692 (void) printf("%s/%s\n", | |
693 p, direntc->d_name); | |
694 } | |
695 } | |
696 (void) closedir(dirc); | |
697 free(charmap); | |
698 } | |
699 } | |
700 } | |
701 if (flag == GET_LOCALE) { | |
702 /* restore the saved loc */ | |
703 (void) setlocale(LC_ALL, save_loc); | |
704 show_loc_entry(); | |
705 } | |
706 (void) closedir(dirp); | |
707 free(filename); | |
708 exit(0); | |
709 } | |
710 | |
711 /* | |
712 * Print out the keyword value or category info. | |
713 * Call print_category() to print the entire locale category, if the name | |
714 * given is recognized as a category. | |
715 * Otherwise, assume that it is a keyword, and call print_keyword(). | |
716 */ | |
717 static int | |
718 print_locale_info(char *name, int cflag, int kflag) | |
719 { | |
720 int i; | |
721 | |
722 for (i = 0; locale_name[i].name != NULL; i++) { | |
723 if (strcmp(locale_name[i].name, name) == 0) { | |
724 /* | |
725 * name is a category name | |
726 * print out all keywords in this category | |
727 */ | |
728 return (print_category(locale_name[i].category, | |
729 cflag, kflag)); | |
730 } | |
731 } | |
732 | |
733 /* The name is a keyword name */ | |
734 return (print_keyword(name, cflag, kflag)); | |
735 } | |
736 | |
737 /* | |
738 * Print out the value of the keyword | |
739 */ | |
740 static int | |
741 print_keyword(char *name, int cflag, int kflag) | |
742 { | |
743 int i, j; | |
744 int first_flag = 1; | |
745 int found = 0; | |
746 | |
747 for (i = 0; key[i].name != NULL; i++) { | |
748 if (strcmp(key[i].name, name) != 0) { | |
749 continue; | |
750 } | |
751 | |
752 found = 1; | |
753 if (first_flag && cflag && key[i].category != LC_LOCDEF) { | |
754 /* print out this category's name */ | |
755 (void) printf("%s\n", | |
756 locale_name[key[i].category].name); | |
757 } | |
758 if (kflag) { | |
759 (void) printf("%s=", name); | |
760 } | |
761 switch (key[i].type) { | |
762 | |
763 /* | |
764 * The grouping fields are a set of bytes, each of which | |
765 * is the numeric value of the next group size, terminated | |
766 * by a \0, or by CHAR_MAX | |
767 */ | |
768 case TYPE_GROUP: | |
769 { | |
770 void *s; | |
771 char *q; | |
772 int first = 1; | |
773 | |
774 s = (*key[i].structure)(); | |
775 /* LINTED */ | |
776 q = *(char **)((char *)s + key[i].offset); | |
777 if (*q == '\0') { | |
778 (void) printf("-1"); | |
779 break; | |
780 } | |
781 while (*q != '\0' && *q != CHAR_MAX) { | |
782 if (!first) { | |
783 (void) putchar(';'); | |
784 } | |
785 first = 0; | |
786 (void) printf("%u", | |
787 *(unsigned char *)q++); | |
788 } | |
789 /* CHAR_MAX: no further grouping performed. */ | |
790 if (!first) { | |
791 (void) putchar(';'); | |
792 } | |
793 if (*q == CHAR_MAX) { | |
794 (void) printf("-1"); | |
795 } else { | |
796 (void) putchar('0'); | |
797 } | |
798 } | |
799 break; | |
800 | |
801 /* | |
802 * Entries like decimal_point states ``the decimal-point | |
803 * character...'' not string. However, it is a char *. | |
804 * This assumes single, narrow, character. | |
805 * Should it permit multibyte characters? | |
806 * Should it permit a whole string, in that case? | |
807 */ | |
808 case TYPE_STR: | |
809 { | |
810 void *s; | |
811 char **q; | |
812 | |
813 s = (*key[i].structure)(); | |
814 /* LINTED */ | |
815 q = (char **)((char *)s + key[i].offset); | |
816 for (j = 0; j < key[i].count; j++) { | |
817 if (j != 0) { | |
818 (void) printf(";"); | |
819 } | |
820 if (kflag) { | |
821 (void) printf("\""); | |
822 outstr(q[j]); | |
823 (void) printf("\""); | |
824 } else { | |
825 (void) printf("%s", q[j]); | |
826 } | |
827 } | |
828 } | |
829 break; | |
830 | |
831 case TYPE_INT: | |
832 { | |
833 void *s; | |
834 int *q; | |
835 | |
836 s = (*key[i].structure)(); | |
837 /* LINTED */ | |
838 q = (int *)((char *)s + key[i].offset); | |
839 (void) printf("%d", *q); | |
840 } | |
841 break; | |
842 | |
843 /* | |
844 * TYPE_CHR: Single byte integer. | |
845 */ | |
846 case TYPE_CHR: | |
847 { | |
848 void *s; | |
849 char *q; | |
850 | |
851 s = (*key[i].structure)(); | |
852 q = (char *)((char *)s + key[i].offset); | |
853 if (*q == CHAR_MAX) { | |
854 (void) printf("-1"); | |
855 } else { | |
856 (void) printf("%u", | |
857 *(unsigned char *)q); | |
858 } | |
859 } | |
860 break; | |
861 | |
862 /* | |
863 * TYPE_PCHR: Single byte, printed as a character if printable | |
864 */ | |
865 case TYPE_PCHR: | |
866 { | |
867 void *s; | |
868 char *q; | |
869 | |
870 s = (*key[i].structure)(); | |
871 q = (char *)((char *)s + key[i].offset); | |
872 if (isprint(*(unsigned char *)q)) { | |
873 if (kflag) { | |
874 (void) printf("\""); | |
875 if ((*q == '\\') || | |
876 (*q == ';') || | |
877 (*q == '"')) { | |
878 (void) putchar(escapec); | |
879 (void) printf("%c", | |
880 *(unsigned char *)q); | |
881 } else { | |
882 (void) printf("%c", | |
883 *(unsigned char *)q); | |
884 } | |
885 (void) printf("\""); | |
886 } else { | |
887 (void) printf("%c", | |
888 *(unsigned char *)q); | |
889 } | |
890 } else if (*q == (char)-1) { | |
891 /* In case no signed chars */ | |
892 (void) printf("-1"); | |
893 } else { | |
894 (void) printf("%u", | |
895 *(unsigned char *)q); | |
896 } | |
897 } | |
898 break; | |
899 | |
900 case TYPE_CTP: | |
901 { | |
902 prt_ctp(key[i].name); | |
903 } | |
904 break; | |
905 | |
906 case TYPE_CNVU: | |
907 { | |
908 prt_cnv(key[i].name); | |
909 } | |
910 break; | |
911 | |
912 case TYPE_CNVL: | |
913 { | |
914 prt_cnv(key[i].name); | |
915 } | |
916 break; | |
917 | |
918 case TYPE_COLLEL: | |
919 { | |
920 prt_collel(key[i].name); | |
921 } | |
922 break; | |
923 } | |
924 } | |
925 if (found) { | |
926 (void) printf("\n"); | |
927 return (0); | |
928 } else { | |
929 (void) fprintf(stderr, | |
930 gettext("Unknown keyword name '%s'.\n"), name); | |
931 return (1); | |
932 } | |
933 } | |
934 | |
935 /* | |
936 * Strings being outputed have to use an unambiguous format -- escape | |
937 * any potentially bad output characters. | |
938 * The standard says that any control character shall be preceeded by | |
939 * the escape character. But it doesn't say that you can format that | |
940 * character at all. | |
941 * Question: If the multibyte character contains a quoting character, | |
942 * should that *byte* be escaped? | |
943 */ | |
944 static void | |
945 outstr(char *s) | |
946 { | |
947 wchar_t ws; | |
948 int c; | |
949 size_t mbcurmax = MB_CUR_MAX; | |
950 | |
951 while (*s != '\0') { | |
952 c = mbtowc(&ws, s, mbcurmax); | |
953 if (c < 0) { | |
954 s++; | |
955 } else if (c == 1) { | |
956 outchar(*s++); | |
957 } else { | |
958 for (; c > 0; c--) { | |
959 (void) putchar(*s++); | |
960 } | |
961 } | |
962 } | |
963 } | |
964 | |
965 static void | |
966 outchar(int c) | |
967 { | |
968 unsigned char uc; | |
969 | |
970 uc = (unsigned char) c; | |
971 | |
972 if ((uc == '\\') || (uc == ';') || (uc == '"')) { | |
973 (void) putchar(escapec); | |
974 (void) putchar(uc); | |
975 } else if (iscntrl(uc)) { | |
976 (void) printf("%cx%02x", escapec, uc); | |
977 } else { | |
978 (void) putchar(uc); | |
979 } | |
980 } | |
981 | |
982 /* | |
983 * print_category(): Print out all the keyword's value | |
984 * in the given category | |
985 */ | |
986 static int | |
987 print_category(int category, int cflag, int kflag) | |
988 { | |
989 int i; | |
990 int retval = 0; | |
991 | |
992 if (category == LC_ALL) { | |
993 retval += print_category(LC_CTYPE, cflag, kflag); | |
994 retval += print_category(LC_NUMERIC, cflag, kflag); | |
995 retval += print_category(LC_TIME, cflag, kflag); | |
996 retval += print_category(LC_COLLATE, cflag, kflag); | |
997 retval += print_category(LC_MONETARY, cflag, kflag); | |
998 retval += print_category(LC_MESSAGES, cflag, kflag); | |
999 } else { | |
1000 if (cflag) { | |
1001 (void) printf("%s\n", | |
1002 locale_name[category].name); | |
1003 } | |
1004 | |
1005 for (i = 0; key[i].name != NULL; i++) { | |
1006 if (key[i].category == category) { | |
1007 retval += print_keyword(key[i].name, 0, kflag); | |
1008 } | |
1009 } | |
1010 } | |
1011 return (retval); | |
1012 } | |
1013 | |
1014 /* | |
1015 * usage message for locale | |
1016 */ | |
1017 static void | |
1018 usage(void) | |
1019 { | |
1020 (void) fprintf(stderr, gettext( | |
1021 "Usage: locale [-a|-m]\n" | |
1022 " locale [-ck] name ...\n")); | |
1023 exit(2); | |
1024 } | |
1025 | |
1026 static void | |
1027 prt_ctp(char *name) | |
1028 { | |
1029 int idx, i, mem; | |
1030 int first = 1; | |
1031 | |
1032 static const char *reg_names[] = { | |
1033 "upper", "lower", "alpha", "digit", "space", "cntrl", | |
1034 "punct", "graph", "print", "xdigit", "blank", NULL | |
1035 }; | |
1036 for (idx = 0; reg_names[idx] != NULL; idx++) { | |
1037 if (strcmp(name, reg_names[idx]) == 0) { | |
1038 break; | |
1039 } | |
1040 } | |
1041 if (reg_names[idx] == NULL) { | |
1042 return; | |
1043 } | |
1044 | |
1045 for (i = 0; i < CSSIZE; i++) { | |
1046 mem = 0; | |
1047 switch (idx) { | |
1048 case 0: | |
1049 mem = isupper(i); | |
1050 break; | |
1051 case 1: | |
1052 mem = islower(i); | |
1053 break; | |
1054 case 2: | |
1055 mem = isalpha(i); | |
1056 break; | |
1057 case 3: | |
1058 mem = isdigit(i); | |
1059 break; | |
1060 case 4: | |
1061 mem = isspace(i); | |
1062 break; | |
1063 case 5: | |
1064 mem = iscntrl(i); | |
1065 break; | |
1066 case 6: | |
1067 mem = ispunct(i); | |
1068 break; | |
1069 case 7: | |
1070 mem = isgraph(i); | |
1071 break; | |
1072 case 8: | |
1073 mem = isprint(i); | |
1074 break; | |
1075 case 9: | |
1076 mem = isxdigit(i); | |
1077 break; | |
1078 case 10: | |
1079 mem = isblank(i); | |
1080 break; | |
1081 } | |
1082 if (mem) { | |
1083 if (!first) { | |
1084 (void) putchar(';'); | |
1085 } | |
1086 first = 0; | |
1087 (void) printf("\""); | |
1088 outchar(i); | |
1089 (void) printf("\""); | |
1090 } | |
1091 } | |
1092 } | |
1093 | |
1094 static void | |
1095 prt_cnv(char *name) | |
1096 { | |
1097 int idx, i, q; | |
1098 int first = 1; | |
1099 | |
1100 static const char *reg_names[] = { | |
1101 "toupper", "tolower", NULL | |
1102 }; | |
1103 for (idx = 0; reg_names[idx] != NULL; idx++) { | |
1104 if (strcmp(name, reg_names[idx]) == 0) { | |
1105 break; | |
1106 } | |
1107 } | |
1108 if (reg_names[idx] == NULL) { | |
1109 return; | |
1110 } | |
1111 | |
1112 for (i = 0; i < CSSIZE; i++) { | |
1113 switch (idx) { | |
1114 case 0: | |
1115 q = toupper(i); | |
1116 if (q == i) { | |
1117 continue; | |
1118 } | |
1119 if (!first) { | |
1120 (void) putchar(';'); | |
1121 } | |
1122 first = 0; | |
1123 /* BEGIN CSTYLED */ | |
1124 (void) printf("\"<'"); | |
1125 /* END CSTYLED */ | |
1126 outchar(i); | |
1127 (void) printf("','"); | |
1128 outchar(q); | |
1129 (void) printf("'>\""); | |
1130 break; | |
1131 case 1: | |
1132 q = tolower(i); | |
1133 if (q == i) { | |
1134 continue; | |
1135 } | |
1136 if (!first) { | |
1137 (void) putchar(';'); | |
1138 } | |
1139 first = 0; | |
1140 /* BEGIN CSTYLED */ | |
1141 (void) printf("\"<'"); | |
1142 /* END CSTYLED */ | |
1143 outchar(i); | |
1144 (void) printf("','"); | |
1145 outchar(q); | |
1146 (void) printf("'>\""); | |
1147 break; | |
1148 } | |
1149 } | |
1150 } | |
1151 | |
1152 /* | |
1153 * prt_collel(): Stub for the collate class which does nothing. | |
1154 */ | |
1155 /* ARGSUSED */ | |
1156 static void | |
1157 prt_collel(char *name) | |
1158 { | |
1159 } | |
1160 | |
1161 static char | |
1162 get_escapechar(void) | |
1163 { | |
1164 return ('\\'); | |
1165 } | |
1166 | |
1167 static char | |
1168 get_commentchar(void) | |
1169 { | |
1170 return ('#'); | |
1171 } |