#define CONFIGURE_VERSION	5

#define EDIT_SOURCE
#define NO_MALLOC
#define NO_SOCKETS
#define NO_OPCODES
#include "std.h"
#include "lex.h"
#include "preprocess.h"
#include "make_func.h"
#include "cc.h"
#include "hash.h"

#ifdef WIN32
#include <process.h>
#include <malloc.h>
#endif

#if defined(DEBUG) || defined(WIN32)
#define TO_DEV_NULL ""
#else
#define TO_DEV_NULL ">/dev/null 2>&1"
#endif

/* Using an include file at this point would be bad */
#ifdef PEDANTIC
char *malloc(int);
char *realloc(char *, int);
void free(char *);
#endif

char *outp;
static int buffered = 0;
static int nexpands = 0;

FILE *yyin = 0, *yyout = 0;

#define SYNTAX "edit_source [-process file] [-options] [-malloc] [-build_func_spec 'command'] [-build_efuns] [-configure]\n"

/* The files we fool with.  (Actually, there are more.  See -process).
 *
 * TODO: teach this file how to fix bugs in the source code :)
 */
#define OPTIONS_INCL      "options_incl.h"
#define PACKAGES          "packages/packages"
#define OPTIONS_H         "options.h"
#define LOCAL_OPTIONS     "local_options"
#define OPTION_DEFINES    "option_defs.c"
#define FUNC_SPEC         "func_spec.c"
#define FUNC_SPEC_CPP     "func_spec.cpp"
#define EFUN_TABLE        "efunctions.h"
#define OPC_PROF          "opc.h"
#define OPCODES           "opcodes.h"
#define EFUN_PROTO        "efun_protos.h"
#define EFUN_DEFS         "efun_defs.c"

#define PRAGMA_NOTE_CASE_START 1

int num_packages = 0;
char *packages[100];
char ppchar;

char *current_file = 0;
int current_line;

int grammar_mode = 0; /* which section we're in for .y files */
int in_c_case, cquote, pragmas, block_nest;

char yytext[MAXLINE];
static char defbuf[DEFMAX];

typedef struct incstate_t {
    struct incstate_t *next;
    FILE *yyin;
    int line;
    char *file;
} incstate;

static incstate *inctop = 0;

#define CHAR_QUOTE 1
#define STRING_QUOTE 2

static void add_define PROT((char *, int, char *));

#ifdef WIN32
#include <io.h>

int compile(char *command) {
   FILE *tf = fopen("trash_me.bat","wt+");
   
   fprintf(tf,"%s%s\n%s",
	"@echo off\n",
      command,
      "if errorlevel == 1 goto error\n"
      "del trash_me.err >nul\n"
      "goto ok\n"
      ":error\n"
      "echo ERROR > trash_me.err\n"
      ":ok\n");
   fclose(tf);

   if (!system("trash_me.bat > nul")) return 1;
   if (_access("trash_me.err",0) ) return 1;
   return 0;
}
#else
int compile P1(char *, str) {
    return system(str);
}
#endif

#if defined(WIN32) || defined(LATTICE)
int dos_style_link P2(char *, x, char *, y) {
    char link_cmd[100];
    sprintf(link_cmd, "copy %s %s", x, y);
    return system(link_cmd);
}
#endif

void yyerror P1(char *, str)
{
    fprintf(stderr, "%s:%d: %s\n", current_file, current_line, str);
    exit(1);
}

void mf_fatal P1(char *, str)
{
    yyerror(str);
}

void yywarn P1(char *, str)
{
    /* ignore errors :)  local_options generates redefinition warnings,
       which we don't want to see */
}

void yyerrorp P1(char *, str)
{
    char buff[200];
    sprintf(buff, str, ppchar);
    fprintf(stderr, "%s:%d: %s\n", current_file, current_line, buff);
    exit(1);
}

static void add_input P1(char *, p)
{
    int l = strlen(p);

    if (outp - l < defbuf) yyerror("Macro expansion buffer overflow.\n");
    strncpy(outp - l, p, l);
    outp -= l;
}

#define SKIPW(foo) while (isspace(*foo)) foo++;

static char *skip_comment(tmp, flag)
     char *tmp;
     int flag;
{
    int c;
    
    for (;;) {
	while ((c = *++tmp) !=  '*') {
	    if (c == EOF) yyerror("End of file in a comment");
            if (c == '\n') {
                nexpands = 0;
                current_line++;
		if (!fgets(yytext, MAXLINE - 1, yyin)) yyerror("End of file in a comment");
		if (flag && yyout) fputs(yytext, yyout);
		tmp = yytext - 1;
	    }
	}
	do {
	    if ((c = *++tmp) == '/')
                return tmp + 1;
            if (c == '\n') {
                nexpands = 0;
                current_line++;
		if (!fgets(yytext, MAXLINE - 1, yyin)) yyerror("End of file in a comment");
		if (flag && yyout) fputs(yytext, yyout);
		tmp = yytext - 1;
	    }
	} while (c == '*');
    }
}

static void refill()
{
    register char *p, *yyp;
    int c;

    if (fgets(p = yyp = defbuf + (DEFMAX >> 1), MAXLINE - 1, yyin)) {
      while (((c = *yyp++) != '\n') && (c != EOF)) {
          if (c == '/') {
              if ((c = *yyp) == '*') {
                  yyp = skip_comment(yyp, 0);
                  continue;
              }
              else if (c == '/') break;
          }
          *p++ = c;
      }
    }
    else yyerror("End of macro definition in \\");
    nexpands = 0;
    current_line++;
    *p = 0;
    return;
}

static void handle_define()
{
    char namebuf[NSIZE];
    char args[NARGS][NSIZE];
    char mtext[MLEN];
    char *end;
    register char *tmp = outp, *q;

    q = namebuf;
    end = q + NSIZE - 1;
    while (isalunum(*tmp)) {
	if (q < end) *q++ = *tmp++;
	else yyerror("Name too long.\n");
    }
    if (q == namebuf) yyerror("Macro name missing.\n");
    if (*namebuf != '_' && !isalpha(*namebuf)) yyerror("Invalid macro name.\n");
    *q = 0;
    if (*tmp == '(') {            /* if "function macro" */
        int arg;
        int inid;
        char *ids = (char *) NULL;

        tmp++;                    /* skip '(' */
        SKIPW(tmp);
        if (*tmp == ')') {
            arg = 0;
	} else {
            for (arg = 0; arg < NARGS;) {
                end = (q = args[arg]) + NSIZE - 1;
		while (isalunum(*tmp) || (*tmp == '#')) {
		    if (q < end) *q++ = *tmp++;
		    else yyerror("Name too long.\n");
		}
		if (q == args[arg]) {
		    char buff[200];
		    sprintf(buff, "Missing argument %d in #define parameter list", arg + 1);
		    yyerror(buff);
		}
                arg++;
                SKIPW(tmp);
                if (*tmp == ')')
                    break;
                if (*tmp++ != ',') {
                    yyerror("Missing ',' in #define parameter list");
		}
                SKIPW(tmp);
	    }
            if (arg == NARGS) yyerror("Too many macro arguments");
	}
        tmp++;                    /* skip ')' */
	end = mtext + MLEN - 2;
        for (inid = 0, q = mtext; *tmp;) {
            if (isalunum(*tmp)) {
                if (!inid) {
                    inid++;
                    ids = tmp;
		}
	    } else {
                if (inid) {
                    int idlen = tmp - ids;
                    int n, l;

                    for (n = 0; n < arg; n++) {
                        l = strlen(args[n]);
                        if (l == idlen && strncmp(args[n], ids, l) == 0) {
                            q -= idlen;
                            *q++ = MARKS;
                            *q++ = n + MARKS + 1;
                            break;
			}
		    }
                    inid = 0;
		}
	    }
            if ((*q = *tmp++) == MARKS) *++q = MARKS;
            if (q < end) q++;
            else yyerror("Macro text too long");
            if (!*tmp && tmp[-2] == '\\') {
                q -= 2;
                refill();
		tmp = defbuf + (DEFMAX >> 1);
	    }
	}
        *--q = 0;
        add_define(namebuf, arg, mtext);
    } else if (isspace(*tmp) || (!*tmp && (*(tmp+1) = '\0', *tmp = ' '))) {
	end = mtext + MLEN - 2;
        for (q = mtext; *tmp;) {
            *q = *tmp++;
            if (q < end) q++;
            else yyerror("Macro text too long");
            if (!*tmp && tmp[-2] == '\\') {
                q -= 2;
                refill();
		tmp = defbuf + (DEFMAX >> 1);
	    }
	}
        *q = 0;
        add_define(namebuf, -1, mtext);
    } else {
        yyerror("Illegal macro symbol");
    }
    return;
}

#define SKPW while (isspace(*outp)) outp++

static int cmygetc() {
    int c;

    for (;;) {
      if ((c = *outp++) == '/') {
          if ((c = *outp) == '*') outp = skip_comment(outp, 0);
          else if (c == '/') return -1;
          else return c;
      } else return c;
    }
}

/* Check if yytext is a macro and expand if it is. */
static int expand_define()
{
    defn_t *p;
    char expbuf[DEFMAX];
    char *args[NARGS];
    char buf[DEFMAX];
    char *q, *e, *b;

    if (nexpands++ > EXPANDMAX) yyerror("Too many macro expansions");
    if (!(p = lookup_define(yytext))) return 0;
    if (p->nargs == -1) {
        add_input(p->exps);
    } else {
        int c, parcnt = 0, dquote = 0, squote = 0;
        int n;

        SKPW;
        if (*outp++ != '(') yyerror("Missing '(' in macro call");
        SKPW;
        if ((c = *outp++) == ')')
            n = 0;
        else {
            q = expbuf;
            args[0] = q;
            for (n = 0; n < NARGS;) {
                switch (c) {
                case '"':
                    if (!squote)
                        dquote ^= 1;
                    break;
                case '\'':
                    if (!dquote)
                        squote ^= 1;
                    break;
                case '(':
                    if (!squote && !dquote)
                        parcnt++;
                    break;
                case ')':
                    if (!squote && !dquote)
                        parcnt--;
                    break;
                case '#':
                    if (!squote && !dquote) {
                        *q++ = c;
                        if (*outp++ != '#') yyerror("'#' expected");
                  }
                    break;
                case '\\':
                    if (squote || dquote) {
                        *q++ = c;
                        c = *outp++;
                  } break;
                case '\n':
                    if (squote || dquote) yyerror("Newline in string");
                  break;
              }
                if (c == ',' && !parcnt && !dquote && !squote) {
                    *q++ = 0;
                    args[++n] = q;
              } else if (parcnt < 0) {
                    *q++ = 0;
                    n++;
                    break;
              } else {
                    if (c == EOF) yyerror("Unexpected end of file");
                    if (q >= expbuf + DEFMAX - 5) {
                        yyerror("Macro argument overflow");
                  } else {
                        *q++ = c;
                  }
              }
                if (!squote && !dquote) {
                    if ((c = cmygetc()) < 0) yyerror("End of macro in // comment");
              }
                else c = *outp++;
          }
            if (n == NARGS) {
                yyerror("Maximum macro argument count exceeded");
                return 0;
          }
      }
        if (n != p->nargs) {
            yyerror("Wrong number of macro arguments");
            return 0;
      }
        /* Do expansion */
        b = buf;
        e = p->exps;
        while (*e) {
            if (*e == '#' && *(e + 1) == '#')
                e += 2;
            if (*e == MARKS) {
                if (*++e == MARKS)
                    *b++ = *e++;
                else {
                    for (q = args[*e++ - MARKS - 1]; *q;) {
                        *b++ = *q++;
                        if (b >= buf + DEFMAX) yyerror("Macro expansion overflow");
                  }
              }
          } else {
                *b++ = *e++;
                if (b >= buf + DEFMAX) yyerror("Macro expansion overflow");
          }
      }
        *b++ = 0;
        add_input(buf);
    }
    return 1;
}

static int exgetc()
{
    register char c, *yyp;

    SKPW;
    while (isalpha(c = *outp) || c == '_') {
      yyp = yytext;
      do {
          *yyp++ = c;
      } while (isalnum(c = *++outp) || (c == '_'));
      *yyp = '\0';
      if (!strcmp(yytext, "defined")) {
          /* handle the defined "function" in #/%if */
          SKPW;
          if (*outp++ != '(') yyerror("Missing ( after 'defined'");
          SKPW;
          yyp = yytext;
          if (isalpha(c = *outp) || c == '_') {
              do {
                  *yyp++ = c;
              } while (isalnum(c = *++outp) || (c == '_'));
              *yyp = '\0';
          }
          else yyerror("Incorrect definition macro after defined(\n");
          SKPW;
          if (*outp != ')') yyerror("Missing ) in defined");
          if (lookup_define(yytext))
              add_input("1 ");
          else
              add_input("0 ");
      } else {
          if (!expand_define())
              add_input("0 ");
          else SKPW;
      }
    }
    return c;
}

static int skip_to(token, atoken)
    char *token, *atoken;
{
    char b[20], *p, *end;
    int c;
    int nest;

    for (nest = 0;;) {
        if (!fgets(outp = defbuf + (DEFMAX >> 1), MAXLINE-1,yyin)) {
            yyerror("Unexpected end of file while skipping");
	}
        current_line++;
        if ((c = *outp++) == ppchar) {
	    while (isspace(*outp)) outp++;
	    end = b + sizeof b - 1;
            for (p = b; (c = *outp++) != '\n' && !isspace(c) && c != EOF;) {
		if (p < end) *p++ = c;
	    }
            *p = 0;
            if (!strcmp(b, "if") || !strcmp(b, "ifdef") || !strcmp(b, "ifndef")) {
                nest++;
	    } else if (nest > 0) {
                if (!strcmp(b, "endif"))
                    nest--;
	    } else {
                if (!strcmp(b, token)) {
		    *--outp = c;
                    add_input(b);
		    *--outp = ppchar;
		    buffered = 1;
                    return 1;
		} else if (atoken && !strcmp(b, atoken)) {
		    *--outp = c;
                    add_input(b);
		    *--outp = ppchar;
		    buffered = 1;
		    return 0;
		} else if (!strcmp(b, "elif")) {
		    *--outp = c;
                    add_input(b);
		    *--outp = ppchar;
		    buffered = 1;
                    return !atoken;
		}
	    }
	}
    }
}

#include "preprocess.c"

static int maybe_open_input_file P1(char *, fn) {
    if ((yyin = fopen(fn, "r")) == NULL) {
	return 0;
    }
    if (current_file) free((char *)current_file);
    current_file = (char *)malloc(strlen(fn) + 1);
    current_line = 0;
    strcpy(current_file, fn);
    return 1;
}

static void open_input_file P1(char *, fn) {
    if (!maybe_open_input_file(fn)) {
	perror(fn);
	exit(-1);
    }
}

static void open_output_file P1(char *, fn) {
    if ((yyout = fopen(fn, "w")) == NULL) {
	perror(fn);
	exit(-1);
    }
}

static void close_output_file() {
    fclose(yyout);
    yyout = 0;
}

static char *protect P1(char *, p) {
    static char buf[1024];
    char *bufp = buf;

    while (*p) {
	if (*p=='"' || *p == '\\') *bufp++ = '\\';
	*bufp++ = *p++;
    }
    *bufp = 0;
    return buf;
}

static void
create_option_defines() {
    defn_t *p;
    int count = 0;
    int i;

    fprintf(stderr, "Writing build options to %s ...\n", OPTION_DEFINES);
    open_output_file(OPTION_DEFINES);
    fprintf(yyout, "{\n");
    for (i = 0; i < DEFHASH; i++) {
	for (p = defns[i]; p; p = p->next) 
	    if (!(p->flags & DEF_IS_UNDEFINED)) {
		count++;
		fprintf(yyout, "  \"__%s__\", \"%s\",\n", 
			p->name, protect(p->exps));
		if (strncmp(p->name, "PACKAGE_", 8)==0) {
		    int len;
		    char *tmp, *t;
		    
		    len = strlen(p->name + 8);
		    t = tmp = (char *)malloc(len + 1);
		    strcpy(tmp, p->name + 8);
		    while (*t) {
			if (isupper(*t))
			    *t = tolower(*t);
			t++;
		    }
		    if (num_packages == 100) {
			fprintf(stderr, "Too many packages.\n");
			exit(-1);
		    }
		    packages[num_packages++] = tmp;
		}
	    }
    }
    fprintf(yyout,"};\n\n#define NUM_OPTION_DEFS %d\n\n", count);
    close_output_file();
}

static void deltrail() {
    register char *p;

    p = outp;
    while (*p && !isspace(*p) && *p != '\n') {
      p++;
    }
    *p = 0;
}

static void
handle_include P1(char *, name)
{
    char *p;
    static char buf[1024];
    FILE *f;
    incstate *is;

    if (*name != '"') {
        defn_t *d;

        if ((d = lookup_define(name)) && d->nargs == -1) {
            char *q;

            q = d->exps;
            while (isspace(*q))
                q++;
            handle_include(q);
      } else {
            yyerrorp("Missing leading \" in %cinclude");
      }
        return;
    }
    for (p = ++name; *p && *p != '"'; p++);
    if (!*p) yyerrorp("Missing trailing \" in %cinclude");

    *p = 0;
    if ((f = fopen(name, "r")) != NULL) {
        is = (incstate *)
            malloc(sizeof(incstate) /*, 61, "handle_include: 1" */);
        is->yyin = yyin;
        is->line = current_line;
        is->file = current_file;
        is->next = inctop;
        inctop = is;
        current_line = 0;
        current_file = (char *)malloc(strlen(name) + 1 /*, 62, "handle_include: 2" */);
        strcpy(current_file, name);
        yyin = f;
    } else {
        sprintf(buf, "Cannot %cinclude %s", ppchar, name);
        yyerror(buf);
    }
}

static void
handle_pragma P1(char *, name)
{
    if (!strcmp(name, "auto_note_compiler_case_start"))
        pragmas |= PRAGMA_NOTE_CASE_START;
    else if (!strcmp(name, "no_auto_note_compiler_case_start"))
        pragmas &= ~PRAGMA_NOTE_CASE_START;
    else if (!strncmp(name, "ppchar:", 7) && *(name + 8))
        ppchar = *(name + 8);
    else yyerrorp("Unidentified %cpragma");
}

static void
preprocess() {
    register char *yyp, *yyp2;
    int c;
    int cond;

    while (buffered ? (yyp = yyp2 = outp) : fgets(yyp = yyp2 = defbuf + (DEFMAX >> 1), MAXLINE-1, yyin)) {
	if (!buffered) current_line++;
	else buffered = 0;
	while (isspace(*yyp2)) yyp2++;
	if ((c = *yyp2) == ppchar) {
	    int quote = 0;
	    char sp_buf = 0, *oldoutp;

	    if (c == '%' && yyp2[1] == '%')
		grammar_mode++;
	    outp = 0;
	    if (yyp != yyp2) yyerrorp("Misplaced '%c'.\n");
	    while (isspace(*++yyp2));
	    yyp++;
	    for (;;) {
		if ((c = *yyp2++) == '"') quote ^= 1;
		else{
		    if (!quote && c == '/') {
			if (*yyp2 == '*') {
			    yyp2 = skip_comment(yyp2, 0);
			    continue;
			}
			else if (*yyp2 == '/') break;
		    }
		    if (!outp && isspace(c)) outp = yyp;
		    if (c == '\n' || c == EOF) break;
		}
		*yyp++ = c;
	    }
	    
	    if (outp) {
		if (yyout) sp_buf = *(oldoutp = outp);
		*outp++ = 0;
		while (isspace(*outp)) outp++;
	    }
	    else outp = yyp;
	    *yyp = 0;
	    yyp = defbuf + (DEFMAX >> 1) + 1;

	    if (!strcmp("define", yyp)) {
		handle_define();
	    } else if (!strcmp("if", yyp)) {
		cond = cond_get_exp(0);
		if (*outp != '\n') yyerrorp("Condition too complex in %cif");
		else handle_cond(cond);
	    } else if (!strcmp("ifdef", yyp)) {
		deltrail();
		handle_cond(lookup_define(outp) != 0);
	    } else if (!strcmp("ifndef", yyp)) {
		deltrail();
		handle_cond(!lookup_define(outp));
	    } else if (!strcmp("elif", yyp)) {
		handle_elif();
	    } else if (!strcmp("else", yyp)) {
		handle_else();
	    } else if (!strcmp("endif", yyp)) {
		handle_endif();
	    } else if (!strcmp("undef", yyp)) {
		defn_t *d;
		
		deltrail();
		if ((d = lookup_definition(outp))) {
		    d->flags |= DEF_IS_UNDEFINED;
		    d->flags &= ~DEF_IS_NOT_LOCAL;
		} else {
		    add_define(outp, -1, " ");
		    d = lookup_definition(outp);
		    d->flags |= DEF_IS_UNDEFINED;
		    d->flags &= ~DEF_IS_NOT_LOCAL;
		}
	    } else if (!strcmp("echo", yyp)) {
		fprintf(stderr, "echo at line %d of %s: %s\n", current_line, current_file, outp);
	    } else if (!strcmp("include", yyp)) {
		handle_include(outp);
	    } else if (!strcmp("pragma", yyp)) {
		handle_pragma(outp);
	    } else if (yyout) {
		if (!strcmp("line", yyp)) {
		    fprintf(yyout, "#line %d \"%s\"\n", current_line,
			    current_file);
		} else {
		    if (sp_buf) *oldoutp = sp_buf;
		    if (pragmas & PRAGMA_NOTE_CASE_START) {
			if (*yyp == '%') pragmas &= ~PRAGMA_NOTE_CASE_START;
		    }
		    fprintf(yyout, "%s\n", yyp-1);
		}
	    } else {
		char buff[200];
		sprintf(buff, "Unrecognised %c directive : %s\n", ppchar, yyp);
		yyerror(buff);
	    }
	}
	else if (c == '/') {
	    if ((c = *++yyp2) == '*') {
		if (yyout) fputs(yyp, yyout);
		yyp2 = skip_comment(yyp2, 1);
	    } else if (c == '/' && !yyout) continue;
	    else if (yyout) {
		fprintf(yyout, "%s", yyp);
	    }
	}
	else if (yyout) {
	    fprintf(yyout, "%s", yyp);
	    if (pragmas & PRAGMA_NOTE_CASE_START) {
		static int line_to_print;
		
		line_to_print = 0;
		
		if (!in_c_case) {
		    while (isalunum(*yyp2)) yyp2++;
		    while (isspace(*yyp2)) yyp2++;
		    if (*yyp2 == ':') {
			in_c_case = 1;
			yyp2++;
		    }
		}
		
		if (in_c_case) {
		    while ((c = *yyp2++)) {
			switch(c) {
			  case '{':
			    {
				if (!cquote && (++block_nest == 1))
				    line_to_print = 1;
				break;
			    }
			    
			  case '}':
			    {
				if (!cquote) {
				    if (--block_nest < 0) yyerror("Too many }'s");
				}
				break;
			    }
			    
			  case '"':
                            if (!(cquote & CHAR_QUOTE)) cquote ^= STRING_QUOTE;
                            break;
			    
			  case '\'':
                            if (!(cquote & STRING_QUOTE)) cquote ^= CHAR_QUOTE;
                            break;
			    
			  case '\\':
                            if (cquote && *yyp2) yyp2++;
                            break;
			    
			  case '/':
                            if (!cquote) {
                                if ((c = *yyp2) == '*') {
                                    yyp2 = skip_comment(yyp2, 1);
                                } else if (c == '/') {
                                    *(yyp2-1) = '\n';
                                    *yyp2 = '\0';
                                }
                            }
                            break;
			    
			  case ':':
                            if (!cquote && !block_nest)
                                yyerror("Case started before ending previous case with ;");
                            break;
			    
			  case ';':
                            if (!cquote && !block_nest) in_c_case = 0;
			}
		    }
		}
		
		if (line_to_print)
		    fprintf(yyout, "#line %d \"%s\"\n", current_line + 1,current_file);

	    }
	}
    }
    if (iftop) {
      ifstate_t *p = iftop;

      while (iftop) {
          p = iftop;
          iftop = p->next;
          free((char *)p);
      }
      yyerrorp("Missing %cendif");
    }
    fclose(yyin);
    free(current_file);
    current_file = 0;
    nexpands = 0;
    if (inctop) {
      incstate *p = inctop;

      current_file = p->file;
      current_line = p->line;
      yyin = p->yyin;
      inctop = p->next;
      free((char *) p);
      preprocess();
    } else yyin = 0;
}

void make_efun_tables()
{
#define NUM_FILES     5
    static char* outfiles[NUM_FILES] = { 
	EFUN_TABLE, OPC_PROF, OPCODES, EFUN_PROTO, EFUN_DEFS 
    };
    FILE *files[NUM_FILES];
    int i;
    
    fprintf(stderr, "Building efun tables ...\n");
    for (i = 0; i < NUM_FILES; i++) {
	files[i] = fopen(outfiles[i], "w");
	if (!files[i]) {
	    fprintf(stderr, "make_func: unable to open %s\n", outfiles[i]);
	    exit(-1);
	}
	fprintf(files[i], 
		"/*\n\tThis file is automatically generated by make_func.\n");
	fprintf(files[i],
		"\tdo not make any manual changes to this file.\n*/\n\n");
    }

    fprintf(files[0],"\n#include \"efun_protos.h\"\n\n");
    fprintf(files[0],"\ntypedef void (*func_t) PROT((void));\n\n");
    fprintf(files[0],"func_t efun_table[] = {\n");

    fprintf(files[1],"\ntypedef struct opc_s { char *name; int count; } opc_t;\n\n");
    fprintf(files[1],"opc_t opc_efun[] = {\n");

    fprintf(files[2], "\n/* operators */\n\n");
    for (i = 0; i < op_code; i++) {
	fprintf(files[2],"#define %-30s %d\n", oper_codes[i], i+1);
    }
    
    fprintf(files[2],"\n/* 1 arg efuns */\n#define BASE %d\n\n", op_code+1);
    for (i = 0; i < efun1_code; i++) {
	fprintf(files[0],"\tf_%s,\n", efun1_names[i]);
	fprintf(files[1],"{\"%s\", 0},\n", efun1_names[i]);
	fprintf(files[2],"#define %-30s %d\n", efun1_codes[i], i+op_code+1);
	fprintf(files[3],"void f_%s PROT((void));\n", efun1_names[i]);
    }

    fprintf(files[2],"\n/* efuns */\n#define ONEARG_MAX %d\n\n", efun1_code + op_code+1);
    for (i = 0; i < efun_code; i++) {
	fprintf(files[0],"\tf_%s,\n", efun_names[i]);
	fprintf(files[1],"{\"%s\", 0},\n", efun_names[i]);
	fprintf(files[2],"#define %-30s %d\n", efun_codes[i], i+op_code+efun1_code+1);
	fprintf(files[3],"void f_%s PROT((void));\n", efun_names[i]);
    }
    fprintf(files[0], "};\n");
    fprintf(files[1], "};\n");

    if (efun1_code + op_code >= 256) {
	fprintf(stderr, "You have way too many efuns.  Contact the MudOS developers if you really need this many.\n");
    }
    if (efun_code >= 256) {
	fprintf(stderr, "You have way too many efuns.  Contact the MudOS developers if you really need this many.\n");
    }
    fprintf(files[2],"\n/* efuns */\n#define NUM_OPCODES %d\n\n", efun_code + efun1_code + op_code);
    
    /* Now sort the main_list */
    for (i = 0; i < num_buff; i++) {
       int j;
       for (j = 0; j < i; j++)
	   if (strcmp(key[i], key[j]) < 0) {
	      char *tmp;
	      tmp = key[i]; key[i] = key[j]; key[j] = tmp;
	      tmp = buf[i]; buf[i] = buf[j]; buf[j] = tmp;
	   }
    }

    /* Now display it... */
    fprintf(files[4], "{\n");
    for (i = 0; i < num_buff; i++)
	fprintf(files[4], "%s", buf[i]);
    fprintf(files[4], "\n};\nint efun_arg_types[] = {\n");
    for (i=0; i < last_current_type; i++) {
	if (arg_types[i] == 0)
	    fprintf(files[4], "0,\n");
	else
	    fprintf(files[4], "%s,", ctype(arg_types[i]));
    }
    fprintf(files[4],"};\n");

    for (i=0; i < NUM_FILES; i++)
	fclose(files[i]);
}

static void handle_local_defines(int check) {
    defn_t *p;
    int i;
    int problem = 0;

    for (i = 0; i < DEFHASH; i++)
	for (p = defns[i]; p; p = p->next)
	    p->flags |= DEF_IS_NOT_LOCAL;

    /* undefine _OPTIONS_H_ so it doesn't get propagated to the mudlib
       or interfere with copies of options.h */
    if ((p = lookup_define("_OPTIONS_H_"))) {
	p->flags |= DEF_IS_UNDEFINED;
	p->flags &= ~DEF_IS_NOT_LOCAL;
    }
    if ((p = lookup_define("DEBUG")))
	p->flags &= ~DEF_IS_NOT_LOCAL;
	
    ppchar = '#';
    preprocess();
    
    if ((p = lookup_define("_OPTIONS_H_")))
	p->flags |= DEF_IS_UNDEFINED;

    if (!check)
	return;

    for (i = 0; i < DEFHASH; i++)
	for (p = defns[i]; p; p = p->next)
	    if (p->flags & DEF_IS_NOT_LOCAL) {
		fprintf(stderr, "No setting for %s in '%s'.\n",
			p->name, LOCAL_OPTIONS);
		problem = 1;
	    }

    if (problem) {
	fprintf(stderr, "\
***This local_options file appears to have been written for an\n\
***earlier version of the MudOS driver.  Please lookup the new options\n\
***(mentioned above) in the options.h file, decide how you would like them\n\
***set, and add those settings to the local_options file.\n");
	exit(-1);
    }
}

static void write_options_incl P1(int, local) {
    open_output_file(OPTIONS_INCL);
    if (local) {
	fprintf(yyout, "#include \"%s\"\n", LOCAL_OPTIONS);
    } else {
	fprintf(yyout, "#include \"%s\"\n", OPTIONS_H);
    }
    close_output_file();
}

static void handle_options(int full) {
    open_input_file(OPTIONS_H);
    ppchar = '#';
    preprocess();

    if (!full) {
	/* don't do any checking, just find out what is defined */
	if (maybe_open_input_file(LOCAL_OPTIONS))
	    handle_local_defines(0);
	return;
    }

    if (maybe_open_input_file(LOCAL_OPTIONS)) {
	fprintf(stdout, "Using '%s' file ...\n", LOCAL_OPTIONS);
	handle_local_defines(1);
	write_options_incl(1);
    } else {
	fprintf(stderr, "No \"%s\" file present.  If you create one from \"%s\",\nyou can use it when you get a new driver, and you will be warned if there are\nchanges to the real %s which you should include in your local file.\n",
		LOCAL_OPTIONS, OPTIONS_H, OPTIONS_H);
	write_options_incl(0);
    }

    create_option_defines();
}

static void handle_build_func_spec P1(char *, command) {
    char buf[1024];
    int i;

    fprintf(stderr, "Building compiler files ...\n");
    sprintf(buf, "%s %s >%s", command, FUNC_SPEC, FUNC_SPEC_CPP);
    system(buf);
    for (i = 0; i < num_packages; i++) {
	sprintf(buf, "%s -I. packages/%s_spec.c >>%s", 
		command, packages[i], FUNC_SPEC_CPP);
	system(buf);
    }

    open_output_file(PACKAGES);
    fprintf(yyout, "SRC=");
    for (i=0; i < num_packages; i++)
	fprintf(yyout, "%s.c ", packages[i]);
    fprintf(yyout, "\nOBJ=");
    for (i=0; i < num_packages; i++)
	fprintf(yyout, "%s.$(O) ", packages[i]);
    fprintf(yyout, "\n");
    close_output_file();
}

static void handle_process P1(char *, file) {
    char buf[1024];
    int l;

    strcpy(buf, file);
    l = strlen(buf);
    if (strcmp(buf + l - 4, ".pre")) {
	fprintf(stderr, "Filename for -process must end in .pre\n");
	exit(-1);
    }
    *(buf + l - 4) = 0;
    
    fprintf(stderr, "Creating '%s' from '%s' ...\n", buf, file);

#ifdef DEBUG
    /* pass down the DEBUG define from CFLAGS */
    add_define("DEBUG", -1, " ");
#endif

    open_input_file(file);
    open_output_file(buf);
    ppchar = '%';
    preprocess();
    close_output_file();
}

static void handle_build_efuns() {
    void yyparse();
    
    num_buff = op_code = efun_code = efun1_code = 0;

    open_input_file(FUNC_SPEC_CPP);
    yyparse();
    make_efun_tables();
}

static handle_applies() {
    FILE *f = fopen("applies", "r");
    FILE *out = fopen("applies.h", "w");
    FILE *table = fopen("applies_table.c", "w");
    char buf[8192];
    char *colon;
    char *p;
    int apply_number = 0;
    
    fprintf(out, "/* autogenerated from 'applies' */\n#ifndef APPLIES_H\n#define APPLIES_H\n\nextern char *applies_table[];\n\n/* the folowing must be the first character of __INIT */\n#define APPLY___INIT_SPECIAL_CHAR\t\t'#'\n");
    fprintf(table, "/* autogenerated from 'applies' */\n\nchar *applies_table[] = {\n");
    
    while (fgets(buf, 8192, f)) {
	buf[strlen(buf)-1] = 0;
	if (buf[0] == '#') break;
	if ((colon = strchr(buf, ':'))) {
	    *colon++ = 0;
	    fprintf(out, "#define APPLY_%-30s\t\"%s\"\n", buf, colon);
	} else {
	    fprintf(out, "#define APPLY_%-30s\t", buf);
	    p = buf;
	    while (*p) {
		*p = tolower(*p);
		p++;
	    }
	    fprintf(out, "\"%s\"\n", buf);
	}
    }
    while (fgets(buf, 8192, f)) {
	buf[strlen(buf)-1] = 0;
	if ((colon = strchr(buf, ':'))) {
	    *colon++ = 0;
	    fprintf(table, "\t\"%s\",\n", colon);
	    fprintf(out, "#define APPLY_%-30s\t%i\n", buf, apply_number++);
	} else {
	    fprintf(out, "#define APPLY_%-30s\t%i\n", buf, apply_number++);
	    p = buf;
	    while (*p) {
		*p = tolower(*p);
		p++;
	    }
	    fprintf(table, "\t\"%s\",\n", buf);
	}
    }
    
    fprintf(table, "};\n");
    fprintf(out, "\n#define NUM_MASTER_APPLIES\t%i\n\n#endif\n", apply_number);

    fclose(out);
    fclose(table);
    fclose(f);
}

static void handle_malloc() {
#ifdef PEDANTIC
    int unlink(char *);
    int link(char *, char *);
#endif
    
    char *the_malloc = 0, *the_wrapper = 0;

    if (lookup_define("SYSMALLOC"))
	the_malloc = "sysmalloc.c";
    if (lookup_define("SMALLOC"))
	the_malloc = "smalloc.c";
    if (lookup_define("BSDMALLOC"))
	the_malloc = "bsdmalloc.c";

    if (lookup_define("WRAPPEDMALLOC"))
	the_wrapper = "wrappedmalloc.c";
    if (lookup_define("DEBUGMALLOC"))
	the_wrapper = "debugmalloc.c";

    if (!the_malloc && !the_wrapper) {
	fprintf(stderr, "Memory package and/or malloc wrapper incorrectly specified in options.h\n");
	exit(-1);
    }

    if (unlink("malloc.c") == -1 && errno != ENOENT)
	perror("unlink malloc.c");
    if (unlink("mallocwrapper.c") == -1 && errno != ENOENT)
	perror("unlink mallocwrapper.c");

    if (the_wrapper) {
	printf("Using memory allocation package: %s\n\t\tWrapped with: %s\n",
	       the_malloc, the_wrapper);
	if (link(the_wrapper, "mallocwrapper.c") == -1)
	    perror("link mallocwrapper.c");
    } else {
	printf("Using memory allocation package: %s\n", the_malloc);
    }
    if (link(the_malloc, "malloc.c") == -1)
	perror("link malloc.c");
}

static int check_include2 P4(char *, tag, char *, file,
			     char *, before, char *, after) {
    char buf[1024];
    FILE *ct;

    printf("Checking for include file <%s> ... ", file);
    ct = fopen("comptest.c", "w");
    fprintf(ct, "#include \"configure.h\"\n#include \"std_incl.h\"\n%s\n#include <%s>\n%s\n", 
	    before, file, after);
     fclose(ct);
     
    sprintf(buf, "%s %s -c comptest.c " TO_DEV_NULL, COMPILER, CFLAGS);
    if (!compile(buf)) {
	fprintf(yyout, "#define %s\n", tag);
	/* Make sure the define exists for later checks */
	fflush(yyout);
	printf("exists\n");
	return 1;
    }
    printf("does not exist or is unusable\n");
    return 0;
}

static int check_include P2(char *, tag, char *, file) {
    char buf[1024];
    FILE *ct;

    printf("Checking for include file <%s> ... ", file);
    ct = fopen("comptest.c", "w");
    fprintf(ct, "#include \"configure.h\"\n#include \"std_incl.h\"\n#include \"file_incl.h\"\n#include <%s>\n", file);
    fclose(ct);
    
    sprintf(buf, "%s %s -c comptest.c " TO_DEV_NULL, COMPILER, CFLAGS);
    if (!compile(buf)) {
	fprintf(yyout, "#define %s\n", tag);
	/* Make sure the define exists for later checks */
	fflush(yyout);
	printf("exists\n");
	return 1;
    }
    printf("does not exist\n");
    return 0;
}

static int check_library P1(char *, lib) {
    char buf[1024];
    FILE *ct;

    printf("Checking for library %s ... ", lib);
    ct = fopen("comptest.c", "w");
    fprintf(ct, "int main() { exit(0); }\n");
    fclose(ct);
    
    sprintf(buf, "%s %s comptest.c %s" TO_DEV_NULL, COMPILER, CFLAGS, lib);
    if (!compile(buf)) {
	fprintf(yyout, " %s", lib);
	printf("exists\n");
	return 1;
    }
    printf("does not exist\n");
    return 0;
}

#if 0 /* not used any more */
static int check_ret_type P4(char *, tag, char *, pre,
			     char *, type, char *, func) {
    char buf[1024];
    FILE *ct;

    printf("Checking return type of %s() ...", func);
    ct = fopen("comptest.c", "w");
    fprintf(ct, "%s\n\n%s%s();\n", pre, type, func);
    fclose(ct);
    
    sprintf(buf, "%s %s -c comptest.c >/dev/null 2>&1", COMPILER, CFLAGS);
    if (!system(buf)) {
	fprintf(yyout, "#define %s\n", tag);
	printf("returns %s\n", type);
	return 1;
    }
    printf("does not return %s\n", type);
    return 0;
}
#endif

/* This should check a.out existence, not exit value */
static int check_prog P4(char *, tag, char *, pre, char *, code, int, andrun) {
    char buf[1024];
    FILE *ct;

    ct = fopen("comptest.c", "w");
    fprintf(ct, "#include \"configure.h\"\n#include \"std_incl.h\"\n%s\n\nint main() {%s}\n", (pre ? pre : ""), code);
    fclose(ct);
    
    sprintf(buf, "%s %s comptest.c -o comptest" TO_DEV_NULL, COMPILER, CFLAGS);
    if (!compile(buf) && (!andrun || !system("./comptest"))) {
	if (tag) {
	    fprintf(yyout, "#define %s\n", tag);
	    fflush(yyout);
	}
	return 1;
    }

    return 0;
}

static int check_code P2(char *, pre, char *, code) {
    char buf[1024];
    FILE *ct;
    int rc;

    ct = fopen("comptest.c", "w");
    fprintf(ct, "#include \"configure.h\"\n#include \"std_incl.h\"\n%s\n\nint main() {%s}\n", (pre ? pre : ""), code);
    fclose(ct);

    sprintf(buf, "%s %s comptest.c -o comptest" TO_DEV_NULL, COMPILER, CFLAGS);
    if (compile(buf) || (rc = system("./comptest")) == 127 || rc == -1) {
	return -1;
    }
    return rc;
}

static void check_linux_libc() {
    char buf[1024];
    FILE *ct;
    
    ct = fopen("comptest.c", "w");
    fprintf(ct, "int main() { }\n");
    fclose(ct);
    
    sprintf(buf, "%s -g comptest.c -o comptest >/dev/null 2>&1", COMPILER);
    if (system(buf)) {
	fprintf(stderr, "   libg.a/so installed wrong, trying workaround ...\n");
	sprintf(buf, "%s -g comptest.c -lc -o comptest >/dev/null 2>&1", COMPILER);
	if (system(buf)) {
	    fprintf(stderr, "*** FAILED.\n");
	    exit(-1);
	}
	fprintf(yyout, " -lc");
    }
}

static char *memmove_prog = "\
char buf[80];\n\
strcpy(buf,\"0123456789ABCDEF\");\n\
memmove(&buf[1],&buf[4],13);\n\
if(strcmp(buf,\"0456789ABCDEF\")) exit(-1);\n\
memmove(&buf[8],&buf[6],9);\n\
if(strcmp(buf,\"0456789A9ABCDEF\")) exit(-1);\n\
return 0;\n";

static int check_memmove P2(char *, tag, char *, str) {
    return check_prog(tag, str, memmove_prog, 1);
}

static void find_memmove() {
    printf("Checking for memmove() ...");
    if (check_memmove(0, "")) {
	printf(" exists\n");
	return;
    }
    if (check_memmove("USE_BCOPY", "#define memmove(a,b,c) bcopy(b,a,c)")) {
	printf(" simulating via bcopy()\n");
	return;
    }
    printf(" missing; using MudOS's version\n");
    fprintf(yyout, "#define MEMMOVE_MISSING\n");
}

static void verbose_check_prog P5(char *, msg, char *, def, char *, pre,
				  char *, prog, int, andrun) {
    printf("%s ...", msg);
    if (check_prog(def, pre, prog, andrun))
	printf(" exists\n");
    else printf(" does not exist\n");
}

static int check_configure_version() {
    char buf[1024];
    FILE *ct;
    
    ct = fopen("comptest.c", "w");
    fprintf(ct, "#include \"configure.h\"\n\n#if CONFIGURE_VERSION < %i\nthrash and die\n#endif\n\nint main() { }\n", CONFIGURE_VERSION);
    fclose(ct);
    
    sprintf(buf, "%s %s comptest.c -o comptest " TO_DEV_NULL, COMPILER, CFLAGS);
    return !compile(buf);
}

static void handle_configure() {
    if (check_configure_version()) return;

    open_output_file("configure.h");
    
#ifndef WIN32
    check_include("INCL_STDLIB_H", "stdlib.h");
    check_include("INCL_UNISTD_H", "unistd.h");
    if (check_include("INCL_TIME_H", "time.h")) {
	if (!check_prog(0, "#include <time.h>", "tzset();", 0)) {
	    if (check_prog(0, 0, "void tzset(); tzset();", 0))
		fprintf(yyout, "#define PROTO_TZSET\n#define USE_TZSET\n");
	}
	else
	    fprintf(yyout, "#define USE_TZSET\n");
    } else {
	if (check_prog(0, 0, "void tzset(); tzset();", 0))
	    fprintf(yyout, "#define PROTO_TZSET\n#define USE_TZSET\n");
    }
    check_include("INCL_SYS_TIMES_H", "sys/times.h");
    check_include("INCL_FCNTL_H", "fcntl.h");
    check_include("INCL_SYS_TIME_H", "sys/time.h");
    check_include("INCL_DOS_H", "dos.h");
    check_include("INCL_USCLKC_H", "usclkc.h");
    check_include("INCL_LIMITS_H", "limits.h");
    check_include("INCL_LOCALE_H", "locale.h");
    if (!check_prog(0, 0, "int x = USHRT_MAX;", 0)) {
	if (!check_prog(0, 0, "int x = MAXSHORT;", 0))
	    check_include("INCL_VALUES_H", "values.h");
	fprintf(yyout, "#define USHRT_MAX  (MAXSHORT)\n");
    }
    
    check_include("INCL_NETINET_IN_H", "netinet/in.h");
    check_include("INCL_ARPA_INET_H", "arpa/inet.h");

    check_include("INCL_SYS_TYPES_H", "sys/types.h");
    check_include("INCL_SYS_IOCTL_H", "sys/ioctl.h");
    check_include("INCL_SYS_SOCKET_H", "sys/socket.h");
    check_include("INCL_NETDB_H", "netdb.h");
    /* TELOPT_NAWS is missing from <arpa/telnet.h> on some systems */
    check_include2("INCL_ARPA_TELNET_H", "arpa/telnet.h", "", "int i=TELOPT_NAWS;");
    check_include("INCL_SYS_SEMA_H", "sys/sema.h");
    check_include("INCL_SYS_SOCKETVAR_H", "sys/socketvar.h");
    check_include("INCL_SOCKET_H", "socket.h");
    check_include("INCL_RESOLVE_H", "resolve.h");

    check_include("INCL_SYS_STAT_H", "sys/stat.h");

    /* sys/dir.h is BSD, dirent is sys V.  Try to do it the BSD way first. */
    /* If that fails, fall back to sys V */
    if (check_prog("BSD_READDIR", "#include <sys/dir.h>", "struct direct *d; d->d_namlen;", 0)) {
	check_include("INCL_SYS_DIR_H", "sys/dir.h");
    } else {
	/* could be either of these */
	check_include("INCL_DIRENT_H", "dirent.h");
	check_include("INCL_SYS_DIRENT_H", "sys/dirent.h");
	fprintf(yyout, "#define USE_STRUCT_DIRENT\n");
    }

    check_include("INCL_SYS_FILIO_H", "sys/filio.h");
    check_include("INCL_SYS_SOCKIO_H", "sys/sockio.h");
    check_include("INCL_SYS_MKDEV_H", "sys/mkdev.h");
    check_include("INCL_SYS_RESOURCE_H", "sys/resource.h");
    check_include("INCL_SYS_RUSAGE_H", "sys/rusage.h");
    check_include("INCL_SYS_WAIT_H", "sys/wait.h");
    check_include("INCL_SYS_CRYPT_H", "sys/crypt.h");
    check_include("INCL_CRYPT_H", "crypt.h");
    check_include("INCL_MALLOC_H", "my_malloc.h");

    /* for NeXT */
    if (!check_include("INCL_MACH_MACH_H", "mach/mach.h"))
	check_include("INCL_MACH_H", "mach.h");

    /* figure out what we need to do to get major()/minor() */
    check_include("INCL_SYS_SYSMACROS_H", "sys/sysmacros.h");
    check_include("INCL_STDARG_H", "stdarg.h");

#ifdef DEBUG
    /* includes just to shut up gcc's warnings on some systems */
    check_include("INCL_BSTRING_H", "bstring.h");
#endif

    /* Runtime loading support */
    if (check_include("INCL_DLFCN_H", "dlfcn.h")) {
	if (!check_prog(0, "#include <dlfcn.h>", "int x = RTLD_LAZY;", 0))
	    fprintf(yyout, "#define RTLD_LAZY      1\n");
    } else {
	if (!check_prog(0, 0, "int x = RTLD_LAZY;", 0))
	    fprintf(yyout, "#define RTLD_LAZY     1\n");
    }

    /* SunOS is missing definition for INADDR_NONE */
    printf("Checking for missing INADDR_NONE ... ");
    if (!check_prog(0, "#include <netinet/in.h>", "int x = INADDR_NONE;", 0)) {
	printf("missing\n");
	fprintf(yyout, "#define INADDR_NONE (unsigned int)0xffffffff\n");
    } else printf("ok\n");

    printf("Checking for random number generator ...");
    if (check_prog("DRAND48", 0, "srand48(0);", 0)) {
	printf(" using drand48()\n");
    } else
    if (check_prog("RAND", 0, "srand(0);", 0)) {
	printf(" using rand()\n");
    } else
    if (check_prog("RANDOM", 0, "srandom(0);", 0)) {
	printf("using random()\n");
    } else {
	printf("WARNING: did not find a random number generator\n");
	exit(-1);
    }

    if (check_prog("USE_BSD_SIGNALS", 0, "SIGCHLD; wait3(0, 0, 0);", 0)) {
	printf("Using BSD signals.\n");
    } else {
	printf("Using System V signals.\n");
    }
    
    printf("Checking if signal() returns SIG_ERR on error ...");
    if (check_prog("SIGNAL_ERROR SIG_ERR", 0, "if (signal(0, 0) == SIG_ERR) ;", 0)) {
	printf(" yes\n");
    } else {
	fprintf(yyout, "#define SIGNAL_ERROR BADSIG\n");
	printf(" no\n");
    }

    printf("Checking for inline ...");
    if (!check_prog("INLINE inline", "inline void foo() { }", "foo();", 0)) {
	printf(" __inline ...");
	if (!check_prog("INLINE __inline", "__inline void foo() {}", "foo();", 0)) {
	    fprintf(yyout, "#define INLINE\n");
	}
    }
    printf(" const ...\n");
    if (!check_prog("CONST const", "int foo(const int *, const int *);", "", 0))
	fprintf(yyout, "#define CONST\n");
    
    verbose_check_prog("Checking for ualarm()", "HAS_UALARM",
		       "", "ualarm(0, 0);", 0);
    verbose_check_prog("Checking for strerror()", "HAS_STRERROR",
		       "", "strerror(12);", 0);
    verbose_check_prog("Checking for POSIX getcwd()", "HAS_GETCWD",
		       "", "getcwd(\"\", 1000);", 0);
    verbose_check_prog("Checking for getrusage()", "RUSAGE",
		       "", "getrusage(0, 0);", 0);
    verbose_check_prog("Checking for times()", "TIMES",
		       "", "times(0);", 0);
    verbose_check_prog("Checking for gettimeofday()", "HAS_GETTIMEOFDAY",
		       "", "gettimeofday(0, 0);", 0);
    verbose_check_prog("Checking for fchmod()", "HAS_FCHMOD",
		       "", "fchmod(0, 0);", 0);

    printf("Checking for big or little endian ... ");
    if (!check_code("char num[] = { 0x11, 0x22, 0x33, 0x44 }; int *foo = (int *)num;",
		    "return (*foo == 0x44332211);")) {
	printf("big\n");
	fprintf(yyout, "#define BIGENDIAN 1\n");
	fflush(yyout);
    } else printf("little\n");
    
    find_memmove();
#endif

    fprintf(yyout, "#define SIZEOF_INT %i\n", sizeof(int));
    fprintf(yyout, "#define SIZEOF_PTR %i\n", sizeof(char *));
    fprintf(yyout, "#define SIZEOF_SHORT %i\n", sizeof(short));
    fprintf(yyout, "#define SIZEOF_FLOAT %i\n", sizeof(float));

    if (sizeof(unsigned long) == 4)
	fprintf(yyout, "#define UINT32 unsigned long\n");
    else if (sizeof(unsigned int) == 4)
	fprintf(yyout, "#define UINT32 unsigned int\n");
    else {
	printf("WARNING: could not find a 32 bit integral type.\n");
	exit(-1);
    }
    
    /* PACKAGE_DB stuff */
    if (lookup_define("PACKAGE_DB")) {
	/* -I would be nicer for added include paths, but we don't have an easy way to
	 * set -I paths right now 
	 */
	if (lookup_define("USE_MSQL")) {
	    if (!(check_include("INCL_LOCAL_MSQL_H", "/usr/local/include/msql.h")
		|| check_include("INCL_LOCAL_MSQL_MSQL_H", "/usr/local/msql/include/msql.h")
		|| check_include("INCL_LOCAL_MINERVA_MSQL_H", "/usr/local/Minerva/include/msql.h")
		|| check_include("INCL_LIB_HUGHES_MSQL_H", "/usr/lib/Hughes/include/msql.h"))) {
		fprintf(stderr, "Cannot find msql.h, compilation is going to fail miserably.\n");
	    }
	}
	if (lookup_define("USE_MYSQL")) {
	    if (!(check_include("INCL_LOCAL_MYSQL_H", "/usr/local/include/mysql.h")
		|| check_include("INCL_LOCAL_INCLUDE_MYSQL_MYSQL_H", "/usr/local/include/mysql/mysql.h")
		|| check_include("INCL_LOCAL_MYSQL_MYSQL_H", "/usr/local/mysql/include/mysql.h")
		|| check_include("INCL_MYSQL_MYSQL_H", "/usr/include/mysql/mysql.h"))) {
		fprintf(stderr, "Cannot find mysql.h, compilation is going to fail miserably.\n");
	    }
	}
    }
    
    fprintf(yyout, "#define CONFIGURE_VERSION	%i\n\n", CONFIGURE_VERSION);

    close_output_file();

#ifdef WIN32
    system("copy windows\\configure.h tmp.config.h");
    system("type configure.h >> tmp.config.h");
    system("del configure.h");
    system("rename tmp.config.h configure.h");
#else

    open_output_file("system_libs");
    check_library("-lresolv");
    check_library("-lbsd");
    check_library("-lBSD");
    check_library("-ly");

    /* don't add -lcrypt if crypt() is in libc.a */
    if (!check_prog(0, "#include \"lint.h\"", 
		    "char *x = crypt(\"foo\", \"bar\");", 0))
	check_library("-lcrypt");
    /* don't add -lmalloc if malloc() works */
    if (!check_prog(0, "", "char *x = malloc(100);", 0))
	check_library("-lmalloc");

    if (!check_prog(0, "", "void *x = dlopen(0, 0);", 0))
	check_library("-ldl");
    
    check_library("-lsocket");
    check_library("-linet");
    check_library("-lnsl");
    check_library("-lnsl_s");
    check_library("-lseq");
    check_library("-lm");

    fprintf(stderr, "Checking for flaky Linux systems ...\n");
    check_linux_libc();

    /* PACKAGE_DB stuff */
    if (lookup_define("PACKAGE_DB") && lookup_define("USE_MSQL")) {
	if (!(check_library("-lmsql") ||
	      check_library("-L/usr/local/lib -lmsql") ||
	      check_library("-L/usr/local/msql/lib -lmsql") ||
	      check_library("-L/usr/local/Minerva/lib -lmsql") ||
	      check_library("-L/usr/lib/Hughes/lib -lmsql"))) {
	    fprintf(stderr, "Cannot find libmsql.a, compilation is going to fail miserably\n");
	}
    }
    if (lookup_define("PACKAGE_DB") && lookup_define("USE_MYSQL")) {
	if (!(check_library("-lmysqlclient") ||
	      check_library("-L/usr/local/lib -lmysqlclient") ||
	      check_library("-L/usr/local/lib/mysql -lmysqlclient") ||
	      check_library("-L/usr/local/mysql/lib -lmysqlclient"))) {
	    fprintf(stderr, "Cannot find libmysqlclient.a, compilation is going to fail miserably\n");
	}
    }

    fprintf(yyout, "\n\n");
    close_output_file();
#endif
}

int main P2(int, argc, char **, argv) {
    int idx = 1;

    while (idx < argc) {
	if (argv[idx][0] != '-') {
	    fprintf(stderr, SYNTAX);
	    exit(-1);
	}
	if (strcmp(argv[idx], "-configure")==0) {
	    handle_options(0);
	    handle_configure();
	} else
	if (strcmp(argv[idx], "-process")==0) {
	    handle_process(argv[++idx]);
	} else
	if (strcmp(argv[idx], "-options")==0) {
	    handle_options(1);
	} else
	if (strcmp(argv[idx], "-malloc")==0) {
	    handle_malloc();
	} else
	if (strcmp(argv[idx], "-build_applies")==0) {
	    handle_applies();
	} else
	if (strcmp(argv[idx], "-build_func_spec")==0) {
	    handle_build_func_spec(argv[++idx]);
	} else
	if (strcmp(argv[idx], "-build_efuns")==0) {
	    handle_build_efuns();
	} else {
	    fprintf(stderr, "Unrecognized flag %s\n", argv[idx]);
	    exit(-1);
	}
	idx++;
    }
    printf("\n");
    return 0;
}
