blob: dfd202954102d3cbaaaf29fd9a6f1e753ce0dd9d [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/* vi: set sw=4 ts=4: */
2/*
3 *
4 * Copyright (c) 2008 STMicroelectronics Ltd
5 * Filippo Arcidiacono (filippo.arcidiacono@st.com)
6 *
7 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
8 *
9 * A 'locale' command implementation for uClibc.
10 *
11 */
12
13#include <string.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <langinfo.h>
17#include <unistd.h>
18#ifdef __UCLIBC_HAS_GETOPT_LONG__
19#include <getopt.h>
20#endif
21
22typedef struct {
23 unsigned char idx_name;
24 char dot_cs; /* 0 if no codeset specified */
25 char cs;
26 unsigned char lc_ctype_row;
27 unsigned char lc_numeric_row;
28 unsigned char lc_monetary_row;
29 unsigned char lc_time_row;
30 unsigned char lc_collate_row;
31 unsigned char lc_messages_row;
32} locale_entry;
33
34/* Need to include this before locale.h and xlocale.h! */
35#include <bits/uClibc_locale.h>
36
37#undef CODESET_LIST
38#define CODESET_LIST (__locale_mmap->codeset_list)
39#include <locale.h>
40#define LOCALE_NAMES (__locale_mmap->locale_names5)
41#define LOCALES (__locale_mmap->locales)
42#define LOCALE_AT_MODIFIERS (__locale_mmap->locale_at_modifiers)
43#define CATEGORY_NAMES (__locale_mmap->lc_names)
44
45#define GET_CODESET_NAME(N) (const char *)(CODESET_LIST + *(CODESET_LIST + N - 3))
46#define GET_LOCALE_ENTRY(R) (locale_entry *)(LOCALES + (__LOCALE_DATA_WIDTH_LOCALES * R))
47#define GET_CATEGORY_NAME(X) (CATEGORY_NAMES + *(CATEGORY_NAMES + X))
48#define GET_LOCALE_NAME(I) (const char *)(LOCALE_NAMES + 5 * (I - 1))
49
50static const char utf8[] = "UTF-8";
51static const char ascii[] = "ASCII";
52
53/* If set print the name of the category. */
54static int show_category_name = 0;
55
56/* If set print the name of the item. */
57static int show_keyword_name = 0;
58
59/* If set print the usage command. */
60static int show_usage = 0;
61
62/* Print names of all available locales. */
63static int do_all = 0;
64
65/* Print names of all available character maps. */
66static int do_charmaps = 0;
67
68static int remaining = 0;
69
70/* We can map the types of the entries into a few categories. */
71enum value_type {
72 none,
73 string,
74 stringarray,
75 byte,
76 bytearray,
77 word,
78 stringlist,
79 wordarray,
80 wstring,
81 wstringarray,
82 wstringlist
83};
84
85/* Definition of the data structure which represents a category and its
86 items. */
87struct category {
88 int cat_id;
89 const char *name;
90 size_t number;
91 struct cat_item {
92 int item_id;
93 const char *name;
94 enum { std, opt } status;
95 enum value_type value_type;
96 int min;
97 int max;
98 } *item_desc;
99};
100
101/* Simple helper macro. */
102#define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
103
104/* For some tricky stuff. */
105#define NO_PAREN(Item, More...) Item, ## More
106
107/* We have all categories defined in `categories.def'. Now construct
108 the description and data structure used for all categories. */
109#define DEFINE_ELEMENT(Item, More...) { Item, ## More },
110#define DEFINE_CATEGORY(category, name, items, postload) \
111 static struct cat_item category##_desc[] = \
112 { \
113 NO_PAREN items \
114 };
115
116#include "categories.def"
117#undef DEFINE_CATEGORY
118
119static struct category category[] = {
120#define DEFINE_CATEGORY(category, name, items, postload) \
121 [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \
122 category##_desc },
123#include "categories.def"
124#undef DEFINE_CATEGORY
125};
126
127#define NCATEGORIES NELEMS (category)
128
129static void usage(const char *name);
130static void usage(const char *name)
131{
132 const char *s;
133
134 s = basename(name);
135#ifdef __UCLIBC_HAS_GETOPT_LONG__
136 fprintf(stderr,
137 "Usage: %s [-a | -m] [FORMAT] name...\n\n"
138 "\t-a, --all-locales\tWrite names of all available locales\n"
139 "\t-m, --charmaps\tWrite names of available charmaps\n"
140 "\nFORMAT:\n"
141 "\t-c, --category-name\tWrite names of selected categories\n"
142 "\t-k, --keyword-name\tWrite names of selected keywords\n"
143 , s);
144#else
145 fprintf(stderr,
146 "Usage: %s [-a | -m] [FORMAT] name...\n\n"
147 "\t-a\tWrite names of all available locales\n"
148 "\t-m\tWrite names of available charmaps\n"
149 "\nFORMAT:\n"
150 "\t-c\tWrite names of selected categories\n"
151 "\t-k\tWrite names of selected keywords\n"
152 , s);
153#endif
154}
155
156static int argp_parse(int argc, char *argv[]);
157static int argp_parse(int argc, char *argv[])
158{
159 int c;
160 char *progname;
161#ifdef __UCLIBC_HAS_GETOPT_LONG__
162 static const struct option long_options[] = {
163 {"all-locales", no_argument, NULL, 'a'},
164 {"charmaps", no_argument, NULL, 'm'},
165 {"category-name", no_argument, NULL, 'c'},
166 {"keyword-name", no_argument, NULL, 'k'},
167 {"help", no_argument, NULL, 'h'},
168 {NULL, 0, NULL, 0}};
169#endif
170 progname = *argv;
171#ifdef __UCLIBC_HAS_GETOPT_LONG__
172 while ((c = getopt_long(argc, argv, "amckh", long_options, NULL)) >= 0)
173#else
174 while ((c = getopt(argc, argv, "amckh")) >= 0)
175#endif
176 switch (c) {
177 case 'a':
178 do_all = 1;
179 break;
180 case 'c':
181 show_category_name = 1;
182 break;
183 case 'm':
184 do_charmaps = 1;
185 break;
186 case 'k':
187 show_keyword_name = 1;
188 break;
189 case 'h':
190 show_usage = 1;
191 break;
192 case '?':
193 fprintf(stderr, "Unknown option.\n");
194 usage(progname);
195 return 1;
196
197 default:
198 fprintf(stderr, "This should never happen!\n");
199 return 1;
200 }
201
202 remaining = optind;
203
204 return 0;
205}
206
207static unsigned const char *find_at(char c);
208static unsigned const char *find_at(char c)
209{
210 const unsigned char *q;
211
212 q = LOCALE_AT_MODIFIERS;
213 do {
214 if (q[1] == c) {
215 return (unsigned const char *) q + 2;
216 }
217 q += 2 + *q;
218 } while (*q);
219
220 return NULL;
221}
222
223static void find_locale_string(locale_entry * loc_rec, char *loc)
224{
225 char at = 0;
226 unsigned char idx;
227 uint16_t dotcs, cs;
228
229 idx = loc_rec->idx_name;
230 if (!idx) {
231 *loc++ = 'C'; /* jump the first locale (C) */
232 *loc = '\0';
233 } else {
234 dotcs = (uint16_t) loc_rec->dot_cs;
235 cs = (uint16_t) loc_rec->cs;;
236 loc = strncpy(loc, GET_LOCALE_NAME(idx), 5);
237
238 if (loc[2] == '_') {
239 sprintf(loc, "%5.5s%c%s\0", loc, (dotcs != 0) ? '.' : ' ',
240 (cs == 1) ? ascii
241 : ((cs == 2) ?
242 utf8
243: GET_CODESET_NAME(cs)));
244 } else {
245 at = loc[2];
246 loc[2] = '_';
247 sprintf(loc, "%5.5s%c%s@%s\0", loc, (dotcs != 0) ? '.' : ' ',
248 (cs ==
249 1) ? ascii : ((cs == 2) ? utf8 : GET_CODESET_NAME(cs)),
250 find_at(at));
251 }
252 }
253}
254
255static void list_locale(void);
256static void list_locale()
257{
258 char loc[40];
259 uint16_t n = 0;
260 locale_entry *locales = (locale_entry *) LOCALES;
261
262 do {
263 find_locale_string(locales, loc);
264 printf("%s\n", loc);
265 ++n;
266 locales++;
267 } while (n < __LOCALE_DATA_NUM_LOCALES);
268}
269
270static void list_charmaps(void);
271static void list_charmaps()
272{
273 unsigned const char *cl;
274
275 cl = CODESET_LIST;
276 do {
277 printf("%s\n", CODESET_LIST + *cl);
278 } while (*++cl);
279
280}
281
282static void print_item(struct cat_item *item);
283static void print_item(struct cat_item *item)
284{
285 switch (item->value_type) {
286 case string:
287 if (show_keyword_name)
288 printf("%s=\"", item->name);
289 fputs(nl_langinfo(item->item_id) ? : "", stdout);
290 if (show_keyword_name)
291 putchar('"');
292 putchar('\n');
293 break;
294 case stringarray:
295 {
296 int cnt;
297 const char *val;
298
299 if (show_keyword_name)
300 printf("%s=\"", item->name);
301
302 for (cnt = 0; cnt < item->max - 1; ++cnt) {
303 val = nl_langinfo(item->item_id + cnt);
304 if (val != NULL)
305 fputs(val, stdout);
306 putchar(';');
307 }
308
309 val = nl_langinfo(item->item_id + cnt);
310 if (val != NULL)
311 fputs(val, stdout);
312
313 if (show_keyword_name)
314 putchar('"');
315 putchar('\n');
316 }
317 break;
318 case stringlist:
319 {
320 int first = 1;
321 const char *val = nl_langinfo(item->item_id) ? : "";
322 int cnt;
323
324 if (show_keyword_name)
325 printf("%s=", item->name);
326
327 for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt) {
328 printf("%s%s%s%s", first ? "" : ";",
329 show_keyword_name ? "\"" : "", val,
330 show_keyword_name ? "\"" : "");
331 val = strchr(val, '\0') + 1;
332 first = 0;
333 }
334 putchar('\n');
335 }
336 break;
337 case byte:
338 {
339 const char *val = nl_langinfo(item->item_id);
340
341 if (show_keyword_name)
342 printf("%s=", item->name);
343
344 if (val != NULL)
345 printf("%d", *val == '\177' ? -1 : *val);
346 putchar('\n');
347 }
348 break;
349 case bytearray:
350 {
351 const char *val = nl_langinfo(item->item_id);
352 int cnt = val ? strlen(val) : 0;
353
354 if (show_keyword_name)
355 printf("%s=", item->name);
356
357 while (cnt > 1) {
358 printf("%d;", *val == '\177' ? -1 : *val);
359 --cnt;
360 ++val;
361 }
362
363 printf("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
364 }
365 break;
366 case word:
367 {
368 union {
369 unsigned int word;
370 char *string;
371 } val;
372
373 val.string = nl_langinfo(item->item_id);
374 if (show_keyword_name)
375 printf("%s=", item->name);
376
377 printf("%d\n", val.word);
378 }
379 break;
380 case wstring:
381 case wstringarray:
382 case wstringlist:
383 /* We don't print wide character information since the same
384 information is available in a multibyte string. */
385 default:
386 break;
387
388 }
389}
390
391/* Show the information request for NAME. */
392static void show_info(const char *name);
393static void show_info(const char *name)
394{
395 size_t cat_no, item_no;
396 const unsigned char *cat_name;
397
398 /* Now all categories in an unspecified order. */
399 for (cat_no = 0; cat_no < __LC_ALL; ++cat_no) {
400 cat_name = GET_CATEGORY_NAME(cat_no);
401 if (strcmp(name, (const char *) cat_name) == 0) {
402 if (show_category_name)
403 printf("%s\n", name);
404
405 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
406 print_item(&category[cat_no].item_desc[item_no]);
407
408 return;
409 }
410
411 for (item_no = 0; item_no < category[cat_no].number; ++item_no)
412 if (strcmp(name, category[cat_no].item_desc[item_no].name) == 0) {
413 if (show_category_name != 0)
414 puts(category[cat_no].name);
415
416 print_item(&category[cat_no].item_desc[item_no]);
417 return;
418 }
419 }
420}
421
422static void show_locale_vars(void);
423static void show_locale_vars()
424{
425 size_t cat_no;
426 int row; /* locale row */
427 const char *lcall = getenv("LC_ALL");
428 const char *lang = getenv("LANG") ? : "";
429 unsigned char *cur_loc = __global_locale->cur_locale + 1;
430 char loc_name[40];
431 locale_entry *locales;
432
433 /* LANG has to be the first value. */
434 printf("LANG=%s\n", lang);
435
436 /* Now all categories in an unspecified order. */
437 for (cat_no = 0; cat_no < __LC_ALL; ++cat_no) {
438 row = (((int) (*cur_loc & 0x7f)) << 7) + (cur_loc[1] & 0x7f);
439/* assert(row < __LOCALE_DATA_NUM_LOCALES); */
440
441 locales = GET_LOCALE_ENTRY(row);
442 find_locale_string(locales, loc_name);
443 printf("%s=%s\n", GET_CATEGORY_NAME(cat_no), loc_name);
444
445 cur_loc += 2;
446 }
447
448 /* The last is the LC_ALL value. */
449 printf("LC_ALL=%s\n", lcall ? : "");
450}
451
452int main(int argc, char *argv[])
453{
454 /* Parse and process arguments. */
455 if (argp_parse(argc, argv))
456 return 1;
457
458 if (do_all) {
459 list_locale();
460 exit(EXIT_SUCCESS);
461 }
462
463 if (do_charmaps) {
464 list_charmaps();
465 exit(EXIT_SUCCESS);
466 }
467
468 if (show_usage) {
469 usage(*argv);
470 exit(EXIT_SUCCESS);
471 }
472
473 /* If no real argument is given we have to print the contents of the
474 current locale definition variables. These are LANG and the LC_*. */
475 if (remaining == argc && show_category_name == 0
476 && show_keyword_name == 0) {
477 show_locale_vars();
478 exit(EXIT_SUCCESS);
479 }
480
481 /* Process all given names. */
482 while (remaining < argc)
483 show_info(argv[remaining++]);
484
485 exit(EXIT_SUCCESS);
486}