/* * * Copyright (c) 2008 STMicroelectronics Ltd * Filippo Arcidiacono (filippo.arcidiacono@st.com) * * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. * * A 'locale' command implementation for uClibc. * */ #include #include #include #include #include #ifdef __UCLIBC_HAS_GETOPT_LONG__ #include #endif typedef struct { unsigned char idx_name; char dot_cs; /* 0 if no codeset specified */ char cs; unsigned char lc_ctype_row; unsigned char lc_numeric_row; unsigned char lc_monetary_row; unsigned char lc_time_row; unsigned char lc_collate_row; unsigned char lc_messages_row; } locale_entry; /* Need to include this before locale.h! */ #include #undef CODESET_LIST #define CODESET_LIST (__locale_mmap->codeset_list) #include #define LOCALE_NAMES (__locale_mmap->locale_names5) #define LOCALES (__locale_mmap->locales) #define LOCALE_AT_MODIFIERS (__locale_mmap->locale_at_modifiers) #define CATEGORY_NAMES (__locale_mmap->lc_names) #define GET_CODESET_NAME(N) (const char *)(CODESET_LIST + *(CODESET_LIST + N - 3)) #define GET_LOCALE_ENTRY(R) (locale_entry *)(LOCALES + (__LOCALE_DATA_WIDTH_LOCALES * R)) #define GET_CATEGORY_NAME(X) (CATEGORY_NAMES + *(CATEGORY_NAMES + X)) #define GET_LOCALE_NAME(I) (const char *)(LOCALE_NAMES + 5 * (I - 1)) static const char utf8[] = "UTF-8"; static const char ascii[] = "ASCII"; /* If set print the name of the category. */ static int show_category_name = 0; /* If set print the name of the item. */ static int show_keyword_name = 0; /* If set print the usage command. */ static int show_usage = 0; /* Print names of all available locales. */ static int do_all = 0; /* Print names of all available character maps. */ static int do_charmaps = 0; static int remaining = 0; /* We can map the types of the entries into a few categories. */ enum value_type { none, string, stringarray, byte, bytearray, word, stringlist, wordarray, wstring, wstringarray, wstringlist }; /* Definition of the data structure which represents a category and its items. */ struct category { int cat_id; const char *name; size_t number; struct cat_item { int item_id; const char *name; enum { std, opt } status; enum value_type value_type; int min; int max; } *item_desc; }; /* Simple helper macro. */ #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0]))) /* For some tricky stuff. */ #define NO_PAREN(Item, More...) Item, ## More /* We have all categories defined in `categories.def'. Now construct the description and data structure used for all categories. */ #define DEFINE_ELEMENT(Item, More...) { Item, ## More }, #define DEFINE_CATEGORY(category, name, items, postload) \ static struct cat_item category##_desc[] = \ { \ NO_PAREN items \ }; #include "categories.def" #undef DEFINE_CATEGORY static struct category category[] = { #define DEFINE_CATEGORY(category, name, items, postload) \ [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \ category##_desc }, #include "categories.def" #undef DEFINE_CATEGORY }; #define NCATEGORIES NELEMS (category) static void usage(const char *name); static void usage(const char *name) { const char *s; s = basename(name); #ifdef __UCLIBC_HAS_GETOPT_LONG__ fprintf(stderr, "Usage: %s [-a | -m] [FORMAT] name...\n\n" "\t-a, --all-locales\tWrite names of all available locales\n" "\t-m, --charmaps\tWrite names of available charmaps\n" "\nFORMAT:\n" "\t-c, --category-name\tWrite names of selected categories\n" "\t-k, --keyword-name\tWrite names of selected keywords\n" , s); #else fprintf(stderr, "Usage: %s [-a | -m] [FORMAT] name...\n\n" "\t-a\tWrite names of all available locales\n" "\t-m\tWrite names of available charmaps\n" "\nFORMAT:\n" "\t-c\tWrite names of selected categories\n" "\t-k\tWrite names of selected keywords\n" , s); #endif } static int argp_parse(int argc, char *argv[]); static int argp_parse(int argc, char *argv[]) { int c; char *progname; #ifdef __UCLIBC_HAS_GETOPT_LONG__ static const struct option long_options[] = { {"all-locales", no_argument, NULL, 'a'}, {"charmaps", no_argument, NULL, 'm'}, {"category-name", no_argument, NULL, 'c'}, {"keyword-name", no_argument, NULL, 'k'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}}; #endif progname = *argv; #ifdef __UCLIBC_HAS_GETOPT_LONG__ while ((c = getopt_long(argc, argv, "amckh", long_options, NULL)) >= 0) #else while ((c = getopt(argc, argv, "amckh")) >= 0) #endif switch (c) { case 'a': do_all = 1; break; case 'c': show_category_name = 1; break; case 'm': do_charmaps = 1; break; case 'k': show_keyword_name = 1; break; case 'h': show_usage = 1; break; case '?': fprintf(stderr, "Unknown option.\n"); usage(progname); return 1; default: fprintf(stderr, "This should never happen!\n"); return 1; } remaining = optind; return 0; } static unsigned const char *find_at(char c); static unsigned const char *find_at(char c) { const unsigned char *q; q = LOCALE_AT_MODIFIERS; do { if (q[1] == c) { return (unsigned const char *) q + 2; } q += 2 + *q; } while (*q); return NULL; } static void find_locale_string(locale_entry * loc_rec, char *loc) { char at = 0; unsigned char idx; uint16_t dotcs, cs; idx = loc_rec->idx_name; if (!idx) { *loc++ = 'C'; /* jump the first locale (C) */ *loc = '\0'; } else { dotcs = (uint16_t) loc_rec->dot_cs; cs = (uint16_t) loc_rec->cs;; loc = strncpy(loc, GET_LOCALE_NAME(idx), 5); if (loc[2] == '_') { sprintf(loc, "%5.5s%c%s\0", loc, (dotcs != 0) ? '.' : ' ', (cs == 1) ? ascii : ((cs == 2) ? utf8 : GET_CODESET_NAME(cs))); } else { at = loc[2]; loc[2] = '_'; sprintf(loc, "%5.5s%c%s@%s\0", loc, (dotcs != 0) ? '.' : ' ', (cs == 1) ? ascii : ((cs == 2) ? utf8 : GET_CODESET_NAME(cs)), find_at(at)); } } } static void list_locale(void); static void list_locale() { char loc[40]; uint16_t n = 0; locale_entry *locales = (locale_entry *) LOCALES; do { find_locale_string(locales, loc); printf("%s\n", loc); ++n; locales++; } while (n < __LOCALE_DATA_NUM_LOCALES); } static void list_charmaps(void); static void list_charmaps() { unsigned const char *cl; cl = CODESET_LIST; do { printf("%s\n", CODESET_LIST + *cl); } while (*++cl); } static void print_item(struct cat_item *item); static void print_item(struct cat_item *item) { switch (item->value_type) { case string: if (show_keyword_name) printf("%s=\"", item->name); fputs(nl_langinfo(item->item_id) ? : "", stdout); if (show_keyword_name) putchar('"'); putchar('\n'); break; case stringarray: { int cnt; const char *val; if (show_keyword_name) printf("%s=\"", item->name); for (cnt = 0; cnt < item->max - 1; ++cnt) { val = nl_langinfo(item->item_id + cnt); if (val != NULL) fputs(val, stdout); putchar(';'); } val = nl_langinfo(item->item_id + cnt); if (val != NULL) fputs(val, stdout); if (show_keyword_name) putchar('"'); putchar('\n'); } break; case stringlist: { int first = 1; const char *val = nl_langinfo(item->item_id) ? : ""; int cnt; if (show_keyword_name) printf("%s=", item->name); for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt) { printf("%s%s%s%s", first ? "" : ";", show_keyword_name ? "\"" : "", val, show_keyword_name ? "\"" : ""); val = strchr(val, '\0') + 1; first = 0; } putchar('\n'); } break; case byte: { const char *val = nl_langinfo(item->item_id); if (show_keyword_name) printf("%s=", item->name); if (val != NULL) printf("%d", *val == '\177' ? -1 : *val); putchar('\n'); } break; case bytearray: { const char *val = nl_langinfo(item->item_id); int cnt = val ? strlen(val) : 0; if (show_keyword_name) printf("%s=", item->name); while (cnt > 1) { printf("%d;", *val == '\177' ? -1 : *val); --cnt; ++val; } printf("%d\n", cnt == 0 || *val == '\177' ? -1 : *val); } break; case word: { union { unsigned int word; char *string; } val; val.string = nl_langinfo(item->item_id); if (show_keyword_name) printf("%s=", item->name); printf("%d\n", val.word); } break; case wstring: case wstringarray: case wstringlist: /* We don't print wide character information since the same information is available in a multibyte string. */ default: break; } } /* Show the information request for NAME. */ static void show_info(const char *name); static void show_info(const char *name) { size_t cat_no, item_no; const unsigned char *cat_name; /* Now all categories in an unspecified order. */ for (cat_no = 0; cat_no < __LC_ALL; ++cat_no) { cat_name = GET_CATEGORY_NAME(cat_no); if (strcmp(name, (const char *) cat_name) == 0) { if (show_category_name) printf("%s\n", name); for (item_no = 0; item_no < category[cat_no].number; ++item_no) print_item(&category[cat_no].item_desc[item_no]); return; } for (item_no = 0; item_no < category[cat_no].number; ++item_no) if (strcmp(name, category[cat_no].item_desc[item_no].name) == 0) { if (show_category_name != 0) puts(category[cat_no].name); print_item(&category[cat_no].item_desc[item_no]); return; } } } static void show_locale_vars(void); static void show_locale_vars() { size_t cat_no; int row; /* locale row */ const char *lcall = getenv("LC_ALL"); const char *lang = getenv("LANG") ? : ""; unsigned char *cur_loc = __global_locale->cur_locale + 1; char loc_name[40]; locale_entry *locales; /* LANG has to be the first value. */ printf("LANG=%s\n", lang); /* Now all categories in an unspecified order. */ for (cat_no = 0; cat_no < __LC_ALL; ++cat_no) { row = (((int) (*cur_loc & 0x7f)) << 7) + (cur_loc[1] & 0x7f); /* assert(row < __LOCALE_DATA_NUM_LOCALES); */ locales = GET_LOCALE_ENTRY(row); find_locale_string(locales, loc_name); printf("%s=%s\n", GET_CATEGORY_NAME(cat_no), loc_name); cur_loc += 2; } /* The last is the LC_ALL value. */ printf("LC_ALL=%s\n", lcall ? : ""); } int main(int argc, char *argv[]) { /* Parse and process arguments. */ if (argp_parse(argc, argv)) return 1; if (do_all) { list_locale(); exit(EXIT_SUCCESS); } if (do_charmaps) { list_charmaps(); exit(EXIT_SUCCESS); } if (show_usage) { usage(*argv); exit(EXIT_SUCCESS); } /* If no real argument is given we have to print the contents of the current locale definition variables. These are LANG and the LC_*. */ if (remaining == argc && show_category_name == 0 && show_keyword_name == 0) { show_locale_vars(); exit(EXIT_SUCCESS); } /* Process all given names. */ while (remaining < argc) show_info(argv[remaining++]); exit(EXIT_SUCCESS); }