Back to index

glibc  2.9
locale.c
Go to the documentation of this file.
00001 /* Implementation of the locale program according to POSIX 9945-2.
00002    Copyright (C) 1995-1997, 1999-2007, 2008 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published
00008    by the Free Software Foundation; version 2 of the License, or
00009    (at your option) any later version.
00010 
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015 
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software Foundation,
00018    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00019 
00020 #ifdef HAVE_CONFIG_H
00021 # include <config.h>
00022 #endif
00023 
00024 #include <argp.h>
00025 #include <argz.h>
00026 #include <dirent.h>
00027 #include <errno.h>
00028 #include <error.h>
00029 #include <fcntl.h>
00030 #include <langinfo.h>
00031 #include <libintl.h>
00032 #include <limits.h>
00033 #include <locale.h>
00034 #include <search.h>
00035 #include <stdio.h>
00036 #include <stdio_ext.h>
00037 #include <stdlib.h>
00038 #include <string.h>
00039 #include <unistd.h>
00040 #include <sys/mman.h>
00041 #include <sys/stat.h>
00042 
00043 #include "localeinfo.h"
00044 #include "charmap-dir.h"
00045 #include "../locarchive.h"
00046 
00047 extern void *xmalloc (size_t __n);
00048 extern char *xstrdup (const char *__str);
00049 
00050 #define ARCHIVE_NAME LOCALEDIR "/locale-archive"
00051 
00052 /* If set print the name of the category.  */
00053 static int show_category_name;
00054 
00055 /* If set print the name of the item.  */
00056 static int show_keyword_name;
00057 
00058 /* Print names of all available locales.  */
00059 static int do_all;
00060 
00061 /* Print names of all available character maps.  */
00062 static int do_charmaps = 0;
00063 
00064 /* Nonzero if verbose output is wanted.  */
00065 static int verbose;
00066 
00067 /* Name and version of program.  */
00068 static void print_version (FILE *stream, struct argp_state *state);
00069 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
00070 
00071 /* Definitions of arguments for argp functions.  */
00072 static const struct argp_option options[] =
00073 {
00074   { NULL, 0, NULL, 0, N_("System information:") },
00075   { "all-locales", 'a', NULL, OPTION_NO_USAGE,
00076     N_("Write names of available locales") },
00077   { "charmaps", 'm', NULL, OPTION_NO_USAGE,
00078     N_("Write names of available charmaps") },
00079   { NULL, 0, NULL, 0, N_("Modify output format:") },
00080   { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
00081   { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
00082   { "verbose", 'v', NULL, 0, N_("Print more information") },
00083   { NULL, 0, NULL, 0, NULL }
00084 };
00085 
00086 /* Short description of program.  */
00087 static const char doc[] = N_("Get locale-specific information.\v\
00088 For bug reporting instructions, please see:\n\
00089 <http://www.gnu.org/software/libc/bugs.html>.\n");
00090 
00091 /* Strings for arguments in help texts.  */
00092 static const char args_doc[] = N_("NAME\n[-a|-m]");
00093 
00094 /* Prototype for option handler.  */
00095 static error_t parse_opt (int key, char *arg, struct argp_state *state);
00096 
00097 /* Data structure to communicate with argp functions.  */
00098 static struct argp argp =
00099 {
00100   options, parse_opt, args_doc, doc
00101 };
00102 
00103 
00104 /* We don't have these constants defined because we don't use them.  Give
00105    default values.  */
00106 #define CTYPE_MB_CUR_MIN 0
00107 #define CTYPE_MB_CUR_MAX 0
00108 #define CTYPE_HASH_SIZE 0
00109 #define CTYPE_HASH_LAYERS 0
00110 #define CTYPE_CLASS 0
00111 #define CTYPE_TOUPPER_EB 0
00112 #define CTYPE_TOLOWER_EB 0
00113 #define CTYPE_TOUPPER_EL 0
00114 #define CTYPE_TOLOWER_EL 0
00115 
00116 /* Definition of the data structure which represents a category and its
00117    items.  */
00118 struct category
00119 {
00120   int cat_id;
00121   const char *name;
00122   size_t number;
00123   struct cat_item
00124   {
00125     int item_id;
00126     const char *name;
00127     enum { std, opt } status;
00128     enum value_type value_type;
00129     int min;
00130     int max;
00131   } *item_desc;
00132 };
00133 
00134 /* Simple helper macro.  */
00135 #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
00136 
00137 /* For some tricky stuff.  */
00138 #define NO_PAREN(Item, More...) Item, ## More
00139 
00140 /* We have all categories defined in `categories.def'.  Now construct
00141    the description and data structure used for all categories.  */
00142 #define DEFINE_ELEMENT(Item, More...) { Item, ## More },
00143 #define DEFINE_CATEGORY(category, name, items, postload) \
00144     static struct cat_item category##_desc[] =                              \
00145       {                                                                     \
00146         NO_PAREN items                                                      \
00147       };
00148 
00149 #include "categories.def"
00150 #undef DEFINE_CATEGORY
00151 
00152 static struct category category[] =
00153   {
00154 #define DEFINE_CATEGORY(category, name, items, postload) \
00155     [category] = { _NL_NUM_##category, name, NELEMS (category##_desc),             \
00156                  category##_desc },
00157 #include "categories.def"
00158 #undef DEFINE_CATEGORY
00159   };
00160 #define NCATEGORIES NELEMS (category)
00161 
00162 
00163 /* Automatically set variable.  */
00164 extern const char *__progname;
00165 
00166 /* helper function for extended name handling.  */
00167 extern void locale_special (const char *name, int show_category_name,
00168                          int show_keyword_name);
00169 
00170 /* Prototypes for local functions.  */
00171 static void print_LC_IDENTIFICATION (void *mapped, size_t size);
00172 static void print_LC_CTYPE (void *mapped, size_t size);
00173 static void write_locales (void);
00174 static int nameentcmp (const void *a, const void *b);
00175 static int write_archive_locales (void **all_datap, char *linebuf);
00176 static void write_charmaps (void);
00177 static void show_locale_vars (void);
00178 static void show_info (const char *name);
00179 
00180 
00181 int
00182 main (int argc, char *argv[])
00183 {
00184   int remaining;
00185 
00186   /* Set initial values for global variables.  */
00187   show_category_name = 0;
00188   show_keyword_name = 0;
00189 
00190   /* Set locale.  Do not set LC_ALL because the other categories must
00191      not be affected (according to POSIX.2).  */
00192   if (setlocale (LC_CTYPE, "") == NULL)
00193     error (0, errno, gettext ("Cannot set LC_CTYPE to default locale"));
00194   if (setlocale (LC_MESSAGES, "") == NULL)
00195     error (0, errno, gettext ("Cannot set LC_MESSAGES to default locale"));
00196 
00197   /* Initialize the message catalog.  */
00198   textdomain (PACKAGE);
00199 
00200   /* Parse and process arguments.  */
00201   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
00202 
00203   /* `-a' requests the names of all available locales.  */
00204   if (do_all != 0)
00205     {
00206       if (setlocale (LC_COLLATE, "") == NULL)
00207        error (0, errno,
00208               gettext ("Cannot set LC_COLLATE to default locale"));
00209       write_locales ();
00210       exit (EXIT_SUCCESS);
00211     }
00212 
00213   /* `m' requests the names of all available charmaps.  The names can be
00214      used for the -f argument to localedef(1).  */
00215   if (do_charmaps != 0)
00216     {
00217       write_charmaps ();
00218       exit (EXIT_SUCCESS);
00219     }
00220 
00221   /* Specific information about the current locale are requested.
00222      Change to this locale now.  */
00223   if (setlocale (LC_ALL, "") == NULL)
00224     error (0, errno, gettext ("Cannot set LC_ALL to default locale"));
00225 
00226   /* If no real argument is given we have to print the contents of the
00227      current locale definition variables.  These are LANG and the LC_*.  */
00228   if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
00229     {
00230       show_locale_vars ();
00231       exit (EXIT_SUCCESS);
00232     }
00233 
00234   /* Process all given names.  */
00235   while (remaining <  argc)
00236     show_info (argv[remaining++]);
00237 
00238   exit (EXIT_SUCCESS);
00239 }
00240 
00241 
00242 /* Handle program arguments.  */
00243 static error_t
00244 parse_opt (int key, char *arg, struct argp_state *state)
00245 {
00246   switch (key)
00247     {
00248     case 'a':
00249       do_all = 1;
00250       break;
00251     case 'c':
00252       show_category_name = 1;
00253       break;
00254     case 'm':
00255       do_charmaps = 1;
00256       break;
00257     case 'k':
00258       show_keyword_name = 1;
00259       break;
00260     case 'v':
00261       verbose = 1;
00262       break;
00263     default:
00264       return ARGP_ERR_UNKNOWN;
00265     }
00266   return 0;
00267 }
00268 
00269 
00270 /* Print the version information.  */
00271 static void
00272 print_version (FILE *stream, struct argp_state *state)
00273 {
00274   fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
00275   fprintf (stream, gettext ("\
00276 Copyright (C) %s Free Software Foundation, Inc.\n\
00277 This is free software; see the source for copying conditions.  There is NO\n\
00278 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
00279 "), "2008");
00280   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
00281 }
00282 
00283 
00284 /* Simple action function which prints arguments as strings.  */
00285 static void
00286 print_names (const void *nodep, VISIT value, int level)
00287 {
00288   if (value == postorder || value == leaf)
00289     puts (*(char **) nodep);
00290 }
00291 
00292 
00293 static int
00294 select_dirs (const struct dirent *dirent)
00295 {
00296   int result = 0;
00297 
00298   if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
00299     {
00300       mode_t mode = 0;
00301 
00302 #ifdef _DIRENT_HAVE_D_TYPE
00303       if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
00304        mode = DTTOIF (dirent->d_type);
00305       else
00306 #endif
00307        {
00308          struct stat64 st;
00309          char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
00310 
00311          stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
00312 
00313          if (stat64 (buf, &st) == 0)
00314            mode = st.st_mode;
00315        }
00316 
00317       result = S_ISDIR (mode);
00318     }
00319 
00320   return result;
00321 }
00322 
00323 
00324 static void
00325 print_LC_IDENTIFICATION (void *mapped, size_t size)
00326 {
00327   /* Read the information from the file.  */
00328   struct
00329     {
00330       unsigned int magic;
00331       unsigned int nstrings;
00332       unsigned int strindex[0];
00333     } *filedata = mapped;
00334 
00335   if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
00336       && (sizeof *filedata
00337          + (filedata->nstrings
00338             * sizeof (unsigned int))
00339          <= size))
00340     {
00341       const char *str;
00342 
00343 #define HANDLE(idx, name) \
00344   str = ((char *) mapped                                             \
00345         + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]);    \
00346   if (*str != '\0')                                                  \
00347     printf ("%9s | %s\n", name, str)
00348       HANDLE (TITLE, "title");
00349       HANDLE (SOURCE, "source");
00350       HANDLE (ADDRESS, "address");
00351       HANDLE (CONTACT, "contact");
00352       HANDLE (EMAIL, "email");
00353       HANDLE (TEL, "telephone");
00354       HANDLE (FAX, "fax");
00355       HANDLE (LANGUAGE, "language");
00356       HANDLE (TERRITORY, "territory");
00357       HANDLE (AUDIENCE, "audience");
00358       HANDLE (APPLICATION, "application");
00359       HANDLE (ABBREVIATION, "abbreviation");
00360       HANDLE (REVISION, "revision");
00361       HANDLE (DATE, "date");
00362     }
00363 }
00364 
00365 
00366 static void
00367 print_LC_CTYPE (void *mapped, size_t size)
00368 {
00369   struct
00370     {
00371       unsigned int magic;
00372       unsigned int nstrings;
00373       unsigned int strindex[0];
00374     } *filedata = mapped;
00375 
00376   if (filedata->magic == LIMAGIC (LC_CTYPE)
00377       && (sizeof *filedata
00378          + (filedata->nstrings
00379             * sizeof (unsigned int))
00380          <= size))
00381     {
00382       const char *str;
00383 
00384       str = ((char *) mapped
00385             + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
00386       if (*str != '\0')
00387        printf ("  codeset | %s\n", str);
00388     }
00389 }
00390 
00391 
00392 /* Write the names of all available locales to stdout.  We have some
00393    sources of the information: the contents of the locale directory
00394    and the locale.alias file.  To avoid duplicates and print the
00395    result is a reasonable order we put all entries is a search tree
00396    and print them afterwards.  */
00397 static void
00398 write_locales (void)
00399 {
00400   char linebuf[80];
00401   void *all_data = NULL;
00402   struct dirent **dirents;
00403   int ndirents;
00404   int cnt;
00405   char *alias_path;
00406   size_t alias_path_len;
00407   char *entry;
00408   int first_locale = 1;
00409 
00410 #define PUT(name) tsearch (name, &all_data, \
00411                         (int (*) (const void *, const void *)) strcoll)
00412 #define GET(name) tfind (name, &all_data, \
00413                         (int (*) (const void *, const void *)) strcoll)
00414 
00415   /* `POSIX' locale is always available (POSIX.2 4.34.3).  */
00416   PUT ("POSIX");
00417   /* And so is the "C" locale.  */
00418   PUT ("C");
00419 
00420   memset (linebuf, '-', sizeof (linebuf) - 1);
00421   linebuf[sizeof (linebuf) - 1] = '\0';
00422 
00423   /* First scan the locale archive.  */
00424   if (write_archive_locales (&all_data, linebuf))
00425     first_locale = 0;
00426 
00427   /* Now we can look for all files in the directory.  */
00428   ndirents = scandir (LOCALEDIR, &dirents, select_dirs, alphasort);
00429   for (cnt = 0; cnt < ndirents; ++cnt)
00430     {
00431       /* Test whether at least the LC_CTYPE data is there.  Some
00432         directories only contain translations.  */
00433       char buf[sizeof (LOCALEDIR) + strlen (dirents[cnt]->d_name)
00434              + sizeof "/LC_IDENTIFICATION"];
00435       char *enddir;
00436       struct stat64 st;
00437 
00438       stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"),
00439                             dirents[cnt]->d_name),
00440              "/LC_IDENTIFICATION");
00441 
00442       if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
00443        {
00444          if (verbose && GET (dirents[cnt]->d_name) == NULL)
00445            {
00446              /* Provide some nice output of all kinds of
00447                information.  */
00448              int fd;
00449 
00450              if (! first_locale)
00451               putchar_unlocked ('\n');
00452              first_locale = 0;
00453 
00454              printf ("locale: %-15.15s directory: %.*s\n%s\n",
00455                     dirents[cnt]->d_name, (int) (enddir - buf), buf,
00456                     linebuf);
00457 
00458              fd = open64 (buf, O_RDONLY);
00459              if (fd != -1)
00460               {
00461                 void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
00462                                     MAP_SHARED, fd, 0);
00463                 if (mapped != MAP_FAILED)
00464                   {
00465                     print_LC_IDENTIFICATION (mapped, st.st_size);
00466 
00467                     munmap (mapped, st.st_size);
00468                   }
00469 
00470                 close (fd);
00471 
00472                 /* Now try to get the charset information.  */
00473                 strcpy (enddir, "/LC_CTYPE");
00474                 fd = open64 (buf, O_RDONLY);
00475                 if (fd != -1 && fstat64 (fd, &st) >= 0
00476                     && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
00477                                        MAP_SHARED, fd, 0))
00478                        != MAP_FAILED))
00479                   {
00480                     print_LC_CTYPE (mapped, st.st_size);
00481 
00482                     munmap (mapped, st.st_size);
00483                   }
00484 
00485                 if (fd != -1)
00486                   close (fd);
00487               }
00488            }
00489 
00490          /* If the verbose format is not selected we simply
00491             collect the names.  */
00492          PUT (xstrdup (dirents[cnt]->d_name));
00493        }
00494     }
00495   if (ndirents > 0)
00496     free (dirents);
00497 
00498   /* Now read the locale.alias files.  */
00499   if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
00500     error (1, errno, gettext ("while preparing output"));
00501 
00502   entry = NULL;
00503   while ((entry = argz_next (alias_path, alias_path_len, entry)))
00504     {
00505       static const char aliasfile[] = "/locale.alias";
00506       FILE *fp;
00507       char full_name[strlen (entry) + sizeof aliasfile];
00508 
00509       stpcpy (stpcpy (full_name, entry), aliasfile);
00510       fp = fopen (full_name, "rm");
00511       if (fp == NULL)
00512        /* Ignore non-existing files.  */
00513        continue;
00514 
00515       /* No threads present.  */
00516       __fsetlocking (fp, FSETLOCKING_BYCALLER);
00517 
00518       while (! feof_unlocked (fp))
00519        {
00520          /* It is a reasonable approach to use a fix buffer here
00521             because
00522             a) we are only interested in the first two fields
00523             b) these fields must be usable as file names and so must
00524                not be that long  */
00525          char buf[BUFSIZ];
00526          char *alias;
00527          char *value;
00528          char *cp;
00529 
00530          if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
00531            /* EOF reached.  */
00532            break;
00533 
00534          cp = buf;
00535          /* Ignore leading white space.  */
00536          while (isspace (cp[0]) && cp[0] != '\n')
00537            ++cp;
00538 
00539          /* A leading '#' signals a comment line.  */
00540          if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
00541            {
00542              alias = cp++;
00543              while (cp[0] != '\0' && !isspace (cp[0]))
00544               ++cp;
00545              /* Terminate alias name.  */
00546              if (cp[0] != '\0')
00547               *cp++ = '\0';
00548 
00549              /* Now look for the beginning of the value.  */
00550              while (isspace (cp[0]))
00551               ++cp;
00552 
00553              if (cp[0] != '\0')
00554               {
00555                 value = cp++;
00556                 while (cp[0] != '\0' && !isspace (cp[0]))
00557                   ++cp;
00558                 /* Terminate value.  */
00559                 if (cp[0] == '\n')
00560                   {
00561                     /* This has to be done to make the following
00562                       test for the end of line possible.  We are
00563                       looking for the terminating '\n' which do not
00564                       overwrite here.  */
00565                     *cp++ = '\0';
00566                     *cp = '\n';
00567                   }
00568                 else if (cp[0] != '\0')
00569                   *cp++ = '\0';
00570 
00571                 /* Add the alias.  */
00572                 if (! verbose && GET (value) != NULL)
00573                   PUT (xstrdup (alias));
00574               }
00575            }
00576 
00577          /* Possibly not the whole line fits into the buffer.
00578             Ignore the rest of the line.  */
00579          while (strchr (cp, '\n') == NULL)
00580            {
00581              cp = buf;
00582              if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
00583               /* Make sure the inner loop will be left.  The outer
00584                  loop will exit at the `feof' test.  */
00585               *cp = '\n';
00586            }
00587        }
00588 
00589       fclose (fp);
00590     }
00591 
00592   if (! verbose)
00593     {
00594       twalk (all_data, print_names);
00595     }
00596 }
00597 
00598 
00599 struct nameent
00600 {
00601   char *name;
00602   uint32_t locrec_offset;
00603 };
00604 
00605 
00606 static int
00607 nameentcmp (const void *a, const void *b)
00608 {
00609   return strcoll (((const struct nameent *) a)->name,
00610                 ((const struct nameent *) b)->name);
00611 }
00612 
00613 
00614 static int
00615 write_archive_locales (void **all_datap, char *linebuf)
00616 {
00617   struct stat64 st;
00618   void *all_data = *all_datap;
00619   size_t len = 0;
00620   struct locarhead *head;
00621   struct namehashent *namehashtab;
00622   char *addr = MAP_FAILED;
00623   int fd, ret = 0;
00624   uint32_t cnt;
00625 
00626   fd = open64 (ARCHIVE_NAME, O_RDONLY);
00627   if (fd < 0)
00628     return 0;
00629 
00630   if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
00631     goto error_out;
00632 
00633   len = st.st_size;
00634   addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
00635   if (addr == MAP_FAILED)
00636     goto error_out;
00637 
00638   head = (struct locarhead *) addr;
00639   if (head->namehash_offset + head->namehash_size > len
00640       || head->string_offset + head->string_size > len
00641       || head->locrectab_offset + head->locrectab_size > len
00642       || head->sumhash_offset + head->sumhash_size > len)
00643     goto error_out;
00644 
00645   namehashtab = (struct namehashent *) (addr + head->namehash_offset);
00646   if (! verbose)
00647     {
00648       for (cnt = 0; cnt < head->namehash_size; ++cnt)
00649        if (namehashtab[cnt].locrec_offset != 0)
00650          {
00651            PUT (xstrdup (addr + namehashtab[cnt].name_offset));
00652            ++ret;
00653          }
00654     }
00655   else
00656     {
00657       struct nameent *names;
00658       uint32_t used;
00659 
00660       names = (struct nameent *) xmalloc (head->namehash_used
00661                                      * sizeof (struct nameent));
00662       for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
00663        if (namehashtab[cnt].locrec_offset != 0)
00664          {
00665            names[used].name = addr + namehashtab[cnt].name_offset;
00666            names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
00667          }
00668 
00669       /* Sort the names.  */
00670       qsort (names, used, sizeof (struct nameent), nameentcmp);
00671 
00672       for (cnt = 0; cnt < used; ++cnt)
00673        {
00674          struct locrecent *locrec;
00675 
00676          PUT (xstrdup (names[cnt].name));
00677 
00678          if (cnt)
00679            putchar_unlocked ('\n');
00680 
00681          printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
00682                 names[cnt].name, linebuf);
00683 
00684          locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
00685 
00686          print_LC_IDENTIFICATION (addr
00687                                + locrec->record[LC_IDENTIFICATION].offset,
00688                                locrec->record[LC_IDENTIFICATION].len);
00689 
00690          print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
00691                        locrec->record[LC_CTYPE].len);
00692        }
00693 
00694       ret = used;
00695     }
00696 
00697 error_out:
00698   if (addr != MAP_FAILED)
00699     munmap (addr, len);
00700   close (fd);
00701   *all_datap = all_data;
00702   return ret;
00703 }
00704 
00705 
00706 /* Write the names of all available character maps to stdout.  */
00707 static void
00708 write_charmaps (void)
00709 {
00710   void *all_data = NULL;
00711   CHARMAP_DIR *dir;
00712   const char *dirent;
00713 
00714   /* Look for all files in the charmap directory.  */
00715   dir = charmap_opendir (CHARMAP_PATH);
00716   if (dir == NULL)
00717     return;
00718 
00719   while ((dirent = charmap_readdir (dir)) != NULL)
00720     {
00721       char **aliases;
00722       char **p;
00723 
00724       PUT (xstrdup (dirent));
00725 
00726       aliases = charmap_aliases (CHARMAP_PATH, dirent);
00727 
00728 #if 0
00729       /* Add the code_set_name and the aliases.  */
00730       for (p = aliases; *p; p++)
00731        PUT (xstrdup (*p));
00732 #else
00733       /* Add the code_set_name only.  Most aliases are obsolete.  */
00734       p = aliases;
00735       if (*p)
00736        PUT (xstrdup (*p));
00737 #endif
00738 
00739       charmap_free_aliases (aliases);
00740     }
00741 
00742   charmap_closedir (dir);
00743 
00744   twalk (all_data, print_names);
00745 }
00746 
00747 
00748 /* We have to show the contents of the environments determining the
00749    locale.  */
00750 static void
00751 show_locale_vars (void)
00752 {
00753   size_t cat_no;
00754   const char *lcall = getenv ("LC_ALL");
00755   const char *lang = getenv ("LANG") ? : "";
00756 
00757   auto void get_source (const char *name);
00758 
00759   void get_source (const char *name)
00760     {
00761       char *val = getenv (name);
00762 
00763       if ((lcall ?: "")[0] != '\0' || val == NULL)
00764        printf ("%s=\"%s\"\n", name,
00765               (lcall ?: "")[0] ? lcall : (lang ?: "")[0] ? lang : "POSIX");
00766       else
00767        printf ("%s=%s\n", name, val);
00768     }
00769 
00770   /* LANG has to be the first value.  */
00771   printf ("LANG=%s\n", lang);
00772 
00773   /* Now all categories in an unspecified order.  */
00774   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
00775     if (cat_no != LC_ALL)
00776       get_source (category[cat_no].name);
00777 
00778   /* The last is the LC_ALL value.  */
00779   printf ("LC_ALL=%s\n", lcall ? : "");
00780 }
00781 
00782 
00783 /* Show the information request for NAME.  */
00784 static void
00785 show_info (const char *name)
00786 {
00787   size_t cat_no;
00788 
00789   auto void print_item (struct cat_item *item);
00790 
00791   void print_item (struct cat_item *item)
00792     {
00793       switch (item->value_type)
00794        {
00795        case string:
00796          if (show_keyword_name)
00797            printf ("%s=\"", item->name);
00798          fputs (nl_langinfo (item->item_id) ? : "", stdout);
00799          if (show_keyword_name)
00800            putchar ('"');
00801          putchar ('\n');
00802          break;
00803        case stringarray:
00804          {
00805            int cnt;
00806            const char *val;
00807 
00808            if (show_keyword_name)
00809              printf ("%s=\"", item->name);
00810 
00811            for (cnt = 0; cnt < item->max - 1; ++cnt)
00812              {
00813               val = nl_langinfo (item->item_id + cnt);
00814               if (val != NULL)
00815                 fputs (val, stdout);
00816               putchar (';');
00817              }
00818 
00819            val = nl_langinfo (item->item_id + cnt);
00820            if (val != NULL)
00821              fputs (val, stdout);
00822 
00823            if (show_keyword_name)
00824              putchar ('"');
00825            putchar ('\n');
00826          }
00827          break;
00828        case stringlist:
00829          {
00830            int first = 1;
00831            const char *val = nl_langinfo (item->item_id) ? : "";
00832            int cnt;
00833 
00834            if (show_keyword_name)
00835              printf ("%s=", item->name);
00836 
00837            for (cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
00838              {
00839               printf ("%s%s%s%s", first ? "" : ";",
00840                      show_keyword_name ? "\"" : "", val,
00841                      show_keyword_name ? "\"" : "");
00842               val = strchr (val, '\0') + 1;
00843               first = 0;
00844              }
00845            putchar ('\n');
00846          }
00847          break;
00848        case byte:
00849          {
00850            const char *val = nl_langinfo (item->item_id);
00851 
00852            if (show_keyword_name)
00853              printf ("%s=", item->name);
00854 
00855            if (val != NULL)
00856              printf ("%d", *val == '\177' ? -1 : *val);
00857            putchar ('\n');
00858          }
00859          break;
00860        case bytearray:
00861          {
00862            const char *val = nl_langinfo (item->item_id);
00863            int cnt = val ? strlen (val) : 0;
00864 
00865            if (show_keyword_name)
00866              printf ("%s=", item->name);
00867 
00868            while (cnt > 1)
00869              {
00870               printf ("%d;", *val == '\177' ? -1 : *val);
00871                 --cnt;
00872               ++val;
00873              }
00874 
00875            printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
00876          }
00877          break;
00878        case word:
00879          {
00880            union { unsigned int word; char *string; } val;
00881            val.string = nl_langinfo (item->item_id);
00882            if (show_keyword_name)
00883              printf ("%s=", item->name);
00884 
00885            printf ("%d\n", val.word);
00886          }
00887          break;
00888        case wstring:
00889        case wstringarray:
00890        case wstringlist:
00891          /* We don't print wide character information since the same
00892             information is available in a multibyte string.  */
00893        default:
00894          break;
00895 
00896        }
00897     }
00898 
00899   for (cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
00900     if (cat_no != LC_ALL)
00901       {
00902        size_t item_no;
00903 
00904        if (strcmp (name, category[cat_no].name) == 0)
00905          /* Print the whole category.  */
00906          {
00907            if (show_category_name != 0)
00908              puts (category[cat_no].name);
00909 
00910            for (item_no = 0; item_no < category[cat_no].number; ++item_no)
00911              print_item (&category[cat_no].item_desc[item_no]);
00912 
00913            return;
00914          }
00915 
00916        for (item_no = 0; item_no < category[cat_no].number; ++item_no)
00917          if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
00918            {
00919              if (show_category_name != 0)
00920               puts (category[cat_no].name);
00921 
00922              print_item (&category[cat_no].item_desc[item_no]);
00923              return;
00924            }
00925       }
00926 
00927   /* The name is not a standard one.
00928      For testing and perhaps advanced use allow some more symbols.  */
00929   locale_special (name, show_category_name, show_keyword_name);
00930 }