(root)/
glibc-2.38/
locale/
programs/
charmap-dir.c
       1  /* Copyright (C) 2000-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     This program is free software; you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published
       6     by the Free Software Foundation; version 2 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program; if not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  #include <dirent.h>
      18  #include <errno.h>
      19  #include <fcntl.h>
      20  #include <libintl.h>
      21  #include <spawn.h>
      22  #include <stdio.h>
      23  #include <stdlib.h>
      24  #include <string.h>
      25  #include <unistd.h>
      26  #include <sys/stat.h>
      27  
      28  #include "localedef.h"
      29  #include "charmap-dir.h"
      30  
      31  /* The data type of a charmap directory being traversed.  */
      32  struct charmap_dir
      33  {
      34    DIR *dir;
      35    /* The directory pathname, ending in a slash.  */
      36    char *directory;
      37    size_t directory_len;
      38    /* Scratch area used for returning pathnames.  */
      39    char *pathname;
      40    size_t pathname_size;
      41  };
      42  
      43  /* Starts a charmap directory traversal.
      44     Returns a CHARMAP_DIR, or NULL if the directory doesn't exist.  */
      45  CHARMAP_DIR *
      46  charmap_opendir (const char *directory)
      47  {
      48    struct charmap_dir *cdir;
      49    DIR *dir;
      50    size_t len;
      51    int add_slash;
      52  
      53    dir = opendir (directory);
      54    if (dir == NULL)
      55      {
      56        record_error (1, errno, gettext ("\
      57  cannot read character map directory `%s'"),
      58  		    directory);
      59        return NULL;
      60      }
      61  
      62    cdir = (struct charmap_dir *) xmalloc (sizeof (struct charmap_dir));
      63    cdir->dir = dir;
      64  
      65    len = strlen (directory);
      66    add_slash = (len == 0 || directory[len - 1] != '/');
      67    cdir->directory = (char *) xmalloc (len + add_slash + 1);
      68    memcpy (cdir->directory, directory, len);
      69    if (add_slash)
      70      cdir->directory[len] = '/';
      71    cdir->directory[len + add_slash] = '\0';
      72    cdir->directory_len = len + add_slash;
      73  
      74    cdir->pathname = NULL;
      75    cdir->pathname_size = 0;
      76  
      77    return cdir;
      78  }
      79  
      80  /* Reads the next directory entry.
      81     Returns its charmap name, or NULL if past the last entry or upon error.
      82     The storage returned may be overwritten by a later charmap_readdir
      83     call on the same CHARMAP_DIR.  */
      84  const char *
      85  charmap_readdir (CHARMAP_DIR *cdir)
      86  {
      87    for (;;)
      88      {
      89        struct dirent64 *dirent;
      90        size_t len;
      91        size_t size;
      92        char *filename;
      93        mode_t mode;
      94  
      95        dirent = readdir64 (cdir->dir);
      96        if (dirent == NULL)
      97          return NULL;
      98        if (strcmp (dirent->d_name, ".") == 0)
      99          continue;
     100        if (strcmp (dirent->d_name, "..") == 0)
     101          continue;
     102  
     103        len = strlen (dirent->d_name);
     104  
     105        size = cdir->directory_len + len + 1;
     106        if (size > cdir->pathname_size)
     107          {
     108            free (cdir->pathname);
     109            if (size < 2 * cdir->pathname_size)
     110              size = 2 * cdir->pathname_size;
     111            cdir->pathname = (char *) xmalloc (size);
     112            cdir->pathname_size = size;
     113          }
     114  
     115        stpcpy (stpcpy (cdir->pathname, cdir->directory), dirent->d_name);
     116        filename = cdir->pathname + cdir->directory_len;
     117  
     118        if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
     119          mode = DTTOIF (dirent->d_type);
     120        else
     121          {
     122            struct stat64 statbuf;
     123  
     124            if (stat64 (cdir->pathname, &statbuf) < 0)
     125              continue;
     126  
     127            mode = statbuf.st_mode;
     128          }
     129  
     130        if (!S_ISREG (mode))
     131          continue;
     132  
     133        /* For compressed charmaps, the canonical charmap name does not
     134           include the extension.  */
     135        if (len > 3 && memcmp (&filename[len - 3], ".gz", 3) == 0)
     136          filename[len - 3] = '\0';
     137        else if (len > 4 && memcmp (&filename[len - 4], ".bz2", 4) == 0)
     138          filename[len - 4] = '\0';
     139  
     140        return filename;
     141      }
     142  }
     143  
     144  /* Finishes a charmap directory traversal, and frees the resources
     145     attached to the CHARMAP_DIR.  */
     146  int
     147  charmap_closedir (CHARMAP_DIR *cdir)
     148  {
     149    DIR *dir = cdir->dir;
     150  
     151    free (cdir->directory);
     152    free (cdir->pathname);
     153    free (cdir);
     154    return closedir (dir);
     155  }
     156  
     157  /* Creates a subprocess decompressing the given pathname, and returns
     158     a stream reading its output (the decompressed data).  */
     159  static
     160  FILE *
     161  fopen_uncompressed (const char *pathname, const char *compressor)
     162  {
     163    int pfd;
     164  
     165    pfd = open (pathname, O_RDONLY);
     166    if (pfd >= 0)
     167      {
     168        struct stat64 statbuf;
     169        int fd[2];
     170  
     171        if (fstat64 (pfd, &statbuf) >= 0
     172            && S_ISREG (statbuf.st_mode)
     173            && pipe (fd) >= 0)
     174          {
     175            char *argv[4]
     176  	    = { (char *) compressor, (char *) "-d", (char *) "-c", NULL };
     177            posix_spawn_file_actions_t actions;
     178  
     179            if (posix_spawn_file_actions_init (&actions) == 0)
     180              {
     181                if (posix_spawn_file_actions_adddup2 (&actions,
     182                                                      fd[1], STDOUT_FILENO) == 0
     183                    && posix_spawn_file_actions_addclose (&actions, fd[1]) == 0
     184                    && posix_spawn_file_actions_addclose (&actions, fd[0]) == 0
     185                    && posix_spawn_file_actions_adddup2 (&actions,
     186                                                         pfd, STDIN_FILENO) == 0
     187                    && posix_spawn_file_actions_addclose (&actions, pfd) == 0
     188                    && posix_spawnp (NULL, compressor, &actions, NULL,
     189                                     argv, environ) == 0)
     190                  {
     191                    posix_spawn_file_actions_destroy (&actions);
     192                    close (fd[1]);
     193                    close (pfd);
     194                    return fdopen (fd[0], "r");
     195                  }
     196                posix_spawn_file_actions_destroy (&actions);
     197              }
     198            close (fd[1]);
     199            close (fd[0]);
     200          }
     201        close (pfd);
     202      }
     203    return NULL;
     204  }
     205  
     206  /* Opens a charmap for reading, given its name (not an alias name).  */
     207  FILE *
     208  charmap_open (const char *directory, const char *name)
     209  {
     210    size_t dlen = strlen (directory);
     211    int add_slash = (dlen == 0 || directory[dlen - 1] != '/');
     212    size_t nlen = strlen (name);
     213    char *pathname;
     214    char *p;
     215    FILE *stream;
     216  
     217    pathname = alloca (dlen + add_slash + nlen + 5);
     218    p = stpcpy (pathname, directory);
     219    if (add_slash)
     220      *p++ = '/';
     221    p = stpcpy (p, name);
     222  
     223    stream = fopen (pathname, "rm");
     224    if (stream != NULL)
     225      return stream;
     226  
     227    memcpy (p, ".gz", 4);
     228    stream = fopen_uncompressed (pathname, "gzip");
     229    if (stream != NULL)
     230      return stream;
     231  
     232    memcpy (p, ".bz2", 5);
     233    stream = fopen_uncompressed (pathname, "bzip2");
     234    if (stream != NULL)
     235      return stream;
     236  
     237    return NULL;
     238  }
     239  
     240  /* An empty alias list.  Avoids the need to return NULL from
     241     charmap_aliases.  */
     242  static char *empty[1];
     243  
     244  /* Returns a NULL terminated list of alias names of a charmap.  */
     245  char **
     246  charmap_aliases (const char *directory, const char *name)
     247  {
     248    FILE *stream;
     249    char **aliases;
     250    size_t naliases;
     251  
     252    stream = charmap_open (directory, name);
     253    if (stream == NULL)
     254      return empty;
     255  
     256    aliases = NULL;
     257    naliases = 0;
     258  
     259    while (!feof (stream))
     260      {
     261        char *alias = NULL;
     262        char junk[BUFSIZ];
     263  
     264        if (fscanf (stream, " <code_set_name> %ms", &alias) == 1
     265            || fscanf (stream, "%% alias %ms", &alias) == 1)
     266          {
     267            aliases = (char **) xrealloc (aliases,
     268                                          (naliases + 2) * sizeof (char *));
     269            aliases[naliases++] = alias;
     270          }
     271  
     272        /* Read the rest of the line.  */
     273        if (fgets (junk, sizeof junk, stream) != NULL)
     274          {
     275            if (strstr (junk, "CHARMAP") != NULL)
     276              /* We cannot expect more aliases from now on.  */
     277              break;
     278  
     279            while (strchr (junk, '\n') == NULL
     280                   && fgets (junk, sizeof junk, stream) != NULL)
     281              continue;
     282          }
     283      }
     284  
     285    fclose (stream);
     286  
     287    if (naliases == 0)
     288      return empty;
     289  
     290    aliases[naliases] = NULL;
     291    return aliases;
     292  }
     293  
     294  /* Frees an alias list returned by charmap_aliases.  */
     295  void
     296  charmap_free_aliases (char **aliases)
     297  {
     298    if (aliases != empty)
     299      {
     300        char **p;
     301  
     302        for (p = aliases; *p; p++)
     303          free (*p);
     304  
     305        free (aliases);
     306      }
     307  }