/*
 * Copyright (c) 2000-2008 Stephen Williams (steve@icarus.com)
 *
 *    This source code is free software; you can redistribute it
 *    and/or modify it in source code form under the terms of the GNU
 *    General Public License as published by the Free Software
 *    Foundation; either version 2 of the License, or (at your option)
 *    any later version.
 *
 *    This program 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 General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

# include "config.h"
# include "version.h"

const char NOTICE[] =
"  This program is free software; you can redistribute it and/or modify\n"
"  it under the terms of the GNU General Public License as published by\n"
"  the Free Software Foundation; either version 2 of the License, or\n"
"  (at your option) any later version.\n"
"\n"
"  This program is distributed in the hope that it will be useful,\n"
"  but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
"  GNU General Public License for more details.\n"
"\n"
"  You should have received a copy of the GNU General Public License\n"
"  along with this program; if not, write to the Free Software\n"
"  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA\n"
;

const char HELP[] =
"Usage: iverilog [-ESvV] [-B base] [-c cmdfile|-f cmdfile] [-g1|-g2|-g2x]\n"
"                [-D macro[=defn]] [-I includedir] [-M depfile] [-m module]\n"
"                [-N file] [-o filename] [-p flag=value]\n"
"                [-s topmodule] [-t target] [-T min|typ|max]\n"
"                [-W class] [-y dir] [-Y suf] source_file(s)\n"
"\n"
"See the man page for details.";

#define MAXSIZE 4096

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <sys/types.h>
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif

#ifdef __MINGW32__
#include <windows.h>
#ifdef HAVE_LIBIBERTY_H
#include <libiberty.h>
#endif
#endif

#if HAVE_GETOPT_H
#include <getopt.h>
#endif

#if defined(__MINGW32__) && !defined(HAVE_GETOPT_H)
extern int getopt(int argc, char*argv[], const char*fmt);
extern int optind;
extern const char*optarg;
#endif

#if !defined(WIFEXITED)
# define WIFEXITED(rc) ((rc&0x7f) == 0)
#endif

#if !defined(WEXITSTATUS)
# define WEXITSTATUS(rc) (rc>>8)
#endif

#ifndef IVL_ROOT
# define IVL_ROOT "."
#endif

# include  "globals.h"
#include "cfparse_misc.h"   /* cfparse() */

#ifdef __MINGW32__
const char sep = '\\';
#else
const char sep = '/';
#endif

extern void cfreset(FILE*fd, const char*path);

const char*base = 0;
const char*pbase = 0;
const char*mtm  = 0;
const char*opath = "a.out";
const char*npath = 0;
const char*targ  = "vvp";
const char*depfile = 0;

const char*generation = "2x";
const char*gen_specify = "specify";
const char*gen_xtypes = "xtypes";
const char*gen_icarus = "icarus-misc";
const char*gen_io_range_error = "io-range-error";
const char*gen_verilog_ams = "no-verilog-ams";

/* Boolean: true means use a default include dir, false means don't */
int gen_std_include = 1;

char warning_flags[16] = "";

unsigned integer_width = 32;

char*mod_list = 0;
char*command_filename = 0;

/* These are used to collect the list of file names that will be
   passed to ivlpp. Keep the list in a file because it can be a long
   list. */
char*source_path = 0;
FILE*source_file = 0;
unsigned source_count = 0;

char*defines_path = 0;
FILE*defines_file = 0;

char*iconfig_path = 0;
FILE*iconfig_file = 0;

char*compiled_defines_path = 0;

static char iconfig_common_path_buf[4096] = "";
char*iconfig_common_path = iconfig_common_path_buf;

int synth_flag = 0;
int verbose_flag = 0;

FILE *fp;

char line[MAXSIZE];
char tmp[MAXSIZE];

static char ivl_root[MAXSIZE];

/* Structure to keep a FIFO list of the command files */
typedef struct t_command_file {
      char *filename;
      struct t_command_file *next;
} s_command_file, *p_command_file;
p_command_file cmd_file_head = NULL;  /* The FIFO head */
p_command_file cmd_file_tail = NULL;  /* The FIFO tail */

/* Function to add a command file name to the FIFO. */
void add_cmd_file(const char* filename)
{
      p_command_file new;

      new = (p_command_file) malloc(sizeof(s_command_file));
      new->filename = strdup(filename);
      new->next = NULL;
      if (cmd_file_head == NULL) {
            cmd_file_head = new;
            cmd_file_tail = new;
      } else {
            cmd_file_tail->next = new;
            cmd_file_tail = new;
      }
}

/* Function to return the top command file name from the FIFO. */
char *get_cmd_file()
{
      char *filename;

      if (cmd_file_head == NULL) filename = NULL;
      else {
            p_command_file head;

            filename = cmd_file_head->filename;
            head = cmd_file_head;
            cmd_file_head = cmd_file_head->next;
            free(head);
      }
      return filename;
}

#ifdef __MINGW32__
# include  <io.h>
# include  <fcntl.h>
static FILE*fopen_safe(const char*path)
{
      FILE*file = 0;
      int fd;

      fd = _open(path, _O_WRONLY|_O_CREAT|_O_EXCL, 0700);
      if (fd != -1)
	    file = _fdopen(fd, "w");

      return file;
}
#else
# include  <fcntl.h>
static FILE*fopen_safe(const char*path)
{
      FILE*file = 0;
      int fd;

      fd = open(path, O_WRONLY|O_CREAT|O_EXCL, 0700);
      if (fd != -1)
	    file = fdopen(fd, "w");

      return file;
}
#endif

static const char*my_tempfile(const char*str, FILE**fout)
{
      FILE*file;
      int retry;

      static char pathbuf[8192];

      const char*tmpdir = getenv("TMP");
      if (tmpdir == 0)
	    tmpdir = getenv("TMPDIR");
      if (tmpdir == 0)
	    tmpdir = getenv("TEMP");
#ifdef __MINGW32__
      if (tmpdir == 0)
	    tmpdir = "C:\\TEMP";
#else
      if (tmpdir == 0)
	    tmpdir = "/tmp";
#endif

      assert(tmpdir);
      assert((strlen(tmpdir) + strlen(str)) < sizeof pathbuf - 10);

      srand(getpid());
      retry = 100;
      file = NULL;
      while ((retry > 0) && (file == NULL)) {
	    unsigned code = rand();
	    sprintf(pathbuf, "%s%c%s%04x", tmpdir, sep, str, code);
	    file = fopen_safe(pathbuf);
	    retry -= 1;
      }

      *fout = file;
      return pathbuf;
}

/*
 * This is the default target type. It looks up the bits that are
 * needed to run the command from the configuration file (which is
 * already parsed for us) so we can handle must of the generic cases.
 */
static int t_default(char*cmd, unsigned ncmd)
{
      unsigned rc;
#ifdef __MINGW32__
      unsigned ncmd_start = ncmd;
#endif

      snprintf(tmp, sizeof tmp, " | %s/ivl", base);
      rc = strlen(tmp);
      cmd = realloc(cmd, ncmd+rc+1);
      strcpy(cmd+ncmd, tmp);
      ncmd += rc;

      if (verbose_flag) {
	    const char*vv = " -v";
	    rc = strlen(vv);
	    cmd = realloc(cmd, ncmd+rc+1);
	    strcpy(cmd+ncmd, vv);
	    ncmd += rc;
      }

      if (npath != 0) {
	    snprintf(tmp, sizeof tmp, " -N%s", npath);
	    rc = strlen(tmp);
	    cmd = realloc(cmd, ncmd+rc+1);
	    strcpy(cmd+ncmd, tmp);
	    ncmd += rc;
      }

      snprintf(tmp, sizeof tmp, " -C%s", iconfig_path);
      rc = strlen(tmp);
      cmd = realloc(cmd, ncmd+rc+1);
      strcpy(cmd+ncmd, tmp);
      ncmd += rc;

      snprintf(tmp, sizeof tmp, " -C%s -- -", iconfig_common_path);
      rc = strlen(tmp);
      cmd = realloc(cmd, ncmd+rc+1);
      strcpy(cmd+ncmd, tmp);
      ncmd += rc;

#ifdef __MINGW32__
      {
	char *t;
	for (t = cmd+ncmd_start; *t; t++)
	  {
	    if (*t == '/') *t = '\\';
	  }
      }
#endif


      if (verbose_flag)
	    printf("translate: %s\n", cmd);


      rc = system(cmd);
      if ( ! getenv("IVERILOG_ICONFIG")) {
	    remove(source_path);
	    remove(iconfig_path);
	    remove(defines_path);
	    remove(compiled_defines_path);
      }
#ifdef __MINGW32__  /* MinGW just returns the exit status, so return it! */
      free(cmd);
      return rc;
#else
      int rtn = 0;
      if (rc != 0) {
	    if (rc == 127) {
		  fprintf(stderr, "Failed to execute: %s\n", cmd);
		  rtn = 1;
	    } else if (WIFEXITED(rc)) {
		  rtn = WEXITSTATUS(rc);
	    } else {
		  fprintf(stderr, "Command signaled: %s\n", cmd);
		  rtn = -1;
	    } 
      }

      free(cmd);
      return rtn;
#endif
}


static void process_warning_switch(const char*name)
{
      if (strcmp(name,"all") == 0) {
	    process_warning_switch("implicit");
	    process_warning_switch("portbind");
	    process_warning_switch("timescale");
      } else if (strcmp(name,"implicit") == 0) {
	    if (! strchr(warning_flags, 'i'))
		  strcat(warning_flags, "i");
      } else if (strcmp(name,"portbind") == 0) {
	    if (! strchr(warning_flags, 'p'))
		  strcat(warning_flags, "p");
      } else if (strcmp(name,"timescale") == 0) {
	    if (! strchr(warning_flags, 't'))
		  strcat(warning_flags, "t");
	/* Since the infinite loop check is not part of 'all' it
	 * does not have a no- version. */
      } else if (strcmp(name,"infloop") == 0) {
	    if (! strchr(warning_flags, 'l'))
		  strcat(warning_flags, "l");
      } else if (strcmp(name,"no-implicit") == 0) {
	    char*cp = strchr(warning_flags, 'i');
	    if (cp) while (*cp) {
		  cp[0] = cp[1];
		  cp += 1;
	    }
      } else if (strcmp(name,"no-portbind") == 0) {
	    char*cp = strchr(warning_flags, 'p');
	    if (cp) while (*cp) {
		  cp[0] = cp[1];
		  cp += 1;
	    }
      } else if (strcmp(name,"no-timescale") == 0) {
	    char*cp = strchr(warning_flags, 't');
	    if (cp) while (*cp) {
		  cp[0] = cp[1];
		  cp += 1;
	    }
      }
}

void process_library_switch(const char *name)
{
      fprintf(iconfig_file, "-y:%s\n", name);
}

void process_library_nocase_switch(const char *name)
{
      fprintf(iconfig_file, "-yl:%s\n", name);
}

void process_library2_switch(const char *name)
{
      fprintf(iconfig_file, "-Y:%s\n", name);
}

void process_include_dir(const char *name)
{
      fprintf(defines_file, "I:%s\n", name);
}

void process_define(const char*name)
{
      fprintf(defines_file,"D:%s\n", name);
}

/*
 * This function is called while processing a file name in a command
 * file, or a file name on the command line. Look to see if there is a
 * .sft suffix, and if so pass that as a sys_func file. Otherwise, it
 * is a Verilog source file to be written into the file list.
 */
void process_file_name(const char*name, int lib_flag)
{
      if (strlen(name) > 4 && strcasecmp(".sft", name+strlen(name)-4) == 0) {
	    fprintf(iconfig_file,"sys_func:%s\n", name);

      } else {
	    fprintf(source_file, "%s\n", name);
	    source_count += 1;
	    if (lib_flag)
		  fprintf(iconfig_file,"library_file:%s\n", name);
      }
}

int process_generation(const char*name)
{
      if (strcmp(name,"1995") == 0)
	    generation = "1995";

      else if (strcmp(name,"2001") == 0)
	    generation = "2001";

      else if (strcmp(name,"2005") == 0)
	    generation = "2005";

      else if (strcmp(name,"1") == 0) { /* Deprecated: use 1995 */
	    generation = "1995";
	    gen_xtypes = "no-xtypes";
	    gen_icarus = "no-icarus-misc";

      } else if (strcmp(name,"2") == 0) { /* Deprecated: use 2001 */
	    generation = "2001";
	    gen_xtypes = "no-xtypes";
	    gen_icarus = "no-icarus-misc";

      } else if (strcmp(name,"2x") == 0) { /* Deprecated: use 2005/xtypes */
	    generation = "2005";
	    gen_xtypes = "xtypes";
	    gen_icarus = "icarus-misc";

      } else if (strcmp(name,"xtypes") == 0)
	    gen_xtypes = "xtypes";

      else if (strcmp(name,"no-xtypes") == 0)
	    gen_xtypes = "no-xtypes";

      else if (strcmp(name,"icarus-misc") == 0)
	    gen_icarus = "icarus-misc";

      else if (strcmp(name,"no-icarus-misc") == 0)
	    gen_icarus = "no-icarus-misc";

      else if (strcmp(name,"specify") == 0)
	    gen_specify = "specify";

      else if (strcmp(name,"no-specify") == 0)
	    gen_specify = "no-specify";

      else if (strcmp(name,"std-include") == 0)
	     gen_std_include = 1;

      else if (strcmp(name,"no-std-include") == 0)
	     gen_std_include = 0;

      else if (strcmp(name,"io-range-error") == 0)
	    gen_io_range_error = "io-range-error";

      else if (strcmp(name,"no-io-range-error") == 0)
	    gen_io_range_error = "no-io-range-error";

      else if (strcmp(name,"verilog-ams") == 0)
	    gen_verilog_ams = "verilog-ams";

      else if (strcmp(name,"no-verilog-ams") == 0)
	    gen_verilog_ams = "no-verilog-ams";

      else {
	    fprintf(stderr, "Unknown/Unsupported Language generation "
		    "%s\n\n", name);
	    fprintf(stderr, "Supported generations are:\n");
	    fprintf(stderr, "    1995 -- IEEE1364-1995\n"
		            "    2001 -- IEEE1364-2001\n"
		            "    2005 -- IEEE1364-2005\n"
		            "Other generation flags:\n"
		            "    specify | no-specify\n"
		            "    verilog-ams | no-verilog-ams\n"
		            "    std-include | no-std-include\n"
		            "    xtypes | no-xtypes\n"
		            "    icarus-misc | no-icarus-misc\n"
		            "    io-range-error | no-io-range-error\n");
	    return 1;
      }

      return 0;
}

/*
 * If it exists add the SFT file for the given module.
 */
void add_sft_file(const char *module)
{
      char *file;
   
      file = (char *) malloc(strlen(base)+1+strlen(module)+4+1);
      sprintf(file, "%s%c%s.sft", base, sep, module);
      if (access(file, R_OK) == 0)
            fprintf(iconfig_file, "sys_func:%s\n", file);
      free(file);
}

int main(int argc, char **argv)
{
      char*cmd;
      unsigned ncmd;
      int e_flag = 0;
      int version_flag = 0;
      int opt, idx, rc;

#ifdef __MINGW32__
      { char * s;
	char basepath[1024];
	GetModuleFileName(NULL,basepath,1024);

	  /* Calculate the ivl_root from the path to the command. This
	     is necessary because of the installation process in
	     Windows. Mostly, it is those darn drive letters, but oh
	     well. We know the command path is formed like this:

	         D:\iverilog\bin\iverilog.exe

	     The IVL_ROOT in a Windows installation is the path:

	         D:\iverilog\lib\ivl

	     so we chop the file name and the last directory by
	     turning the last two \ characters to null. Then we append
	     the lib\ivl to finish. */

        strncpy(ivl_root, basepath, MAXSIZE);
	s = strrchr(ivl_root, sep);
	if (s) *s = 0;
	s = strrchr(ivl_root, sep);
	if (s) *s = 0;
	strcat(ivl_root, "\\lib\\ivl");

	base = ivl_root;
      }

#else
        /* In a UNIX environment, the IVL_ROOT from the Makefile is
	   dependable. It points to the $prefix/lib/ivl directory,
	   where the sub-parts are installed. */
      strcpy(ivl_root, IVL_ROOT);
      base = ivl_root;
#endif

	/* Create a temporary file for communicating input parameters
	   to the preprocessor. */
      source_path = strdup(my_tempfile("ivrlg", &source_file));
      if (NULL == source_file) {
	    fprintf(stderr, "%s: Error opening temporary file %s\n",
		    argv[0], source_path);
	    fprintf(stderr, "%s: Please check TMP or TMPDIR.\n", argv[0]);
	    return 1;
      }

      defines_path = strdup(my_tempfile("ivrlg2", &defines_file));
      if (NULL == defines_file) {
	    fprintf(stderr, "%s: Error opening temporary file %s\n",
		    argv[0], defines_path);
	    fprintf(stderr, "%s: Please check TMP or TMPDIR.\n", argv[0]);

	    fclose(source_file);
	    remove(source_path);
	    return 1;
      }

      fprintf(defines_file, "D:__ICARUS__=1\n");
      if (strcmp(gen_verilog_ams,"verilog-ams") == 0)
	    fprintf(defines_file, "D:__VAMS_ENABLE__=1\n");

	/* Create another temporary file for passing configuration
	   information to ivl. */

      if ( (iconfig_path = getenv("IVERILOG_ICONFIG")) ) {
	    fprintf(stderr, "%s: IVERILOG_ICONFIG=%s\n",
		    argv[0], iconfig_path);

	    iconfig_file = fopen(iconfig_path, "w");

      } else {

	    iconfig_path = strdup(my_tempfile("ivrlh", &iconfig_file));
      }

      if (NULL == iconfig_file) {
	    fprintf(stderr, "%s: Error opening temporary file %s\n",
		    argv[0], iconfig_path);
	    fprintf(stderr, "%s: Please check TMP or TMPDIR.\n", argv[0]);
	    fclose(source_file);
	    remove(source_path);

	    fclose(defines_file);
	    remove(defines_path);
	    return 1;
      }

	/* Create a temporary file (I only really need the path) that
	   can carry preprocessor precompiled defines to the library. */
      { FILE*tmp_file = 0;
	compiled_defines_path = strdup(my_tempfile("ivrli", &tmp_file));
	if (tmp_file) {
	      fclose(tmp_file);
	}
      }

      while ((opt = getopt(argc, argv, "B:c:D:d:Ef:g:hI:M:m:N::o:p:Ss:T:t:vVW:y:Y:")) != EOF) {

	    switch (opt) {
		case 'B':
		    /* Undocumented feature: The preprocessor itself
		       may be located at a different location. If the
		       base starts with a 'P', set this special base
		       instead of the main base. */
		  if (optarg[0] == 'P') {
			pbase = optarg+1;
		  } else {
			base=optarg;
		  }
		  break;
		case 'c':
		case 'f':
		  add_cmd_file(optarg);
 		  break;
		case 'D':
		  process_define(optarg);
		  break;
		case 'E':
		  e_flag = 1;
		  break;
		case 'p':
		  fprintf(iconfig_file, "flag:%s\n", optarg);
		  break;
		case 'd':
		  fprintf(iconfig_file, "debug:%s\n", optarg);
		  break;

		case 'g':
		  rc = process_generation(optarg);
		  if (rc != 0)
			return -1;
		  break;
 		case 'h':
 		  fprintf(stderr, "%s\n", HELP);
 		  return 1;

		case 'I':
		  process_include_dir(optarg);
		  break;

		case 'M':
		  depfile = optarg;
		  break;

		case 'm':
		  fprintf(iconfig_file, "module:%s\n", optarg);
		  add_sft_file(optarg);
		  break;

		case 'N':
		  npath = optarg;
		  break;

		case 'o':
		  opath = optarg;
		  break;

		case 'S':
		  synth_flag = 1;
		  break;
		case 's':
		  fprintf(iconfig_file, "root:%s\n", optarg);
		  break;
		case 'T':
		  if (strcmp(optarg,"min") == 0) {
			mtm = "min";
		  } else if (strcmp(optarg,"typ") == 0) {
			mtm = "typ";
		  } else if (strcmp(optarg,"max") == 0) {
			mtm = "max";
		  } else {
			fprintf(stderr, "%s: invalid -T%s argument\n",
				argv[0], optarg);
			return 1;
		  }
		  break;
		case 't':
		  targ = optarg;
		  break;
		case 'v':
		  verbose_flag = 1;
		  break;
		case 'V':
		  version_flag = 1;
		  break;
		case 'W':
		  process_warning_switch(optarg);
		  break;
		case 'y':
		  process_library_switch(optarg);
		  break;
		case 'Y':
		  process_library2_switch(optarg);
		  break;
		case '?':
		default:
		  return 1;
	    }
      }

      if (pbase == 0)
	    pbase = base;

      if (version_flag || verbose_flag) {
	    printf("Icarus Verilog version " VERSION " (" VERSION_TAG ")\n\n");
	    printf("Copyright 1998-2008 Stephen Williams\n");
	    puts(NOTICE);

	    if (version_flag)
		  return 0;
      }

	/* Make a common conf file path to reflect the target. */
      sprintf(iconfig_common_path, "%s%c%s%s.conf",
	      base,sep, targ, synth_flag? "-s" : "");

	/* Write values to the iconfig file. */
      fprintf(iconfig_file, "basedir:%s\n", base);

	/* Tell the core where to find the system.sft. This file
	   describes the system functions so that elaboration knows
	   how to handle them. */
      fprintf(iconfig_file, "sys_func:%s%csystem.sft\n", base, sep);

	/* If verilog-ams is enabled, then include the va_math module
	   as well. */
      if (strcmp(gen_verilog_ams,"verilog-ams") == 0) {
	    fprintf(iconfig_file, "sys_func:%s%cva_math.sft\n", base, sep);
	    fprintf(iconfig_file, "module:va_math\n");
      }

      if (mtm != 0) fprintf(iconfig_file, "-T:%s\n", mtm);
      fprintf(iconfig_file, "generation:%s\n", generation);
      fprintf(iconfig_file, "generation:%s\n", gen_specify);
      fprintf(iconfig_file, "generation:%s\n", gen_xtypes);
      fprintf(iconfig_file, "generation:%s\n", gen_io_range_error);
      fprintf(iconfig_file, "generation:%s\n", gen_verilog_ams);
      fprintf(iconfig_file, "generation:%s\n", gen_icarus);
      fprintf(iconfig_file, "warnings:%s\n", warning_flags);
      fprintf(iconfig_file, "out:%s\n", opath);
      if (depfile) fprintf(iconfig_file, "depfile:%s\n", depfile);

      while ( (command_filename = get_cmd_file()) ) {
	    int rc;

	    if (( fp = fopen(command_filename, "r")) == NULL ) {
		  fprintf(stderr, "%s: cannot open command file %s "
			  "for reading.\n", argv[0], command_filename);
		  return 1;
	    }

	    cfreset(fp, command_filename);
	    rc = cfparse();
	    if (rc != 0) {
		  fprintf(stderr, "%s: parsing failed in base command "
		          "file %s.\n", argv[0], command_filename);
		  return 1;
	    }
            free(command_filename);
      }

      if (depfile) {
	    fprintf(defines_file, "M:%s\n", depfile);
      }

	/* Finally, process all the remaining words on the command
	   line as file names. */
      for (idx = optind ;  idx < argc ;  idx += 1)
	    process_file_name(argv[idx], 0);

	/* If the use of a default include directory is not
	   specifically disabled, then write that directory as the
	   very last include directory to use... always. */
      if (gen_std_include) {
	    fprintf(defines_file, "I:%s%cinclude", base, sep);
      }

      fclose(source_file);
      source_file = 0;

      fclose(defines_file);
      defines_file = 0;

      if (source_count == 0) {
	    fprintf(stderr, "%s: no source files.\n\n%s\n", argv[0], HELP);
	    return 1;
      }


	/* Start building the preprocess command line. */

      sprintf(tmp, "%s%civlpp %s%s -F%s -f%s -p%s ", pbase,sep,
	      verbose_flag?" -v":"",
	      e_flag?"":" -L", defines_path, source_path,
	      compiled_defines_path);

      ncmd = strlen(tmp);
      cmd = malloc(ncmd + 1);
      strcpy(cmd, tmp);

	/* If the -E flag was given on the command line, then all we
	   do is run the preprocessor and put the output where the
	   user wants it. */
      if (e_flag) {
	    int rc;
	    if (strcmp(opath,"-") != 0) {
		  sprintf(tmp, " > %s", opath);
		  cmd = realloc(cmd, ncmd+strlen(tmp)+1);
		  strcpy(cmd+ncmd, tmp);
		  ncmd += strlen(tmp);
	    }

	    if (verbose_flag)
		  printf("preprocess: %s\n", cmd);

	    rc = system(cmd);
	    remove(source_path);
	    fclose(iconfig_file);
	    if ( ! getenv("IVERILOG_ICONFIG")) {
		  remove(iconfig_path);
		  remove(defines_path);
		  remove(compiled_defines_path);
	    }

	    if (rc != 0) {
		  if (WIFEXITED(rc)) {
			fprintf(stderr, "errors preprocessing Verilog program.\n");
			return WEXITSTATUS(rc);
		  }

		  fprintf(stderr, "Command signaled: %s\n", cmd);
		  free(cmd);
		  return -1;
	    }

	    return 0;
      }

      fprintf(iconfig_file, "iwidth:%u\n", integer_width);

	/* Write the preprocessor command needed to preprocess a
	   single file. This may be used to preprocess library
	   files. */
      fprintf(iconfig_file, "ivlpp:%s%civlpp -L -F%s -P%s\n",
	      pbase, sep, defines_path, compiled_defines_path);

	/* Done writing to the iconfig file. Close it now. */
      fclose(iconfig_file);

      return t_default(cmd, ncmd);
}
