blob: dfd202954102d3cbaaaf29fd9a6f1e753ce0dd9d [file] [log] [blame]
/* 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);
}