]>
Commit | Line | Data |
---|---|---|
5107cf1d | 1 | /* Implementation of the locale program according to POSIX 9945-2. |
7ef90c15 | 2 | Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. |
6d52618b UD |
3 | This file is part of the GNU C Library. |
4 | Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. | |
2b83a2a4 | 5 | |
6d52618b UD |
6 | The GNU C Library is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU Library General Public License as | |
8 | published by the Free Software Foundation; either version 2 of the | |
9 | License, or (at your option) any later version. | |
2b83a2a4 | 10 | |
6d52618b UD |
11 | The GNU C Library is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | Library General Public License for more details. | |
2b83a2a4 | 15 | |
6d52618b UD |
16 | You should have received a copy of the GNU Library General Public |
17 | License along with the GNU C Library; see the file COPYING.LIB. If not, | |
18 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
19 | Boston, MA 02111-1307, USA. */ | |
2b83a2a4 | 20 | |
299a95b9 RM |
21 | #ifdef HAVE_CONFIG_H |
22 | # include <config.h> | |
23 | #endif | |
24 | ||
1fb05e3d UD |
25 | #include <argp.h> |
26 | #include <argz.h> | |
2b83a2a4 | 27 | #include <dirent.h> |
1fb05e3d | 28 | #include <errno.h> |
299a95b9 | 29 | #include <error.h> |
2b83a2a4 RM |
30 | #include <langinfo.h> |
31 | #include <libintl.h> | |
32 | #include <limits.h> | |
33 | #include <locale.h> | |
1fb05e3d | 34 | #include <search.h> |
2b83a2a4 RM |
35 | #include <stdio.h> |
36 | #include <stdlib.h> | |
a1470b6f | 37 | #include <string.h> |
1fb05e3d UD |
38 | #include <unistd.h> |
39 | #include <sys/stat.h> | |
2b83a2a4 | 40 | |
19bc17a9 | 41 | #include "localeinfo.h" |
2b83a2a4 RM |
42 | |
43 | ||
2b83a2a4 RM |
44 | /* If set print the name of the category. */ |
45 | static int show_category_name; | |
46 | ||
47 | /* If set print the name of the item. */ | |
48 | static int show_keyword_name; | |
49 | ||
1fb05e3d UD |
50 | /* Print names of all available locales. */ |
51 | static int do_all; | |
52 | ||
53 | /* Print names of all available character maps. */ | |
54 | static int do_charmaps = 0; | |
55 | ||
56 | /* Name and version of program. */ | |
57 | static void print_version (FILE *stream, struct argp_state *state); | |
58 | void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; | |
59 | ||
60 | /* Definitions of arguments for argp functions. */ | |
5a97622d | 61 | static const struct argp_option options[] = |
19bc17a9 | 62 | { |
5a97622d UD |
63 | { NULL, 0, NULL, 0, N_("System information:") }, |
64 | { "all-locales", 'a', NULL, OPTION_NO_USAGE, | |
65 | N_("Write names of available locales") }, | |
66 | { "charmaps", 'm', NULL, OPTION_NO_USAGE, | |
67 | N_("Write names of available charmaps") }, | |
68 | { NULL, 0, NULL, 0, N_("Modify output format:") }, | |
69 | { "category-name", 'c', NULL, 0, N_("Write names of selected categories") }, | |
70 | { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") }, | |
1fb05e3d UD |
71 | { NULL, 0, NULL, 0, NULL } |
72 | }; | |
73 | ||
74 | /* Short description of program. */ | |
5a97622d | 75 | static const char doc[] = N_("Get locale-specific information."); |
1fb05e3d UD |
76 | |
77 | /* Strings for arguments in help texts. */ | |
5a97622d | 78 | static const char args_doc[] = N_("NAME\n[-a|-m]"); |
1fb05e3d UD |
79 | |
80 | /* Prototype for option handler. */ | |
81 | static error_t parse_opt (int key, char *arg, struct argp_state *state); | |
82 | ||
83 | /* Function to print some extra text in the help message. */ | |
84 | static char *more_help (int key, const char *text, void *input); | |
85 | ||
86 | /* Data structure to communicate with argp functions. */ | |
87 | static struct argp argp = | |
88 | { | |
89 | options, parse_opt, args_doc, doc, NULL, more_help | |
19bc17a9 | 90 | }; |
2b83a2a4 RM |
91 | |
92 | ||
93 | /* We don't have these constants defined because we don't use them. Give | |
94 | default values. */ | |
95 | #define CTYPE_MB_CUR_MIN 0 | |
96 | #define CTYPE_MB_CUR_MAX 0 | |
97 | #define CTYPE_HASH_SIZE 0 | |
98 | #define CTYPE_HASH_LAYERS 0 | |
99 | #define CTYPE_CLASS 0 | |
100 | #define CTYPE_TOUPPER_EB 0 | |
101 | #define CTYPE_TOLOWER_EB 0 | |
102 | #define CTYPE_TOUPPER_EL 0 | |
103 | #define CTYPE_TOLOWER_EL 0 | |
19bc17a9 | 104 | |
299a95b9 RM |
105 | /* Definition of the data structure which represents a category and its |
106 | items. */ | |
107 | struct category | |
19bc17a9 | 108 | { |
299a95b9 | 109 | int cat_id; |
19bc17a9 | 110 | const char *name; |
299a95b9 RM |
111 | size_t number; |
112 | struct cat_item | |
113 | { | |
114 | int item_id; | |
115 | const char *name; | |
116 | enum { std, opt } status; | |
117 | enum value_type value_type; | |
118 | int min; | |
119 | int max; | |
120 | } *item_desc; | |
19bc17a9 RM |
121 | }; |
122 | ||
299a95b9 RM |
123 | /* Simple helper macro. */ |
124 | #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0]))) | |
125 | ||
126 | /* For some tricky stuff. */ | |
127 | #define NO_PAREN(Item, More...) Item, ## More | |
2b83a2a4 RM |
128 | |
129 | /* We have all categories defined in `categories.def'. Now construct | |
130 | the description and data structure used for all categories. */ | |
299a95b9 | 131 | #define DEFINE_ELEMENT(Item, More...) { Item, ## More }, |
2b83a2a4 RM |
132 | #define DEFINE_CATEGORY(category, name, items, postload, in, check, out) \ |
133 | static struct cat_item category##_desc[] = \ | |
134 | { \ | |
135 | NO_PAREN items \ | |
136 | }; | |
137 | ||
299a95b9 | 138 | #include "categories.def" |
2b83a2a4 RM |
139 | #undef DEFINE_CATEGORY |
140 | ||
141 | static struct category category[] = | |
142 | { | |
143 | #define DEFINE_CATEGORY(category, name, items, postload, in, check, out) \ | |
036cc82f RM |
144 | [category] = { _NL_NUM_##category, name, NELEMS (category##_desc), \ |
145 | category##_desc }, | |
299a95b9 | 146 | #include "categories.def" |
2b83a2a4 RM |
147 | #undef DEFINE_CATEGORY |
148 | }; | |
149 | #define NCATEGORIES NELEMS (category) | |
150 | ||
151 | ||
036cc82f RM |
152 | /* Automatically set variable. */ |
153 | extern const char *__progname; | |
154 | ||
155 | /* helper function for extended name handling. */ | |
156 | extern void locale_special (const char *name, int show_category_name, | |
157 | int show_keyword_name); | |
158 | ||
2b83a2a4 | 159 | /* Prototypes for local functions. */ |
2b83a2a4 RM |
160 | static void write_locales (void); |
161 | static void write_charmaps (void); | |
162 | static void show_locale_vars (void); | |
163 | static void show_info (const char *name); | |
2b83a2a4 RM |
164 | |
165 | ||
166 | int | |
167 | main (int argc, char *argv[]) | |
168 | { | |
2f6d1f1b UD |
169 | int remaining; |
170 | ||
bba7bb78 | 171 | /* Set initial values for global variables. */ |
2b83a2a4 RM |
172 | show_category_name = 0; |
173 | show_keyword_name = 0; | |
174 | ||
175 | /* Set locale. Do not set LC_ALL because the other categories must | |
6d52618b | 176 | not be affected (according to POSIX.2). */ |
2b83a2a4 RM |
177 | setlocale (LC_CTYPE, ""); |
178 | setlocale (LC_MESSAGES, ""); | |
179 | ||
180 | /* Initialize the message catalog. */ | |
181 | textdomain (PACKAGE); | |
182 | ||
1fb05e3d | 183 | /* Parse and process arguments. */ |
2f6d1f1b | 184 | argp_parse (&argp, argc, argv, 0, &remaining, NULL); |
2b83a2a4 | 185 | |
2b83a2a4 RM |
186 | /* `-a' requests the names of all available locales. */ |
187 | if (do_all != 0) | |
188 | { | |
1fb05e3d | 189 | setlocale (LC_COLLATE, ""); |
2b83a2a4 RM |
190 | write_locales (); |
191 | exit (EXIT_SUCCESS); | |
192 | } | |
193 | ||
194 | /* `m' requests the names of all available charmaps. The names can be | |
8f2ece69 | 195 | used for the -f argument to localedef(1). */ |
2b83a2a4 RM |
196 | if (do_charmaps != 0) |
197 | { | |
198 | write_charmaps (); | |
199 | exit (EXIT_SUCCESS); | |
200 | } | |
201 | ||
76060ec0 RM |
202 | /* Specific information about the current locale are requested. |
203 | Change to this locale now. */ | |
204 | setlocale (LC_ALL, ""); | |
205 | ||
2b83a2a4 RM |
206 | /* If no real argument is given we have to print the contents of the |
207 | current locale definition variables. These are LANG and the LC_*. */ | |
2f6d1f1b | 208 | if (remaining == argc && show_keyword_name == 0 && show_category_name == 0) |
2b83a2a4 RM |
209 | { |
210 | show_locale_vars (); | |
211 | exit (EXIT_SUCCESS); | |
212 | } | |
213 | ||
214 | /* Process all given names. */ | |
2f6d1f1b UD |
215 | while (remaining < argc) |
216 | show_info (argv[remaining++]); | |
2b83a2a4 RM |
217 | |
218 | exit (EXIT_SUCCESS); | |
219 | } | |
220 | ||
221 | ||
1fb05e3d UD |
222 | /* Handle program arguments. */ |
223 | static error_t | |
224 | parse_opt (int key, char *arg, struct argp_state *state) | |
2b83a2a4 | 225 | { |
1fb05e3d | 226 | switch (key) |
fafaa44e | 227 | { |
1fb05e3d UD |
228 | case 'a': |
229 | do_all = 1; | |
230 | break; | |
231 | case 'c': | |
232 | show_category_name = 1; | |
233 | break; | |
234 | case 'm': | |
235 | do_charmaps = 1; | |
236 | break; | |
237 | case 'k': | |
238 | show_keyword_name = 1; | |
239 | break; | |
240 | default: | |
241 | return ARGP_ERR_UNKNOWN; | |
fafaa44e | 242 | } |
1fb05e3d UD |
243 | return 0; |
244 | } | |
245 | ||
2b83a2a4 | 246 | |
1fb05e3d UD |
247 | static char * |
248 | more_help (int key, const char *text, void *input) | |
249 | { | |
250 | switch (key) | |
251 | { | |
252 | case ARGP_KEY_HELP_EXTRA: | |
253 | /* We print some extra information. */ | |
254 | return strdup (gettext ("\ | |
f2ea0f5b | 255 | Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n")); |
1fb05e3d UD |
256 | default: |
257 | break; | |
258 | } | |
259 | return (char *) text; | |
260 | } | |
261 | ||
262 | /* Print the version information. */ | |
263 | static void | |
264 | print_version (FILE *stream, struct argp_state *state) | |
265 | { | |
266 | fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION); | |
267 | fprintf (stream, gettext ("\ | |
268 | Copyright (C) %s Free Software Foundation, Inc.\n\ | |
269 | This is free software; see the source for copying conditions. There is NO\n\ | |
270 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ | |
7ef90c15 | 271 | "), "1995, 1996, 1997, 1998"); |
1fb05e3d | 272 | fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); |
2b83a2a4 RM |
273 | } |
274 | ||
275 | ||
1fb05e3d UD |
276 | /* Simple action function which prints arguments as strings. */ |
277 | static void | |
278 | print_names (const void *nodep, VISIT value, int level) | |
279 | { | |
280 | if (value == postorder || value == leaf) | |
281 | puts (*(char **) nodep); | |
282 | } | |
283 | ||
284 | ||
285 | /* Write the names of all available locales to stdout. We have some | |
286 | sources of the information: the contents of the locale directory | |
287 | and the locale.alias file. To avoid duplicates and print the | |
288 | result is a reasonable order we put all entries is a search tree | |
289 | and print them afterwards. */ | |
2b83a2a4 RM |
290 | static void |
291 | write_locales (void) | |
292 | { | |
1fb05e3d | 293 | void *all_data = NULL; |
2b83a2a4 RM |
294 | DIR *dir; |
295 | struct dirent *dirent; | |
1fb05e3d UD |
296 | char *alias_path; |
297 | size_t alias_path_len; | |
298 | char *entry; | |
2b83a2a4 | 299 | |
1fb05e3d UD |
300 | #define PUT(name) tsearch ((name), &all_data, \ |
301 | (int (*) (const void *, const void *)) strcoll) | |
2b83a2a4 | 302 | |
1fb05e3d | 303 | dir = opendir (LOCALEDIR); |
2b83a2a4 RM |
304 | if (dir == NULL) |
305 | { | |
306 | error (1, errno, gettext ("cannot read locale directory `%s'"), | |
1fb05e3d | 307 | LOCALEDIR); |
2b83a2a4 RM |
308 | return; |
309 | } | |
310 | ||
1fb05e3d UD |
311 | /* `POSIX' locale is always available (POSIX.2 4.34.3). */ |
312 | PUT ("POSIX"); | |
313 | /* And so is the "C" locale. */ | |
314 | PUT ("C"); | |
315 | ||
2b83a2a4 RM |
316 | /* Now we can look for all files in the directory. */ |
317 | while ((dirent = readdir (dir)) != NULL) | |
318 | if (strcmp (dirent->d_name, ".") != 0 | |
319 | && strcmp (dirent->d_name, "..") != 0) | |
1fb05e3d UD |
320 | { |
321 | mode_t mode; | |
322 | #ifdef _DIRENT_HAVE_D_TYPE | |
323 | if (dirent->d_type != DT_UNKNOWN) | |
324 | mode = DTTOIF (dirent->d_type); | |
325 | else | |
326 | #endif | |
327 | { | |
328 | struct stat st; | |
329 | char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1]; | |
330 | ||
331 | stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name); | |
332 | ||
333 | if (stat (buf, &st) < 0) | |
334 | continue; | |
335 | mode = st.st_mode; | |
336 | } | |
337 | ||
338 | if (S_ISDIR (mode)) | |
339 | PUT (strdup (dirent->d_name)); | |
340 | } | |
2b83a2a4 RM |
341 | |
342 | closedir (dir); | |
1fb05e3d UD |
343 | |
344 | /* Now read the locale.alias files. */ | |
345 | if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len)) | |
346 | error (1, errno, gettext ("while preparing output")); | |
347 | ||
348 | entry = NULL; | |
349 | while ((entry = argz_next (alias_path, alias_path_len, entry))) | |
350 | { | |
351 | static const char aliasfile[] = "/locale.alias"; | |
352 | FILE *fp; | |
353 | char full_name[strlen (entry) + sizeof aliasfile]; | |
354 | ||
355 | stpcpy (stpcpy (full_name, entry), aliasfile); | |
356 | fp = fopen (full_name, "r"); | |
357 | if (fp == NULL) | |
358 | /* Ignore non-existing files. */ | |
359 | continue; | |
360 | ||
361 | while (! feof (fp)) | |
362 | { | |
363 | /* It is a reasonable approach to use a fix buffer here | |
364 | because | |
365 | a) we are only interested in the first two fields | |
366 | b) these fields must be usable as file names and so must | |
367 | not be that long */ | |
368 | char buf[BUFSIZ]; | |
369 | char *alias; | |
370 | char *value; | |
371 | char *cp; | |
372 | ||
373 | if (fgets (buf, BUFSIZ, fp) == NULL) | |
374 | /* EOF reached. */ | |
375 | break; | |
376 | ||
377 | cp = buf; | |
378 | /* Ignore leading white space. */ | |
379 | while (isspace (cp[0])) | |
380 | ++cp; | |
381 | ||
382 | /* A leading '#' signals a comment line. */ | |
383 | if (cp[0] != '\0' && cp[0] != '#') | |
384 | { | |
385 | alias = cp++; | |
386 | while (cp[0] != '\0' && !isspace (cp[0])) | |
387 | ++cp; | |
388 | /* Terminate alias name. */ | |
389 | if (cp[0] != '\0') | |
390 | *cp++ = '\0'; | |
391 | ||
392 | /* Now look for the beginning of the value. */ | |
393 | while (isspace (cp[0])) | |
394 | ++cp; | |
395 | ||
396 | if (cp[0] != '\0') | |
397 | { | |
398 | value = cp++; | |
399 | while (cp[0] != '\0' && !isspace (cp[0])) | |
400 | ++cp; | |
401 | /* Terminate value. */ | |
402 | if (cp[0] == '\n') | |
403 | { | |
404 | /* This has to be done to make the following | |
405 | test for the end of line possible. We are | |
406 | looking for the terminating '\n' which do not | |
407 | overwrite here. */ | |
408 | *cp++ = '\0'; | |
409 | *cp = '\n'; | |
410 | } | |
411 | else if (cp[0] != '\0') | |
412 | *cp++ = '\0'; | |
413 | ||
414 | /* Add the alias. */ | |
415 | PUT (strdup (alias)); | |
416 | } | |
417 | } | |
418 | ||
419 | /* Possibly not the whole line fits into the buffer. | |
420 | Ignore the rest of the line. */ | |
421 | while (strchr (cp, '\n') == NULL) | |
422 | { | |
423 | cp = buf; | |
424 | if (fgets (buf, BUFSIZ, fp) == NULL) | |
425 | /* Make sure the inner loop will be left. The outer | |
426 | loop will exit at the `feof' test. */ | |
427 | *cp = '\n'; | |
428 | } | |
429 | } | |
430 | ||
431 | fclose (fp); | |
432 | } | |
433 | ||
434 | twalk (all_data, print_names); | |
2b83a2a4 RM |
435 | } |
436 | ||
437 | ||
438 | /* Write the names of all available character maps to stdout. */ | |
439 | static void | |
440 | write_charmaps (void) | |
441 | { | |
1fb05e3d | 442 | void *all_data = NULL; |
2b83a2a4 RM |
443 | DIR *dir; |
444 | struct dirent *dirent; | |
445 | ||
446 | dir = opendir (CHARMAP_PATH); | |
447 | if (dir == NULL) | |
448 | { | |
449 | error (1, errno, gettext ("cannot read character map directory `%s'"), | |
450 | CHARMAP_PATH); | |
451 | return; | |
452 | } | |
453 | ||
454 | /* Now we can look for all files in the directory. */ | |
455 | while ((dirent = readdir (dir)) != NULL) | |
456 | if (strcmp (dirent->d_name, ".") != 0 | |
457 | && strcmp (dirent->d_name, "..") != 0) | |
1fb05e3d | 458 | { |
51702635 | 459 | char *buf = NULL; |
1fb05e3d | 460 | mode_t mode; |
51702635 | 461 | |
1fb05e3d UD |
462 | #ifdef _DIRENT_HAVE_D_TYPE |
463 | if (dirent->d_type != DT_UNKNOWN) | |
464 | mode = DTTOIF (dirent->d_type); | |
465 | else | |
466 | #endif | |
467 | { | |
468 | struct stat st; | |
51702635 UD |
469 | |
470 | buf = alloca (sizeof (CHARMAP_PATH) + strlen (dirent->d_name) + 1); | |
1fb05e3d UD |
471 | |
472 | stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"), dirent->d_name); | |
473 | ||
474 | if (stat (buf, &st) < 0) | |
475 | continue; | |
476 | mode = st.st_mode; | |
477 | } | |
478 | ||
479 | if (S_ISREG (mode)) | |
51702635 UD |
480 | { |
481 | FILE *fp; | |
482 | ||
483 | PUT (strdup (dirent->d_name)); | |
484 | ||
485 | /* Read the file and learn about the code set name. */ | |
486 | if (buf == NULL) | |
487 | { | |
488 | buf = alloca (sizeof (CHARMAP_PATH) | |
489 | + strlen (dirent->d_name) + 1); | |
490 | ||
491 | stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"), | |
492 | dirent->d_name); | |
493 | } | |
494 | ||
495 | fp = fopen (buf, "r"); | |
496 | if (fp != NULL) | |
497 | { | |
498 | char *name = NULL; | |
499 | ||
500 | while (!feof (fp)) | |
501 | { | |
502 | char junk[BUFSIZ]; | |
503 | ||
504 | if (fscanf (fp, " <code_set_name> %as", &name) == 1) | |
505 | break; | |
506 | ||
1f205a47 UD |
507 | while (fgets (junk, sizeof junk, fp) != NULL |
508 | && strchr (junk, '\n') == NULL) | |
509 | continue; | |
51702635 UD |
510 | } |
511 | ||
512 | fclose (fp); | |
513 | ||
514 | if (name != NULL) | |
515 | PUT (name); | |
516 | } | |
517 | } | |
1fb05e3d | 518 | } |
2b83a2a4 RM |
519 | |
520 | closedir (dir); | |
1fb05e3d UD |
521 | |
522 | twalk (all_data, print_names); | |
2b83a2a4 RM |
523 | } |
524 | ||
525 | ||
526 | /* We have to show the contents of the environments determining the | |
527 | locale. */ | |
528 | static void | |
529 | show_locale_vars (void) | |
530 | { | |
531 | size_t cat_no; | |
532 | const char *lcall = getenv ("LC_ALL"); | |
533 | const char *lang = getenv ("LANG") ? : "POSIX"; | |
534 | ||
535 | void get_source (const char *name) | |
536 | { | |
537 | char *val = getenv (name); | |
538 | ||
51702635 UD |
539 | if ((lcall ?: "")[0] != '\0' || val == NULL) |
540 | printf ("%s=\"%s\"\n", name, (lcall ?: "")[0] ? lcall : lang); | |
2b83a2a4 RM |
541 | else |
542 | printf ("%s=%s\n", name, val); | |
543 | } | |
544 | ||
545 | /* LANG has to be the first value. */ | |
546 | printf ("LANG=%s\n", lang); | |
547 | ||
548 | /* Now all categories in an unspecified order. */ | |
549 | for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no) | |
550 | get_source (category[cat_no].name); | |
551 | ||
552 | /* The last is the LC_ALL value. */ | |
553 | printf ("LC_ALL=%s\n", lcall ? : ""); | |
554 | } | |
555 | ||
556 | ||
8f2ece69 UD |
557 | /* Some of the "string" we print contain non-printable characters. We |
558 | encode them here. */ | |
559 | static void | |
560 | print_escaped (const char *string) | |
561 | { | |
562 | const unsigned char *ch; | |
563 | ||
564 | ch = string; | |
565 | while ('\0' != *ch) | |
566 | { | |
567 | if (isprint (*ch)) | |
568 | putchar (*ch); | |
569 | else | |
570 | printf("<0x%02x>", *ch); | |
571 | ++ch; | |
572 | } | |
573 | } | |
574 | ||
575 | ||
2b83a2a4 RM |
576 | /* Show the information request for NAME. */ |
577 | static void | |
578 | show_info (const char *name) | |
579 | { | |
580 | size_t cat_no; | |
581 | ||
582 | void print_item (struct cat_item *item) | |
583 | { | |
584 | if (show_keyword_name != 0) | |
585 | printf ("%s=", item->name); | |
586 | ||
587 | switch (item->value_type) | |
588 | { | |
589 | case string: | |
8f2ece69 UD |
590 | if (show_keyword_name) |
591 | putchar ('"'); | |
592 | print_escaped (nl_langinfo (item->item_id) ? : ""); | |
593 | if (show_keyword_name) | |
594 | putchar ('"'); | |
2b83a2a4 RM |
595 | break; |
596 | case stringarray: | |
597 | { | |
598 | int cnt; | |
599 | const char *val; | |
600 | ||
601 | if (show_keyword_name) | |
602 | putchar ('"'); | |
603 | ||
604 | for (cnt = 0; cnt < item->max - 1; ++cnt) | |
605 | { | |
606 | val = nl_langinfo (item->item_id + cnt); | |
8f2ece69 UD |
607 | if (val != NULL) |
608 | print_escaped (val); | |
609 | putchar (';'); | |
2b83a2a4 RM |
610 | } |
611 | ||
612 | val = nl_langinfo (item->item_id + cnt); | |
8f2ece69 UD |
613 | if (val != NULL) |
614 | print_escaped (val); | |
2b83a2a4 RM |
615 | |
616 | if (show_keyword_name) | |
617 | putchar ('"'); | |
618 | } | |
619 | break; | |
51702635 UD |
620 | case stringlist: |
621 | { | |
622 | int first = 1; | |
623 | const char *val = nl_langinfo (item->item_id) ? : ""; | |
624 | ||
625 | while (*val != '\0') | |
626 | { | |
627 | printf ("%s%s%s%s", first ? "" : ";", | |
628 | show_keyword_name ? "\"" : "", val, | |
629 | show_keyword_name ? "\"" : ""); | |
630 | val = strchr (val, '\0') + 1; | |
631 | first = 0; | |
632 | } | |
633 | } | |
634 | break; | |
2b83a2a4 RM |
635 | case byte: |
636 | { | |
637 | const char *val = nl_langinfo (item->item_id); | |
638 | ||
639 | if (val != NULL) | |
640 | printf ("%d", *val == CHAR_MAX ? -1 : *val); | |
641 | } | |
642 | break; | |
643 | case bytearray: | |
644 | { | |
645 | const char *val = nl_langinfo (item->item_id); | |
646 | int cnt = val ? strlen (val) : 0; | |
647 | ||
648 | while (cnt > 1) | |
649 | { | |
650 | printf ("%d;", *val == CHAR_MAX ? -1 : *val); | |
651 | --cnt; | |
652 | ++val; | |
653 | } | |
654 | ||
655 | printf ("%d", cnt == 0 || *val == CHAR_MAX ? -1 : *val); | |
656 | } | |
657 | break; | |
299a95b9 RM |
658 | case word: |
659 | { | |
9756dfe1 UD |
660 | unsigned int val = |
661 | (unsigned int) (unsigned long int) nl_langinfo (item->item_id); | |
299a95b9 RM |
662 | printf ("%d", val); |
663 | } | |
664 | break; | |
2b83a2a4 RM |
665 | default: |
666 | } | |
667 | putchar ('\n'); | |
668 | } | |
669 | ||
670 | for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no) | |
671 | { | |
672 | size_t item_no; | |
673 | ||
2b83a2a4 RM |
674 | if (strcmp (name, category[cat_no].name) == 0) |
675 | /* Print the whole category. */ | |
676 | { | |
677 | if (show_category_name != 0) | |
678 | puts (category[cat_no].name); | |
679 | ||
680 | for (item_no = 0; item_no < category[cat_no].number; ++item_no) | |
681 | print_item (&category[cat_no].item_desc[item_no]); | |
682 | ||
683 | return; | |
684 | } | |
19bc17a9 | 685 | |
2b83a2a4 RM |
686 | for (item_no = 0; item_no < category[cat_no].number; ++item_no) |
687 | if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0) | |
688 | { | |
689 | if (show_category_name != 0) | |
690 | puts (category[cat_no].name); | |
691 | ||
692 | print_item (&category[cat_no].item_desc[item_no]); | |
693 | return; | |
694 | } | |
695 | } | |
036cc82f | 696 | |
6d52618b UD |
697 | /* The name is not a standard one. |
698 | For testing and perhaps advanced use allow some more symbols. */ | |
036cc82f | 699 | locale_special (name, show_category_name, show_keyword_name); |
2b83a2a4 | 700 | } |