dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / locale / locale.c
blob372b0628702e915c35488834fdfc7e64037c56b8
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * locale -- get current locale information
30 * Copyright 1991, 1993 by Mortice Kern Systems Inc. All rights reserved.
34 #pragma ident "%Z%%M% %I% %E% SMI"
37 * locale: get locale-specific information
38 * usage: locale [-a|-m]
39 * locale [-ck] name ...
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>.
47 #define _LCONV_C99
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>
62 #define LC_LOCDEF 999 /* Random number! */
64 #define LOCALE_DIR "/usr/lib/locale/"
65 #define CHARMAP_DIR "/usr/lib/localedef/src/"
66 #define CHARMAP_NAME "charmap.src"
68 #define GET_LOCALE 0
69 #define GET_CHARMAP 1
70 #define CSSIZE 128
72 #ifndef isblank
73 #define isblank(c) ((__ctype + 1)[c] & _B)
74 #endif
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 */
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);
102 static char *save_loc;
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.
110 struct yesno {
111 char *yes_expr;
112 char *no_expr;
113 char *yes_str;
114 char *no_str;
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;
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;
144 static struct yesno *
145 getyesno(void)
147 static struct yesno yn;
148 static int loaded = 0;
150 if (loaded) {
151 return (&yn);
152 /* NOTREACHED */
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));
160 loaded = 1;
161 return (&yn);
164 static struct dtconv *
165 localedtconv(void)
167 static struct dtconv _dtconv;
168 static int loaded = 0;
170 if (loaded) {
171 return (&_dtconv);
172 /* NOTREACHED */
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));
225 loaded = 1;
226 return (&_dtconv);
229 static struct localedef *
230 localeldconv(void)
232 static struct localedef _locdef;
233 static int loaded = 0;
235 if (loaded) {
236 return (&_locdef);
237 /* NOTREACHED */
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();
247 loaded = 1;
248 return (&_locdef);
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.
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
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
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[] = {
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},
297 {"tolower", SPECIAL LC_CTYPE, TYPE_CNVL},
298 {"toupper", SPECIAL LC_CTYPE, TYPE_CNVU},
300 {"collating-element", 0, 0, 0, LC_COLLATE, TYPE_COLLEL},
301 {"character-collation", 0, 1, 0, LC_COLLATE, TYPE_COLLEL},
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)},
324 #undef dt
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)},
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)},
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
372 * Following keywords have no official method of obtaining them
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
388 {NULL, NULL, 0, 0}
391 static char escapec;
394 main(int argc, char **argv)
396 int c;
397 int retval = 0;
398 int cflag, kflag, aflag, mflag;
400 (void) setlocale(LC_ALL, "");
401 #if !defined(TEXT_DOMAIN)
402 #define TEXT_DOMAIN "SYS_TEST"
403 #endif
404 (void) textdomain(TEXT_DOMAIN);
406 cflag = kflag = aflag = mflag = 0;
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;
429 /* -a OR -m OR (-c and/or -k) */
430 if ((aflag && mflag) || ((aflag || mflag) && (cflag || kflag))) {
431 usage();
432 /* NOTREACHED */
435 escapec = get_escapechar();
437 if (aflag) {
438 print_all_info(GET_LOCALE);
439 /* NOTREACHED */
442 if (mflag) {
443 print_all_info(GET_CHARMAP);
444 /* NOTREACHED */
447 if (optind == argc && !cflag && !kflag) {
448 print_cur_locale();
449 /* NOTREACHED */
451 if (optind == argc) {
452 usage();
453 /* NOTREACHED */
456 for (; optind < argc; optind++) {
457 retval += print_locale_info(argv[optind], cflag, kflag);
459 return (retval);
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.
472 static void
473 print_cur_locale(void)
475 char *lc_allp;
476 char *env, *eff;
477 int i;
479 if ((env = getenv("LANG")) != NULL) {
480 (void) printf("LANG=%s\n", env);
481 } else {
482 (void) printf("LANG=\n");
485 lc_allp = getenv("LC_ALL");
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 = "";
493 env = getenv(locale_name[i].name);
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);
508 (void) putchar('\n');
511 (void) printf("LC_ALL=");
512 if (lc_allp != NULL) {
513 outstr(lc_allp);
515 (void) putchar('\n');
516 exit(0);
519 static int num_of_loc = 0;
520 static int num_of_entries = 0;
521 static char **entries = NULL;
523 static void
524 add_loc_entry(char *loc)
526 #define _INC_NUM 10
527 char *s;
529 if (num_of_loc >= num_of_entries) {
530 char **tmp;
531 num_of_entries += _INC_NUM;
532 tmp = reallocarray(entries, num_of_entries, sizeof (char *));
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);
540 entries = tmp;
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);
550 entries[num_of_loc] = s;
552 num_of_loc++;
555 static int
556 loccmp(const char **str1, const char **str2)
558 return (strcmp(*str1, *str2));
561 static void
562 show_loc_entry(void)
564 int i;
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]);
573 static void
574 check_loc(char *loc)
576 int cat;
578 /* first, try LC_ALL */
579 if (setlocale(LC_ALL, loc) != NULL) {
580 /* succeeded */
581 add_loc_entry(loc);
582 return;
586 * LC_ALL failed.
587 * try each category.
589 for (cat = 0; cat <= _LastCategory; cat++) {
590 if (setlocale(cat, loc) != NULL) {
591 /* succeeded */
592 add_loc_entry(loc);
593 return;
597 /* loc is not a valid locale */
601 * print_all_info(): Print out all the locales and
602 * charmaps supported by the system
604 static void
605 print_all_info(int flag)
607 struct dirent *direntp;
608 DIR *dirp;
609 char *filename; /* filename[PATH_MAX] */
610 char *p;
612 if ((filename = malloc(PATH_MAX)) == NULL) {
613 (void) fprintf(stderr,
614 gettext("locale: cannot allocate buffer"));
615 exit(1);
618 (void) memset(filename, 0, PATH_MAX);
620 if (flag == GET_LOCALE) {
621 /* save the current locale */
622 save_loc = setlocale(LC_ALL, NULL);
624 (void) strcpy(filename, LOCALE_DIR);
625 add_loc_entry("POSIX");
626 } else { /* CHARMAP */
627 (void) strcpy(filename, CHARMAP_DIR);
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);
640 p = filename + strlen(filename);
641 while ((direntp = readdir(dirp)) != NULL) {
642 struct stat stbuf;
644 (void) strcpy(p, direntp->d_name);
645 if (stat(filename, &stbuf) < 0) {
646 continue;
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);
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;
664 if ((charmap = malloc(PATH_MAX)) == NULL) {
665 (void) fprintf(stderr,
666 gettext("locale: cannot allocate buffer"));
667 exit(1);
670 (void) memset(charmap, 0, PATH_MAX);
672 (void) strcpy(charmap, filename);
674 if ((dirc = opendir(charmap)) == NULL) {
675 exit(0);
678 c = charmap + strlen(charmap);
679 *c++ = '/';
680 while ((direntc = readdir(dirc)) != NULL) {
681 struct stat stbuf;
683 (void) strcpy(c, direntc->d_name);
684 if (stat(charmap, &stbuf) < 0) {
685 continue;
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);
696 (void) closedir(dirc);
697 free(charmap);
701 if (flag == GET_LOCALE) {
702 /* restore the saved loc */
703 (void) setlocale(LC_ALL, save_loc);
704 show_loc_entry();
706 (void) closedir(dirp);
707 free(filename);
708 exit(0);
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().
717 static int
718 print_locale_info(char *name, int cflag, int kflag)
720 int i;
722 for (i = 0; locale_name[i].name != NULL; i++) {
723 if (strcmp(locale_name[i].name, name) == 0) {
725 * name is a category name
726 * print out all keywords in this category
728 return (print_category(locale_name[i].category,
729 cflag, kflag));
733 /* The name is a keyword name */
734 return (print_keyword(name, cflag, kflag));
738 * Print out the value of the keyword
740 static int
741 print_keyword(char *name, int cflag, int kflag)
743 int i, j;
744 int first_flag = 1;
745 int found = 0;
747 for (i = 0; key[i].name != NULL; i++) {
748 if (strcmp(key[i].name, name) != 0) {
749 continue;
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);
758 if (kflag) {
759 (void) printf("%s=", name);
761 switch (key[i].type) {
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
768 case TYPE_GROUP:
770 void *s;
771 char *q;
772 int first = 1;
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;
781 while (*q != '\0' && *q != CHAR_MAX) {
782 if (!first) {
783 (void) putchar(';');
785 first = 0;
786 (void) printf("%u",
787 *(unsigned char *)q++);
789 /* CHAR_MAX: no further grouping performed. */
790 if (!first) {
791 (void) putchar(';');
793 if (*q == CHAR_MAX) {
794 (void) printf("-1");
795 } else {
796 (void) putchar('0');
799 break;
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?
808 case TYPE_STR:
810 void *s;
811 char **q;
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(";");
820 if (kflag) {
821 (void) printf("\"");
822 outstr(q[j]);
823 (void) printf("\"");
824 } else {
825 (void) printf("%s", q[j]);
829 break;
831 case TYPE_INT:
833 void *s;
834 int *q;
836 s = (*key[i].structure)();
837 /* LINTED */
838 q = (int *)((char *)s + key[i].offset);
839 (void) printf("%d", *q);
841 break;
844 * TYPE_CHR: Single byte integer.
846 case TYPE_CHR:
848 void *s;
849 char *q;
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);
860 break;
863 * TYPE_PCHR: Single byte, printed as a character if printable
865 case TYPE_PCHR:
867 void *s;
868 char *q;
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);
885 (void) printf("\"");
886 } else {
887 (void) printf("%c",
888 *(unsigned char *)q);
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);
898 break;
900 case TYPE_CTP:
902 prt_ctp(key[i].name);
904 break;
906 case TYPE_CNVU:
908 prt_cnv(key[i].name);
910 break;
912 case TYPE_CNVL:
914 prt_cnv(key[i].name);
916 break;
918 case TYPE_COLLEL:
920 prt_collel(key[i].name);
922 break;
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);
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?
944 static void
945 outstr(char *s)
947 wchar_t ws;
948 int c;
949 size_t mbcurmax = MB_CUR_MAX;
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++);
965 static void
966 outchar(int c)
968 unsigned char uc;
970 uc = (unsigned char) c;
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);
983 * print_category(): Print out all the keyword's value
984 * in the given category
986 static int
987 print_category(int category, int cflag, int kflag)
989 int i;
990 int retval = 0;
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);
1005 for (i = 0; key[i].name != NULL; i++) {
1006 if (key[i].category == category) {
1007 retval += print_keyword(key[i].name, 0, kflag);
1011 return (retval);
1015 * usage message for locale
1017 static void
1018 usage(void)
1020 (void) fprintf(stderr, gettext(
1021 "Usage: locale [-a|-m]\n"
1022 " locale [-ck] name ...\n"));
1023 exit(2);
1026 static void
1027 prt_ctp(char *name)
1029 int idx, i, mem;
1030 int first = 1;
1032 static const char *reg_names[] = {
1033 "upper", "lower", "alpha", "digit", "space", "cntrl",
1034 "punct", "graph", "print", "xdigit", "blank", NULL
1036 for (idx = 0; reg_names[idx] != NULL; idx++) {
1037 if (strcmp(name, reg_names[idx]) == 0) {
1038 break;
1041 if (reg_names[idx] == NULL) {
1042 return;
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;
1082 if (mem) {
1083 if (!first) {
1084 (void) putchar(';');
1086 first = 0;
1087 (void) printf("\"");
1088 outchar(i);
1089 (void) printf("\"");
1094 static void
1095 prt_cnv(char *name)
1097 int idx, i, q;
1098 int first = 1;
1100 static const char *reg_names[] = {
1101 "toupper", "tolower", NULL
1103 for (idx = 0; reg_names[idx] != NULL; idx++) {
1104 if (strcmp(name, reg_names[idx]) == 0) {
1105 break;
1108 if (reg_names[idx] == NULL) {
1109 return;
1112 for (i = 0; i < CSSIZE; i++) {
1113 switch (idx) {
1114 case 0:
1115 q = toupper(i);
1116 if (q == i) {
1117 continue;
1119 if (!first) {
1120 (void) putchar(';');
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;
1136 if (!first) {
1137 (void) putchar(';');
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;
1153 * prt_collel(): Stub for the collate class which does nothing.
1155 /* ARGSUSED */
1156 static void
1157 prt_collel(char *name)
1161 static char
1162 get_escapechar(void)
1164 return ('\\');
1167 static char
1168 get_commentchar(void)
1170 return ('#');