sima/autoconf/
sima/hosts/i386/
sima/mudlib/
sima/mudlib/kernel/
sima/mudlib/obj/
sima/mudlib/sys/
sima/synhash/mips/
%{
/* Copyright 1991 - 1995, 1997 J"orn Rennecke */

#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#define MAKE_FUNC
#include "common.h"
#include "config.h"
#include "exec.h"
#include "lex.h"

#define FUNC_SPEC 	"func_spec"
#define CONFIG		"config.h"
#define PRO_LANG        "prolang.y"
#define THE_LANG        "lang.y"
#define THE_INSTRS	"instrs.h"
/*
   make_func has little problems with low memory. Therefore, I prefer to
   be genorous with the maximum allowed line length and simply leave out
   handling of overflow ( which can cause unpredictable behaviour this way...
   but the same is true for some text-editors when faced with so long lines.)
 */
#define MAKE_FUNC_MAXLINE		4096

#define MAX_FUNC  	2048 
#define MAX_TOKENS	2048
#undef malloc
#undef realloc
#undef free

#undef isalunum
#define isalunum(c) (isascii(c) && (isalnum(c) || (c) == '_' ))
#define lexwhite(c) (isascii(c) && isspace(c) && (c) != '\n')
#undef lexdigit
#define lexdigit(c) (isascii(c) && isdigit(c))

#define MF_TYPE__ARRAY     TYPE__ARRAY
#define MF_TYPE__REFERENCE TYPE_REFERENCE

#define C_TOKEN 0
#define C_CODE  1
#define C_EFUN  2
#define C_XCODE 3
#define C_XEFUN 4
#define C_TCODE 5
#define C_TEFUN 6
#define C_VCODE 7
#define C_VEFUN 8
#define C_ALIAS 9
#define C_TOTAL 10

#define C_IS_CODE(x) \
 ((x) == C_CODE || (x) == C_XCODE || (x) == C_TCODE || (x) == C_VCODE)
#define C_IS_EFUN(x) \
 ((x) == C_EFUN || (x) == C_XEFUN || (x) == C_TEFUN || (x) == C_VEFUN)

int num_buff = 0, num_instr[C_TOTAL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
char *token_f_names[MAX_TOKENS];

static struct code {
    char *key, *f_name, *Default;
    short code_class, ordinal;
    short min_arg, max_arg, check_arg, arg_start, return_type;
    short is_const;
    short padding[1];
} codes[MAX_FUNC];

int min_arg = -1, is_varargs = 0;

/*
 * arg_types holds information for compile type checking of efun arguments.
 * For each argument, there is a bitmask of type alternatives.
 */
long arg_types[500], *arg_types_end = &arg_types[0];

long curr_arg_types[MAX_PARAM], *curr_arg_types_end = &curr_arg_types[0];

void yyerror(char *);
int yylex(void);
int yyparse(void);
static void print_comp_type(int);
static void print_typemask(unsigned long m);
static void mf_fatal(char *);

int current_code_class;

/* ultrix 4.2 doesn't know strdup ?!? */
static char *mystrdup(char *str) {
    char *copy = malloc(strlen(str)+1);
    if (!copy) mf_fatal("strdup failed");
    strcpy(copy, str);
    return copy;
}

static void mf_fatal(char *str) {
    fprintf(stderr, "%s\n", str);
    fflush(stdout);
    exit(1);
}

static char *make_f_name(char *str) {
    char f_name[500];
    int i, len;

    if (strlen(str) + 1 + 2 > sizeof f_name)
	mf_fatal("A local buffer was too small!(1)");
    sprintf(f_name, "F_%s", str);
    len = strlen(f_name);
    for (i=0; i < len; i++) {
	if (islower(f_name[i]))
	    f_name[i] = toupper(f_name[i]);
    }
    return mystrdup(f_name);
}

#define VOID 0
#define TYPE_ZERO 1

struct type {
    char *name;
    char *cname;
    long mask;
} types[] = {
  {"void",	"TYPE_VOID"},
  {"",		"0"},
  {"string",	"TYPE_STRING",	1<<TYPE_STRING},
  {"int",	"TYPE_NUMBER",	1<<TYPE_NUMBER},
  {"object",	"TYPE_OBJECT",	1<<TYPE_OBJECT},
  {"mapping",	"TYPE_MAPPING",	1<<TYPE_MAPPING},
  {"float",	"TYPE_FLOAT",	1<<TYPE_FLOAT},
  {"closure",	"TYPE_CLOSURE",	1<<TYPE_CLOSURE},
  {"symbol",	"TYPE_SYMBOL",	1<<TYPE_SYMBOL},
  {"varargs",	"TYPE_VARARGS"},
  {"mixed",	"TYPE_ANY",	~(1<<TYPE_REFERENCE)},
  {"unknown",	"TYPE_UNKNOWN"},
  {"quoted_array","TYPE_QUOTED_ARRAY"},
  {"",		"TYPE_REFERENCE", 1<<TYPE_REFERENCE},
  {"",		"TYPE_ANY", 1<<TYPE_ANY},
};

static int need_void = -1; /* >= 0 is true */

void store_arg_type(int mode, int arg) {
    static long current_arg = 0;

    if (mode != VOID) {
	if ( !(arg & TYPE__ARRAY) ) {
	    current_arg |= types[arg].mask;
	    return;
	}
	if (arg == MF_TYPE__REFERENCE) {
	    current_arg |= 1<<TYPE_REFERENCE;
	    return;
	}
	current_arg |=
	  (types[arg & ~TYPE__ARRAY].mask << TYPE__ARRAY) &
	    ~(1<<TYPE_REFERENCE);
	return;
    }
    if (min_arg < 0) {
	if (arg == VOID) {
	    min_arg = curr_arg_types_end - curr_arg_types;
	    return;
	}
    } else {
	if (arg == VOID) {
	    need_void = -1;
	    return;
	}
	if (need_void >= 0) {
	    fprintf(stderr, "arg %d: ", curr_arg_types_end - curr_arg_types);
	    yyerror("non-void argument after void one");
	}
    }
    need_void = min_arg;
    if (!current_arg)
	mf_fatal("storing 0 type\n");
    *curr_arg_types_end++ = current_arg;
    current_arg = 0;
    if (curr_arg_types_end == &curr_arg_types[NELEM(curr_arg_types)])
	yyerror("Too many arguments");
}

%}
%union {
    int number;
    char *string;
}

%token NAME ID

%token DEFAULT BASIC_TYPE CONST

%token TOKENS CODES EFUNS XCODES XEFUNS TCODES TEFUNS END

%type <number> BASIC_TYPE opt_star opt_ref
%type <number> arg_list type arg_alternative formal_arg
%type <number> optional_const

%type <string> ID optional_token optional_default NAME optional_name

%%

all: tokens END codes END efuns END xcodes END xefuns END tcodes END tefuns;

tokens:	  TOKENS
	| tokens token;

codes:	  CODES
	| codes code;

xcodes:	  XCODES
	| xcodes code;

tcodes:	  TCODES
	| tcodes code;

efuns:	EFUNS  funcs;

xefuns:	XEFUNS funcs;

tefuns:	TEFUNS funcs;

token:	ID
    {
	token_f_names[num_instr[0]++] = make_f_name($1);
    }

optional_name: /* empty */ { $$ = 0; }
	| NAME;

code:	optional_name ID
    {
	struct code *p;

	num_instr[current_code_class]++;
	p = &codes[num_buff];
	p->code_class = current_code_class;
	p->ordinal = ++num_buff;
	p->f_name = make_f_name($2);
	if ($1) {
	    p->key = $1;
	    free($2);
	} else {
	    p->key = $2;
	}
	p->is_const = 1;
	p->min_arg = p->max_arg = 0;
	p->arg_start = 0;
	p->return_type = TYPE_ZERO;
	p->Default = "-1";
    }

funcs: /* empty */ | funcs func ;

optional_token: ID | /* empty */ { $$ = ""; } ;

optional_default: DEFAULT ':' ID { $$ = $3; } | /* empty */ { $$ = "0"; } ;

optional_const: CONST { $$ = 0; } | /* empty */ { $$ = 1; } ;

func: optional_const type ID optional_token '(' arg_list optional_default ')' ';'
    {
	char *f_name;
	long *haystack;
	struct code *r;

	need_void = -1;
	if ($4[0] == '\0') {
	    f_name = make_f_name($3);
	    num_instr[
	      (int)(codes[num_buff].code_class =
		is_varargs && current_code_class == C_TEFUN ?
		  C_VEFUN : current_code_class)
	    ]++;
	} else {
	    f_name = $4;
	    codes[num_buff].code_class = C_ALIAS;
	    num_instr[C_ALIAS]++;
	}
	for (haystack = arg_types;; haystack++) {
	    long *p = haystack, *q = curr_arg_types;

	    for (;;) {
		if (q == curr_arg_types_end)
		    goto arguments_stored;
		if (p == arg_types_end) {
		    do {
			if (p == &arg_types[NELEM(arg_types)])
			    mf_fatal("Array 'arg_types' is too small");
			*p++ = *q++;
		    } while(q < curr_arg_types_end);
		    arg_types_end = p;
		    goto arguments_stored;
		}
		if (*p++ != *q++)
		    break;
	    }
	}
      arguments_stored:
	r = &codes[num_buff];
	r->ordinal = ++num_buff;
        r->key = $3;
        r->f_name = f_name;
	r->is_const = $1;
	r->min_arg = min_arg < 0 ? $6 : min_arg;
	r->max_arg = is_varargs | $6;
	r->check_arg = curr_arg_types_end - curr_arg_types;
	r->arg_start = haystack - arg_types;
	r->Default = $7;
	r->return_type = $2;
	min_arg = -1;
	is_varargs = 0;
	curr_arg_types_end = &curr_arg_types[0];
    } ;

type: BASIC_TYPE opt_star opt_ref { $$ = $1 | $2 | $3; };

opt_star : '*' { $$ = MF_TYPE__ARRAY; }
	|      { $$ = 0;                   } ;

opt_ref : '&' { $$ = MF_TYPE__REFERENCE; }
	|     { $$ = 0;                     } ;

arg_list: /* empty */		{ $$ = 0; min_arg = 0; /* $$: length */ }
	| formal_arg		{ $$ = 1; }
	| arg_list ',' formal_arg { $$ = $1 + 1; };

formal_arg: arg_alternative
    {
	store_arg_type(VOID, TYPE_ZERO);
    }
    | '.' '.' '.'
    {
	is_varargs = 255;
	if (min_arg < 0)
	    min_arg = curr_arg_types_end - curr_arg_types;
	/* store_arg_type(VOID, TYPE_ZERO); */
    };

arg_alternative: type { store_arg_type($1, $1); }
     | arg_alternative '|' type 	{ store_arg_type($3, $3); } ;

%%

int current_line;
char *current_file;
int last_line;

extern int hashstr(unsigned char *, mp_int, int);
#define MAKE_FUNC_DEFHASH 512
#define defhash(str) (idhash(str).hash & (MAKE_FUNC_DEFHASH - 1))

struct mf_defn {
    char *name;
    char *exps;
    int  num_arg;
    struct mf_defn *next;
};

static struct mf_defn *deftab[MAKE_FUNC_DEFHASH];

static char *outp;

static int mygetc() {
    return *outp++;
}

static void myungetc(int c) {
    *--outp = c;
}

static void add_input(char *p) {
    int l;

    l = strlen(p);
    outp -= l;
    memcpy(outp, p, l);
}

static void add_define(char *name, int num_arg, char *exps) {
    int i;
    struct mf_defn *ndef;

    i = defhash(name);
    ndef = (struct mf_defn *)malloc(sizeof(struct mf_defn));
    if (!ndef) {
	abort();
    }
    ndef->next    = deftab[i];
    ndef->exps    = mystrdup(exps);
    ndef->num_arg = num_arg;
    ndef->name    = mystrdup(name);
    deftab[i]     = ndef;
}

static struct mf_defn *lookup_define(char *s) {
    struct mf_defn *curr, *prev;
    int h;

    h = defhash(s);

    curr = deftab[h];
    prev = 0;
    while (curr) {
	if (!strcmp(curr->name, s)) { /* found it */
	    if (prev) { /* not at head of list */
		prev->next = curr->next;
		curr->next = deftab[h];
		deftab[h] = curr;
	    }
	    return curr;
	}
	prev = curr;
	curr = curr->next;
    } /* not found */
    return 0;
}

static int expand_define(char *s) {
    struct mf_defn *p;

    p = lookup_define(s);
    if (!p)
	return 0;
    if (p->num_arg < 0) {
	add_input(p->exps);
    } else {
	return -1;
    }
    return 1;

}

static char *nextword(char *str) {
    char *cp;

    while(!lexwhite(*str)) str++;
    while( lexwhite(*str)) str++;
    for(cp = str; isalunum(*cp); ) cp++;
    *cp = '\0';
    return str;
}

static struct ifstate {
    struct ifstate *next;
    int expect_else;
} *iftop = 0;

FILE *fpr, *fpw;

static int skip_to(char mark, char *token, char *atoken) {
    char b[8], *p;
    int c;
    int nest;
    FILE *fp = fpr;

    for(nest = 0;;) {
	current_line++;
	c = fgetc(fp);
	if (c == mark) {
	    do {
		c = fgetc(fp);
	    } while(lexwhite(c));
	    for(p = b; isalpha(c) && p < &b[sizeof b - 1]; ) {
		*p++ = c;
		c = fgetc(fp);
	    }
	    *(isspace(c) ? p : b) = '\0';
	    while (c != '\n') {
		if (c == EOF) {
		    fprintf(stderr, "%cendif expected\n", mark);
		    abort();
		}
		c = fgetc(fp);
	    }
	    if (*(short *)b == *(short *)"if") {
		if (!b[2] || !strcmp(b+2, "def") || !strcmp(b+2, "ndef"))
		    nest++;
	    } else if (nest > 0) {
		if (strcmp(b, "endif") == 0)
		    nest--;
	    } else {
		if (strcmp(b, token) == 0)
		    return 1;
		else if (atoken && strcmp(b, atoken) == 0)
		    return 0;
	    }
	} else {
            while (c != '\n') {
		if (c == EOF) {
		    fprintf(stderr, "%cendif expected\n", mark);
		    abort();
		}
		c = fgetc(fp);
	    } 
	}
    }
}

static void compensate_lines() {
    for (; last_line <= current_line; last_line++)
        fputc('\n', fpw);
}

static void handle_cond(char mark, int c) {
    struct ifstate *p;

    if (c || skip_to(mark, "else", "endif")) {
	p = (struct ifstate *)malloc(sizeof(struct ifstate));
	p->next = iftop;
	p->expect_else = c;
	iftop = p;
    }
    if (mark == '%')
	compensate_lines();
}

static int cond_get_exp();

static void handle_if(char mark, char *str) {
    int cond;

    add_input(str);
    cond = cond_get_exp(0);
    if (mygetc() != '\n') {
	yyerror("Condition too complex in #/%if");
	fflush(stdout);
	if (mark == '%') exit(1);
	while(mygetc() != '\n');
    } else
	handle_cond(mark, cond );
}

static void handle_else(char mark) {
    if (iftop && iftop->expect_else) {
	struct ifstate *p = iftop;

	iftop = p->next;
	free((char *)p);
	skip_to(mark, "endif", (char *)0);
    } else {
	fprintf(stderr, "Unexpected #/%%else line %d\n", current_line);
	abort();
    }
}

static void handle_endif() {
    if (iftop) {
	struct ifstate *p = iftop;

	iftop = p->next;
	free((char *)p);
    } else {
	fprintf(stderr, "Unexpected #/%%endif line %d\n", current_line);
	abort();
    }
}

static int name_to_type(char *name) {
    while (isspace(*name))
	name++;
    if ( strncmp(name, "TYPE_", 5) )
	return -1;
    name += 5;
    if ( !strcmp(name, "ANY") )
	return TYPE_ANY;
    if ( !strcmp(name, "NUMBER") )
	return TYPE_NUMBER;
    if ( !strcmp(name, "FLOAT") )
	return TYPE_FLOAT;
    if ( !strcmp(name, "OBJECT") )
	return TYPE_OBJECT;
    if ( !strcmp(name, "MAPPING") )
	return TYPE_MAPPING;
    if ( !strcmp(name, "CLOSURE") )
	return TYPE_CLOSURE;
    return -1;
}

static int name_to_hook(char *name) {
    while (isspace(*name))
	name++;
    if ( strncmp(name, "H_", 2) )
	return -1;
    name += 2;
    if ( !strcmp(name, "MOVE_OBJECT0") )
	return H_MOVE_OBJECT0;
    if ( !strcmp(name, "MOVE_OBJECT1") )
	return H_MOVE_OBJECT1;
    if ( !strcmp(name, "LOAD_UID") )
	return H_LOAD_UID;
    if ( !strcmp(name, "CLONE_UID") )
	return H_CLONE_UID;
    if ( !strcmp(name, "CREATE_SUPER") )
	return H_CREATE_SUPER;
    if ( !strcmp(name, "CREATE_OB") )
	return H_CREATE_OB;
    if ( !strcmp(name, "CREATE_CLONE") )
	return H_CREATE_CLONE;
    if ( !strcmp(name, "RESET") )
	return H_RESET;
    if ( !strcmp(name, "CLEAN_UP") )
	return H_CLEAN_UP;
    if ( !strcmp(name, "NO_IPC_SLOT") )
	return H_NO_IPC_SLOT;
    if ( !strcmp(name, "INCLUDE_DIRS") )
	return H_INCLUDE_DIRS;
    return -1;
}

static void handle_map(char *str, int size, int (*name_to_index)(char *)) {
    char **map, deflt[256];
    char *del = 0, *val;
    int i;
    char *output_del = "";

    map = (char **)alloca(size * sizeof *map);
    strcpy(deflt, "0");
    for (i = 0; i < size; i++) {
	map[i] = deflt;
    }
    do {
	while (isspace(*str))
	    str++;
	if (*str == '\\') {
	    str = alloca(MAKE_FUNC_MAXLINE + 1);
	    if (!fgets(str, MAKE_FUNC_MAXLINE, fpr))
		break;
	    if (del) {
		output_del = "\n";
	    }
	}
	del = strchr(str, ':');
	if (!del)
	    break;
	*del = '\0';
	val = del+1;
	del = strchr(val, ',');
	if (!del) {
	    del = strchr(val, '\n');
	    if (del) {
		*del = '\0';
		del = 0;
	    }
	} else {
	    *del = '\0';
	}
	if ( !strcmp(str, "default") ) {
	    strncpy(deflt, val, sizeof(deflt));
	    deflt[sizeof deflt - 1] = '\0';
	} else {
	    i = (*name_to_index)(str);
	    if (i < 0)
		exit(-1);
	    map[i] = val;
	}
	str = del+1;
    } while (del);
    fprintf(fpw, "{");
    fprintf(fpw, output_del);
    for (i = 0; i < size; i++) {
	fprintf(fpw, "%s,", map[i]);
	fprintf(fpw, output_del);
    }
    fprintf(fpw, "};\n");
}

static int exgetc() {
    register char c;

    c=mygetc();
    while ( isalpha(c) || c=='_' ) {
	char word[512], *p;
	int space_left;

	p = word;
	space_left = sizeof(word);
	do {
	    *p++ = c;
	    c=mygetc();
	} while ( isalunum(c) && --space_left);
	if (!space_left) mf_fatal("Too long word.");
	myungetc(c);
	*p='\0';
	if (strcmp(word, "defined") == 0) {
	    /* handle the defined "function" in #if */
	    do c = mygetc(); while(lexwhite(c));
	    if (c != '(') {
		yyerror("Missing ( in defined");
		continue;
	    }
	    do c = mygetc(); while(lexwhite(c));
	    p = word;
	    space_left = sizeof(word);
	    while ( isalunum(c) && --space_left) {
		*p++ = c;
		c=mygetc();
	    }
	    *p='\0';
	    while(lexwhite(c)) c = mygetc();
	    if (c != ')') {
		yyerror("Missing ) in defined");
		continue;
	    }
	    if (lookup_define(word))
	    	add_input(" 1 ");
	    else
		add_input(" 0 ");
	} else {
	    int res;

	    res = expand_define(word);
	    if (res < 0) {
		yyerror("Unimplemented macro expansion");
		return 0;
	    }
	    if (!res) add_input(" 0 ");
	}
	c=mygetc();
    }
    return c;
}

#define BNOT   1
#define LNOT   2
#define UMINUS 3
#define UPLUS  4

#define MULT   1
#define DIV    2
#define MOD    3
#define BPLUS  4
#define BMINUS 5
#define LSHIFT 6
#define RSHIFT 7
#define LESS   8
#define LEQ    9
#define GREAT 10
#define GEQ   11
#define EQ    12
#define NEQ   13
#define BAND  14
#define XOR   15
#define BOR   16
#define LAND  17
#define LOR   18
#define QMARK 19

static char _optab[]=
{0,4,0,0,0,26,56,0,0,0,18,14,0,10,0,22,0,0,0,0,0,0,0,0,0,0,0,0,30,50,40,74,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,0,1};
static char optab2[]=
{BNOT,0,0,LNOT,'=',NEQ,7,0,0,UMINUS,0,BMINUS,10,UPLUS,0,BPLUS,10,
0,0,MULT,11,0,0,DIV,11,0,0,MOD,11,
0,'<',LSHIFT,9,'=',LEQ,8,0,LESS,8,0,'>',RSHIFT,9,'=',GEQ,8,0,GREAT,8,
0,'=',EQ,7,0,0,0,'&',LAND,3,0,BAND,6,0,'|',LOR,2,0,BOR,4,
0,0,XOR,5,0,0,QMARK,1};
#define optab1 (_optab-' ')

static int cond_get_exp(int priority) {
    int c;
    int value,value2,x;

    do c=exgetc(); while ( lexwhite(c) );
    if ( c=='(' ) {

	value=cond_get_exp(0);
	do c=exgetc(); while ( lexwhite(c) );
	if ( c!=')' ) {
	    yyerror("bracket not paired in #if");
	    if (!c) myungetc('\0');
	}
    } else if ( ispunct(c) ) {
	x=optab1[c];
	if (!x) {
	    yyerror("illegal character in #if");
	    return 0;
	}
	value=cond_get_exp(12);
	switch ( optab2[x-1] ) {
	  case BNOT  : value = ~value; break;
	  case LNOT  : value = !value; break;
	  case UMINUS: value = -value; break;
	  case UPLUS : value =  value; break;
	  default :
	    yyerror("illegal unary operator in #if");
	    return 0;
	}
    } else {
	int base;

	if ( !lexdigit(c) ) {
	    if (!c) {
		yyerror("missing expression in #if");
		myungetc('\0');
	    } else {
		yyerror("illegal character in #if");
	    }
	    return 0;
	}
	value=0;
	if ( c!='0' ) {
	    base=10;
	} else {
	    c=mygetc();
	    if ( c=='x' || c=='X' ) {
		base=16;
		c=mygetc();
	    } else {
		base=8;
	    }
	}
	for(;;) {
	    if ( isdigit(c) ) x = -'0';
	    else if ( isupper(c) ) x = -'A'+10;
	    else if ( islower(c) ) x = -'a'+10;
	    else break;
	    x+=c;
	    if ( x > base ) break;
	    value=value*base+x;
	    c=mygetc();
	}
	myungetc(c);
    }
    for (;;) {
	do c=exgetc(); while ( lexwhite(c) );
	if ( !ispunct(c) ) break;
	x=optab1[c];
	if (!x) break;
	value2=mygetc();
	for(;;x+=3) {
	    if ( !optab2[x] ) {
		myungetc(value2);
		if ( !optab2[x+1] ) {
		    yyerror("illegal operator use in #if");
		    return 0;
		}
		break;
	    }
	    if ( value2==optab2[x] ) break;
	}
	if ( priority >= optab2[x+2] ) {
	    if( optab2[x] ) myungetc(value2);
	    break;
	}
	value2=cond_get_exp(optab2[x+2]);
	switch ( optab2[x+1] ) {
	  case MULT : value *= value2;	break;
	  case DIV  : value /= value2;	break;
	  case MOD  : value %= value2;	break;
	  case BPLUS  : value += value2;	break;
	  case BMINUS : value -= value2;	break;
	  case LSHIFT : value <<= value2;	break;
	  case RSHIFT : value >>= value2;	break;
	  case LESS   : value = value <  value2;	break;
	  case LEQ    : value = value <= value2;	break;
	  case GREAT  : value = value >  value2;	break;
	  case GEQ    : value = value >= value2;	break;
	  case EQ     : value = value == value2;	break;
	  case NEQ    : value = value != value2;	break;
	  case BAND   : value &= value2;	break;
	  case XOR    : value ^= value2;	break;
	  case BOR    : value |= value2;	break;
	  case LAND   : value = value && value2;	break;
	  case LOR    : value = value || value2;	break;
	  case QMARK  :
	    do c=exgetc(); while( lexwhite(c) );
	    if ( c!=':' ) {
		yyerror("'?' without ':' in #if");
		myungetc(c);
		return 0;
	    }
	    if ( value ) {
		cond_get_exp(1);
		value=value2;
	    } else {
		value=cond_get_exp(1);
	    }
	    break;
	}
    }
    myungetc(c);
    return value;
}

static int make_func_isescaped(char c) {
    switch(c) {
      case '\007':
      case '\b'  :
      case '\t'  :
      case '\n'  :
      case '\013':
      case '\014':
      case '\r'  :
        return 1;
    }
    if (c == '\\' || c == '\"') return 1;
    return 0;
}

static int efuncmp(const void *x, const void *y) {
    const struct code *a = x, *b = y;
    int result;

    if (result = a->code_class - b->code_class)
	return result;
    if (C_IS_CODE(a->code_class)) return a->ordinal - b->ordinal;
    return strcmp(a->key, b->key);
}

void print_code(struct code *p) {
    fputc('{', stdout);
    print_comp_type(p->return_type);
    printf(",%d,%d,%d,%d,%s,%d,\"%s\"},\n",
      p->is_const, p->max_arg , p->min_arg, p->check_arg,
      p->Default, p->arg_start, p->key);
}

int main(int argc, char **argv) {
    int i, j, k;
    int match_tmp;
    unsigned char c;
    char line_buffer[MAKE_FUNC_MAXLINE + 1];
    char defbuf[MAKE_FUNC_MAXLINE + 1];

#ifdef AMIGA
    add_define("AMIGA",-1,"");
#endif
#ifdef HAVE_GETRUSAGE
    add_define("HAVE_GETRUSAGE",-1,"");
#endif
#ifdef TRACE_CODE
    add_define("TRACE_CODE",-1,"");
#endif
    if ((fpw = fopen(THE_LANG, "w")) == 0) {
       perror(THE_LANG);
       exit(1);
    }
    fprintf(fpw, "%s", "\
%union{ int i; char *p; }\n\
%type <p> all\n\
%%\n\
all: { $<p>$ = 0; } 'a'\n\
%%\n\
");
    fclose(fpw);
    sprintf(line_buffer, "%s %s", YACC, THE_LANG);
    printf("#if 0\n");
    fflush(stdout);
    fprintf(stderr, "checking default & anonymous rules in %s\n", YACC);
    if (system(line_buffer)) {
	fprintf(
	  stderr,
"It seems to have trouble with this combination, I'll avoid the latter.\n"
	);
	add_define("YACC_CANNOT_MIX_ANONYMOUS_WITH_DEFAULT", -1, "");
    }
    printf("#endif\n\n");
    outp = defbuf + sizeof(defbuf) - 1;
    /* read in the defines of the configuration file */
    if ((fpr = fopen(CONFIG, "r")) == 0) {
       perror(CONFIG);
       fflush(stdout);
       exit(1);
    }
    current_line = 0;
    current_file = CONFIG;

#define MATCH(str) (isspace(line_buffer[1+(match_tmp=strlen(str))]) &&\
			strncmp(str, line_buffer+1, match_tmp) == 0)

    while (fgets(line_buffer, MAKE_FUNC_MAXLINE, fpr)) {
	char *name;

	current_line++;
	if ( *line_buffer != '#' )
	    continue;
	if MATCH("if") {
	    handle_if('#', line_buffer+4);
	    continue;
	}
	if MATCH("ifdef") {
	    handle_cond('#', lookup_define(nextword(line_buffer)) != 0);
	    continue;
	}
	if MATCH("ifndef") {
	    handle_cond('#', lookup_define(nextword(line_buffer)) == 0);
	    continue;
	}
	if MATCH("else") {
	    handle_else('#');
	    continue;
	}
	if MATCH("endif") {
	    handle_endif();
	    continue;
	}
	if MATCH("undef") {
	    struct mf_defn *old_def;
	    old_def = lookup_define(nextword(line_buffer));
	    if (old_def)
		old_def->name[0] = '\0';
	    continue;
	}
	if ( !MATCH("define") ) {
	    continue;
	}
	/* CONFIG is trusted to be syntactically correct. After all, it was
	 * included by the source of this program.
	 */
	{
	    char *cp, *exps;
	    int num_arg;

	    cp = line_buffer+8;
	    while( isspace(*cp)) cp++;
	    name = cp;
	    while(isalunum(*cp)) cp++;
	    num_arg = *cp == '(' ? 0 : -1;
	    if (*cp == '\n') {
		exps = cp;
		*cp = '\0';
	    } else {
		*cp++ = '\0';
		while( isspace(*cp)) cp++;
		exps = cp;
		while(*cp != '\n') cp++;
		*cp = '\0';
	    }
	    add_define(name, num_arg, exps);
	}
    }
    fclose(fpr);

    if ((fpr = fopen(FUNC_SPEC, "r")) == NULL) { 
	perror(FUNC_SPEC);
	exit(1);
    }
    current_line = 1;
    current_file = FUNC_SPEC;
    yyparse();
    fclose(fpr);
    fprintf(
	stderr, "\
Primary codes: %d Secondary codes: %d\n\
Tabled codes: %d Tabled varargs codes: %d\n",
	num_instr[C_CODE]  + num_instr[C_EFUN],
	num_instr[C_XCODE] + num_instr[C_XEFUN],
	num_instr[C_TCODE] + num_instr[C_TEFUN],
	num_instr[C_VCODE] + num_instr[C_VEFUN]
    );
    if ( num_instr[C_CODE] + num_instr[C_EFUN] & ~0xff ||
         ( num_instr[C_XCODE]+ num_instr[C_XEFUN] |
           num_instr[C_TCODE]+ num_instr[C_TEFUN] |
           num_instr[C_VCODE]+ num_instr[C_VEFUN] ) & ~0x7f)
    {
	mf_fatal("Codes exhausted!");
    }
    /* Now sort the efuns */
    qsort(codes, num_buff, sizeof codes[0], efuncmp);
    /* Now output the instrs array. */
    printf("#include \"exec.h\" /* for size of struct instr */\n\n");
    printf("struct instr instrs[]={\n");
    j = num_instr[C_CODE] + num_instr[C_EFUN];
    for (i = 0; i < j; i++) {
	print_code(&codes[i]);
    }
    for (i -= 256 ; ++i <= 0; ) {
	printf(" {0,0,0,0,0,-1,0,0},\n");
    }
    i = j;
    j += num_instr[C_XCODE] + num_instr[C_XEFUN];
    for (; i < j; i++) {
	print_code(&codes[i]);
    }
    for (i = 128 - num_instr[C_XCODE] - num_instr[C_XEFUN]; --i >= 0; ) {
	printf(" {0,0,0,0,0,-1,0,0},\n");
    }
    i = j;
    j += num_instr[C_TCODE] + num_instr[C_TEFUN];
    for (; i < j; i++) {
	print_code(&codes[i]);
    }
    for (i = 128 - num_instr[C_TCODE] - num_instr[C_TEFUN]; --i >= 0; ) {
	printf(" {0,0,0,0,0,-1,0,0},\n");
    }
    i = j;
    for (; i < num_buff; i++) {
	print_code(&codes[i]);
    }
    printf("};\nshort efun_aliases[]={\n");
    for (i = 0; i < num_buff; i++) {
	if (codes[i].code_class == C_ALIAS)
	    printf(" %s,\n", codes[i].f_name);
    }
    printf("};\nint32 efun_arg_types[] = {\n ");
    {
	long *p;

	for (p = arg_types; p < arg_types_end; p++) {
	    print_typemask(*p);
	    fputs(",\n ", stdout);
	}
    }
    printf("};\n\n");
    for(j = 1, i = 0; j < C_TCODE; j++)
	i += num_instr[j];
    j = i + num_instr[C_TCODE] + num_instr[C_TEFUN];
    k = i;
    while(k < j) {
	printf(
	  "union svalue *f_%s(union svalue *);\n", codes[k++].key);
    }
    printf("\nunion svalue *(*efun_table[])(union svalue *) = {\n");
    while(i < j) {
	printf(" f_%s,\n", codes[i++].key);
    }
    printf("};\n\n");
    j = i + num_instr[C_VCODE] + num_instr[C_VEFUN];
    k = i;
    while(k < j) {
	printf(
	  "union svalue *f_%s(union svalue *);\n", codes[k++].key);
    }
    printf("\nunion svalue *(*vefun_table[])(union svalue *) = {\n");
    while(i < j) {
	printf(" f_%s,\n", codes[i++].key);
    }
    printf("};\n\n");
    /* Make our own character type classification. This is for two reasons:
     * i)  The standard isXXX macros are only defined on ascii values.
     *     there might be non-ascii characters in the compiled files.
     * ii) We actually need some nonstandard classifications, and the lexical
     *     scanner can be costly in terms of cpu usage.
     */

	printf("#define lexwhite(c) (_my_ctype[(unsigned char)(c)]&%d)\n",_MCTs);
	printf("#define leXdigit(c) (_my_ctype[(unsigned char)(c)]&%d)\n",_MCTx);
	printf("unsigned char _my_ctype[]={");
	c = '\0';
	do {
	    if (!(c & 0xf)) printf("\n    ");
	    printf("%d,", !isascii(c) ? 0 :
	        ( make_func_isescaped(c)   ? _MCTe : 0 ) |
	        ( isdigit (c)		   ? _MCTd : 0 ) |
	        ( isspace (c) && c != '\n' ? _MCTs : 0 ) |
	        ( isxdigit(c)		   ? _MCTx : 0 ) |
	        ( isalnum (c) || c == '_'  ? _MCTa : 0 ) );
	    c++;
	} while (c != '\0');
	printf("};\n\n");

    /* Create a table to 'cook' backslash-escaped characters */
	printf("char escchars[]={");
	c = '\0';
	do {
	    if (!(c & 0xf)) printf("\n    ");
	    i = c;
	    switch(i) {
	      case '0': case '1': case '2': case '3': case '4': case '5':
	      case '6': case '7':
	      case '\n': case '\r':
	      case LC_DEFARG: case LC_IDENT: case LC_STRING: case LC_POP:
		i = '0';
	      case 'a': case 'b': case 'e': case 'f': case 'n': case 'r':
	      case 't': case 'v': case '\\': case '\'': case '\"':
		printf("\'\\%c\',", i);
		break;
	      default:
		printf((isascii(i) && isprint(i))  ? "\'%c\'," : "%d,", i);
		break;
	    }
	} while(++c != '\0');
	printf("};\n");
    if ((fpw = fopen(THE_INSTRS, "w")) == 0) {
       perror(THE_INSTRS);
       exit(1);
    }
    fprintf(fpw, "#include \"exec.h\" /* for size of struct instr */\n\n");
    fprintf(fpw, "#define LAST_INSTRUCTION_CODE %d\n",
	256+128+128+num_instr[C_VCODE]+num_instr[C_VEFUN] - 1
    );
    fprintf(fpw, "\nextern struct instr instrs[%d];\n",
	256+128+128+num_instr[C_VCODE]+num_instr[C_VEFUN]+num_instr[C_ALIAS]
    );
    fprintf(fpw, "extern short efun_aliases[%d];\n\n", num_instr[C_ALIAS]);
    for (i = j = 0; i < num_buff; i++) {
	if (j == num_instr[C_CODE] + num_instr[C_EFUN]) {
	    j = 256;
	}
	if (j == 256 + num_instr[C_XCODE] + num_instr[C_XEFUN]) {
	    j = 256+128;
	}
	if (j == 256 + 128 + num_instr[C_TCODE] + num_instr[C_TEFUN]) {
	    j = 256+128+128;
	}
	if (codes[i].code_class != C_ALIAS) {
	    fprintf(fpw, "#define %s %d\n", codes[i].f_name, j);
	    j++;
	}
    }
    fclose(fpw);
    /*
     * Create lang.y from prolang.y , doing some useful transformations
     * and inserting the token list at the appropriate place.
     */
    if ((fpr = fopen(PRO_LANG, "r")) == 0) {
       perror(PRO_LANG);
       exit(1);
    }
    if ((fpw = fopen(THE_LANG, "w")) == 0) {
       perror(THE_LANG);
       exit(1);
    }
    current_line = 0;
    while (fgets(line_buffer, MAKE_FUNC_MAXLINE, fpr)) {
	current_line++;
	if (*line_buffer == '%') {
	    if MATCH("efuns") {
		for (i = 0; i < num_instr[C_TOKEN]; i++) {
		    fprintf(fpw, "%%token YYF%s\n", token_f_names[i]+1);
		}
		for (i = 0; i < num_instr[C_CODE]; i++) {
		    if (codes[i].code_class != C_ALIAS) {
			fprintf(fpw , "%%token YY%s\n", codes[i].f_name);
		    }
		}
		continue;
	    }
	    last_line = current_line;
	    if MATCH("if") {
		handle_if('%', line_buffer+4);
		continue;
	    }
	    if MATCH("ifdef") {
		handle_cond('%', lookup_define(nextword(line_buffer)) != 0);
		continue;
	    }
	    if MATCH("ifndef") {
		handle_cond('%', lookup_define(nextword(line_buffer)) == 0);
		continue;
	    }
	    if MATCH("else") {
	        handle_else('%');
	        compensate_lines();
		continue;
	    }
	    if MATCH("endif") {
	        handle_endif();
	        compensate_lines();
		continue;
	    }
	    if MATCH("line") {
		fprintf(fpw, "#line %d \"%s\"\n", current_line+1, PRO_LANG);
		continue;
	    }
	    if MATCH("typemap") {
		handle_map(line_buffer+9, TYPEMAP_SIZE, name_to_type);
		continue;
	    }
	    if MATCH("hookmap") {
		handle_map(line_buffer+9, NUM_DRIVER_HOOKS, name_to_hook);
		continue;
	    }
	    if MATCH("error_nargs") {
		static void print_error_nargs();

		print_error_nargs();
		continue;
	    }
	    if MATCH("ce_error_nargs") {
		static void print_ce_error_nargs();

		print_ce_error_nargs();
		continue;
	    }
	    if MATCH("//") {
		/* c++ - resembling comment */
		fputs("", fpw);
		continue;
	    }
        }
        fputs(line_buffer, fpw);
    }
    fclose(fpr), fclose(fpw);
    return 0;
}

#undef MATCH

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

static int ident(int c) {
    char buff[96];
    int len, i;

    for (len = 0; isalunum(c); c = getc(fpr)) {
	if (len == sizeof buff - 1)
	    yyerror("Too long indentifier");
	buff[len++] = c;
    }
    ungetc(c, fpr);
    buff[len] = '\0';
    if ( C_IS_EFUN(current_code_class) ) {
	for (i = NELEM(types); --i >= 0; ) {
	    if (!strcmp(buff, types[i].name)) {
		yylval.number = i;
		return BASIC_TYPE;
	    }
	}
	if (strcmp(buff, "default") == 0)
	    return DEFAULT;
	if (strcmp(buff, "const") == 0)
	    return CONST;
    }
    yylval.string = mystrdup(buff);
    return ID;
}

static void skip_comment() {
    int c;

    for(;;) {
	while((c = getc(fpr)) != '*') {
	    if (c == EOF) {
	        yyerror("End of file in a comment");
		return;
	    }
	    if (c == '\n') {
		current_line++;
	    }
	}
	do {
	    if ((c = getc(fpr)) == '/')
		return;
	    if (c == '\n') {
		current_line++;
	    }
	} while(c == '*');
    }
}

static int yylex1() {
    register int c;
    
    for(;;) {
	int match_tmp;
#define MATCH(str) (isspace(line_buffer[match_tmp=strlen(str)]) &&\
			strncmp(str, line_buffer, match_tmp) == 0)

	char line_buffer[MAKE_FUNC_MAXLINE+1];

	switch(c = getc(fpr)) {
	  case ' ':
	  case '\t':
	    continue;
	  case '#':
	  {
	    int line;
	    char file[2048]; /* does any operating system support
				longer pathnames? */
	    fgets(line_buffer, MAKE_FUNC_MAXLINE, fpr);
	    if ( sscanf(line_buffer, "%d \"%s\"",&line,file ) == 2 ) {
		current_line = line+1;
		continue;
	    }
	    current_line++;
	    if MATCH("if") {
		handle_if('#', line_buffer+3);
	    } else if MATCH("ifdef") {
		handle_cond('#', lookup_define(nextword(line_buffer)) != 0);
	    } else if MATCH("ifndef") {
		handle_cond('#', lookup_define(nextword(line_buffer)) == 0);
		continue;
	    } else if MATCH("else") {
		handle_else('#');
	    } else if MATCH("endif") {
		handle_endif();
	    } else {
		yyerror("unrecognised '#' directive");
	    }
	    continue;
	  }
	  case '%':
	  {
	    static int send_end = 0;

	    if (send_end) {
		send_end = 0;
		ungetc('%', fpr);
		return END;
	    }
	    send_end = 1;
	    fgets(line_buffer, MAKE_FUNC_MAXLINE, fpr);
	    current_line++;
	    if (MATCH("tokens")) { current_code_class=C_TOKEN; return TOKENS; }
	    if (MATCH("codes"))  { current_code_class=C_CODE;  return CODES;  }
	    if (MATCH("efuns"))  { current_code_class=C_EFUN;  return EFUNS;  }
	    if (MATCH("xcodes")) { current_code_class=C_XCODE; return XCODES; }
	    if (MATCH("xefuns")) { current_code_class=C_XEFUN; return XEFUNS; }
	    if (MATCH("tcodes")) { current_code_class=C_TCODE; return TCODES; }
	    if (MATCH("tefuns")) { current_code_class=C_TEFUN; return TEFUNS; }
	    return '%';

#undef MATCH
	  }
	  case '\"':
	  {
	    char buff[100];
	    int len;

	    for (len=0; c = getc(fpr),c != '\"';) {
		if (len == sizeof buff - 1) {
		    yyerror("Too long name");
		    do
			c = getc(fpr);
		    while (c != '\"' && c != '\n' && c != EOF);
		    (void)ungetc(c, fpr);
		    break;
		}
		if (c == '\n' || c == EOF) {
		    yyerror("unterminated name");
		    (void)ungetc(c, fpr);
		    break;
		}
		buff[len++] = c;
	    }
	    buff[len] = '\0';
	    yylval.string = mystrdup(buff);
	    return NAME;
	  }
	  case '\n':
	    current_line++;
	    continue;
	  case EOF:
	    return -1;
	  case '/':
	    if ( (c=getc(fpr)) == '*') {
		skip_comment();
		continue;
	    } else {
		(void)ungetc(c, fpr);
		return '/';
	    }
	  default:
	    return isalpha(c) ? ident(c) : c;
	}
    }
}

int yylex() {
    int res;

    res = yylex1();
#if 0
    fprintf(stderr,"yylex returns %d '%c'\n",res,res);
#endif
    return res;
}

static void print_comp_type(int n) {
    if (n == TYPE_REFERENCE) {
	fputs("TYPE_REFERENCE", stdout);
	return;
    }
    if (n & MF_TYPE__ARRAY)
	fputs("TYPE__ARRAY|", stdout);
    n &= ~(MF_TYPE__ARRAY);
    if ((unsigned)n >= NELEM(types)) {
	mf_fatal("Bad type!");
    }
    fputs(types[n].cname, stdout);
}

static void print_typemask(unsigned long m) {
    unsigned long m2;

    if (m & ~(1<<TYPE_REFERENCE)) {
	m |= 1<<TYPE_ANY;
    }
    if (m & ~(1<<TYPE_REFERENCE) & -1<<TYPE__ARRAY) {
	m |= 1<<TYPE_ANY+TYPE__ARRAY;
    }
    if (m == ~(1<<TYPE_REFERENCE)) {
	fputs("~(1<<TYPE_REFERENCE)", stdout);
	return;
    }
    if (m == ~0) {
	fputs("~0", stdout);
	return;
    }
    if (m & 1<<TYPE_REFERENCE) {
	 fputs("1<<TYPE_REFERENCE", stdout);
	m &= ~(1<<TYPE_REFERENCE);
	if (m)
	    fputc('|', stdout);
	else return;
    }
    if ((m | (1<<TYPE__ARRAY)-1 | 1<<TYPE_REFERENCE) == (unsigned long)(-1)) {
	fputs("~(1<<TYPE_REFERENCE)&-1<<TYPE__ARRAY", stdout);
	m &= ~( ~(1<<TYPE_REFERENCE) & -1<<TYPE__ARRAY );
	if (m)
	    fputc('|', stdout);
	else return;
    }
    for (m2 = 1; ;m2 <<= 1) {
	if (m & m2) {
	    int i;

	    fputs("1<<", stdout);
	    for (i = 0; ; ) {
		if (m2 == types[i].mask) {
		    fputs(types[i].cname, stdout);
		    break;
		}
		if (m2>>TYPE__ARRAY && m2>>TYPE__ARRAY == types[i].mask) {
		    fputs(types[i].cname, stdout);
		    fputs("+TYPE__ARRAY", stdout);
		    break;
		}
		if (++i >= NELEM(types)) {
		    fprintf(stderr, "%lx %lx\n", m, m2);
		    mf_fatal("Bad argument to print_typemask\n");
		}
	    }
	    m &= ~m2;
	    if (!m)
		break;
	    putc('|', stdout);
	}
    }
}

static void print_error_nargs() {
    int i, nargs[IE_NUM_ERRORS];

    for (i = 0; i < NELEM(nargs); i++) {
	nargs[i] = 0;
    }
    nargs[IE_BAD_EFUN_ARG] = 2;
    nargs[IE_PRIVILEGED	] = 2;
    fputs("{\n", fpw);
    for (i = 0; i < NELEM(nargs); i++) {
	fprintf(fpw, "\t%d,\n", nargs[i]);
    }
    fputs("}\n", fpw);
}

static void print_ce_error_nargs() {
    int i, nargs[CE_NUM_ERRORS];

    for (i = 0; i < NELEM(nargs); i++) {
	nargs[i] = 0;
    }
    nargs[CE_VARNDECL	] = 1;
    nargs[CE_VARREDEF	] = 1;
    nargs[CE_NPARAM	] = 1;
    nargs[CE_DUPPAR	] = 1;
    nargs[CE_BADTYPE	] = 2;
    nargs[CE_NOMASK_SIM	] = 1;
    nargs[CE_NO_NUMELSE	] = 1;
    nargs[CE_NO_NUMENDIF] = 1;
    nargs[CE_CL_FUN_UNDEF] = 1;
    nargs[CE_MFILE] = 1;
    nargs[CE_NFILE] = 1;
    fputs("{\n", fpw);
    for (i = 0; i < NELEM(nargs); i++) {
	fprintf(fpw, "\t%d,\n", nargs[i]);
    }
    fputs("}\n", fpw);
}