| /* vi: set sw=4 ts=4: */ |
| /* |
| * |
| * 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 <string.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <langinfo.h> |
| #include <unistd.h> |
| #ifdef __UCLIBC_HAS_GETOPT_LONG__ |
| #include <getopt.h> |
| #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 and xlocale.h! */ |
| #include <bits/uClibc_locale.h> |
| |
| #undef CODESET_LIST |
| #define CODESET_LIST (__locale_mmap->codeset_list) |
| #include <locale.h> |
| #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); |
| } |