ldmud-3.2.9/doc/
ldmud-3.2.9/doc/efun/
ldmud-3.2.9/mud/
ldmud-3.2.9/mud/heaven7/
ldmud-3.2.9/mud/heaven7/lib/
ldmud-3.2.9/mud/lp-245/
ldmud-3.2.9/mud/lp-245/banish/
ldmud-3.2.9/mud/lp-245/doc/
ldmud-3.2.9/mud/lp-245/doc/examples/
ldmud-3.2.9/mud/lp-245/doc/sefun/
ldmud-3.2.9/mud/lp-245/log/
ldmud-3.2.9/mud/lp-245/obj/Go/
ldmud-3.2.9/mud/lp-245/players/lars/
ldmud-3.2.9/mud/lp-245/room/death/
ldmud-3.2.9/mud/lp-245/room/maze1/
ldmud-3.2.9/mud/lp-245/room/sub/
ldmud-3.2.9/mud/lp-245/secure/
ldmud-3.2.9/mud/morgengrauen/
ldmud-3.2.9/mud/morgengrauen/lib/
ldmud-3.2.9/mud/sticklib/
ldmud-3.2.9/mud/sticklib/src/
ldmud-3.2.9/mudlib/uni-crasher/
ldmud-3.2.9/pkg/
ldmud-3.2.9/pkg/debugger/
ldmud-3.2.9/pkg/diff/
ldmud-3.2.9/pkg/misc/
ldmud-3.2.9/src/autoconf/
ldmud-3.2.9/src/bugs/
ldmud-3.2.9/src/bugs/MudCompress/
ldmud-3.2.9/src/bugs/b-020916-files/
ldmud-3.2.9/src/bugs/doomdark/
ldmud-3.2.9/src/bugs/ferrycode/ferry/
ldmud-3.2.9/src/bugs/ferrycode/obj/
ldmud-3.2.9/src/bugs/psql/
ldmud-3.2.9/src/done/
ldmud-3.2.9/src/done/order_alist/
ldmud-3.2.9/src/done/order_alist/obj/
ldmud-3.2.9/src/done/order_alist/room/
ldmud-3.2.9/src/gcc/
ldmud-3.2.9/src/gcc/2.7.0/
ldmud-3.2.9/src/gcc/2.7.1/
ldmud-3.2.9/src/hosts/
ldmud-3.2.9/src/hosts/GnuWin32/
ldmud-3.2.9/src/hosts/amiga/NetIncl/
ldmud-3.2.9/src/hosts/amiga/NetIncl/netinet/
ldmud-3.2.9/src/hosts/amiga/NetIncl/sys/
ldmud-3.2.9/src/hosts/i386/
ldmud-3.2.9/src/hosts/msdos/byacc/
ldmud-3.2.9/src/hosts/msdos/doc/
ldmud-3.2.9/src/hosts/os2/
ldmud-3.2.9/src/hosts/win32/
ldmud-3.2.9/src/util/
ldmud-3.2.9/src/util/erq/
ldmud-3.2.9/src/util/indent/hosts/next/
ldmud-3.2.9/src/util/xerq/
ldmud-3.2.9/src/util/xerq/lpc/
ldmud-3.2.9/src/util/xerq/lpc/www/
%{
/*---------------------------------------------------------------------------
 * make_func: Generation of the parser description and other tables
 *
 *---------------------------------------------------------------------------
 * make_func is an essential part of build process, as it creates the
 * actual LPC compiler description from a template, as well as some
 * internal tables from a list of code and efun specs.
 *
 * make_func takes one commandline argument which determines what it
 * should create:
 *
 *   make_func instrs
 *     creates efun_defs.c and instrs.h, reads config.h and func_spec.
 *
 *   make_func lang
 *     creates lang.y from prolang.y, reads config.h.
 *
 *   make_func strings
 *     creates stdstrings.[ch], reads config.h and string_spec.
 *
 * The calls are described in detail below.
 *---------------------------------------------------------------------------
 * make_func instrs
 * ----------------
 *
 * make_func reads the specifications of all interpreter bytecodes from
 * the file func_spec and creates the source files needed to compile
 * the driver.
 *
 * The bytecodes both cover interpreter internal instructions as well
 * as efuns. A complication is that every bytecode can occupy only one
 * byte, but there are more than 256 codes and functions. This is solved
 * by the use of multi-byte instructions, where the first byte is
 * one of a set of prefix bytes, followed by the instructions code
 * byte within the range for this prefix. The code ranges are:
 *    normal instructions:   0..255 (0x000..0x0ff)
 *          xcodes+xefuns: 256..383 (0x100..0x17f)
 *          tcodes+tefuns: 384..511 (0x180..0x1ff)
 *          vcodes+vefuns: 512..639 (0x200..0x27f)
 *
 * When make_func is run, it first reads the file 'config.h' to determine
 * which defines are used in the compilation of the driver. For this
 * make_func implements a simple C preprocessor, capable of numeric
 * expressions and simple macro expansions (no function macros), which
 * also allows the use of C-like block comments. With the knowledge of
 * the defines, make_func then reads the file 'func_spec'
 * with the information about the codes and efuns.
 *
 * From this information, make_func generates these files:
 *   efun_defs.c
 *     This file contains the table of all instructions, instrs[].
 *     The table defines for every machine instruction if it is an
 *     internal code or efun, what arguments of what type it takes, etc.
 *     The file also contains the tables for the driver-specific ctype
 *     implementation.
 *     It is included into the lexer lex.c and compiled there.
 *     Note that 'efun_defs.c' is not created directly, instead all
 *     this data is just printed on stdout.
 *
 *  instrs.h
 *     This file defines the bytecodes representing all the efuns
 *     and non-efun instructions which are not compiler tokens.
 *
 *
 * func_spec is divided into several sections, each introduced with its
 * own %-keyword. The required order and meaning of the sections is this:
 *
 *     %codes
 *         Internal machine codes used by the compiler to generate code.
 *         The first codes must be the special codes 'illegal', 'dummy',
 *         'escape', 'tefun' and 'vefun. The interpreter requires these
 *         to have fixed values in order to apply simple bit-manipulation.
 *         TODO: Bad interpreter. No cookie.
 *
 *     %efuns
 *         The efuns which are represented by one bytecode.
 *
 *     %xcodes
 *         Internal machine codes used by the compiler to generate code.
 *         These codes are multi-byte codes, using 'escape' as prefix.
 *
 *     %xefuns
 *         The efuns which are represented by 'escape' prefix codes.
 *
 *     %tcodes
 *         Internal machines codes, represented by 'tefun' prefix codes.
 *
 *     %tefuns
 *         The tabled efuns which are represented by 'tefun' prefix codes,
 *         resp. 'vefun' prefix codes if the efun takes an arbitrary
 *         number of arguments.
 *         The special thing about these functions is that their 'instruction
 *         code' is in fact just an index into (v)efun_table[] generated
 *         in efun_defs.c which holds the address of the actual function.
 *
 * The entries in the sections are separated by whitespace.
 *
 * A token or code entry follows this syntax:
 *
 *    [ "name" ] id
 *
 *        <id> is the name used in the compiler source, the generated token
 *        will be called F_<ID>. If <name> is specified, it will be used
 *        in tracedumps instead of the plain <id>.
 *
 * An efun is defined this way:
 *
 *    ret-type name [ alias ] ([argtype-1, ... , argtype-n]) [ "msg" ];
 *
 *        This is the efun <name>, with the compiler source name F_<NAME>,
 *        which returns a value of type <ret-type> and takes n >= 0
 *        arguments of type <argtype-x>. If <alias> is given, it must
 *        be the source name (F_FOO) of another efun this one is an
 *        alias for.
 *
 *        The types are given in the usual LPC syntax, with the additional
 *        type of 'unknown' for return types.
 *
 *        Argument types can be the combination of several types in
 *        the form 'type|type|...', e.g. 'string|object'. If one of the
 *        types is 'void', the argument is optional (but then only optional
 *        arguments may follow).
 *
 *        The last argument can take a default value, which is then specified
 *        as 'default: F_FOO' with F_FOO being the source name of an efun
 *        yielding the desired value. Example:
 *            ..(object default: F_THIS_OBJECT)
 *
 *        Alternatively, the last argument can be given as '...' if the
 *        efun can handle arbitrarily many arguments.
 *
 *        If the efun is deprecated, <msg> is the warning message to
 *        print when the efun is used and pragma warn_deprecated is in
 *        effect. The message will be prefixed by the compiler
 *        with "<name> is deprecated: ".
 *
 *---------------------------------------------------------------------------
 * make_func lang
 * --------------
 *
 * make_func implements a preprocessor used to generate the LPC compiler
 * lang.y from the file prolang.y . This step is necessary because
 * no known yacc allows to enable or disable rules conditinally, like
 * for INITIALIZATION_BY___INIT.
 *
 * When make_func is run, it first reads the file 'config.h' to determine
 * which defines are used in the compilation of the driver. For this
 * make_func implements a simple C preprocessor, capable of numeric
 * expressions and simple macro expansions (no function macros), which
 * also allows the use of C-like block comments. It also performs
 * a short test with the configured yacc to test its limitations.
 *
 * From this information, make_func generates this file:
 *  lang.y
 *     This is the LPC compiler, which is created by reading and
 *     modifying the template prolang.y . During this process, these
 *     keywords are recognized and replaced:
 *       %line:  generates a #line statement to synchronize the C compiler.
 *       %typemap: generates a lookup table using types as indices
 *                 (see handle_map()).
 *       %hookmap: generates a lookup table using driverhooks as indices.
 *     In addition, the keywords %if, %elif, %else and %endif can be
 *     used in a preprocessor fashion to control which parts of prolang.y
 *     end up in lang.y and which don't.
 *---------------------------------------------------------------------------
 * make_func strings
 * -----------------
 *
 * make_func reads from the file string_spec the definition of all strings
 * which the driver shall predefine as shared strings. It creates the
 * source files stdstrings.[ch] with the implementation.
 *
 * The strings are mostly the names of lfuns called by the interpreter.
 * Predefining them as shared strings and using the resulting pointers
 * speeds up the function calls.
 *
 * string_spec is read line by line, each line defining one string:
 *
 *    id "string"
 *
 *    <id> is the symbolic name used in the compiler source, for the
 *    string <string>. The symbolic name will be made upper case and
 *    is used in two forms:
 *       STR_<id>: the pointer to the shared string
 *       SHX_<id>: the index of the shared string in the table of
 *                 all predefined strings.
 *
 * And that's it.
 *---------------------------------------------------------------------------
 */

#undef lint  /* undef so that precompiled headers can be used */

#include "driver.h"

#include "my-alloca.h"
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>

#include "exec.h"
#include "hash.h"

#include "../mudlib/sys/driver_hook.h"

#define lint  /* redef again to prevent spurious warnings */

/* driver.h defines its own memory management functions,
 * which we don't need.
 */
#undef malloc
#undef realloc
#undef free

/*-------------------------------------------------------------------------*/

/* Filenames used by make_func */

#define FUNC_SPEC    "func_spec"
  /* The input file with the description of all machine instructions
   * and efuns.
   */

#define STRING_SPEC  "string_spec"
  /* The input file with the definition of all standard
   * shared strings.
   */

#define CONFIG       "config.h"
  /* The configuration file.
   */

#define PRO_LANG     "prolang.y"
  /* The LPC parser template.
   */

#define THE_LANG     "lang.y"
  /* The LPC parser to generate.
   */

#define THE_INSTRS   "instrs.h"
  /* The instruction and ctype table declarations to generate.
   */

#define EFUN_DEFS    "efun_defs.c"
  /* The instruction and ctype table definitions to generate.
   */

#define STDSTRINGS   "stdstrings"
  /* The basename (without extension) of the standard shared
   * string implementation files to generate.
   */

/*-------------------------------------------------------------------------*/

#define MAKE_FUNC_MAXLINE    4096
  /* Maximum length of an input line and other buffers.
   */

#define MAX_FUNC      2048
#define MAX_TOKENS    2048
  /* Maximum number of functions and tokens we care to handle.
   */

#define MAX_ARGTYPES  500
  /* Size of the arg_types[] array.
   */

#define MF_TYPE_MOD_POINTER   0x10000
#define MF_TYPE_MOD_REFERENCE 0x20000
  /* Type modifier for array and reference types.
   * These values are bitflags which are |'ed onto the type values
   * produced by the parser (INT, STRING, etc).
   */

/*-------------------------------------------------------------------------*/

#if defined(AMIGA) && !defined(__GNUC__)
#    define isascii(c) ((c) >= 0 && (c) <= 255)
#endif

#undef isalunum
#define isalunum(c) (isascii((unsigned char)c) && (isalnum((unsigned char)c) || (c) == '_'))

#define lexwhite(c) (isascii((unsigned char)c) && isspace((unsigned char)c) && (c) != '\n')

#undef lexdigit
#define lexdigit(c) (isascii((unsigned char)c) && isdigit((unsigned char)c))
  /* Our own char classification predicates.
   */

#define NELEMS(arr)    (sizeof arr / sizeof arr[0])
  /* Handy macro to statically determine the number of elements in
   * an array.
   */

/*-------------------------------------------------------------------------*/

/* Parsed functions and instructions are put into different classes.
 * These are the class type definitions and associated predicate macros.
 */

#define C_CODE    0  /* Internal machine instructions */
#define C_EFUN    1  /* Efuns with own instruction codes */
#define C_XCODE   2  /* Dito for the 'escape' prefix */
#define C_XEFUN   3
#define C_TCODE   4  /* Dito for the 'tefun' prefix */
#define C_TEFUN   5
#define C_VCODE   6  /* Dito for the 'vefun' prefix */
#define C_VEFUN   7
#define C_ALIAS   8  /* Aliased efuns */
#define C_TOTAL   9  /* Number of different classes */

#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)

/*-------------------------------------------------------------------------*/

static int num_buff = 0;
  /* Total number of instructions encountered so far.
   */

static int num_instr[C_TOTAL] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
  /* Number of instructions encountered, counted separately for
   * every type.
   */

struct instrdata_s {
    char *f_name;      /* 'F-'-name of code/efun */
    char *key;         /* internal name of code/efun */
    char *buf;         /* instrs[] entry for code/efun */
    int   code_class;  /* code class of code/efun */
} instr[MAX_FUNC];
  /* Array describing all codes and efuns encountered in FUNC_SPEC.
   * The array is built in the order found in func_spec and later
   * sorted by code_class and key.
   *
   * Also used to hold all string definitions found in STRING_SPEC:
   *   .key is (in upper case) the symbolic name of the string;
   *   .buf is the actual string text.
   */

static int arg_types[MAX_ARGTYPES];
  /* All distinct function argument signatures encountered so far.
   * One signature is simply a list of all argument types, identified
   * by the starting index of the first argument's type.
   * The description of one argument's type is itself a list of the
   * typecodes, terminated by 0.
   * Different signatures may overlap, e.g. a (T_STRING) signature
   * could be embedded in a (T_POINTER, T_INT|T_STRING) signature.
   *
   * The content of this array is later written as efun_arg_types[]
   * into EFUN_DEFS and used by the instrs[].arg_index entries.
   */

static int last_current_type;
  /* Index of the first unused entry in arg_types[].
   */

/*-------------------------------------------------------------------------*/

/* Variables used when parsing the lines of FUNC_SPEC */

static int got_error = 0;
  /* Set to TRUE if an error was found.
   */

static int min_arg = -1;
  /* Minimum number of arguments for this function if there
   * are optional arguments, or -1 if all arguments are needed.
   */

static Bool limit_max = MY_FALSE;
  /* True when the last argument for a function is the '...'
   */

static int current_code_class;
  /* The current code class (C_CODE, C_EFUN, ...) a parsed identifier
   * shall be assigned to.
   */

static int curr_arg_types[MAX_LOCAL];
  /* The function argument signature of the current function.
   * The signature is a list of all argument types, which themselves
   * are lists of typecodes, each argument's list terminated by 0.
   */

static int curr_arg_type_size;
  /* Index of the first unused entry in curr_arg_types[].
   */

/*-------------------------------------------------------------------------*/

/* Forward declarations */

static void yyerror(const char *);
static int yylex(void);
int yyparse(void);
int ungetc(int c, FILE *f);
static const char *type_str(int);
static const char *etype(int);
static const char *etype1(int);
static const char *ctype(int);
#ifndef toupper
int toupper(int);
#endif
static int fatal(const char *str);
static int cond_get_exp(int);

/*-------------------------------------------------------------------------*/
static char *
mystrdup (const char *str)

/* Copy <str> into a freshly allocated memory block and return that one.
 *
 * This function is needed on Ultrix 4.2 which doesn't seem to know strdup().
 */

{
    char *copy = malloc(strlen(str)+1);
    if (!copy)
        fatal("strdup failed\n");
    strcpy(copy, str);
    return copy;
}

/*-------------------------------------------------------------------------*/
static int
fatal (const char *str)

/* Print <str> on stderr, flush stdout and exit the program with
 * exitcode 1.
 */

{
    fprintf(stderr, "%s", str);
    fflush(stdout);
    exit(1);
    /* NOTREACHED */
    return 0;
}

/*-------------------------------------------------------------------------*/
static char *
make_f_name (char *str)

/* Take <str> and return a string 'F_<STR>' in its own memory block.
 */

{
    char f_name[500];
    size_t i, len;

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

/*-------------------------------------------------------------------------*/
static int
check_for_duplicate_instr (const char *f_name, const char *key, int redef_ok)

/* Check if either <f_name> or <key> already appear in instr[], and print
 * appropriate diagnostics. If <redef_ok> is true, a new <key> for an existing
 * <f_name> is allowed.
 * Return true if there is a duplicate for <f_name>, false if not.
 */

{
    size_t i;
    int rc;

    rc = 0;

    for (i = 0; i < (size_t)num_buff; i++)
    {
        if (!strcmp(f_name, instr[i].f_name))
        {
            if (!strcmp(key, instr[i].key))
            {
                rc = 1;
                got_error = 1;
                fprintf(stderr, "Error: Entry '%s':'%s' duplicated.\n"
                              , f_name, key);
            }
            else if (!redef_ok)
            {
                rc = 1;
                got_error = 1;
                fprintf(stderr, "Error: Entry '%s':'%s' redefined to '%s'.\n"
                              , f_name, instr[i].key, key);
            }
        }
        else if (!strcmp(key, instr[i].key))
        {
             fprintf(stderr, "Entry '%s':'%s' duplicated as '%s':... .\n"
                           , instr[i].f_name, instr[i].key, f_name);
        }

    }
    return rc;
}

/*-------------------------------------------------------------------------*/
static int
check_for_duplicate_string (const char *key, const char *buf)

/* Check if either <key> or <buf> already appear in instr[], and print
 * appropriate diagnostics.
 * Return true if there is a duplicate for <key>, false if not.
 */

{
    size_t i;
    int rc;

    rc = 0;

    for (i = 0; i < (size_t)num_buff; i++)
    {
        if (!strcmp(key, instr[i].key))
        {
            rc = 1;
            got_error = 1;
            if (!strcmp(buf, instr[i].buf))
            {
                fprintf(stderr, "Error: Entry '%s':'%s' duplicated.\n"
                              , key, buf);
            }
            else
            {
                fprintf(stderr, "Error: Entry '%s':'%s' redefined to '%s'.\n"
                              , key, instr[i].buf, buf);
            }
        }
        else if (!strcmp(buf, instr[i].buf))
        {
             fprintf(stderr, "Warning: Entry '%s':'%s' duplicated as '%s':... .\n"
                           , instr[i].key, instr[i].buf, key);
        }

    }
    return rc;
}

#if defined(__MWERKS__) && !defined(WARN_ALL)
#    pragma warn_possunwant off
#    pragma warn_implicitconv off
#endif
%}

/*=========================================================================*/

/*                           P A R S E R                                   */

/*-------------------------------------------------------------------------*/

%union {
    int number;
    char *string;
}

%token PARSE_FUNC_SPEC PARSE_STRING_SPEC

%token NAME ID

%token VOID INT STRING OBJECT MAPPING FLOAT CLOSURE SYMBOL QUOTED_ARRAY
%token MIXED UNKNOWN

%token DEFAULT

%token CODES EFUNS XCODES XEFUNS TCODES TEFUNS END

%type <number> VOID MIXED UNKNOWN
%type <number> INT STRING OBJECT MAPPING FLOAT CLOSURE SYMBOL QUOTED_ARRAY
%type <number> basic arg_type
  /* Value is the basic type value
   */

%type <number> opt_star opt_ref
  /* Value is the appropriate bitflag or 0.
   */

%type <number> type
  /* Value is the complete type, incl. *-  and &-modifier bitflags
   */

%type <number> arg_list typel typel2
  /* Value is the number of arguments (so far)
   */

%type <string> ID optional_ID optional_default NAME optional_name
  /* Value is the parsed identifier or NULL (resp. "" for optional_ID).
   */

%%

all: PARSE_FUNC_SPEC   func_spec
   | PARSE_STRING_SPEC string_spec
;

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/

func_spec: codes END efuns END xcodes END xefuns END tcodes END tefuns;

codes:    CODES
        | codes code;

xcodes:   XCODES
        | xcodes code;

tcodes:   TCODES
        | tcodes code;

efuns:    EFUNS  funcs;

xefuns:   XEFUNS funcs;

tefuns:   TEFUNS funcs;

/* --- Codes --- */

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

code:     optional_name ID
    {
        char *f_name, buff[500];

        if (num_buff >= MAX_FUNC)
            yyerror("Too many codes and efuns in total!\n");
        if (!$1)
            $1 = mystrdup($2);
        f_name = make_f_name($2);
        check_for_duplicate_instr(f_name, $2, 0);
        instr[num_buff].code_class = current_code_class;
        num_instr[current_code_class]++;
        sprintf(buff, "{ 0, 0, { 0, 0 }, -1, 0, 0, \"%s\", NULL },\n", $1);
        if (strlen(buff) > sizeof buff)
            fatal("Local buffer overflow!\n");
        instr[num_buff].f_name = f_name;
        instr[num_buff].key = mystrdup($2);
        instr[num_buff].buf = mystrdup(buff);
        num_buff++;
        free($1);
    }
    ;

/* --- Efuns --- */

funcs:   /* empty */
       | funcs func ;

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

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

func: type ID optional_ID '(' arg_list optional_default ')' optional_name ';'
    {
        char buff[1000];
        char *f_name;
        int i;

        if (num_buff >= MAX_FUNC)
            yyerror("Too many codes and efuns in total!\n");

        if (min_arg == -1)
            min_arg = $5;

        /* Make the correct f_name and determine the code class
         */
        if ($3[0] == '\0')
        {
            f_name = make_f_name($2);
            check_for_duplicate_instr(f_name, $2, 0);
            num_instr[
              (int)(instr[num_buff].code_class =
                (limit_max || $5 != min_arg) && current_code_class == C_TEFUN ?
                  C_VEFUN : current_code_class)
            ]++;
        }
        else
        {
            f_name = mystrdup($3);
            check_for_duplicate_instr(f_name, $2, 1);
            instr[num_buff].code_class = C_ALIAS;
            num_instr[C_ALIAS]++;
        }

        /* Search the function's signature in arg_types[] */

        for (i = 0; i < last_current_type; i++)
        {
            int j;
            for (j = 0; j+i < last_current_type && j < curr_arg_type_size; j++)
            {
                if (curr_arg_types[j] != arg_types[i+j])
                    break;
            }
            if (j == curr_arg_type_size)
                break;
        }

        if (i == last_current_type)
        {
            /* It's a new signature, put it into arg_types[] */
            int j;
            for (j = 0; j < curr_arg_type_size; j++)
            {
                arg_types[last_current_type++] = curr_arg_types[j];
                if (last_current_type == NELEMS(arg_types))
                    yyerror("Array 'arg_types' is too small");
            }
        }

        /* Store the data.
         * etype() used a static buffer, so we must separate the calls.
         */

        sprintf(buff, "{ %d, %d, { %s, "
                    , limit_max ? -1 : $5, min_arg
                    , etype(0)
               );
        sprintf(buff+strlen(buff), " %s }, %s, %s, %d, \"%s\""
                                 , etype(1) , $6, ctype($1), i, $2
               );
        if ($8 != NULL)
            sprintf(buff+strlen(buff), ", \"%s\""
                                     , $8
                   );
        else
            strcat(buff, ", NULL");

        strcat(buff, " },\n");

        if (strlen(buff) > sizeof buff)
             fatal("Local buffer overwritten !\n");

        instr[num_buff].f_name = f_name;
        instr[num_buff].key = mystrdup($2);
        instr[num_buff].buf = mystrdup(buff);
        num_buff++;

        /* Reset for next function */

        min_arg = -1;
        limit_max = MY_FALSE;
        curr_arg_type_size = 0;
    } ;

/* --- Types and Argument lists --- */

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

basic: VOID | INT | STRING | MAPPING | FLOAT | MIXED | OBJECT | CLOSURE |
        UNKNOWN | SYMBOL | QUOTED_ARRAY ;

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

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

arg_list: /* empty */             { $$ = 0; }
        | typel2                  { $$ = 1; if ($1) min_arg = 0; }
        | arg_list ',' typel2     { $$ = $1 + 1; if ($3) min_arg = $$ - 1; } ;

typel2: typel
    {
        $$ = $1;
        curr_arg_types[curr_arg_type_size++] = 0;
        if (curr_arg_type_size == NELEMS(curr_arg_types))
            yyerror("Too many arguments");
    } ;

arg_type: type
    {
        if ($1 != VOID)
        {
            curr_arg_types[curr_arg_type_size++] = $1;
            if (curr_arg_type_size == NELEMS(curr_arg_types))
                yyerror("Too many arguments");
        }
        $$ = $1;
    } ;

typel: arg_type              { $$ = ($1 == VOID && min_arg == -1); }
     | typel '|' arg_type    { $$ = (min_arg == -1 && ($1 || $3 == VOID));}
     | '.' '.' '.'           { $$ = min_arg == -1 ; limit_max = MY_TRUE; } ;

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/

string_spec: stringdefs ;

stringdefs:  /* empty */
       | stringdefs stringdef ;

stringdef: ID NAME
    {
        char *cp;

        if (num_buff >= MAX_FUNC)
            yyerror("Too many string definitions!\n");

        /* Copy the data parsed into the instr[] array */
        /* Make the correct f_name and determine the code class
         */
        check_for_duplicate_string($1, $2);
        instr[num_buff].key = mystrdup($1);
        instr[num_buff].buf = mystrdup($2);

        /* Make sure that the .key is all uppercase */
        for (cp = instr[num_buff].key; *cp != '\0'; cp++)
            if (isalpha((unsigned char)*cp) && islower((unsigned char)*cp))
                *cp = toupper(*cp);

        /* Prepare for next string */

        num_buff++;
    }
; /* stringdef */

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/

%%

#ifdef __MWERKS__
#    pragma warn_possunwant reset
#    pragma warn_implicitconv reset
#endif

/*=========================================================================*/

/*                            L E X E R                                    */

/* The Lexer is used to parse CONFIG, FUNC_SPEC and STRING_SPEC,
 * and to create THE_LANG from PRO_LANG.
 */

/*-------------------------------------------------------------------------*/

/* The recognized type names */

struct type {
    const char *name;  /* name of the type */
    int         num;   /* the type's parser code */
};

static struct type types[]
  = { { "void",         VOID }
    , { "int",          INT }
    , { "string",       STRING }
    , { "object",       OBJECT }
    , { "mapping",      MAPPING }
    , { "float",        FLOAT }
    , { "closure",      CLOSURE }
    , { "symbol",       SYMBOL }
    , { "quoted_array", QUOTED_ARRAY }
    , { "mixed",        MIXED }
    , { "unknown",      UNKNOWN }
    };

/*-------------------------------------------------------------------------*/

/* Defined macros.
 *
 * The macros are kept in the usual hashtable.
 */

#define MAKE_FUNC_DEFHASH 101
#define defhash(str) (whashstr((str), 12) % MAKE_FUNC_DEFHASH)

struct defn {
    char *name;         /* Macro name */
    char *exps;         /* Expanded macro text */
    int  num_arg;       /* Number of arguments or -1 for plain macros */
    struct defn *next;  /* Next entry in the hash chain */
};

static struct defn *deftab[MAKE_FUNC_DEFHASH];

/*-------------------------------------------------------------------------*/

/* The stack to handle pending #ifs.
 */

static struct ifstate {
    struct ifstate *next;
    int state;
} *iftop = NULL;

/* Values of ifstate.state: */

#define EXPECT_ELSE   1
#define EXPECT_ENDIF  2

/*-------------------------------------------------------------------------*/

static FILE *fpr, *fpw;
  /* Input and output file.
   */

static int current_line;
  /* Number of the current line.
   */

static char *current_file;
  /* Name of the current file.
   */

static int last_line;
  /* Last line outside an %if-block.
   */

static char *outp;
  /* Next unprocessed character in the input buffer
   */

static int parsetype;
  /* Either PARSE_FUNC_SPEC or PARSE_STRING_SPEC, sent as first token back
   * to the parser.
   */

static Bool parsetype_sent = MY_FALSE;
  /* Set to TRUE after parsetype was sent to the parser.
   */

/*-------------------------------------------------------------------------*/

/* The definitions and tables for the preprocessor expression evaluator.
 * For a detailed explanation look into lex.c - it uses slightly
 * different tables, but the basic structure is the same.
 */


#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(c) (_optab[(c)-' '])

/*-------------------------------------------------------------------------*/
static char
mygetc (void)

/* Return the next character from the input buffer.
 */

{
    return *outp++;
}

/*-------------------------------------------------------------------------*/
static void
myungetc (char c)

/* Unput character <c> so that the next mygetc() returns it.
 */

{
    *--outp = c;
}

/*-------------------------------------------------------------------------*/
static void
add_input (const char *p)

/* Insert text <p> at the current point in the input stream so that
 * the next mygetc()s will read it.
 */

{
    size_t l;

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

/*-------------------------------------------------------------------------*/
static void
add_define (const char * name, int num_arg, const char *exps)

/* Add the definition for the macro <name> with <num_arg> arguments
 * and replacement text <exps> to the table of macros.
 */

{
    int i;
    struct defn *ndef;

    i = defhash(name);
    ndef = malloc(sizeof(struct 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 defn *
lookup_define (const char *s)

/* Lookup the macro <s> and return a pointer to its defn structure.
 * Return NULL if the macro is not defined.
 */

{
    struct 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 NULL;
}

/*-------------------------------------------------------------------------*/
static int
expand_define (char *s)

/* Try to expand macro <s>.
 * If it is not defined, just return 0.
 * If it is a plain macro, add it to the input stream and return 1.
 * If it is a function macro, just return -1.
 */

{
    struct 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)

/* Find the next word in the input stream starting with <str> and
 * return a pointer to its first character. The end of the word
 * is marked with a '\0'.
 */

{
    char *cp;

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

/*-------------------------------------------------------------------------*/
static Bool
skip_to (char mark, const char *token, const char *atoken)

/* Skip the file fpr linewise until one of the following control statements
 * is encountered:
 *   <mark>token:  returns true
 *   <mark>atoken: returns false (only if <atoken> is not NULL).
 *
 * An end of file aborts the program.
 * Nested <mark>if...<mark>endif blocks are skipped altogether.
 */

{
    char  b[20]; /* The word after <mark> */
    char *p;
    int   c;     /* Current character */
    int   nest;  /* <mark>if nesting depth */
    FILE *fp = fpr;

    for (nest = 0;;)
    {
        current_line++;
        c = fgetc(fp);

        if (c == mark)
        {
            /* Get the statement word into b[] */

            do {
                c = fgetc(fp);
            } while(lexwhite(c));

            for (p = b; c != '\n' && c != EOF; )
            {
                if (p < b+sizeof b-1)
                    *p++ = (char)c;
                c = fgetc(fp);
            }
            *p++ = '\0';
            for(p = b; *p && !lexwhite(*p); p++) NOOP;
            *p = '\0';

            if (strcmp(b, "if") == 0
             || strcmp(b, "ifdef") == 0
             || strcmp(b, "ifndef") == 0)
            {
                nest++;
            }
            else if (nest > 0)
            {
                if (strcmp(b, "endif") == 0)
                    nest--;
            }
            else
            {
                if (strcmp(b, token) == 0)
                    return MY_TRUE;
                else if (atoken && strcmp(b, atoken) == 0)
                    return MY_FALSE;
            }
        }
        else
        {
            /* Skip the line altogether */

            while (c != '\n' && c != EOF)
            {
                c = fgetc(fp);
            }
            if (c == EOF)
            {
                fprintf(stderr, "Unexpected end of file while skipping");
                abort();
            }
        }
    } /* for() */

    /* NOTREACHED */
} /* skip_to() */

/*-------------------------------------------------------------------------*/
static void
compensate_lines (void)

/* To compensate for skipped blocks, print as many \n to fpw as
 * the difference between last_line and current_line determines.
 * last_line is current_line+1 at the end.
 */
{
    for (; last_line <= current_line; last_line++)
        fputc('\n', fpw);
}

/*-------------------------------------------------------------------------*/
static void
handle_cond (char mark, int c)

/* Evaluate the boolean condition <c> of a preprocessor #if statement.
 * If necessary, skip to the condition branch to read next, and/or
 * push a new state onto the ifstate-stack.
 * If <mark> is '%', the number of skipped lines is compensated.
 */

{
    struct ifstate *p;

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

/*-------------------------------------------------------------------------*/
static void
handle_if (char mark, const char *str)

/* Evaluate the <mark>if condition <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') NOOP;
    }
    else
        handle_cond(mark, cond);
}

/*-------------------------------------------------------------------------*/
static void
handle_else (char mark)

/* Handle an <mark>else statement.
 */

{
    if (iftop && iftop->state == EXPECT_ELSE)
    {
        struct ifstate *p = iftop;

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

/*-------------------------------------------------------------------------*/
static void
handle_endif (void)

/* Handle an <mark>else statement.
 */

{
    if (iftop
     && (   iftop->state == EXPECT_ENDIF
         || iftop->state == EXPECT_ELSE))
    {
        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)

/* Return the proper TYPE_ value for the type <name> which must
 * start with 'TYPE_'.
 * Return -1 if the name was not recognized.
 */

{
    while (isspace((unsigned char)*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, "STRING") )
        return TYPE_STRING;
    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)

/* Return the proper H_ value for the driverhook <name> which must
 * start with 'H_'
 * Return -1 if the name was not recognized.
 */

{
    while (isspace((unsigned char)*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_UIDS") )
        return H_LOAD_UIDS;
    if ( !strcmp(name, "CLONE_UIDS") )
        return H_CLONE_UIDS;
    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, "MODIFY_COMMAND") )
        return H_MODIFY_COMMAND;
    if ( !strcmp(name, "NOTIFY_FAIL") )
        return H_NOTIFY_FAIL;
    if ( !strcmp(name, "NO_IPC_SLOT") )
        return H_NO_IPC_SLOT;
    if ( !strcmp(name, "INCLUDE_DIRS") )
        return H_INCLUDE_DIRS;
    if ( !strcmp(name, "TELNET_NEG") )
        return H_TELNET_NEG;
    if ( !strcmp(name, "NOECHO") )
        return H_NOECHO;
    if ( !strcmp(name, "ERQ_STOP") )
        return H_ERQ_STOP;
    if ( !strcmp(name, "MODIFY_COMMAND_FNAME") )
        return H_MODIFY_COMMAND_FNAME;
    if ( !strcmp(name, "COMMAND") )
        return H_COMMAND;
    if ( !strcmp(name, "SEND_NOTIFY_FAIL") )
        return H_SEND_NOTIFY_FAIL;
    if ( !strcmp(name, "AUTO_INCLUDE") )
        return H_AUTO_INCLUDE;
    return -1;
}

/*-------------------------------------------------------------------------*/
static void
handle_map (char *str, int size, int (* name_to_index)(char *) )

/* Create a lookup table with <size> elements from the input
 * text <str> (and possibly more lines read from fpr if <str>
 * ends in \<newline>).
 *
 * The input text is supposed to be a comma separated list
 * of <index>:<value> pairs: the text "<index>" is parsed
 * and translated into a number by <name_to_index>().
 * <value> is the assigned to the table element indexed
 * by this number.
 *
 * Unassigned table elements default to the text '0' or
 * to the value set with a 'default:<value>' pair.
 *
 * After the list of pairs is read, the text for the table
 * is written as '{ <value>, ... , <value> }' to fpw.
 */

{
    char **map;        /* The map */
    char deflt[256];   /* Default table entry text */
    char *del = NULL;  /* Position of the current ':' or ',' */
    char *val;         /* Position of the current <value> */
    int i;
    char *output_del = "";  /* How to separate table entries */

    /* Initialize the map */

    map = (char **)alloca(size * sizeof *map);
    strcpy(deflt, "0");
    for (i = 0; i < size; i++) {
        map[i] = deflt;
    }

    /* Process the input list */

    do {

        /* Set str to the next non-blank character,
         * reading a new line if necessary
         */
        while (isspace((unsigned char)*str))
            str++;
        if (*str == '\\')
        {
            str = alloca(MAKE_FUNC_MAXLINE + 1);
            if (!fgets(str, MAKE_FUNC_MAXLINE, fpr))
                break;
            current_line++;
            if (del)
            {
                output_del = "\n";
            }
        }

        /* Find and mark the elements of the next pair */

        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 = NULL;
            }
        }
        else
        {
            *del = '\0';
        }

        /* Evaluate the current pair */
        if ( !strcmp(str, "default") )
        {
            strncpy(deflt, val, sizeof(deflt));
            deflt[sizeof deflt - 1] = '\0';
        }
        else
        {
            i = (*name_to_index)(str);
            if (i < 0)
            {
                fprintf(stderr, "Can't translate '%s' into an index.\n", str);
                exit(-1);
            }
            map[i] = val;
        }
        str = del+1;
    } while (del);

    /* Write the generated map */

    fprintf(fpw, "{");
    fputs(output_del, fpw);
    for (i = 0; i < size; i++)
    {
        fprintf(fpw, "%s,", map[i]);
        fputs(output_del, fpw);
    }
    fprintf(fpw, "};\n");
} /* handle_map() */

/*-------------------------------------------------------------------------*/
static int
exgetc(void)

/* Get the first character of the next element of a condition
 * and return it, leaving the input pointing to the rest of it.
 * Comments are skipped, identifiers not defined as macros are
 * replaced with ' 0 ', the predicate 'defined(<name>)' is
 * replaced with ' 0 ' or ' 1 ' depending on the result.
 */

{
    char c;

    c = mygetc();
    while (isalpha((unsigned char)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)
             fatal("Too long word.\n");
        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;
}

/*-------------------------------------------------------------------------*/
static int
cond_get_exp (int priority)

/* Evaluate the expression in the input buffer at a priority of at least
 * <priority> and return the result.
 *
 * The function assumes to be called at the proper beginning of
 * an expression, i.e. if it encounters an operator even before a value,
 * it must be unary.
 */

{
    int c;
    int value,value2,x;

    do c = exgetc(); while ( lexwhite(c) );

    /* Evaluate the first value */

    if (c == '(')
    {

        /* It's a parenthesized subexpression */

        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) )
    {
        /* It is an unary operator */

        x = optab1(c);
        if (!x)
        {
            yyerror("illegal character in #if");
            return 0;
        }

        /* Get the value for this unary operator */
        value = cond_get_exp(12);

        /* Evaluate the operator */
        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
    {
        /* It must be a number */

        int base;

        if ( !lexdigit(c) )
        {
            if (!c)
            {
                yyerror("missing expression in #if");
                myungetc('\0');
            }
            else
                yyerror("illegal character in #if");
            return 0;
        }
        value = 0;

        /* Determine the base of the number */
        if (c != '0')
            base=10;
        else
        {
            c = mygetc();
            if (c == 'x' || c == 'X' )
            {
                base = 16;
                c = mygetc();
            }
            else
                base = 8;
        }

        /* Now parse the number */
        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((char)c);
    }

    /* Now evaluate the following <binop> <expr> pairs (if any) */

    for (;;)
    {
        do c = exgetc(); while ( lexwhite(c) );

        /* An operator must come next */
        if ( !ispunct(c) )
            break;

        /* Can it be an operator at all? */
        x = optab1(c);
        if (!x)
            break;

        /* See if the optab[] defines an operator for these characters
         */
        value2 = mygetc();
        for (;; x += 3)
        {
            if (!optab2[x])
            {
                myungetc((char)value2);
                if ( !optab2[x+1] )
                {
                    yyerror("illegal operator use in #if");
                    return 0;
                }
                break;
            }
            if (value2 == optab2[x])
                break;
        }

        /* If the priority of the operator is too low, we are done
         * with this (sub)expression.
         */
        if (priority >= optab2[x+2])
        {
            if (optab2[x])
                myungetc((char)value2);
            break;
        }

        /* Get the second operand and perform the operation */
        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((char)c);
                return 0;
            }
            if ( value )
            {
                cond_get_exp(1);
                value = value2;
            }
            else
                value = cond_get_exp(1);
            break;
        } /* switch() */
  } /* for() */

  myungetc((char)c);
  return value;
} /* cond_get_expr() */

/*-------------------------------------------------------------------------*/
static Bool
make_func_isescaped (char c)

/* Return true if <c> is one of the escapable characters (e.g. \r or \"),
 * false if not.
 */

{
    switch(c) {
      case '\007':
      case '\b'  :
      case '\t'  :
      case '\n'  :
      case '\013':
      case '\014':
      case '\r'  :
        return MY_TRUE;
    }
    if (c == '\\' || c == '\"')
        return MY_TRUE;
    return MY_FALSE;
}

/*-------------------------------------------------------------------------*/
static int
ident (char c)

/* Parse an identifier (first character is <c>) from fpr and classify it.
 * The typenames in types[] return the associated type code, the
 * string "default" returns DEFAULT (both only when currently parsing
 * for EFUN class identifiers in FUNC_SPEC).
 * Other identifiers are stored in yylval.string and return ID.
 */

{
    char buff[100];
    size_t len, i;

    for (len = 0; isalunum(c); c = (char)getc(fpr))
    {
        if (len == sizeof buff - 1)
        {
            yyerror("Too long indentifier");
            do c = (char)getc(fpr); while (isalunum(c));
            break;
        }
        buff[len++] = c;
    }
    (void)ungetc(c, fpr);

    buff[len] = '\0';

    if ( parsetype == PARSE_FUNC_SPEC && C_IS_EFUN(current_code_class) )
    {
        for (i=0; i < NELEMS(types); i++)
        {
            if (strcmp(buff, types[i].name) == 0)
            {
                yylval.number = types[i].num;
                return types[i].num;
            }
        }
        if (strcmp(buff, "default") == 0)
            return DEFAULT;
    }
    yylval.string = mystrdup(buff);
    return ID;
} /* ident() */

/*-------------------------------------------------------------------------*/
static const char *
type_str (int n)

/* Create a string representation of type <n> in a static buffer
 * and return it. Unknown types return 'What?'.
 */

{
    int type = n & 0xffff;
    size_t i;

    for (i = 0; i < NELEMS(types); i++)
    {
        if (types[i].num == type)
        {
            if (n & MF_TYPE_MOD_REFERENCE)
            {
                static char buff[100];
                const char *str;

                str = type_str(n & ~MF_TYPE_MOD_REFERENCE);
                if (strlen(str) + 3 > sizeof buff)
                    fatal("Local buffer too small in type_str()!\n");
                sprintf(buff, "%s &", str);
                return buff;
            }
            if (n & MF_TYPE_MOD_POINTER)
            {
                static char buff[100];

                if (strlen(types[i].name) + 3 > sizeof buff)
                    fatal("Local buffer too small in type_str()!\n");
                sprintf(buff, "%s *", types[i].name);
                return buff;
            }
            return types[i].name;
        }
    }
    return "What?";
} /* type_str() */

/*-------------------------------------------------------------------------*/
static void
skip_comment (void)

/* Skip a block comment on the input stream fpr. It is assumed that
 * the begin marker was already read.
 */

{
    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 == '*');
    }
} /* skip_comment() */

/*-------------------------------------------------------------------------*/
static int
yylex1 (void)

/* Parse the next lexical element from the input file and return
 * its token value and -1 on end of file.
 */

{
    register int c;

    for(;;)
    {
        size_t match_tmp;
#define MATCH(str) (isspace((unsigned char)line_buffer[match_tmp=strlen(str)]) &&\
                        strncmp(str, line_buffer, match_tmp) == 0)

        char line_buffer[MAKE_FUNC_MAXLINE+1];

        line_buffer[MAKE_FUNC_MAXLINE] = '\0';

        switch(c = getc(fpr))
        {
        case ' ':
        case '\t':
        case '\r':
            continue;

        case '#':
        {
            int line;
            char file[MAXPATHLEN+1];

            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\n");
            }
            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 (parsetype == PARSE_FUNC_SPEC)
            {
                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++] = (char)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:
            if (isalpha(c))
                return ident((char)c);
            return c;
        }
    } /* for() */
} /* yylex1() */

/*-------------------------------------------------------------------------*/
static int
yylex (void)

/* Parse the next lexical element from the input file and return
 * its token value and -1 on end of file.
 * This just calls yylex1().
 */

{
    int res;

    if (!parsetype_sent)
    {
        parsetype_sent = MY_TRUE;
        return parsetype;
    }

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

/*-------------------------------------------------------------------------*/
static void
yyerror (const char *str)

/* Print the error message <str> with information about the current
 * parsing position and exit.
 */

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

/*=========================================================================*/

/*-------------------------------------------------------------------------*/
static const char *
etype1 (int n)

/* Express type <n> in the runtime type symbols of interpret.h.
 * Return a pointer to a constant string.
 */

{
    if (n & MF_TYPE_MOD_REFERENCE)
        return "T_LVALUE";
    if (n & MF_TYPE_MOD_POINTER)
        return "T_POINTER";
    switch(n) {
    case INT:
        return "T_NUMBER";
    case OBJECT:
        return "T_OBJECT";
    case STRING:
        return "T_STRING";
    case MAPPING:
        return "T_MAPPING";
    case FLOAT:
        return "T_FLOAT";
    case CLOSURE:
        return "T_CLOSURE";
    case SYMBOL:
        return "T_SYMBOL";
    case QUOTED_ARRAY:
        return "T_QUOTED_ARRAY";
    case MIXED:
        return "0";        /* 0 means any type */
    default:
        yyerror("Illegal type for argument");
    }
    return "What ?";
}

/*-------------------------------------------------------------------------*/
static const char *
etype (int n)

/* Express the type(s) of current function argument <n> in the
 * runtime type symbols of interpret.h, multiple types concatenated by '|'.
 * Return a pointer to a local static buffer with the text.
 */

{
    int i;
    static char buff[100];

    /* Find the argument <n> in the current signature */

    for (i = 0; i < curr_arg_type_size; i++)
    {
        if (n == 0)
            break;
        if (curr_arg_types[i] == 0)
            n--;
    }
    if (i == curr_arg_type_size || !curr_arg_types[i])
    {
        return "0";
    }

    /* Create the type string */

    buff[0] = '\0';
    for(; curr_arg_types[i] != 0; i++)
    {
        const char *p;
        if (curr_arg_types[i] == VOID)
            continue;
        if (buff[0] != '\0')
            strcat(buff, "|");
        p = etype1(curr_arg_types[i]);
        /* The number 2 below is to include the zero-byte and the next
         * '|' (which may not come).
         */
        if (strlen(p) + strlen(buff) + 2 > sizeof(buff))
        {
            fprintf(stderr, "Buffer overflow!\n");
            exit(1);
        }
        strcat(buff, p);
    }

    return buff;
} /* etype() */

/*-------------------------------------------------------------------------*/
static const char *
ctype (int n)

/* Express type <n> in the compiler type symbols of exec.h.
 * Return a pointer to a constant string.
 */

{
    static char buff[100];        /* 100 is such a comfortable size :-) */
    const char *p = NULL;

    buff[0] = '\0';
    if (n & MF_TYPE_MOD_REFERENCE)
        strcat(buff, "TYPE_MOD_REFERENCE|");
    if (n & MF_TYPE_MOD_POINTER)
        strcat(buff, "TYPE_MOD_POINTER|");
    n &= ~(MF_TYPE_MOD_REFERENCE|MF_TYPE_MOD_POINTER);
    switch(n) {
      case VOID:    p = "TYPE_VOID";    break;
      case STRING:  p = "TYPE_STRING";  break;
      case INT:     p = "TYPE_NUMBER";  break;
      case OBJECT:  p = "TYPE_OBJECT";  break;
      case MAPPING: p = "TYPE_MAPPING"; break;
      case FLOAT:   p = "TYPE_FLOAT";   break;
      case CLOSURE: p = "TYPE_CLOSURE"; break;
      case SYMBOL:  p = "TYPE_SYMBOL";  break;
      case MIXED:   p = "TYPE_ANY";     break;
      case UNKNOWN: p = "TYPE_UNKNOWN"; break;
      case QUOTED_ARRAY:
        p = "TYPE_QUOTED_ARRAY"; break;
    default: yyerror("Bad type!"); return 0;
    }
    strcat(buff, p);
    if (strlen(buff) + 1 > sizeof buff)
        fatal("Local buffer overwritten in ctype()");
    return buff;
} /* ctype() */

/*-------------------------------------------------------------------------*/
static int
efuncmp (int i, int j)

/* Compare the function entries <i> and <j>.
 * Return 1 if <i> is greater than <j>, 0 if equal, <j> if lesser.
 *
 * This predicate is used to sort the function/code tables read
 * from FUNC_SPEC.
 */

{
    int result;

    result = instr[i].code_class - instr[j].code_class;
    if ( result )
        return result;
    if (C_IS_CODE(instr[i].code_class))
        return 0;
    return strcmp(instr[i].key, instr[j].key);
}

/*-------------------------------------------------------------------------*/
static void
read_config (void)

/* Read the file CONFIG to learn about the defines used by the gamedriver.
 */

{
    size_t match_tmp;
    char line_buffer[MAKE_FUNC_MAXLINE + 1];
    char defbuf[MAKE_FUNC_MAXLINE + 1];

    /* Some predefined macros */

#ifdef AMIGA
    add_define("AMIGA",-1,"");
#endif
#ifdef __BEOS__
    add_define("__BEOS__", -1, "");
#endif
#ifdef DEBUG
    add_define("DEBUG", -1, "");
#endif
#ifdef HAVE_GETRUSAGE
    add_define("HAVE_GETRUSAGE",-1,"");
#endif
#ifdef TRACE_CODE
    add_define("TRACE_CODE",-1,"");
#endif

    /* --- Read in CONFIG to see what defines are used by the gamedriver ---
     */

    outp = defbuf + sizeof(defbuf) - 1;
    if ((fpr = fopen(CONFIG, "r")) == 0)
    {
       perror(CONFIG);
       fflush(stdout);
       exit(1);
    }
    current_line = 0;
    current_file = CONFIG;

#define MATCH(str) (isspace((unsigned char)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 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((unsigned char)*cp)) cp++;
            name = cp;
            while(isalunum((unsigned char)*cp)) cp++;
            num_arg = *cp == '(' ? 0 : -1;
            if (*cp == '\n') {
                exps = cp;
                *cp = '\0';
            } else {
                *cp++ = '\0';
                while( isspace((unsigned char)*cp)) cp++;
                exps = cp;
                while(*cp != '\n') cp++;
                *cp = '\0';
            }
            add_define(name, num_arg, exps);
        }
    }
    fclose(fpr);

#undef MATCH

    /* Sanity check on some of those USE_ defines: undefine
     * those which are not supported on the host system.
     */
    {
        const char * defnames[] = {
#ifndef HAS_IPV6
                             "USE_IPV6",
#endif
#ifndef HAS_MYSQL
                             "USE_MYSQL",
#endif
                             NULL };
        int i;

        for (i = 0; defnames[i] != NULL; i++)
        {
            struct defn *old_def;
            old_def = lookup_define(defnames[i]);
            if (old_def)
            {
                old_def->name[0] = '\0';
            }
        }
    }
} /* read_config() */

/*-------------------------------------------------------------------------*/
static void
read_func_spec (void)

/* Read the file FUNC_SPEC and create the instruction tables.
 */

{
    int i, j;

    if ((fpr = fopen(FUNC_SPEC, "r")) == NULL)
    {
        perror(FUNC_SPEC);
        exit(1);
    }
    got_error = 0;
    current_line = 1;
    current_file = FUNC_SPEC;
    parsetype = PARSE_FUNC_SPEC;
    parsetype_sent = MY_FALSE;
    num_buff = 0;
    yyparse();
    fclose(fpr);

    /* Print code usage statistics */

    fprintf(stderr,
"Primary codes: %3d (%3d + %3d) - Secondary codes:      %3d (%3d + %3d)\n"
"Tabled codes:  %3d (%3d + %3d) - Tabled varargs codes: %3d (%3d + %3d)\n"
           , num_instr[C_CODE]  + num_instr[C_EFUN]
           , num_instr[C_CODE],   num_instr[C_EFUN]
           , num_instr[C_XCODE] + num_instr[C_XEFUN]
           , num_instr[C_XCODE],  num_instr[C_XEFUN]
           , num_instr[C_TCODE] + num_instr[C_TEFUN]
           , num_instr[C_TCODE],  num_instr[C_TEFUN]
           , num_instr[C_VCODE] + num_instr[C_VEFUN]
           , 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)
    {
        fatal("Codes exhausted!");
    }

    /* Now sort the table of functions and codes (eeek, bubblesort!)
     */

    for (i = num_buff; --i >= 0; )
    {
        for (j = 0; j < i; j++)
            if (efuncmp(i,j) < 0)
            {
                struct instrdata_s tmp;

                tmp = instr[i]; instr[i] = instr[j]; instr[j] = tmp;
            }
    }

} /* read_func_spec() */

/*-------------------------------------------------------------------------*/
static void
read_string_spec (void)

/* Read the file STRING_SPEC.
 */

{
    if ((fpr = fopen(STRING_SPEC, "r")) == NULL)
    {
        perror(STRING_SPEC);
        exit(1);
    }
    got_error = 0;
    current_line = 1;
    current_file = STRING_SPEC;
    parsetype = PARSE_STRING_SPEC;
    parsetype_sent = MY_FALSE;
    num_buff = 0;
    yyparse();
    fclose(fpr);

} /* read_string_spec() */

/*-------------------------------------------------------------------------*/
static void
create_efun_defs (void)

/* Create the file EFUN_DEFS
 */

{
    int i, j, k;
    char c;

    if ((fpw = fopen(EFUN_DEFS, "w")) == NULL)
    {
       perror(EFUN_DEFS);
       exit(1);
    }

    fprintf(fpw,
"#ifndef EFUN_DEFS_C__\n"
"#define EFUN_DEFS_C__ 1\n"
"\n"
"/* DO NOT EDIT!\n"
" *\n"
" * This file is created automatically by make_func from\n"
" * the specifications in " FUNC_SPEC ".\n"
" * It is meant to be included in lex.c\n"
" */\n"
"\n"
"#include \"exec.h\"  /* struct instr_s == instr_t */\n"
"\n"
           );

    fprintf(fpw,
"/*----------------------------------------------------------------------*/\n"
"\n"
"/* The table of all instructions\n"
" */\n"
"\n"
"instr_t instrs[] = {\n"
           );
    fprintf(fpw, "\n  /* --- codes and efuns --- */\n\n");
    k = 0;
    j = num_instr[C_CODE] + num_instr[C_EFUN];
    for (i = 0; i < j; i++, k++) {
        fprintf(fpw, "  /* %3d */ %s", k, instr[i].buf);
    }
    for (i -= 256 ; ++i <= 0; k++) {
        fprintf(fpw
               , "  /* %3d */ { 0, 0, { 0, 0 }, -1, 0, 0, 0 }, /* unused */\n"
               , k
               );
    }
    fprintf(fpw, "\n  /* --- xcodes and xefuns --- */\n\n");
    i = j;
    j += num_instr[C_XCODE] + num_instr[C_XEFUN];
    for (; i < j; i++, k++) {
        fprintf(fpw, "  /* %3d */ %s", k, instr[i].buf);
    }
    for (i = 128 - num_instr[C_XCODE] - num_instr[C_XEFUN]; --i >= 0; k++) {
        fprintf(fpw
               , "  /* %3d */ { 0, 0, { 0, 0 }, -1, 0, 0, 0 }, /* unused */\n"
               , k
               );
    }
    fprintf(fpw, "\n  /* --- tcodes and tefuns --- */\n\n");
    i = j;
    j += num_instr[C_TCODE] + num_instr[C_TEFUN];
    for (; i < j; i++, k++) {
        fprintf(fpw, "  /* %3d */ %s", k, instr[i].buf);
    }
    for (i = 128 - num_instr[C_TCODE] - num_instr[C_TEFUN]; --i >= 0; k++) {
        fprintf(fpw
               , "  /* %3d */ { 0, 0, { 0, 0 }, -1, 0, 0, 0 }, /* unused */\n"
               , k
               );
    }
    fprintf(fpw, "\n  /* --- vcodes and vefuns --- */\n\n");
    i = j;
    j += num_instr[C_VCODE] + num_instr[C_VEFUN];
    for (; i < j; i++, k++) {
        fprintf(fpw, "  /* %3d */ %s", k, instr[i].buf);
    }
    fprintf(fpw, "\n  /* --- aliased efuns --- */\n\n");
    i = j;
    for (; i < num_buff; i++, k++) {
        fprintf(fpw, "  /* %3d */ %s", k, instr[i].buf);
    }
    fprintf(fpw, "};\n\n\n");

    fprintf(fpw, "/* Aliased efuns.\n"
                 " * Index it with <code>-F_LAST_INSTRUCTION-1 to retrieve\n"
                 " * the real instruction code.\n"
                 " */\n\n");
    fprintf(fpw, "short efun_aliases[] = {\n");
    for (i = 0; i < num_buff; i++) {
        if (instr[i].code_class == C_ALIAS)
            fprintf(fpw, "    %s,\n", instr[i].f_name);
    }
    fprintf(fpw, "};\n\n\n");

    fprintf(fpw,
"/* Table of function argument signatures\n"
" * The internal structure is that of arg_types[] in make_func.\n"
" */\n\n"
          );
    fprintf(fpw, "fulltype_t efun_arg_types[] = {\n ");
    for (i = 0; i < last_current_type; i++) {
        if (arg_types[i] == 0)
            fprintf(fpw, "0,\n ");
        else
            fprintf(fpw, "%s,", ctype(arg_types[i]));
    }
    fprintf(fpw, "};\n\n\n");

    fprintf(fpw, "/* Prototypes of the tabled efuns\n */\n\n");
    for (j = C_CODE, 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)
    {
        fprintf(fpw, "extern svalue_t *f_%s(svalue_t *);\n", instr[k++].key);
    }
    fprintf(fpw, "\n\n");

    fprintf(fpw, "/* The table of tabled efuns\n */\n\n");
    fprintf(fpw, "svalue_t *(*efun_table[]) (svalue_t *) = {\n");
    while(i < j) {
        fprintf(fpw, "  f_%s,\n", instr[i++].key);
    }
    fprintf(fpw, "};\n\n\n");

    fprintf(fpw, "/* Prototypes of the tabled vararg efuns\n */\n\n");
    j = i + num_instr[C_VCODE] + num_instr[C_VEFUN];
    k = i;
    while(k < j) {
        fprintf(fpw, "extern svalue_t *f_%s(svalue_t *, int);\n", instr[k++].key);
    }
    fprintf(fpw, "\n\n");

    fprintf(fpw, "/* The table of tabled vararg efuns\n */\n\n");
    fprintf(fpw, "svalue_t *(*vefun_table[])(svalue_t *, int) = {\n");
    while(i < j) {
        fprintf(fpw, "  f_%s,\n", instr[i++].key);
    }
    fprintf(fpw, "};\n\n\n");

    /* TODO: create a my-ctype.[ch] and a utility program to create
     * TODO:: these two files once and for all.
     */
    fprintf(fpw,
"/*----------------------------------------------------------------------*/\n"
"\n"
"/* Our own ctype implementation. This way we can be sure that\n"
" *   (a) we won't choke on non-ASCII characters\n"
" *   (b) we are fast\n"
" *   (c) we get the non-standard classifications we need anyway\n"
" */\n"
"\n"
           );

    fprintf(fpw, "#define lexwhite(c) (_my_ctype[(unsigned char)(c)]&%d)\n",_MCTs);
    fprintf(fpw, "#define leXdigit(c) (_my_ctype[(unsigned char)(c)]&%d)\n",_MCTx);

    fprintf(fpw, "\n" "unsigned char _my_ctype[] = {");
    c = '\0';
    do {
        if (!(c & 0xf))
            fprintf(fpw, "\n    ");
        fprintf(fpw, "%d,"
               ,  ( (isascii(c) && make_func_isescaped(c)) ? _MCTe : 0 )
                | ( (isascii(c) && isdigit ((unsigned char)c))  ? _MCTd : 0 )
                | ( (isascii(c) && isspace ((unsigned char)c) && c != '\n')
                    ? _MCTs : 0 )
                | ( (isascii(c) && isxdigit((unsigned char)c))  ? _MCTx : 0 )
                | ( ((isascii(c) && (isalnum ((unsigned char)c) || c == '_'))
                   || (((unsigned char)c) >= 0xC0 && ((unsigned char)c) <= 0xFF))
                    ? _MCTa : 0 )
               );
        c++;
    } while (c != '\0');
    fprintf(fpw, "\n};\n");


    fprintf(fpw,
"\n"
"/************************************************************************/\n"
"\n"
"#endif /* EFUN_DEFS_C__ */\n"
           );

    fclose(fpw);

} /* create_efun_defs() */

/*-------------------------------------------------------------------------*/
static void
create_instrs (void)

/* Create the file THE_INSTRS
 */

{
    int i, j;

    if ((fpw = fopen(THE_INSTRS, "w")) == NULL)
    {
       perror(THE_INSTRS);
       exit(1);
    }

    fprintf(fpw,
"#ifndef INSTRS_H__\n"
"#define INSTRS_H__ 1\n"
"\n"
"/* DO NOT EDIT!\n"
" *\n"
" * This file is created automatically by make_func from\n"
" * the specifications in " FUNC_SPEC ".\n"
" *\n"
" * It holds the bytecode values for all machine instructions, plus\n"
" * declarations of the tables holding information about the instructions.\n"
" */\n"
"\n"
"#include \"exec.h\"  /* struct instr_s == instr_t */\n"
"\n"
           );

    fprintf(fpw,
"#define LAST_INSTRUCTION_CODE %d\n"
"  /* The highest token value in use.\n"
"   */\n\n"
           , 256+128+128+num_instr[C_VCODE]+num_instr[C_VEFUN] - 1
    );
    fprintf(fpw, "extern instr_t instrs[];\n"
                 "extern short efun_aliases[];\n"
                 "extern fulltype_t efun_arg_types[];\n"
                 "extern svalue_t *(*efun_table[])(svalue_t *);\n"
                 "extern svalue_t *(*vefun_table[])(svalue_t *, int);\n"
    );
    fprintf(fpw,
"  /* All tables are defined in " EFUN_DEFS " and compiled into lex.c\n"
"   * TODO: We might as well create efun_defs.h and compile it separately.\n"
"   */\n\n"
           );

    fprintf(fpw,"/* --- codes --- */\n\n");
    for (i = j = 0; i < num_buff; i++)
    {
        if (j == num_instr[C_CODE])
        {
            fprintf(fpw,"\n/* --- efuns --- */\n\n");
        }
        if (j == num_instr[C_CODE] + num_instr[C_EFUN])
        {
            j = 256;
            fprintf(fpw,"\n/* --- xefuns and xcodes --- */\n\n");
        }
        if (j == 256 + num_instr[C_XCODE] + num_instr[C_XEFUN])
        {
            j = 256+128;
            fprintf(fpw,"\n/* --- tefuns and tcodes --- */\n\n");
        }
        if (j == 256 + 128 + num_instr[C_TCODE] + num_instr[C_TEFUN])
        {
            j = 256+128+128;
            fprintf(fpw,"\n/* --- vefuns and vcodes --- */\n\n");
        }
        if (instr[i].code_class != C_ALIAS)
        {
            fprintf(fpw, "#define %-30s (%d)\n"
                       , make_f_name(instr[i].key), j);
            j++;
        }
    }

    fprintf(fpw,
"\n"
"/************************************************************************/\n"
"\n"
"#endif /* INSTRS_H__ */\n"
           );

    fclose(fpw);
} /* create_instrs() */

/*-------------------------------------------------------------------------*/
static void
create_lang (void)

/* Create the file THE_LANG from PRO_LANG,
 */

{
    size_t match_tmp;
    char line_buffer[MAKE_FUNC_MAXLINE + 1];
    Bool bPrintedNotice;

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

    if ((fpr = fopen(PRO_LANG, "r")) == NULL)
    {
       perror(PRO_LANG);
       exit(1);
    }

    if ((fpw = fopen(THE_LANG, "w")) == NULL)
    {
       perror(THE_LANG);
       exit(1);
    }
    current_line = 0;
    bPrintedNotice = MY_FALSE;
    while (fgets(line_buffer, MAKE_FUNC_MAXLINE, fpr))
    {
        current_line++;
        if (*line_buffer == '%') {
            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("//") {
                /* c++ - resembling comment */
                fputs("\n", fpw);
                continue;
            }
            if (!bPrintedNotice) {
                if MATCH("{") {
                    bPrintedNotice = MY_TRUE;
                    fputs(
"%{\n"
"\n"
"/* DO NOT EDIT!\n"
" *\n"
" * This file is created automatically by make_func from\n"
" * the template " PRO_LANG " according to the specs in " FUNC_SPEC ".\n"
" */\n"
"\n"
                         , fpw);
+                    fprintf(fpw, "#line %d \"%s\"\n", current_line+1, PRO_LANG);
                    continue;
                }
            }
        }
        fputs(line_buffer, fpw);
    }
    fclose(fpr), fclose(fpw);

#undef MATCH

} /* create_lang() */

/*-------------------------------------------------------------------------*/
static void
create_stdstrings (void)

/* Create the files STDSTRINGS.[ch].
 */

{
    int i;

    /* Create stdstrings.h */

    if ((fpw = fopen(STDSTRINGS ".h", "w")) == NULL)
    {
       perror(STDSTRINGS ".h");
       exit(1);
    }

    fputs(
"#ifndef STDSTRINGS_H__\n"
"#define STDSTRINGS_H__ 1\n"
"\n"
"/* DO NOT EDIT!\n"
" *\n"
" * This file is created automatically by make_func from\n"
" * the specifications in " STRING_SPEC ".\n"
" */\n"
"\n"
"/* --- Common used shared strings. --- */\n"
"/* The most common strings, including all the predefined applies,\n"
" * are kept in shstrings[] for faster usage.\n"
" * The indices are SHX_xxx, the defines STR_xxx expand to shstring[SHX_xxx]\n"
" */\n"
"\n"
"enum StandardStrings {\n"
         , fpw);

    for (i = 0; i < num_buff; i++)
    {
        if (!i)
            fprintf(fpw, "    SHX_%-12s = 0 /* \"%s\" */\n"
                       , instr[i].key, instr[i].buf);
        else
            fprintf(fpw, "  , SHX_%-16s /* \"%s\" */\n"
                       , instr[i].key, instr[i].buf);
    }

    fputs(
"\n"
"  , SHSTR_NOSTRINGS /* The number of strings */\n"
"};\n"
"\n"
"extern char *shstring[SHSTR_NOSTRINGS];\n"
"\n"
         , fpw);

     for (i = 0; i < num_buff; i++)
     {
          fprintf(fpw, "#define STR_%-16s  shstring[SHX_%s]\n"
                     , instr[i].key, instr[i].key);
     }

    fputs(
"\n"
"/* --- Prototypes --- */\n"
"extern void init_standard_strings(void);\n"
"\n"
"#endif /* STDSTRINGS_H__ */\n"
         , fpw);

    fclose(fpw);

    /* Create stdstrings.c */

    if ((fpw = fopen(STDSTRINGS ".c", "w")) == NULL)
    {
       perror(STDSTRINGS ".c");
       exit(1);
    }

    fputs(
"/* DO NOT EDIT!\n"
" *\n"
" * This file is created automatically by make_func from\n"
" * the specifications in " STRING_SPEC ".\n"
" *\n"
" * It's purpose is to define and initialize the standard shared string\n"
" * table shstring[].\n"
" * TODO: When shared strings use proper structures, we can build the table\n"
" * TODO:: directly from the structures and avoid copying the string texts.\n"
" */\n"
"\n"
"#define NO_REF_STRING\n"
"#include \"driver.h\"\n"
"\n"
"#include \"" STDSTRINGS ".h\"\n"
"#include \"stralloc.h\"\n"
"\n"
"/*-------------------------------------------------------------------------*/\n"
"\n"
"char *shstring[SHSTR_NOSTRINGS];\n"
"  /* Table of common used and therefore shared strings.\n"
"   */\n"
"\n"
"/*-------------------------------------------------------------------------*/\n"
"void\n"
"init_standard_strings (void)\n"
"\n"
"/* Initialize the common string table.\n"
" */\n"
"\n"
"{\n"
"#   define INIT(x,s) shstring[x] = make_shared_string(s);\n"
"\n"
         , fpw);

    for (i = 0; i < num_buff; i++)
        fprintf(fpw, "    INIT(SHX_%s, \"%s\");\n"
                   , instr[i].key, instr[i].buf);

    fputs(
"\n"
"#   undef INIT\n"
"} /* init_standard_strings() */\n"
"\n"
"/************************************************************************/\n"
         , fpw);

    fclose(fpw);

} /* create_stdstrings() */

/*-------------------------------------------------------------------------*/
int
main (int argc, char ** argv)

/* The main program.
 */

{
    enum { NONE = 0, MakeInstrs, MakeLang, MakeStrings } action = NONE;
    char line_buffer[MAKE_FUNC_MAXLINE + 1];

    /* --- Check what we have to do --- */
    if (argc == 2)
    {
        if (!strcasecmp(argv[1], "instrs")) action = MakeInstrs;
        else if (!strcasecmp(argv[1], "lang")) action = MakeLang;
        else if (!strcasecmp(argv[1], "strings")) action = MakeStrings;
    }

    if (action == NONE)
    {
        fputs(
"Usage: make_func instrs|lang|strings\n"
"\n"
"  make_func instrs\n"
"    creates " EFUN_DEFS " and " THE_INSTRS ", reads " CONFIG " and " FUNC_SPEC ".\n"
"\n"
"  make_func lang\n"
"    creates " THE_LANG " from " PRO_LANG ", reads " CONFIG ".\n"
"\n"
"  make_func strings\n"
"    creates " STDSTRINGS ".[ch], reads " CONFIG " and " STRING_SPEC ".\n"
             , stderr);
        return 1; /* TODO: There are constants for this */
    }

    if (action == MakeLang)
    {
        /* --- Test YACC for default and anonymous rules ---
         *
         * This test uses THE_LANG as temporary file.
         */
        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);

        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, "");
        }
        else
            fprintf(stderr, "...good, it can handle them.\n");
    }


    /* --- Read the config files --- */
    read_config();
    if (action == MakeInstrs)
        read_func_spec();

    if (action == MakeStrings)
        read_string_spec();

    if (got_error)
        return 1;

    /* --- Create the output files --- */
    if (action == MakeInstrs)
    {
        create_efun_defs();
        create_instrs();
    }
    if (action == MakeLang)
        create_lang();

    if (action == MakeStrings)
        create_stdstrings();

    /* --- That's it --- */

    return 0;

} /* main() */

#if defined(__MWERKS__) && !defined(WARN_ALL)
#    pragma warn_possunwant off
#    pragma warn_implicitconv off
#endif

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