| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License, Version 1.0 only |
| * (the "License"). You may not use this file except in compliance |
| * with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright 2003 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| /* |
| * locale -- get current locale information |
| * |
| * Copyright 1991, 1993 by Mortice Kern Systems Inc. All rights reserved. |
| * |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| /* |
| * locale: get locale-specific information |
| * usage: locale [-a|-m] |
| * locale [-ck] name ... |
| */ |
| |
| /* |
| * New members added in the struct lconv by IEEE Std 1003.1-2001 |
| * are always activated in the locale object. |
| * See <iso/locale_iso.h>. |
| */ |
| #define _LCONV_C99 |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <limits.h> |
| #include <string.h> |
| #include <dirent.h> |
| #include <ctype.h> |
| #include <stddef.h> |
| #include <nl_types.h> |
| #include <langinfo.h> |
| #include <locale.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| |
| #define LC_LOCDEF 999 /* Random number! */ |
| |
| #define LOCALE_DIR "/usr/lib/locale/" |
| #define CHARMAP_DIR "/usr/lib/localedef/src/" |
| #define CHARMAP_NAME "charmap.src" |
| |
| #define GET_LOCALE 0 |
| #define GET_CHARMAP 1 |
| #define CSSIZE 128 |
| |
| #ifndef isblank |
| #define isblank(c) ((__ctype + 1)[c] & _B) |
| #endif |
| |
| enum types { |
| TYPE_STR, /* char * */ |
| TYPE_GROUP, /* char *, for mon_grouping, and grouping */ |
| TYPE_INT, /* int */ |
| TYPE_CHR, /* char, printed as signed integer */ |
| TYPE_PCHR, /* char, printed as printable character */ |
| TYPE_CTP, /* ctype entry */ |
| TYPE_CNVL, /* convert to lower */ |
| TYPE_CNVU, /* convert to upper */ |
| TYPE_COLLEL /* print the multi-character collating elements */ |
| }; |
| |
| static int print_locale_info(char *keyword, int cflag, int kflag); |
| static int print_category(int category, int cflag, int kflag); |
| static int print_keyword(char *name, int cflag, int kflag); |
| static void usage(void); |
| static void print_all_info(int); |
| static void print_cur_locale(void); |
| static void outstr(char *s); |
| static void outchar(int); |
| static void prt_ctp(char *); |
| static void prt_cnv(char *); |
| static void prt_collel(char *); |
| static char get_escapechar(void); |
| static char get_commentchar(void); |
| |
| static char *save_loc; |
| |
| /* |
| * yes/no is not in the localeconv structure for xpg style. |
| * We dummy up a new structure for purposes of the code below. |
| * If YESEXPR is available per XPG4, we use it. |
| * Otherwise, use YESSTR, the old method with less functionality from XPG3. |
| */ |
| struct yesno { |
| char *yes_expr; |
| char *no_expr; |
| char *yes_str; |
| char *no_str; |
| }; |
| |
| struct dtconv { |
| char *date_time_format; |
| char *date_format; |
| char *time_format; |
| char *time_format_ampm; |
| char *am_string; |
| char *pm_string; |
| char *abbrev_day_names[7]; |
| char *day_names[7]; |
| char *abbrev_month_names[12]; |
| char *month_names[12]; |
| char *era; |
| char *era_d_fmt; |
| char *era_d_t_fmt; |
| char *era_t_fmt; |
| char *alt_digits; |
| }; |
| |
| struct localedef { |
| char *charmap; |
| char *code_set_name; |
| char escape_char; |
| char comment_char; |
| int mb_cur_max; |
| int mb_cur_min; |
| }; |
| |
| static struct yesno * |
| getyesno(void) |
| { |
| static struct yesno yn; |
| static int loaded = 0; |
| |
| if (loaded) { |
| return (&yn); |
| /* NOTREACHED */ |
| } |
| |
| yn.yes_expr = strdup(nl_langinfo(YESEXPR)); |
| yn.no_expr = strdup(nl_langinfo(NOEXPR)); |
| yn.yes_str = strdup(nl_langinfo(YESSTR)); |
| yn.no_str = strdup(nl_langinfo(NOSTR)); |
| |
| loaded = 1; |
| return (&yn); |
| } |
| |
| static struct dtconv * |
| localedtconv(void) |
| { |
| static struct dtconv _dtconv; |
| static int loaded = 0; |
| |
| if (loaded) { |
| return (&_dtconv); |
| /* NOTREACHED */ |
| } |
| |
| _dtconv.date_time_format = strdup(nl_langinfo(D_T_FMT)); |
| _dtconv.date_format = strdup(nl_langinfo(D_FMT)); |
| _dtconv.time_format = strdup(nl_langinfo(T_FMT)); |
| _dtconv.time_format_ampm = strdup(nl_langinfo(T_FMT_AMPM)); |
| _dtconv.am_string = strdup(nl_langinfo(AM_STR)); |
| _dtconv.pm_string = strdup(nl_langinfo(PM_STR)); |
| _dtconv.abbrev_day_names[0] = strdup(nl_langinfo(ABDAY_1)); |
| _dtconv.abbrev_day_names[1] = strdup(nl_langinfo(ABDAY_2)); |
| _dtconv.abbrev_day_names[2] = strdup(nl_langinfo(ABDAY_3)); |
| _dtconv.abbrev_day_names[3] = strdup(nl_langinfo(ABDAY_4)); |
| _dtconv.abbrev_day_names[4] = strdup(nl_langinfo(ABDAY_5)); |
| _dtconv.abbrev_day_names[5] = strdup(nl_langinfo(ABDAY_6)); |
| _dtconv.abbrev_day_names[6] = strdup(nl_langinfo(ABDAY_7)); |
| _dtconv.day_names[0] = strdup(nl_langinfo(DAY_1)); |
| _dtconv.day_names[1] = strdup(nl_langinfo(DAY_2)); |
| _dtconv.day_names[2] = strdup(nl_langinfo(DAY_3)); |
| _dtconv.day_names[3] = strdup(nl_langinfo(DAY_4)); |
| _dtconv.day_names[4] = strdup(nl_langinfo(DAY_5)); |
| _dtconv.day_names[5] = strdup(nl_langinfo(DAY_6)); |
| _dtconv.day_names[6] = strdup(nl_langinfo(DAY_7)); |
| _dtconv.abbrev_month_names[0] = strdup(nl_langinfo(ABMON_1)); |
| _dtconv.abbrev_month_names[1] = strdup(nl_langinfo(ABMON_2)); |
| _dtconv.abbrev_month_names[2] = strdup(nl_langinfo(ABMON_3)); |
| _dtconv.abbrev_month_names[3] = strdup(nl_langinfo(ABMON_4)); |
| _dtconv.abbrev_month_names[4] = strdup(nl_langinfo(ABMON_5)); |
| _dtconv.abbrev_month_names[5] = strdup(nl_langinfo(ABMON_6)); |
| _dtconv.abbrev_month_names[6] = strdup(nl_langinfo(ABMON_7)); |
| _dtconv.abbrev_month_names[7] = strdup(nl_langinfo(ABMON_8)); |
| _dtconv.abbrev_month_names[8] = strdup(nl_langinfo(ABMON_9)); |
| _dtconv.abbrev_month_names[9] = strdup(nl_langinfo(ABMON_10)); |
| _dtconv.abbrev_month_names[10] = strdup(nl_langinfo(ABMON_11)); |
| _dtconv.abbrev_month_names[11] = strdup(nl_langinfo(ABMON_12)); |
| _dtconv.month_names[0] = strdup(nl_langinfo(MON_1)); |
| _dtconv.month_names[1] = strdup(nl_langinfo(MON_2)); |
| _dtconv.month_names[2] = strdup(nl_langinfo(MON_3)); |
| _dtconv.month_names[3] = strdup(nl_langinfo(MON_4)); |
| _dtconv.month_names[4] = strdup(nl_langinfo(MON_5)); |
| _dtconv.month_names[5] = strdup(nl_langinfo(MON_6)); |
| _dtconv.month_names[6] = strdup(nl_langinfo(MON_7)); |
| _dtconv.month_names[7] = strdup(nl_langinfo(MON_8)); |
| _dtconv.month_names[8] = strdup(nl_langinfo(MON_9)); |
| _dtconv.month_names[9] = strdup(nl_langinfo(MON_10)); |
| _dtconv.month_names[10] = strdup(nl_langinfo(MON_11)); |
| _dtconv.month_names[11] = strdup(nl_langinfo(MON_12)); |
| _dtconv.era = strdup(nl_langinfo(ERA)); |
| _dtconv.era_d_fmt = strdup(nl_langinfo(ERA_D_FMT)); |
| _dtconv.era_d_t_fmt = strdup(nl_langinfo(ERA_D_T_FMT)); |
| _dtconv.era_t_fmt = strdup(nl_langinfo(ERA_T_FMT)); |
| _dtconv.alt_digits = strdup(nl_langinfo(ALT_DIGITS)); |
| |
| loaded = 1; |
| return (&_dtconv); |
| } |
| |
| static struct localedef * |
| localeldconv(void) |
| { |
| static struct localedef _locdef; |
| static int loaded = 0; |
| |
| if (loaded) { |
| return (&_locdef); |
| /* NOTREACHED */ |
| } |
| |
| _locdef.charmap = strdup(nl_langinfo(CODESET)); |
| _locdef.code_set_name = strdup(nl_langinfo(CODESET)); |
| _locdef.mb_cur_max = MB_CUR_MAX; |
| _locdef.mb_cur_min = 1; |
| _locdef.escape_char = get_escapechar(); |
| _locdef.comment_char = get_commentchar(); |
| |
| loaded = 1; |
| return (&_locdef); |
| } |
| |
| /* |
| * The locale_name array also defines a canonical ordering for the categories. |
| * The function tocanon() translates the LC_* manifests to their canonical |
| * values. |
| */ |
| static struct locale_name { |
| char *name; |
| int category; |
| } locale_name[] = { |
| {"LC_CTYPE", LC_CTYPE}, |
| {"LC_NUMERIC", LC_NUMERIC}, |
| {"LC_TIME", LC_TIME}, |
| {"LC_COLLATE", LC_COLLATE}, |
| {"LC_MONETARY", LC_MONETARY}, |
| {"LC_MESSAGES", LC_MESSAGES}, |
| {"LC_ALL", LC_ALL}, |
| NULL |
| }; |
| |
| /* |
| * The structure key contains all keywords string name, |
| * symbolic name, category, and type (STR INT ...) |
| * the type will decide the way the value of the item be printed out |
| */ |
| static struct key { |
| char *name; |
| void *(*structure)(void); |
| int offset; |
| int count; |
| int category; |
| enum types type; |
| } key[] = { |
| |
| #define SPECIAL 0, 0, 0, |
| {"lower", SPECIAL LC_CTYPE, TYPE_CTP}, |
| {"upper", SPECIAL LC_CTYPE, TYPE_CTP}, |
| {"alpha", SPECIAL LC_CTYPE, TYPE_CTP}, |
| {"digit", SPECIAL LC_CTYPE, TYPE_CTP}, |
| {"space", SPECIAL LC_CTYPE, TYPE_CTP}, |
| {"cntrl", SPECIAL LC_CTYPE, TYPE_CTP}, |
| {"punct", SPECIAL LC_CTYPE, TYPE_CTP}, |
| {"graph", SPECIAL LC_CTYPE, TYPE_CTP}, |
| {"print", SPECIAL LC_CTYPE, TYPE_CTP}, |
| {"xdigit", SPECIAL LC_CTYPE, TYPE_CTP}, |
| {"blank", SPECIAL LC_CTYPE, TYPE_CTP}, |
| |
| {"tolower", SPECIAL LC_CTYPE, TYPE_CNVL}, |
| {"toupper", SPECIAL LC_CTYPE, TYPE_CNVU}, |
| |
| {"collating-element", 0, 0, 0, LC_COLLATE, TYPE_COLLEL}, |
| {"character-collation", 0, 1, 0, LC_COLLATE, TYPE_COLLEL}, |
| |
| #define dt(member, count) \ |
| (void *(*)(void))localedtconv, \ |
| offsetof(struct dtconv, member), \ |
| count, \ |
| LC_TIME, \ |
| TYPE_STR |
| {"d_t_fmt", dt(date_time_format, 1)}, |
| {"d_fmt", dt(date_format, 1)}, |
| {"t_fmt", dt(time_format, 1)}, |
| {"t_fmt_ampm", dt(time_format_ampm, 1)}, |
| {"am_pm", dt(am_string, 2)}, |
| {"day", dt(day_names, 7)}, |
| {"abday", dt(abbrev_day_names, 7)}, |
| {"mon", dt(month_names, 12)}, |
| {"abmon", dt(abbrev_month_names, 12)}, |
| {"era", dt(era, 1)}, |
| {"era_d_fmt", dt(era_d_fmt, 1)}, |
| {"era_d_t_fmt", dt(era_d_t_fmt, 1)}, |
| {"era_t_fmt", dt(era_t_fmt, 1)}, |
| {"alt_digits", dt(alt_digits, 1)}, |
| |
| #undef dt |
| |
| #define lc(member, locale, type) \ |
| (void *(*)(void))localeconv, \ |
| offsetof(struct lconv, member), \ |
| 1, \ |
| locale, \ |
| type |
| {"decimal_point", lc(decimal_point, LC_NUMERIC, TYPE_STR) }, |
| {"thousands_sep", lc(thousands_sep, LC_NUMERIC, TYPE_STR) }, |
| {"grouping", lc(grouping, LC_NUMERIC, TYPE_GROUP)}, |
| {"int_curr_symbol", lc(int_curr_symbol, LC_MONETARY, TYPE_STR)}, |
| {"currency_symbol", lc(currency_symbol, LC_MONETARY, TYPE_STR)}, |
| {"mon_decimal_point", lc(mon_decimal_point, LC_MONETARY, TYPE_STR)}, |
| {"mon_thousands_sep", lc(mon_thousands_sep, LC_MONETARY, TYPE_STR)}, |
| {"mon_grouping", lc(mon_grouping, LC_MONETARY, TYPE_GROUP)}, |
| {"positive_sign", lc(positive_sign, LC_MONETARY, TYPE_STR)}, |
| {"negative_sign", lc(negative_sign, LC_MONETARY, TYPE_STR)}, |
| |
| {"int_frac_digits", lc(int_frac_digits, LC_MONETARY, TYPE_CHR)}, |
| {"frac_digits", lc(frac_digits, LC_MONETARY, TYPE_CHR)}, |
| {"p_cs_precedes", lc(p_cs_precedes, LC_MONETARY, TYPE_CHR)}, |
| {"p_sep_by_space", lc(p_sep_by_space, LC_MONETARY, TYPE_CHR)}, |
| {"n_cs_precedes", lc(n_cs_precedes, LC_MONETARY, TYPE_CHR)}, |
| {"n_sep_by_space", lc(n_sep_by_space, LC_MONETARY, TYPE_CHR)}, |
| {"p_sign_posn", lc(p_sign_posn, LC_MONETARY, TYPE_CHR)}, |
| {"n_sign_posn", lc(n_sign_posn, LC_MONETARY, TYPE_CHR)}, |
| {"int_p_cs_precedes", lc(int_p_cs_precedes, LC_MONETARY, TYPE_CHR)}, |
| {"int_p_sep_by_space", lc(int_p_sep_by_space, LC_MONETARY, TYPE_CHR)}, |
| {"int_n_cs_precedes", lc(int_n_cs_precedes, LC_MONETARY, TYPE_CHR)}, |
| {"int_n_sep_by_space", lc(int_n_sep_by_space, LC_MONETARY, TYPE_CHR)}, |
| {"int_p_sign_posn", lc(int_p_sign_posn, LC_MONETARY, TYPE_CHR)}, |
| {"int_n_sign_posn", lc(int_n_sign_posn, LC_MONETARY, TYPE_CHR)}, |
| |
| #undef lc |
| #define lc(member) \ |
| (void *(*)(void))getyesno, \ |
| offsetof(struct yesno, member), \ |
| 1, \ |
| LC_MESSAGES, \ |
| TYPE_STR |
| {"yesexpr", lc(yes_expr)}, |
| {"noexpr", lc(no_expr)}, |
| {"yesstr", lc(yes_str)}, |
| {"nostr", lc(no_str)}, |
| #undef lc |
| |
| /* |
| * Following keywords have no official method of obtaining them |
| */ |
| #define ld(member, locale, type) \ |
| (void *(*)(void))localeldconv, \ |
| offsetof(struct localedef, member), \ |
| 1, \ |
| locale, \ |
| type |
| {"charmap", ld(charmap, LC_LOCDEF, TYPE_STR)}, |
| {"code_set_name", ld(code_set_name, LC_LOCDEF, TYPE_STR)}, |
| {"escape_char", ld(escape_char, LC_LOCDEF, TYPE_PCHR)}, |
| {"comment_char", ld(comment_char, LC_LOCDEF, TYPE_PCHR)}, |
| {"mb_cur_max", ld(mb_cur_max, LC_LOCDEF, TYPE_INT)}, |
| {"mb_cur_min", ld(mb_cur_min, LC_LOCDEF, TYPE_INT)}, |
| #undef ld |
| |
| {NULL, NULL, 0, 0} |
| }; |
| |
| static char escapec; |
| |
| int |
| main(int argc, char **argv) |
| { |
| int c; |
| int retval = 0; |
| int cflag, kflag, aflag, mflag; |
| |
| (void) setlocale(LC_ALL, ""); |
| #if !defined(TEXT_DOMAIN) |
| #define TEXT_DOMAIN "SYS_TEST" |
| #endif |
| (void) textdomain(TEXT_DOMAIN); |
| |
| cflag = kflag = aflag = mflag = 0; |
| |
| while ((c = getopt(argc, argv, "amck")) != EOF) { |
| switch (c) { |
| case 'a': |
| aflag = 1; |
| break; |
| case 'm': |
| mflag = 1; |
| break; |
| case 'c': |
| cflag = 1; |
| break; |
| case 'k': |
| kflag = 1; |
| break; |
| default: |
| usage(); |
| /* NOTREACHED */ |
| break; |
| } |
| } |
| |
| /* -a OR -m OR (-c and/or -k) */ |
| if ((aflag && mflag) || ((aflag || mflag) && (cflag || kflag))) { |
| usage(); |
| /* NOTREACHED */ |
| } |
| |
| escapec = get_escapechar(); |
| |
| if (aflag) { |
| print_all_info(GET_LOCALE); |
| /* NOTREACHED */ |
| } |
| |
| if (mflag) { |
| print_all_info(GET_CHARMAP); |
| /* NOTREACHED */ |
| } |
| |
| if (optind == argc && !cflag && !kflag) { |
| print_cur_locale(); |
| /* NOTREACHED */ |
| } |
| if (optind == argc) { |
| usage(); |
| /* NOTREACHED */ |
| } |
| |
| for (; optind < argc; optind++) { |
| retval += print_locale_info(argv[optind], cflag, kflag); |
| } |
| return (retval); |
| } |
| |
| /* |
| * No options or operands. |
| * Print out the current locale names from the environment, or implied. |
| * Variables directly set in the environment are printed as-is, those |
| * implied are printed in quotes. |
| * The strings are printed ``appropriately quoted for possible later re-entry |
| * to the shell''. We use the routine outstr to do this -- however we |
| * want the shell escape character, the backslash, not the locale escape |
| * character, so we quietly save and restore the locale escape character. |
| */ |
| static void |
| print_cur_locale(void) |
| { |
| char *lc_allp; |
| char *env, *eff; |
| int i; |
| |
| if ((env = getenv("LANG")) != NULL) { |
| (void) printf("LANG=%s\n", env); |
| } else { |
| (void) printf("LANG=\n"); |
| } |
| |
| lc_allp = getenv("LC_ALL"); |
| |
| for (i = 0; i < LC_ALL; i++) { |
| (void) printf("%s=", locale_name[i].name); |
| eff = setlocale(i, NULL); |
| if (eff == NULL) { |
| eff = ""; |
| } |
| env = getenv(locale_name[i].name); |
| |
| if (env == NULL) { |
| (void) putchar('"'); |
| outstr(eff); |
| (void) putchar('"'); |
| } else { |
| if (strcmp(env, eff) != 0) { |
| (void) putchar('"'); |
| outstr(eff); |
| (void) putchar('"'); |
| } else { |
| outstr(eff); |
| } |
| } |
| (void) putchar('\n'); |
| } |
| |
| (void) printf("LC_ALL="); |
| if (lc_allp != NULL) { |
| outstr(lc_allp); |
| } |
| (void) putchar('\n'); |
| exit(0); |
| } |
| |
| static int num_of_loc = 0; |
| static int num_of_entries = 0; |
| static char **entries = NULL; |
| |
| static void |
| add_loc_entry(char *loc) |
| { |
| #define _INC_NUM 10 |
| char *s; |
| |
| if (num_of_loc >= num_of_entries) { |
| char **tmp; |
| num_of_entries += _INC_NUM; |
| tmp = realloc(entries, sizeof (char *) * num_of_entries); |
| if (tmp == NULL) { |
| /* restoring original locale */ |
| (void) setlocale(LC_ALL, save_loc); |
| (void) fprintf(stderr, |
| gettext("locale: cannot allocate buffer")); |
| exit(1); |
| } |
| entries = tmp; |
| } |
| s = strdup(loc); |
| if (s == NULL) { |
| /* restoring original locale */ |
| (void) setlocale(LC_ALL, save_loc); |
| (void) fprintf(stderr, |
| gettext("locale: cannot allocate buffer")); |
| exit(1); |
| } |
| entries[num_of_loc] = s; |
| |
| num_of_loc++; |
| } |
| |
| static int |
| loccmp(const char **str1, const char **str2) |
| { |
| return (strcmp(*str1, *str2)); |
| } |
| |
| static void |
| show_loc_entry(void) |
| { |
| int i; |
| |
| qsort(entries, num_of_loc, sizeof (char *), |
| (int (*)(const void *, const void *))loccmp); |
| for (i = 0; i < num_of_loc; i++) { |
| (void) printf("%s\n", entries[i]); |
| } |
| } |
| |
| static void |
| check_loc(char *loc) |
| { |
| int cat; |
| |
| /* first, try LC_ALL */ |
| if (setlocale(LC_ALL, loc) != NULL) { |
| /* succeeded */ |
| add_loc_entry(loc); |
| return; |
| } |
| |
| /* |
| * LC_ALL failed. |
| * try each category. |
| */ |
| for (cat = 0; cat <= _LastCategory; cat++) { |
| if (setlocale(cat, loc) != NULL) { |
| /* succeeded */ |
| add_loc_entry(loc); |
| return; |
| } |
| } |
| |
| /* loc is not a valid locale */ |
| } |
| |
| /* |
| * print_all_info(): Print out all the locales and |
| * charmaps supported by the system |
| */ |
| static void |
| print_all_info(int flag) |
| { |
| struct dirent *direntp; |
| DIR *dirp; |
| char *filename; /* filename[PATH_MAX] */ |
| char *p; |
| |
| if ((filename = malloc(PATH_MAX)) == NULL) { |
| (void) fprintf(stderr, |
| gettext("locale: cannot allocate buffer")); |
| exit(1); |
| } |
| |
| (void) memset(filename, 0, PATH_MAX); |
| |
| if (flag == GET_LOCALE) { |
| /* save the current locale */ |
| save_loc = setlocale(LC_ALL, NULL); |
| |
| (void) strcpy(filename, LOCALE_DIR); |
| add_loc_entry("POSIX"); |
| } else { /* CHARMAP */ |
| (void) strcpy(filename, CHARMAP_DIR); |
| } |
| |
| if ((dirp = opendir(filename)) == NULL) { |
| if (flag == GET_LOCALE) |
| exit(0); |
| else { /* CHARMAP */ |
| (void) fprintf(stderr, gettext( |
| "locale: charmap information not available.\n")); |
| exit(2); |
| } |
| } |
| |
| p = filename + strlen(filename); |
| while ((direntp = readdir(dirp)) != NULL) { |
| struct stat stbuf; |
| |
| (void) strcpy(p, direntp->d_name); |
| if (stat(filename, &stbuf) < 0) { |
| continue; |
| } |
| |
| if (flag == GET_LOCALE) { |
| if (S_ISDIR(stbuf.st_mode) && |
| (direntp->d_name[0] != '.') && |
| /* "POSIX" has already been printed out */ |
| strcmp(direntp->d_name, "POSIX") != 0) { |
| check_loc(direntp->d_name); |
| } |
| } else { /* CHARMAP */ |
| if (S_ISDIR(stbuf.st_mode) && |
| direntp->d_name[0] != '.') { |
| struct dirent *direntc; |
| DIR *dirc; |
| char *charmap; |
| char *c; |
| |
| if ((charmap = malloc(PATH_MAX)) == NULL) { |
| (void) fprintf(stderr, |
| gettext("locale: cannot allocate buffer")); |
| exit(1); |
| } |
| |
| (void) memset(charmap, 0, PATH_MAX); |
| |
| (void) strcpy(charmap, filename); |
| |
| if ((dirc = opendir(charmap)) == NULL) { |
| exit(0); |
| } |
| |
| c = charmap + strlen(charmap); |
| *c++ = '/'; |
| while ((direntc = readdir(dirc)) != NULL) { |
| struct stat stbuf; |
| |
| (void) strcpy(c, direntc->d_name); |
| if (stat(charmap, &stbuf) < 0) { |
| continue; |
| } |
| |
| if (S_ISREG(stbuf.st_mode) && |
| (strcmp(direntc->d_name, |
| CHARMAP_NAME) == 0) && |
| (direntc->d_name[0] != '.')) { |
| (void) printf("%s/%s\n", |
| p, direntc->d_name); |
| } |
| } |
| (void) closedir(dirc); |
| free(charmap); |
| } |
| } |
| } |
| if (flag == GET_LOCALE) { |
| /* restore the saved loc */ |
| (void) setlocale(LC_ALL, save_loc); |
| show_loc_entry(); |
| } |
| (void) closedir(dirp); |
| free(filename); |
| exit(0); |
| } |
| |
| /* |
| * Print out the keyword value or category info. |
| * Call print_category() to print the entire locale category, if the name |
| * given is recognized as a category. |
| * Otherwise, assume that it is a keyword, and call print_keyword(). |
| */ |
| static int |
| print_locale_info(char *name, int cflag, int kflag) |
| { |
| int i; |
| |
| for (i = 0; locale_name[i].name != NULL; i++) { |
| if (strcmp(locale_name[i].name, name) == 0) { |
| /* |
| * name is a category name |
| * print out all keywords in this category |
| */ |
| return (print_category(locale_name[i].category, |
| cflag, kflag)); |
| } |
| } |
| |
| /* The name is a keyword name */ |
| return (print_keyword(name, cflag, kflag)); |
| } |
| |
| /* |
| * Print out the value of the keyword |
| */ |
| static int |
| print_keyword(char *name, int cflag, int kflag) |
| { |
| int i, j; |
| int first_flag = 1; |
| int found = 0; |
| |
| for (i = 0; key[i].name != NULL; i++) { |
| if (strcmp(key[i].name, name) != 0) { |
| continue; |
| } |
| |
| found = 1; |
| if (first_flag && cflag && key[i].category != LC_LOCDEF) { |
| /* print out this category's name */ |
| (void) printf("%s\n", |
| locale_name[key[i].category].name); |
| } |
| if (kflag) { |
| (void) printf("%s=", name); |
| } |
| switch (key[i].type) { |
| |
| /* |
| * The grouping fields are a set of bytes, each of which |
| * is the numeric value of the next group size, terminated |
| * by a \0, or by CHAR_MAX |
| */ |
| case TYPE_GROUP: |
| { |
| void *s; |
| char *q; |
| int first = 1; |
| |
| s = (*key[i].structure)(); |
| /* LINTED */ |
| q = *(char **)((char *)s + key[i].offset); |
| if (*q == '\0') { |
| (void) printf("-1"); |
| break; |
| } |
| while (*q != '\0' && *q != CHAR_MAX) { |
| if (!first) { |
| (void) putchar(';'); |
| } |
| first = 0; |
| (void) printf("%u", |
| *(unsigned char *)q++); |
| } |
| /* CHAR_MAX: no further grouping performed. */ |
| if (!first) { |
| (void) putchar(';'); |
| } |
| if (*q == CHAR_MAX) { |
| (void) printf("-1"); |
| } else { |
| (void) putchar('0'); |
| } |
| } |
| break; |
| |
| /* |
| * Entries like decimal_point states ``the decimal-point |
| * character...'' not string. However, it is a char *. |
| * This assumes single, narrow, character. |
| * Should it permit multibyte characters? |
| * Should it permit a whole string, in that case? |
| */ |
| case TYPE_STR: |
| { |
| void *s; |
| char **q; |
| |
| s = (*key[i].structure)(); |
| /* LINTED */ |
| q = (char **)((char *)s + key[i].offset); |
| for (j = 0; j < key[i].count; j++) { |
| if (j != 0) { |
| (void) printf(";"); |
| } |
| if (kflag) { |
| (void) printf("\""); |
| outstr(q[j]); |
| (void) printf("\""); |
| } else { |
| (void) printf("%s", q[j]); |
| } |
| } |
| } |
| break; |
| |
| case TYPE_INT: |
| { |
| void *s; |
| int *q; |
| |
| s = (*key[i].structure)(); |
| /* LINTED */ |
| q = (int *)((char *)s + key[i].offset); |
| (void) printf("%d", *q); |
| } |
| break; |
| |
| /* |
| * TYPE_CHR: Single byte integer. |
| */ |
| case TYPE_CHR: |
| { |
| void *s; |
| char *q; |
| |
| s = (*key[i].structure)(); |
| q = (char *)((char *)s + key[i].offset); |
| if (*q == CHAR_MAX) { |
| (void) printf("-1"); |
| } else { |
| (void) printf("%u", |
| *(unsigned char *)q); |
| } |
| } |
| break; |
| |
| /* |
| * TYPE_PCHR: Single byte, printed as a character if printable |
| */ |
| case TYPE_PCHR: |
| { |
| void *s; |
| char *q; |
| |
| s = (*key[i].structure)(); |
| q = (char *)((char *)s + key[i].offset); |
| if (isprint(*(unsigned char *)q)) { |
| if (kflag) { |
| (void) printf("\""); |
| if ((*q == '\\') || |
| (*q == ';') || |
| (*q == '"')) { |
| (void) putchar(escapec); |
| (void) printf("%c", |
| *(unsigned char *)q); |
| } else { |
| (void) printf("%c", |
| *(unsigned char *)q); |
| } |
| (void) printf("\""); |
| } else { |
| (void) printf("%c", |
| *(unsigned char *)q); |
| } |
| } else if (*q == (char)-1) { |
| /* In case no signed chars */ |
| (void) printf("-1"); |
| } else { |
| (void) printf("%u", |
| *(unsigned char *)q); |
| } |
| } |
| break; |
| |
| case TYPE_CTP: |
| { |
| prt_ctp(key[i].name); |
| } |
| break; |
| |
| case TYPE_CNVU: |
| { |
| prt_cnv(key[i].name); |
| } |
| break; |
| |
| case TYPE_CNVL: |
| { |
| prt_cnv(key[i].name); |
| } |
| break; |
| |
| case TYPE_COLLEL: |
| { |
| prt_collel(key[i].name); |
| } |
| break; |
| } |
| } |
| if (found) { |
| (void) printf("\n"); |
| return (0); |
| } else { |
| (void) fprintf(stderr, |
| gettext("Unknown keyword name '%s'.\n"), name); |
| return (1); |
| } |
| } |
| |
| /* |
| * Strings being outputed have to use an unambiguous format -- escape |
| * any potentially bad output characters. |
| * The standard says that any control character shall be preceeded by |
| * the escape character. But it doesn't say that you can format that |
| * character at all. |
| * Question: If the multibyte character contains a quoting character, |
| * should that *byte* be escaped? |
| */ |
| static void |
| outstr(char *s) |
| { |
| wchar_t ws; |
| int c; |
| size_t mbcurmax = MB_CUR_MAX; |
| |
| while (*s != '\0') { |
| c = mbtowc(&ws, s, mbcurmax); |
| if (c < 0) { |
| s++; |
| } else if (c == 1) { |
| outchar(*s++); |
| } else { |
| for (; c > 0; c--) { |
| (void) putchar(*s++); |
| } |
| } |
| } |
| } |
| |
| static void |
| outchar(int c) |
| { |
| unsigned char uc; |
| |
| uc = (unsigned char) c; |
| |
| if ((uc == '\\') || (uc == ';') || (uc == '"')) { |
| (void) putchar(escapec); |
| (void) putchar(uc); |
| } else if (iscntrl(uc)) { |
| (void) printf("%cx%02x", escapec, uc); |
| } else { |
| (void) putchar(uc); |
| } |
| } |
| |
| /* |
| * print_category(): Print out all the keyword's value |
| * in the given category |
| */ |
| static int |
| print_category(int category, int cflag, int kflag) |
| { |
| int i; |
| int retval = 0; |
| |
| if (category == LC_ALL) { |
| retval += print_category(LC_CTYPE, cflag, kflag); |
| retval += print_category(LC_NUMERIC, cflag, kflag); |
| retval += print_category(LC_TIME, cflag, kflag); |
| retval += print_category(LC_COLLATE, cflag, kflag); |
| retval += print_category(LC_MONETARY, cflag, kflag); |
| retval += print_category(LC_MESSAGES, cflag, kflag); |
| } else { |
| if (cflag) { |
| (void) printf("%s\n", |
| locale_name[category].name); |
| } |
| |
| for (i = 0; key[i].name != NULL; i++) { |
| if (key[i].category == category) { |
| retval += print_keyword(key[i].name, 0, kflag); |
| } |
| } |
| } |
| return (retval); |
| } |
| |
| /* |
| * usage message for locale |
| */ |
| static void |
| usage(void) |
| { |
| (void) fprintf(stderr, gettext( |
| "Usage: locale [-a|-m]\n" |
| " locale [-ck] name ...\n")); |
| exit(2); |
| } |
| |
| static void |
| prt_ctp(char *name) |
| { |
| int idx, i, mem; |
| int first = 1; |
| |
| static const char *reg_names[] = { |
| "upper", "lower", "alpha", "digit", "space", "cntrl", |
| "punct", "graph", "print", "xdigit", "blank", NULL |
| }; |
| for (idx = 0; reg_names[idx] != NULL; idx++) { |
| if (strcmp(name, reg_names[idx]) == 0) { |
| break; |
| } |
| } |
| if (reg_names[idx] == NULL) { |
| return; |
| } |
| |
| for (i = 0; i < CSSIZE; i++) { |
| mem = 0; |
| switch (idx) { |
| case 0: |
| mem = isupper(i); |
| break; |
| case 1: |
| mem = islower(i); |
| break; |
| case 2: |
| mem = isalpha(i); |
| break; |
| case 3: |
| mem = isdigit(i); |
| break; |
| case 4: |
| mem = isspace(i); |
| break; |
| case 5: |
| mem = iscntrl(i); |
| break; |
| case 6: |
| mem = ispunct(i); |
| break; |
| case 7: |
| mem = isgraph(i); |
| break; |
| case 8: |
| mem = isprint(i); |
| break; |
| case 9: |
| mem = isxdigit(i); |
| break; |
| case 10: |
| mem = isblank(i); |
| break; |
| } |
| if (mem) { |
| if (!first) { |
| (void) putchar(';'); |
| } |
| first = 0; |
| (void) printf("\""); |
| outchar(i); |
| (void) printf("\""); |
| } |
| } |
| } |
| |
| static void |
| prt_cnv(char *name) |
| { |
| int idx, i, q; |
| int first = 1; |
| |
| static const char *reg_names[] = { |
| "toupper", "tolower", NULL |
| }; |
| for (idx = 0; reg_names[idx] != NULL; idx++) { |
| if (strcmp(name, reg_names[idx]) == 0) { |
| break; |
| } |
| } |
| if (reg_names[idx] == NULL) { |
| return; |
| } |
| |
| for (i = 0; i < CSSIZE; i++) { |
| switch (idx) { |
| case 0: |
| q = toupper(i); |
| if (q == i) { |
| continue; |
| } |
| if (!first) { |
| (void) putchar(';'); |
| } |
| first = 0; |
| /* BEGIN CSTYLED */ |
| (void) printf("\"<'"); |
| /* END CSTYLED */ |
| outchar(i); |
| (void) printf("','"); |
| outchar(q); |
| (void) printf("'>\""); |
| break; |
| case 1: |
| q = tolower(i); |
| if (q == i) { |
| continue; |
| } |
| if (!first) { |
| (void) putchar(';'); |
| } |
| first = 0; |
| /* BEGIN CSTYLED */ |
| (void) printf("\"<'"); |
| /* END CSTYLED */ |
| outchar(i); |
| (void) printf("','"); |
| outchar(q); |
| (void) printf("'>\""); |
| break; |
| } |
| } |
| } |
| |
| /* |
| * prt_collel(): Stub for the collate class which does nothing. |
| */ |
| /* ARGSUSED */ |
| static void |
| prt_collel(char *name) |
| { |
| } |
| |
| static char |
| get_escapechar(void) |
| { |
| return ('\\'); |
| } |
| |
| static char |
| get_commentchar(void) |
| { |
| return ('#'); |
| } |