Blame locale/programs/locale.c

Packit 6c4009
/* Implementation of the locale program according to POSIX 9945-2.
Packit 6c4009
   Copyright (C) 1995-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1995.
Packit 6c4009
Packit 6c4009
   This program is free software; you can redistribute it and/or modify
Packit 6c4009
   it under the terms of the GNU General Public License as published
Packit 6c4009
   by the Free Software Foundation; version 2 of the License, or
Packit 6c4009
   (at your option) any later version.
Packit 6c4009
Packit 6c4009
   This program is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 6c4009
   GNU General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU General Public License
Packit 6c4009
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#ifdef HAVE_CONFIG_H
Packit 6c4009
# include <config.h>
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#include <argp.h>
Packit 6c4009
#include <argz.h>
Packit 6c4009
#include <dirent.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <error.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <langinfo.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
#include <limits.h>
Packit 6c4009
#include <locale.h>
Packit 6c4009
#include <search.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdio_ext.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
#include <sys/stat.h>
Packit 6c4009
Packit 6c4009
#include "record-status.h"
Packit 6c4009
#include "localeinfo.h"
Packit 6c4009
#include "charmap-dir.h"
Packit 6c4009
#include "../locarchive.h"
Packit 6c4009
#include <programs/xmalloc.h>
Packit 6c4009
Packit 6c4009
#define ARCHIVE_NAME COMPLOCALEDIR "/locale-archive"
Packit 6c4009
Packit 6c4009
/* If set print the name of the category.  */
Packit 6c4009
static int show_category_name;
Packit 6c4009
Packit 6c4009
/* If set print the name of the item.  */
Packit 6c4009
static int show_keyword_name;
Packit 6c4009
Packit 6c4009
/* Print names of all available locales.  */
Packit 6c4009
static int do_all;
Packit 6c4009
Packit 6c4009
/* Print names of all available character maps.  */
Packit 6c4009
static int do_charmaps = 0;
Packit 6c4009
Packit 6c4009
/* Name and version of program.  */
Packit 6c4009
static void print_version (FILE *stream, struct argp_state *state);
Packit 6c4009
void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
Packit 6c4009
Packit 6c4009
/* Definitions of arguments for argp functions.  */
Packit 6c4009
static const struct argp_option options[] =
Packit 6c4009
{
Packit 6c4009
  { NULL, 0, NULL, 0, N_("System information:") },
Packit 6c4009
  { "all-locales", 'a', NULL, OPTION_NO_USAGE,
Packit 6c4009
    N_("Write names of available locales") },
Packit 6c4009
  { "charmaps", 'm', NULL, OPTION_NO_USAGE,
Packit 6c4009
    N_("Write names of available charmaps") },
Packit 6c4009
  { NULL, 0, NULL, 0, N_("Modify output format:") },
Packit 6c4009
  { "category-name", 'c', NULL, 0, N_("Write names of selected categories") },
Packit 6c4009
  { "keyword-name", 'k', NULL, 0, N_("Write names of selected keywords") },
Packit 6c4009
  { "verbose", 'v', NULL, 0, N_("Print more information") },
Packit 6c4009
  { NULL, 0, NULL, 0, NULL }
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* Short description of program.  */
Packit 6c4009
static const char doc[] = N_("Get locale-specific information.");
Packit 6c4009
Packit 6c4009
/* Strings for arguments in help texts.  */
Packit 6c4009
static const char args_doc[] = N_("NAME\n[-a|-m]");
Packit 6c4009
Packit 6c4009
/* Prototype for option handler.  */
Packit 6c4009
static error_t parse_opt (int key, char *arg, struct argp_state *state);
Packit 6c4009
Packit 6c4009
/* Function to print some extra text in the help message.  */
Packit 6c4009
static char *more_help (int key, const char *text, void *input);
Packit 6c4009
Packit 6c4009
/* Data structure to communicate with argp functions.  */
Packit 6c4009
static struct argp argp =
Packit 6c4009
{
Packit 6c4009
  options, parse_opt, args_doc, doc, NULL, more_help
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* We don't have these constants defined because we don't use them.  Give
Packit 6c4009
   default values.  */
Packit 6c4009
#define CTYPE_MB_CUR_MIN 0
Packit 6c4009
#define CTYPE_MB_CUR_MAX 0
Packit 6c4009
#define CTYPE_HASH_SIZE 0
Packit 6c4009
#define CTYPE_HASH_LAYERS 0
Packit 6c4009
#define CTYPE_CLASS 0
Packit 6c4009
#define CTYPE_TOUPPER_EB 0
Packit 6c4009
#define CTYPE_TOLOWER_EB 0
Packit 6c4009
#define CTYPE_TOUPPER_EL 0
Packit 6c4009
#define CTYPE_TOLOWER_EL 0
Packit 6c4009
Packit 6c4009
/* Definition of the data structure which represents a category and its
Packit 6c4009
   items.  */
Packit 6c4009
struct category
Packit 6c4009
{
Packit 6c4009
  int cat_id;
Packit 6c4009
  const char *name;
Packit 6c4009
  size_t number;
Packit 6c4009
  struct cat_item
Packit 6c4009
  {
Packit 6c4009
    int item_id;
Packit 6c4009
    const char *name;
Packit 6c4009
    enum { std, opt } status;
Packit 6c4009
    enum value_type value_type;
Packit 6c4009
    int min;
Packit 6c4009
    int max;
Packit 6c4009
  } *item_desc;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* Simple helper macro.  */
Packit 6c4009
#define NELEMS(arr) ((sizeof (arr)) / (sizeof (arr[0])))
Packit 6c4009
Packit 6c4009
/* For some tricky stuff.  */
Packit 6c4009
#define NO_PAREN(Item, More...) Item, ## More
Packit 6c4009
Packit 6c4009
/* We have all categories defined in `categories.def'.  Now construct
Packit 6c4009
   the description and data structure used for all categories.  */
Packit 6c4009
#define DEFINE_ELEMENT(Item, More...) { Item, ## More },
Packit 6c4009
#define DEFINE_CATEGORY(category, name, items, postload) \
Packit 6c4009
    static struct cat_item category##_desc[] =				      \
Packit 6c4009
      {									      \
Packit 6c4009
	NO_PAREN items							      \
Packit 6c4009
      };
Packit 6c4009
Packit 6c4009
#include "categories.def"
Packit 6c4009
#undef DEFINE_CATEGORY
Packit 6c4009
Packit 6c4009
static struct category category[] =
Packit 6c4009
  {
Packit 6c4009
#define DEFINE_CATEGORY(category, name, items, postload) \
Packit 6c4009
    [category] = { _NL_NUM_##category, name, NELEMS (category##_desc),	      \
Packit 6c4009
		   category##_desc },
Packit 6c4009
#include "categories.def"
Packit 6c4009
#undef DEFINE_CATEGORY
Packit 6c4009
  };
Packit 6c4009
#define NCATEGORIES NELEMS (category)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Automatically set variable.  */
Packit 6c4009
extern const char *__progname;
Packit 6c4009
Packit 6c4009
/* helper function for extended name handling.  */
Packit 6c4009
extern void locale_special (const char *name, int show_category_name,
Packit 6c4009
			    int show_keyword_name);
Packit 6c4009
Packit 6c4009
/* Prototypes for local functions.  */
Packit 6c4009
static void print_LC_IDENTIFICATION (void *mapped, size_t size);
Packit 6c4009
static void print_LC_CTYPE (void *mapped, size_t size);
Packit 6c4009
static void write_locales (void);
Packit 6c4009
static int nameentcmp (const void *a, const void *b);
Packit 6c4009
static int write_archive_locales (void **all_datap, char *linebuf);
Packit 6c4009
static void write_charmaps (void);
Packit 6c4009
static void show_locale_vars (void);
Packit 6c4009
static void show_info (const char *name);
Packit Service be9950
static void try_setlocale (int category, const char *category_name);
Packit Service be9950
static char *quote_string (const char *input);
Packit Service be9950
static void setlocale_diagnostics (void);
Packit 6c4009
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
main (int argc, char *argv[])
Packit 6c4009
{
Packit 6c4009
  int remaining;
Packit 6c4009
Packit 6c4009
  /* Set initial values for global variables.  */
Packit 6c4009
  show_category_name = 0;
Packit 6c4009
  show_keyword_name = 0;
Packit 6c4009
Packit 6c4009
  /* Set locale.  Do not set LC_ALL because the other categories must
Packit 6c4009
     not be affected (according to POSIX.2).  */
Packit Service be9950
  try_setlocale (LC_CTYPE, "LC_CTYPE");
Packit Service be9950
  try_setlocale (LC_MESSAGES, "LC_MESSAGES");
Packit 6c4009
Packit 6c4009
  /* Initialize the message catalog.  */
Packit 6c4009
  textdomain (PACKAGE);
Packit 6c4009
Packit 6c4009
  /* Parse and process arguments.  */
Packit 6c4009
  argp_parse (&argp, argc, argv, 0, &remaining, NULL);
Packit 6c4009
Packit 6c4009
  /* `-a' requests the names of all available locales.  */
Packit 6c4009
  if (do_all != 0)
Packit 6c4009
    {
Packit Service be9950
      setlocale_diagnostics ();
Packit Service be9950
      try_setlocale (LC_COLLATE, "LC_COLLATE");
Packit 6c4009
      write_locales ();
Packit 6c4009
      exit (EXIT_SUCCESS);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* `m' requests the names of all available charmaps.  The names can be
Packit 6c4009
     used for the -f argument to localedef(1).  */
Packit 6c4009
  if (do_charmaps != 0)
Packit 6c4009
    {
Packit Service be9950
      setlocale_diagnostics ();
Packit 6c4009
      write_charmaps ();
Packit 6c4009
      exit (EXIT_SUCCESS);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Specific information about the current locale are requested.
Packit 6c4009
     Change to this locale now.  */
Packit Service be9950
  try_setlocale (LC_ALL, "LC_ALL");
Packit Service be9950
  setlocale_diagnostics ();
Packit 6c4009
Packit 6c4009
  /* If no real argument is given we have to print the contents of the
Packit 6c4009
     current locale definition variables.  These are LANG and the LC_*.  */
Packit 6c4009
  if (remaining == argc && show_keyword_name == 0 && show_category_name == 0)
Packit 6c4009
    {
Packit 6c4009
      show_locale_vars ();
Packit 6c4009
      exit (EXIT_SUCCESS);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Process all given names.  */
Packit 6c4009
  while (remaining <  argc)
Packit 6c4009
    show_info (argv[remaining++]);
Packit 6c4009
Packit 6c4009
  exit (EXIT_SUCCESS);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Handle program arguments.  */
Packit 6c4009
static error_t
Packit 6c4009
parse_opt (int key, char *arg, struct argp_state *state)
Packit 6c4009
{
Packit 6c4009
  switch (key)
Packit 6c4009
    {
Packit 6c4009
    case 'a':
Packit 6c4009
      do_all = 1;
Packit 6c4009
      break;
Packit 6c4009
    case 'c':
Packit 6c4009
      show_category_name = 1;
Packit 6c4009
      break;
Packit 6c4009
    case 'm':
Packit 6c4009
      do_charmaps = 1;
Packit 6c4009
      break;
Packit 6c4009
    case 'k':
Packit 6c4009
      show_keyword_name = 1;
Packit 6c4009
      break;
Packit 6c4009
    case 'v':
Packit 6c4009
      verbose = 1;
Packit 6c4009
      break;
Packit 6c4009
    default:
Packit 6c4009
      return ARGP_ERR_UNKNOWN;
Packit 6c4009
    }
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static char *
Packit 6c4009
more_help (int key, const char *text, void *input)
Packit 6c4009
{
Packit 6c4009
  char *tp = NULL;
Packit 6c4009
  switch (key)
Packit 6c4009
    {
Packit 6c4009
    case ARGP_KEY_HELP_EXTRA:
Packit 6c4009
      /* We print some extra information.  */
Packit 6c4009
      if (asprintf (&tp, gettext ("\
Packit 6c4009
For bug reporting instructions, please see:\n\
Packit 6c4009
%s.\n"), REPORT_BUGS_TO) < 0)
Packit 6c4009
	return NULL;
Packit 6c4009
      return tp;
Packit 6c4009
    default:
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
  return (char *) text;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Print the version information.  */
Packit 6c4009
static void
Packit 6c4009
print_version (FILE *stream, struct argp_state *state)
Packit 6c4009
{
Packit 6c4009
  fprintf (stream, "locale %s%s\n", PKGVERSION, VERSION);
Packit 6c4009
  fprintf (stream, gettext ("\
Packit 6c4009
Copyright (C) %s Free Software Foundation, Inc.\n\
Packit 6c4009
This is free software; see the source for copying conditions.  There is NO\n\
Packit 6c4009
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
Packit 6c4009
"), "2018");
Packit 6c4009
  fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Simple action function which prints arguments as strings.  */
Packit 6c4009
static void
Packit 6c4009
print_names (const void *nodep, VISIT value, int level)
Packit 6c4009
{
Packit 6c4009
  if (value == postorder || value == leaf)
Packit 6c4009
    puts (*(char **) nodep);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
select_dirs (const struct dirent *dirent)
Packit 6c4009
{
Packit 6c4009
  int result = 0;
Packit 6c4009
Packit 6c4009
  if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0)
Packit 6c4009
    {
Packit 6c4009
      mode_t mode = 0;
Packit 6c4009
Packit 6c4009
      if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
Packit 6c4009
	mode = DTTOIF (dirent->d_type);
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  struct stat64 st;
Packit 6c4009
	  char buf[sizeof (COMPLOCALEDIR)
Packit 6c4009
		   + strlen (dirent->d_name) + 1];
Packit 6c4009
Packit 6c4009
	  stpcpy (stpcpy (stpcpy (buf, COMPLOCALEDIR), "/"),
Packit 6c4009
		  dirent->d_name);
Packit 6c4009
Packit 6c4009
	  if (stat64 (buf, &st) == 0)
Packit 6c4009
	    mode = st.st_mode;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      result = S_ISDIR (mode);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
print_LC_IDENTIFICATION (void *mapped, size_t size)
Packit 6c4009
{
Packit 6c4009
  /* Read the information from the file.  */
Packit 6c4009
  struct
Packit 6c4009
    {
Packit 6c4009
      unsigned int magic;
Packit 6c4009
      unsigned int nstrings;
Packit 6c4009
      unsigned int strindex[0];
Packit 6c4009
    } *filedata = mapped;
Packit 6c4009
Packit 6c4009
  if (filedata->magic == LIMAGIC (LC_IDENTIFICATION)
Packit 6c4009
      && (sizeof *filedata
Packit 6c4009
	  + (filedata->nstrings
Packit 6c4009
	     * sizeof (unsigned int))
Packit 6c4009
	  <= size))
Packit 6c4009
    {
Packit 6c4009
      const char *str;
Packit 6c4009
Packit 6c4009
#define HANDLE(idx, name) \
Packit 6c4009
  str = ((char *) mapped						      \
Packit 6c4009
	 + filedata->strindex[_NL_ITEM_INDEX (_NL_IDENTIFICATION_##idx)]);    \
Packit 6c4009
  if (*str != '\0')							      \
Packit 6c4009
    printf ("%9s | %s\n", name, str)
Packit 6c4009
      HANDLE (TITLE, "title");
Packit 6c4009
      HANDLE (SOURCE, "source");
Packit 6c4009
      HANDLE (ADDRESS, "address");
Packit 6c4009
      HANDLE (CONTACT, "contact");
Packit 6c4009
      HANDLE (EMAIL, "email");
Packit 6c4009
      HANDLE (TEL, "telephone");
Packit 6c4009
      HANDLE (FAX, "fax");
Packit 6c4009
      HANDLE (LANGUAGE, "language");
Packit 6c4009
      HANDLE (TERRITORY, "territory");
Packit 6c4009
      HANDLE (AUDIENCE, "audience");
Packit 6c4009
      HANDLE (APPLICATION, "application");
Packit 6c4009
      HANDLE (ABBREVIATION, "abbreviation");
Packit 6c4009
      HANDLE (REVISION, "revision");
Packit 6c4009
      HANDLE (DATE, "date");
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
print_LC_CTYPE (void *mapped, size_t size)
Packit 6c4009
{
Packit 6c4009
  struct
Packit 6c4009
    {
Packit 6c4009
      unsigned int magic;
Packit 6c4009
      unsigned int nstrings;
Packit 6c4009
      unsigned int strindex[0];
Packit 6c4009
    } *filedata = mapped;
Packit 6c4009
Packit 6c4009
  if (filedata->magic == LIMAGIC (LC_CTYPE)
Packit 6c4009
      && (sizeof *filedata
Packit 6c4009
	  + (filedata->nstrings
Packit 6c4009
	     * sizeof (unsigned int))
Packit 6c4009
	  <= size))
Packit 6c4009
    {
Packit 6c4009
      const char *str;
Packit 6c4009
Packit 6c4009
      str = ((char *) mapped
Packit 6c4009
	     + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]);
Packit 6c4009
      if (*str != '\0')
Packit 6c4009
	printf ("  codeset | %s\n", str);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Write the names of all available locales to stdout.  We have some
Packit 6c4009
   sources of the information: the contents of the locale directory
Packit 6c4009
   and the locale.alias file.  To avoid duplicates and print the
Packit 6c4009
   result is a reasonable order we put all entries is a search tree
Packit 6c4009
   and print them afterwards.  */
Packit 6c4009
static void
Packit 6c4009
write_locales (void)
Packit 6c4009
{
Packit 6c4009
  char linebuf[80];
Packit 6c4009
  void *all_data = NULL;
Packit 6c4009
  struct dirent **dirents;
Packit 6c4009
  int ndirents;
Packit 6c4009
  int cnt;
Packit 6c4009
  char *alias_path;
Packit 6c4009
  size_t alias_path_len;
Packit 6c4009
  char *entry;
Packit 6c4009
  int first_locale = 1;
Packit 6c4009
Packit 6c4009
#define PUT(name) tsearch (name, &all_data, \
Packit 6c4009
			   (int (*) (const void *, const void *)) strcoll)
Packit 6c4009
#define GET(name) tfind (name, &all_data, \
Packit 6c4009
			   (int (*) (const void *, const void *)) strcoll)
Packit 6c4009
Packit 6c4009
  /* `POSIX' locale is always available (POSIX.2 4.34.3).  */
Packit 6c4009
  PUT ("POSIX");
Packit 6c4009
  /* And so is the "C" locale.  */
Packit 6c4009
  PUT ("C");
Packit 6c4009
Packit 6c4009
  memset (linebuf, '-', sizeof (linebuf) - 1);
Packit 6c4009
  linebuf[sizeof (linebuf) - 1] = '\0';
Packit 6c4009
Packit 6c4009
  /* First scan the locale archive.  */
Packit 6c4009
  if (write_archive_locales (&all_data, linebuf))
Packit 6c4009
    first_locale = 0;
Packit 6c4009
Packit 6c4009
  /* Now we can look for all files in the directory.  */
Packit 6c4009
  ndirents = scandir (COMPLOCALEDIR, &dirents, select_dirs,
Packit 6c4009
		      alphasort);
Packit 6c4009
  for (cnt = 0; cnt < ndirents; ++cnt)
Packit 6c4009
    {
Packit 6c4009
      /* Test whether at least the LC_CTYPE data is there.  Some
Packit 6c4009
	 directories only contain translations.  */
Packit 6c4009
      char buf[sizeof (COMPLOCALEDIR)
Packit 6c4009
	       + strlen (dirents[cnt]->d_name)
Packit 6c4009
	       + sizeof "/LC_IDENTIFICATION"];
Packit 6c4009
      char *enddir;
Packit 6c4009
      struct stat64 st;
Packit 6c4009
Packit 6c4009
      stpcpy (enddir = stpcpy (stpcpy (stpcpy (buf,
Packit 6c4009
					       COMPLOCALEDIR),
Packit 6c4009
					       "/"),
Packit 6c4009
			       dirents[cnt]->d_name),
Packit 6c4009
	      "/LC_IDENTIFICATION");
Packit 6c4009
Packit 6c4009
      if (stat64 (buf, &st) == 0 && S_ISREG (st.st_mode))
Packit 6c4009
	{
Packit 6c4009
	  if (verbose && GET (dirents[cnt]->d_name) == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      /* Provide some nice output of all kinds of
Packit 6c4009
		 information.  */
Packit 6c4009
	      int fd;
Packit 6c4009
Packit 6c4009
	      if (! first_locale)
Packit 6c4009
		putchar_unlocked ('\n');
Packit 6c4009
	      first_locale = 0;
Packit 6c4009
Packit 6c4009
	      printf ("locale: %-15.15s directory: %.*s\n%s\n",
Packit 6c4009
		      dirents[cnt]->d_name, (int) (enddir - buf), buf,
Packit 6c4009
		      linebuf);
Packit 6c4009
Packit 6c4009
	      fd = open64 (buf, O_RDONLY);
Packit 6c4009
	      if (fd != -1)
Packit 6c4009
		{
Packit 6c4009
		  void *mapped = mmap64 (NULL, st.st_size, PROT_READ,
Packit 6c4009
					 MAP_SHARED, fd, 0);
Packit 6c4009
		  if (mapped != MAP_FAILED)
Packit 6c4009
		    {
Packit 6c4009
		      print_LC_IDENTIFICATION (mapped, st.st_size);
Packit 6c4009
Packit 6c4009
		      munmap (mapped, st.st_size);
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  close (fd);
Packit 6c4009
Packit 6c4009
		  /* Now try to get the charset information.  */
Packit 6c4009
		  strcpy (enddir, "/LC_CTYPE");
Packit 6c4009
		  fd = open64 (buf, O_RDONLY);
Packit 6c4009
		  if (fd != -1 && fstat64 (fd, &st) >= 0
Packit 6c4009
		      && ((mapped = mmap64 (NULL, st.st_size, PROT_READ,
Packit 6c4009
					    MAP_SHARED, fd, 0))
Packit 6c4009
			  != MAP_FAILED))
Packit 6c4009
		    {
Packit 6c4009
		      print_LC_CTYPE (mapped, st.st_size);
Packit 6c4009
Packit 6c4009
		      munmap (mapped, st.st_size);
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  if (fd != -1)
Packit 6c4009
		    close (fd);
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* If the verbose format is not selected we simply
Packit 6c4009
	     collect the names.  */
Packit 6c4009
	  PUT (xstrdup (dirents[cnt]->d_name));
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  if (ndirents > 0)
Packit 6c4009
    free (dirents);
Packit 6c4009
Packit 6c4009
  /* Now read the locale.alias files.  */
Packit 6c4009
  if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
Packit 6c4009
    error (1, errno, gettext ("while preparing output"));
Packit 6c4009
Packit 6c4009
  entry = NULL;
Packit 6c4009
  while ((entry = argz_next (alias_path, alias_path_len, entry)))
Packit 6c4009
    {
Packit 6c4009
      static const char aliasfile[] = "/locale.alias";
Packit 6c4009
      FILE *fp;
Packit 6c4009
      char full_name[strlen (entry) + sizeof aliasfile];
Packit 6c4009
Packit 6c4009
      stpcpy (stpcpy (full_name, entry), aliasfile);
Packit 6c4009
      fp = fopen (full_name, "rm");
Packit 6c4009
      if (fp == NULL)
Packit 6c4009
	/* Ignore non-existing files.  */
Packit 6c4009
	continue;
Packit 6c4009
Packit 6c4009
      /* No threads present.  */
Packit 6c4009
      __fsetlocking (fp, FSETLOCKING_BYCALLER);
Packit 6c4009
Packit 6c4009
      while (! feof_unlocked (fp))
Packit 6c4009
	{
Packit 6c4009
	  /* It is a reasonable approach to use a fix buffer here
Packit 6c4009
	     because
Packit 6c4009
	     a) we are only interested in the first two fields
Packit 6c4009
	     b) these fields must be usable as file names and so must
Packit 6c4009
		not be that long  */
Packit 6c4009
	  char buf[BUFSIZ];
Packit 6c4009
	  char *alias;
Packit 6c4009
	  char *value;
Packit 6c4009
	  char *cp;
Packit 6c4009
Packit 6c4009
	  if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
Packit 6c4009
	    /* EOF reached.  */
Packit 6c4009
	    break;
Packit 6c4009
Packit 6c4009
	  cp = buf;
Packit 6c4009
	  /* Ignore leading white space.  */
Packit 6c4009
	  while (isspace (cp[0]) && cp[0] != '\n')
Packit 6c4009
	    ++cp;
Packit 6c4009
Packit 6c4009
	  /* A leading '#' signals a comment line.  */
Packit 6c4009
	  if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n')
Packit 6c4009
	    {
Packit 6c4009
	      alias = cp++;
Packit 6c4009
	      while (cp[0] != '\0' && !isspace (cp[0]))
Packit 6c4009
		++cp;
Packit 6c4009
	      /* Terminate alias name.  */
Packit 6c4009
	      if (cp[0] != '\0')
Packit 6c4009
		*cp++ = '\0';
Packit 6c4009
Packit 6c4009
	      /* Now look for the beginning of the value.  */
Packit 6c4009
	      while (isspace (cp[0]))
Packit 6c4009
		++cp;
Packit 6c4009
Packit 6c4009
	      if (cp[0] != '\0')
Packit 6c4009
		{
Packit 6c4009
		  value = cp++;
Packit 6c4009
		  while (cp[0] != '\0' && !isspace (cp[0]))
Packit 6c4009
		    ++cp;
Packit 6c4009
		  /* Terminate value.  */
Packit 6c4009
		  if (cp[0] == '\n')
Packit 6c4009
		    {
Packit 6c4009
		      /* This has to be done to make the following
Packit 6c4009
			 test for the end of line possible.  We are
Packit 6c4009
			 looking for the terminating '\n' which do not
Packit 6c4009
			 overwrite here.  */
Packit 6c4009
		      *cp++ = '\0';
Packit 6c4009
		      *cp = '\n';
Packit 6c4009
		    }
Packit 6c4009
		  else if (cp[0] != '\0')
Packit 6c4009
		    *cp++ = '\0';
Packit 6c4009
Packit 6c4009
		  /* Add the alias.  */
Packit 6c4009
		  if (! verbose && GET (value) != NULL)
Packit 6c4009
		    PUT (xstrdup (alias));
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Possibly not the whole line fits into the buffer.
Packit 6c4009
	     Ignore the rest of the line.  */
Packit 6c4009
	  while (strchr (cp, '\n') == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      cp = buf;
Packit 6c4009
	      if (fgets_unlocked (buf, BUFSIZ, fp) == NULL)
Packit 6c4009
		/* Make sure the inner loop will be left.  The outer
Packit 6c4009
		   loop will exit at the `feof' test.  */
Packit 6c4009
		*cp = '\n';
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      fclose (fp);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (! verbose)
Packit 6c4009
    {
Packit 6c4009
      twalk (all_data, print_names);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
struct nameent
Packit 6c4009
{
Packit 6c4009
  char *name;
Packit 6c4009
  uint32_t locrec_offset;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
nameentcmp (const void *a, const void *b)
Packit 6c4009
{
Packit 6c4009
  return strcoll (((const struct nameent *) a)->name,
Packit 6c4009
		  ((const struct nameent *) b)->name);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
write_archive_locales (void **all_datap, char *linebuf)
Packit 6c4009
{
Packit 6c4009
  struct stat64 st;
Packit 6c4009
  void *all_data = *all_datap;
Packit 6c4009
  size_t len = 0;
Packit 6c4009
  struct locarhead *head;
Packit 6c4009
  struct namehashent *namehashtab;
Packit 6c4009
  char *addr = MAP_FAILED;
Packit 6c4009
  int fd, ret = 0;
Packit 6c4009
  uint32_t cnt;
Packit 6c4009
Packit 6c4009
  fd = open64 (ARCHIVE_NAME, O_RDONLY);
Packit 6c4009
  if (fd < 0)
Packit 6c4009
    return 0;
Packit 6c4009
Packit 6c4009
  if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (*head))
Packit 6c4009
    goto error_out;
Packit 6c4009
Packit 6c4009
  len = st.st_size;
Packit 6c4009
  addr = mmap64 (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
Packit 6c4009
  if (addr == MAP_FAILED)
Packit 6c4009
    goto error_out;
Packit 6c4009
Packit 6c4009
  head = (struct locarhead *) addr;
Packit 6c4009
  if (head->namehash_offset + head->namehash_size > len
Packit 6c4009
      || head->string_offset + head->string_size > len
Packit 6c4009
      || head->locrectab_offset + head->locrectab_size > len
Packit 6c4009
      || head->sumhash_offset + head->sumhash_size > len)
Packit 6c4009
    goto error_out;
Packit 6c4009
Packit 6c4009
  namehashtab = (struct namehashent *) (addr + head->namehash_offset);
Packit 6c4009
  if (! verbose)
Packit 6c4009
    {
Packit 6c4009
      for (cnt = 0; cnt < head->namehash_size; ++cnt)
Packit 6c4009
	if (namehashtab[cnt].locrec_offset != 0)
Packit 6c4009
	  {
Packit 6c4009
	    PUT (xstrdup (addr + namehashtab[cnt].name_offset));
Packit 6c4009
	    ++ret;
Packit 6c4009
	  }
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      struct nameent *names;
Packit 6c4009
      uint32_t used;
Packit 6c4009
Packit 6c4009
      names = (struct nameent *) xmalloc (head->namehash_used
Packit 6c4009
					  * sizeof (struct nameent));
Packit 6c4009
      for (cnt = used = 0; cnt < head->namehash_size; ++cnt)
Packit 6c4009
	if (namehashtab[cnt].locrec_offset != 0)
Packit 6c4009
	  {
Packit 6c4009
	    names[used].name = addr + namehashtab[cnt].name_offset;
Packit 6c4009
	    names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
      /* Sort the names.  */
Packit 6c4009
      qsort (names, used, sizeof (struct nameent), nameentcmp);
Packit 6c4009
Packit 6c4009
      for (cnt = 0; cnt < used; ++cnt)
Packit 6c4009
	{
Packit 6c4009
	  struct locrecent *locrec;
Packit 6c4009
Packit 6c4009
	  PUT (xstrdup (names[cnt].name));
Packit 6c4009
Packit 6c4009
	  if (cnt)
Packit 6c4009
	    putchar_unlocked ('\n');
Packit 6c4009
Packit 6c4009
	  printf ("locale: %-15.15s archive: " ARCHIVE_NAME "\n%s\n",
Packit 6c4009
		  names[cnt].name, linebuf);
Packit 6c4009
Packit 6c4009
	  locrec = (struct locrecent *) (addr + names[cnt].locrec_offset);
Packit 6c4009
Packit 6c4009
	  print_LC_IDENTIFICATION (addr
Packit 6c4009
				   + locrec->record[LC_IDENTIFICATION].offset,
Packit 6c4009
				   locrec->record[LC_IDENTIFICATION].len);
Packit 6c4009
Packit 6c4009
	  print_LC_CTYPE (addr + locrec->record[LC_CTYPE].offset,
Packit 6c4009
			  locrec->record[LC_CTYPE].len);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      ret = used;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
error_out:
Packit 6c4009
  if (addr != MAP_FAILED)
Packit 6c4009
    munmap (addr, len);
Packit 6c4009
  close (fd);
Packit 6c4009
  *all_datap = all_data;
Packit 6c4009
  return ret;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Write the names of all available character maps to stdout.  */
Packit 6c4009
static void
Packit 6c4009
write_charmaps (void)
Packit 6c4009
{
Packit 6c4009
  void *all_data = NULL;
Packit 6c4009
  CHARMAP_DIR *dir;
Packit 6c4009
  const char *dirent;
Packit 6c4009
Packit 6c4009
  /* Look for all files in the charmap directory.  */
Packit 6c4009
  dir = charmap_opendir (CHARMAP_PATH);
Packit 6c4009
  if (dir == NULL)
Packit 6c4009
    return;
Packit 6c4009
Packit 6c4009
  while ((dirent = charmap_readdir (dir)) != NULL)
Packit 6c4009
    {
Packit 6c4009
      char **aliases;
Packit 6c4009
      char **p;
Packit 6c4009
Packit 6c4009
      PUT (xstrdup (dirent));
Packit 6c4009
Packit 6c4009
      aliases = charmap_aliases (CHARMAP_PATH, dirent);
Packit 6c4009
Packit 6c4009
#if 0
Packit 6c4009
      /* Add the code_set_name and the aliases.  */
Packit 6c4009
      for (p = aliases; *p; p++)
Packit 6c4009
	PUT (xstrdup (*p));
Packit 6c4009
#else
Packit 6c4009
      /* Add the code_set_name only.  Most aliases are obsolete.  */
Packit 6c4009
      p = aliases;
Packit 6c4009
      if (*p)
Packit 6c4009
	PUT (xstrdup (*p));
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
      charmap_free_aliases (aliases);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  charmap_closedir (dir);
Packit 6c4009
Packit 6c4009
  twalk (all_data, print_names);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Print a properly quoted assignment of NAME with VAL, using double
Packit 6c4009
   quotes iff DQUOTE is true.  */
Packit 6c4009
static void
Packit 6c4009
print_assignment (const char *name, const char *val, bool dquote)
Packit 6c4009
{
Packit 6c4009
  printf ("%s=", name);
Packit 6c4009
  if (dquote)
Packit 6c4009
    putchar ('"');
Packit 6c4009
  while (*val != '\0')
Packit 6c4009
    {
Packit 6c4009
      size_t segment
Packit 6c4009
	= strcspn (val, dquote ? "$`\"\\" : "~|&;<>()$`\\\"' \t\n");
Packit 6c4009
      printf ("%.*s", (int) segment, val);
Packit 6c4009
      val += segment;
Packit 6c4009
      if (*val == '\0')
Packit 6c4009
	break;
Packit 6c4009
      putchar ('\\');
Packit 6c4009
      putchar (*val++);
Packit 6c4009
    }
Packit 6c4009
  if (dquote)
Packit 6c4009
    putchar ('"');
Packit 6c4009
  putchar ('\n');
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* We have to show the contents of the environments determining the
Packit 6c4009
   locale.  */
Packit 6c4009
static void
Packit 6c4009
show_locale_vars (void)
Packit 6c4009
{
Packit 6c4009
  const char *lcall = getenv ("LC_ALL") ?: "";
Packit 6c4009
  const char *lang = getenv ("LANG") ?: "";
Packit 6c4009
Packit 6c4009
  /* LANG has to be the first value.  */
Packit 6c4009
  print_assignment ("LANG", lang, false);
Packit 6c4009
Packit 6c4009
  /* Now all categories in an unspecified order.  */
Packit 6c4009
  for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
Packit 6c4009
    if (cat_no != LC_ALL)
Packit 6c4009
      {
Packit 6c4009
	const char *name = category[cat_no].name;
Packit 6c4009
	const char *val = getenv (name);
Packit 6c4009
Packit 6c4009
	if (lcall[0] != '\0' || val == NULL)
Packit 6c4009
	  print_assignment (name,
Packit 6c4009
			    lcall[0] != '\0' ? lcall
Packit 6c4009
			    : lang[0] != '\0' ? lang
Packit 6c4009
			    : "POSIX",
Packit 6c4009
			    true);
Packit 6c4009
	else
Packit 6c4009
	  print_assignment (name, val, false);
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  /* The last is the LC_ALL value.  */
Packit 6c4009
  print_assignment ("LC_ALL", lcall, false);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Subroutine of show_info, below.  */
Packit 6c4009
static void
Packit 6c4009
print_item (struct cat_item *item)
Packit 6c4009
{
Packit 6c4009
  switch (item->value_type)
Packit 6c4009
    {
Packit 6c4009
    case string:
Packit 6c4009
      if (show_keyword_name)
Packit 6c4009
	printf ("%s=\"", item->name);
Packit 6c4009
      fputs (nl_langinfo (item->item_id) ? : "", stdout);
Packit 6c4009
      if (show_keyword_name)
Packit 6c4009
	putchar ('"');
Packit 6c4009
      putchar ('\n');
Packit 6c4009
      break;
Packit 6c4009
    case stringarray:
Packit 6c4009
      {
Packit 6c4009
	const char *val;
Packit 6c4009
	int cnt;
Packit 6c4009
Packit 6c4009
	if (show_keyword_name)
Packit 6c4009
	  printf ("%s=\"", item->name);
Packit 6c4009
Packit 6c4009
	for (cnt = 0; cnt < item->max - 1; ++cnt)
Packit 6c4009
	  {
Packit 6c4009
	    val = nl_langinfo (item->item_id + cnt);
Packit 6c4009
	    if (val != NULL)
Packit 6c4009
	      fputs (val, stdout);
Packit 6c4009
	    putchar (';');
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
	val = nl_langinfo (item->item_id + cnt);
Packit 6c4009
	if (val != NULL)
Packit 6c4009
	  fputs (val, stdout);
Packit 6c4009
Packit 6c4009
	if (show_keyword_name)
Packit 6c4009
	  putchar ('"');
Packit 6c4009
	putchar ('\n');
Packit 6c4009
      }
Packit 6c4009
      break;
Packit 6c4009
    case stringlist:
Packit 6c4009
      {
Packit 6c4009
	int first = 1;
Packit 6c4009
	const char *val = nl_langinfo (item->item_id) ? : "";
Packit 6c4009
Packit 6c4009
	if (show_keyword_name)
Packit 6c4009
	  printf ("%s=", item->name);
Packit 6c4009
Packit 6c4009
	for (int cnt = 0; cnt < item->max && *val != '\0'; ++cnt)
Packit 6c4009
	  {
Packit 6c4009
	    printf ("%s%s%s%s", first ? "" : ";",
Packit 6c4009
		    show_keyword_name ? "\"" : "", val,
Packit 6c4009
		    show_keyword_name ? "\"" : "");
Packit 6c4009
	    val = strchr (val, '\0') + 1;
Packit 6c4009
	    first = 0;
Packit 6c4009
	  }
Packit 6c4009
	putchar ('\n');
Packit 6c4009
      }
Packit 6c4009
      break;
Packit 6c4009
    case byte:
Packit 6c4009
      {
Packit 6c4009
	const char *val = nl_langinfo (item->item_id);
Packit 6c4009
Packit 6c4009
	if (show_keyword_name)
Packit 6c4009
	  printf ("%s=", item->name);
Packit 6c4009
Packit 6c4009
	if (val != NULL)
Packit 6c4009
	  printf ("%d", *val == '\377' ? -1 : *val);
Packit 6c4009
	putchar ('\n');
Packit 6c4009
      }
Packit 6c4009
      break;
Packit 6c4009
    case bytearray:
Packit 6c4009
      {
Packit 6c4009
	const char *val = nl_langinfo (item->item_id);
Packit 6c4009
	int cnt = val ? strlen (val) : 0;
Packit 6c4009
Packit 6c4009
	if (show_keyword_name)
Packit 6c4009
	  printf ("%s=", item->name);
Packit 6c4009
Packit 6c4009
	while (cnt > 1)
Packit 6c4009
	  {
Packit 6c4009
	    printf ("%d;", *val == '\177' ? -1 : *val);
Packit 6c4009
	    --cnt;
Packit 6c4009
	    ++val;
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
	printf ("%d\n", cnt == 0 || *val == '\177' ? -1 : *val);
Packit 6c4009
      }
Packit 6c4009
      break;
Packit 6c4009
    case word:
Packit 6c4009
      {
Packit 6c4009
	union { unsigned int word; char *string; } val;
Packit 6c4009
	val.string = nl_langinfo (item->item_id);
Packit 6c4009
	if (show_keyword_name)
Packit 6c4009
	  printf ("%s=", item->name);
Packit 6c4009
Packit 6c4009
	printf ("%d\n", val.word);
Packit 6c4009
      }
Packit 6c4009
      break;
Packit 6c4009
    case wordarray:
Packit 6c4009
      {
Packit 6c4009
	int first = 1;
Packit 6c4009
	union { unsigned int *wordarray; char *string; } val;
Packit 6c4009
Packit 6c4009
	val.string = nl_langinfo (item->item_id);
Packit 6c4009
	if (show_keyword_name)
Packit 6c4009
	  printf ("%s=", item->name);
Packit 6c4009
Packit 6c4009
	for (int cnt = 0; cnt < item->max; ++cnt)
Packit 6c4009
	  {
Packit 6c4009
	    printf ("%s%d", first ? "" : ";", val.wordarray[cnt]);
Packit 6c4009
	    first = 0;
Packit 6c4009
	  }
Packit 6c4009
	putchar ('\n');
Packit 6c4009
      }
Packit 6c4009
      break;
Packit 6c4009
    case wstring:
Packit 6c4009
    case wstringarray:
Packit 6c4009
    case wstringlist:
Packit 6c4009
      /* We don't print wide character information since the same
Packit 6c4009
	 information is available in a multibyte string.  */
Packit 6c4009
    default:
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Show the information request for NAME.  */
Packit 6c4009
static void
Packit 6c4009
show_info (const char *name)
Packit 6c4009
{
Packit 6c4009
  for (size_t cat_no = 0; cat_no < NCATEGORIES; ++cat_no)
Packit 6c4009
    if (cat_no != LC_ALL)
Packit 6c4009
      {
Packit 6c4009
	if (strcmp (name, category[cat_no].name) == 0)
Packit 6c4009
	  /* Print the whole category.  */
Packit 6c4009
	  {
Packit 6c4009
	    if (show_category_name != 0)
Packit 6c4009
	      puts (category[cat_no].name);
Packit 6c4009
Packit 6c4009
	    for (size_t item_no = 0;
Packit 6c4009
		 item_no < category[cat_no].number;
Packit 6c4009
		 ++item_no)
Packit 6c4009
	      print_item (&category[cat_no].item_desc[item_no]);
Packit 6c4009
Packit 6c4009
	    return;
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
	for (size_t item_no = 0; item_no < category[cat_no].number; ++item_no)
Packit 6c4009
	  if (strcmp (name, category[cat_no].item_desc[item_no].name) == 0)
Packit 6c4009
	    {
Packit 6c4009
	      if (show_category_name != 0)
Packit 6c4009
		puts (category[cat_no].name);
Packit 6c4009
Packit 6c4009
	      print_item (&category[cat_no].item_desc[item_no]);
Packit 6c4009
	      return;
Packit 6c4009
	    }
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  /* The name is not a standard one.
Packit 6c4009
     For testing and perhaps advanced use allow some more symbols.  */
Packit 6c4009
  locale_special (name, show_category_name, show_keyword_name);
Packit 6c4009
}
Packit Service be9950
Packit Service be9950
/* Set to true by try_setlocale if setlocale fails.  Used by
Packit Service be9950
   setlocale_diagnostics.  */
Packit Service be9950
static bool setlocale_failed;
Packit Service be9950
Packit Service be9950
/* Call setlocale, with non-fatal error reporting.  */
Packit Service be9950
static void
Packit Service be9950
try_setlocale (int category, const char *category_name)
Packit Service be9950
{
Packit Service be9950
  if (setlocale (category, "") == NULL)
Packit Service be9950
    {
Packit Service be9950
      error (0, errno, gettext ("Cannot set %s to default locale"),
Packit Service be9950
	     category_name);
Packit Service be9950
      setlocale_failed = true;
Packit Service be9950
    }
Packit Service be9950
}
Packit Service be9950
Packit Service be9950
/* Return a quoted version of the passed string, or NULL on error.  */
Packit Service be9950
static char *
Packit Service be9950
quote_string (const char *input)
Packit Service be9950
{
Packit Service be9950
  char *buffer;
Packit Service be9950
  size_t length;
Packit Service be9950
  FILE *stream = open_memstream (&buffer, &length);
Packit Service be9950
  if (stream == NULL)
Packit Service be9950
    return NULL;
Packit Service be9950
Packit Service be9950
  while (true)
Packit Service be9950
    {
Packit Service be9950
      unsigned char ch = *input++;
Packit Service be9950
      if (ch == '\0')
Packit Service be9950
	break;
Packit Service be9950
Packit Service be9950
      /* Use C backslash escapes for those control characters for
Packit Service be9950
         which they are defined.  */
Packit Service be9950
      switch (ch)
Packit Service be9950
        {
Packit Service be9950
          case '\a':
Packit Service be9950
            putc_unlocked ('\\', stream);
Packit Service be9950
            putc_unlocked ('a', stream);
Packit Service be9950
            break;
Packit Service be9950
          case '\b':
Packit Service be9950
            putc_unlocked ('\\', stream);
Packit Service be9950
            putc_unlocked ('b', stream);
Packit Service be9950
            break;
Packit Service be9950
          case '\f':
Packit Service be9950
            putc_unlocked ('\\', stream);
Packit Service be9950
            putc_unlocked ('f', stream);
Packit Service be9950
            break;
Packit Service be9950
          case '\n':
Packit Service be9950
            putc_unlocked ('\\', stream);
Packit Service be9950
            putc_unlocked ('n', stream);
Packit Service be9950
            break;
Packit Service be9950
          case '\r':
Packit Service be9950
            putc_unlocked ('\\', stream);
Packit Service be9950
            putc_unlocked ('r', stream);
Packit Service be9950
            break;
Packit Service be9950
          case '\t':
Packit Service be9950
            putc_unlocked ('\\', stream);
Packit Service be9950
            putc_unlocked ('t', stream);
Packit Service be9950
            break;
Packit Service be9950
          case '\v':
Packit Service be9950
            putc_unlocked ('\\', stream);
Packit Service be9950
            putc_unlocked ('v', stream);
Packit Service be9950
            break;
Packit Service be9950
          case '\\':
Packit Service be9950
          case '\'':
Packit Service be9950
          case '\"':
Packit Service be9950
            putc_unlocked ('\\', stream);
Packit Service be9950
            putc_unlocked (ch, stream);
Packit Service be9950
            break;
Packit Service be9950
        default:
Packit Service be9950
          if (ch < ' ' || ch > '~')
Packit Service be9950
            /* Use octal sequences because they are fixed width,
Packit Service be9950
               unlike hexadecimal sequences.  */
Packit Service be9950
            fprintf (stream, "\\%03o", ch);
Packit Service be9950
          else
Packit Service be9950
            putc_unlocked (ch, stream);
Packit Service be9950
        }
Packit Service be9950
    }
Packit Service be9950
Packit Service be9950
  if (ferror (stream))
Packit Service be9950
    {
Packit Service be9950
      fclose (stream);
Packit Service be9950
      free (buffer);
Packit Service be9950
      return NULL;
Packit Service be9950
    }
Packit Service be9950
  if (fclose (stream) != 0)
Packit Service be9950
    {
Packit Service be9950
      free (buffer);
Packit Service be9950
      return NULL;
Packit Service be9950
    }
Packit Service be9950
Packit Service be9950
  return buffer;
Packit Service be9950
}
Packit Service be9950
Packit Service be9950
/* Print additional information if there was a setlocale error (during
Packit Service be9950
   try_setlocale).  */
Packit Service be9950
static void
Packit Service be9950
setlocale_diagnostics (void)
Packit Service be9950
{
Packit Service be9950
  if (setlocale_failed)
Packit Service be9950
    {
Packit Service be9950
      const char *locpath = getenv ("LOCPATH");
Packit Service be9950
      if (locpath != NULL)
Packit Service be9950
	{
Packit Service be9950
	  char *quoted = quote_string (locpath);
Packit Service be9950
	  if (quoted != NULL)
Packit Service be9950
	    fprintf (stderr,
Packit Service be9950
		     gettext ("\
Packit Service be9950
warning: The LOCPATH variable is set to \"%s\"\n"),
Packit Service be9950
		     quoted);
Packit Service be9950
	  else
Packit Service be9950
	    fputs ("warning: The LOCPATH variable is set\n", stderr);
Packit Service be9950
	  free (quoted);
Packit Service be9950
	}
Packit Service be9950
    }
Packit Service be9950
}