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 "%Z%%M% %I% %E% 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 *
getyesno(void)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 *
localedtconv(void)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 *
localeldconv(void)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
main(int argc,char ** argv)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
print_cur_locale(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
add_loc_entry(char * loc)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
loccmp(const char ** str1,const char ** str2)556 loccmp(const char **str1, const char **str2)
557 {
558 return (strcmp(*str1, *str2));
559 }
560
561 static void
show_loc_entry(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
check_loc(char * loc)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
print_all_info(int flag)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
print_locale_info(char * name,int cflag,int kflag)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
print_keyword(char * name,int cflag,int kflag)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
outstr(char * s)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
outchar(int c)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
print_category(int category,int cflag,int kflag)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
usage(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
prt_ctp(char * name)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
prt_cnv(char * name)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
prt_collel(char * name)1157 prt_collel(char *name)
1158 {
1159 }
1160
1161 static char
get_escapechar(void)1162 get_escapechar(void)
1163 {
1164 return ('\\');
1165 }
1166
1167 static char
get_commentchar(void)1168 get_commentchar(void)
1169 {
1170 return ('#');
1171 }
1172