|
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 |
}
|