Implement a Makefile for Inform.
[inform.git] / src / inform.c
diff --git a/src/inform.c b/src/inform.c
new file mode 100644 (file)
index 0000000..8888077
--- /dev/null
@@ -0,0 +1,1871 @@
+/* ------------------------------------------------------------------------- */
+/*   "inform" :  The top level of Inform: switches, pathnames, filenaming    */
+/*               conventions, ICL (Inform Command Line) files, main          */
+/*                                                                           */
+/* Copyright (c) Graham Nelson 1993 - 2018                                   */
+/*                                                                           */
+/* This file is part of Inform.                                              */
+/*                                                                           */
+/* Inform is free software: you can redistribute it and/or modify            */
+/* it under the terms of the GNU General Public License as published by      */
+/* the Free Software Foundation, either version 3 of the License, or         */
+/* (at your option) any later version.                                       */
+/*                                                                           */
+/* Inform is distributed in the hope that it will be useful,                 */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the              */
+/* GNU General Public License for more details.                              */
+/*                                                                           */
+/* You should have received a copy of the GNU General Public License         */
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
+/*                                                                           */
+/* ------------------------------------------------------------------------- */
+
+#define MAIN_INFORM_FILE
+#include "header.h"
+
+/* ------------------------------------------------------------------------- */
+/*   Compiler progress                                                       */
+/* ------------------------------------------------------------------------- */
+
+static int no_compilations;
+
+int endofpass_flag;      /* set to TRUE when an "end" directive is reached
+                            (the inputs routines insert one into the stream
+                            if necessary)                                    */
+
+/* ------------------------------------------------------------------------- */
+/*   Version control                                                         */
+/* ------------------------------------------------------------------------- */
+
+int version_number,      /* 3 to 8 (Z-code)                                  */
+    instruction_set_number,
+                         /* 3 to 6: versions 7 and 8 use instruction set of
+                            version 5                                        */
+    extend_memory_map;   /* extend using function- and string-offsets        */
+int32 scale_factor,      /* packed address multiplier                        */
+    length_scale_factor; /* length-in-header multiplier                      */
+
+int32 requested_glulx_version;
+
+extern void select_version(int vn)
+{   version_number = vn;
+    extend_memory_map = FALSE;
+    if ((version_number==6)||(version_number==7)) extend_memory_map = TRUE;
+
+    scale_factor = 4;
+    if (version_number==3) scale_factor = 2;
+    if (version_number==8) scale_factor = 8;
+
+    length_scale_factor = scale_factor;
+    if ((version_number==6)||(version_number==7)) length_scale_factor = 8;
+
+    instruction_set_number = version_number;
+    if ((version_number==7)||(version_number==8)) instruction_set_number = 5;
+}
+
+static int select_glulx_version(char *str)
+{
+  /* Parse an "X.Y.Z" style version number, and store it for later use. */
+  char *cx = str;
+  int major=0, minor=0, patch=0;
+
+  while (isdigit(*cx))
+    major = major*10 + ((*cx++)-'0');
+  if (*cx == '.') {
+    cx++;
+    while (isdigit(*cx))
+      minor = minor*10 + ((*cx++)-'0');
+    if (*cx == '.') {
+      cx++;
+      while (isdigit(*cx))
+        patch = patch*10 + ((*cx++)-'0');
+    }
+  }
+
+  requested_glulx_version = ((major & 0x7FFF) << 16) 
+    + ((minor & 0xFF) << 8) 
+    + (patch & 0xFF);
+  return (cx - str);
+}
+
+/* ------------------------------------------------------------------------- */
+/*   Target: variables which vary between the Z-machine and Glulx            */
+/* ------------------------------------------------------------------------- */
+
+int   WORDSIZE;            /* Size of a machine word: 2 or 4 */
+int32 MAXINTWORD;          /* 0x7FFF or 0x7FFFFFFF */
+
+/* The first property number which is an individual property. The
+   eight class-system i-props (create, recreate, ... print_to_array)
+   are numbered from INDIV_PROP_START to INDIV_PROP_START+7.
+*/
+int INDIV_PROP_START;
+
+/* The length of an object, as written in tables.c. It's easier to define
+   it here than to repeat the same expression all over the source code.
+   Not used in Z-code. 
+*/
+int OBJECT_BYTE_LENGTH;
+/* The total length of a dict entry, in bytes. Not used in Z-code. 
+*/
+int DICT_ENTRY_BYTE_LENGTH;
+/* The position in a dict entry that the flag values begin.
+   Not used in Z-code. 
+*/
+int DICT_ENTRY_FLAG_POS;
+
+static void select_target(int targ)
+{
+  if (!targ) {
+    /* Z-machine */
+    WORDSIZE = 2;
+    MAXINTWORD = 0x7FFF;
+
+    if (INDIV_PROP_START != 64) {
+        INDIV_PROP_START = 64;
+        fatalerror("You cannot change INDIV_PROP_START in Z-code");
+    }
+    if (DICT_WORD_SIZE != 6) {
+      DICT_WORD_SIZE = 6;
+      fatalerror("You cannot change DICT_WORD_SIZE in Z-code");
+    }
+    if (DICT_CHAR_SIZE != 1) {
+      DICT_CHAR_SIZE = 1;
+      fatalerror("You cannot change DICT_CHAR_SIZE in Z-code");
+    }
+    if (NUM_ATTR_BYTES != 6) {
+      NUM_ATTR_BYTES = 6;
+      fatalerror("You cannot change NUM_ATTR_BYTES in Z-code");
+    }
+    if (MAX_LOCAL_VARIABLES != 16) {
+      MAX_LOCAL_VARIABLES = 16;
+      fatalerror("You cannot change MAX_LOCAL_VARIABLES in Z-code");
+    }
+    if (MAX_GLOBAL_VARIABLES != 240) {
+      MAX_GLOBAL_VARIABLES = 240;
+      fatalerror("You cannot change MAX_GLOBAL_VARIABLES in Z-code");
+    }
+    if (MAX_VERBS > 255) {
+      MAX_VERBS = 255;
+      fatalerror("MAX_VERBS can only go above 255 when Glulx is used");
+    }
+  }
+  else {
+    /* Glulx */
+    WORDSIZE = 4;
+    MAXINTWORD = 0x7FFFFFFF;
+    scale_factor = 0; /* It should never even get used in Glulx */
+
+    if (INDIV_PROP_START < 256) {
+        INDIV_PROP_START = 256;
+        warning_numbered("INDIV_PROP_START should be at least 256 in Glulx. Setting to", INDIV_PROP_START);
+    }
+
+    if (NUM_ATTR_BYTES % 4 != 3) {
+      NUM_ATTR_BYTES += (3 - (NUM_ATTR_BYTES % 4)); 
+      warning_numbered("NUM_ATTR_BYTES must be a multiple of four, plus three. Increasing to", NUM_ATTR_BYTES);
+    }
+
+    if (DICT_CHAR_SIZE != 1 && DICT_CHAR_SIZE != 4) {
+      DICT_CHAR_SIZE = 4;
+      warning_numbered("DICT_CHAR_SIZE must be either 1 or 4. Setting to", DICT_CHAR_SIZE);
+    }
+  }
+
+  if (MAX_LOCAL_VARIABLES >= 120) {
+    MAX_LOCAL_VARIABLES = 119;
+    warning("MAX_LOCAL_VARIABLES cannot exceed 119; resetting to 119");
+    /* This is because the keyword table in the lexer only has 120
+       entries. */
+  }
+  if (DICT_WORD_SIZE > MAX_DICT_WORD_SIZE) {
+    DICT_WORD_SIZE = MAX_DICT_WORD_SIZE;
+    warning_numbered(
+      "DICT_WORD_SIZE cannot exceed MAX_DICT_WORD_SIZE; resetting", 
+      MAX_DICT_WORD_SIZE);
+    /* MAX_DICT_WORD_SIZE can be increased in header.h without fear. */
+  }
+  if (NUM_ATTR_BYTES > MAX_NUM_ATTR_BYTES) {
+    NUM_ATTR_BYTES = MAX_NUM_ATTR_BYTES;
+    warning_numbered(
+      "NUM_ATTR_BYTES cannot exceed MAX_NUM_ATTR_BYTES; resetting",
+      MAX_NUM_ATTR_BYTES);
+    /* MAX_NUM_ATTR_BYTES can be increased in header.h without fear. */
+  }
+
+  /* Set up a few more variables that depend on the above values */
+
+  if (!targ) {
+    /* Z-machine */
+    DICT_WORD_BYTES = DICT_WORD_SIZE;
+    /* The Z-code generator doesn't use the following variables, although 
+       it would be a little cleaner if it did. */
+    OBJECT_BYTE_LENGTH = 0;
+    DICT_ENTRY_BYTE_LENGTH = (version_number==3)?7:9;
+    DICT_ENTRY_FLAG_POS = 0;
+  }
+  else {
+    /* Glulx */
+    OBJECT_BYTE_LENGTH = (1 + (NUM_ATTR_BYTES) + 6*4 + (GLULX_OBJECT_EXT_BYTES));
+    DICT_WORD_BYTES = DICT_WORD_SIZE*DICT_CHAR_SIZE;
+    if (DICT_CHAR_SIZE == 1) {
+      DICT_ENTRY_BYTE_LENGTH = (7+DICT_WORD_BYTES);
+      DICT_ENTRY_FLAG_POS = (1+DICT_WORD_BYTES);
+    }
+    else {
+      DICT_ENTRY_BYTE_LENGTH = (12+DICT_WORD_BYTES);
+      DICT_ENTRY_FLAG_POS = (4+DICT_WORD_BYTES);
+    }
+  }
+}
+
+/* ------------------------------------------------------------------------- */
+/*   Tracery: output control variables                                       */
+/* ------------------------------------------------------------------------- */
+
+int asm_trace_level,     /* trace assembly: 0 for off, 1 for assembly
+                            only, 2 for full assembly tracing with hex dumps */
+    line_trace_level,    /* line tracing: 0 off, 1 on                        */
+    expr_trace_level,    /* expression tracing: 0 off, 1 full, 2 brief       */
+    linker_trace_level,  /* set by -y: 0 to 4 levels of tracing              */
+    tokens_trace_level;  /* lexer output tracing: 0 off, 1 on                */
+
+/* ------------------------------------------------------------------------- */
+/*   On/off switch variables (by default all FALSE); other switch settings   */
+/* ------------------------------------------------------------------------- */
+
+int bothpasses_switch,              /* -b */
+    concise_switch,                 /* -c */
+    economy_switch,                 /* -e */
+    frequencies_switch,             /* -f */
+    ignore_switches_switch,         /* -i */
+    listobjects_switch,             /* -j */
+    debugfile_switch,               /* -k */
+    listing_switch,                 /* -l */
+    memout_switch,                  /* -m */
+    printprops_switch,              /* -n */
+    offsets_switch,                 /* -o */
+    percentages_switch,             /* -p */
+    obsolete_switch,                /* -q */
+    transcript_switch,              /* -r */
+    statistics_switch,              /* -s */
+    optimise_switch,                /* -u */
+    version_set_switch,             /* -v */
+    nowarnings_switch,              /* -w */
+    hash_switch,                    /* -x */
+    memory_map_switch,              /* -z */
+    oddeven_packing_switch,         /* -B */
+    define_DEBUG_switch,            /* -D */
+    temporary_files_switch,         /* -F */
+    module_switch,                  /* -M */
+    runtime_error_checking_switch,  /* -S */
+    define_USE_MODULES_switch,      /* -U */
+    define_INFIX_switch;            /* -X */
+#ifdef ARC_THROWBACK
+int throwback_switch;               /* -T */
+#endif
+#ifdef ARCHIMEDES
+int riscos_file_type_format;        /* set by -R */
+#endif
+int compression_switch;             /* set by -H */
+int character_set_setting,          /* set by -C0 through -C9 */
+    character_set_unicode,          /* set by -Cu */
+    error_format,                   /* set by -E */
+    asm_trace_setting,              /* set by -a and -t: value of
+                                       asm_trace_level to use when tracing */
+    double_space_setting,           /* set by -d: 0, 1 or 2 */
+    trace_fns_setting,              /* set by -g: 0, 1 or 2 */
+    linker_trace_setting,           /* set by -y: ditto for linker_... */
+    store_the_text;                 /* when set, record game text to a chunk
+                                       of memory (used by both -r & -k) */
+static int r_e_c_s_set;             /* has -S been explicitly set? */
+
+int glulx_mode;                     /* -G */
+
+static void reset_switch_settings(void)
+{   asm_trace_setting=0;
+    linker_trace_level=0;
+    tokens_trace_level=0;
+
+    store_the_text = FALSE;
+
+    bothpasses_switch = FALSE;
+    concise_switch = FALSE;
+    double_space_setting = 0;
+    economy_switch = FALSE;
+    frequencies_switch = FALSE;
+    trace_fns_setting = 0;
+    ignore_switches_switch = FALSE;
+    listobjects_switch = FALSE;
+    debugfile_switch = FALSE;
+    listing_switch = FALSE;
+    memout_switch = FALSE;
+    printprops_switch = FALSE;
+    offsets_switch = FALSE;
+    percentages_switch = FALSE;
+    obsolete_switch = FALSE;
+    transcript_switch = FALSE;
+    statistics_switch = FALSE;
+    optimise_switch = FALSE;
+    version_set_switch = FALSE;
+    nowarnings_switch = FALSE;
+    hash_switch = FALSE;
+    memory_map_switch = FALSE;
+    oddeven_packing_switch = FALSE;
+    define_DEBUG_switch = FALSE;
+#ifdef USE_TEMPORARY_FILES
+    temporary_files_switch = TRUE;
+#else
+    temporary_files_switch = FALSE;
+#endif
+    define_USE_MODULES_switch = FALSE;
+    module_switch = FALSE;
+#ifdef ARC_THROWBACK
+    throwback_switch = FALSE;
+#endif
+    runtime_error_checking_switch = TRUE;
+    r_e_c_s_set = FALSE;
+    define_INFIX_switch = FALSE;
+#ifdef ARCHIMEDES
+    riscos_file_type_format = 0;
+#endif
+    error_format=DEFAULT_ERROR_FORMAT;
+
+    character_set_setting = 1;         /* Default is ISO Latin-1 */
+    character_set_unicode = FALSE;
+
+    compression_switch = TRUE;
+    glulx_mode = FALSE;
+    requested_glulx_version = 0;
+}
+
+/* ------------------------------------------------------------------------- */
+/*   Number of files given as command line parameters (0, 1 or 2)            */
+/* ------------------------------------------------------------------------- */
+
+static int cli_files_specified,
+           convert_filename_flag;
+
+char Source_Name[PATHLEN];             /* Processed name of first input file */
+char Code_Name[PATHLEN];               /* Processed name of output file      */
+
+static char *cli_file1, *cli_file2;    /* Unprocessed (and unsafe to alter)  */
+
+/* ========================================================================= */
+/*   Data structure management routines                                      */
+/* ------------------------------------------------------------------------- */
+
+static void init_vars(void)
+{
+    init_arrays_vars();
+    init_asm_vars();
+    init_bpatch_vars();
+    init_chars_vars();
+    init_directs_vars();
+    init_errors_vars();
+    init_expressc_vars();
+    init_expressp_vars();
+    init_files_vars();
+    init_lexer_vars();
+    init_linker_vars();
+    init_memory_vars();
+    init_objects_vars();
+    init_states_vars();
+    init_symbols_vars();
+    init_syntax_vars();
+    init_tables_vars();
+    init_text_vars();
+    init_veneer_vars();
+    init_verbs_vars();
+}
+
+static void begin_pass(void)
+{
+    arrays_begin_pass();
+    asm_begin_pass();
+    bpatch_begin_pass();
+    chars_begin_pass();
+    directs_begin_pass();
+    errors_begin_pass();
+    expressc_begin_pass();
+    expressp_begin_pass();
+    files_begin_pass();
+
+    endofpass_flag = FALSE;
+    line_trace_level = 0; expr_trace_level = 0;
+    asm_trace_level = asm_trace_setting;
+    linker_trace_level = linker_trace_setting;
+    if (listing_switch) line_trace_level=1;
+
+    lexer_begin_pass();
+    linker_begin_pass();
+    memory_begin_pass();
+    objects_begin_pass();
+    states_begin_pass();
+    symbols_begin_pass();
+    syntax_begin_pass();
+    tables_begin_pass();
+    text_begin_pass();
+    veneer_begin_pass();
+    verbs_begin_pass();
+
+    if (!module_switch)
+    {
+        /*  Compile a Main__ routine (see "veneer.c")  */
+
+        compile_initial_routine();
+
+        /*  Make the four metaclasses: Class must be object number 1, so
+            it must come first  */
+
+        veneer_mode = TRUE;
+
+        make_class("Class");
+        make_class("Object");
+        make_class("Routine");
+        make_class("String");
+
+        veneer_mode = FALSE;
+    }
+}
+
+extern void allocate_arrays(void)
+{
+    arrays_allocate_arrays();
+    asm_allocate_arrays();
+    bpatch_allocate_arrays();
+    chars_allocate_arrays();
+    directs_allocate_arrays();
+    errors_allocate_arrays();
+    expressc_allocate_arrays();
+    expressp_allocate_arrays();
+    files_allocate_arrays();
+
+    lexer_allocate_arrays();
+    linker_allocate_arrays();
+    memory_allocate_arrays();
+    objects_allocate_arrays();
+    states_allocate_arrays();
+    symbols_allocate_arrays();
+    syntax_allocate_arrays();
+    tables_allocate_arrays();
+    text_allocate_arrays();
+    veneer_allocate_arrays();
+    verbs_allocate_arrays();
+}
+
+extern void free_arrays(void)
+{
+    /*  One array may survive this routine, all_the_text (used to hold
+        game text until the abbreviations optimiser begins work on it): this
+        array (if it was ever allocated) is freed at the top level.          */
+
+    arrays_free_arrays();
+    asm_free_arrays();
+    bpatch_free_arrays();
+    chars_free_arrays();
+    directs_free_arrays();
+    errors_free_arrays();
+    expressc_free_arrays();
+    expressp_free_arrays();
+    files_free_arrays();
+
+    lexer_free_arrays();
+    linker_free_arrays();
+    memory_free_arrays();
+    objects_free_arrays();
+    states_free_arrays();
+    symbols_free_arrays();
+    syntax_free_arrays();
+    tables_free_arrays();
+    text_free_arrays();
+    veneer_free_arrays();
+    verbs_free_arrays();
+}
+
+/* ------------------------------------------------------------------------- */
+/*    Name translation code for filenames                                    */
+/* ------------------------------------------------------------------------- */
+
+static char Source_Path[PATHLEN];
+static char Include_Path[PATHLEN];
+static char Code_Path[PATHLEN];
+static char Module_Path[PATHLEN];
+static char Temporary_Path[PATHLEN];
+static char current_source_path[PATHLEN];
+       char Debugging_Name[PATHLEN];
+       char Transcript_Name[PATHLEN];
+       char Language_Name[PATHLEN];
+       char Charset_Map[PATHLEN];
+static char ICL_Path[PATHLEN];
+
+/* Set one of the above Path buffers to the given location, or list of
+   locations. (A list is comma-separated, and only accepted for Source_Path,
+   Include_Path, ICL_Path, Module_Path.)
+*/
+static void set_path_value(char *path, char *value)
+{   int i, j;
+
+    for (i=0, j=0;;)
+    {
+        if (i >= PATHLEN-1) {
+            printf("A specified path is longer than %d characters.\n",
+                PATHLEN-1);
+            exit(1);
+        }
+        if ((value[j] == FN_ALT) || (value[j] == 0))
+        {   if ((value[j] == FN_ALT)
+                && (path != Source_Path) && (path != Include_Path)
+                && (path != ICL_Path) && (path != Module_Path))
+            {   printf("The character '%c' is used to divide entries in a list \
+of possible locations, and can only be used in the Include_Path, Source_Path, \
+Module_Path or ICL_Path variables. Other paths are for output only.\n", FN_ALT);
+                exit(1);
+            }
+            if ((path != Debugging_Name) && (path != Transcript_Name)
+                 && (path != Language_Name) && (path != Charset_Map)
+                 && (i>0) && (isalnum(path[i-1]))) path[i++] = FN_SEP;
+            path[i++] = value[j++];
+            if (value[j-1] == 0) return;
+        }
+        else path[i++] = value[j++];
+    }
+}
+
+/* Prepend the given location or list of locations to one of the above
+   Path buffers. This is only permitted for Source_Path, Include_Path, 
+   ICL_Path, Module_Path.
+
+   An empty field (in the comma-separated list) means the current
+   directory. If the Path buffer is entirely empty, we assume that
+   we want to search both value and the current directory, so
+   the result will be "value,".
+*/
+static void prepend_path_value(char *path, char *value)
+{
+    int i, j;
+    int oldlen = strlen(path);
+    int newlen;
+    char new_path[PATHLEN];
+
+    if ((path != Source_Path) && (path != Include_Path)
+        && (path != ICL_Path) && (path != Module_Path))
+    {   printf("The character '+' is used to add to a list \
+of possible locations, and can only be used in the Include_Path, Source_Path, \
+Module_Path or ICL_Path variables. Other paths are for output only.\n");
+        exit(1);
+    }
+
+    for (i=0, j=0;;)
+    {
+        if (i >= PATHLEN-1) {
+            printf("A specified path is longer than %d characters.\n",
+                PATHLEN-1);
+            exit(1);
+        }
+        if ((value[j] == FN_ALT) || (value[j] == 0))
+        {   if ((path != Debugging_Name) && (path != Transcript_Name)
+                 && (path != Language_Name) && (path != Charset_Map)
+                 && (i>0) && (isalnum(new_path[i-1]))) new_path[i++] = FN_SEP;
+            new_path[i++] = value[j++];
+            if (value[j-1] == 0) {
+                newlen = i-1;
+                break;
+            }
+        }
+        else new_path[i++] = value[j++];
+    }
+
+    if (newlen+1+oldlen >= PATHLEN-1) {
+        printf("A specified path is longer than %d characters.\n",
+            PATHLEN-1);
+        exit(1);
+    }
+
+    i = newlen;
+    new_path[i++] = FN_ALT;
+    for (j=0; j<oldlen;)
+        new_path[i++] = path[j++];
+    new_path[i] = 0;
+    
+    strcpy(path, new_path);
+}
+
+static void set_default_paths(void)
+{
+    set_path_value(Source_Path,     Source_Directory);
+    set_path_value(Include_Path,    Include_Directory);
+    set_path_value(Code_Path,       Code_Directory);
+    set_path_value(Module_Path,     Module_Directory);
+    set_path_value(ICL_Path,        ICL_Directory);
+    set_path_value(Temporary_Path,  Temporary_Directory);
+    set_path_value(Debugging_Name,  Debugging_File);
+    set_path_value(Transcript_Name, Transcript_File);
+    set_path_value(Language_Name,   "english");
+    set_path_value(Charset_Map,     "");
+}
+
+/* Parse a path option which looks like "dir", "+dir", "pathname=dir",
+   or "+pathname=dir". If there is no "=", we assume "include_path=...".
+   If the option begins with a "+" the directory is prepended to the
+   existing path instead of replacing it.
+*/
+static void set_path_command(char *command)
+{   int i, j; char *path_to_set = NULL;
+    int prepend = 0;
+
+    if (command[0] == '+') {
+        prepend = 1;
+        command++;
+    }
+
+    for (i=0; (command[i]!=0) && (command[i]!='=');i++) ;
+
+    path_to_set=Include_Path; 
+
+    if (command[i] == '=') { 
+        char pathname[PATHLEN];
+        if (i>=PATHLEN) i=PATHLEN-1;
+        for (j=0;j<i;j++) {
+            char ch = command[j];
+            if (isupper(ch)) ch=tolower(ch);
+            pathname[j]=ch;
+        }
+        pathname[j]=0;
+        command = command+i+1;
+
+        path_to_set = NULL;
+        if (strcmp(pathname, "source_path")==0)  path_to_set=Source_Path;
+        if (strcmp(pathname, "include_path")==0) path_to_set=Include_Path;
+        if (strcmp(pathname, "code_path")==0)    path_to_set=Code_Path;
+        if (strcmp(pathname, "module_path")==0)  path_to_set=Module_Path;
+        if (strcmp(pathname, "icl_path")==0)     path_to_set=ICL_Path;
+        if (strcmp(pathname, "temporary_path")==0) path_to_set=Temporary_Path;
+        if (strcmp(pathname, "debugging_name")==0) path_to_set=Debugging_Name;
+        if (strcmp(pathname, "transcript_name")==0) path_to_set=Transcript_Name;
+        if (strcmp(pathname, "language_name")==0) path_to_set=Language_Name;
+        if (strcmp(pathname, "charset_map")==0) path_to_set=Charset_Map;
+
+        if (path_to_set == NULL)
+        {   printf("No such path setting as \"%s\"\n", pathname);
+            exit(1);
+        }
+    }
+
+    if (!prepend)
+        set_path_value(path_to_set, command);
+    else
+        prepend_path_value(path_to_set, command);
+}
+
+static int contains_separator(char *name)
+{   int i;
+    for (i=0; name[i]!=0; i++)
+        if (name[i] == FN_SEP) return 1;
+    return 0;
+}
+
+static int write_translated_name(char *new_name, char *old_name,
+                                 char *prefix_path, int start_pos,
+                                 char *extension)
+{   int x;
+    if (strlen(old_name)+strlen(extension) >= PATHLEN) {
+        printf("One of your filenames is longer than %d characters.\n", PATHLEN);
+        exit(1);
+    }
+    if (prefix_path == NULL)
+    {   sprintf(new_name,"%s%s", old_name, extension);
+        return 0;
+    }
+    strcpy(new_name, prefix_path + start_pos);
+    for (x=0; (new_name[x]!=0) && (new_name[x]!=FN_ALT); x++) ;
+    if (new_name[x] == 0) start_pos = 0; else start_pos += x+1;
+    if (x+strlen(old_name)+strlen(extension) >= PATHLEN) {
+        printf("One of your pathnames is longer than %d characters.\n", PATHLEN);
+        exit(1);
+    }
+    sprintf(new_name + x, "%s%s", old_name, extension);
+    return start_pos;
+}
+
+#ifdef FILE_EXTENSIONS
+static char *check_extension(char *name, char *extension)
+{   int i;
+
+    /* If a filename ends in '.', remove the dot and add no file extension: */
+    i = strlen(name)-1;
+    if (name[i] == '.') { name[i]=0; return ""; }
+
+    /* Remove the new extension if it's already got one: */
+
+    for (; (i>=0) && (name[i]!=FN_SEP); i--)
+        if (name[i] == '.') return "";
+    return extension;
+}
+#endif
+
+/* ------------------------------------------------------------------------- */
+/*    Three translation routines have to deal with path variables which may  */
+/*    contain alternative locations separated by the FN_ALT character.       */
+/*    These have the protocol:                                               */
+/*                                                                           */
+/*        int translate_*_filename(int last_value, ...)                      */
+/*                                                                           */
+/*    and should first be called with last_value equal to 0.  If the         */
+/*    translated filename works, fine.  Otherwise, if the returned integer   */
+/*    was zero, the caller knows that no filename works and can issue an     */
+/*    error message.  If it was non-zero, the caller should pass it on as    */
+/*    the last_value again.                                                  */
+/*                                                                           */
+/*    As implemented below, last_value is the position in the path variable  */
+/*    string at which the next directory name to try begins.                 */
+/* ------------------------------------------------------------------------- */
+
+extern int translate_in_filename(int last_value,
+    char *new_name, char *old_name,
+    int same_directory_flag, int command_line_flag)
+{   char *prefix_path = NULL;
+    char *extension;
+    int add_path_flag = 1;
+    int i;
+
+    if ((same_directory_flag==0)
+        && (contains_separator(old_name)==1)) add_path_flag=0;
+
+    if (add_path_flag==1)
+    {   if (command_line_flag == 0)
+        {   /* File is opened as a result of an Include directive */
+
+            if (same_directory_flag==1)
+                prefix_path = current_source_path;
+            else
+                if (Include_Path[0]!=0) prefix_path = Include_Path;
+        }
+        /* Main file being opened from the command line */
+
+        else if (Source_Path[0]!=0) prefix_path = Source_Path;
+    }
+
+#ifdef FILE_EXTENSIONS
+    /* Which file extension is expected? */
+
+    if ((command_line_flag==1)||(same_directory_flag==1))
+        extension = Source_Extension;
+    else
+        extension = Include_Extension;
+
+    extension = check_extension(old_name, extension);
+#else
+    extension = "";
+#endif
+
+    last_value = write_translated_name(new_name, old_name,
+                     prefix_path, last_value, extension);
+
+    /* Set the "current source path" (for use of Include ">...") */
+
+    if (command_line_flag==1)
+    {   strcpy(current_source_path, new_name);
+        for (i=strlen(current_source_path)-1;
+             ((i>0)&&(current_source_path[i]!=FN_SEP));i--) ;
+
+        if (i!=0) current_source_path[i+1] = 0; /* Current file in subdir   */
+        else current_source_path[0] = 0;        /* Current file at root dir */
+    }
+
+    return last_value;
+}
+
+extern int translate_link_filename(int last_value,
+    char *new_name, char *old_name)
+{   char *prefix_path = NULL;
+    char *extension;
+
+    if (contains_separator(old_name)==0)
+        if (Module_Path[0]!=0)
+            prefix_path = Module_Path;
+
+#ifdef FILE_EXTENSIONS
+    extension = check_extension(old_name, Module_Extension);
+#else
+    extension = "";
+#endif
+
+    return write_translated_name(new_name, old_name,
+               prefix_path, last_value, extension);
+}
+
+static int translate_icl_filename(int last_value,
+    char *new_name, char *old_name)
+{   char *prefix_path = NULL;
+    char *extension = "";
+
+    if (contains_separator(old_name)==0)
+        if (ICL_Path[0]!=0)
+            prefix_path = ICL_Path;
+
+#ifdef FILE_EXTENSIONS
+    extension = check_extension(old_name, ICL_Extension);
+#endif
+
+    return write_translated_name(new_name, old_name,
+               prefix_path, last_value, extension);
+}
+
+extern void translate_out_filename(char *new_name, char *old_name)
+{   char *prefix_path;
+    char *extension = "";
+    int i;
+
+    /* If !convert_filename_flag, then the old_name is just the <file2>
+       parameter on the Inform command line, which we leave alone. */
+
+    if (!convert_filename_flag)
+    {   strcpy(new_name, old_name); return;
+    }
+
+    /* Remove any pathname or extension in <file1>. */
+
+    if (contains_separator(old_name)==1)
+    {   for (i=strlen(old_name)-1; (i>0)&&(old_name[i]!=FN_SEP) ;i--) { };
+        if (old_name[i]==FN_SEP) i++;
+        old_name += i;
+    }
+#ifdef FILE_EXTENSIONS
+    for (i=strlen(old_name)-1; (i>=0)&&(old_name[i]!='.') ;i--) ;
+    if (old_name[i] == '.') old_name[i] = 0;
+#endif
+
+    prefix_path = NULL;
+    if (module_switch)
+    {   extension = Module_Extension;
+        if (Module_Path[0]!=0) prefix_path = Module_Path;
+    }
+    else
+    {
+        if (!glulx_mode) {
+            switch(version_number)
+            {   case 3: extension = Code_Extension;   break;
+                case 4: extension = V4Code_Extension; break;
+                case 5: extension = V5Code_Extension; break;
+                case 6: extension = V6Code_Extension; break;
+                case 7: extension = V7Code_Extension; break;
+                case 8: extension = V8Code_Extension; break;
+            }
+        }
+        else {
+            extension = GlulxCode_Extension;
+        }
+        if (Code_Path[0]!=0) prefix_path = Code_Path;
+    }
+
+#ifdef FILE_EXTENSIONS
+    extension = check_extension(old_name, extension);
+#endif
+
+    write_translated_name(new_name, old_name, prefix_path, 0, extension);
+}
+
+static char *name_or_unset(char *p)
+{   if (p[0]==0) return "(unset)";
+    return p;
+}
+
+static void help_on_filenames(void)
+{   char old_name[PATHLEN];
+    char new_name[PATHLEN];
+    int save_mm = module_switch, x;
+
+    module_switch = FALSE;
+
+    printf("Help information on filenames:\n\n");
+
+    printf(
+"The command line can take one of two forms:\n\n\
+  inform [commands...] <file1>\n\
+  inform [commands...] <file1> <file2>\n\n\
+Inform translates <file1> into a source file name (see below) for its input.\n\
+<file2> is usually omitted: if so, the output filename is made from <file1>\n\
+by cutting out the name part and translating that (see below).\n\
+If <file2> is given, however, the output filename is set to just <file2>\n\
+(not altered in any way).\n\n");
+
+    printf(
+"Filenames given in the game source (with commands like Include \"name\" and\n\
+Link \"name\") are also translated by the rules below.\n\n");
+
+    printf(
+"Rules of translation:\n\n\
+Inform translates plain filenames (such as \"xyzzy\") into full pathnames\n\
+(such as \"adventure%cgames%cxyzzy\") according to the following rules.\n\n\
+1. If the name contains a '%c' character (so it's already a pathname), it\n\
+   isn't changed.\n\n", FN_SEP, FN_SEP, FN_SEP);
+
+    printf(
+"   [Exception: when the name is given in an Include command using the >\n\
+   form (such as Include \">prologue\"), the \">\" is replaced by the path\n\
+   of the file doing the inclusion");
+#ifdef FILE_EXTENSIONS
+                          printf(" and a suitable file extension is added");
+#endif
+    printf(".]\n\n");
+
+    printf(
+"   Filenames must never contain double-quotation marks \".  To use filenames\n\
+   which contain spaces, write them in double-quotes: for instance,\n\n\
+   \"inform +code_path=\"Jigsaw Final Version\" jigsaw\".\n\n");
+
+    printf(
+"2. The file is looked for at a particular \"path\" (the filename of a\n\
+   directory), depending on what kind of file it is.\n\n\
+       File type              Name                Current setting\n\n\
+       Source code (in)       source_path         %s\n\
+       Include file (in)      include_path        %s\n\
+       Story file (out)       code_path           %s\n",
+   name_or_unset(Source_Path), name_or_unset(Include_Path),
+   name_or_unset(Code_Path));
+
+    printf(
+"       Temporary file (out)   temporary_path      %s\n\
+       ICL command file (in)  icl_path            %s\n\
+       Module (in & out)      module_path         %s\n\n",
+   name_or_unset(Temporary_Path),
+   name_or_unset(ICL_Path), name_or_unset(Module_Path));
+
+    printf(
+"   If the path is unset, then the current working directory is used (so\n\
+   the filename doesn't change): if, for instance, include_path is set to\n\
+   \"backup%coldlib\" then when \"parser\" is included it is looked for at\n\
+   \"backup%coldlib%cparser\".\n\n\
+   The paths can be set or unset on the Inform command line by, eg,\n\
+   \"inform +code_path=finished jigsaw\" or\n\
+   \"inform +include_path= balances\" (which unsets include_path).\n\n",
+        FN_SEP, FN_SEP, FN_SEP);
+
+    printf(
+"   The four input path variables can be set to lists of alternative paths\n\
+   separated by '%c' characters: these alternatives are always tried in\n\
+   the order they are specified in, that is, left to right through the text\n\
+   in the path variable.\n\n",
+   FN_ALT);
+    printf(
+"   If two '+' signs are used (\"inform ++include_path=dir jigsaw\") then\n\
+   the path or paths are added to the existing list.\n\n");
+    printf(
+"   (Modules are written to the first alternative in the module_path list;\n\
+   it is an error to give alternatives at all for purely output paths.)\n\n");
+
+#ifdef FILE_EXTENSIONS
+    printf("3. The following file extensions are added:\n\n\
+      Source code:     %s\n\
+      Include files:   %s\n\
+      Story files:     %s (Version 3), %s (v4), %s (v5, the default),\n\
+                       %s (v6), %s (v7), %s (v8), %s (Glulx)\n\
+      Temporary files: .tmp\n\
+      Modules:         %s\n\n",
+      Source_Extension, Include_Extension,
+      Code_Extension, V4Code_Extension, V5Code_Extension, V6Code_Extension,
+      V7Code_Extension, V8Code_Extension, GlulxCode_Extension, 
+      Module_Extension);
+    printf("\
+   except that any extension you give (on the command line or in a filename\n\
+   used in a program) will override these.  If you give the null extension\n\
+   \".\" then Inform uses no file extension at all (removing the \".\").\n\n");
+#endif
+
+    printf("Names of four individual files can also be set using the same\n\
+  + command notation (though they aren't really pathnames).  These are:\n\n\
+      transcript_name  (text written by -r switch): now \"%s\"\n\
+      debugging_name   (data written by -k switch): now \"%s\"\n\
+      language_name    (library file defining natural language of game):\n\
+                       now \"%s\"\n\
+      charset_map      (file for character set mapping): now \"%s\"\n\n",
+    Transcript_Name, Debugging_Name, Language_Name, Charset_Map);
+
+    translate_in_filename(0, new_name, "rezrov", 0, 1);
+    printf("Examples: 1. \"inform rezrov\"\n\
+  the source code is read from \"%s\"\n",
+        new_name);
+    convert_filename_flag = TRUE;
+    translate_out_filename(new_name, "rezrov");
+    printf("  and a story file is compiled to \"%s\".\n\n", new_name);
+
+    translate_in_filename(0, new_name, "frotz", 0, 1);
+    printf("2. \"inform -M frotz\"\n\
+  the source code is read from \"%s\"\n",
+        new_name);
+    module_switch = TRUE;
+    convert_filename_flag = TRUE;
+    translate_out_filename(new_name, "frotz");
+    printf("  and a module is compiled to \"%s\".\n\n", new_name);
+
+    module_switch = FALSE;
+
+    sprintf(old_name, "demos%cplugh", FN_SEP);
+    printf("3. \"inform %s\"\n", old_name);
+    translate_in_filename(0, new_name, old_name, 0, 1);
+    printf("  the source code is read from \"%s\"\n", new_name);
+    sprintf(old_name, "demos%cplugh", FN_SEP);
+    convert_filename_flag = TRUE;
+    translate_out_filename(new_name, old_name);
+    printf("  and a story file is compiled to \"%s\".\n\n", new_name);
+
+    printf("4. \"inform plover my_demo\"\n");
+    translate_in_filename(0, new_name, "plover", 0, 1);
+    printf("  the source code is read from \"%s\"\n", new_name);
+    convert_filename_flag = FALSE;
+    translate_out_filename(new_name, "my_demo");
+    printf("  and a story file is compiled to \"%s\".\n\n", new_name);
+
+    strcpy(old_name, Source_Path);
+    sprintf(new_name, "%cnew%cold%crecent%cold%cancient",
+        FN_ALT, FN_ALT, FN_SEP, FN_ALT, FN_SEP);
+    printf("5. \"inform +source_path=%s zooge\"\n", new_name);
+    printf(
+"   Note that four alternative paths are given, the first being the empty\n\
+   path-name (meaning: where you are now).  Inform looks for the source code\n\
+   by trying these four places in turn, stopping when it finds anything:\n\n");
+
+    set_path_value(Source_Path, new_name);
+    x = 0;
+    do
+    {   x = translate_in_filename(x, new_name, "zooge", 0, 1);
+        printf("     \"%s\"\n", new_name);
+    } while (x != 0);
+    strcpy(Source_Path, old_name);
+    module_switch = save_mm;
+}
+
+/* ------------------------------------------------------------------------- */
+/*  Naming temporary files                                                   */
+/*       (Arguably temporary files should be made using "tmpfile" in         */
+/*        the ANSI C library, but many supposed ANSI libraries lack it.)     */
+/* ------------------------------------------------------------------------- */
+
+extern void translate_temp_filename(int i)
+{   char *p = NULL;
+    switch(i)
+    {   case 1: p=Temp1_Name; break;
+        case 2: p=Temp2_Name; break;
+        case 3: p=Temp3_Name; break;
+    }
+    if (strlen(Temporary_Path)+strlen(Temporary_File)+6 >= PATHLEN) {
+        printf ("Temporary_Path is too long.\n");
+        exit(1);
+    }
+    sprintf(p,"%s%s%d", Temporary_Path, Temporary_File, i);
+#ifdef INCLUDE_TASK_ID
+    sprintf(p+strlen(p), "_proc%08lx", (long int) unique_task_id());
+#endif
+#ifdef FILE_EXTENSIONS
+    sprintf(p+strlen(p), ".tmp");
+#endif
+}
+
+#ifdef ARCHIMEDES
+static char riscos_ft_buffer[4];
+
+extern char *riscos_file_type(void)
+{
+    if (riscos_file_type_format == 1)
+    {   if (module_switch) return("data");
+        return("11A");
+    }
+
+    if (module_switch) return("075");
+
+    sprintf(riscos_ft_buffer, "%03x", 0x60 + version_number);
+    return(riscos_ft_buffer);
+}
+#endif
+
+/* ------------------------------------------------------------------------- */
+/*   The compilation pass                                                    */
+/* ------------------------------------------------------------------------- */
+
+static void run_pass(void)
+{
+    lexer_begin_prepass();
+    files_begin_prepass();
+    load_sourcefile(Source_Name, 0);
+
+    begin_pass();
+
+    parse_program(NULL);
+
+    find_the_actions();
+    issue_unused_warnings();
+    compile_veneer();
+
+    lexer_endpass();
+    if (module_switch) linker_endpass();
+
+    close_all_source();
+    if (hash_switch && hash_printed_since_newline) printf("\n");
+
+    if (temporary_files_switch)
+    {   if (module_switch) flush_link_data();
+        check_temp_files();
+    }
+    sort_dictionary();
+    if (track_unused_routines)
+        locate_dead_functions();
+    construct_storyfile();
+}
+
+int output_has_occurred;
+
+static void rennab(int32 time_taken)
+{   /*  rennab = reverse of banner  */
+
+    int t = no_warnings + no_suppressed_warnings;
+
+    if (memout_switch) print_memory_usage();
+
+    if ((no_errors + t)!=0)
+    {   printf("Compiled with ");
+        if (no_errors > 0)
+        {   printf("%d error%s", no_errors,(no_errors==1)?"":"s");
+            if (t > 0) printf(" and ");
+        }
+        if (no_warnings > 0)
+            printf("%d warning%s", t, (t==1)?"":"s");
+        if (no_suppressed_warnings > 0)
+        {   if (no_warnings > 0)
+                printf(" (%d suppressed)", no_suppressed_warnings);
+            else
+            printf("%d suppressed warning%s", no_suppressed_warnings,
+                (no_suppressed_warnings==1)?"":"s");
+        }
+        if (output_has_occurred == FALSE) printf(" (no output)");
+        printf("\n");
+    }
+
+    if (no_compiler_errors > 0) print_sorry_message();
+
+    if (statistics_switch)
+        printf("Completed in %ld seconds\n", (long int) time_taken);
+}
+
+/* ------------------------------------------------------------------------- */
+/*   The compiler abstracted to a routine.                                   */
+/* ------------------------------------------------------------------------- */
+
+static int execute_icl_header(char *file1);
+
+static int compile(int number_of_files_specified, char *file1, char *file2)
+{   int32 time_start;
+
+    if (execute_icl_header(file1))
+      return 1;
+
+    select_target(glulx_mode);
+
+    if (define_INFIX_switch && glulx_mode) {
+        printf("Infix (-X) facilities are not available in Glulx: \
+disabling -X switch\n");
+        define_INFIX_switch = FALSE;
+    }
+
+    if (module_switch && glulx_mode) {
+        printf("Modules are not available in Glulx: \
+disabling -M switch\n");
+        module_switch = FALSE;
+    }
+
+    if (define_INFIX_switch && module_switch)
+    {   printf("Infix (-X) facilities are not available when compiling \
+modules: disabling -X switch\n");
+        define_INFIX_switch = FALSE;
+    }
+    if (runtime_error_checking_switch && module_switch)
+    {   printf("Strict checking (-S) facilities are not available when \
+compiling modules: disabling -S switch\n");
+        runtime_error_checking_switch = FALSE;
+    }
+
+    time_start=time(0); no_compilations++;
+
+    strcpy(Source_Name, file1); convert_filename_flag = TRUE;
+    strcpy(Code_Name, file1);
+    if (number_of_files_specified == 2)
+    {   strcpy(Code_Name, file2); convert_filename_flag = FALSE;
+    }
+
+    init_vars();
+
+    if (debugfile_switch) begin_debug_file();
+
+    allocate_arrays();
+
+    if (transcript_switch) open_transcript_file(Source_Name);
+
+    run_pass();
+
+    if (transcript_switch)
+    {   write_dictionary_to_transcript();
+        close_transcript_file();
+    }
+
+    if (no_errors==0) { output_file(); output_has_occurred = TRUE; }
+    else { output_has_occurred = FALSE; }
+
+    if (debugfile_switch)
+    {   end_debug_file();
+    }
+
+    if (temporary_files_switch && (no_errors>0)) remove_temp_files();
+
+    free_arrays();
+
+    rennab((int32) (time(0)-time_start));
+
+    if (optimise_switch) optimise_abbreviations();
+
+    if (store_the_text) my_free(&all_text,"transcription text");
+
+    return (no_errors==0)?0:1;
+}
+
+/* ------------------------------------------------------------------------- */
+/*   The command line interpreter                                            */
+/* ------------------------------------------------------------------------- */
+
+static void cli_print_help(int help_level)
+{
+    printf(
+"\nThis program is a compiler of Infocom format (also called \"Z-machine\")\n\
+story files: copyright (c) Graham Nelson 1993 - 2017.\n\n");
+
+   /* For people typing just "inform", a summary only: */
+
+   if (help_level==0)
+   {
+
+#ifndef PROMPT_INPUT
+  printf("Usage: \"inform [commands...] <file1> [<file2>]\"\n\n");
+#else
+  printf("When run, Inform prompts you for commands (and switches),\n\
+which are optional, then an input <file1> and an (optional) output\n\
+<file2>.\n\n");
+#endif
+
+  printf(
+"<file1> is the Inform source file of the game to be compiled. <file2>,\n\
+if given, overrides the filename Inform would normally use for the\n\
+compiled output.  Try \"inform -h1\" for file-naming conventions.\n\n\
+One or more words can be supplied as \"commands\". These may be:\n\n\
+  -switches     a list of compiler switches, 1 or 2 letter\n\
+                (see \"inform -h2\" for the full range)\n\n\
+  +dir          set Include_Path to this directory\n\
+  +PATH=dir     change the PATH to this directory\n\n\
+  $...          one of the following memory commands:\n");
+  printf(
+"     $list            list current memory allocation settings\n\
+     $huge            make standard \"huge game\" settings %s\n\
+     $large           make standard \"large game\" settings %s\n\
+     $small           make standard \"small game\" settings %s\n\
+     $?SETTING        explain briefly what SETTING is for\n\
+     $SETTING=number  change SETTING to given number\n\n\
+  (filename)    read in a list of commands (in the format above)\n\
+                from this \"setup file\"\n\n",
+    (DEFAULT_MEMORY_SIZE==HUGE_SIZE)?"(default)":"",
+    (DEFAULT_MEMORY_SIZE==LARGE_SIZE)?"(default)":"",
+    (DEFAULT_MEMORY_SIZE==SMALL_SIZE)?"(default)":"");
+
+#ifndef PROMPT_INPUT
+    printf("For example: \"inform -dexs $huge curses\".\n\n");
+#endif
+
+       return;
+   }
+
+   /* The -h1 (filenaming) help information: */
+
+   if (help_level == 1) { help_on_filenames(); return; }
+
+   /* The -h2 (switches) help information: */
+
+   printf("Help on the full list of legal switch commands:\n\n\
+  a   trace assembly-language (without hex dumps; see -t)\n\
+  c   more concise error messages\n\
+  d   contract double spaces after full stops in text\n\
+  d2  contract double spaces after exclamation and question marks, too\n\
+  e   economy mode (slower): make use of declared abbreviations\n");
+
+   printf("\
+  f   frequencies mode: show how useful abbreviations are\n\
+  g   traces calls to functions (except in the library)\n\
+  g2  traces calls to all functions\n\
+  h   print this information\n");
+
+   printf("\
+  i   ignore default switches set within the file\n\
+  j   list objects as constructed\n\
+  k   output Infix debugging information to \"%s\" (and switch -D on)\n\
+  l   list every statement run through Inform\n\
+  m   say how much memory has been allocated\n\
+  n   print numbers of properties, attributes and actions\n",
+          Debugging_Name);
+   printf("\
+  o   print offset addresses\n\
+  p   give percentage breakdown of story file\n\
+  q   keep quiet about obsolete usages\n\
+  r   record all the text to \"%s\"\n\
+  s   give statistics\n\
+  t   trace assembly-language (with full hex dumps; see -a)\n",
+      Transcript_Name);
+
+   printf("\
+  u   work out most useful abbreviations (very very slowly)\n\
+  v3  compile to version-3 (\"Standard\") story file\n\
+  v4  compile to version-4 (\"Plus\") story file\n\
+  v5  compile to version-5 (\"Advanced\") story file: the default\n\
+  v6  compile to version-6 (graphical) story file\n\
+  v8  compile to version-8 (expanded \"Advanced\") story file\n\
+  w   disable warning messages\n\
+  x   print # for every 100 lines compiled\n\
+  y   trace linking system\n\
+  z   print memory map of the Z-machine\n\n");
+
+printf("\
+  B   use big memory model (for large V6/V7 files)\n\
+  C0  text character set is plain ASCII only\n\
+  Cu  text character set is UTF-8\n\
+  Cn  text character set is ISO 8859-n (n = 1 to 9)\n\
+      (1 to 4, Latin1 to Latin4; 5, Cyrillic; 6, Arabic;\n\
+       7, Greek; 8, Hebrew; 9, Latin5.  Default is -C1.)\n");
+printf("  D   insert \"Constant DEBUG;\" automatically\n");
+printf("  E0  Archimedes-style error messages%s\n",
+      (error_format==0)?" (current setting)":"");
+printf("  E1  Microsoft-style error messages%s\n",
+      (error_format==1)?" (current setting)":"");
+printf("  E2  Macintosh MPW-style error messages%s\n",
+      (error_format==2)?" (current setting)":"");
+#ifdef USE_TEMPORARY_FILES
+printf("  F0  use extra memory rather than temporary files\n");
+#else
+printf("  F1  use temporary files to reduce memory consumption\n");
+#endif
+printf("  G   compile a Glulx game file\n");
+printf("  H   use Huffman encoding to compress Glulx strings\n");
+printf("  M   compile as a Module for future linking\n");
+
+#ifdef ARCHIMEDES
+printf("\
+  R0  use filetype 060 + version number for games (default)\n\
+  R1  use official Acorn filetype 11A for all games\n");
+#endif
+printf("  S   compile strict error-checking at run-time (on by default)\n");
+#ifdef ARC_THROWBACK
+printf("  T   enable throwback of errors in the DDE\n");
+#endif
+printf("  U   insert \"Constant USE_MODULES;\" automatically\n");
+printf("  V   print the version and date of this program\n");
+printf("  Wn  header extension table is at least n words (n = 3 to 99)\n");
+printf("  X   compile with INFIX debugging facilities present\n");
+  printf("\n");
+}
+
+extern void switches(char *p, int cmode)
+{   int i, s=1, state;
+    /* Here cmode is 0 if switches list is from a "Switches" directive
+       and 1 if from a "-switches" command-line or ICL list */
+
+    if (cmode==1)
+    {   if (p[0]!='-')
+        {   printf(
+                "Ignoring second word which should be a -list of switches.\n");
+            return;
+        }
+    }
+    for (i=cmode; p[i]!=0; i+=s, s=1)
+    {   state = TRUE;
+        if (p[i] == '~')
+        {   state = FALSE;
+            i++;
+        }
+        switch(p[i])
+        {
+        case 'a': asm_trace_setting = 1; break;
+        case 'b': bothpasses_switch = state; break;
+        case 'c': concise_switch = state; break;
+        case 'd': switch(p[i+1])
+                  {   case '1': double_space_setting=1; s=2; break;
+                      case '2': double_space_setting=2; s=2; break;
+                      default: double_space_setting=1; break;
+                  }
+                  break;
+        case 'e': economy_switch = state; break;
+        case 'f': frequencies_switch = state; break;
+        case 'g': switch(p[i+1])
+                  {   case '1': trace_fns_setting=1; s=2; break;
+                      case '2': trace_fns_setting=2; s=2; break;
+                      default: trace_fns_setting=1; break;
+                  }
+                  break;
+        case 'h': switch(p[i+1])
+                  {   case '1': cli_print_help(1); s=2; break;
+                      case '2': cli_print_help(2); s=2; break;
+                      case '0': s=2;
+                      default:  cli_print_help(0); break;
+                  }
+                  break;
+        case 'i': ignore_switches_switch = state; break;
+        case 'j': listobjects_switch = state; break;
+        case 'k': if (cmode == 0)
+                      error("The switch '-k' can't be set with 'Switches'");
+                  else
+                  {   debugfile_switch = state;
+                      if (state) define_DEBUG_switch = TRUE;
+                  }
+                  break;
+        case 'l': listing_switch = state; break;
+        case 'm': memout_switch = state; break;
+        case 'n': printprops_switch = state; break;
+        case 'o': offsets_switch = state; break;
+        case 'p': percentages_switch = state; break;
+        case 'q': obsolete_switch = state; break;
+        case 'r': if (cmode == 0)
+                      error("The switch '-r' can't be set with 'Switches'");
+                  else
+                      transcript_switch = state; break;
+        case 's': statistics_switch = state; break;
+        case 't': asm_trace_setting=2; break;
+        case 'u': if (cmode == 0) {
+                      error("The switch '-u' can't be set with 'Switches'");
+                      break;
+                  }
+                  optimise_switch = state; break;
+        case 'v': if (glulx_mode) { s = select_glulx_version(p+i+1)+1; break; }
+                  if ((cmode==0) && (version_set_switch)) { s=2; break; }
+                  version_set_switch = TRUE; s=2;
+                  switch(p[i+1])
+                  {   case '3': select_version(3); break;
+                      case '4': select_version(4); break;
+                      case '5': select_version(5); break;
+                      case '6': select_version(6); break;
+                      case '7': select_version(7); break;
+                      case '8': select_version(8); break;
+                      default:  printf("-v must be followed by 3 to 8\n");
+                                version_set_switch=0; s=1;
+                                break;
+                  }
+                  if ((version_number < 5) && (r_e_c_s_set == FALSE))
+                      runtime_error_checking_switch = FALSE;
+                  break;
+        case 'w': nowarnings_switch = state; break;
+        case 'x': hash_switch = state; break;
+        case 'y': s=2; linker_trace_setting=p[i+1]-'0'; break;
+        case 'z': memory_map_switch = state; break;
+        case 'B': oddeven_packing_switch = state; break;
+        case 'C': s=2;
+                  if (p[i+1] == 'u') {
+                      character_set_unicode = TRUE;
+                      /* Leave the set_setting on Latin-1, because that 
+                         matches the first block of Unicode. */
+                      character_set_setting = 1;
+                  }
+                  else 
+                  {   character_set_setting=p[i+1]-'0';
+                      if ((character_set_setting < 0)
+                          || (character_set_setting > 9))
+                      {   printf("-C must be followed by 'u' or 0 to 9. Defaulting to ISO-8859-1.\n");
+                          character_set_unicode = FALSE;
+                          character_set_setting = 1;
+                      }
+                  }
+                  if (cmode == 0) change_character_set();
+                  break;
+        case 'D': define_DEBUG_switch = state; break;
+        case 'E': switch(p[i+1])
+                  {   case '0': s=2; error_format=0; break;
+                      case '1': s=2; error_format=1; break;
+                      case '2': s=2; error_format=2; break;
+                      default:  error_format=1; break;
+                  }
+                  break;
+        case 'F': if (cmode == 0) {
+                      error("The switch '-F' can't be set with 'Switches'");
+                      break;
+                  }
+                  switch(p[i+1])
+                  {   case '0': s=2; temporary_files_switch = FALSE; break;
+                      case '1': s=2; temporary_files_switch = TRUE; break;
+                      default:  temporary_files_switch = state; break;
+                  }
+                  break;
+        case 'M': module_switch = state;
+                  if (state && (r_e_c_s_set == FALSE))
+                      runtime_error_checking_switch = FALSE;
+                  break;
+#ifdef ARCHIMEDES
+        case 'R': switch(p[i+1])
+                  {   case '0': s=2; riscos_file_type_format=0; break;
+                      case '1': s=2; riscos_file_type_format=1; break;
+                      default:  riscos_file_type_format=1; break;
+                  }
+                  break;
+#endif
+#ifdef ARC_THROWBACK
+        case 'T': throwback_switch = state; break;
+#endif
+        case 'S': runtime_error_checking_switch = state;
+                  r_e_c_s_set = TRUE; break;
+        case 'G': if (cmode == 0)
+                      error("The switch '-G' can't be set with 'Switches'");
+                  else
+                  {   glulx_mode = state;
+                      adjust_memory_sizes();
+                  }
+                  break;
+        case 'H': compression_switch = state; break;
+        case 'U': define_USE_MODULES_switch = state; break;
+        case 'V': exit(0); break;
+        case 'W': if ((p[i+1]>='0') && (p[i+1]<='9'))
+                  {   s=2; ZCODE_HEADER_EXT_WORDS = p[i+1]-'0';
+                      if ((p[i+2]>='0') && (p[i+2]<='9'))
+                      {   s=3; ZCODE_HEADER_EXT_WORDS *= 10;
+                          ZCODE_HEADER_EXT_WORDS += p[i+2]-'0';
+                      }
+                  }
+                  break;
+        case 'X': define_INFIX_switch = state; break;
+        default:
+          printf("Switch \"-%c\" unknown (try \"inform -h2\" for the list)\n",
+              p[i]);
+          break;
+        }
+    }
+
+    if (optimise_switch && (!store_the_text))
+    {   store_the_text=TRUE;
+#ifdef PC_QUICKC
+        if (memout_switch)
+            printf("Allocation %ld bytes for transcription text\n",
+                (long) MAX_TRANSCRIPT_SIZE);
+        all_text = halloc(MAX_TRANSCRIPT_SIZE,1);
+        malloced_bytes += MAX_TRANSCRIPT_SIZE;
+        if (all_text==NULL)
+         fatalerror("Can't hallocate memory for transcription text.  Darn.");
+#else
+        all_text=my_malloc(MAX_TRANSCRIPT_SIZE,"transcription text");
+#endif
+    }
+}
+
+static int icl_command(char *p)
+{   if ((p[0]=='+')||(p[0]=='-')||(p[0]=='$')
+        || ((p[0]=='(')&&(p[strlen(p)-1]==')')) ) return TRUE;
+    return FALSE;
+}
+
+static void icl_error(char *filename, int line)
+{   printf("Error in ICL file '%s', line %d:\n", filename, line);
+}
+
+static void icl_header_error(char *filename, int line)
+{   printf("Error in ICL header of file '%s', line %d:\n", filename, line);
+}
+
+static int copy_icl_word(char *from, char *to, int max)
+{
+    /*  Copies one token from 'from' to 'to', null-terminated:
+        returns the number of chars in 'from' read past (possibly 0).  */
+
+    int i, j, quoted_mode, truncated;
+
+    i = 0; truncated = 0;
+    while ((from[i] == ' ') || (from[i] == TAB_CHARACTER)
+           || (from[i] == (char) 10) || (from[i] == (char) 13)) i++;
+
+    if (from[i] == '!')
+    {   while (from[i] != 0) i++;
+        to[0] = 0; return i;
+    }
+
+    for (quoted_mode = FALSE, j=0;;)
+    {   if (from[i] == 0) break;
+        if (from[i] == 10) break;
+        if (from[i] == 13) break;
+        if (from[i] == TAB_CHARACTER) break;
+        if ((from[i] == ' ') && (!quoted_mode)) break;
+        if (from[i] == '\"') { quoted_mode = !quoted_mode; i++; }
+        else to[j++] = from[i++];
+        if (j == max) {
+            j--;
+            truncated = 1;
+        }
+    }
+    to[j] = 0;
+    if (truncated == 1)
+        printf("The following parameter has been truncated:\n%s\n", to);
+    return i;
+}
+
+static void execute_icl_command(char *p);
+
+static int execute_icl_header(char *argname)
+{
+  FILE *command_file;
+  char cli_buff[256], fw[256];
+  int line = 0;
+  int errcount = 0;
+  int i;
+  char filename[PATHLEN]; 
+  int x = 0;
+
+  do
+    {   x = translate_in_filename(x, filename, argname, 0, 1);
+        command_file = fopen(filename,"r");
+    } while ((command_file == NULL) && (x != 0));
+  if (!command_file) {
+    /* Fail silently. The regular compiler will try to open the file
+       again, and report the problem. */
+    return 0;
+  }
+
+  while (feof(command_file)==0) {
+    if (fgets(cli_buff,256,command_file)==0) break;
+    line++;
+    if (!(cli_buff[0] == '!' && cli_buff[1] == '%'))
+      break;
+    i = copy_icl_word(cli_buff+2, fw, 256);
+    if (icl_command(fw)) {
+      execute_icl_command(fw);
+      copy_icl_word(cli_buff+2 + i, fw, 256);
+      if ((fw[0] != 0) && (fw[0] != '!')) {
+        icl_header_error(filename, line);
+        errcount++;
+        printf("expected comment or nothing but found '%s'\n", fw);
+      }
+    }
+    else {
+      if (fw[0]!=0) {
+        icl_header_error(filename, line);
+        errcount++;
+        printf("Expected command or comment but found '%s'\n", fw);
+      }
+    }
+  }
+  fclose(command_file);
+
+  return (errcount==0)?0:1;
+}
+
+
+static void run_icl_file(char *filename, FILE *command_file)
+{   char cli_buff[256], fw[256];
+    int i, x, line = 0;
+    printf("[Running ICL file '%s']\n", filename);
+
+    while (feof(command_file)==0)
+    {   if (fgets(cli_buff,256,command_file)==0) break;
+        line++;
+        i = copy_icl_word(cli_buff, fw, 256);
+        if (icl_command(fw))
+        {   execute_icl_command(fw);
+            copy_icl_word(cli_buff + i, fw, 256);
+            if ((fw[0] != 0) && (fw[0] != '!'))
+            {   icl_error(filename, line);
+                printf("expected comment or nothing but found '%s'\n", fw);
+            }
+        }
+        else
+        {   if (strcmp(fw, "compile")==0)
+            {   char story_name[PATHLEN], code_name[PATHLEN];
+                i += copy_icl_word(cli_buff + i, story_name, PATHLEN);
+                i += copy_icl_word(cli_buff + i, code_name, PATHLEN);
+
+                if (code_name[0] != 0) x=2;
+                else if (story_name[0] != 0) x=1;
+                else x=0;
+
+                switch(x)
+                {   case 0: icl_error(filename, line);
+                            printf("No filename given to 'compile'\n");
+                            break;
+                    case 1: printf("[Compiling <%s>]\n", story_name);
+                            compile(x, story_name, code_name);
+                            break;
+                    case 2: printf("[Compiling <%s> to <%s>]\n",
+                                story_name, code_name);
+                            compile(x, story_name, code_name);
+                            copy_icl_word(cli_buff + i, fw, 256);
+                            if (fw[0]!=0)
+                            {   icl_error(filename, line);
+                        printf("Expected comment or nothing but found '%s'\n",
+                                fw);
+                            }
+                            break;
+                }
+            }
+            else
+            if (fw[0]!=0)
+            {   icl_error(filename, line);
+                printf("Expected command or comment but found '%s'\n", fw);
+            }
+        }
+    }
+}
+
+static void execute_icl_command(char *p)
+{   char filename[PATHLEN], cli_buff[256];
+    FILE *command_file;
+
+    switch(p[0])
+    {   case '+': set_path_command(p+1); break;
+        case '-': switches(p,1); break;
+        case '$': memory_command(p+1); break;
+        case '(': strcpy(cli_buff,p+1); cli_buff[strlen(cli_buff)-1]=0;
+                  {   int x = 0;
+                      do
+                      {   x = translate_icl_filename(x, filename, cli_buff);
+                          command_file = fopen(filename,"r");
+                      } while ((command_file == NULL) && (x != 0));
+                  }
+                  if (command_file == NULL)
+                      printf("Error in ICL: Couldn't open command file '%s'\n",
+                          filename);
+                  else
+                  {   run_icl_file(filename, command_file);
+                      fclose(command_file);
+                  }
+                  break;
+    }
+}
+
+/* ------------------------------------------------------------------------- */
+/*   Opening and closing banners                                             */
+/* ------------------------------------------------------------------------- */
+
+char banner_line[80];
+
+static void banner(void)
+{
+    sprintf(banner_line, "Inform %s",
+        (VERSION));
+#ifdef RELEASE_SUFFIX
+    strcat(banner_line, RELEASE_SUFFIX);
+#endif
+#ifdef MACHINE_STRING
+    sprintf(banner_line+strlen(banner_line), " for %s", MACHINE_STRING);
+#endif
+    sprintf(banner_line+strlen(banner_line), " (%s)", RELEASE_DATE);
+    printf("%s\n", banner_line);
+}
+
+/* ------------------------------------------------------------------------- */
+/*   Input from the outside world                                            */
+/* ------------------------------------------------------------------------- */
+
+#ifdef PROMPT_INPUT
+static void read_command_line(int argc, char **argv)
+{   int i;
+    char buffer1[PATHLEN], buffer2[PATHLEN], buffer3[PATHLEN];
+    i=0;
+    printf("Source filename?\n> ");
+    while (gets(buffer1)==NULL); cli_file1=buffer1;
+    printf("Output filename (RETURN for the same)?\n> ");
+    while (gets(buffer2)==NULL); cli_file2=buffer2;
+    cli_files_specified=1;
+    if (buffer2[0]!=0) cli_files_specified=2;
+    do
+    {   printf("List of commands (RETURN to finish; \"-h\" for help)?\n> ");
+        while (gets(buffer3)==NULL); execute_icl_command(buffer3);
+    } while (buffer3[0]!=0);
+}
+#else
+static void read_command_line(int argc, char **argv)
+{   int i;
+    if (argc==1) switches("-h",1);
+
+    for (i=1, cli_files_specified=0; i<argc; i++)
+        if (icl_command(argv[i]))
+            execute_icl_command(argv[i]);
+        else
+            switch(++cli_files_specified)
+            {   case 1: cli_file1 = argv[i]; break;
+                case 2: cli_file2 = argv[i]; break;
+                default:
+                    printf("Command line error: unknown parameter '%s'\n",
+                        argv[i]); return;
+            }
+}
+#endif
+
+/* ------------------------------------------------------------------------- */
+/*   M A I N : An outer shell for machine-specific quirks                    */
+/*   Omitted altogether if EXTERNAL_SHELL is defined, as for instance is     */
+/*   needed for the Macintosh front end.                                     */
+/* ------------------------------------------------------------------------- */
+
+#ifdef EXTERNAL_SHELL
+extern int sub_main(int argc, char **argv);
+#else
+
+static int sub_main(int argc, char **argv);
+#ifdef MAC_MPW
+int main(int argc, char **argv, char *envp[])
+#else
+int main(int argc, char **argv)
+#endif
+{   int rcode;
+#ifdef MAC_MPW
+    InitCursorCtl((acurHandle)NULL); Show_Cursor(WATCH_CURSOR);
+#endif
+    rcode = sub_main(argc, argv);
+#ifdef ARC_THROWBACK
+    throwback_end();
+#endif
+    return rcode;
+}
+
+#endif
+
+/* ------------------------------------------------------------------------- */
+/*   M A I N  II:  Starting up ICL with the command line                     */
+/* ------------------------------------------------------------------------- */
+
+#ifdef EXTERNAL_SHELL
+extern int sub_main(int argc, char **argv)
+#else
+static int sub_main(int argc, char **argv)
+#endif
+{   int return_code;
+
+#ifdef MAC_FACE
+    ProcessEvents (&g_proc);
+    if (g_proc != true)
+    {   free_arrays();
+        if (store_the_text) my_free(&all_text,"transcription text");
+        longjmp (g_fallback, 1);
+    }
+#endif
+
+    banner();
+
+    set_memory_sizes(DEFAULT_MEMORY_SIZE); set_default_paths();
+    reset_switch_settings(); select_version(5);
+
+    cli_files_specified = 0; no_compilations = 0;
+    cli_file1 = "source"; cli_file2 = "output";
+
+    read_command_line(argc, argv);
+
+    if (cli_files_specified > 0)
+    {   return_code = compile(cli_files_specified, cli_file1, cli_file2);
+
+        if (return_code != 0) return(return_code);
+    }
+
+    if (no_compilations == 0)
+        printf("\n[No compilation requested]\n");
+    if (no_compilations > 1)
+        printf("[%d compilations completed]\n", no_compilations);
+
+    return(0);
+}
+
+/* ========================================================================= */