Blame locale/programs/charmap-dir.c

Packit 6c4009
/* Copyright (C) 2000-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   This program is free software; you can redistribute it and/or modify
Packit 6c4009
   it under the terms of the GNU General Public License as published
Packit 6c4009
   by the Free Software Foundation; version 2 of the License, or
Packit 6c4009
   (at your option) any later version.
Packit 6c4009
Packit 6c4009
   This program is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 6c4009
   GNU General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU General Public License
Packit 6c4009
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <dirent.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
#include <spawn.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <sys/stat.h>
Packit 6c4009
Packit 6c4009
#include "localedef.h"
Packit 6c4009
#include "charmap-dir.h"
Packit 6c4009
Packit 6c4009
/* The data type of a charmap directory being traversed.  */
Packit 6c4009
struct charmap_dir
Packit 6c4009
{
Packit 6c4009
  DIR *dir;
Packit 6c4009
  /* The directory pathname, ending in a slash.  */
Packit 6c4009
  char *directory;
Packit 6c4009
  size_t directory_len;
Packit 6c4009
  /* Scratch area used for returning pathnames.  */
Packit 6c4009
  char *pathname;
Packit 6c4009
  size_t pathname_size;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* Starts a charmap directory traversal.
Packit 6c4009
   Returns a CHARMAP_DIR, or NULL if the directory doesn't exist.  */
Packit 6c4009
CHARMAP_DIR *
Packit 6c4009
charmap_opendir (const char *directory)
Packit 6c4009
{
Packit 6c4009
  struct charmap_dir *cdir;
Packit 6c4009
  DIR *dir;
Packit 6c4009
  size_t len;
Packit 6c4009
  int add_slash;
Packit 6c4009
Packit 6c4009
  dir = opendir (directory);
Packit 6c4009
  if (dir == NULL)
Packit 6c4009
    {
Packit 6c4009
      record_error (1, errno, gettext ("\
Packit 6c4009
cannot read character map directory `%s'"),
Packit 6c4009
		    directory);
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  cdir = (struct charmap_dir *) xmalloc (sizeof (struct charmap_dir));
Packit 6c4009
  cdir->dir = dir;
Packit 6c4009
Packit 6c4009
  len = strlen (directory);
Packit 6c4009
  add_slash = (len == 0 || directory[len - 1] != '/');
Packit 6c4009
  cdir->directory = (char *) xmalloc (len + add_slash + 1);
Packit 6c4009
  memcpy (cdir->directory, directory, len);
Packit 6c4009
  if (add_slash)
Packit 6c4009
    cdir->directory[len] = '/';
Packit 6c4009
  cdir->directory[len + add_slash] = '\0';
Packit 6c4009
  cdir->directory_len = len + add_slash;
Packit 6c4009
Packit 6c4009
  cdir->pathname = NULL;
Packit 6c4009
  cdir->pathname_size = 0;
Packit 6c4009
Packit 6c4009
  return cdir;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Reads the next directory entry.
Packit 6c4009
   Returns its charmap name, or NULL if past the last entry or upon error.
Packit 6c4009
   The storage returned may be overwritten by a later charmap_readdir
Packit 6c4009
   call on the same CHARMAP_DIR.  */
Packit 6c4009
const char *
Packit 6c4009
charmap_readdir (CHARMAP_DIR *cdir)
Packit 6c4009
{
Packit 6c4009
  for (;;)
Packit 6c4009
    {
Packit 6c4009
      struct dirent64 *dirent;
Packit 6c4009
      size_t len;
Packit 6c4009
      size_t size;
Packit 6c4009
      char *filename;
Packit 6c4009
      mode_t mode;
Packit 6c4009
Packit 6c4009
      dirent = readdir64 (cdir->dir);
Packit 6c4009
      if (dirent == NULL)
Packit 6c4009
        return NULL;
Packit 6c4009
      if (strcmp (dirent->d_name, ".") == 0)
Packit 6c4009
        continue;
Packit 6c4009
      if (strcmp (dirent->d_name, "..") == 0)
Packit 6c4009
        continue;
Packit 6c4009
Packit 6c4009
      len = strlen (dirent->d_name);
Packit 6c4009
Packit 6c4009
      size = cdir->directory_len + len + 1;
Packit 6c4009
      if (size > cdir->pathname_size)
Packit 6c4009
        {
Packit 6c4009
          free (cdir->pathname);
Packit 6c4009
          if (size < 2 * cdir->pathname_size)
Packit 6c4009
            size = 2 * cdir->pathname_size;
Packit 6c4009
          cdir->pathname = (char *) xmalloc (size);
Packit 6c4009
          cdir->pathname_size = size;
Packit 6c4009
        }
Packit 6c4009
Packit 6c4009
      stpcpy (stpcpy (cdir->pathname, cdir->directory), dirent->d_name);
Packit 6c4009
      filename = cdir->pathname + cdir->directory_len;
Packit 6c4009
Packit 6c4009
      if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK)
Packit 6c4009
        mode = DTTOIF (dirent->d_type);
Packit 6c4009
      else
Packit 6c4009
        {
Packit 6c4009
          struct stat64 statbuf;
Packit 6c4009
Packit 6c4009
          if (stat64 (cdir->pathname, &statbuf) < 0)
Packit 6c4009
            continue;
Packit 6c4009
Packit 6c4009
          mode = statbuf.st_mode;
Packit 6c4009
        }
Packit 6c4009
Packit 6c4009
      if (!S_ISREG (mode))
Packit 6c4009
        continue;
Packit 6c4009
Packit 6c4009
      /* For compressed charmaps, the canonical charmap name does not
Packit 6c4009
         include the extension.  */
Packit 6c4009
      if (len > 3 && memcmp (&filename[len - 3], ".gz", 3) == 0)
Packit 6c4009
        filename[len - 3] = '\0';
Packit 6c4009
      else if (len > 4 && memcmp (&filename[len - 4], ".bz2", 4) == 0)
Packit 6c4009
        filename[len - 4] = '\0';
Packit 6c4009
Packit 6c4009
      return filename;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Finishes a charmap directory traversal, and frees the resources
Packit 6c4009
   attached to the CHARMAP_DIR.  */
Packit 6c4009
int
Packit 6c4009
charmap_closedir (CHARMAP_DIR *cdir)
Packit 6c4009
{
Packit 6c4009
  DIR *dir = cdir->dir;
Packit 6c4009
Packit 6c4009
  free (cdir->directory);
Packit 6c4009
  free (cdir->pathname);
Packit 6c4009
  free (cdir);
Packit 6c4009
  return closedir (dir);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Creates a subprocess decompressing the given pathname, and returns
Packit 6c4009
   a stream reading its output (the decompressed data).  */
Packit 6c4009
static
Packit 6c4009
FILE *
Packit 6c4009
fopen_uncompressed (const char *pathname, const char *compressor)
Packit 6c4009
{
Packit 6c4009
  int pfd;
Packit 6c4009
Packit 6c4009
  pfd = open (pathname, O_RDONLY);
Packit 6c4009
  if (pfd >= 0)
Packit 6c4009
    {
Packit 6c4009
      struct stat64 statbuf;
Packit 6c4009
      int fd[2];
Packit 6c4009
Packit 6c4009
      if (fstat64 (pfd, &statbuf) >= 0
Packit 6c4009
          && S_ISREG (statbuf.st_mode)
Packit 6c4009
          && pipe (fd) >= 0)
Packit 6c4009
        {
Packit 6c4009
          char *argv[4]
Packit 6c4009
	    = { (char *) compressor, (char *) "-d", (char *) "-c", NULL };
Packit 6c4009
          posix_spawn_file_actions_t actions;
Packit 6c4009
Packit 6c4009
          if (posix_spawn_file_actions_init (&actions) == 0)
Packit 6c4009
            {
Packit 6c4009
              if (posix_spawn_file_actions_adddup2 (&actions,
Packit 6c4009
                                                    fd[1], STDOUT_FILENO) == 0
Packit 6c4009
                  && posix_spawn_file_actions_addclose (&actions, fd[1]) == 0
Packit 6c4009
                  && posix_spawn_file_actions_addclose (&actions, fd[0]) == 0
Packit 6c4009
                  && posix_spawn_file_actions_adddup2 (&actions,
Packit 6c4009
                                                       pfd, STDIN_FILENO) == 0
Packit 6c4009
                  && posix_spawn_file_actions_addclose (&actions, pfd) == 0
Packit 6c4009
                  && posix_spawnp (NULL, compressor, &actions, NULL,
Packit 6c4009
                                   argv, environ) == 0)
Packit 6c4009
                {
Packit 6c4009
                  posix_spawn_file_actions_destroy (&actions);
Packit 6c4009
                  close (fd[1]);
Packit 6c4009
                  close (pfd);
Packit 6c4009
                  return fdopen (fd[0], "r");
Packit 6c4009
                }
Packit 6c4009
              posix_spawn_file_actions_destroy (&actions);
Packit 6c4009
            }
Packit 6c4009
          close (fd[1]);
Packit 6c4009
          close (fd[0]);
Packit 6c4009
        }
Packit 6c4009
      close (pfd);
Packit 6c4009
    }
Packit 6c4009
  return NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Opens a charmap for reading, given its name (not an alias name).  */
Packit 6c4009
FILE *
Packit 6c4009
charmap_open (const char *directory, const char *name)
Packit 6c4009
{
Packit 6c4009
  size_t dlen = strlen (directory);
Packit 6c4009
  int add_slash = (dlen == 0 || directory[dlen - 1] != '/');
Packit 6c4009
  size_t nlen = strlen (name);
Packit 6c4009
  char *pathname;
Packit 6c4009
  char *p;
Packit 6c4009
  FILE *stream;
Packit 6c4009
Packit 6c4009
  pathname = alloca (dlen + add_slash + nlen + 5);
Packit 6c4009
  p = stpcpy (pathname, directory);
Packit 6c4009
  if (add_slash)
Packit 6c4009
    *p++ = '/';
Packit 6c4009
  p = stpcpy (p, name);
Packit 6c4009
Packit 6c4009
  stream = fopen (pathname, "rm");
Packit 6c4009
  if (stream != NULL)
Packit 6c4009
    return stream;
Packit 6c4009
Packit 6c4009
  memcpy (p, ".gz", 4);
Packit 6c4009
  stream = fopen_uncompressed (pathname, "gzip");
Packit 6c4009
  if (stream != NULL)
Packit 6c4009
    return stream;
Packit 6c4009
Packit 6c4009
  memcpy (p, ".bz2", 5);
Packit 6c4009
  stream = fopen_uncompressed (pathname, "bzip2");
Packit 6c4009
  if (stream != NULL)
Packit 6c4009
    return stream;
Packit 6c4009
Packit 6c4009
  return NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* An empty alias list.  Avoids the need to return NULL from
Packit 6c4009
   charmap_aliases.  */
Packit 6c4009
static char *empty[1];
Packit 6c4009
Packit 6c4009
/* Returns a NULL terminated list of alias names of a charmap.  */
Packit 6c4009
char **
Packit 6c4009
charmap_aliases (const char *directory, const char *name)
Packit 6c4009
{
Packit 6c4009
  FILE *stream;
Packit 6c4009
  char **aliases;
Packit 6c4009
  size_t naliases;
Packit 6c4009
Packit 6c4009
  stream = charmap_open (directory, name);
Packit 6c4009
  if (stream == NULL)
Packit 6c4009
    return empty;
Packit 6c4009
Packit 6c4009
  aliases = NULL;
Packit 6c4009
  naliases = 0;
Packit 6c4009
Packit 6c4009
  while (!feof (stream))
Packit 6c4009
    {
Packit 6c4009
      char *alias = NULL;
Packit 6c4009
      char junk[BUFSIZ];
Packit 6c4009
Packit 6c4009
      if (fscanf (stream, " <code_set_name> %ms", &alias) == 1
Packit 6c4009
          || fscanf (stream, "%% alias %ms", &alias) == 1)
Packit 6c4009
        {
Packit 6c4009
          aliases = (char **) xrealloc (aliases,
Packit 6c4009
                                        (naliases + 2) * sizeof (char *));
Packit 6c4009
          aliases[naliases++] = alias;
Packit 6c4009
        }
Packit 6c4009
Packit 6c4009
      /* Read the rest of the line.  */
Packit 6c4009
      if (fgets (junk, sizeof junk, stream) != NULL)
Packit 6c4009
        {
Packit 6c4009
          if (strstr (junk, "CHARMAP") != NULL)
Packit 6c4009
            /* We cannot expect more aliases from now on.  */
Packit 6c4009
            break;
Packit 6c4009
Packit 6c4009
          while (strchr (junk, '\n') == NULL
Packit 6c4009
                 && fgets (junk, sizeof junk, stream) != NULL)
Packit 6c4009
            continue;
Packit 6c4009
        }
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  fclose (stream);
Packit 6c4009
Packit 6c4009
  if (naliases == 0)
Packit 6c4009
    return empty;
Packit 6c4009
Packit 6c4009
  aliases[naliases] = NULL;
Packit 6c4009
  return aliases;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Frees an alias list returned by charmap_aliases.  */
Packit 6c4009
void
Packit 6c4009
charmap_free_aliases (char **aliases)
Packit 6c4009
{
Packit 6c4009
  if (aliases != empty)
Packit 6c4009
    {
Packit 6c4009
      char **p;
Packit 6c4009
Packit 6c4009
      for (p = aliases; *p; p++)
Packit 6c4009
        free (*p);
Packit 6c4009
Packit 6c4009
      free (aliases);
Packit 6c4009
    }
Packit 6c4009
}