summaryrefslogtreecommitdiff
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);
}