Blame src/convert_extern.c

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
 */