about summary refs log tree commit diff
path: root/locale/programs/locale.c
diff options
context:
space:
mode:
Diffstat (limited to 'locale/programs/locale.c')
-rw-r--r--locale/programs/locale.c353
1 files changed, 258 insertions, 95 deletions
diff --git a/locale/programs/locale.c b/locale/programs/locale.c
index 8587345bf2..5a68c562ef 100644
--- a/locale/programs/locale.c
+++ b/locale/programs/locale.c
@@ -1,4 +1,4 @@
-/* locale - Implementation of the locale program according to POSIX 1003.2
+/* Implementation of the locale program according to POSIX 1003.2.
    Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
@@ -22,18 +22,21 @@
 # include <config.h>
 #endif
 
+#include <argp.h>
+#include <argz.h>
 #include <dirent.h>
+#include <errno.h>
 #include <error.h>
-#include <getopt.h>
 #include <langinfo.h>
 #include <libintl.h>
 #include <limits.h>
 #include <locale.h>
+#include <search.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
 #include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
 
 #include "localeinfo.h"
 
@@ -44,16 +47,44 @@ static int show_category_name;
 /* If set print the name of the item.  */
 static int show_keyword_name;
 
-/* Long options.  */
-static const struct option long_options[] =
+/* Print names of all available locales.  */
+static int do_all;
+
+/* Print names of all available character maps.  */
+static int do_charmaps = 0;
+
+/* Name and version of program.  */
+static void print_version (FILE *stream, struct argp_state *state);
+void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
+
+/* Definitions of arguments for argp functions.  */
+static struct argp_option options[] =
 {
-  { "all-locales", no_argument, NULL, 'a' },
-  { "category-name", no_argument, &show_category_name, 1 },
-  { "charmaps", no_argument, NULL, 'm' },
-  { "help", no_argument, NULL, 'h' },
-  { "keyword-name", no_argument, &show_keyword_name, 1 },
-  { "version", no_argument, NULL, 'V' },
-  { NULL, 0, NULL, 0 }
+  { NULL, 0, NULL, 0, "System information:" },
+  { "all-locales", 'a', NULL, 0, "Write names of available locales" },
+  { "charmaps", 'm', NULL, 0, "Write names of available charmaps" },
+  { NULL, 0, NULL, 0, "Modify output format:" },
+  { "category-name", 'c', NULL, 0, "Write names of selected categories" },
+  { "keyword-name", 'k', NULL, 0, "Write names of selected keywords" },
+  { NULL, 0, NULL, 0, NULL }
+};
+
+/* Short description of program.  */
+static const char doc[] = "Get locale-specific information.";
+
+/* Strings for arguments in help texts.  */
+static const char args_doc[] = "NAME\n[-a|-m]";
+
+/* Prototype for option handler.  */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Function to print some extra text in the help message.  */
+static char *more_help (int key, const char *text, void *input);
+
+/* Data structure to communicate with argp functions.  */
+static struct argp argp =
+{
+  options, parse_opt, args_doc, doc, NULL, more_help
 };
 
 
@@ -124,7 +155,6 @@ extern void locale_special (const char *name, int show_category_name,
 			    int show_keyword_name);
 
 /* Prototypes for local functions.  */
-static void usage (int status) __attribute__ ((noreturn));
 static void write_locales (void);
 static void write_charmaps (void);
 static void show_locale_vars (void);
@@ -134,12 +164,6 @@ static void show_info (const char *name);
 int
 main (int argc, char *argv[])
 {
-  int optchar;
-  int do_all = 0;
-  int do_help = 0;
-  int do_version = 0;
-  int do_charmaps = 0;
-
   /* Set initial values for global variables.  */
   show_category_name = 0;
   show_keyword_name = 0;
@@ -152,55 +176,13 @@ main (int argc, char *argv[])
   /* Initialize the message catalog.  */
   textdomain (PACKAGE);
 
-  while ((optchar = getopt_long (argc, argv, "achkmV", long_options, NULL))
-         != -1)
-    switch (optchar)
-      {
-      case '\0':		/* Long option.  */
-	break;
-      case 'a':
-	do_all = 1;
-	break;
-      case 'c':
-	show_category_name = 1;
-	break;
-      case 'h':
-	do_help = 1;
-	break;
-      case 'k':
-	show_keyword_name = 1;
-	break;
-      case 'm':
-	do_charmaps = 1;
-	break;
-      case 'V':
-	do_version = 1;
-	break;
-      default:
-	usage (EXIT_FAILURE);
-      }
-
-  /* Version information is requested.  */
-  if (do_version)
-    {
-      printf ("locale (GNU %s) %s\n", PACKAGE, VERSION);
-      printf (_("\
-Copyright (C) %s Free Software Foundation, Inc.\n\
-This is free software; see the source for copying conditions.  There is NO\n\
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
-"), "1995, 1996, 1997");
-      printf (_("Written by %s.\n"), "Ulrich Drepper");
-
-      exit (EXIT_SUCCESS);
-    }
-
-  /* Help is requested.  */
-  if (do_help)
-    usage (EXIT_SUCCESS);
+  /* Parse and process arguments.  */
+  argp_parse (&argp, argc, argv, 0, 0, NULL);
 
   /* `-a' requests the names of all available locales.  */
   if (do_all != 0)
     {
+      setlocale (LC_COLLATE, "");
       write_locales ();
       exit (EXIT_SUCCESS);
     }
@@ -233,61 +215,219 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
 }
 
 
-/* Display usage information and exit.  */
-static void
-usage (int status)
+/* Handle program arguments.  */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
 {
-  if (status != EXIT_SUCCESS)
-    fprintf (stderr, gettext ("Try `%s --help' for more information.\n"),
-	     __progname);
-  else
+  switch (key)
     {
-      printf (gettext ("\
-Usage: %s [OPTION]... name\n\
-Mandatory arguments to long options are mandatory for short options too.\n\
-  -h, --help            display this help and exit\n\
-  -V, --version         output version information and exit\n\
-\n\
-  -a, --all-locales     write names of available locales\n\
-  -m, --charmaps        write names of available charmaps\n\
-\n\
-  -c, --category-name   write names of selected categories\n\
-  -k, --keyword-name    write names of selected keywords\n"),
-	      __progname);
-      fputs (gettext ("\
-Report bugs using the `glibcbug' script to <bugs@gnu.ai.mit.edu>.\n"),
-	     stdout);
+    case 'a':
+      do_all = 1;
+      break;
+    case 'c':
+      show_category_name = 1;
+      break;
+    case 'm':
+      do_charmaps = 1;
+      break;
+    case 'k':
+      show_keyword_name = 1;
+      break;
+    default:
+      return ARGP_ERR_UNKNOWN;
     }
+  return 0;
+}
+
+
+static char *
+more_help (int key, const char *text, void *input)
+{
+  switch (key)
+    {
+    case ARGP_KEY_HELP_EXTRA:
+      /* We print some extra information.  */
+      return strdup (gettext ("\
+Report bugs using the `glibcbug' script to <bugs@gnu.ai.mit.edu>.\n"));
+    default:
+      break;
+    }
+  return (char *) text;
+}
+
+/* Print the version information.  */
+static void
+print_version (FILE *stream, struct argp_state *state)
+{
+  fprintf (stream, "locale (GNU %s) %s\n", PACKAGE, VERSION);
+  fprintf (stream, gettext ("\
+Copyright (C) %s Free Software Foundation, Inc.\n\
+This is free software; see the source for copying conditions.  There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
+"), "1995, 1996, 1997");
+  fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
+}
 
-  exit (status);
+
+/* Simple action function which prints arguments as strings.  */
+static void
+print_names (const void *nodep, VISIT value, int level)
+{
+  if (value == postorder || value == leaf)
+    puts (*(char **) nodep);
 }
 
 
-/* Write the names of all available locales to stdout.  */
+/* Write the names of all available locales to stdout.  We have some
+   sources of the information: the contents of the locale directory
+   and the locale.alias file.  To avoid duplicates and print the
+   result is a reasonable order we put all entries is a search tree
+   and print them afterwards.  */
 static void
 write_locales (void)
 {
+  void *all_data = NULL;
   DIR *dir;
   struct dirent *dirent;
+  char *alias_path;
+  size_t alias_path_len;
+  char *entry;
 
-  /* `POSIX' locale is always available (POSIX.2 4.34.3).  */
-  puts ("POSIX");
+#define PUT(name) tsearch ((name), &all_data, \
+			   (int (*) (const void *, const void *)) strcoll)
 
-  dir = opendir (LOCALE_PATH);
+  dir = opendir (LOCALEDIR);
   if (dir == NULL)
     {
       error (1, errno, gettext ("cannot read locale directory `%s'"),
-	     LOCALE_PATH);
+	     LOCALEDIR);
       return;
     }
 
+  /* `POSIX' locale is always available (POSIX.2 4.34.3).  */
+  PUT ("POSIX");
+  /* And so is the "C" locale.  */
+  PUT ("C");
+
   /* Now we can look for all files in the directory.  */
   while ((dirent = readdir (dir)) != NULL)
     if (strcmp (dirent->d_name, ".") != 0
 	&& strcmp (dirent->d_name, "..") != 0)
-      puts (dirent->d_name);
+      {
+	mode_t mode;
+#ifdef _DIRENT_HAVE_D_TYPE
+	if (dirent->d_type != DT_UNKNOWN)
+	  mode = DTTOIF (dirent->d_type);
+	else
+#endif
+	  {
+	    struct stat st;
+	    char buf[sizeof (LOCALEDIR) + strlen (dirent->d_name) + 1];
+
+	    stpcpy (stpcpy (stpcpy (buf, LOCALEDIR), "/"), dirent->d_name);
+
+	    if (stat (buf, &st) < 0)
+	      continue;
+	    mode = st.st_mode;
+	  }
+
+	if (S_ISDIR (mode))
+	  PUT (strdup (dirent->d_name));
+      }
 
   closedir (dir);
+
+  /* Now read the locale.alias files.  */
+  if (argz_create_sep (LOCALE_ALIAS_PATH, ':', &alias_path, &alias_path_len))
+    error (1, errno, gettext ("while preparing output"));
+
+  entry = NULL;
+  while ((entry = argz_next (alias_path, alias_path_len, entry)))
+    {
+      static const char aliasfile[] = "/locale.alias";
+      FILE *fp;
+      char full_name[strlen (entry) + sizeof aliasfile];
+
+      stpcpy (stpcpy (full_name, entry), aliasfile);
+      fp = fopen (full_name, "r");
+      if (fp == NULL)
+	/* Ignore non-existing files.  */
+	continue;
+
+      while (! feof (fp))
+	{
+	  /* It is a reasonable approach to use a fix buffer here
+	     because
+	     a) we are only interested in the first two fields
+	     b) these fields must be usable as file names and so must
+	        not be that long  */
+	  char buf[BUFSIZ];
+	  char *alias;
+	  char *value;
+	  char *cp;
+
+	  if (fgets (buf, BUFSIZ, fp) == NULL)
+	    /* EOF reached.  */
+	    break;
+
+	  cp = buf;
+	  /* Ignore leading white space.  */
+	  while (isspace (cp[0]))
+	    ++cp;
+
+	  /* A leading '#' signals a comment line.  */
+	  if (cp[0] != '\0' && cp[0] != '#')
+	    {
+	      alias = cp++;
+	      while (cp[0] != '\0' && !isspace (cp[0]))
+		++cp;
+	      /* Terminate alias name.  */
+	      if (cp[0] != '\0')
+		*cp++ = '\0';
+
+	      /* Now look for the beginning of the value.  */
+	      while (isspace (cp[0]))
+		++cp;
+
+	      if (cp[0] != '\0')
+		{
+		  value = cp++;
+		  while (cp[0] != '\0' && !isspace (cp[0]))
+		    ++cp;
+		  /* Terminate value.  */
+		  if (cp[0] == '\n')
+		    {
+		      /* This has to be done to make the following
+			 test for the end of line possible.  We are
+			 looking for the terminating '\n' which do not
+			 overwrite here.  */
+		      *cp++ = '\0';
+		      *cp = '\n';
+		    }
+		  else if (cp[0] != '\0')
+		    *cp++ = '\0';
+
+		  /* Add the alias.  */
+		  PUT (strdup (alias));
+		}
+	    }
+
+	  /* Possibly not the whole line fits into the buffer.
+	     Ignore the rest of the line.  */
+	  while (strchr (cp, '\n') == NULL)
+	    {
+	      cp = buf;
+	      if (fgets (buf, BUFSIZ, fp) == NULL)
+		/* Make sure the inner loop will be left.  The outer
+		   loop will exit at the `feof' test.  */
+		*cp = '\n';
+	    }
+	}
+
+      fclose (fp);
+    }
+
+  twalk (all_data, print_names);
 }
 
 
@@ -295,6 +435,7 @@ write_locales (void)
 static void
 write_charmaps (void)
 {
+  void *all_data = NULL;
   DIR *dir;
   struct dirent *dirent;
 
@@ -310,9 +451,31 @@ write_charmaps (void)
   while ((dirent = readdir (dir)) != NULL)
     if (strcmp (dirent->d_name, ".") != 0
 	&& strcmp (dirent->d_name, "..") != 0)
-      puts (dirent->d_name);
+      {
+	mode_t mode;
+#ifdef _DIRENT_HAVE_D_TYPE
+	if (dirent->d_type != DT_UNKNOWN)
+	  mode = DTTOIF (dirent->d_type);
+	else
+#endif
+	  {
+	    struct stat st;
+	    char buf[sizeof (CHARMAP_PATH) + strlen (dirent->d_name) + 1];
+
+	    stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"), dirent->d_name);
+
+	    if (stat (buf, &st) < 0)
+	      continue;
+	    mode = st.st_mode;
+	  }
+
+	if (S_ISREG (mode))
+	  PUT (strdup (dirent->d_name));
+      }
 
   closedir (dir);
+
+  twalk (all_data, print_names);
 }