about summary refs log tree commit diff
path: root/locale
diff options
context:
space:
mode:
Diffstat (limited to 'locale')
-rw-r--r--locale/Makefile5
-rw-r--r--locale/programs/charmap-dir.c312
-rw-r--r--locale/programs/charmap-dir.h48
-rw-r--r--locale/programs/charmap.c142
-rw-r--r--locale/programs/linereader.c14
-rw-r--r--locale/programs/linereader.h3
-rw-r--r--locale/programs/locale.c110
7 files changed, 471 insertions, 163 deletions
diff --git a/locale/Makefile b/locale/Makefile
index 2b278d574f..a5abf5414a 100644
--- a/locale/Makefile
+++ b/locale/Makefile
@@ -33,7 +33,7 @@ distribute	= localeinfo.h categories.def iso-639.def iso-3166.def \
 			      charmap-kw.gperf charmap-kw.h locfile-token.h \
 			      locfile-kw.gperf locfile-kw.h linereader.h \
 			      locfile.h charmap.h repertoire.h localedef.h \
-			      3level.h)
+			      3level.h charmap-dir.h)
 routines	= setlocale findlocale loadlocale localeconv nl_langinfo \
 		  nl_langinfo_l mb_cur_max codeset_name \
 		  newlocale duplocale freelocale
@@ -59,7 +59,7 @@ vpath %.gperf programs
 localedef-modules	:= $(categories:%=ld-%) charmap linereader locfile \
 			   repertoire
 locale-modules		:= locale-spec
-lib-modules		:= simple-hash xmalloc xstrdup
+lib-modules		:= charmap-dir simple-hash xmalloc xstrdup
 
 
 GPERF = gperf
@@ -94,6 +94,7 @@ locale-CPPFLAGS := -DLOCALE_PATH='$(localepath)' \
 
 CFLAGS-charmap.c = -Wno-write-strings -Wno-char-subscripts
 CFLAGS-locfile.c = -Wno-write-strings -Wno-char-subscripts
+CFLAGS-charmap-dir.c = -Wno-write-strings
 
 # Depend on libc.so so a DT_NEEDED is generated in the shared objects.
 # This ensures they will load libc.so for needed symbols if loaded by
diff --git a/locale/programs/charmap-dir.c b/locale/programs/charmap-dir.c
new file mode 100644
index 0000000000..a1eb84336d
--- /dev/null
+++ b/locale/programs/charmap-dir.c
@@ -0,0 +1,312 @@
+/* Copyright (C) 2000 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <dirent.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <spawn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "charmap-dir.h"
+
+extern void *xmalloc (size_t n);
+extern void *xrealloc (void *p, size_t n);
+
+/* The data type of a charmap directory being traversed.  */
+struct charmap_dir
+{
+  DIR *dir;
+  /* The directory pathname, ending in a slash.  */
+  char *directory;
+  size_t directory_len;
+  /* Scratch area used for returning pathnames.  */
+  char *pathname;
+  size_t pathname_size;
+};
+
+/* Starts a charmap directory traversal.
+   Returns a CHARMAP_DIR, or NULL if the directory doesn't exist.  */
+CHARMAP_DIR *
+charmap_opendir (const char *directory)
+{
+  struct charmap_dir *cdir;
+  DIR *dir;
+  size_t len;
+  int add_slash;
+
+  dir = opendir (directory);
+  if (dir == NULL)
+    {
+      error (1, errno, gettext ("cannot read character map directory `%s'"),
+             directory);
+      return NULL;
+    }
+
+  cdir = (struct charmap_dir *) xmalloc (sizeof (struct charmap_dir));
+  cdir->dir = dir;
+
+  len = strlen (directory);
+  add_slash = (len == 0 || directory[len - 1] != '/');
+  cdir->directory = (char *) xmalloc (len + add_slash + 1);
+  memcpy (cdir->directory, directory, len);
+  if (add_slash)
+    cdir->directory[len] = '/';
+  cdir->directory[len + add_slash] = '\0';
+  cdir->directory_len = len + add_slash;
+
+  cdir->pathname = NULL;
+  cdir->pathname_size = 0;
+
+  return cdir;
+}
+
+/* Reads the next directory entry.
+   Returns its charmap name, or NULL if past the last entry or upon error.
+   The storage returned may be overwritten by a later charmap_readdir
+   call on the same CHARMAP_DIR.  */
+const char *
+charmap_readdir (CHARMAP_DIR *cdir)
+{
+  for (;;)
+    {
+      struct dirent *dirent;
+      size_t len;
+      size_t size;
+      char *filename;
+      mode_t mode;
+
+      dirent = readdir (cdir->dir);
+      if (dirent == NULL)
+        return NULL;
+      if (strcmp (dirent->d_name, ".") == 0)
+        continue;
+      if (strcmp (dirent->d_name, "..") == 0)
+        continue;
+
+      len = strlen (dirent->d_name);
+
+      size = cdir->directory_len + len + 1;
+      if (size > cdir->pathname_size)
+        {
+          free (cdir->pathname);
+          if (size < 2 * cdir->pathname_size)
+            size = 2 * cdir->pathname_size;
+          cdir->pathname = (char *) xmalloc (size);
+          cdir->pathname_size = size;
+        }
+
+      stpcpy (stpcpy (cdir->pathname, cdir->directory), dirent->d_name);
+      filename = cdir->pathname + cdir->directory_len;
+
+#ifdef _DIRENT_HAVE_D_TYPE
+      if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
+        mode = DTTOIF (dirent->d_type);
+      else
+#endif
+        {
+          struct stat statbuf;
+
+          if (stat (cdir->pathname, &statbuf) < 0)
+            continue;
+
+          mode = statbuf.st_mode;
+        }
+
+      if (!S_ISREG (mode))
+        continue;
+
+      /* For compressed charmaps, the canonical charmap name does not
+         include the extension.  */
+      if (len > 3 && memcmp (&filename[len - 3], ".gz", 3) == 0)
+        filename[len - 3] = '\0';
+      else if (len > 4 && memcmp (&filename[len - 4], ".bz2", 4) == 0)
+        filename[len - 4] = '\0';
+
+      return filename;
+    }
+}
+
+/* Finishes a charmap directory traversal, and frees the resources
+   attached to the CHARMAP_DIR.  */
+int
+charmap_closedir (CHARMAP_DIR *cdir)
+{
+  DIR *dir = cdir->dir;
+
+  free (cdir->directory);
+  free (cdir->pathname);
+  free (cdir);
+  return closedir (dir);
+}
+
+/* Creates a subprocess decompressing the given pathname, and returns
+   a stream reading its output (the decompressed data).  */
+static
+FILE *
+fopen_uncompressed (const char *pathname, char *compressor)
+{
+  int pfd;
+
+  pfd = open (pathname, O_RDONLY);
+  if (pfd >= 0)
+    {
+      struct stat statbuf;
+      int fd[2];
+
+      if (fstat (pfd, &statbuf) >= 0
+          && S_ISREG (statbuf.st_mode)
+          && pipe (fd) >= 0)
+        {
+          char *argv[4] = { compressor, "-d", "-c", NULL };
+          posix_spawn_file_actions_t actions;
+
+          if (posix_spawn_file_actions_init (&actions) == 0)
+            {
+              if (posix_spawn_file_actions_adddup2 (&actions,
+                                                    fd[1], STDOUT_FILENO) == 0
+                  && posix_spawn_file_actions_addclose (&actions, fd[1]) == 0
+                  && posix_spawn_file_actions_addclose (&actions, fd[0]) == 0
+                  && posix_spawn_file_actions_adddup2 (&actions,
+                                                       pfd, STDIN_FILENO) == 0
+                  && posix_spawn_file_actions_addclose (&actions, pfd) == 0
+                  && posix_spawnp (NULL, compressor, &actions, NULL,
+                                   argv, environ) == 0)
+                {
+                  posix_spawn_file_actions_destroy (&actions);
+                  close (fd[1]);
+                  close (pfd);
+                  return fdopen (fd[0], "r");
+                }
+              posix_spawn_file_actions_destroy (&actions);
+            }
+          close (fd[1]);
+          close (fd[0]);
+        }
+      close (pfd);
+    }
+  return NULL;
+}
+
+/* Opens a charmap for reading, given its name (not an alias name).  */
+FILE *
+charmap_open (const char *directory, const char *name)
+{
+  size_t dlen = strlen (directory);
+  int add_slash = (dlen == 0 || directory[dlen - 1] != '/');
+  size_t nlen = strlen (name);
+  char *pathname;
+  char *p;
+  FILE *stream;
+
+  pathname = alloca (dlen + add_slash + nlen + 5);
+  p = stpcpy (pathname, directory);
+  if (add_slash)
+    *p++ = '/';
+  p = stpcpy (p, name);
+
+  stream = fopen (pathname, "r");
+  if (stream != NULL)
+    return stream;
+
+  memcpy (p, ".gz", 4);
+  stream = fopen_uncompressed (pathname, "gzip");
+  if (stream != NULL)
+    return stream;
+
+  memcpy (p, ".bz2", 5);
+  stream = fopen_uncompressed (pathname, "bzip2");
+  if (stream != NULL)
+    return stream;
+
+  return NULL;
+}
+
+/* An empty alias list.  Avoids the need to return NULL from
+   charmap_aliases.  */
+static char *empty[1];
+
+/* Returns a NULL terminated list of alias names of a charmap.  */
+char **
+charmap_aliases (const char *directory, const char *name)
+{
+  FILE *stream;
+  char **aliases;
+  size_t naliases;
+
+  stream = charmap_open (directory, name);
+  if (stream == NULL)
+    return empty;
+
+  aliases = NULL;
+  naliases = 0;
+
+  while (!feof (stream))
+    {
+      char *alias = NULL;
+      char junk[BUFSIZ];
+
+      if (fscanf (stream, " <code_set_name> %as", &alias) == 1
+          || fscanf (stream, "%% alias %as", &alias) == 1)
+        {
+          aliases = (char **) xrealloc (aliases,
+                                        (naliases + 2) * sizeof (char *));
+          aliases[naliases++] = alias;
+        }
+
+      /* Read the rest of the line.  */
+      if (fgets (junk, sizeof junk, stream) != NULL)
+        {
+          if (strstr (junk, "CHARMAP") != NULL)
+            /* We cannot expect more aliases from now on.  */
+            break;
+
+          while (strchr (junk, '\n') == NULL
+                 && fgets (junk, sizeof junk, stream) != NULL)
+            continue;
+        }
+    }
+
+  fclose (stream);
+
+  if (naliases == 0)
+    return empty;
+
+  aliases[naliases] = NULL;
+  return aliases;
+}
+
+/* Frees an alias list returned by charmap_aliases.  */
+void
+charmap_free_aliases (char **aliases)
+{
+  if (aliases != empty)
+    {
+      char **p;
+
+      for (p = aliases; *p; p++)
+        free (*p);
+
+      free (aliases);
+    }
+}
diff --git a/locale/programs/charmap-dir.h b/locale/programs/charmap-dir.h
new file mode 100644
index 0000000000..ac19624bd2
--- /dev/null
+++ b/locale/programs/charmap-dir.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2000 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef _CHARMAP_DIR_H
+#define _CHARMAP_DIR_H 1
+
+/* The data type of a charmap directory being traversed.  */
+typedef struct charmap_dir CHARMAP_DIR;
+
+/* Starts a charmap directory traversal.
+   Returns a CHARMAP_DIR, or NULL if the directory doesn't exist.  */
+extern CHARMAP_DIR *charmap_opendir (const char *directory);
+
+/* Reads the next directory entry.
+   Returns its charmap name, or NULL if past the last entry or upon error.
+   The storage returned may be overwritten by a later charmap_readdir
+   call on the same CHARMAP_DIR.  */
+extern const char *charmap_readdir (CHARMAP_DIR *dir);
+
+/* Finishes a charmap directory traversal, and frees the resources
+   attached to the CHARMAP_DIR.  */
+extern int charmap_closedir (CHARMAP_DIR *dir);
+
+/* Returns a NULL terminated list of alias names of a charmap.  */
+extern char **charmap_aliases (const char *directory, const char *name);
+
+/* Frees an alias list returned by charmap_aliases.  */
+extern void charmap_free_aliases (char **aliases);
+
+/* Opens a charmap for reading, given its name (not an alias name).  */
+extern FILE *charmap_open (const char *directory, const char *name);
+
+#endif /* _CHARMAP_DIR_H */
diff --git a/locale/programs/charmap.c b/locale/programs/charmap.c
index b8e994fdac..776d6ff92c 100644
--- a/locale/programs/charmap.c
+++ b/locale/programs/charmap.c
@@ -22,18 +22,17 @@
 #endif
 
 #include <ctype.h>
-#include <dirent.h>
 #include <errno.h>
 #include <libintl.h>
 #include <limits.h>
 #include <obstack.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 
 #include "error.h"
 #include "linereader.h"
 #include "charmap.h"
+#include "charmap-dir.h"
 #include "locfile.h"
 #include "repertoire.h"
 
@@ -55,6 +54,31 @@ static void charmap_new_char (struct linereader *lr, struct charmap_t *cm,
 			      int nbytes, char *bytes, const char *from,
 			      const char *to, int decimal_ellipsis, int step);
 
+static struct linereader *
+cmlr_open (const char *directory, const char *name, kw_hash_fct_t hf)
+{
+  FILE *fp;
+
+  fp = charmap_open (directory, name);
+  if (fp == NULL)
+    return NULL;
+  else
+    {
+      size_t dlen = strlen (directory);
+      int add_slash = (dlen == 0 || directory[dlen - 1] != '/');
+      size_t nlen = strlen (name);
+      char *pathname;
+      char *p;
+
+      pathname = alloca (dlen + add_slash + nlen + 1);
+      p = stpcpy (pathname, directory);
+      if (add_slash)
+	*p++ = '/';
+      stpcpy (p, name);
+
+      return lr_create (fp, pathname, hf);
+    }
+}
 
 struct charmap_t *
 charmap_read (const char *filename)
@@ -76,26 +100,20 @@ charmap_read (const char *filename)
 	      char *i18npath = getenv ("I18NPATH");
 	      if (i18npath != NULL && *i18npath != '\0')
 		{
-		  char path[strlen (filename) + 1 + strlen (i18npath)
-			   + sizeof ("/charmaps/") - 1];
+		  char path[strlen (i18npath) + sizeof ("/charmaps")];
 		  char *next;
 		  i18npath = strdupa (i18npath);
 
-
 		  while (cmfile == NULL
 			 && (next = strsep (&i18npath, ":")) != NULL)
 		    {
-		      stpcpy (stpcpy (stpcpy (path, next), "/charmaps/"),
-			      filename);
-
-		      cmfile = lr_open (path, charmap_hash);
+		      stpcpy (stpcpy (path, next), "/charmaps");
+		      cmfile = cmlr_open (path, filename, charmap_hash);
 
 		      if (cmfile == NULL)
 			{
 			  /* Try without the "/charmaps" part.  */
-			  stpcpy (stpcpy (path, next), filename);
-
-			  cmfile = lr_open (path, charmap_hash);
+			  cmfile = cmlr_open (next, filename, charmap_hash);
 			}
 		    }
 		}
@@ -103,10 +121,7 @@ charmap_read (const char *filename)
 	      if (cmfile == NULL)
 		{
 		  /* Try the default directory.  */
-		  char path[sizeof (CHARMAP_PATH) + strlen (filename) + 1];
-
-		  stpcpy (stpcpy (stpcpy (path, CHARMAP_PATH), "/"), filename);
-		  cmfile = lr_open (path, charmap_hash);
+		  cmfile = cmlr_open (CHARMAP_PATH, filename, charmap_hash);
 		}
 	    }
 	}
@@ -125,73 +140,42 @@ charmap_read (const char *filename)
       /* OK, one more try.  We also accept the names given to the
 	 character sets in the files.  Sometimes they differ from the
 	 file name.  */
-      DIR *dir;
-      struct dirent *dirent;
+      CHARMAP_DIR *dir;
 
-      dir = opendir (CHARMAP_PATH);
+      dir = charmap_opendir (CHARMAP_PATH);
       if (dir != NULL)
 	{
-	  while ((dirent = readdir (dir)) != NULL)
-	    if (strcmp (dirent->d_name, ".") != 0
-		&& strcmp (dirent->d_name, "..") != 0)
-	      {
-		char buf[sizeof (CHARMAP_PATH)
-			+ strlen (dirent->d_name) + 1];
-		FILE *fp;
-#ifdef _DIRENT_HAVE_D_TYPE
-		if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_REG)
-		  continue;
-#endif
-		stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"),
-			dirent->d_name);
+	  const char *dirent;
 
-		fp = fopen (buf, "r");
-		if (fp != NULL)
+	  while ((dirent = charmap_readdir (dir)) != NULL)
+	    {
+	      char **aliases;
+	      char **p;
+	      int found;
+
+	      aliases = charmap_aliases (CHARMAP_PATH, dirent);
+	      found = 0;
+	      for (p = aliases; *p; p++)
+		if (strcasecmp (*p, filename) == 0)
 		  {
-		    char *name = NULL;
-
-		    while (!feof (fp))
-		      {
-			char junk[BUFSIZ];
-
-			if (fscanf (fp, " <code_set_name> %as", &name) == 1
-			    || fscanf (fp, "%% alias %as", &name) == 1)
-			  {
-			    if (strcasecmp (name, filename) == 0)
-			      break;
-
-			    free (name);
-			    name = NULL;
-			  }
-
-			if (fgets (junk, sizeof junk, fp) != NULL)
-			  {
-			    if (strstr (junk, "CHARMAP") != NULL)
-			      /* We cannot expect more aliases from now on.  */
-			      break;
-
-			    while (strchr (junk, '\n') == NULL
-				   && fgets (junk, sizeof junk, fp) != NULL)
-			      continue;
-			  }
-		      }
-
-		    fclose (fp);
-
-		    if (name != NULL)
-		      {
-			struct linereader *cmfile;
-
-			cmfile = lr_open (buf, charmap_hash);
-			result = (cmfile == NULL
-				  ? NULL : parse_charmap (cmfile));
-
-			break;
-		      }
+		    found = 1;
+		    break;
 		  }
-	      }
+	      charmap_free_aliases (aliases);
+
+	      if (found)
+		{
+		  struct linereader *cmfile;
+
+		  cmfile = cmlr_open (CHARMAP_PATH, dirent, charmap_hash);
+		  if (cmfile != NULL)
+		    result = parse_charmap (cmfile);
 
-	  closedir (dir);
+		  break;
+		}
+	    }
+
+	  charmap_closedir (dir);
 	}
     }
 
@@ -199,9 +183,9 @@ charmap_read (const char *filename)
     {
       struct linereader *cmfile;
 
-      cmfile = lr_open (CHARMAP_PATH "/" DEFAULT_CHARMAP, charmap_hash);
-
-      result = cmfile == NULL ? NULL : parse_charmap (cmfile);
+      cmfile = cmlr_open (CHARMAP_PATH, DEFAULT_CHARMAP, charmap_hash);
+      if (cmfile != NULL)
+	result = parse_charmap (cmfile);
 
       if (result == NULL)
 	error (4, errno, _("default character map file `%s' not found"),
diff --git a/locale/programs/linereader.c b/locale/programs/linereader.c
index 6237094f07..3638ba138a 100644
--- a/locale/programs/linereader.c
+++ b/locale/programs/linereader.c
@@ -47,23 +47,29 @@ struct linereader *
 lr_open (const char *fname, kw_hash_fct_t hf)
 {
   FILE *fp;
-  struct linereader *result;
-  int n;
 
   if (fname == NULL || strcmp (fname, "-") == 0
       || strcmp (fname, "/dev/stdin") == 0)
-    fp = stdin;
+    return lr_create (stdin, "<stdin>", hf);
   else
     {
       fp = fopen (fname, "r");
       if (fp == NULL)
 	return NULL;
+      return lr_create (fp, fname, hf);
     }
+}
+
+struct linereader *
+lr_create (FILE *fp, const char *fname, kw_hash_fct_t hf)
+{
+  struct linereader *result;
+  int n;
 
   result = (struct linereader *) xmalloc (sizeof (*result));
 
   result->fp = fp;
-  result->fname = xstrdup (fname ? : "<stdin>");
+  result->fname = xstrdup (fname);
   result->buf = NULL;
   result->bufsize = 0;
   result->lineno = 1;
diff --git a/locale/programs/linereader.h b/locale/programs/linereader.h
index 1c98f686c1..77fa61a002 100644
--- a/locale/programs/linereader.h
+++ b/locale/programs/linereader.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper, <drepper@gnu.org>.
 
@@ -85,6 +85,7 @@ struct linereader
 
 /* Functions defined in linereader.c.  */
 extern struct linereader *lr_open (const char *fname, kw_hash_fct_t hf);
+extern struct linereader *lr_create (FILE *fp, const char *fname, kw_hash_fct_t hf);
 extern int lr_eof (struct linereader *lr);
 extern void lr_close (struct linereader *lr);
 extern int lr_next (struct linereader *lr);
diff --git a/locale/programs/locale.c b/locale/programs/locale.c
index c37dad8649..7a42f1e3a4 100644
--- a/locale/programs/locale.c
+++ b/locale/programs/locale.c
@@ -35,10 +35,12 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 #include <sys/stat.h>
 
 #include "localeinfo.h"
+#include "charmap-dir.h"
+
+extern char *xstrdup (const char *__str);
 
 
 /* If set print the name of the category.  */
@@ -251,7 +253,7 @@ more_help (int key, const char *text, void *input)
     {
     case ARGP_KEY_HELP_EXTRA:
       /* We print some extra information.  */
-      return strdup (gettext ("\
+      return xstrdup (gettext ("\
 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
     default:
       break;
@@ -320,7 +322,7 @@ write_locales (void)
       {
 	mode_t mode;
 #ifdef _DIRENT_HAVE_D_TYPE
-	if (dirent->d_type != DT_UNKNOWN)
+	if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
 	  mode = DTTOIF (dirent->d_type);
 	else
 #endif
@@ -348,7 +350,7 @@ write_locales (void)
 		    "/LC_CTYPE");
 
 	    if (stat (buf, &st) == 0 && S_ISREG (st.st_mode))
-	      PUT (strdup (dirent->d_name));
+	      PUT (xstrdup (dirent->d_name));
 	  }
       }
 
@@ -425,7 +427,7 @@ write_locales (void)
 		    *cp++ = '\0';
 
 		  /* Add the alias.  */
-		  PUT (strdup (alias));
+		  PUT (xstrdup (alias));
 		}
 	    }
 
@@ -453,84 +455,38 @@ static void
 write_charmaps (void)
 {
   void *all_data = NULL;
-  DIR *dir;
-  struct dirent *dirent;
+  CHARMAP_DIR *dir;
+  const char *dirent;
 
-  dir = opendir (CHARMAP_PATH);
+  /* Look for all files in the charmap directory.  */
+  dir = charmap_opendir (CHARMAP_PATH);
   if (dir == NULL)
-    {
-      error (1, errno, gettext ("cannot read character map directory `%s'"),
-	     CHARMAP_PATH);
-      return;
-    }
-
-  /* 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)
-      {
-	char *buf = NULL;
-	mode_t mode;
+    return;
 
-#ifdef _DIRENT_HAVE_D_TYPE
-	if (dirent->d_type != DT_UNKNOWN)
-	  mode = DTTOIF (dirent->d_type);
-	else
+  while ((dirent = charmap_readdir (dir)) != NULL)
+    {
+      char **aliases;
+      char **p;
+
+      PUT (xstrdup (dirent));
+
+      aliases = charmap_aliases (CHARMAP_PATH, dirent);
+
+#if 0
+      /* Add the code_set_name and the aliases.  */
+      for (p = aliases; *p; p++)
+	PUT (xstrdup (*p));
+#else
+      /* Add the code_set_name only.  Most aliases are obsolete.  */
+      p = aliases;
+      if (*p)
+	PUT (xstrdup (*p));
 #endif
-	  {
-	    struct stat st;
-
-	    buf = alloca (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))
-	  {
-	    FILE *fp;
-
-	    PUT (strdup (dirent->d_name));
-
-	    /* Read the file and learn about the code set name.  */
-	    if (buf == NULL)
-	      {
-		buf = alloca (sizeof (CHARMAP_PATH)
-			      + strlen (dirent->d_name) + 1);
-
-		stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"),
-			dirent->d_name);
-	      }
 
-	    fp = fopen (buf, "r");
-	    if (fp != NULL)
-	      {
-		char *name = NULL;
-
-		while (!feof (fp))
-		  {
-		    char junk[BUFSIZ];
-
-		    if (fscanf (fp, " <code_set_name> %as", &name) == 1)
-		      break;
-
-		    while (fgets (junk, sizeof junk, fp) != NULL
-			   && strchr (junk, '\n') == NULL)
-		      continue;
-		  }
-
-		fclose (fp);
-
-		if (name != NULL)
-		  PUT (name);
-	      }
-	  }
-      }
+      charmap_free_aliases (aliases);
+    }
 
-  closedir (dir);
+  charmap_closedir (dir);
 
   twalk (all_data, print_names);
 }