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