(root)/
glibc-2.38/
locale/
programs/
locale.c
       1  /* Implementation of the locale program according to POSIX 9945-2.
       2     Copyright (C) 1995-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     This program is free software; you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published
       7     by the Free Software Foundation; version 2 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program; if not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  #ifdef HAVE_CONFIG_H
      19  # include <config.h>
      20  #endif
      21  
      22  #include <argp.h>
      23  #include <argz.h>
      24  #include <dirent.h>
      25  #include <errno.h>
      26  #include <error.h>
      27  #include <fcntl.h>
      28  #include <langinfo.h>
      29  #include <libintl.h>
      30  #include <limits.h>
      31  #include <locale.h>
      32  #include <search.h>
      33  #include <stdio.h>
      34  #include <stdio_ext.h>
      35  #include <stdlib.h>
      36  #include <string.h>
      37  #include <unistd.h>
      38  #include <stdint.h>
      39  #include <sys/mman.h>
      40  #include <sys/stat.h>
      41  
      42  #include "record-status.h"
      43  #include "localeinfo.h"
      44  #include "charmap-dir.h"
      45  #include "../locarchive.h"
      46  #include <programs/xmalloc.h>
      47  
      48  #define ARCHIVE_NAME COMPLOCALEDIR "/locale-archive"
      49  
      50  /* If set print the name of the category.  */
      51  static int show_category_name;
      52  
      53  /* If set print the name of the item.  */
      54  static int show_keyword_name;
      55  
      56  /* Print names of all available locales.  */
      57  static int do_all;
      58  
      59  /* Print names of all available character maps.  */
      60  static int do_charmaps = 0;
      61  
      62  /* Name and version of program.  */
      63  static void print_version (FILE *stream, struct argp_state *state);
      64  void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
      65  
      66  /* Definitions of arguments for argp functions.  */
      67  static const struct argp_option options[] =
      68  {
      69    { NULL, 0, NULL, 0, N_("System information:") },
      70    { "all-locales", 'a', NULL, OPTION_NO_USAGE,
      71      N_("Write names of available locales") },
      72    { "charmaps", 'm', NULL, OPTION_NO_USAGE,
      73      N_("Write names of available charmaps") },
      74    { NULL, 0, NULL, 0, N_("Modify output format:") },
      75    { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
      76    { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
      77    { "verbose", 'v', NULL, 0, N_("Print more information") },
      78    { NULL, 0, NULL, 0, NULL }
      79  };
      80  
      81  /* Short description of program.  */
      82  static const char doc[] = N_("Get locale-specific information.");
      83  
      84  /* Strings for arguments in help texts.  */
      85  static const char args_doc[] = N_("NAME\n[-a|-m]");
      86  
      87  /* Prototype for option handler.  */
      88  static error_t parse_opt (int key, char *arg, struct argp_state *state);
      89  
      90  /* Function to print some extra text in the help message.  */
      91  static char *more_help (int key, const char *text, void *input);
      92  
      93  /* Data structure to communicate with argp functions.  */
      94  static struct argp argp =
      95  {
      96    options, parse_opt, args_doc, doc, NULL, more_help
      97  };
      98  
      99  
     100  /* We don't have these constants defined because we don't use them.  Give
     101     default values.  */
     102  #define CTYPE_MB_CUR_MIN 0
     103  #define CTYPE_MB_CUR_MAX 0
     104  #define CTYPE_HASH_SIZE 0
     105  #define CTYPE_HASH_LAYERS 0
     106  #define CTYPE_CLASS 0
     107  #define CTYPE_TOUPPER_EB 0
     108  #define CTYPE_TOLOWER_EB 0
     109  #define CTYPE_TOUPPER_EL 0
     110  #define CTYPE_TOLOWER_EL 0
     111  
     112  /* Definition of the data structure which represents a category and its
     113     items.  */
     114  struct category
     115  {
     116    int cat_id;
     117    const char *name;
     118    size_t number;
     119    struct cat_item
     120    {
     121      int item_id;
     122      const char *name;
     123      enum { std, opt } status;
     124      enum value_type value_type;
     125      int min;
     126      int max;
     127    } *item_desc;
     128  };
     129  
     130  /* Simple helper macro.  */
     131  #define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
     132  
     133  /* For some tricky stuff.  */
     134  #define NO_PAREN(Item, More...) Item, ## More
     135  
     136  /* We have all categories defined in `categories.def'.  Now construct
     137     the description and data structure used for all categories.  */
     138  #define DEFINE_ELEMENT(Item, More...) { Item, ## More },
     139  #define DEFINE_CATEGORY(category, name, items, postload) \
     140      static struct cat_item category##_desc[] =				      \
     141        {									      \
     142  	NO_PAREN items							      \
     143        };
     144  
     145  #include "categories.def"
     146  #undef DEFINE_CATEGORY
     147  
     148  static struct category category[] =
     149    {
     150  #define DEFINE_CATEGORY(category, name, items, postload) \
     151      [category] = { _NL_NUM_##category, name, NELEMS (category##_desc),	      \
     152  		   category##_desc },
     153  #include "categories.def"
     154  #undef DEFINE_CATEGORY
     155    };
     156  #define NCATEGORIES NELEMS (category)
     157  
     158  
     159  /* Automatically set variable.  */
     160  extern const char *__progname;
     161  
     162  /* helper function for extended name handling.  */
     163  extern void locale_special (const char *name, int show_category_name,
     164  			    int show_keyword_name);
     165  
     166  /* Prototypes for local functions.  */
     167  static void print_LC_IDENTIFICATION (void *mapped, size_t size);
     168  static void print_LC_CTYPE (void *mapped, size_t size);
     169  static void write_locales (void);
     170  static int nameentcmp (const void *a, const void *b);
     171  static int write_archive_locales (void **all_datap, char *linebuf);
     172  static void write_charmaps (void);
     173  static void show_locale_vars (void);
     174  static void show_info (const char *name);
     175  static void try_setlocale (int category, const char *category_name);
     176  static char *quote_string (const char *input);
     177  static void setlocale_diagnostics (void);
     178  
     179  
     180  int
     181  main (int argc, char *argv[])
     182  {
     183    int remaining;
     184  
     185    /* Set initial values for global variables.  */
     186    show_category_name = 0;
     187    show_keyword_name = 0;
     188  
     189    /* Set locale.  Do not set LC_ALL because the other categories must
     190       not be affected (according to POSIX.2).  */
     191    try_setlocale (LC_CTYPE, "LC_CTYPE");
     192    try_setlocale (LC_MESSAGES, "LC_MESSAGES");
     193  
     194    /* Initialize the message catalog.  */
     195    textdomain (PACKAGE);
     196  
     197    /* Parse and process arguments.  */
     198    argp_parse (&argp, argc, argv, 0, &remaining, NULL);
     199  
     200    /* `-a' requests the names of all available locales.  */
     201    if (do_all != 0)
     202      {
     203        setlocale_diagnostics ();
     204        try_setlocale (LC_COLLATE, "LC_COLLATE");
     205        write_locales ();
     206        exit (EXIT_SUCCESS);
     207      }
     208  
     209    /* `m' requests the names of all available charmaps.  The names can be
     210       used for the -f argument to localedef(1).  */
     211    if (do_charmaps != 0)
     212      {
     213        setlocale_diagnostics ();
     214        write_charmaps ();
     215        exit (EXIT_SUCCESS);
     216      }
     217  
     218    /* Specific information about the current locale are requested.
     219       Change to this locale now.  */
     220    try_setlocale (LC_ALL, "LC_ALL");
     221    setlocale_diagnostics ();
     222  
     223    /* If no real argument is given we have to print the contents of the
     224       current locale definition variables.  These are LANG and the LC_*.  */
     225    if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
     226      {
     227        show_locale_vars ();
     228        exit (EXIT_SUCCESS);
     229      }
     230  
     231    /* Process all given names.  */
     232    while (remaining <  argc)
     233      show_info (argv[remaining++]);
     234  
     235    exit (EXIT_SUCCESS);
     236  }
     237  
     238  
     239  /* Handle program arguments.  */
     240  static error_t
     241  parse_opt (int key, char *arg, struct argp_state *state)
     242  {
     243    switch (key)
     244      {
     245      case 'a':
     246        do_all = 1;
     247        break;
     248      case 'c':
     249        show_category_name = 1;
     250        break;
     251      case 'm':
     252        do_charmaps = 1;
     253        break;
     254      case 'k':
     255        show_keyword_name = 1;
     256        break;
     257      case 'v':
     258        verbose = 1;
     259        break;
     260      default:
     261        return ARGP_ERR_UNKNOWN;
     262      }
     263    return 0;
     264  }
     265  
     266  
     267  static char *
     268  more_help (int key, const char *text, void *input)
     269  {
     270    char *tp = NULL;
     271    switch (key)
     272      {
     273      case ARGP_KEY_HELP_EXTRA:
     274        /* We print some extra information.  */
     275        if (asprintf (&tp, gettext ("\
     276  For bug reporting instructions, please see:\n\
     277  %s.\n"), REPORT_BUGS_TO) < 0)
     278  	return NULL;
     279        return tp;
     280      default:
     281        break;
     282      }
     283    return (char *) text;
     284  }
     285  
     286  
     287  /* Print the version information.  */
     288  static void
     289  print_version (FILE *stream, struct argp_state *state)
     290  {
     291    fprintf (stream, "locale %s%s\n", PKGVERSION, VERSION);
     292    fprintf (stream, gettext ("\
     293  Copyright (C) %s Free Software Foundation, Inc.\n\
     294  This is free software; see the source for copying conditions.  There is NO\n\
     295  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
     296  "), "2023");
     297    fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
     298  }
     299  
     300  
     301  /* Simple action function which prints arguments as strings.  */
     302  static void
     303  print_names (const void *nodep, VISIT value, int level)
     304  {
     305    if (value == postorder || value == leaf)
     306      puts (*(char **) nodep);
     307  }
     308  
     309  
     310  static int
     311  select_dirs (const struct dirent *dirent)
     312  {
     313    int result = 0;
     314  
     315    if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
     316      {
     317        mode_t mode = 0;
     318  
     319        if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
     320  	mode = DTTOIF (dirent->d_type);
     321        else
     322  	{
     323  	  struct stat64 st;
     324  	  char buf[sizeof (COMPLOCALEDIR)
     325  		   + strlen (dirent->d_name) + 1];
     326  
     327  	  stpcpy (stpcpy (stpcpy (buf, COMPLOCALEDIR), "/"),
     328  		  dirent->d_name);
     329  
     330  	  if (stat64 (buf, &st) == 0)
     331  	    mode = st.st_mode;
     332  	}
     333  
     334        result = S_ISDIR (mode);
     335      }
     336  
     337    return result;
     338  }
     339  
     340  
     341  static void
     342  print_LC_IDENTIFICATION (void *mapped, size_t size)
     343  {
     344    /* Read the information from the file.  */
     345    struct
     346      {
     347        unsigned int magic;
     348        unsigned int nstrings;
     349        unsigned int strindex[0];
     350      } *filedata = mapped;
     351  
     352    if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
     353        && (sizeof *filedata
     354  	  + (filedata->nstrings
     355  	     * sizeof (unsigned int))
     356  	  <= size))
     357      {
     358        const char *str;
     359  
     360  #define HANDLE(idx, name) \
     361    str = ((char *) mapped						      \
     362  	 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]);    \
     363    if (*str != '\0')							      \
     364      printf ("%9s | %s\n", name, str)
     365        HANDLE (TITLE, "title");
     366        HANDLE (SOURCE, "source");
     367        HANDLE (ADDRESS, "address");
     368        HANDLE (CONTACT, "contact");
     369        HANDLE (EMAIL, "email");
     370        HANDLE (TEL, "telephone");
     371        HANDLE (FAX, "fax");
     372        HANDLE (LANGUAGE, "language");
     373        HANDLE (TERRITORY, "territory");
     374        HANDLE (AUDIENCE, "audience");
     375        HANDLE (APPLICATION, "application");
     376        HANDLE (ABBREVIATION, "abbreviation");
     377        HANDLE (REVISION, "revision");
     378        HANDLE (DATE, "date");
     379      }
     380  }
     381  
     382  
     383  static void
     384  print_LC_CTYPE (void *mapped, size_t size)
     385  {
     386    struct
     387      {
     388        unsigned int magic;
     389        unsigned int nstrings;
     390        unsigned int strindex[0];
     391      } *filedata = mapped;
     392  
     393    if (filedata->magic == LIMAGIC (LC_CTYPE)
     394        && (sizeof *filedata
     395  	  + (filedata->nstrings
     396  	     * sizeof (unsigned int))
     397  	  <= size))
     398      {
     399        const char *str;
     400  
     401        str = ((char *) mapped
     402  	     + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
     403        if (*str != '\0')
     404  	printf ("  codeset | %s\n", str);
     405      }
     406  }
     407  
     408  
     409  /* Write the names of all available locales to stdout.  We have some
     410     sources of the information: the contents of the locale directory
     411     and the locale.alias file.  To avoid duplicates and print the
     412     result is a reasonable order we put all entries is a search tree
     413     and print them afterwards.  */
     414  static void
     415  write_locales (void)
     416  {
     417    char linebuf[80];
     418    void *all_data = NULL;
     419    struct dirent **dirents;
     420    int ndirents;
     421    int cnt;
     422    char *alias_path;
     423    size_t alias_path_len;
     424    char *entry;
     425    int first_locale = 1;
     426  
     427  #define PUT(name) tsearch (name, &all_data, \
     428  			   (int (*) (const void *, const void *)) strcoll)
     429  #define GET(name) tfind (name, &all_data, \
     430  			   (int (*) (const void *, const void *)) strcoll)
     431  
     432    /* `POSIX' locale is always available (POSIX.2 4.34.3).  */
     433    PUT ("POSIX");
     434    /* And so is the "C" locale.  */
     435    PUT ("C");
     436  
     437    memset (linebuf, '-', sizeof (linebuf) - 1);
     438    linebuf[sizeof (linebuf) - 1] = '\0';
     439  
     440    /* First scan the locale archive.  */
     441    if (write_archive_locales (&all_data, linebuf))
     442      first_locale = 0;
     443  
     444    /* Now we can look for all files in the directory.  */
     445    ndirents = scandir (COMPLOCALEDIR, &dirents, select_dirs,
     446  		      alphasort);
     447    for (cnt = 0; cnt < ndirents; ++cnt)
     448      {
     449        /* Test whether at least the LC_CTYPE data is there.  Some
     450  	 directories only contain translations.  */
     451        char buf[sizeof (COMPLOCALEDIR)
     452  	       + strlen (dirents[cnt]->d_name)
     453  	       + sizeof "/LC_IDENTIFICATION"];
     454        char *enddir;
     455        struct stat64 st;
     456  
     457        stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf,
     458  					       COMPLOCALEDIR),
     459  					       "/"),
     460  			       dirents[cnt]->d_name),
     461  	      "/LC_IDENTIFICATION");
     462  
     463        if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
     464  	{
     465  	  if (verbose && GET (dirents[cnt]->d_name) == NULL)
     466  	    {
     467  	      /* Provide some nice output of all kinds of
     468  		 information.  */
     469  	      int fd;
     470  
     471  	      if (! first_locale)
     472  		putchar_unlocked ('\n');
     473  	      first_locale = 0;
     474  
     475  	      printf ("locale: %-15.15s directory: %.*s\n%s\n",
     476  		      dirents[cnt]->d_name, (int) (enddir - buf), buf,
     477  		      linebuf);
     478  
     479  	      fd = open64 (buf, O_RDONLY);
     480  	      if (fd != -1)
     481  		{
     482  		  void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
     483  					 MAP_SHARED, fd, 0);
     484  		  if (mapped != MAP_FAILED)
     485  		    {
     486  		      print_LC_IDENTIFICATION (mapped, st.st_size);
     487  
     488  		      munmap (mapped, st.st_size);
     489  		    }
     490  
     491  		  close (fd);
     492  
     493  		  /* Now try to get the charset information.  */
     494  		  strcpy (enddir, "/LC_CTYPE");
     495  		  fd = open64 (buf, O_RDONLY);
     496  		  if (fd != -1 && fstat64 (fd, &st) >= 0
     497  		      && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
     498  					    MAP_SHARED, fd, 0))
     499  			  != MAP_FAILED))
     500  		    {
     501  		      print_LC_CTYPE (mapped, st.st_size);
     502  
     503  		      munmap (mapped, st.st_size);
     504  		    }
     505  
     506  		  if (fd != -1)
     507  		    close (fd);
     508  		}
     509  	    }
     510  
     511  	  /* If the verbose format is not selected we simply
     512  	     collect the names.  */
     513  	  PUT (xstrdup (dirents[cnt]->d_name));
     514  	}
     515      }
     516    if (ndirents > 0)
     517      free (dirents);
     518  
     519    /* Now read the locale.alias files.  */
     520    if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
     521      error (1, errno, gettext ("while preparing output"));
     522  
     523    entry = NULL;
     524    while ((entry = argz_next (alias_path, alias_path_len, entry)))
     525      {
     526        static const char aliasfile[] = "/locale.alias";
     527        FILE *fp;
     528        char full_name[strlen (entry) + sizeof aliasfile];
     529  
     530        stpcpy (stpcpy (full_name, entry), aliasfile);
     531        fp = fopen (full_name, "rm");
     532        if (fp == NULL)
     533  	/* Ignore non-existing files.  */
     534  	continue;
     535  
     536        /* No threads present.  */
     537        __fsetlocking (fp, FSETLOCKING_BYCALLER);
     538  
     539        while (! feof_unlocked (fp))
     540  	{
     541  	  /* It is a reasonable approach to use a fix buffer here
     542  	     because
     543  	     a) we are only interested in the first two fields
     544  	     b) these fields must be usable as file names and so must
     545  		not be that long  */
     546  	  char buf[BUFSIZ];
     547  	  char *alias;
     548  	  char *value;
     549  	  char *cp;
     550  
     551  	  if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
     552  	    /* EOF reached.  */
     553  	    break;
     554  
     555  	  cp = buf;
     556  	  /* Ignore leading white space.  */
     557  	  while (isspace (cp[0]) && cp[0] != '\n')
     558  	    ++cp;
     559  
     560  	  /* A leading '#' signals a comment line.  */
     561  	  if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
     562  	    {
     563  	      alias = cp++;
     564  	      while (cp[0] != '\0' && !isspace (cp[0]))
     565  		++cp;
     566  	      /* Terminate alias name.  */
     567  	      if (cp[0] != '\0')
     568  		*cp++ = '\0';
     569  
     570  	      /* Now look for the beginning of the value.  */
     571  	      while (isspace (cp[0]))
     572  		++cp;
     573  
     574  	      if (cp[0] != '\0')
     575  		{
     576  		  value = cp++;
     577  		  while (cp[0] != '\0' && !isspace (cp[0]))
     578  		    ++cp;
     579  		  /* Terminate value.  */
     580  		  if (cp[0] == '\n')
     581  		    {
     582  		      /* This has to be done to make the following
     583  			 test for the end of line possible.  We are
     584  			 looking for the terminating '\n' which do not
     585  			 overwrite here.  */
     586  		      *cp++ = '\0';
     587  		      *cp = '\n';
     588  		    }
     589  		  else if (cp[0] != '\0')
     590  		    *cp++ = '\0';
     591  
     592  		  /* Add the alias.  */
     593  		  if (! verbose && GET (value) != NULL)
     594  		    PUT (xstrdup (alias));
     595  		}
     596  	    }
     597  
     598  	  /* Possibly not the whole line fits into the buffer.
     599  	     Ignore the rest of the line.  */
     600  	  while (strchr (cp, '\n') == NULL)
     601  	    {
     602  	      cp = buf;
     603  	      if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
     604  		/* Make sure the inner loop will be left.  The outer
     605  		   loop will exit at the `feof' test.  */
     606  		*cp = '\n';
     607  	    }
     608  	}
     609  
     610        fclose (fp);
     611      }
     612  
     613    if (! verbose)
     614      {
     615        twalk (all_data, print_names);
     616      }
     617  }
     618  
     619  
     620  struct nameent
     621  {
     622    char *name;
     623    uint32_t locrec_offset;
     624  };
     625  
     626  
     627  static int
     628  nameentcmp (const void *a, const void *b)
     629  {
     630    return strcoll (((const struct nameent *) a)->name,
     631  		  ((const struct nameent *) b)->name);
     632  }
     633  
     634  
     635  static int
     636  write_archive_locales (void **all_datap, char *linebuf)
     637  {
     638    struct stat64 st;
     639    void *all_data = *all_datap;
     640    size_t len = 0;
     641    struct locarhead *head;
     642    struct namehashent *namehashtab;
     643    char *addr = MAP_FAILED;
     644    int fd, ret = 0;
     645    uint32_t cnt;
     646  
     647    fd = open64 (ARCHIVE_NAME, O_RDONLY);
     648    if (fd < 0)
     649      return 0;
     650  
     651    if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
     652      goto error_out;
     653  
     654    len = st.st_size;
     655    addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
     656    if (addr == MAP_FAILED)
     657      goto error_out;
     658  
     659    head = (struct locarhead *) addr;
     660    if (head->namehash_offset + head->namehash_size > len
     661        || head->string_offset + head->string_size > len
     662        || head->locrectab_offset + head->locrectab_size > len
     663        || head->sumhash_offset + head->sumhash_size > len)
     664      goto error_out;
     665  
     666    namehashtab = (struct namehashent *) (addr + head->namehash_offset);
     667    if (! verbose)
     668      {
     669        for (cnt = 0; cnt < head->namehash_size; ++cnt)
     670  	if (namehashtab[cnt].locrec_offset != 0)
     671  	  {
     672  	    PUT (xstrdup (addr + namehashtab[cnt].name_offset));
     673  	    ++ret;
     674  	  }
     675      }
     676    else
     677      {
     678        struct nameent *names;
     679        uint32_t used;
     680  
     681        names = (struct nameent *) xmalloc (head->namehash_used
     682  					  * sizeof (struct nameent));
     683        for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
     684  	if (namehashtab[cnt].locrec_offset != 0)
     685  	  {
     686  	    names[used].name = addr + namehashtab[cnt].name_offset;
     687  	    names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
     688  	  }
     689  
     690        /* Sort the names.  */
     691        qsort (names, used, sizeof (struct nameent), nameentcmp);
     692  
     693        for (cnt = 0; cnt < used; ++cnt)
     694  	{
     695  	  struct locrecent *locrec;
     696  
     697  	  PUT (xstrdup (names[cnt].name));
     698  
     699  	  if (cnt)
     700  	    putchar_unlocked ('\n');
     701  
     702  	  printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
     703  		  names[cnt].name, linebuf);
     704  
     705  	  locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
     706  
     707  	  print_LC_IDENTIFICATION (addr
     708  				   + locrec->record[LC_IDENTIFICATION].offset,
     709  				   locrec->record[LC_IDENTIFICATION].len);
     710  
     711  	  print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
     712  			  locrec->record[LC_CTYPE].len);
     713  	}
     714  
     715        free (names);
     716  
     717        ret = used;
     718      }
     719  
     720  error_out:
     721    if (addr != MAP_FAILED)
     722      munmap (addr, len);
     723    close (fd);
     724    *all_datap = all_data;
     725    return ret;
     726  }
     727  
     728  
     729  /* Write the names of all available character maps to stdout.  */
     730  static void
     731  write_charmaps (void)
     732  {
     733    void *all_data = NULL;
     734    CHARMAP_DIR *dir;
     735    const char *dirent;
     736  
     737    /* Look for all files in the charmap directory.  */
     738    dir = charmap_opendir (CHARMAP_PATH);
     739    if (dir == NULL)
     740      return;
     741  
     742    while ((dirent = charmap_readdir (dir)) != NULL)
     743      {
     744        char **aliases;
     745        char **p;
     746  
     747        PUT (xstrdup (dirent));
     748  
     749        aliases = charmap_aliases (CHARMAP_PATH, dirent);
     750  
     751  #if 0
     752        /* Add the code_set_name and the aliases.  */
     753        for (p = aliases; *p; p++)
     754  	PUT (xstrdup (*p));
     755  #else
     756        /* Add the code_set_name only.  Most aliases are obsolete.  */
     757        p = aliases;
     758        if (*p)
     759  	PUT (xstrdup (*p));
     760  #endif
     761  
     762        charmap_free_aliases (aliases);
     763      }
     764  
     765    charmap_closedir (dir);
     766  
     767    twalk (all_data, print_names);
     768  }
     769  
     770  /* Print a properly quoted assignment of NAME with VAL, using double
     771     quotes iff DQUOTE is true.  */
     772  static void
     773  print_assignment (const char *name, const char *val, bool dquote)
     774  {
     775    printf ("%s=", name);
     776    if (dquote)
     777      putchar ('"');
     778    while (*val != '\0')
     779      {
     780        size_t segment
     781  	= strcspn (val, dquote ? "$`\"\\" : "~|&;<>()$`\\\"' \t\n");
     782        printf ("%.*s", (int) segment, val);
     783        val += segment;
     784        if (*val == '\0')
     785  	break;
     786        putchar ('\\');
     787        putchar (*val++);
     788      }
     789    if (dquote)
     790      putchar ('"');
     791    putchar ('\n');
     792  }
     793  
     794  /* We have to show the contents of the environments determining the
     795     locale.  */
     796  static void
     797  show_locale_vars (void)
     798  {
     799    const char *lcall = getenv ("LC_ALL") ?: "";
     800    const char *lang = getenv ("LANG") ?: "";
     801  
     802    /* LANG has to be the first value.  */
     803    print_assignment ("LANG", lang, false);
     804  
     805    /* Now all categories in an unspecified order.  */
     806    for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
     807      if (cat_no != LC_ALL)
     808        {
     809  	const char *name = category[cat_no].name;
     810  	const char *val = getenv (name);
     811  
     812  	if (lcall[0] != '\0' || val == NULL)
     813  	  print_assignment (name,
     814  			    lcall[0] != '\0' ? lcall
     815  			    : lang[0] != '\0' ? lang
     816  			    : "POSIX",
     817  			    true);
     818  	else
     819  	  print_assignment (name, val, false);
     820        }
     821  
     822    /* The last is the LC_ALL value.  */
     823    print_assignment ("LC_ALL", lcall, false);
     824  }
     825  
     826  
     827  /* Subroutine of show_info, below.  */
     828  static void
     829  print_item (struct cat_item *item)
     830  {
     831    switch (item->value_type)
     832      {
     833      case string:
     834        if (show_keyword_name)
     835  	printf ("%s=\"", item->name);
     836        fputs (nl_langinfo (item->item_id) ? : "", stdout);
     837        if (show_keyword_name)
     838  	putchar ('"');
     839        putchar ('\n');
     840        break;
     841      case stringarray:
     842        {
     843  	const char *val;
     844  	int cnt;
     845  
     846  	if (show_keyword_name)
     847  	  printf ("%s=\"", item->name);
     848  
     849  	for (cnt = 0; cnt < item->max - 1; ++cnt)
     850  	  {
     851  	    val = nl_langinfo (item->item_id + cnt);
     852  	    if (val != NULL)
     853  	      fputs (val, stdout);
     854  	    putchar (';');
     855  	  }
     856  
     857  	val = nl_langinfo (item->item_id + cnt);
     858  	if (val != NULL)
     859  	  fputs (val, stdout);
     860  
     861  	if (show_keyword_name)
     862  	  putchar ('"');
     863  	putchar ('\n');
     864        }
     865        break;
     866      case stringlist:
     867        {
     868  	int first = 1;
     869  	const char *val = nl_langinfo (item->item_id) ? : "";
     870  
     871  	if (show_keyword_name)
     872  	  printf ("%s=", item->name);
     873  
     874  	for (int cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
     875  	  {
     876  	    printf ("%s%s%s%s", first ? "" : ";",
     877  		    show_keyword_name ? "\"" : "", val,
     878  		    show_keyword_name ? "\"" : "");
     879  	    val = strchr (val, '\0') + 1;
     880  	    first = 0;
     881  	  }
     882  	putchar ('\n');
     883        }
     884        break;
     885      case byte:
     886        {
     887  	const char *val = nl_langinfo (item->item_id);
     888  
     889  	if (show_keyword_name)
     890  	  printf ("%s=", item->name);
     891  
     892  	if (val != NULL)
     893  	  printf ("%d", *val == '\377' ? -1 : *val);
     894  	putchar ('\n');
     895        }
     896        break;
     897      case bytearray:
     898        {
     899  	const char *val = nl_langinfo (item->item_id);
     900  	int cnt = val ? strlen (val) : 0;
     901  
     902  	if (show_keyword_name)
     903  	  printf ("%s=", item->name);
     904  
     905  	while (cnt > 1)
     906  	  {
     907  	    printf ("%d;", *val == '\177' ? -1 : *val);
     908  	    --cnt;
     909  	    ++val;
     910  	  }
     911  
     912  	printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
     913        }
     914        break;
     915      case word:
     916        {
     917  	union { unsigned int word; char *string; } val;
     918  	val.string = nl_langinfo (item->item_id);
     919  	if (show_keyword_name)
     920  	  printf ("%s=", item->name);
     921  
     922  	printf ("%d\n", val.word);
     923        }
     924        break;
     925      case wordarray:
     926        {
     927  	int first = 1;
     928  	union { unsigned int *wordarray; char *string; } val;
     929  
     930  	val.string = nl_langinfo (item->item_id);
     931  	if (show_keyword_name)
     932  	  printf ("%s=", item->name);
     933  
     934  	for (int cnt = 0; cnt < item->max; ++cnt)
     935  	  {
     936  	    printf ("%s%d", first ? "" : ";", val.wordarray[cnt]);
     937  	    first = 0;
     938  	  }
     939  	putchar ('\n');
     940        }
     941        break;
     942      case wstring:
     943      case wstringarray:
     944      case wstringlist:
     945        /* We don't print wide character information since the same
     946  	 information is available in a multibyte string.  */
     947      default:
     948        break;
     949      }
     950  }
     951  
     952  /* Show the information request for NAME.  */
     953  static void
     954  show_info (const char *name)
     955  {
     956    for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
     957      if (cat_no != LC_ALL)
     958        {
     959  	if (strcmp (name, category[cat_no].name) == 0)
     960  	  /* Print the whole category.  */
     961  	  {
     962  	    if (show_category_name != 0)
     963  	      puts (category[cat_no].name);
     964  
     965  	    for (size_t item_no = 0;
     966  		 item_no < category[cat_no].number;
     967  		 ++item_no)
     968  	      print_item (&category[cat_no].item_desc[item_no]);
     969  
     970  	    return;
     971  	  }
     972  
     973  	for (size_t item_no = 0; item_no < category[cat_no].number; ++item_no)
     974  	  if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
     975  	    {
     976  	      if (show_category_name != 0)
     977  		puts (category[cat_no].name);
     978  
     979  	      print_item (&category[cat_no].item_desc[item_no]);
     980  	      return;
     981  	    }
     982        }
     983  
     984    /* The name is not a standard one.
     985       For testing and perhaps advanced use allow some more symbols.  */
     986    locale_special (name, show_category_name, show_keyword_name);
     987  }
     988  
     989  /* Set to true by try_setlocale if setlocale fails.  Used by
     990     setlocale_diagnostics.  */
     991  static bool setlocale_failed;
     992  
     993  /* Call setlocale, with non-fatal error reporting.  */
     994  static void
     995  try_setlocale (int category, const char *category_name)
     996  {
     997    if (setlocale (category, "") == NULL)
     998      {
     999        error (0, errno, gettext ("Cannot set %s to default locale"),
    1000  	     category_name);
    1001        setlocale_failed = true;
    1002      }
    1003  }
    1004  
    1005  /* Return a quoted version of the passed string, or NULL on error.  */
    1006  static char *
    1007  quote_string (const char *input)
    1008  {
    1009    char *buffer;
    1010    size_t length;
    1011    FILE *stream = open_memstream (&buffer, &length);
    1012    if (stream == NULL)
    1013      return NULL;
    1014  
    1015    while (true)
    1016      {
    1017        unsigned char ch = *input++;
    1018        if (ch == '\0')
    1019  	break;
    1020  
    1021        /* Use C backslash escapes for those control characters for
    1022           which they are defined.  */
    1023        switch (ch)
    1024          {
    1025            case '\a':
    1026              putc_unlocked ('\\', stream);
    1027              putc_unlocked ('a', stream);
    1028              break;
    1029            case '\b':
    1030              putc_unlocked ('\\', stream);
    1031              putc_unlocked ('b', stream);
    1032              break;
    1033            case '\f':
    1034              putc_unlocked ('\\', stream);
    1035              putc_unlocked ('f', stream);
    1036              break;
    1037            case '\n':
    1038              putc_unlocked ('\\', stream);
    1039              putc_unlocked ('n', stream);
    1040              break;
    1041            case '\r':
    1042              putc_unlocked ('\\', stream);
    1043              putc_unlocked ('r', stream);
    1044              break;
    1045            case '\t':
    1046              putc_unlocked ('\\', stream);
    1047              putc_unlocked ('t', stream);
    1048              break;
    1049            case '\v':
    1050              putc_unlocked ('\\', stream);
    1051              putc_unlocked ('v', stream);
    1052              break;
    1053            case '\\':
    1054            case '\'':
    1055            case '\"':
    1056              putc_unlocked ('\\', stream);
    1057              putc_unlocked (ch, stream);
    1058              break;
    1059          default:
    1060            if (ch < ' ' || ch > '~')
    1061              /* Use octal sequences because they are fixed width,
    1062                 unlike hexadecimal sequences.  */
    1063              fprintf (stream, "\\%03o", ch);
    1064            else
    1065              putc_unlocked (ch, stream);
    1066          }
    1067      }
    1068  
    1069    if (ferror (stream))
    1070      {
    1071        fclose (stream);
    1072        free (buffer);
    1073        return NULL;
    1074      }
    1075    if (fclose (stream) != 0)
    1076      {
    1077        free (buffer);
    1078        return NULL;
    1079      }
    1080  
    1081    return buffer;
    1082  }
    1083  
    1084  /* Print additional information if there was a setlocale error (during
    1085     try_setlocale).  */
    1086  static void
    1087  setlocale_diagnostics (void)
    1088  {
    1089    if (setlocale_failed)
    1090      {
    1091        const char *locpath = getenv ("LOCPATH");
    1092        if (locpath != NULL)
    1093  	{
    1094  	  char *quoted = quote_string (locpath);
    1095  	  if (quoted != NULL)
    1096  	    fprintf (stderr,
    1097  		     gettext ("\
    1098  warning: The LOCPATH variable is set to \"%s\"\n"),
    1099  		     quoted);
    1100  	  else
    1101  	    fputs ("warning: The LOCPATH variable is set\n", stderr);
    1102  	  free (quoted);
    1103  	}
    1104      }
    1105  }