/* * 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. * */ /* * 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 . */ #define _LCONV_C99 #include #include #include #include #include #include #include #include #include #include #include #include #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 ('#'); }