/* runht.c: routines to call tex4ht.

Copyright (C) 2000 Fabrice POPINEAU.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* dirty fix for ht --ak */
int isHT = 0;
/* ------------- */

#include <kpathsea/config.h>
#include <kpathsea/lib.h>
#include <kpathsea/progname.h>
#include <kpathsea/c-dir.h>
#include <kpathsea/c-pathmx.h>
#include <kpathsea/getopt.h>
#include <kpathsea/tex-file.h>
#include <process.h>

typedef enum {
  MATH_BITMAP = 0,
  MATH_BITMAP_UNICODE,
  MATH_MATHML,
  MATH_MOZILLA,
  MATH_LAST
} math_type;

typedef enum {
  DTD_HTML = 0,
  DTD_XHTML,
  DTD_TEI,
  DTD_DOCBOOK,
  DTD_EBOOK,
  DTD_HTML4WORD,
  DTD_XHTML4WORD,
  DTD_LAST
} dtd_type;

char *stdcfg[MATH_LAST][DTD_LAST][4] = {
  /* MATH_BITMAP */
  {
    { "html", "", "alias,iso8859", "" },                 /* HTML */
    { "xhtml", "", "alias,iso8859", "" },                /* XHTML */
    { "", "", "", "" },	                 /* TEI, INVALID */
    { "", "", "", ""},	         /* DOCBOOK, INVALID */
    { "", "", "", ""},           /* EBOOK, INVALID */
    { "html,word", "", "symbol,alias,iso8859", "" },	 /* HTML4WORD */
    { "xhtml,word", "", "symbol,alias,iso8859", "" },	 /* XHTML4WORD */      
  },
  /* MATH_BITMAP_UNICODE */
  {
    { "html,uni-html4", "", "unicode", "" },             /* HTML */            
    { "xhtml,uni-html4", "", "unicode", "" },		 /* XHTML */           
    { "xhtml,tei", "", "unicode", "" },			 /* TEI */    
    { "xhtml,docbook", "", "unicode", ""},		 /* DOCBOOK */
    { "xhtml,ebmath", "-g.png", "unicode", ""},		 /* EBOOK */  
    { "", "", "", "" },		 /* HTML4WORD, INVALID */       
    { "", "", "", "" },		 /* XHTML4WORD,INVALID */      
  },
  /* MATH_MATHML */
  {
    { "", "", "", "" },                 /* HTML, INVALID */            
    { "xhtml,mathml", "", "unicode", "" },		 /* XHTML */           
    { "xhtml,tei-mml", "", "unicode", "" },		 /* TEI */    
    { "xhtml,docbook-mml", "", "unicode", ""},		 /* DOCBOOK */
    { "xhtml,ebook-mml", "-g.png", "unicode", ""},	 /* EBOOK */  
    { "", "", "", "" },		 /* HTML4WORD, INVALID */
    { "", "", "", "" },		 /* XHTML4WORD, INVALID */
  },
  /* MATH_MOZILLA */
  {
    { "", "", "", "" },                 /* HTML, INVALID */            
    { "xhtml,mozilla", "", "mozilla,unicode", "" },	 /* XHTML */           
    { "", "", "", "" },			 /* TEI, INVALID */    
    { "", "", "", ""},		 /* DOCBOOK, INVALID */
    { "", "", "", ""},		 /* EBOOK, INVALID */  
    { "", "", "", "" },		 /* HTML4WORD, INVALID */       
    { "", "", "", "" },		 /* XHTML4WORD, INVALID */      
  }
};

char *long_usage_string =  "Usage: %s [options] file.tex [params1] [params2] [params3]\n\
\toptions = --math=(bitmap|mathml)\n\
            --dtd=(html|xhtml|tei|docbook|ebook)\n\
            --mozilla\n\
            --unicode\n\
            --dbcs\n\
            --symbol\n\
            --msword\n\
            --output-dir=<dir>\n\
            --output-name=<name>\n\
            --force\n\
            --dry-run\n\
            --cleanup\n\
            --help\n\
\tparams1 = (html|xhtml|3.2|mathml.4ht|unicode.4ht|...)*\n\
\tparams2 = symbol,unicode,...\n\
\tparams3 = \" -p\" (space and -p)\n";

char *long_usage_stringht = "Usage: %s [options] texRunfile file.tex [params1]\n\
\toptions = --math=(bitmap|mathml)\n\
            --dtd=(html|xhtml|tei|docbook|ebook)\n\
            --mozilla\n\
            --unicode\n\
            --dbcs\n\
            --symbol\n\
            --msword\n\
            --output-dir=<dir>\n\
            --output-name=<name>\n\
            --force\n\
            --dry-run\n\
            --cleanup\n\
            --help\n\
\tparams1 = (html|xhtml|3.2|mathml.4ht|unicode.4ht|...)*\n";

char  *runstr_simple = "%s %s %s";
char  *runstr_complex;
char  runstr_buffer[4096];
char  tex4ht_opt[128];

boolean force = false;		/* Should we force LaTeX runs ? */
boolean dry_run = false;	/* Verbose trace */
boolean cleanup = false;	/* remove unused files */
dtd_type dtd = DTD_HTML;
math_type math = MATH_BITMAP;
char *texargs = NULL;
char *output_dir = NULL;
char *output_name = NULL;
char *texfile = NULL;
char *texsrc = NULL;
boolean newdest = false;
boolean mozilla = false;
boolean dbcs = false;
boolean symbol = false;
boolean msword = false;
boolean unicode = false;

#define TESTNZ(x) ((x != NULL) && (*(x) != 0))
#define TESTZ(x)  ((x == NULL) || (*(x) == 0))

#define OPT_FORCE 'f'
#define OPT_DRY_RUN 'r'
#define OPT_MOZILLA 'z'
#define OPT_MSWORD 'w'
#define OPT_UNICODE 'u'
#define OPT_DBCS 'd'
#define OPT_SYMBOL 's'
#define OPT_OUTPUT_DIR 'p'
#define OPT_OUTPUT_NAME 'o'
#define OPT_DTD 't'
#define OPT_MATH 'm'
#define OPT_CLEANUP 'c'
#define OPT_HELP '?'

const struct option long_options[] =
{
    "force",            no_argument,            0,      OPT_FORCE,
    "dry-run",		no_argument,		0,	OPT_DRY_RUN,
    "mozilla",          no_argument,            0,      OPT_MOZILLA,
    "msword",           no_argument,            0,      OPT_MSWORD,
    "unicode",          no_argument,            0,      OPT_UNICODE,
    "dbcs",             no_argument,            0,      OPT_DBCS,
    "symbol",           no_argument,            0,      OPT_SYMBOL,
    "output-dir",       required_argument,      0,      OPT_OUTPUT_DIR,
    "output-name",      required_argument,      0,      OPT_OUTPUT_NAME,
    "dtd",              required_argument,      0,      OPT_DTD,
    "math",             required_argument,      0,      OPT_MATH,
    "cleanup",          no_argument,            0,      OPT_CLEANUP,
    "help",		no_argument,		0,	OPT_HELP,
    0,			no_argument,		0,	0,
};

char cmd[4096];
char *progname = NULL;
char cwd[MAXPATHLEN];
char *ext_to_cleanup[] = { "log", "aux", "toc", "lof", "lot", "dvi", 
			   "idv", "otc", "4ct", "4tc", "lg", "tmp",
                           "xref" };

void popd();

void do_cleanup()
{
  int i;
  char *ext;

#if defined(_WIN32)
  WIN32_FIND_DATA find_file_data;
  HANDLE hnd;
  char *root = xstrdup(texfile);
  int rootlen = strlen(root);

  if (rootlen > 4 && _stricmp(root + rootlen - 4, ".tex") == 0)
    root[rootlen - 4] = '\0';
  rootlen = strlen(root);

  hnd = FindFirstFile("*", &find_file_data);

  while (hnd != INVALID_HANDLE_VALUE && 
	   FindNextFile(hnd, &find_file_data) != FALSE) { 
    int len = strlen(find_file_data.cFileName);

    if (_strnicmp(find_file_data.cFileName, root, rootlen) != 0)
      continue;
    
    if (newdest) {
      if (dry_run) {
	  fprintf(stderr, "%s: cleaning up %s\n", progname, find_file_data.cFileName);
      }
      else {
	if (unlink(find_file_data.cFileName) == -1) {
	  fprintf(stderr, "%s: can't remove %s\n", progname, find_file_data.cFileName);
	  perror(find_file_data.cFileName);
	}
      }
      continue;
    }

    for (i = 0, ext = ext_to_cleanup[0]; 
	 i < sizeof(ext_to_cleanup)/sizeof(ext_to_cleanup[0]); 
	 ext = ext_to_cleanup[++i]) {
      if (stricmp(find_file_data.cFileName + len - strlen(ext), ext) != 0)
	continue;
      if (dry_run) {
	fprintf(stderr, "%s: cleaning up %s\n", progname, find_file_data.cFileName);
      }
      else {
	if (unlink(find_file_data.cFileName) == -1) {
	  fprintf(stderr, "%s: can't remove %s\n", progname, find_file_data.cFileName);
	  perror(find_file_data.cFileName);
	}
      }
    }
  }
  FindClose(hnd);

#else /* ! WIN32 */
  /* FIXME : adapt to what win32 is doing. */
  DIR *dp;
  struct dirent *ep;

  if ((dp = opendir("."))) {
    while ((ep = readdir(dp))) {
      int len = strlen(ep->d_name);
      if ((strlen(ep->d_name) < 4) ||
	  (ep->d_name[len - 4] != '.'))
	continue;
      for (i = 0, ext = ext_to_cleanup[0]; 
	   i < sizeof(ext_to_cleanup)/sizeof(ext_to_cleanup[0]); 
	   ext = ext_to_cleanup[++i]) {
	if (strcmp(ep->d_name[len - 3], ext) != 0)
	  continue;
	if (dry_run) {
	  fprintf(stderr, "%s: cleaning up %s\n", progname, find_file_data.cFileName);
	}
	else {
	  if (unlink(find_file_data.cFileName) == -1) {
	    fprintf(stderr, "%s: can't remove %s\n", progname, find_file_data.cFileName);
	    perror(find_file_data.cFileName);
	  }
	}
      }
    }
    closedir(dp);
  }
  else
    perror(path);
#endif /* WIN32 */
}

void mt_exit(int code)
{
  if (cleanup) {
    do_cleanup();
  }
    
  if (newdest) {
    DeleteFile(texfile);
  }

  exit(code);
}

#ifdef _WIN32
BOOL sigint_handler(DWORD dwCtrlType)
{
  /* Fix me : there is a problem if a system() command is running.
     We should wait for the son process to be interrupted.
     Only way I can think of to do that : rewrite system() based on
     spawn() with parsing of the command line and set a global pid
     Next cwait(pid) in the HandlerRoutine. 
     */
  mt_exit(2);
  return FALSE;			/* return value mandatory */
}
#else
void sigint_handler (int sig)
{
  mt_exit(2);
}
#endif

#if 0
/* pushd */
void pushd(char *p)
{
  if (getcwd(cwd, MAXPATHLEN) == NULL) {
    fprintf(stderr, "%s: pushd error, can't getcwd() !\n", progname);
    perror(p);
    exit(1);
  }

  if (chdir(p) == -1) {
    fprintf(stderr, "%s: pushd error, can't chdir() !\n", progname);
    perror(p);
    exit(1);
  }
}

/* popd */
void popd()
{
  if (cwd == NULL || *cwd == '\0') {
    fprintf(stderr, "%s: popd warning, cwd empty !\n", progname);
    return;
  }

  if (chdir(cwd) == -1) {
    fprintf(stderr, "%s: popd warning, can't chdir() !\n", progname);
    perror(cwd);
    return;
  }

  return;
}
#endif

void usage()
{
  if(isHT == 0) {
    fprintf(stderr, long_usage_string, progname);
  }
  else {
    fprintf(stderr, long_usage_stringht, progname);
  }
    exit(1);
}

int run_string_as_cmd(char *x)
{
  char *p, *q;
  char name[64];
  int  retval = 0;

  if (dry_run)
    fprintf(stderr, "%s: running command:\n%s\n", progname, x);
  else {
    p = x;
    q = name;
    while(*p != ' ') *q++ = *p++;
    *q = '\0';
    while(*p == ' ') p++;
    retval = _spawnlp(_P_WAIT, name, name, p, NULL);
  }
  return retval;
}

int ht(int argc, char *argv[])
{
  int i;

  if (argc != 3 && argc != 4) {
    usage();
  }

  if (TESTZ(stdcfg[math][dtd][0])) {
    fprintf(stderr, "%s: error, invalid combination of options. Exiting...\n", progname);
    mt_exit(1);
  }

  sprintf(cmd, runstr_simple, argv[1], texargs, argv[2]);
  for (i = 0; i < 3; i++)
    run_string_as_cmd(cmd);

  sprintf(cmd, "tex4ht %s %s", stdcfg[math][dtd][1], texfile);
  run_string_as_cmd(cmd);

/*
  if (TESTNZ(argv[3])) {
    sprintf(cmd, "t4ht %s %s", argv[2], argv[3]);
  }
  else {
    sprintf(cmd, "t4ht %s", argv[2]);
  }
*/

  sprintf(cmd, "t4ht %s", argv[2]);
  if (TESTNZ(output_dir)) {
    strcat(cmd, " -d");
    strcat(cmd, output_dir);
  }

  run_string_as_cmd(cmd);
  
  return 0;
}

int ht_engine(char *runstr, int argc, char *argv[])
{
  int i;

  if (argc < 2 || argc > 5) {
    fprintf(stderr, long_usage_string, progname);
    return 1;
  }

  sprintf(cmd, runstr, texargs, argv[2], texfile);

  for (i = 0; i < 3; i++)
    run_string_as_cmd(cmd);

  if (TESTNZ(argv[3])) {
    xputenv("TEX4HTFONTSET", argv[3]);
  }
  else {
    xputenv("TEX4HTFONTSET", "alias,iso8859");
  }
  
  if (dry_run) {
    fprintf(stderr, "TEX4HTFONTSET = %s\n", getenv("TEX4HTFONTSET"));
  }

  if(tex4ht_opt[0] == '0') tex4ht_opt[0] = '\0';
  sprintf(cmd, "tex4ht %s %s %s", stdcfg[math][dtd][1], texfile, tex4ht_opt);
  run_string_as_cmd(cmd);

  if (TESTNZ(argv[4])) {
    sprintf(cmd, "t4ht %s %s", texfile, argv[4]);
  }
  else {
    sprintf(cmd, "t4ht %s", texfile);
  }

  if (TESTNZ(output_dir)) {
    strcat(cmd, " -d");
    strcat(cmd, output_dir);
  }

  run_string_as_cmd(cmd);
  
  return 0;
}

char **addto_argv(char *arg, int *argc, char **argv, int *max_args)
{
    if (*argc == *max_args) {
	max_args += 10;
	argv = (char**)(realloc(argv, *max_args * sizeof(argv[0])));
    }
    argv[ *argc++ ] = strdup(arg);
    return argv;
}

char **parse_arguments(int *argc, char *argv[])
{
  int option_index = 0;
  char c;
  int maxargs = 0;
  int i;

  char **newargv;

  optind = 0;
  while ((c = getopt_long_only(*argc, argv, "", long_options, &option_index)) != EOF) {
    switch (c) {
      
    case OPT_HELP:
      usage();
      break;
	    
    case OPT_DRY_RUN:
      dry_run = true;
      break;
	    
    case OPT_FORCE:
      force = true;
      break;
		  
    case OPT_DTD:
      if (_strcmpi(optarg, "html") == 0) {
	dtd = DTD_HTML;
      }
      else if (_strcmpi(optarg, "xhtml") == 0) {
	dtd = DTD_XHTML;
      }
      else if (_strcmpi(optarg, "tei") == 0) {
	dtd = DTD_TEI;
      }
      else if (_strcmpi(optarg, "docbook") == 0) {
	dtd = DTD_DOCBOOK;
      }
      else if (_strcmpi(optarg, "ebook") == 0) {
	dtd = DTD_EBOOK;
      }
      else {
	fprintf(stderr, "%s: invalid --dtd=%s option.\n", progname, optarg);
	exit(1);
      }
      break;
      
    case OPT_MATH:
      if (_strcmpi(optarg, "bitmap") == 0) {
	math = MATH_BITMAP;
      }
      else if (_strcmpi(optarg, "mathml") == 0) {
	math = MATH_MATHML;
      }
      else {
	fprintf(stderr, "%s: invalid --math=%s option.\n", progname, optarg);
	exit(1);
      }
      break;

    case OPT_MSWORD:
      msword = true;
      break;

    case OPT_MOZILLA:
      mozilla = true;
      break;

    case OPT_UNICODE:
      unicode = true;
      break;

    case OPT_DBCS:
      dbcs = true;
      break;

    case OPT_SYMBOL:
      symbol = true;
      break;

    case OPT_OUTPUT_DIR:
      output_dir = xstrdup(optarg);
      if (!dir_p(optarg)) {
	fprintf(stderr, "%s: can't output to directory %s\n", progname, optarg);
	exit(1);
      }
      output_dir = xstrdup(optarg);
      break;

    case OPT_OUTPUT_NAME:
      output_name = xstrdup(optarg);
      break;

    case OPT_CLEANUP:
      cleanup = true;
      break;

    default:
      usage();
      break;
    }
  }

  if (dtd == DTD_TEI || dtd == DTD_EBOOK || dtd == DTD_DOCBOOK) {
    if (math == MATH_BITMAP && ! unicode) {
      fprintf(stderr, "%s: warning, TEI, eBook and DocBook dtds valid only with `--unicode' option.\nTurning on option `--unicode'.\n", progname);
      unicode = true;
      math = MATH_BITMAP_UNICODE;
      }
    else if (mozilla) {
      fprintf(stderr, "%s: warning, `--mozilla' valid only with XHTML dtd. Ignored.\n", progname);
      mozilla = false;
    }
  }

  if (dtd = DTD_HTML) {
    if (math == MATH_MATHML) {
      fprintf(stderr, "%s: warning, MathML incompatible with HTML dtd, reverting to bitmap math.\n", progname);
      math = MATH_BITMAP;
    }
    if (mozilla) {
      fprintf(stderr, "%s: warning, `--mozilla' valid only with XHTML dtd. Ignored.\n", progname);
      mozilla = false;
    }
  }

  if (msword) {
    if (dtd == DTD_HTML) {
      dtd = DTD_HTML4WORD;
    }
    else if (dtd == DTD_XHTML) {
      dtd = DTD_XHTML4WORD;
    }
    else {
      fprintf(stderr, "%s: error, `--msword' incompatible with dtds other than HTML or XHTML. Exiting...\n", progname);
      mt_exit(1);
    }
    if (unicode) {
      fprintf(stderr, "%s: warning, `--unicode' incompatible with `--msword'. Exiting...\n", progname);
      mt_exit(1);
    }
    if (math == MATH_MATHML) {
      fprintf(stderr, "%s: warning, MathML incompatible with `--msword'. Exiting...\n", progname);
      mt_exit(1);
    }
    if (mozilla) {
      fprintf(stderr, "%s: warning, `--mozilla' incompatible with `--msword'. Exiting...\n", progname);
      mt_exit(1);
    }
  }

  if (math == MATH_BITMAP && unicode)
    math = MATH_BITMAP_UNICODE;

  newargv = (char **)malloc(5 * sizeof(char *));

  newargv[0] = argv[0];
  /* shifting options from argv[] list */
  for (i = 1; optind < *argc; i++, optind++)
    newargv[i] = argv[optind];
  newargv[i] = NULL;
  *argc = i;
  for (i = *argc; i < 5; i++)
    newargv[i] = NULL;

  if(isHT == 0) {
    if (TESTNZ(newargv[2]))
      newargv[2] = concat3(newargv[2], ",", stdcfg[math][dtd][0]);
    else
      newargv[2] = stdcfg[math][dtd][0];
    if (TESTNZ(newargv[3]))
      newargv[3] = concat3(newargv[3], ",", stdcfg[math][dtd][2]);
    else
      newargv[3] = stdcfg[math][dtd][2];
  }
  else {
    if (TESTNZ(newargv[3]))
      newargv[3] = concat3(newargv[3], ",", stdcfg[math][dtd][0]);
    else
      newargv[3] = stdcfg[math][dtd][0];
    if (TESTNZ(newargv[4]))
      newargv[4] = concat3(newargv[4], ",", stdcfg[math][dtd][2]);
    else
      newargv[4] = stdcfg[math][dtd][4];
  }
  if(isHT == 0) {
    if (dbcs)
      newargv[3] = concat("dbcs,", newargv[3]);
    if (symbol)
      newargv[3] = concat("symbol,", newargv[3]);
  }
  else {
    if (dbcs)
      newargv[4] = concat("dbcs,", newargv[4]);
    if (symbol)
      newargv[4] = concat("symbol,", newargv[4]);
  }

  if (*argc < 4)
    *argc = 4;

  return newargv;
}

int main(int argc, char *argv[])
{
  int i;
  int retval = 0;
  FILE *prologue;
  char prologuename[64];

  if(!stricmp(argv[0], "ht")) isHT = 1;
  kpse_set_program_name (argv[0], "tex4ht");
  progname = xstrdup(kpse_program_name);
  strcpy(prologuename, progname);
  strcat(prologuename, ".txt");

#ifdef _WIN32
  SetConsoleCtrlHandler((PHANDLER_ROUTINE)sigint_handler, TRUE);
#else
# ifdef SIGHUP
  signal (SIGHUP, sigint_handler);
# endif
# ifdef SIGINT
  signal (SIGINT, sigint_handler);
# endif
# ifdef SIGQUIT
  signal (SIGQUIT, sigint_handler);
# endif
# ifdef SIGEMT
  signal (SIGEMT, sigint_handler);
# endif
# ifdef SIGPIPE
  signal (SIGPIPE, sigint_handler);
# endif
# ifdef SIGTERM
  signal (SIGTERM, sigint_handler);
# endif
#endif

  if (strlen(progname) > 4 && FILESTRNCASEEQ(progname + strlen(progname) - 4, ".exe", 4)) {
    *(progname + strlen(progname) - 4) = '\0';
  }

  argv = parse_arguments(&argc, argv);

  texargs = (force ? "--interaction=nonstopmode" : "");

  if(isHT == 0) {
    if (TESTZ(argv[1])) {
      fprintf(stderr, "%s: error, no file specified.\n", progname);
      exit(1);
    }
  }
  else {
    if (TESTZ(argv[1])) {
      fprintf(stderr, "%s: error, no TeX-run file specified.\n", progname);
      exit(1);
    }
  }

  if(isHT == 0) {
    texsrc = xstrdup(argv[1]);
  }
  else {
    texsrc = xstrdup(argv[2]);
  }

  if (TESTNZ(output_name)) {

    if ((strlen(output_name) < 4) 
	|| _strnicmp(output_name + strlen(output_name) - 4, ".tex", 4) != 0) {
      output_name = concat(output_name, ".tex");
    }

    if (_stricmp(texsrc, output_name) != 0) {    
      newdest = true;		/* Will remove file on mt_exit */
      if (dry_run) {
	fprintf(stderr, "%s: copying %s to %s\n", progname, texsrc, output_name);
      }
      else {
	if (CopyFile(texsrc, output_name, false) == 0) {
	  fprintf(stderr, "%s: failed to copy %s to %s (Error %d)\n",
		  progname, texsrc, output_name, GetLastError());
	  mt_exit(1);
	}
      }
    }
    texfile = output_name;
  }
  else {
    texfile = texsrc;
  }

  if (FILESTRCASEEQ(progname, "ht")) {
    retval = ht(argc, argv);
  }
  else {
    prologue = kpse_open_file(prologuename, kpse_program_text_format);
    if(!prologue) {
      fprintf(stderr, "%s: %s is unknown, aborting.\n", argv[0], progname);
      retval = 1;
    } else {
      (void)fgets(runstr_buffer, 4096, prologue);
      (void)fgets(tex4ht_opt, 128, prologue);
      fclose(prologue);
      for(i=0; i < 128; i++) {
	if(tex4ht_opt[i] == '\r' || tex4ht_opt[i] == '\n')
	  tex4ht_opt[i] = '\0';
      }
      for(i=0; i < 4096; i++) {
	if(runstr_buffer[i] == '\n' || runstr_buffer[i] == '\r') {
	  runstr_buffer[i] = '\0';
	  break;
	}
	if(runstr_buffer[i] == '\0') break;
      }
      runstr_complex = runstr_buffer;
      retval = ht_engine(runstr_complex, argc, argv);
    }
  }

  mt_exit(retval);

  /* Not Reached */
  return retval;
}
