Mercurial > illumos > onarm
diff usr/src/cmd/locale/locale.c @ 0:c9caec207d52 b86
Initial porting based on b86
author | Koji Uno <koji.uno@sun.com> |
---|---|
date | Tue, 02 Jun 2009 18:56:50 +0900 |
parents | |
children | 1a15d5aaf794 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/locale/locale.c Tue Jun 02 18:56:50 2009 +0900 @@ -0,0 +1,1171 @@ +/* + * 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 "@(#)locale.c 1.13 05/06/08 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 ('#'); +}