/****************************************************************************

NAME
   perpet.c -- main routine for C-INTERCAL compiler.

DESCRIPTION
   This is where all the dirty work begins and ends.

****************************************************************************/
// SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
// SPDX-License-Identifier: GPL-2.0-or-later
/*LINTLIBRARY */
#include "config.h" /* AIS: Generated by autoconf */
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <string.h>
#include <signal.h>
#include <time.h>
#include <assert.h>
#include "ick.h"
#include "feh.h"
#include "parser.h"
#include "sizes.h"
#include "ick_lose.h"
#include "uncommon.h"

/* AIS: split ICKDATADIR from ICKLIBDIR */
#ifndef ICKINCLUDEDIR
# define ICKINCLUDEDIR "/usr/local/include"
#endif
#ifndef ICKSYSDIR
# ifdef ICKDATADIR
#  define ICKSYSDIR ICKDATADIR
# else
#  define ICKSYSDIR "/usr/local/share"
# endif
#endif
#ifndef ICKCSKELDIR
# ifdef ICKDATADIR
#  define ICKCSKELDIR ICKDATADIR
# else
#  define ICKCSKELDIR "/usr/local/share"
# endif
#endif
#ifndef ICKLIBDIR
# define ICKLIBDIR "/usr/local/lib"
#endif
#ifndef ICKBINDIR
# define ICKBINDIR "/usr/local/bin"
#endif
#ifndef CC
# define CC "gcc"
#endif

#define ARGSTRING "abcdefghlmoptuvwxyCEFHOPUYX@"

/* unused st variable quiets a GCC4 warning */
#define ICK_SYSTEM(x) do{int st;if(showsystem)fprintf(stderr,"%s\n",x); \
    st=system(x);}while(0)

#ifdef USE_YYRESTART
/* function supplied by lex */
extern void yyrestart(FILE*);

#endif /* USE_YYRESTART */

/* function created by yacc */
extern int yyparse(void);

//int yydebug;

/* compilation options */
bool compile_only; 	/* just compile into C, don't run the linker */
bool ick_traditional;	/* insist on strict INTERCAL-72 conformance */
bool nocompilerbug;	/* disable error IE774 */
bool yukdebug;          /* AIS: Use the yuk debugger. */
bool yukprofile;        /* AIS: Use the yuk profiler. */
bool useprintflow;      /* AIS: Add +printflow support. */
extern bool ick_coreonerr;   /* AIS: Dump core on IE778. (defined in ick_lose.c) */
bool multithread;       /* AIS: Allow multithreading and backtracking. */
bool variableconstants; /* AIS: Allow anything on the left of an assignment. */
bool createsused;       /* AIS: Allow the use of CREATE. */
bool useickec;          /* AIS: Link together INTERCAL and C. */
static bool nosyslib;   /* AIS: Don't link syslib under any circumstances. */
static bool showsystem; /* AIS: Show the command lines in system() calls */
bool cdebug;            /* AIS: Pass -g to our C compiler, and leave C code. */
int optdebug;           /* AIS: Debug the optimizer. Value is 0, 1, 2, or 3. */
bool flowoptimize;      /* AIS: Do flow optimizations (in INTERCAL!). */
bool coopt;             /* AIS: The constant-output optimization. This should
			   mean that INTERCAL will beat any other language at
			   many benchmark programs (!) */
extern bool ick_printfopens; /* AIS: Print messages whenever attempting to open a
			       file: from uncommon.c */
extern bool ick_checkforbugs;/* AIS: Try to find possible bugs in the source code */
bool pickcompile;       /* AIS: Compile for PIC? */
/*@-exportlocal@*/      /* AIS: relevant to the lexer */
bool clclex;            /* AIS: 1 means use CLC-INTERCAL meanings for @, ? */
/*@=exportlocal@*/
bool ick_clcsemantics;       /* AIS: CLC semantics for I/O, abstaining GIVE UP, &c*/
static bool outtostdout;     /* AIS: Output on stdout rather than the output file */

/* AIS: Autodetected compilation options */
int compucomecount;    /* Computed COME FROM count */
bool compucomesused;   /* Are computed COME FROMs used? */
bool gerucomesused;    /* Is COME FROM gerund used? */
bool nextfromsused;    /* Is NEXT FROM used? */
bool opoverused;       /* Is operand overloading used? */
/*@null@*/ node* firstslat=0;      /* The first slat expression in the program */
/*@null@*/ node* prevslat=0;       /* The last slat expression used so far */

static bool dooptimize;	/* do optimizations? (controlled by -O) */
static bool ick_clockface;	/* set up output to do IIII for IV */

#define SKELETON  "ick-wrap.c"
#define PSKELETON "pickwrap.c"
#define SYSLIB    "syslib"

/* numeric base defaults, exported to other files */

#define DEFAULT_BASE 2
#define DEFAULT_SMALL_DIGITS 16
#define DEFAULT_LARGE_DIGITS 32
#define DEFAULT_MAX_SMALL 0xffffL
#define DEFAULT_MAX_LARGE 0xffffffffL

int ick_Base;
int ick_Small_digits;
int ick_Large_digits;
unsigned int ick_Max_small;
unsigned int ick_Max_large;

int ick_lineno;	/* after yyparse, this is the total number of statements */

/* currently supported numeric bases, not exported */
static const int maxbase = 7;
static const int smallsizes[8] = {0, 0, 16, 10, 8, 6, 6, 5};
static const unsigned int maxsmalls[8] =
{0, 0, 65535, 59048, 65535, 15624, 46655, 16806};

/*@observer@*/ static const char *compiler;

atom *oblist = NULL, *obdex;
int obcount = 0;
int nonespots, ntwospots, ntails, nhybrids;
int nmeshes; /* AIS */

tuple *tuples = NULL;
int tuplecount = 0;

tuple *optuple = NULL; /* AIS: Tuple being optimized */

/*
 * mappings from magic line ranges to system library components are
 * declared here.
 */
struct linerange_t {
    int start, end;
    char *libname;
};
/* Note! "syslib" must be last in this list, as other parts of perpet.c
   care about whether syslib in particular was included. */
struct linerange_t lineranges[] = {
    {5000, 5699, "floatlib"},  /* the floating-point support */
    {1000, 1999, "syslib"},    /* the system library */
    {0, 0, NULL},
};

extern const assoc varstores[]; /* AIS: Need to know this for PIC compilation */

#ifndef HAVE_UNISTD_H
/* AIS: We don't have unistd.h, so we can't use getopt. Write our own version
   that's less general but good enough. */
int optind=1;
int optopt;
int getopt(int argc, char * const *argv, const char *options)
{
  if(optind>argc) return EOF; /* Out of command line */
  if(!argv[optind]) return EOF; /* Out of command line */
  while(!strcmp(argv[optind],"-"))
  {
    optind++; /* Go to ick_next argument */
    if(!argv[optind]) return EOF;
  }
  if(*(argv[optind])!='-') return EOF; /* this arg is not an option */
  optopt=argv[optind][1];
  memmove(argv[optind]+1,argv[optind]+2,strlen(argv[optind]+1));
  if(optopt=='-') {optind++; return EOF;} /* -- means end of options */
  if(strchr(options, optopt)) return optopt; /* valid option */
  return '?'; /* invalid option */
}
#endif

static int myfgetc(FILE* in)
{
  char c;
  size_t dummy;
  dummy = fread(&c,1,1,in);
  if(feof(in)) return EOF;
  return (int)c;
}

static void abend(int signim)
{
  /*@-noeffect@*/ (void) signim; /*@=noeffect@*/
  ick_lose(IE778, iyylineno, (const char *)NULL);
}
static void print_usage(const char *prog, const char *options)
{
  fprintf(stderr,"Usage: %s [-%s] <file> [<file> ...]\n",prog,options);
  fprintf(stderr,"\t-b\t:reduce the probability of E774 to zero\n");
  fprintf(stderr,"\t-c\t:compile INTERCAL to C, but don't compile C\n");
  fprintf(stderr,"\t-d\t:print yacc debugging information (implies -c)\n");
  fprintf(stderr,"\t-e\t:link together INTERCAL and C files as one program\n");
  fprintf(stderr,"\t\t (without this option, all INTERCAL files produce\n");
  fprintf(stderr,"\t\t separate output files; with it, the first file given\n");
  fprintf(stderr,"\t\t must be the only INTERCAL file) (prevents -mypPf)\n");
  fprintf(stderr,"\t-E\t:never include system libraries (prevents -P)\n");
  fprintf(stderr,"\t-t\t:traditional mode, accept only INTERCAL-72\n");
  fprintf(stderr,"\t-C\t:clockface output (e.g. use IIII instead of IV)\n");
  fprintf(stderr,"\t-O\t:optimize expresssions in generated code\n");
  /* AIS: Changed the help message for the previous line (because the
     function of -O has changed). I wrote the next group of options. */
  fprintf(stderr,"\t-f\t:optimize control flow in generated code "
	  "(prevents -yp)\n");
#ifdef HAVE_PROG_SH
# ifdef HAVE_SYS_INTERPRETER
  fprintf(stderr,"\t-F\t:optimize everything in generated code for\n"
	  "\t\t speed, regardless of how slow the compiler becomes or how\n"
	  "\t\t large the object file becomes. Implies -fO, "
	  "prevents -cdeghpyH\n");
# else
  fprintf(stderr,"\t-F\t:unsupported on computers without #! support\n");
# endif
#else
  fprintf(stderr,"\t-F\t:unsupported on computers without sh or bash\n");
#endif
  fprintf(stderr,"\t-h\t:print optimizer debugging information "
	  "(implies -cO)\n");
  fprintf(stderr,"\t-H\t:print verbose optimizer debugging information "
	  "(implies -cO)\n");
  fprintf(stderr,"\t-hH\t:print optimizer debugging information in a\n"
	  "\t\t different form (implies -cO)\n");
#ifdef HAVE_UNISTD_H
  fprintf(stderr,"\t-y\t:run the yuk debugger on the code (prevents -fme)\n");
  fprintf(stderr,"\t-p\t:run the yuk profiler on the code (prevents -fme)\n");
#else
  fprintf(stderr,"\t-y\t:unsupported on computers without <unistd.h>\n");
  fprintf(stderr,"\t-p\t:unsupported on computers without <unistd.h>\n");
#endif
  fprintf(stderr,"\t-w\t:add support for the +printflow option\n");
  fprintf(stderr,"\t-m\t:allow multithreading and backtracking\n"
	  "\t\t (prevents -ype, implies -w)\n");
  fprintf(stderr,"\t-a\t:allow the use of CREATE (prevents -P)\n");
  fprintf(stderr,"\t-v\t:allow anything on the left of an assignment. This "
	  "is required\n\t\t if you want operand overloading to change "
	  "meshes.\n\t\t (prevents -fFOP)\n");
  fprintf(stderr,"\t-P\t:compile PIC-INTERCAL rather than INTERCAL\n");
  fprintf(stderr,"\t\t (prevents -amFvxeE, implies -cfO)\n");
  fprintf(stderr,"\t-o\t:output to stdout rather than .c (implies -c)\n");
  fprintf(stderr,"\t-X\t:interpret ambiguous syntax as Princeton not\n"
	  "\t\t Atari (i.e. CLC-INTERCAL not C-INTERCAL)\n");
  fprintf(stderr,"\t-x\t:use CLC-INTERCAL rules for I/O and abstaining\n"
	  "\t\t from a GIVE UP by label (prevents -P)\n");
  fprintf(stderr,"\t-u\t:print a message whenever the compiler tries to "
	  "open a file\n");
  fprintf(stderr,"\t-U\t:dump core on IE778 after printing an error\n");
  fprintf(stderr,"\t-Y\t:display the command line used whenever an external\n"
	  "\t\t program is invoked\n");
  fprintf(stderr,"\t-g\t:compile to both debuggable executable and C\n");
  fprintf(stderr,"\t-l\t:attempt to report likely bugs "
	  "and nonportabilities (implies -O)\n");
  /* AIS: End of options I added. */
  fprintf(stderr,"\t<file>\tINTERCAL source file (use extension .i\n");
  fprintf(stderr,"\t\tfor base 2 or .3i, etc., for base 3, etc.).\n");
}

#if __DJGPP__
/* AIS: Determine whether an environment variable exists (this is used to
   find a temp directory) */
static int isenv(char* e)
{
  char* x=getenv(e);
  return x != NULL && *x != '\0';
}
#endif

extern int	optind;		/* set by getopt */

/**
 * This parses command line options.
 * @param argc What do you think?
 * @param argv Likewise.
 * @note May (directly) call ick_lose() with IE111 and IE256.
 */
static void parse_options(int argc, char *argv[])
{
  int		c;

  /* getopt is POSIX, and I provide my own version if the POSIX version
     isn't found, so the unrecog warning is a false positive. */
  /*@-unrecog@*/
  while ((c = getopt(argc, argv, ARGSTRING)) != EOF)
    /*@=unrecog@*/
  {
    switch (c)
    {
    case 'b':
      nocompilerbug = true;
      break;

    case 'c':
      compile_only = true;
      /* AIS */ coopt = false;
      break;

    case 'o': /* AIS */
      compile_only = true;
      outtostdout = true;
      coopt = false;
      break;

    case 'd':
	yydebug = 1;
	compile_only = true;
      /* AIS */ coopt = false;
      break;

    case 'e': /* AIS */
      useickec = true;
      multithread = pickcompile = coopt = yukdebug = yukprofile = false;
      break;

    case 'E': /* AIS */
      nosyslib = true;
      pickcompile = false;
      break;

    case 'C':
      ick_clockface = true;
      break;

    case 't':
      ick_traditional = true;
      if(multithread) ick_lose(IE111, 1, (const char*) NULL); /* AIS */
      if(pickcompile) ick_lose(IE111, 1, (const char*) NULL); /* AIS */
      break;

    case 'O':
      dooptimize = true;
      variableconstants = false; /* AIS */
      break;

    case 'f': /* By AIS */
      flowoptimize = true;
      yukdebug = yukprofile = false;
      variableconstants = false;
      break;

    case 'F': /* By AIS */
      coopt = flowoptimize = dooptimize = true;
      variableconstants = useickec = false;
      yukdebug = yukprofile = outtostdout = compile_only = cdebug = false;
      yydebug = 0;
      if(pickcompile) ick_lose(IE256, 1, (const char*) NULL);
      break;

    case 'h': /* By AIS */
      optdebug|=1;
      compile_only=dooptimize=true;
      coopt=false;
      break;

    case 'H': /* By AIS */
      optdebug|=2;
      compile_only=dooptimize=true;
      coopt=false;
      break;

    case 'y': /* By AIS */
#ifdef HAVE_UNISTD_H
      yukdebug=true;
      multithread=flowoptimize=coopt=useickec=false;
#endif
      break;

    case 'p': /* By AIS */
#ifdef HAVE_UNISTD_H
      yukprofile=true;
      multithread=flowoptimize=coopt=useickec=false;
#endif
      break;

    case 'w': /* By AIS */
      useprintflow = true;
      break;

    case 'm': /* By AIS */
      multithread=true;
      yukprofile=false;
      yukdebug=false;
      useickec=false;
      if(ick_traditional) ick_lose(IE111, 1, (const char*) NULL);
      break;

    case 'a': /* By AIS */
      createsused=true;
      pickcompile=false;
      break;

    case 'v': /* By AIS */
      variableconstants=true;
      dooptimize=false;
      flowoptimize=false;
      coopt=false;
      pickcompile=false;
      break;

    case 'l': /* By AIS */
      ick_checkforbugs=true;
      dooptimize=true;
      break;

    case 'U': /* By AIS */
      ick_coreonerr=true;
      break;

    case 'u': /* By AIS */
      ick_printfopens=true;
      break;

    case 'Y': /* By AIS */
      showsystem=true;
      break;

    case 'P': /* By AIS */
      pickcompile=true;
      multithread=coopt=variableconstants=createsused=false;
      ick_clcsemantics=useickec=nosyslib=false;
      compile_only=true;
      dooptimize=flowoptimize=true; /* needed for PICs */
      break;

    case 'X': /* By AIS */
      clclex=true;
      break;

    case 'x': /* By AIS */
      ick_clcsemantics=true;
      pickcompile=false;
      break;

    case 'g': /* By AIS */
      cdebug=true;
      coopt=false;
      break;

    case '?':
    default:
    case '@':
      print_usage(argv[0], ARGSTRING);
      exit(EXIT_FAILURE);
      /*@-unreachable@*/ break; /*@=unreachable@*/
    }
  }
}


/**
 * This code handles archives (for -e).
 * @param libbuf Pointer to a buffer to which extra files to link in prelink()
 *               will be added. Need to be initialized up to the first zero byte.
 * @param libbuf_size Size of the buffer libbuf.
 * @param library The cmd line argument used for the library (but without the
 *                extension).
 */
static void handle_archive(char *libbuf, size_t libbuf_size,
                           /*@observer@*/ const char* library)
{
  /* AIS: request for a library. Given a filename of the form
     libwhatever.a, it adds  -lwhatever to libbuf (that's with
     a preceding space). If the filename doesn't start with lib,
     it instead adds a space and the filename to libbuf. */
  if(library[0]=='l'&&library[1]=='i'&&
      library[2]=='b')
      (void) ick_snprintf_or_die(libbuf+strlen(libbuf),libbuf_size - strlen(libbuf),
			" -l%s",library+3);
  else
      (void) ick_snprintf_or_die(libbuf+strlen(libbuf),libbuf_size - strlen(libbuf),
			" %s.a",library);
}


/**
 * This handles Befunge 98 (for -e).
 * @param libbuf Pointer to a buffer to which extra files to link in prelink()
 *               will be added. Need to be initialized up to the first zero byte.
 * @param libbuf_size Size of the buffer libbuf.
 * @param libdir The ick library directory.
 * @param argv0 Should be argv[0], which wasn't modified.
 * @param filename The file name  of the Befunge file (but without the extension).
 * @note May (directly) call ick_lose() with IE888 and IE899.
 */
static void handle_befunge98(char *libbuf, size_t libbuf_size,
                             /*@observer@*/ const char* libdir,
                             /*@observer@*/ const char* argv0,
                             /*@observer@*/ const char* filename)
{
  /* AIS: Compile the .b98 file into a .cio so that it can be used
     later, and include the necessary libraries to use it, or error
     if the libraries aren't installed yet. I use a somewhat dubious
     trick here: the .b98 file's .cio, and the necessary libraries,
     are added in the libraries section of the command line, whereas
     the space on the command line where the .b98 file was is used
     for the expansion library ecto_b98. This is because ecto_b98
     requires preprocessing/prelinking/interprocessing or whatever
     you want to call it, whereas unlike for other .cios, the .cio
     produced from the Befunge file doesn't.
   */

#define MARKERMAX 128

  FILE* of;
  int x,y,jlb;
  char outputfilename[BUFSIZ];
  int markerposns[MARKERMAX][2];
  int markercount=0;

  /* Error if libick_ecto_b98.a is missing. It might be, and not
     just due to installation problems. */
  if(!ick_findandtestopen("libick_ecto_b98.a",libdir,"rb",argv0))
    ick_lose(IE899,-1,(const char *)NULL);

  /* Compile the .b98 file into a .cio. It's open on stdin right now,
     so we just need to handle the output side of things. */

  (void) ick_snprintf_or_die(outputfilename, sizeof outputfilename, "%s.cio", filename);

  if(!((of = ick_debfopen(outputfilename,"w"))))
    ick_lose(IE888,-1,(const char *)NULL);

  fprintf(of,"const unsigned char* ick_iffi_befungeString=\n\"");

  x=0; y=0; jlb=0;
  for(;;)
  {
    int c=getchar();
    if(c==EOF) break;
    if(c==0xB7)
    {
      /* Middot (0xB7) has special handling. */
      c='M';
      markerposns[markercount][0]=x;
      markerposns[markercount++][1]=y;
    }
    if(c=='\r') {jlb = 1; x=0; y++; c='\n';}
    else if(c=='\n' && jlb) {jlb = 0; continue;}
    else if(c=='\n') {x=0; y++; jlb = 0;}
    else x++;
    fprintf(of,"\\x%x",(unsigned int)c);
    if(!x) fprintf(of,"\"\n\"");
  }
  fprintf(of,"\";\n\nint ick_iffi_markercount=%d;\n"
	  "long long ick_iffi_markerposns[][2]={\n",markercount);
  if(!markercount) fprintf(of,"{0,0}\n");
  while(markercount--) fprintf(of,"{%d,%d},\n",
			       markerposns[markercount][0],
			       markerposns[markercount][1]);
  fprintf(of,"};\n");

  (void) fclose(of);

  /* Put the libraries and .cio file in the command line. */
  (void) ick_snprintf_or_die(libbuf+strlen(libbuf),libbuf_size - strlen(libbuf),
		      " %s.cio -lick_ecto_b98 -lm -lncurses", filename);
}


/**
 * This computes what type the INTERCAL source file is.
 * It will change various globals.
 * @param chp A pointer to the first letter of the extension of the file. Note
 *            that it won't be changed, but can't be const char* due to being
 *            passed as the second parameter to strtol() as well.
 * @note May (directly) call ick_lose() with IE111, IE256 and IE998.
 */
static void find_intercal_base(char* chp)
{
  /* wwp: reset the base variables to defaults, because if the  */
  /* sourcefile has extension .i they will not be reset in the  */
  /* following chunk of code. but i don't want to modify the    */
  /* following chunk of code because i think it is very clever; */
  /* grabs the base on the first pass, then validates the rest  */
  /* of the extension on the second.                            */
  ick_Base = DEFAULT_BASE;
  ick_Small_digits = DEFAULT_SMALL_DIGITS;
  ick_Large_digits = DEFAULT_LARGE_DIGITS;
  ick_Max_small = (unsigned)DEFAULT_MAX_SMALL;
  ick_Max_large = (unsigned)DEFAULT_MAX_LARGE;

  /* determine the file type from the extension */
  while (strcmp(chp,"i"))
  {
    ick_Base = (int)strtol(chp,&chp,10);
    if (ick_Base < 2 || ick_Base > maxbase)
      ick_lose(IE998, 1, (const char *)NULL);
    else if (ick_traditional && ick_Base != 2)
      ick_lose(IE111, 1, (const char *)NULL);
    else if (pickcompile && ick_Base != 2)
      ick_lose(IE256, 1, (const char *)NULL); /* AIS */
    ick_Small_digits = smallsizes[ick_Base];
    ick_Large_digits = 2 * ick_Small_digits;
    ick_Max_small = maxsmalls[ick_Base];
    if (ick_Max_small == 0xffff)
      ick_Max_large = (unsigned)0xffffffffLU;
    else
      ick_Max_large = (ick_Max_small + 1) * (ick_Max_small + 1) - 1;
  }
}


/**
 * This checks if we automagically need to include syslib
 * @param buffer Output buffer that may be modified to contain the path of
 *               syslib.i or syslib.Ni (where N is 3-7).
 * @param size Size of buffer.
 * @param needsyslib Pointer to the bool needsyslib declared in main().
 * @param argv0 Should be argv[0], which wasn't modified.
 * @param ick_sysdir The ick data directory.
 * @note May (directly) call ick_lose() with IE127.
 */
static void check_syslib(/*@partial@*/ char *buffer,
                         size_t size,
                         /*@out@*/ bool *needsyslib,
                         /*@observer@*/ const char *argv0,
                         /*@observer@*/ const char *ick_sysdir)
{
    tuple *tp;
    struct linerange_t *lp;

    if (nosyslib || pickcompile) { 
	*needsyslib = false;
	return;
    }

    /*
     * magical inclusion of system libraries is done here
     */
    for (lp = lineranges; lp->start; lp++)
    {
	*needsyslib = false;
	for (tp = tuples; tp->type; tp++) {
	    /*
	     * If some label in the specified range is defined,
	     * then assume the library we seek is already there, so we
	     * can stop searching.
	     */
	    if (tp->label >= lp->start && tp->label <= lp->end) {
		*needsyslib = false;
		goto breakout;
	    }
	    /*
	     * If some label in the specified range is being
	     * called, we might need the library.
	     */
	    if (tp->type == NEXT &&
		tp->u.target >= lp->start && tp->u.target <= lp->end)
		*needsyslib = true;
	}
	if (*needsyslib) {
	    if (ick_Base == 2)
		(void) ick_snprintf_or_die(buffer, size,
					   "%s.i", lp->libname);
	    else
		(void) ick_snprintf_or_die(buffer, size,
					   "%s%d.%di", lp->libname, ick_Base, ick_Base);
	    if (ick_findandfreopen(buffer, ick_sysdir, "r", argv0, stdin) == NULL)
		ick_lose(IE127, 1, (const char*) NULL);
#ifdef USE_YYRESTART
	    yyrestart(stdin);
#endif /* USE_YYRESTART */
	    (void) yyparse();
	    textlinecount=iyylineno;
	}
    breakout:
	;
    }
}

/**
 * This code propagates type information up the expression tree.
 * It also does some unrelated stuff such as checking for WRITE IN and disabling
 * coopt if that is found.
 */
static void propagate_typeinfo(void)
{
  tuple *tp;
  /*
   * Now propagate type information up the expression tree.
   * We need to do this because the unary-logical operations
   * are sensitive to the type widths of their operands, so
   * we have to generate different code depending on the
   * deducible type of the operand.
   */
  for (tp = tuples; tp->type; tp++)
  {
    if (tp->type == GETS || tp->type == RESIZE
	|| tp->type == WRITE_IN || tp->type == READ_OUT
	|| tp->type == FROM || tp->type == MANYFROM
	|| tp->type == FORGET || tp->type == RESUME
	|| tp->type == COMPUCOME || tp->type == UNKNOWN)
      typecast(tp->type == MANYFROM ? tp->u.node->lval : tp->u.node);
    if (tp->type == WRITE_IN) coopt = false; /* AIS: may as well do
					        this here */
  }
}


/**
 * This runs the optimiser.
 */
static void run_optimiser(void)
{
  tuple *tp;
  /* perform optimizations */
  if (dooptimize)
    for (tp = tuples; tp->type; tp++)
    {
      /* AIS: Allow breaching of the only specification on tuples
	  at this point; I've checked that tuples isn't reallocated
	  during the block, so this is fine. */
      /*@-onlytrans@*/
      optuple = tp;
      /*@=onlytrans@*/
      if (tp->type == GETS || tp->type == RESIZE
	  || tp->type == FORGET || tp->type == RESUME
	  || tp->type == FROM || tp->type == COMPUCOME)
	optimize(tp->u.node);
      if (tp->type == MANYFROM) optimize(tp->u.node->lval);
    } /* AIS: Added FROM and MANYFROM support. */

  /* AIS: perform flow optimizations */
  if (flowoptimize) optimizef();
}


/**
 * Generate random line number for E774.
 * @returns A random line number, or -1 for no random bug generated.
 */
static int randomise_bugline(void)
{
  /* decide if and where to place the compiler bug */
#ifdef USG
  if (!nocompilerbug && lrand48() % 10 == 0)
    return (int)(lrand48() % ick_lineno);
#else
  if (!nocompilerbug && rand() % 10 == 0)
    return rand() % ick_lineno;
#endif
  else
    return -1;
}


/**
 * This opens the outfile.
 * @param filename The filename to open.
 * @returns A pointer to a FILE for the filename. If the global outtostdout is
 * set then it will return stdout.
 * @note May (directly) call ick_lose() with IE888.
 */
static /*@dependent@*/ FILE* open_outfile(/*@observer@*/ const char * filename)
{
  FILE *ofp;
  /* AIS: ofp holds fopened storage if !outtostdout, and local-copy
     storage if outtostdout, and this is not a bug, although it
     confuses Splint. */
  /*@-branchstate@*/
  if(outtostdout) ofp=stdout; /* AIS */
  else if((ofp = ick_debfopen(filename, "w")) == (FILE *)NULL)
    ick_lose(IE888, 1, (const char *)NULL);
  /*@=branchstate@*/
  return ofp;
}


/**
 * This generates the CC command line.
 * @param buffer Output buffer.
 * @param size Size of the output buffer.
 * @param sourcefile The name of the C file.
 * @param includedir The ick include directory.
 * @param path The path of the ick binary (execuding filename).
 * @param libdir The ick library directory.
 * @param outputfile The name of the output file.
 */
static void gen_cc_command(char* buffer, size_t size,
                           /*@observer@*/ const char* sourcefile,
                           /*@observer@*/ const char* includedir,
                           /*@observer@*/ const char* path,
                           /*@observer@*/ const char* libdir,
                           /*@observer@*/ const char* outputfile)
{
  (void) ick_snprintf_or_die(buffer, size,
			     "%s %s%s-I%s -I%s -I%s/../include -L%s -L%s -L%s/../lib -O%c -o %s" EXEEXT " -lick%s%s",
#ifdef __DJGPP__
			     "",
#else
			     compiler,
#endif
#ifdef HAVE_CLOCK_GETTIME /* implies -lrt is available */
			     sourcefile, yukdebug||yukprofile?" -lyuk -lrt ":" ",
#else
			     sourcefile, yukdebug||yukprofile?" -lyuk ":" ",
#endif
			     includedir, path, path, libdir, path, path,
			     cdebug?'0':coopt?'3':'2', /* AIS: If coopting, optimize as much as possible
							   JH: [d]on't optimise when compiling with debugger support */
			     outputfile, multithread?"mt":"", cdebug?" -g":"");
  /* AIS: Possibly link in the debugger yuk and/or libickmt.a here. */
  /* AIS: Added -g support. */
  /* AIS: Added argv[0] (now path) to the -I, -L settings. */
}


/**
 * This generates the actual C code.
 * @param ifp This should be opened to either ick-wrap.c or pickwrap.c.
 * @param ofp The out file.
 * @param source_name_stem Source name stem
 * @param needsyslib Pointer to the needsyslib bool in main().
 * @param bugline What line number to add a random bug to.
 * @param compilercommand The compiler command line.
 * @note May (directly) call ick_lose() with IE256.
 */
static void generate_code(FILE *ifp, FILE *ofp,
                          /*@observer@*/  const char* source_name_stem,
                          bool *needsyslib,
                          int bugline,
                          /*@observer@*/ const char* compilercommand)
{
  int maxabstain;
  int           c, i;
  tuple   *tp;
  atom    *op;
  while ((c = myfgetc(ifp)) != EOF)
    if (c != (int)'$')
      (void) fputc(c, ofp);
    else switch(myfgetc(ifp))
	 {
	 case 'A':	/* source name stem */
	   (void) fputs(source_name_stem, ofp);
	   break;

	 case 'B':	/* # of statements */
	   (void) fprintf(ofp, "%d", ick_lineno);
	   break;

	 case 'C':	/* initial abstentions */
	   /* AIS: Modified to check for coopt, pickcompile */
	   maxabstain = 0;
	   for (tp = tuples; tp->type; tp++)
	     if (((tp->exechance <= 0 || tp->exechance >= 101)
		  && tp - tuples + 1 > maxabstain)
		 || coopt || pickcompile)
	       maxabstain = tp - tuples + 1;
	   if (maxabstain)
	   {
	     if(!pickcompile) (void) fprintf(ofp, " = {");
	     for (tp = tuples; tp < tuples + maxabstain; tp++)
	     {
	       if(tp->exechance != 100 && tp->exechance != -100)
	       { /* AIS: The double-oh-seven operator prevents
		    coopt working. However, syslib contains a
		    double-oh-seven. feh.c has checked that that
		    isn't referenced; if it isn't, we can allow
		    one double-oh-seven if syslib was
		    automagically inclulded. */
		 if(*needsyslib) *needsyslib = false; else coopt = false;
	       }
	       if(!pickcompile)
	       {
		 if (tp->exechance > 0)
		 {
		   (void) fprintf(ofp, "0, ");
		   tp->initabstain=0; /* AIS: -f might not be
					 given, so we can't rely
					 on dekludge.c doing
					 this */
		 }
		 else {
		   (void) fprintf(ofp, "1, ");
		   tp->exechance = -tp->exechance;
		   tp->initabstain=true; /* AIS: As above */
		   /* AIS: If the line was ick_abstained, we need to
		      swap ONCEs and AGAINs on it round, to suit
		      the code degenerator. */
		   if(tp->onceagainflag == onceagain_ONCE)
		     tp->onceagainflag = onceagain_AGAIN;
		   else if(tp->onceagainflag == onceagain_AGAIN)
		     tp->onceagainflag = onceagain_ONCE;
		 }
		 if(tp->exechance >= 101)
		 {
		   /* AIS: This line has a MAYBE */
		   tp->maybe = true;
		   tp->exechance /= 100;
		 }
		 else tp->maybe = false;
	       }
	       else /* AIS: hardcoded abstain bits for PICs */
	       {
		 if(!tp->abstainable) continue;
		 if(tp->exechance > 0)
		   (void) fprintf(ofp, "ICK_INT1 ICKABSTAINED(%d)=0;\n",(int)(tp-tuples));
		 else
		   (void) fprintf(ofp, "ICK_INT1 ICKABSTAINED(%d)=1;\n",(int)(tp-tuples));
	       }
	     }
	     if(!pickcompile) (void) fprintf(ofp, "}");
	   }
	   break;

	 case 'D':	/* linetypes array for abstention handling */
	   maxabstain = 0;
	   for (tp = tuples; tp->type; tp++)
	     if (tp->type == ENABLE || tp->type == DISABLE || tp->type == MANYFROM)
	       maxabstain++;
	   if (maxabstain || /* AIS */ gerucomesused)
	   {
	     int j=0; /* AIS */
	     /* AIS: Changed to use enablersm1 */
	     i = 0;
	     for (;i < (int)(sizeof(enablersm1)/sizeof(char *));i++)
	       (void) fprintf(ofp,
			      "#define %s\t%d\n",
			      enablersm1[i], i);

	     (void) fprintf(ofp, "int linetype[] = {\n");
	     for (tp = tuples; tp->type; tp++)
	       if(tp->ppnewtype) /* AIS */
		 (void) fprintf(ofp,"    %s,\n",
				enablers[tp->ppnewtype - GETS]);
	       else if(tp->preproc) /* AIS */
		 (void) fprintf(ofp,"    PREPROC,\n");
	       else if (tp->type >= GETS && tp->type <= FROM)
		 /* AIS: FROM added */
		 (void) fprintf(ofp,
				"    %s,\n",
				enablers[tp->type - GETS]);
	       else
		 /* AIS: I didn't change this code, but relied on
		    it when implementing just-in-case compilation;
		    SPLATTERED and UNKNOWN (the two types of
		    syntax error, unsalvageable and salvageable
		    respectively) both become UNKNOWN in the
		    linetypes array. */
		 (void) fprintf(ofp, " UNKNOWN,\n");
	     (void) fprintf(ofp, "};\n");
	     /* AIS: Implement the reverse of this array too (i.e.
		from line types to lines); this significantly
		speeds up up reinstate/abstain on gerunds. Joris
		Huizer originally suggested the optimisation in
		question; this implements the same algorithm in a
		more maintainable way. (I didn't want to have to
		keep five copies of the command list in sync; two
		is bad enough!) */
	     (void) fprintf(ofp, "int revlinetype[] = {\n");
	     for(i=0;i < (int)(sizeof(enablersm1)/sizeof(char *));i++)
	     {
	       (void) fprintf(ofp,"/* %s */",enablersm1[i]);
	       for (tp = tuples; tp->type; tp++)
	       {
		 if((tp->ppnewtype && tp->ppnewtype-GETS == i-1) ||
		    (!tp->ppnewtype && tp->preproc &&
		     i-1 == PREPROC-GETS) ||
		    (!tp->ppnewtype && !tp->preproc &&
		     tp->type >= GETS && tp->type <= FROM &&
		     tp->type-GETS == i-1) ||
		    (!i && !tp->ppnewtype && !tp->preproc &&
		     (tp->type < GETS || tp->type > FROM)))
		   (void) fprintf(ofp, " %ld,",(long)(tp-tuples));
	       }
	       (void) fprintf(ofp,"\n");
	     }
	     (void) fprintf(ofp, "};\n");
	     (void) fprintf(ofp, "int revlineindex[] = {\n");
	     for(i=0;i < (int)(sizeof(enablersm1)/sizeof(char *));i++)
	     {
	       (void) fprintf(ofp,"/* %s */",enablersm1[i]);
	       (void) fprintf(ofp," %d,\n",j);
	       for (tp = tuples; tp->type; tp++)
	       {
		 if((tp->ppnewtype && tp->ppnewtype-GETS == i-1) ||
		    (!tp->ppnewtype && tp->preproc &&
		     i-1 == PREPROC-GETS) ||
		    (!tp->ppnewtype && !tp->preproc &&
		     tp->type >= GETS && tp->type <= FROM &&
		     tp->type-GETS == i-1) ||
		    (!i && !tp->ppnewtype && !tp->preproc &&
		     (tp->type < GETS || tp->type > FROM)))
		   j++;
	       }
	     }
	     (void) fprintf(ofp, "/* end */ %d\n};\n",j);
	   }
	   break;

	 case 'E':	/* extern to intern map */
	   if(!pickcompile)
	   {
	     (void) fprintf(ofp,"int ick_Base = %d;\n",ick_Base);
	     (void) fprintf(ofp,"int ick_Small_digits = %d;\n",
			    ick_Small_digits);
	     (void) fprintf(ofp,"int ick_Large_digits = %d;\n",
			    ick_Large_digits);
	     (void) fprintf(ofp,"unsigned int ick_Max_small = 0x%x;\n",
			    ick_Max_small);
	     (void) fprintf(ofp,"unsigned int ick_Max_large = 0x%x;\n",
			    ick_Max_large);
	     if (yukprofile || yukdebug || multithread || useickec)
	     { /* AIS: yuk.c, multithreading require all these to exist */
	       if(!nonespots) nonespots = 1;
	       if(!ntwospots) ntwospots = 1;
	       if(!ntails) ntails = 1;
	       if(!nhybrids) nhybrids = 1;
	     }
	     else if(opoverused)
	     {
	       /* AIS: The operand-overloading code requires onespot and
		  twospot variables to exist. */
	       if(!nonespots) nonespots = 1;
	       if(!ntwospots) ntwospots = 1;
	     }
	     /* AIS:I de-staticed all these so they could be accessed by
		yuk and cesspool, and added all the mentions of yuk and
		multithread. Then I changed it so the variables would be
		allocated dynamically, to speed up multithreading. (It's
		an O(1) change to the speed of ordinary programs, so I
		thought I could get away with it. The order is wrt the
		number of lines in the program. The change is O(n) wrt
		the number of variables, but again I hope that doesn't
		matter, and I won't get the entire INTERCAL community
		angry with me for daring to implement an extension that
		slows down existing programs.) */
	     if (variableconstants) /* AIS */
	     {
	       int temp=0;
	       (void) fprintf(ofp, "ick_type32 meshes[%d] = {",nmeshes);
	       while(temp<nmeshes)
	       {
		 (void) fprintf(ofp, "%luLU, ", varextern((unsigned long)temp,MESH));
		 temp++;
	       }
	       (void) fprintf(ofp, "};\n");
	     }

	     if (nonespots)
	     {
	       (void) fprintf(ofp,
			      "ick_type16* ick_onespots;\n");
	       (void) fprintf(ofp,
			      "bool* ick_oneforget;\n");
	       if(yukprofile || yukdebug)
	       {
		 (void) fprintf(ofp,
				"ick_type16 oneold[%d];\n",
				nonespots);
		 (void) fprintf(ofp,
				"signed char onewatch[%d];\n",
				nonespots);
	       }
	       if(multithread)
	       {
		 (void) fprintf(ofp,
				"int onespotcount = %d;\n",
				nonespots);
	       }
	       if(multithread || opoverused || useickec) /* AIS */
	       {
		 int temp=nonespots;
		 (void) fprintf(ofp,
				"ick_overop* ick_oo_onespots = 0;\n");
		 if(opoverused)
		   while(temp--)
		     (void) fprintf(ofp,
				    "ick_type32 og1spot%d(ick_type32 t)\n{\n  (void)t;\n  return ick_onespots[%d];\n}\n"
				    "void os1spot%d(ick_type32 val, void(*f)())\n{\n  (void)f;\n  ick_assign((void*)"
				    "(ick_onespots+%d), ick_ONESPOT, ick_oneforget[%d], val);\n}\n",temp,temp,temp,temp,temp);
	       }
	     }
	     if (ntwospots)
	     {
	       (void) fprintf(ofp,
			      "ick_type32* ick_twospots;\n");
	       (void) fprintf(ofp,
			      "bool* ick_twoforget;\n");
	       if(yukprofile || yukdebug)
	       {
		 (void) fprintf(ofp,
				"ick_type32 twoold[%d];\n",
				ntwospots);
		 (void) fprintf(ofp,
				"signed char twowatch[%d];\n",
				ntwospots);
	       }
	       if(multithread)
	       {
		 (void) fprintf(ofp,
				"int twospotcount = %d;\n",
				ntwospots);
	       }
	       if(multithread || opoverused || useickec) /* AIS */
	       {
		 int temp=ntwospots;
		 (void) fprintf(ofp,
				"ick_overop* ick_oo_twospots = 0;\n");
		 if(opoverused)
		   while(temp--)
		     (void) fprintf(ofp,
				    "ick_type32 og2spot%d(ick_type32 t)\n{\n  (void)t;\n  return ick_twospots[%d];\n}\n"
				    "void os2spot%d(ick_type32 val, void(*f)())\n{\n  (void)f;\n  ick_assign((void*)"
				    "(ick_twospots+%d), ick_TWOSPOT, ick_twoforget[%d], val);\n}\n",temp,temp,temp,temp,temp);
	       }
	     }
	     if (ntails)
	     {
	       (void) fprintf(ofp,
			      "ick_array* ick_tails;\n");
	       (void) fprintf(ofp,
			      "bool* ick_tailforget;\n");
	       if(multithread)
	       {
		 (void) fprintf(ofp,
				"int tailcount = %d;\n",
				ntails);
	       }
	     }
	     if (nhybrids)
	     {
	       (void) fprintf(ofp,
			      "ick_array* ick_hybrids;\n");
	       (void) fprintf(ofp,
			      "bool* ick_hyforget;\n");
	       if(multithread)
	       {
		 (void) fprintf(ofp,
				"int hybridcount = %d;\n",
				nhybrids);
	       }
	     }
	     if (yydebug || compile_only)
	     {
	       assert(oblist != NULL);
	       for (op = oblist; op < obdex; op++)
		 if(op->type!=MESH) /* AIS: Added this check */
		   (void) fprintf(ofp, " /* %s %lu -> %lu */\n",
				  nameof(op->type, vartypes),
				  op->extindex,
				  op->intindex);
	     }
	     if (yukdebug || yukprofile)
	     { /* AIS: drop intern to extern map into the program */
	       (void) fprintf(ofp, "\nyukvar yukvars[]={\n");
	       assert(oblist != NULL);
	       for (op = oblist; op < obdex; op++)
		 if(op->type!=MESH) /* AIS: Added this check */
		   (void) fprintf(ofp,"    {%s,%lu,%lu},\n",
				  nameof(op->type, vartypes),
				  op->extindex,
				  op->intindex);
	       (void) fprintf(ofp,"    {YUKEND,0,0}};\n");
	     }
	     else if(useickec)
	     { /* AIS: likewise, but with different identifiers */
	       (void) fprintf(ofp, "\nick_ec_var ick_ec_vars[]={\n");
	       assert(oblist != NULL);
	       for (op = oblist; op < obdex; op++)
		 if(op->type!=MESH)
		   (void) fprintf(ofp,"    {%s,%lu,%lu},\n",
				  nameof(op->type, vartypes),
				  op->extindex,
				  op->intindex);
	       (void) fprintf(ofp,"    {ICK_EC_VARS_END,0,0}};\n");
	     }
	   }
	   else
	   {
	     /* Compiling for PIC */
	     /* Arrays not supported on PICs */
	     if(ntails || nhybrids)
	       ick_lose(IE256, iyylineno, (const char*) NULL);
	     /* and neither are variable constants */
	     if(variableconstants)
	       ick_lose(IE256, iyylineno, (const char*) NULL);
	     assert(oblist != NULL);
	     for (op = oblist; op < obdex; op++)
	     {
	       (void) fprintf(ofp, " /* %s %lu -> %lu */\n",
			      nameof(op->type, vartypes),
			      op->extindex,
			      op->intindex);
	       (void) fprintf(ofp, "#define %s%lu %s[%lu]\n",
			      nameof(op->type, vartypes),
			      op->extindex,
			      nameof(op->type, varstores),
			      op->intindex);
	       if(op->ignorable)
		 (void) fprintf(ofp, "ICK_INT1 ignore%s%lu = 0;\n",
				nameof(op->type, varstores),
				op->intindex);
	     }
	     (void) fprintf(ofp, "#include \"pick1.h\"\n");
	     if(nonespots)
	     {
	       (void) fprintf(ofp,
			      "ICK_INT16 ick_onespots[%d];\n"
			      "ICK_INT16 onespotsstash[%d];\n",
			      nonespots,
			      nonespots);
	       if(opoverused) /* AIS */
	       {
		 int temp=nonespots;
		 (void) fprintf(ofp,"ick_overop* ick_oo_onespots;\n");
		 while(temp--)
		   (void) fprintf(ofp,
				  "ick_type32 og1spot%d(ick_type32 t)\n{\n  (void)t;\n  return ick_onespots[%d];\n}\n"
				  "void os1spot%d(ick_type32 val,void(*f)())\n{\n  (void)f;\n  if(!ignoreonespots%d)"
				  " ick_onespots[%d]=val;\n}\n",temp,temp,temp,temp,temp);
	       }
	     }
	     if(ntwospots)
	     {
	       (void) fprintf(ofp,
			      "ICK_INT32 ick_twospots[%d];\n"
			      "ICK_INT32 twospotsstash[%d];\n",
			      ntwospots,
			      ntwospots);
	       if(opoverused) /* AIS */
	       {
		 int temp=ntwospots;
		 (void) fprintf(ofp,"ick_overop* ick_oo_twospots;\n");
		 while(temp--)
		   (void) fprintf(ofp,
				  "ick_type32 og2spot%d(ick_type32 t)\n{\n  (void)t;\n  return ick_twospots[%d];\n}\n"
				  "void os2spot%d(ick_type32 val,void(*f)())\n{\n  (void)f;\n  if(!ignoretwospots%d)"
				  " ick_twospots[%d]=val;\n}\n",temp,temp,temp,temp,temp);
	       }
	     }
	     (void) fprintf(ofp, "#include \"pick2.h\"\n");
	   }
	   break;

	 case 'F':	/* set options from command line */
	   if (ick_clockface)
	     (void) fprintf(ofp, "ick_clockface(true);\n");
	   if (ick_clcsemantics) /* AIS */
	     (void) fprintf(ofp, "ick_setclcsemantics(true);\n");
	   break;

	 case 'G':	/* degenerated code */
	   for (tp = tuples, i = 0; tp->type; tp++, i++)
	   {
	     emit(tp, ofp);
	     if (i == bugline)
	       (void) fprintf(ofp, "    ick_lose(IE774, ick_lineno, "
			      "(char *)NULL);\n");
	   }
	   break;

	 case 'H':	/* COMPUCOME, and dispatching for resumes */
	   /* AIS: Added COMPUCOME here. This line must be fully guarded
	      to prevent a longjmp to an uninitialised buffer (it's
	      guarded by a ick_lose() in ick-wrap.c.) Also checks for
	      multithread; programs that mix normal and computed COME
	      FROM need to use the same conventions for both, so even
	      if no computed COME FROMs are used, the normal ones need
	      this line so that COME FROMs can be handled consistently.*/
	   if(compucomesused || multithread)
	   {
	     (void) fprintf(ofp, "CCFL: ; CCF%d: longjmp(ick_cjb,1);\n",
			    compucomecount);
	   }
	   break;

	 case 'J':	/* # of source file lines */
	   (void) fprintf(ofp, "%d", iyylineno);
	   break;

	 case 'K':       /* AIS: yuk information (or not) */
	   if(yukdebug||yukprofile)
	   {
	     (void) fprintf(ofp, "#include \"config.h\"\n\n");
	     (void) fprintf(ofp, "#include \"yuk.h\"\n\n");
	     (void) fprintf(ofp, "char* textlines[] = {\n");
	     emittextlines(ofp); /* from feh.c */
	     (void) fprintf(ofp, "\"\"};\n\n");
	     (void) fprintf(ofp, "char* yukexplain[] = {\n");
	     for (tp = tuples; tp->type; tp++)
	     {
	       if (tp->type == GETS || tp->type == FORGET || tp->type == RESUME
		   || tp->type == FROM || tp->type == COMPUCOME
		   || tp->type == MANYFROM)
	       {
		 (void) fprintf(ofp, "\"");
		 explexpr(tp->type == MANYFROM ? tp->u.node->lval :
			  tp->type == GETS ? tp->u.node->rval : tp->u.node, ofp);
		 (void) fprintf(ofp, "\",\n");
	       }
	       else (void) fprintf(ofp, "0,");
	     }
	     (void) fprintf(ofp, "0};\n\n");
	     (void) fprintf(ofp, "int lineofaboff[] = {\n");
	     for (tp = tuples; tp->type; tp++)
	     {
	       fprintf(ofp,"%d,",tp->ick_lineno);
	     }
	     (void) fprintf(ofp, "-1};\n\n");
	     /*@+boolint@*/
	     (void) fprintf(ofp, "int yukopts = %d;\n", yukprofile+yukdebug*2);
	     /*@=boolint@*/
	     (void) fprintf(ofp, "yptimer ypexectime[%d];\n", ick_lineno);
	     (void) fprintf(ofp, "ypcounter ypexecount[%d];\n",ick_lineno);
	     (void) fprintf(ofp, "ypcounter ypabscount[%d];\n",ick_lineno);
	   }
	   break;

	 case 'L': /* AIS: increase Emacs compatibility */
	   (void) fprintf(ofp,
			  "/* -*- mode:c; compile-command:\"%s%s%s\" -*- */",
#ifdef __DJGPP__
			  compiler," ",
#else
			  "","",
#endif
			  compilercommand);
	   break;

	 case 'M': /* AIS: place new features defines in program */
	   /* This is needed even in a non-multithread program, to let
	      the header files know it's non-multithread */
	     (void) fprintf(ofp, "#define MULTITHREAD %d\n", multithread?1:0);
	   /* Likewise, to let the header files know whether it
	      overloads operands (I don't think this is used at
	      the moment, though) */
	   (void) fprintf(ofp, "#define OPOVERUSED %d\n",opoverused?1:0);
	   /* and whether to use the ICK_EC code */
	   if(useickec)
	     (void) fprintf(ofp, "#define ICK_EC 1\n");
	   break;

	 case 'N':	/* allocate variables */
	   /* AIS:I de-staticed all these so they could be accessed by
	      yuk and cesspool, and added all the mentions of yuk and
	      multithread. Then I changed it so the variables would be
	      allocated dynamically, to speed up multithreading (it's
	      an O(1) change to the speed of ordinary programs, so I
	      thought I could get away with it). At this point, the
	      'E' case must already have been done. calloc sets all
	      the integer values to 0, as before. In the case of
	      arrays, it will not zero pointers, but the number-of-
	      dimensions value will become 0, which can serve as a
	      'deallocated' flag. */
	   if (nonespots)
	   {
	     if(!pickcompile) /* AIS */
	     {
	       (void) fprintf(ofp,
			      "    ick_onespots = calloc("
			      "%d, sizeof *ick_onespots);\n",
			      nonespots);
	       (void) fprintf(ofp,
			      "    ick_oneforget = calloc("
			      "%d, sizeof *ick_oneforget);\n",
			      nonespots);
	     }
	     if(opoverused)
	     {
	       int temp=nonespots;
	       (void) fprintf(ofp,
			      "    ick_oo_onespots=malloc(%d*sizeof*ick_oo_onespots);\n",temp);
	       while(temp--)
		 (void) fprintf(ofp,
				"    ick_oo_onespots[%d].get=og1spot%d;\n    ick_oo_onespots[%d].set=os1spot%d;\n",
				temp,temp,temp,temp);
	     }
	   }
	   if (ntwospots)
	   {
	     if(!pickcompile)
	     {
	       (void) fprintf(ofp,
			      "    ick_twospots = calloc("
			      "%d, sizeof *ick_twospots);\n",
			      ntwospots);
	       (void) fprintf(ofp,
			      "    ick_twoforget = calloc("
			      "%d, sizeof *ick_twoforget);\n",
			      ntwospots);
	     }
	     if(opoverused)
	     {
	       int temp=ntwospots;
	       (void) fprintf(ofp,
			      "    ick_oo_twospots=malloc(%d*sizeof*ick_oo_twospots);\n",temp);
	       while(temp--)
		 (void) fprintf(ofp,
				"    ick_oo_twospots[%d].get=og2spot%d;\n    ick_oo_twospots[%d].set=os2spot%d;\n",
				temp,temp,temp,temp);
	     }
	   }
	   if (ntails&&!pickcompile)
	   {
	     (void) fprintf(ofp,
			    "    ick_tails = calloc("
			    "%d, sizeof *ick_tails);\n",
			    ntails);
	     (void) fprintf(ofp,
			    "    ick_tailforget = calloc("
			    "%d, sizeof *ick_tailforget);\n",
			    ntails);
	   }
	   if (nhybrids&&!pickcompile)
	   {
	     (void) fprintf(ofp,
			    "    ick_hybrids = calloc("
			    "%d, sizeof *ick_hybrids);\n",
			    nhybrids);
	     (void) fprintf(ofp,
			    "    ick_hyforget = calloc("
			    "%d, sizeof *ick_hyforget);\n",
			    nhybrids);
	   }
	   break;
	 case 'O': /* AIS; for GERUCOME and operand overloading */
	   if(gerucomesused || nextfromsused)
	     fprintf(ofp,"unsigned truelineno = 0;\n");
	   if(opoverused)
	     fprintf(ofp,"%s trueval;\n",
		     pickcompile?"ICK_INT32":"ick_type32");
	   break;
	 case 'P': /* AIS: for operand overloading */
	   if(opoverused)
	     emitslatproto(ofp);
	   break;
	 case 'Q': /* AIS: for operand overloading */
	   if(opoverused)
	     emitslat(ofp);
	   break;
	 }
}


/**
 * This runs the C compiler, and may invoke yuk.
 * @param cc_command The compiler command line to use. Constructed by gen_cc_command().
 * @param oldstdin The previous stdin, used for yuk.
 * @param yukcmdstr The command line to use for running yuk.
 * @param sourcefile The output filename.
 * @param binaryname The name of the binary.
 */
static void run_cc_and_maybe_debugger(/*@observer@*/ const char *cc_command,
                                      int oldstdin,
                                      /*@observer@*/ const char *yukcmdstr,
                                      /*@observer@*/ const char *sourcefile,
                                      /*@observer@*/ const char *binaryname)
{
#ifndef __DJGPP__
  /* OK, now sic the C compiler on the results */
  if (!compile_only&&!yukdebug&&!yukprofile&&!useickec)
  {
    /* AIS: buf2 now assigned elsewhere so $L works */
    ICK_SYSTEM(cc_command);
    /* AIS: no unlink if cdebug */ if(!cdebug) (void) unlink(sourcefile);
  }
  else if(!compile_only&&!useickec)
  { /* AIS: run, then delete all output but yuk.out */
    /* Note that the output must be deleted for copyright
       reasons (so as not to GPL a non-GPL file automatically) */
    ICK_SYSTEM(cc_command);
#ifdef HAVE_UNISTD_H
    (void) dup2(oldstdin,0); /* restore stdin */
#endif
    ICK_SYSTEM(yukcmdstr);
    (void) unlink(sourcefile);
    (void) unlink(binaryname);
  }
#else /* we are using DJGPP */
  /* OK, now sic the C compiler on the results */
  if (!compile_only&&!useickec)
  {
    /* AIS: buf2 now assigned elsewhere so $L works */
    /* AIS: This changes somewhat for DJGPP, due to the
       command-line cap. It creates a temporary file
       with the arguments needed to give gcc. */
    FILE* rsp;
    /* Use current dir as temp if needed */
    const char* tempfn="gcc @ickgcc.rsp";
    /* Four tries are used to find a temp directory.
       ICKTEMP is the preferred environment variable to check;
       if, as expected, this isn't set, try TMPDIR (which DJGPP
       sets to its own temp directory, at least when running under
       bash), TEMP and TMP (in that order). DJGPP offers /dev/env
       as a method of accessing environment variables in filenames.*/
    if(isenv("TMP")) tempfn="gcc @/dev/env/TMP/ickgcc.rsp";
    if(isenv("TEMP")) tempfn="gcc @/dev/env/TEMP/ickgcc.rsp";
    if(isenv("TMPDIR")) tempfn="gcc @/dev/env/TMPDIR/ickgcc.rsp";
    if(isenv("ICKTEMP")) tempfn="gcc @/dev/env/ICKTEMP/ickgcc.rsp";
    rsp=ick_debfopen(tempfn+5,"w");
    fprintf(rsp,"%s\n",cc_command);
    fclose(rsp);
    ICK_SYSTEM(tempfn);
    remove(tempfn+5);
    if(yukdebug || yukprofile)
    {
      char buffer[BUFSIZ];
#ifdef HAVE_UNISTD_H
      dup2(oldstdin,0); /* restore stdin */
#endif
/* FIXME: This looks broken (the buf2 usage and such). */
      ick_snprintf_or_die(buffer, sizeof(buffer), "%s" EXEEXT,binaryname);
      ICK_SYSTEM(yukcmdstr);
      remove(sourcefile);
      remove(buffer);
    }
    else if(!cdebug)
    {
      remove(sourcefile);
    }
  }
#endif
}


/**
 * This runs coopt.sh if -F is given and the program can be "coopted".
 * @param cooptsh Path to coopt.sh
 * @param binaryname The output binary filename.
 */
static void run_coopt(/*@observer@*/ /*@null@*/ /*@unused@*/ const char* cooptsh,
                      /*@observer@*/ /*@unused@*/ const char* binaryname)
{
  /* Note: Params are marked unused because they may not be used if sh isn't supported. */
  /* Assume that sh exists if #! does; sh is needed to run autoconf, so this would
     otherwise have to be set by hand anyway. */
#ifdef HAVE_SYS_INTERPRETER
  if(coopt) /* AIS */
  {
    /* The constant-output optimizer is a form of post-processor.
       IMPORTANT NOTE: This MUST NOT be run if the input program
       takes any input or is affected in any way by the state of
       the system, as the degenerated program may be wrong. At the
       moment, the only INTERCAL command that takes input is
       WRITE IN. Double-oh-sevens screw this up, too. */
    if(cooptsh)
    {
      char commandlinebuf[BUFSIZ];
      (void) ick_snprintf_or_die(commandlinebuf, sizeof commandlinebuf,
	                         "sh %s %s", cooptsh, binaryname);
      ICK_SYSTEM(commandlinebuf); /* replaces the output executable if
	                             neccesary */
    }
  }
#endif
}


/**
 * This is for -e, runs prelinking.
 * @param argc Exactly what you think.
 * @param argv Also what you think.
 * @param oldoptind The original optind.
 * @param libdir The ick library directory.
 * @param includedir The ick include directory.
 * @param path The path of the ick binary (execuding filename).
 * @param libbuf A string with -lfoo to add to compiler command line.
 * @note May (directly) call ick_lose() with IE666. IE778 and IE888.
 */
static void prelink(int argc, char *argv[], int oldoptind,
                    /*@observer@*/ const char* libdir,
                    /*@observer@*/ const char *includedir,
                    /*@observer@*/ const char* path,
                    /*@observer@*/ const char* libbuf)
{
  char buffer[BUFSIZ];
  FILE* cioin;
  FILE* cioallec;
  char* buf2ptr;
  long remspace;
  const char* tempfn="ickectmp.c";
  int needc=89;
#if __DJGPP__
  /* Look for a temp directory, as above. */
  if(isenv("TMP")) tempfn="/dev/env/TMP/ickectmp.c";
  if(isenv("TEMP")) tempfn="/dev/env/TEMP/ickectmp.c";
  if(isenv("TMPDIR")) tempfn="/dev/env/TMPDIR/ickectmp.c";
  if(isenv("ICKTEMP")) tempfn="/dev/env/ICKTEMP/ickectmp.c";
#else
  tempfn="/tmp/ickectmp.c"; /* always a valid temporary folder on POSIX */
#endif
  cioallec=ick_debfopen(tempfn,"w");
  if(cioallec == NULL)
    ick_lose(IE888, -1, (const char*) NULL);
  (void) fprintf(cioallec,"void ick_doresume(unsigned short,int);\n");
  (void) fprintf(cioallec,"extern int ick_global_checkmode;\n");
  (void) fprintf(cioallec,"void ick_allecfuncs(void)\n{\n");

  /* Here, we run the C preprocessor on the files in question, then our
     own preprocessor, and finally link all the files together into one
     executable. */
  for(optind=oldoptind; optind < argc; optind++)
  {
    int thisneedc = argv[optind][strlen(argv[optind])+2]=='9'?99:
      argv[optind][strlen(argv[optind])+2]=='1'?11:89;
    if(needc==89 || thisneedc==11)
      needc=thisneedc;
    (void) ick_snprintf_or_die(buffer, sizeof buffer,
			       "%s --std=c%d -E -DICK_HAVE_STDINT_H=%d "
			       "-I%s -I%s -I%s/../include "
			       "-x c %s.c%c%c > %s.cio",
			       compiler, thisneedc,
			       ICK_HAVE_STDINT_H+1-1,
			       includedir, path, path, argv[optind],
                               thisneedc==99?'9':thisneedc==11?'1':' ',
                               thisneedc==99?'9':thisneedc==11?'1':' ',
			       argv[optind]);
    if(*(argv[optind]) && /* there is some file to compile */
       (argv[optind][strlen(argv[optind])+2]=='\0' /* a .c or .i file */
        ||argv[optind][strlen(argv[optind])+3]!='o')) /* not a .cio file */
      ICK_SYSTEM(buffer); /* run the C preprocessor */
    buf2ptr = strrchr(buffer,'>'); /* get the .cio's filename */
    cioin=NULL;
    /* Do our preprocessing, by editing the file in place using rb+. */
    if(buf2ptr != NULL && buf2ptr[1] != '\0' && buf2ptr[2] != '\0')
      cioin=ick_debfopen(buf2ptr+2,"rb+");
    if(cioin)
    {
      int inchar=fgetc(cioin);
      int toparencount=0;
      /* The ppnums are replacements for strings in the .cio file.
	 The choice of 65538 means that we don't clash with any
	 line numbers in the program, but do clash with the other
	 C-INTERCAL preprocessor (that handles WHILE); that isn't a
	 problem because external calls are inconsistent with
	 multithreading anyway. */
      static long ppnum1=65538L*2L;
      static long ppnum2=65538L*2L;
      static long ppnum3=65538L*2L;
      static long ppnum6=65538L*2L;
      long ciopos=0L;
      /*@+charintliteral@*/ /* literal chars are ints */
      while(inchar != EOF)
      {
	if(inchar=='I')
	{
	  /* Look for the ICK_EC_PP_ string that indicates preprocessing
	     is needed. This method of doing it works as long as the
	     ICK_EC_PP_ string is never preceded by something which looks
	     like part of the same string, but luckily, it never is. */
	  if((inchar=fgetc(cioin))!='C') continue;
	  if((inchar=fgetc(cioin))!='K') continue;
	  if((inchar=fgetc(cioin))!='_') continue;
	  if((inchar=fgetc(cioin))!='E') continue;
	  if((inchar=fgetc(cioin))!='C') continue;
	  if((inchar=fgetc(cioin))!='_') continue;
	  if((inchar=fgetc(cioin))!='P') continue;
	  if((inchar=fgetc(cioin))!='P') continue;
	  if((inchar=fgetc(cioin))!='_') continue;
	  inchar=fgetc(cioin);
	  toparencount=0;
	  if(inchar=='0')
	  {
	    fprintf(cioallec,"#undef X\n");
	    fprintf(cioallec,"#define X ");
	    while(fputc(fgetc(cioin),cioallec) != ')') toparencount++;
	  }
	  (void) fseek(cioin,ciopos,SEEK_SET);
	  switch(inchar)
	  {
	  case '0': /* a function exists */
	    fprintf(cioin,"            ");
	    fprintf(cioallec,"\nvoid X(void); X();\n"
		    "if(ick_global_checkmode==5) ick_doresume(1,-1);\n");
	    while(toparencount--) (void) fputc(' ',cioin);
	    break;
	  case '1':
	    fprintf(cioin,"%-11ld",ppnum1++/2);
	    break;
	  case '2':
	    fprintf(cioin,"%-11ld",ppnum2++/2);
	    break;
	  case '3':
	    fprintf(cioin,"%-11ld",ppnum3++/2);
	    break;
	  case '4':
	    fprintf(cioin,"%-11d",optind);
	    break;
	  case '6':
	    fprintf(cioin,"%-11ld",ppnum6++/2);
	    break;
	  default:
	    ick_lose(IE778, -1, (const char*) NULL);
	  }
	  (void) fseek(cioin,0L,SEEK_CUR); /* synch the file */
	}
	ciopos=ftell(cioin);
	inchar=fgetc(cioin);
      }
      /*@=charintliteral@*/
      (void) fclose(cioin);
    }
  }
  fprintf(cioallec,"if(ick_global_checkmode==2)\n");
  fprintf(cioallec,"  ick_global_checkmode=4;\n");
  fprintf(cioallec,"};\n");
  (void) fclose(cioallec);

  /* NOTE: buffer changes use around here. */

  /* This command line needs some explanation, and is specific to gcc and
     GNU ld. The -x causes gcc to interpret the .cio files as C; the
     -Wl,-z,muldefs is an instruction to GNU ld, telling it to link in the
     first main found and ignore the others.  (That way, there can be a
     main function in each .cio, but the .cios can be linked in any order,
     with the right main function foremost each time.)
   */
  (void) ick_snprintf_or_die(buffer, sizeof buffer,
			     "%s -L%s -L%s -L%s/../lib -O2 -o %s" EXEEXT "%s "
#ifndef __DJGPP__
			     "-Wl,-z,muldefs "
#endif
			     "-DICK_HAVE_STDINT_H=%d -x c --std=c%d %s", compiler, libdir,
			     path, path, argv[oldoptind], cdebug?" -g":"", ICK_HAVE_STDINT_H+1==2?1:0,
			     needc,tempfn);
  remspace = (long)(sizeof buffer - strlen(buffer) - 1);
  for(optind=oldoptind; optind < argc; optind++)
  {
    if(!*(argv[optind])) continue;
    remspace -= strlen(argv[optind]) - 5; /* 5 for <space>.cio */
    if(remspace <= 0)
      ick_lose(IE666, -1, (const char*)NULL);
    strcat(buffer," ");
    strcat(buffer,argv[optind]);
    strcat(buffer,".cio");
  }
  remspace -= strlen(libbuf);
  if(remspace <= 0)
    ick_lose(IE666, -1, (const char*)NULL);
  strcat(buffer,libbuf);
  remspace -= strlen(" -lickec");
  if(remspace <= 0)
    ick_lose(IE666, -1, (const char*)NULL);
  strcat(buffer," -lickec");
  ICK_SYSTEM(buffer);
  (void) remove(tempfn);
}


/*@-redef@*/
int main(int argc, char *argv[])
/*@=redef@*/
{
  char	buf[BUFSIZ], buf2[BUFSIZ], *chp, yukcmdstr[BUFSIZ], path[BUFSIZ],
    libbuf[BUFSIZ];
  /*@-shadow@*/ /* no it doesn't, cesspool isn't linked to perpet */
  const char	*includedir, *libdir, *ick_sysdir, *ick_cskeldir;
  /*@=shadow@*/
  /* AIS: removed getenv(), added ick_sysdir */
  const char        *cooptsh; /* AIS */
  FILE	*ifp, *ofp;
  int		/* nextcount, AIS */ bugline;
  bool        needsyslib, firstfile;
  int         oldoptind;
#ifdef HAVE_UNISTD_H
  int         oldstdin; /* AIS: for keeping track of where stdin was */
#endif
  if (!(includedir = getenv("ICKINCLUDEDIR")))
    includedir = ICKINCLUDEDIR;
  if (!(libdir = getenv("ICKLIBDIR")))
    libdir = ICKLIBDIR;
  if (!(ick_sysdir = getenv("ICKSYSDIR")))
    ick_sysdir = ICKSYSDIR;
  if (!(ick_cskeldir = getenv("ICKCSKELDIR")))
    ick_cskeldir = ICKCSKELDIR;
/*
  AIS: nothing actually uses this at the moment,
  commenting it out for future use

  if (!(bindir = getenv("ICKBINDIR")))
  bindir = ICKBINDIR;
*/
  if (!(compiler = getenv("CC")))
    compiler = CC;

  /* Parse the options. */
  parse_options(argc, argv);

  (void) signal(SIGSEGV, abend);
#ifdef SIGBUS
  (void) signal(SIGBUS, abend);
#endif /* SIGBUS */

  if (!nocompilerbug) {
#ifdef USG
    srand48(time(NULL) + getpid());
#else
    srand((unsigned)time(NULL));
#endif /* UNIX */
  }

  /* AIS: New function for enhanced file-finding */
  ifp = ick_findandfopen(pickcompile?PSKELETON:SKELETON,
			 ick_cskeldir, "r", argv[0]);
  if(!ifp) ick_lose(IE999, 1, (const char *)NULL);

  /* now substitute in tokens in the skeleton */

  /* AIS: This doesn't actually seem to do anything, and buf is
     uninitialised at this point, so it's actually dangerous
     because it's undefined behaviour.
     buf[strlen(buf) - 2] = '\0'; */

  /* AIS: Save the old stdin, if we can */
#ifdef HAVE_UNISTD_H
  oldstdin=dup(0);
#endif

  oldoptind=optind; /* AIS */
  *libbuf = '\0'; /* AIS */
  /* Begin file loop */
  for (firstfile = true; optind < argc; optind++, firstfile = false)
  {
    /* AIS: Read as binary to pick up Latin-1 and UTF-8 better */
    if (/* AIS */ strrchr(argv[optind],'.') != NULL &&
	freopen(argv[optind], "rb", stdin) == (FILE *)NULL &&
	/* AIS */ strcmp(strchr(argv[optind],'.')+1,"a"))
	ick_lose(IE777, 1, (const char *)NULL);
    else
    {
      /* strip off the file extension */
      if(!(chp = strrchr(argv[optind],'.')))
      {
	if(useickec && firstfile == false) /* By AIS */
	{
	  /* the filename indicates a request for an expansion library,
	     along the same lines as CLC-INTERCAL's preloads. Search for
	     it in the usual places, then make a copy in a temp directory
	     and substitute that on the command line. */
	  const char* tempfn;
	  FILE* fromcopy;
	  FILE* tocopy;
	  int c2;
	fixexpansionlibrary:
	  tempfn="%s.c";
	  (void) ick_snprintf_or_die(buf2, sizeof buf2, "%s.c", argv[optind]);
	  fromcopy = ick_findandfopen(buf2,ick_cskeldir,"rb",argv[0]);
	  if(!fromcopy) /* same error as for syslib */
	    ick_lose(IE127, 1, (const char*) NULL);
#if __DJGPP__
	  /* Look for a temp directory to store a copy of the C file,
	     the resulting .cio, .o files, etc. */
	  if(isenv("TMP")) tempfn="/dev/env/TMP/%s.c";
	  if(isenv("TEMP")) tempfn="/dev/env/TEMP/%s.c";
	  if(isenv("TMPDIR")) tempfn="/dev/env/TMPDIR/%s.c";
	  if(isenv("ICKTEMP")) tempfn="/dev/env/ICKTEMP/%s.c";
#else
	  tempfn="/tmp/%s.c"; /* always valid on POSIX */
#endif
	  /*@-formatconst@*/ /* all possibilities are fine */
	  (void) ick_snprintf_or_die(buf2, sizeof buf2, tempfn, argv[optind]);
	  /*@=formatconst@*/
	  if((tocopy = fopen(buf2,"wb")) == NULL)
	    ick_lose(IE888, 1, (const char*) NULL);

	  for(;;)
	  {
	    c2=fgetc(fromcopy);
	    if(c2==EOF) break;
	    (void) fputc(c2,tocopy);
	  }
	  (void) fclose(fromcopy); (void) fclose(tocopy);
	  /*@+onlytrans@*/
	  /* this is a memory leak that will need sorting out later,
	     thus the explicit turn-warning-on */
	  argv[optind]=malloc(sizeof(buf2)+1);
	  /*@=onlytrans@*/
	  if(!(argv[optind]))
	    ick_lose(IE888, 1, (const char*) NULL);
	  strcpy(argv[optind],buf2);
	  *(strrchr(argv[optind],'.')) = '\0';
	  continue;
	}

	ick_lose(IE998, 1, (const char *)NULL);
      }
      *chp++ = '\0';

      /* Beginning of block that figures out file type from extension. */
      if(useickec && (!strcmp(chp,"c") || !strcmp(chp,"cio") ||
		      !strcmp(chp,"c99") || !strcmp(chp,"c11"))) /* AIS */
      {
	if(firstfile != false) /* need exactly 1 INTERCAL file */
	  ick_lose(IE998, 1, (const char *)NULL);
	continue; /* don't process C or cio files further yet */
      }

      if(useickec && !strcmp(chp,"a"))
      {
	/* AIS: request for a library. Given a filename of the form
	   libwhatever.a, it adds  -lwhatever to libbuf (that's with
	   a preceding space). If the filename doesn't start with lib,
	   it instead adds a space and the filename to libbuf. */
	handle_archive(libbuf, sizeof libbuf,
	               argv[optind] /* Archive name without extension. */);
	*argv[optind]='\0';
	continue;
      }

      if(useickec && !strcmp(chp,"b98"))
      {
	handle_befunge98(libbuf, sizeof libbuf, libdir, argv[0],
	                 argv[optind] /* Filename without extension. */);
	/* Sort out the ecto_b98 expansion library. */
	argv[optind] = "ecto_b98";
	goto fixexpansionlibrary;
      }

      if(useickec && firstfile == false) /* AIS */
	ick_lose(IE998, 1, (const char *)NULL);

      /* determine the file type from the extension */
      /* AN: chp isn't used again after this it seems? */
      find_intercal_base(chp);

      /* End of block that figures out file type from extension. */

      /* zero out tuple and oblist storage */
      treset();
      politesse = 0;
      /* JH: default to no op-overusage and no computed come from */
      opoverused = false;
      compucomesused = false;
      compucomecount = 0;
      gerucomesused = false; /* AIS: you forgot this one */
      /* AIS: ensure that at least one variable exists, to prevent
	 NULL pointers later on */
      (void) intern(ick_ONESPOT, 1); /* mention .1 */

      /* reset the lex/yacc environment */
      if (!firstfile)
      {
#ifdef NEED_YYRESTART
	yyrestart(stdin);
#endif /* NEED_YYRESTART */
	iyylineno = 1;
      }

      /* compile tuples from current input source */
      (void) yyparse();

      if(variableconstants)
      {
	/* AIS: Up to 4 extra meshes may be needed by feh.c. */
	(void) intern(MESH, 0xFFFFFFFFLU);
	(void) intern(MESH, 0xFFFFLU);
	(void) intern(MESH, 0xAAAAAAAALU);
	(void) intern(MESH, 0x55555555LU);
      }


      /*
       * Miss Manners lives.
       */
      if (ick_lineno > 2)
      {
	if (politesse == 0 || (ick_lineno - 1) / politesse >= 5)
	  ick_lose(IE079, iyylineno, (const char *)NULL);
	else if (ick_lineno / politesse < 3)
	  ick_lose(IE099, iyylineno, (const char *)NULL);
      }

      /* Check if we should auto add the system library. */
      check_syslib(buf2, sizeof buf2, &needsyslib, argv[0], ick_sysdir);

      /*
       * Now propagate type information up the expression tree.
       * We need to do this because the unary-logical operations
       * are sensitive to the type widths of their operands, so
       * we have to generate different code depending on the
       * deducible type of the operand.
       */
      propagate_typeinfo();

      codecheck();	/* check for compile-time errors */
      /* AIS: And importantly, sort out line number references */
      run_optimiser();

      /* decide if and where to place the compiler bug */
      bugline = randomise_bugline();

      /* set up the generated C output file name */
      (void) ick_snprintf_or_die(buf, sizeof buf, "%s.c", argv[optind]);
      /* Open output file. */
      ofp = open_outfile(buf /* Output filename */);

      (void) fseek(ifp,0L,0);	/* rewind skeleton file */

      /* AIS: Before changing argv[0], locate coopt.sh. */
      /* AN: Even though argv[0] isn't changed any more this breaks if moved out
       * of the per-file loop since ick_findandtestopen() returns a pointer to a
       * static buffer. Should be fixed.
       */
      cooptsh = ick_findandtestopen("coopt.sh", ick_sysdir, "rb", argv[0]);
      /* AIS: and calculate yukcmdstr. */
      (void) ick_snprintf_or_die(yukcmdstr, sizeof yukcmdstr, "%s%s" EXEEXT " %s %s",
				 strchr(argv[optind],'/')||strchr(argv[optind],'\\')?
				 "":"./",argv[optind],ick_sysdir,argv[0]);

      /* AIS: Remove the filename from argv[0], leaving only a directory.
	 If this would leave it blank, change argv[0] to '.'.
	 This is so gcc can find the includes/libraries the same way that
	 ick_findandfreopen does. */
      /* JH: use a copy of argv[0] for the path, to ensure argv[0] is
       * available for the next round
       */
      strcpy(path,argv[0]);
      if(strchr(path,'/')) *(strrchr(path,'/')) = '\0';
      else strcpy(path,".");

      /* Generate the compiler command. */
      gen_cc_command(buf2 /* output */, sizeof buf2, buf /* Source filename. */,
		     includedir, path, libdir, argv[optind] /* Output binary filename. */);

      textlinecount=0; /* AIS: If there are no files, there's
			  no need to free any textlines */
      /* Generate code using ick-wrap.c (or pickwrap.c) */
      generate_code(ifp, ofp, argv[optind] /* Source file name stem. */,
                    &needsyslib, bugline, buf2 /* CC command. */);

      if(!outtostdout) (void) fclose(ofp);

      /* OK, now sic the C compiler on the results */
      /* Also: if -y was given, run debugger */
      run_cc_and_maybe_debugger(buf2, oldstdin, yukcmdstr, buf /* C file name */,
                                argv[optind] /* Binary filename */);

      /* Run the constant-output optimizer (a form of post-processor). */
      run_coopt(cooptsh, argv[optind]);

    }
  }
  /* Here ends the per-file loop. */
  (void) fclose(ifp);

  if(!compile_only && useickec) /* AIS */
    prelink(argc, argv, oldoptind, libdir, includedir, path, libbuf);

  /* AIS: Free malloc'd memory. */
  if(textlines)
  {
    /* Marking what textlines points to as only would be the 'right'
       way to do this (because it is only), but I can't figure out the
       syntax to do it, so instead I'm supressing the warning that comes
       up because it isn't marked as only. */
    /*@-unqualifiedtrans@*/
    while(textlinecount--) free(textlines[textlinecount]);
    free(textlines);
    /*@=unqualifiedtrans@*/
  }

#ifdef HAVE_UNISTD_H
  (void) close(oldstdin); /* AIS */
#endif
  /* This point is the very end of the program. So it's correct for
     normal DO NOT FREE UNDER ANY CIRCUMSTANCES globals to be free
     at this point, so supressing the warning given as a result. */
  /*@-globstate@*/
  return 0;
  /*@=globstate@*/
}

/* perpet.c ends here */
