Back to index

glibc  2.9
charmap-dir.c
Go to the documentation of this file.
00001 /* Copyright (C) 2000, 2001, 2002, 2003, 2005, 2007
00002    Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    This program is free software; you can redistribute it and/or modify
00006    it under the terms of the GNU General Public License as published
00007    by the Free Software Foundation; version 2 of the License, or
00008    (at your option) any later version.
00009 
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013    GNU General Public License for more details.
00014 
00015    You should have received a copy of the GNU General Public License
00016    along with this program; if not, write to the Free Software Foundation,
00017    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00018 
00019 #include <dirent.h>
00020 #include <errno.h>
00021 #include <error.h>
00022 #include <fcntl.h>
00023 #include <libintl.h>
00024 #include <spawn.h>
00025 #include <stdio.h>
00026 #include <stdlib.h>
00027 #include <string.h>
00028 #include <unistd.h>
00029 #include <sys/stat.h>
00030 
00031 #include "localedef.h"
00032 #include "charmap-dir.h"
00033 
00034 /* The data type of a charmap directory being traversed.  */
00035 struct charmap_dir
00036 {
00037   DIR *dir;
00038   /* The directory pathname, ending in a slash.  */
00039   char *directory;
00040   size_t directory_len;
00041   /* Scratch area used for returning pathnames.  */
00042   char *pathname;
00043   size_t pathname_size;
00044 };
00045 
00046 /* Starts a charmap directory traversal.
00047    Returns a CHARMAP_DIR, or NULL if the directory doesn't exist.  */
00048 CHARMAP_DIR *
00049 charmap_opendir (const char *directory)
00050 {
00051   struct charmap_dir *cdir;
00052   DIR *dir;
00053   size_t len;
00054   int add_slash;
00055 
00056   dir = opendir (directory);
00057   if (dir == NULL)
00058     {
00059       WITH_CUR_LOCALE (error (1, errno, gettext ("\
00060 cannot read character map directory `%s'"), directory));
00061       return NULL;
00062     }
00063 
00064   cdir = (struct charmap_dir *) xmalloc (sizeof (struct charmap_dir));
00065   cdir->dir = dir;
00066 
00067   len = strlen (directory);
00068   add_slash = (len == 0 || directory[len - 1] != '/');
00069   cdir->directory = (char *) xmalloc (len + add_slash + 1);
00070   memcpy (cdir->directory, directory, len);
00071   if (add_slash)
00072     cdir->directory[len] = '/';
00073   cdir->directory[len + add_slash] = '\0';
00074   cdir->directory_len = len + add_slash;
00075 
00076   cdir->pathname = NULL;
00077   cdir->pathname_size = 0;
00078 
00079   return cdir;
00080 }
00081 
00082 /* Reads the next directory entry.
00083    Returns its charmap name, or NULL if past the last entry or upon error.
00084    The storage returned may be overwritten by a later charmap_readdir
00085    call on the same CHARMAP_DIR.  */
00086 const char *
00087 charmap_readdir (CHARMAP_DIR *cdir)
00088 {
00089   for (;;)
00090     {
00091       struct dirent64 *dirent;
00092       size_t len;
00093       size_t size;
00094       char *filename;
00095       mode_t mode;
00096 
00097       dirent = readdir64 (cdir->dir);
00098       if (dirent == NULL)
00099         return NULL;
00100       if (strcmp (dirent->d_name, ".") == 0)
00101         continue;
00102       if (strcmp (dirent->d_name, "..") == 0)
00103         continue;
00104 
00105       len = strlen (dirent->d_name);
00106 
00107       size = cdir->directory_len + len + 1;
00108       if (size > cdir->pathname_size)
00109         {
00110           free (cdir->pathname);
00111           if (size < 2 * cdir->pathname_size)
00112             size = 2 * cdir->pathname_size;
00113           cdir->pathname = (char *) xmalloc (size);
00114           cdir->pathname_size = size;
00115         }
00116 
00117       stpcpy (stpcpy (cdir->pathname, cdir->directory), dirent->d_name);
00118       filename = cdir->pathname + cdir->directory_len;
00119 
00120 #ifdef _DIRENT_HAVE_D_TYPE
00121       if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
00122         mode = DTTOIF (dirent->d_type);
00123       else
00124 #endif
00125         {
00126           struct stat statbuf;
00127 
00128           if (stat (cdir->pathname, &statbuf) < 0)
00129             continue;
00130 
00131           mode = statbuf.st_mode;
00132         }
00133 
00134       if (!S_ISREG (mode))
00135         continue;
00136 
00137       /* For compressed charmaps, the canonical charmap name does not
00138          include the extension.  */
00139       if (len > 3 && memcmp (&filename[len - 3], ".gz", 3) == 0)
00140         filename[len - 3] = '\0';
00141       else if (len > 4 && memcmp (&filename[len - 4], ".bz2", 4) == 0)
00142         filename[len - 4] = '\0';
00143 
00144       return filename;
00145     }
00146 }
00147 
00148 /* Finishes a charmap directory traversal, and frees the resources
00149    attached to the CHARMAP_DIR.  */
00150 int
00151 charmap_closedir (CHARMAP_DIR *cdir)
00152 {
00153   DIR *dir = cdir->dir;
00154 
00155   free (cdir->directory);
00156   free (cdir->pathname);
00157   free (cdir);
00158   return closedir (dir);
00159 }
00160 
00161 /* Creates a subprocess decompressing the given pathname, and returns
00162    a stream reading its output (the decompressed data).  */
00163 static
00164 FILE *
00165 fopen_uncompressed (const char *pathname, const char *compressor)
00166 {
00167   int pfd;
00168 
00169   pfd = open (pathname, O_RDONLY);
00170   if (pfd >= 0)
00171     {
00172       struct stat statbuf;
00173       int fd[2];
00174 
00175       if (fstat (pfd, &statbuf) >= 0
00176           && S_ISREG (statbuf.st_mode)
00177           && pipe (fd) >= 0)
00178         {
00179           char *argv[4]
00180            = { (char *) compressor, (char *) "-d", (char *) "-c", NULL };
00181           posix_spawn_file_actions_t actions;
00182 
00183           if (posix_spawn_file_actions_init (&actions) == 0)
00184             {
00185               if (posix_spawn_file_actions_adddup2 (&actions,
00186                                                     fd[1], STDOUT_FILENO) == 0
00187                   && posix_spawn_file_actions_addclose (&actions, fd[1]) == 0
00188                   && posix_spawn_file_actions_addclose (&actions, fd[0]) == 0
00189                   && posix_spawn_file_actions_adddup2 (&actions,
00190                                                        pfd, STDIN_FILENO) == 0
00191                   && posix_spawn_file_actions_addclose (&actions, pfd) == 0
00192                   && posix_spawnp (NULL, compressor, &actions, NULL,
00193                                    argv, environ) == 0)
00194                 {
00195                   posix_spawn_file_actions_destroy (&actions);
00196                   close (fd[1]);
00197                   close (pfd);
00198                   return fdopen (fd[0], "r");
00199                 }
00200               posix_spawn_file_actions_destroy (&actions);
00201             }
00202           close (fd[1]);
00203           close (fd[0]);
00204         }
00205       close (pfd);
00206     }
00207   return NULL;
00208 }
00209 
00210 /* Opens a charmap for reading, given its name (not an alias name).  */
00211 FILE *
00212 charmap_open (const char *directory, const char *name)
00213 {
00214   size_t dlen = strlen (directory);
00215   int add_slash = (dlen == 0 || directory[dlen - 1] != '/');
00216   size_t nlen = strlen (name);
00217   char *pathname;
00218   char *p;
00219   FILE *stream;
00220 
00221   pathname = alloca (dlen + add_slash + nlen + 5);
00222   p = stpcpy (pathname, directory);
00223   if (add_slash)
00224     *p++ = '/';
00225   p = stpcpy (p, name);
00226 
00227   stream = fopen (pathname, "rm");
00228   if (stream != NULL)
00229     return stream;
00230 
00231   memcpy (p, ".gz", 4);
00232   stream = fopen_uncompressed (pathname, "gzip");
00233   if (stream != NULL)
00234     return stream;
00235 
00236   memcpy (p, ".bz2", 5);
00237   stream = fopen_uncompressed (pathname, "bzip2");
00238   if (stream != NULL)
00239     return stream;
00240 
00241   return NULL;
00242 }
00243 
00244 /* An empty alias list.  Avoids the need to return NULL from
00245    charmap_aliases.  */
00246 static char *empty[1];
00247 
00248 /* Returns a NULL terminated list of alias names of a charmap.  */
00249 char **
00250 charmap_aliases (const char *directory, const char *name)
00251 {
00252   FILE *stream;
00253   char **aliases;
00254   size_t naliases;
00255 
00256   stream = charmap_open (directory, name);
00257   if (stream == NULL)
00258     return empty;
00259 
00260   aliases = NULL;
00261   naliases = 0;
00262 
00263   while (!feof (stream))
00264     {
00265       char *alias = NULL;
00266       char junk[BUFSIZ];
00267 
00268       if (fscanf (stream, " <code_set_name> %ms", &alias) == 1
00269           || fscanf (stream, "%% alias %ms", &alias) == 1)
00270         {
00271           aliases = (char **) xrealloc (aliases,
00272                                         (naliases + 2) * sizeof (char *));
00273           aliases[naliases++] = alias;
00274         }
00275 
00276       /* Read the rest of the line.  */
00277       if (fgets (junk, sizeof junk, stream) != NULL)
00278         {
00279           if (strstr (junk, "CHARMAP") != NULL)
00280             /* We cannot expect more aliases from now on.  */
00281             break;
00282 
00283           while (strchr (junk, '\n') == NULL
00284                  && fgets (junk, sizeof junk, stream) != NULL)
00285             continue;
00286         }
00287     }
00288 
00289   fclose (stream);
00290 
00291   if (naliases == 0)
00292     return empty;
00293 
00294   aliases[naliases] = NULL;
00295   return aliases;
00296 }
00297 
00298 /* Frees an alias list returned by charmap_aliases.  */
00299 void
00300 charmap_free_aliases (char **aliases)
00301 {
00302   if (aliases != empty)
00303     {
00304       char **p;
00305 
00306       for (p = aliases; *p; p++)
00307         free (*p);
00308 
00309       free (aliases);
00310     }
00311 }