|
Packit |
57a33d |
/*
|
|
Packit |
57a33d |
interface to external converter programs
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
Copyright (C) 2000-2003 David Necas (Yeti) <yeti@physics.muni.cz>
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
This program is free software; you can redistribute it and/or modify it
|
|
Packit |
57a33d |
under the terms of version 2 of the GNU General Public License as published
|
|
Packit |
57a33d |
by the Free Software Foundation.
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
Packit |
57a33d |
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
Packit |
57a33d |
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
Packit |
57a33d |
more details.
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
You should have received a copy of the GNU General Public License along
|
|
Packit |
57a33d |
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
Packit |
57a33d |
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
|
Packit |
57a33d |
*/
|
|
Packit |
57a33d |
#include "common.h"
|
|
Packit |
57a33d |
#ifdef ENABLE_EXTERNAL
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
#ifdef HAVE_SYS_WAIT_H
|
|
Packit |
57a33d |
# include <sys/wait.h>
|
|
Packit |
57a33d |
#else
|
|
Packit |
57a33d |
pid_t waitpid(pid_t pid, int *status, int options);
|
|
Packit |
57a33d |
#endif
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
/* We can't go on w/o this, defining struct stat manually is braindamaged. */
|
|
Packit |
57a33d |
#include <sys/types.h>
|
|
Packit |
57a33d |
#include <sys/stat.h>
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
#include <unistd.h>
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
#ifdef HAVE_LIMITS_H
|
|
Packit |
57a33d |
# include <limits.h>
|
|
Packit |
57a33d |
#endif /* HAVE_LIMITS_H */
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
/* Resolve all the forking mess. */
|
|
Packit |
57a33d |
#ifdef HAVE_WORKING_VFORK
|
|
Packit |
57a33d |
# ifdef HAVE_VFORK_H
|
|
Packit |
57a33d |
# include <vfork.h>
|
|
Packit |
57a33d |
# endif /* HAVE_VFORK_H */
|
|
Packit |
57a33d |
#else /* HAVE_WORKING_VFORK */
|
|
Packit |
57a33d |
# define vfork fork
|
|
Packit |
57a33d |
#endif /* HAVE_WORKING_VFORK */
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
/* external converter command */
|
|
Packit |
57a33d |
static char *extern_converter = NULL;
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
/* fork and the child executes Settings.Converter on fname
|
|
Packit |
57a33d |
create temporary file containing stdin when fname == NULL and convert it
|
|
Packit |
57a33d |
passing special option STDOUT to converter (that is assumed to delete
|
|
Packit |
57a33d |
the temporary file itself)
|
|
Packit |
57a33d |
from_enc, to_enc are encoding names as should be passed to converter
|
|
Packit |
57a33d |
returns 0 on success, nonzero on failure;
|
|
Packit |
57a33d |
on critical failure (like we cannot fork()) it simply aborts */
|
|
Packit |
57a33d |
int
|
|
Packit |
57a33d |
convert_external(File *file,
|
|
Packit |
57a33d |
const EncaEncoding from_enc)
|
|
Packit |
57a33d |
{
|
|
Packit |
57a33d |
/* special fourth parameter passed to external converter to instruct it to
|
|
Packit |
57a33d |
send result to stdout */
|
|
Packit |
57a33d |
static const char *STDOUT_CONV = "-";
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
pid_t pid;
|
|
Packit |
57a33d |
int status;
|
|
Packit |
57a33d |
File *tempfile = NULL;
|
|
Packit |
57a33d |
char *from_name, *target_name;
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
if (*extern_converter == '\0') {
|
|
Packit |
57a33d |
fprintf(stderr, "%s: No external converter defined!\n", program_name);
|
|
Packit |
57a33d |
return ERR_CANNOT;
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
if (options.verbosity_level > 2)
|
|
Packit |
57a33d |
fprintf(stderr, " launching `%s' to convert `%s'\n",
|
|
Packit |
57a33d |
extern_converter, ffname_r(file->name));
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
/* Is conversion of stdin requested? */
|
|
Packit |
57a33d |
if (file->name == NULL) {
|
|
Packit |
57a33d |
/* Then we have to copy it to a temporary file. */
|
|
Packit |
57a33d |
tempfile = file_temporary(file->buffer, 0);
|
|
Packit |
57a33d |
if (tempfile == NULL)
|
|
Packit |
57a33d |
return ERR_IOFAIL;
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
if (copy_and_convert(file, tempfile, NULL) != 0) {
|
|
Packit |
57a33d |
file_unlink(tempfile->name);
|
|
Packit |
57a33d |
file_free(tempfile);
|
|
Packit |
57a33d |
return ERR_IOFAIL;
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
/* Construct the charset names before fork() */
|
|
Packit |
57a33d |
from_name = enca_strconcat(enca_charset_name(from_enc.charset,
|
|
Packit |
57a33d |
ENCA_NAME_STYLE_ENCA),
|
|
Packit |
57a33d |
enca_get_surface_name(from_enc.surface,
|
|
Packit |
57a33d |
ENCA_NAME_STYLE_ENCA),
|
|
Packit |
57a33d |
NULL);
|
|
Packit |
57a33d |
if (enca_charset_is_known(options.target_enc.charset)
|
|
Packit |
57a33d |
&& (options.target_enc.surface & ENCA_SURFACE_UNKNOWN) == 0) {
|
|
Packit |
57a33d |
target_name
|
|
Packit |
57a33d |
= enca_strconcat(enca_charset_name(options.target_enc.charset,
|
|
Packit |
57a33d |
ENCA_NAME_STYLE_ENCA),
|
|
Packit |
57a33d |
enca_get_surface_name(options.target_enc.surface,
|
|
Packit |
57a33d |
ENCA_NAME_STYLE_ENCA),
|
|
Packit |
57a33d |
NULL);
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
else
|
|
Packit |
57a33d |
target_name = enca_strdup(options.target_enc_str);
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
/* Fork. */
|
|
Packit |
57a33d |
pid = vfork();
|
|
Packit |
57a33d |
if (pid == 0) {
|
|
Packit |
57a33d |
/* Child. */
|
|
Packit |
57a33d |
if (tempfile)
|
|
Packit |
57a33d |
execlp(extern_converter, extern_converter,
|
|
Packit |
57a33d |
from_name, target_name, tempfile->name,
|
|
Packit |
57a33d |
STDOUT_CONV, NULL);
|
|
Packit |
57a33d |
else
|
|
Packit |
57a33d |
execlp(extern_converter, extern_converter,
|
|
Packit |
57a33d |
from_name, target_name, file->name, NULL);
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
exit(ERR_EXEC);
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
/* Parent. */
|
|
Packit |
57a33d |
if (pid == -1) {
|
|
Packit |
57a33d |
fprintf(stderr, "%s: Cannot fork() to execute converter: %s\n",
|
|
Packit |
57a33d |
program_name,
|
|
Packit |
57a33d |
strerror(errno));
|
|
Packit |
57a33d |
exit(EXIT_TROUBLE);
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
/* Wait until the child returns. */
|
|
Packit |
57a33d |
if (waitpid(pid, &status, 0) == -1) {
|
|
Packit |
57a33d |
/* Error. */
|
|
Packit |
57a33d |
fprintf(stderr, "%s: wait_pid() error while waiting for converter: %s\n",
|
|
Packit |
57a33d |
program_name,
|
|
Packit |
57a33d |
strerror(errno));
|
|
Packit |
57a33d |
exit(EXIT_TROUBLE);
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
if (!WIFEXITED(status)) {
|
|
Packit |
57a33d |
/* Child exited abnormally. */
|
|
Packit |
57a33d |
fprintf(stderr, "%s: Child converter process has been murdered.\n",
|
|
Packit |
57a33d |
program_name);
|
|
Packit |
57a33d |
exit(EXIT_TROUBLE);
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
enca_free(from_name);
|
|
Packit |
57a33d |
enca_free(target_name);
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
if (tempfile) {
|
|
Packit |
57a33d |
unlink(tempfile->name);
|
|
Packit |
57a33d |
file_free(tempfile);
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
/* Child exited normally, test exit status. */
|
|
Packit |
57a33d |
if (WEXITSTATUS(status) != EXIT_SUCCESS) {
|
|
Packit |
57a33d |
/* This means child was unable to execute converter or converter failed. */
|
|
Packit |
57a33d |
fprintf(stderr, "%s: External converter failed (error code %d)\n",
|
|
Packit |
57a33d |
program_name,
|
|
Packit |
57a33d |
WEXITSTATUS(status));
|
|
Packit |
57a33d |
if (WEXITSTATUS(status) == ERR_EXEC)
|
|
Packit |
57a33d |
return ERR_EXEC;
|
|
Packit |
57a33d |
else
|
|
Packit |
57a33d |
return ERR_CANNOT;
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
/* Success! Wow! */
|
|
Packit |
57a33d |
return ERR_OK;
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
/* set external converter to extc */
|
|
Packit |
57a33d |
void
|
|
Packit |
57a33d |
set_external_converter(const char *extc)
|
|
Packit |
57a33d |
{
|
|
Packit |
57a33d |
enca_free(extern_converter);
|
|
Packit |
57a33d |
if (strchr(extc, '/') == NULL) {
|
|
Packit |
57a33d |
if (extc[0] == 'b' && extc[1] == '-') {
|
|
Packit |
57a33d |
extc += 2;
|
|
Packit |
57a33d |
fprintf(stderr, "%s: The `b-' prefix for standard external converters "
|
|
Packit |
57a33d |
"is deprecated.\n"
|
|
Packit |
57a33d |
"I'll pretend you said `%s'.\n",
|
|
Packit |
57a33d |
program_name,
|
|
Packit |
57a33d |
extc);
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
extern_converter = enca_strconcat(EXTCONV_DIR, "/", extc, NULL);
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
else
|
|
Packit |
57a33d |
extern_converter = enca_strdup(extc);
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
/* return nonzero if external converter seems ok */
|
|
Packit |
57a33d |
int
|
|
Packit |
57a33d |
check_external_converter(void)
|
|
Packit |
57a33d |
{
|
|
Packit |
57a33d |
/* FIXME: This creates a race condition. However we don't want to do all
|
|
Packit |
57a33d |
* the checking before every execlp() when conveting 500 files in a row,
|
|
Packit |
57a33d |
* and even if doing that, something can still sneak between stat() and
|
|
Packit |
57a33d |
* execlp(), so what. This is just a simple sanity check, nothing strict.
|
|
Packit |
57a33d |
*/
|
|
Packit |
57a33d |
if (*extern_converter == '\0'
|
|
Packit |
57a33d |
|| access(extern_converter, X_OK) != 0) {
|
|
Packit |
57a33d |
fprintf(stderr, "%s: Converter `%s' doesn't seem to be executable.\n"
|
|
Packit |
57a33d |
"Note as of enca-1.3 external converters must be\n"
|
|
Packit |
57a33d |
"(a) one of the standard ones residing in %s\n"
|
|
Packit |
57a33d |
"(b) specified with full path\n",
|
|
Packit |
57a33d |
program_name,
|
|
Packit |
57a33d |
extern_converter,
|
|
Packit |
57a33d |
EXTCONV_DIR);
|
|
Packit |
57a33d |
return 0;
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
return 1;
|
|
Packit |
57a33d |
}
|
|
Packit |
57a33d |
|
|
Packit |
57a33d |
#endif /* ENABLE_EXTERNAL */
|
|
Packit |
57a33d |
/* vim: ts=2
|
|
Packit |
57a33d |
*/
|