Implement a Makefile for Inform.
[inform.git] / src / symbols.c
diff --git a/src/symbols.c b/src/symbols.c
new file mode 100644 (file)
index 0000000..3263166
--- /dev/null
@@ -0,0 +1,1493 @@
+/* ------------------------------------------------------------------------- */
+/*   "symbols" :  The symbols table; creating stock of reserved words        */
+/*                                                                           */
+/*  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/                  */
+/*                                                                           */
+/* ------------------------------------------------------------------------- */
+
+#include "header.h"
+
+/* ------------------------------------------------------------------------- */
+/*   This section of Inform is a service detached from the rest.             */
+/*   Only two variables are accessible from the outside:                     */
+/* ------------------------------------------------------------------------- */
+
+int no_symbols;                        /* Total number of symbols defined    */
+int no_named_constants;                         /* Copied into story file    */
+
+/* ------------------------------------------------------------------------- */
+/*   Plus six arrays.  Each symbol has its own index n (an int32) and        */
+/*                                                                           */
+/*       svals[n]   is its value (must be 32 bits wide, i.e. an int32, tho'  */
+/*                  it is used to hold an unsigned 16 bit Z-machine value)   */
+/*       sflags[n]  holds flags (see "header.h" for a list)                  */
+/*       stypes[n]  is the "type", distinguishing between the data type of   */
+/*                  different kinds of constants/variables.                  */
+/*                  (See the "typename()" below.)                            */
+/*       symbs[n]   (needs to be cast to (char *) to be used) is the name    */
+/*                  of the symbol, in the same case form as when created.    */
+/*       slines[n]  is the source line on which the symbol value was first   */
+/*                  assigned                                                 */
+/*       symbol_debug_backpatch_positions[n]                                 */
+/*                  is a file position in the debug information file where   */
+/*                  the symbol's value should be written after backpatching, */
+/*                  or else the null position if the value was known and     */
+/*                  written beforehand                                       */
+/*       replacement_debug_backpatch_positions[n]                            */
+/*                  is a file position in the debug information file where   */
+/*                  the symbol's name can be erased if it is replaced, or    */
+/*                  else null if the name will never need to be replaced     */
+/*                                                                           */
+/*   Comparison is case insensitive.                                         */
+/*   Note that local variable names are not entered into the symbols table,  */
+/*   as their numbers and scope are too limited for this to be efficient.    */
+/* ------------------------------------------------------------------------- */
+/*   Caveat editor: some array types are set up to work even on machines     */
+/*   where sizeof(int32 *) differs from, e.g., sizeof(char *): so do not     */
+/*   alter the types unless you understand what is going on!                 */
+/* ------------------------------------------------------------------------- */
+
+  int32  **symbs;
+  int32  *svals;
+  int    *smarks;            /* Glulx-only */
+  brief_location  *slines;
+  int    *sflags;
+#ifdef VAX
+  char   *stypes;            /* In VAX C, insanely, "signed char" is illegal */
+#else
+  signed char *stypes;
+#endif
+  maybe_file_position  *symbol_debug_backpatch_positions;
+  maybe_file_position  *replacement_debug_backpatch_positions;
+
+/* ------------------------------------------------------------------------- */
+/*   Memory to hold the text of symbol names: note that this memory is       */
+/*   allocated as needed in chunks of size SYMBOLS_CHUNK_SIZE.               */
+/* ------------------------------------------------------------------------- */
+
+#define MAX_SYMBOL_CHUNKS (100)
+
+static uchar *symbols_free_space,       /* Next byte free to hold new names  */
+           *symbols_ceiling;            /* Pointer to the end of the current
+                                           allocation of memory for names    */
+
+static char** symbol_name_space_chunks; /* For chunks of memory used to hold
+                                           the name strings of symbols       */
+static int no_symbol_name_space_chunks;
+
+typedef struct value_pair_struct {
+    int original_symbol;
+    int renamed_symbol;
+} value_pair_t;
+static value_pair_t *symbol_replacements;
+static int symbol_replacements_count;
+static int symbol_replacements_size; /* calloced size */
+
+/* ------------------------------------------------------------------------- */
+/*   The symbols table is "hash-coded" into a disjoint union of linked       */
+/*   lists, so that for any symbol i, next_entry[i] is either -1 (meaning    */
+/*   that it's the last in its list) or the next in the list.                */
+/*                                                                           */
+/*   Each list contains, in alphabetical order, all the symbols which share  */
+/*   the same "hash code" (a numerical function of the text of the symbol    */
+/*   name, designed with the aim that roughly equal numbers of symbols are   */
+/*   given each possible hash code).  The hash codes are 0 to HASH_TAB_SIZE  */
+/*   (which is a memory setting) minus 1: start_of_list[h] gives the first   */
+/*   symbol with hash code h, or -1 if no symbol exists with hash code h.    */
+/*                                                                           */
+/*   Note that the running time of the symbol search algorithm is about      */
+/*                                                                           */
+/*       O ( n^2 / HASH_TAB_SIZE )                                           */
+/*                                                                           */
+/*   (where n is the number of symbols in the program) so that it is a good  */
+/*   idea to choose HASH_TAB_SIZE as large as conveniently possible.         */
+/* ------------------------------------------------------------------------- */
+
+static int   *next_entry;
+static int32 *start_of_list;
+
+/* ------------------------------------------------------------------------- */
+/*   Initialisation.                                                         */
+/* ------------------------------------------------------------------------- */
+
+static void init_symbol_banks(void)
+{   int i;
+    for (i=0; i<HASH_TAB_SIZE; i++) start_of_list[i] = -1;
+}
+
+/* ------------------------------------------------------------------------- */
+/*   The hash coding we use is quite standard; the variable hashcode is      */
+/*   expected to overflow a good deal.  (The aim is to produce a number      */
+/*   so that similar names do not produce the same number.)  Note that       */
+/*   30011 is prime.  It doesn't matter if the unsigned int to int cast      */
+/*   behaves differently on different ports.                                 */
+/* ------------------------------------------------------------------------- */
+
+int case_conversion_grid[128];
+
+static void make_case_conversion_grid(void)
+{
+    /*  Assumes that A to Z are contiguous in the host OS character set:
+        true for ASCII but not for EBCDIC, for instance.                     */
+
+    int i;
+    for (i=0; i<128; i++) case_conversion_grid[i] = i;
+    for (i=0; i<26; i++) case_conversion_grid['A'+i]='a'+i;
+}
+
+extern int hash_code_from_string(char *p)
+{   uint32 hashcode=0;
+    for (; *p; p++) hashcode=hashcode*30011 + case_conversion_grid[(uchar)*p];
+    return (int) (hashcode % HASH_TAB_SIZE);
+}
+
+extern int strcmpcis(char *p, char *q)
+{
+    /*  Case insensitive strcmp  */
+
+    int i, j, pc, qc;
+    for (i=0;p[i] != 0;i++)
+    {   pc = p[i]; if (isupper(pc)) pc = tolower(pc);
+        qc = q[i]; if (isupper(qc)) qc = tolower(qc);
+        j = pc - qc;
+        if (j!=0) return j;
+    }
+    qc = q[i]; if (isupper(qc)) qc = tolower(qc);
+    return -qc;
+}
+
+/* ------------------------------------------------------------------------- */
+/*   Symbol finding, creating, and removing.                                 */
+/* ------------------------------------------------------------------------- */
+
+extern int symbol_index(char *p, int hashcode)
+{
+    /*  Return the index in the symbs/svals/sflags/stypes/... arrays of symbol
+        "p", creating a new symbol with that name if it isn't already there.
+
+        New symbols are created with flag UNKNOWN_SFLAG, value 0x100
+        (a 2-byte quantity in Z-machine terms) and type CONSTANT_T.
+
+        The string "p" is undamaged.                                         */
+
+    int32 new_entry, this, last; char *r;
+
+    if (hashcode == -1) hashcode = hash_code_from_string(p);
+
+    this = start_of_list[hashcode]; last = -1;
+
+    do
+    {   if (this == -1) break;
+
+        r = (char *)symbs[this];
+        new_entry = strcmpcis(r, p);
+        if (new_entry == 0) 
+        {
+            if (track_unused_routines)
+                df_note_function_symbol(this);
+            return this;
+        }
+        if (new_entry > 0) break;
+
+        last = this;
+        this = next_entry[this];
+    } while (this != -1);
+
+    if (no_symbols >= MAX_SYMBOLS)
+        memoryerror("MAX_SYMBOLS", MAX_SYMBOLS);
+
+    if (last == -1)
+    {   next_entry[no_symbols]=start_of_list[hashcode];
+        start_of_list[hashcode]=no_symbols;
+    }
+    else
+    {   next_entry[no_symbols]=this;
+        next_entry[last]=no_symbols;
+    }
+
+    if (symbols_free_space+strlen(p)+1 >= symbols_ceiling)
+    {   symbols_free_space
+            = my_malloc(SYMBOLS_CHUNK_SIZE, "symbol names chunk");
+        symbols_ceiling = symbols_free_space + SYMBOLS_CHUNK_SIZE;
+        /* If we've passed MAX_SYMBOL_CHUNKS chunks, we print an error
+           message telling the user to increase SYMBOLS_CHUNK_SIZE.
+           That is the correct cure, even though the error comes out
+           worded inaccurately. */
+        if (no_symbol_name_space_chunks >= MAX_SYMBOL_CHUNKS)
+            memoryerror("SYMBOLS_CHUNK_SIZE", SYMBOLS_CHUNK_SIZE);
+        symbol_name_space_chunks[no_symbol_name_space_chunks++]
+            = (char *) symbols_free_space;
+        if (symbols_free_space+strlen(p)+1 >= symbols_ceiling)
+            memoryerror("SYMBOLS_CHUNK_SIZE", SYMBOLS_CHUNK_SIZE);
+    }
+
+    strcpy((char *) symbols_free_space, p);
+    symbs[no_symbols] = (int32 *) symbols_free_space;
+    symbols_free_space += strlen((char *)symbols_free_space) + 1;
+
+    svals[no_symbols]   =  0x100; /* ###-wrong? Would this fix the
+                                     unbound-symbol-causes-asm-error? */
+    sflags[no_symbols]  =  UNKNOWN_SFLAG;
+    stypes[no_symbols]  =  CONSTANT_T;
+    slines[no_symbols]  =  get_brief_location(&ErrorReport);
+    if (debugfile_switch)
+    {   nullify_debug_file_position
+            (&symbol_debug_backpatch_positions[no_symbols]);
+        nullify_debug_file_position
+            (&replacement_debug_backpatch_positions[no_symbols]);
+    }
+
+    if (track_unused_routines)
+        df_note_function_symbol(no_symbols);
+    return(no_symbols++);
+}
+
+extern void end_symbol_scope(int k)
+{
+    /* Remove the given symbol from the hash table, making it
+       invisible to symbol_index. This is used by the Undef directive.
+       If the symbol is not found, this silently does nothing.
+    */
+
+    int j;
+    j = hash_code_from_string((char *) symbs[k]);
+    if (start_of_list[j] == k)
+    {   start_of_list[j] = next_entry[k];
+        return;
+    }
+    j = start_of_list[j];
+    while (j != -1)
+    {
+        if (next_entry[j] == k)
+        {   next_entry[j] = next_entry[k];
+            return;
+        }
+        j = next_entry[j];
+    }
+}
+
+/* ------------------------------------------------------------------------- */
+/*   Printing diagnostics                                                    */
+/* ------------------------------------------------------------------------- */
+
+extern char *typename(int type)
+{   switch(type)
+    {
+        /*  These are the possible symbol types.  Note that local variables
+            do not reside in the symbol table (for scope and efficiency
+            reasons) and actions have their own name-space (via routine
+            names with "Sub" appended).                                      */
+
+        case ROUTINE_T:             return("Routine");
+        case LABEL_T:               return("Label");
+        case GLOBAL_VARIABLE_T:     return("Global variable");
+        case ARRAY_T:               return("Array");
+        case CONSTANT_T:            return("Defined constant");
+        case ATTRIBUTE_T:           return("Attribute");
+        case PROPERTY_T:            return("Property");
+        case INDIVIDUAL_PROPERTY_T: return("Individual property");
+        case OBJECT_T:              return("Object");
+        case CLASS_T:               return("Class");
+        case FAKE_ACTION_T:         return("Fake action");
+
+        default:                   return("(Unknown type)");
+    }
+}
+
+static void describe_flags(int flags)
+{   if (flags & UNKNOWN_SFLAG)  printf("(?) ");
+    if (flags & USED_SFLAG)     printf("(used) ");
+    if (flags & REPLACE_SFLAG)  printf("(Replaced) ");
+    if (flags & DEFCON_SFLAG)   printf("(Defaulted) ");
+    if (flags & STUB_SFLAG)     printf("(Stubbed) ");
+    if (flags & CHANGE_SFLAG)   printf("(value will change) ");
+    if (flags & IMPORT_SFLAG)   printf("(Imported) ");
+    if (flags & EXPORT_SFLAG)   printf("(Exported) ");
+    if (flags & SYSTEM_SFLAG)   printf("(System) ");
+    if (flags & INSF_SFLAG)     printf("(created in sys file) ");
+    if (flags & UERROR_SFLAG)   printf("('Unknown' error issued) ");
+    if (flags & ALIASED_SFLAG)  printf("(aliased) ");
+    if (flags & ACTION_SFLAG)   printf("(Action name) ");
+    if (flags & REDEFINABLE_SFLAG) printf("(Redefinable) ");
+}
+
+extern void describe_symbol(int k)
+{   printf("%4d  %-16s  %2d:%04d  %04x  %s  ",
+        k, (char *) (symbs[k]), 
+        (int)(slines[k].file_index),
+        (int)(slines[k].line_number),
+        svals[k], typename(stypes[k]));
+    describe_flags(sflags[k]);
+}
+
+extern void list_symbols(int level)
+{   int k;
+    for (k=0; k<no_symbols; k++)
+    {   if ((level==2) ||
+            ((sflags[k] & (SYSTEM_SFLAG + UNKNOWN_SFLAG + INSF_SFLAG)) == 0))
+        {   describe_symbol(k); printf("\n");
+        }
+    }
+}
+
+extern void issue_unused_warnings(void)
+{   int32 i;
+
+    if (module_switch) return;
+
+    /*  Update any ad-hoc variables that might help the library  */
+    if (glulx_mode)
+    {   global_initial_value[10]=statusline_flag;
+    }
+    /*  Now back to mark anything necessary as used  */
+
+    i = symbol_index("Main", -1);
+    if (!(sflags[i] & UNKNOWN_SFLAG)) sflags[i] |= USED_SFLAG;
+
+    for (i=0;i<no_symbols;i++)
+    {   if (((sflags[i]
+             & (SYSTEM_SFLAG + UNKNOWN_SFLAG + EXPORT_SFLAG
+                + INSF_SFLAG + USED_SFLAG + REPLACE_SFLAG)) == 0)
+             && (stypes[i] != OBJECT_T))
+            dbnu_warning(typename(stypes[i]), (char *) symbs[i], slines[i]);
+    }
+}
+
+/* ------------------------------------------------------------------------- */
+/*   These are arrays used only during story file (never module) creation,   */
+/*   and not allocated until then.                                           */
+
+       int32 *individual_name_strings; /* Packed addresses of Z-encoded
+                                          strings of the names of the
+                                          properties: this is an array
+                                          indexed by the property ID         */
+       int32 *action_name_strings;     /* Ditto for actions                  */
+       int32 *attribute_name_strings;  /* Ditto for attributes               */
+       int32 *array_name_strings;      /* Ditto for arrays                   */
+
+extern void write_the_identifier_names(void)
+{   int i, j, k, t, null_value; char idname_string[256];
+    static char unknown_attribute[20] = "<unknown attribute>";
+
+    for (i=0; i<no_individual_properties; i++)
+        individual_name_strings[i] = 0;
+
+    if (module_switch) return;
+
+    veneer_mode = TRUE;
+
+    null_value = compile_string(unknown_attribute, FALSE, FALSE);
+    for (i=0; i<NUM_ATTR_BYTES*8; i++) attribute_name_strings[i] = null_value;
+
+    for (i=0; i<no_symbols; i++)
+    {   t=stypes[i];
+        if ((t == INDIVIDUAL_PROPERTY_T) || (t == PROPERTY_T))
+        {   if (sflags[i] & ALIASED_SFLAG)
+            {   if (individual_name_strings[svals[i]] == 0)
+                {   sprintf(idname_string, "%s", (char *) symbs[i]);
+
+                    for (j=i+1, k=0; (j<no_symbols && k<3); j++)
+                    {   if ((stypes[j] == stypes[i])
+                            && (svals[j] == svals[i]))
+                        {   sprintf(idname_string+strlen(idname_string),
+                                "/%s", (char *) symbs[j]);
+                            k++;
+                        }
+                    }
+
+                    individual_name_strings[svals[i]]
+                        = compile_string(idname_string, FALSE, FALSE);
+                }
+            }
+            else
+            {   sprintf(idname_string, "%s", (char *) symbs[i]);
+
+                individual_name_strings[svals[i]]
+                    = compile_string(idname_string, FALSE, FALSE);
+            }
+        }
+        if (t == ATTRIBUTE_T)
+        {   if (sflags[i] & ALIASED_SFLAG)
+            {   if (attribute_name_strings[svals[i]] == null_value)
+                {   sprintf(idname_string, "%s", (char *) symbs[i]);
+
+                    for (j=i+1, k=0; (j<no_symbols && k<3); j++)
+                    {   if ((stypes[j] == stypes[i])
+                            && (svals[j] == svals[i]))
+                        {   sprintf(idname_string+strlen(idname_string),
+                                "/%s", (char *) symbs[j]);
+                            k++;
+                        }
+                    }
+
+                    attribute_name_strings[svals[i]]
+                        = compile_string(idname_string, FALSE, FALSE);
+                }
+            }
+            else
+            {   sprintf(idname_string, "%s", (char *) symbs[i]);
+
+                attribute_name_strings[svals[i]]
+                    = compile_string(idname_string, FALSE, FALSE);
+            }
+        }
+        if (sflags[i] & ACTION_SFLAG)
+        {   sprintf(idname_string, "%s", (char *) symbs[i]);
+            idname_string[strlen(idname_string)-3] = 0;
+
+            if (debugfile_switch)
+            {   debug_file_printf("<action>");
+                debug_file_printf
+                    ("<identifier>##%s</identifier>", idname_string);
+                debug_file_printf("<value>%d</value>", svals[i]);
+                debug_file_printf("</action>");
+            }
+
+            action_name_strings[svals[i]]
+                = compile_string(idname_string, FALSE, FALSE);
+        }
+    }
+
+    for (i=0; i<no_symbols; i++)
+    {   if (stypes[i] == FAKE_ACTION_T)
+        {   sprintf(idname_string, "%s", (char *) symbs[i]);
+            idname_string[strlen(idname_string)-3] = 0;
+
+            action_name_strings[svals[i]
+                    - ((grammar_version_number==1)?256:4096) + no_actions]
+                = compile_string(idname_string, FALSE, FALSE);
+        }
+    }
+
+    for (j=0; j<no_arrays; j++)
+    {   i = array_symbols[j];
+        sprintf(idname_string, "%s", (char *) symbs[i]);
+
+        array_name_strings[j]
+            = compile_string(idname_string, FALSE, FALSE);
+    }
+  if (define_INFIX_switch)
+  { for (i=0; i<no_symbols; i++)
+    {   if (stypes[i] == GLOBAL_VARIABLE_T)
+        {   sprintf(idname_string, "%s", (char *) symbs[i]);
+            array_name_strings[no_arrays + svals[i] -16]
+                = compile_string(idname_string, FALSE, FALSE);
+        }
+    }
+
+    for (i=0; i<no_named_routines; i++)
+    {   sprintf(idname_string, "%s", (char *) symbs[named_routine_symbols[i]]);
+            array_name_strings[no_arrays + no_globals + i]
+                = compile_string(idname_string, FALSE, FALSE);
+    }
+
+    for (i=0, no_named_constants=0; i<no_symbols; i++)
+    {   if (((stypes[i] == OBJECT_T) || (stypes[i] == CLASS_T)
+            || (stypes[i] == CONSTANT_T))
+            && ((sflags[i] & (UNKNOWN_SFLAG+ACTION_SFLAG))==0))
+        {   sprintf(idname_string, "%s", (char *) symbs[i]);
+            array_name_strings[no_arrays + no_globals + no_named_routines
+                + no_named_constants++]
+                = compile_string(idname_string, FALSE, FALSE);
+        }
+    }
+  }
+
+    veneer_mode = FALSE;
+}
+/* ------------------------------------------------------------------------- */
+/*   Creating symbols                                                        */
+/* ------------------------------------------------------------------------- */
+
+static void assign_symbol_base(int index, int32 value, int type)
+{   svals[index]  = value;
+    stypes[index] = type;
+    if (sflags[index] & UNKNOWN_SFLAG)
+    {   sflags[index] &= (~UNKNOWN_SFLAG);
+        if (is_systemfile()) sflags[index] |= INSF_SFLAG;
+        slines[index] = get_brief_location(&ErrorReport);
+    }
+}
+
+extern void assign_symbol(int index, int32 value, int type)
+{
+    if (!glulx_mode) {
+        assign_symbol_base(index, value, type);
+    }
+    else {
+        smarks[index] = 0;
+        assign_symbol_base(index, value, type);
+    }
+}
+
+extern void assign_marked_symbol(int index, int marker, int32 value, int type)
+{
+    if (!glulx_mode) {
+        assign_symbol_base(index, (int32)marker*0x10000 + (value % 0x10000),
+            type);
+    }
+    else {
+        smarks[index] = marker;
+        assign_symbol_base(index, value, type);
+    }
+}
+
+static void emit_debug_information_for_predefined_symbol
+    (char *name, int32 symbol, int32 value, int type)
+{   if (debugfile_switch)
+    {   switch (type)
+        {   case CONSTANT_T:
+                debug_file_printf("<constant>");
+                debug_file_printf("<identifier>%s</identifier>", name);
+                write_debug_symbol_optional_backpatch(symbol);
+                debug_file_printf("</constant>");
+                break;
+            case GLOBAL_VARIABLE_T:
+                debug_file_printf("<global-variable>");
+                debug_file_printf("<identifier>%s</identifier>", name);
+                debug_file_printf("<address>");
+                write_debug_global_backpatch(value);
+                debug_file_printf("</address>");
+                debug_file_printf("</global-variable>");
+                break;
+            case OBJECT_T:
+                if (value)
+                {   compiler_error("Non-nothing object predefined");
+                }
+                debug_file_printf("<object>");
+                debug_file_printf("<identifier>%s</identifier>", name);
+                debug_file_printf("<value>0</value>");
+                debug_file_printf("</object>");
+                break;
+            case ATTRIBUTE_T:
+                debug_file_printf("<attribute>");
+                debug_file_printf("<identifier>%s</identifier>", name);
+                debug_file_printf("<value>%d</value>", value);
+                debug_file_printf("</attribute>");
+                break;
+            case PROPERTY_T:
+            case INDIVIDUAL_PROPERTY_T:
+                debug_file_printf("<property>");
+                debug_file_printf("<identifier>%s</identifier>", name);
+                debug_file_printf("<value>%d</value>", value);
+                debug_file_printf("</property>");
+                break;
+            default:
+                compiler_error
+                    ("Unable to emit debug information for predefined symbol");
+            break;
+        }
+    }
+}
+
+static void create_symbol(char *p, int32 value, int type)
+{   int i = symbol_index(p, -1);
+    svals[i] = value; stypes[i] = type; slines[i] = blank_brief_location;
+    sflags[i] = USED_SFLAG + SYSTEM_SFLAG;
+    emit_debug_information_for_predefined_symbol(p, i, value, type);
+}
+
+static void create_rsymbol(char *p, int value, int type)
+{   int i = symbol_index(p, -1);
+    svals[i] = value; stypes[i] = type; slines[i] = blank_brief_location;
+    sflags[i] = USED_SFLAG + SYSTEM_SFLAG + REDEFINABLE_SFLAG;
+    emit_debug_information_for_predefined_symbol(p, i, value, type);
+}
+
+static void stockup_symbols(void)
+{
+    if (!glulx_mode)
+        create_symbol("TARGET_ZCODE", 0, CONSTANT_T);
+    else 
+        create_symbol("TARGET_GLULX", 0, CONSTANT_T);
+
+    create_symbol("nothing",        0, OBJECT_T);
+    create_symbol("name",           1, PROPERTY_T);
+
+    create_symbol("true",           1, CONSTANT_T);
+    create_symbol("false",          0, CONSTANT_T);
+
+    /* Glulx defaults to GV2; Z-code to GV1 */
+    if (!glulx_mode)
+        create_rsymbol("Grammar__Version", 1, CONSTANT_T);
+    else
+        create_rsymbol("Grammar__Version", 2, CONSTANT_T);
+    grammar_version_symbol = symbol_index("Grammar__Version", -1);
+
+    if (module_switch)
+        create_rsymbol("MODULE_MODE",0, CONSTANT_T);
+
+    if (runtime_error_checking_switch)
+        create_rsymbol("STRICT_MODE",0, CONSTANT_T);
+
+    if (define_DEBUG_switch)
+        create_rsymbol("DEBUG",      0, CONSTANT_T);
+
+    if (define_USE_MODULES_switch)
+        create_rsymbol("USE_MODULES",0, CONSTANT_T);
+
+    if (define_INFIX_switch)
+    {   create_rsymbol("INFIX",      0, CONSTANT_T);
+        create_symbol("infix__watching", 0, ATTRIBUTE_T);
+    }
+
+    create_symbol("WORDSIZE",        WORDSIZE, CONSTANT_T);
+    create_symbol("DICT_ENTRY_BYTES", DICT_ENTRY_BYTE_LENGTH, CONSTANT_T);
+    if (!glulx_mode) {
+        create_symbol("DICT_WORD_SIZE", ((version_number==3)?4:6), CONSTANT_T);
+        create_symbol("NUM_ATTR_BYTES", ((version_number==3)?4:6), CONSTANT_T);
+    }
+    else {
+        create_symbol("DICT_WORD_SIZE",     DICT_WORD_SIZE, CONSTANT_T);
+        create_symbol("DICT_CHAR_SIZE",     DICT_CHAR_SIZE, CONSTANT_T);
+        if (DICT_CHAR_SIZE != 1)
+            create_symbol("DICT_IS_UNICODE", 1, CONSTANT_T);
+        create_symbol("NUM_ATTR_BYTES",     NUM_ATTR_BYTES, CONSTANT_T);
+        create_symbol("GOBJFIELD_CHAIN",    GOBJFIELD_CHAIN(), CONSTANT_T);
+        create_symbol("GOBJFIELD_NAME",     GOBJFIELD_NAME(), CONSTANT_T);
+        create_symbol("GOBJFIELD_PROPTAB",  GOBJFIELD_PROPTAB(), CONSTANT_T);
+        create_symbol("GOBJFIELD_PARENT",   GOBJFIELD_PARENT(), CONSTANT_T);
+        create_symbol("GOBJFIELD_SIBLING",  GOBJFIELD_SIBLING(), CONSTANT_T);
+        create_symbol("GOBJFIELD_CHILD",    GOBJFIELD_CHILD(), CONSTANT_T);
+        create_symbol("GOBJ_EXT_START",     1+NUM_ATTR_BYTES+6*WORDSIZE, CONSTANT_T);
+        create_symbol("GOBJ_TOTAL_LENGTH",  1+NUM_ATTR_BYTES+6*WORDSIZE+GLULX_OBJECT_EXT_BYTES, CONSTANT_T);
+        create_symbol("INDIV_PROP_START",   INDIV_PROP_START, CONSTANT_T);
+    }    
+
+    if (!glulx_mode) {
+        create_symbol("temp_global",  255, GLOBAL_VARIABLE_T);
+        create_symbol("temp__global2", 254, GLOBAL_VARIABLE_T);
+        create_symbol("temp__global3", 253, GLOBAL_VARIABLE_T);
+        create_symbol("temp__global4", 252, GLOBAL_VARIABLE_T);
+        create_symbol("self",         251, GLOBAL_VARIABLE_T);
+        create_symbol("sender",       250, GLOBAL_VARIABLE_T);
+        create_symbol("sw__var",      249, GLOBAL_VARIABLE_T);
+        
+        create_symbol("sys__glob0",     16, GLOBAL_VARIABLE_T);
+        create_symbol("sys__glob1",     17, GLOBAL_VARIABLE_T);
+        create_symbol("sys__glob2",     18, GLOBAL_VARIABLE_T);
+        
+        create_symbol("create",        64, INDIVIDUAL_PROPERTY_T);
+        create_symbol("recreate",      65, INDIVIDUAL_PROPERTY_T);
+        create_symbol("destroy",       66, INDIVIDUAL_PROPERTY_T);
+        create_symbol("remaining",     67, INDIVIDUAL_PROPERTY_T);
+        create_symbol("copy",          68, INDIVIDUAL_PROPERTY_T);
+        create_symbol("call",          69, INDIVIDUAL_PROPERTY_T);
+        create_symbol("print",         70, INDIVIDUAL_PROPERTY_T);
+        create_symbol("print_to_array",71, INDIVIDUAL_PROPERTY_T);
+    }
+    else {
+        /* In Glulx, these system globals are entered in order, not down 
+           from 255. */
+        create_symbol("temp_global",  MAX_LOCAL_VARIABLES+0, 
+          GLOBAL_VARIABLE_T);
+        create_symbol("temp__global2", MAX_LOCAL_VARIABLES+1, 
+          GLOBAL_VARIABLE_T);
+        create_symbol("temp__global3", MAX_LOCAL_VARIABLES+2, 
+          GLOBAL_VARIABLE_T);
+        create_symbol("temp__global4", MAX_LOCAL_VARIABLES+3, 
+          GLOBAL_VARIABLE_T);
+        create_symbol("self",         MAX_LOCAL_VARIABLES+4, 
+          GLOBAL_VARIABLE_T);
+        create_symbol("sender",       MAX_LOCAL_VARIABLES+5, 
+          GLOBAL_VARIABLE_T);
+        create_symbol("sw__var",      MAX_LOCAL_VARIABLES+6, 
+          GLOBAL_VARIABLE_T);
+
+        /* These are almost certainly meaningless, and can be removed. */
+        create_symbol("sys__glob0",     MAX_LOCAL_VARIABLES+7, 
+          GLOBAL_VARIABLE_T);
+        create_symbol("sys__glob1",     MAX_LOCAL_VARIABLES+8, 
+          GLOBAL_VARIABLE_T);
+        create_symbol("sys__glob2",     MAX_LOCAL_VARIABLES+9, 
+          GLOBAL_VARIABLE_T);
+
+        /* value of statusline_flag to be written later */
+        create_symbol("sys_statusline_flag",  MAX_LOCAL_VARIABLES+10, 
+          GLOBAL_VARIABLE_T);
+
+        /* These are created in order, but not necessarily at a fixed
+           value. */
+        create_symbol("create",        INDIV_PROP_START+0, 
+          INDIVIDUAL_PROPERTY_T);
+        create_symbol("recreate",      INDIV_PROP_START+1, 
+          INDIVIDUAL_PROPERTY_T);
+        create_symbol("destroy",       INDIV_PROP_START+2, 
+          INDIVIDUAL_PROPERTY_T);
+        create_symbol("remaining",     INDIV_PROP_START+3, 
+          INDIVIDUAL_PROPERTY_T);
+        create_symbol("copy",          INDIV_PROP_START+4, 
+          INDIVIDUAL_PROPERTY_T);
+        create_symbol("call",          INDIV_PROP_START+5, 
+          INDIVIDUAL_PROPERTY_T);
+        create_symbol("print",         INDIV_PROP_START+6, 
+          INDIVIDUAL_PROPERTY_T);
+        create_symbol("print_to_array",INDIV_PROP_START+7, 
+          INDIVIDUAL_PROPERTY_T);
+
+        /* Floating-point constants. Note that FLOAT_NINFINITY is not
+           -FLOAT_INFINITY, because float negation doesn't work that
+           way. Also note that FLOAT_NAN is just one of many possible
+           "not-a-number" values. */
+        create_symbol("FLOAT_INFINITY",  0x7F800000, CONSTANT_T);
+        create_symbol("FLOAT_NINFINITY", 0xFF800000, CONSTANT_T);
+        create_symbol("FLOAT_NAN",       0x7FC00000, CONSTANT_T);
+    }
+}
+
+/* ------------------------------------------------------------------------- */
+/*   The symbol replacement table. This is needed only for the               */
+/*   "Replace X Y" directive.                                                */
+/* ------------------------------------------------------------------------- */
+
+extern void add_symbol_replacement_mapping(int original, int renamed)
+{
+    int ix;
+
+    if (original == renamed) {
+        error_named("A routine cannot be 'Replace'd to itself:", (char *)symbs[original]);
+        return;        
+    }
+
+    if (symbol_replacements_count == symbol_replacements_size) {
+        int oldsize = symbol_replacements_size;
+        if (symbol_replacements_size == 0) 
+            symbol_replacements_size = 4;
+        else
+            symbol_replacements_size *= 2;
+        my_recalloc(&symbol_replacements, sizeof(value_pair_t), oldsize,
+            symbol_replacements_size, "symbol replacement table");
+    }
+
+    /* If the original form is already in our table, report an error.
+       Same goes if the replaced form is already in the table as an
+       original. (Other collision cases have already been
+       detected.) */
+
+    for (ix=0; ix<symbol_replacements_count; ix++) {
+        if (original == symbol_replacements[ix].original_symbol) {
+            error_named("A routine cannot be 'Replace'd to more than one new name:", (char *)symbs[original]);
+        }
+        if (renamed == symbol_replacements[ix].original_symbol) {
+            error_named("A routine cannot be 'Replace'd to a 'Replace'd name:", (char *)symbs[original]);
+        }
+    }
+
+    symbol_replacements[symbol_replacements_count].original_symbol = original;
+    symbol_replacements[symbol_replacements_count].renamed_symbol = renamed;
+    symbol_replacements_count++;
+}
+
+extern int find_symbol_replacement(int *value)
+{
+    int changed = FALSE;
+    int ix;
+
+    if (!symbol_replacements)
+        return FALSE;
+
+    for (ix=0; ix<symbol_replacements_count; ix++) {
+        if (*value == symbol_replacements[ix].original_symbol) {
+            *value = symbol_replacements[ix].renamed_symbol;
+            changed = TRUE;
+        }
+    }
+
+    return changed;
+}
+
+/* ------------------------------------------------------------------------- */
+/*   The dead-function removal optimization.                                 */
+/* ------------------------------------------------------------------------- */
+
+int track_unused_routines; /* set if either WARN_UNUSED_ROUTINES or
+                              OMIT_UNUSED_ROUTINES is nonzero */
+int df_dont_note_global_symbols; /* temporarily set at times in parsing */
+static int df_tables_closed; /* set at end of compiler pass */
+
+typedef struct df_function_struct df_function_t;
+typedef struct df_reference_struct df_reference_t;
+
+struct df_function_struct {
+    char *name; /* borrowed reference, generally to the symbs[] table */
+    brief_location source_line; /* copied from routine_starts_line */
+    int sysfile; /* does this occur in a system file? */
+    uint32 address; /* function offset in zcode_area (not the final address) */
+    uint32 newaddress; /* function offset after stripping */
+    uint32 length;
+    int usage;
+    df_reference_t *refs; /* chain of references made *from* this function */
+    int processed;
+
+    df_function_t *funcnext; /* in forward functions order */
+    df_function_t *todonext; /* in the todo chain */
+    df_function_t *next; /* in the hash table */
+};
+
+struct df_reference_struct {
+    uint32 address; /* function offset in zcode_area (not the final address) */
+    int symbol; /* index in symbols array */
+
+    df_reference_t *refsnext; /* in the function's refs chain */
+    df_reference_t *next; /* in the hash table */
+};
+
+/* Bitmask flags for how functions are used: */
+#define DF_USAGE_GLOBAL   (1<<0) /* In a global variable, array, etc */
+#define DF_USAGE_EMBEDDED (1<<1) /* An anonymous function in a property */
+#define DF_USAGE_MAIN     (1<<2) /* Main() or Main__() */
+#define DF_USAGE_FUNCTION (1<<3) /* Used from another used function */
+
+#define DF_FUNCTION_HASH_BUCKETS (1023)
+
+/* Table of all compiled functions. (Only created if track_unused_routines
+   is set.) This is a hash table. */
+static df_function_t **df_functions;
+/* List of all compiled functions, in address order. The first entry
+   has address DF_NOT_IN_FUNCTION, and stands in for the global namespace. */
+static df_function_t *df_functions_head;
+static df_function_t *df_functions_tail;
+/* Used during output_file(), to track how far the code-area output has
+   gotten. */
+static df_function_t *df_iterator;
+
+/* Array of all compiled functions in address order. (Does not include
+   the global namespace entry.) This is generated only if needed. */
+static df_function_t **df_functions_sorted;
+static int df_functions_sorted_count;
+
+#define DF_NOT_IN_FUNCTION ((uint32)0xFFFFFFFF)
+#define DF_SYMBOL_HASH_BUCKETS (4095)
+
+/* Map of what functions reference what other functions. (Only created if
+   track_unused_routines is set.) */
+static df_reference_t **df_symbol_map;
+
+/* Globals used while a function is being compiled. When a function
+  *isn't* being compiled, df_current_function_addr will be DF_NOT_IN_FUNCTION
+  and df_current_function will refer to the global namespace record. */
+static df_function_t *df_current_function;
+static char *df_current_function_name;
+static uint32 df_current_function_addr;
+
+/* Size totals for compiled code. These are only meaningful if
+   track_unused_routines is true. (If we're only doing WARN_UNUSED_ROUTINES,
+   these values will be set, but the "after" value will not affect the
+   final game file.) */
+uint32 df_total_size_before_stripping;
+uint32 df_total_size_after_stripping;
+
+/* When we begin compiling a function, call this to note that fact.
+   Any symbol referenced from now on will be associated with the function.
+*/
+extern void df_note_function_start(char *name, uint32 address, 
+    int embedded_flag, brief_location source_line)
+{
+    df_function_t *func;
+    int bucket;
+
+    if (df_tables_closed)
+        error("Internal error in stripping: Tried to start a new function after tables were closed.");
+
+    /* We retain the name only for debugging output. Note that embedded
+       functions all show up as "<embedded>" -- their "obj.prop" name
+       never gets stored in permanent memory. */
+    df_current_function_name = name;
+    df_current_function_addr = address;
+
+    func = my_malloc(sizeof(df_function_t), "df function entry");
+    memset(func, 0, sizeof(df_function_t));
+    func->name = name;
+    func->address = address;
+    func->source_line = source_line;
+    func->sysfile = (address == DF_NOT_IN_FUNCTION || is_systemfile());
+    /* An embedded function is stored in an object property, so we
+       consider it to be used a priori. */
+    if (embedded_flag)
+        func->usage |= DF_USAGE_EMBEDDED;
+
+    if (!df_functions_head) {
+        df_functions_head = func;
+        df_functions_tail = func;
+    }
+    else {
+        df_functions_tail->funcnext = func;
+        df_functions_tail = func;
+    }
+
+    bucket = address % DF_FUNCTION_HASH_BUCKETS;
+    func->next = df_functions[bucket];
+    df_functions[bucket] = func;
+
+    df_current_function = func;
+}
+
+/* When we're done compiling a function, call this. Any symbol referenced
+   from now on will be associated with the global namespace.
+*/
+extern void df_note_function_end(uint32 endaddress)
+{
+    df_current_function->length = endaddress - df_current_function->address;
+
+    df_current_function_name = NULL;
+    df_current_function_addr = DF_NOT_IN_FUNCTION;
+    df_current_function = df_functions_head; /* the global namespace */
+}
+
+/* Find the function record for a given address. (Addresses are offsets
+   in zcode_area.)
+*/
+static df_function_t *df_function_for_address(uint32 address)
+{
+    int bucket = address % DF_FUNCTION_HASH_BUCKETS;
+    df_function_t *func;
+    for (func = df_functions[bucket]; func; func = func->next) {
+        if (func->address == address)
+            return func;
+    }
+    return NULL;
+}
+
+/* Whenever a function is referenced, we call this to note who called it.
+*/
+extern void df_note_function_symbol(int symbol)
+{
+    int bucket, symtype;
+    df_reference_t *ent;
+
+    /* If the compiler pass is over, looking up symbols does not create
+       a global reference. */
+    if (df_tables_closed)
+        return;
+    /* In certain cases during parsing, looking up symbols does not
+       create a global reference. (For example, when reading the name
+       of a function being defined.) */
+    if (df_dont_note_global_symbols)
+        return;
+
+    /* We are only interested in functions, or forward-declared symbols
+       that might turn out to be functions. */
+    symtype = stypes[symbol];
+    if (symtype != ROUTINE_T && symtype != CONSTANT_T)
+        return;
+    if (symtype == CONSTANT_T && !(sflags[symbol] & UNKNOWN_SFLAG))
+        return;
+
+    bucket = (df_current_function_addr ^ (uint32)symbol) % DF_SYMBOL_HASH_BUCKETS;
+    for (ent = df_symbol_map[bucket]; ent; ent = ent->next) {
+        if (ent->address == df_current_function_addr && ent->symbol == symbol)
+            return;
+    }
+
+    /* Create a new reference entry in df_symbol_map. */
+    ent = my_malloc(sizeof(df_reference_t), "df symbol map entry");
+    ent->address = df_current_function_addr;
+    ent->symbol = symbol;
+    ent->next = df_symbol_map[bucket];
+    df_symbol_map[bucket] = ent;
+
+    /* Add the reference to the function's entry as well. */
+    /* The current function is the most recently added, so it will be
+       at the top of its bucket. That makes this call fast. Unless
+       we're in global scope, in which case it might be slower.
+       (I suppose we could cache the df_function_t pointer of the
+       current function, to speed things up.) */
+    if (!df_current_function || df_current_function_addr != df_current_function->address)
+        compiler_error("DF: df_current_function does not match current address.");
+    ent->refsnext = df_current_function->refs;
+    df_current_function->refs = ent;
+}
+
+/* This does the hard work of figuring out what functions are truly dead.
+   It's called near the end of run_pass() in inform.c.
+*/
+extern void locate_dead_functions(void)
+{
+    df_function_t *func, *tofunc;
+    df_reference_t *ent;
+    int ix;
+
+    if (!track_unused_routines)
+        compiler_error("DF: locate_dead_functions called, but function references have not been mapped");
+
+    df_tables_closed = TRUE;
+    df_current_function = NULL;
+
+    /* Note that Main__ was tagged as global implicitly during
+       compile_initial_routine(). Main was tagged during
+       issue_unused_warnings(). But for the sake of thoroughness,
+       we'll mark them specially. */
+
+    ix = symbol_index("Main__", -1);
+    if (stypes[ix] == ROUTINE_T) {
+        uint32 addr = svals[ix] * (glulx_mode ? 1 : scale_factor);
+        tofunc = df_function_for_address(addr);
+        if (tofunc)
+            tofunc->usage |= DF_USAGE_MAIN;
+    }
+    ix = symbol_index("Main", -1);
+    if (stypes[ix] == ROUTINE_T) {
+        uint32 addr = svals[ix] * (glulx_mode ? 1 : scale_factor);
+        tofunc = df_function_for_address(addr);
+        if (tofunc)
+            tofunc->usage |= DF_USAGE_MAIN;
+    }
+
+    /* Go through all the functions referenced at the global level;
+       mark them as used. */
+
+    func = df_functions_head;
+    if (!func || func->address != DF_NOT_IN_FUNCTION)
+        compiler_error("DF: Global namespace entry is not at the head of the chain.");
+
+    for (ent = func->refs; ent; ent=ent->refsnext) {
+        uint32 addr;
+        int symbol = ent->symbol;
+        if (stypes[symbol] != ROUTINE_T)
+            continue;
+        addr = svals[symbol] * (glulx_mode ? 1 : scale_factor);
+        tofunc = df_function_for_address(addr);
+        if (!tofunc) {
+            error_named("Internal error in stripping: global ROUTINE_T symbol is not found in df_function map:", (char *)symbs[symbol]);
+            continue;
+        }
+        /* A function may be marked here more than once. That's fine. */
+        tofunc->usage |= DF_USAGE_GLOBAL;
+    }
+
+    /* Perform a breadth-first search through functions, starting with
+       the ones that are known to be used at the top level. */
+    {
+        df_function_t *todo, *todotail;
+        df_function_t *func;
+        todo = NULL;
+        todotail = NULL;
+
+        for (func = df_functions_head; func; func = func->funcnext) {
+            if (func->address == DF_NOT_IN_FUNCTION)
+                continue;
+            if (func->usage == 0)
+                continue;
+            if (!todo) {
+                todo = func;
+                todotail = func;
+            }
+            else {
+                todotail->todonext = func;
+                todotail = func;
+            }
+        }
+        
+        /* todo is a linked list of functions which are known to be
+           used. If a function's usage field is nonzero, it must be
+           either be on the todo list or have come off already (in
+           which case processed will be set). */
+
+        while (todo) {
+            /* Pop the next function. */
+            func = todo;
+            todo = todo->todonext;
+            if (!todo)
+                todotail = NULL;
+
+            if (func->processed)
+                error_named("Internal error in stripping: function has been processed twice:", func->name);
+
+            /* Go through the function's symbol references. Any
+               reference to a routine, push it into the todo list (if
+               it isn't there already). */
+
+            for (ent = func->refs; ent; ent=ent->refsnext) {
+                uint32 addr;
+                int symbol = ent->symbol;
+                if (stypes[symbol] != ROUTINE_T)
+                    continue;
+                addr = svals[symbol] * (glulx_mode ? 1 : scale_factor);
+                tofunc = df_function_for_address(addr);
+                if (!tofunc) {
+                    error_named("Internal error in stripping: function ROUTINE_T symbol is not found in df_function map:", (char *)symbs[symbol]);
+                    continue;
+                }
+                if (tofunc->usage)
+                    continue;
+
+                /* Not yet known to be used. Add it to the todo list. */
+                tofunc->usage |= DF_USAGE_FUNCTION;
+                if (!todo) {
+                    todo = tofunc;
+                    todotail = tofunc;
+                }
+                else {
+                    todotail->todonext = tofunc;
+                    todotail = tofunc;
+                }
+            }
+
+            func->processed = TRUE;
+        }
+    }
+
+    /* Go through all functions; figure out how much space is consumed,
+       with and without useless functions. */
+
+    {
+        df_function_t *func;
+
+        df_total_size_before_stripping = 0;
+        df_total_size_after_stripping = 0;
+
+        for (func = df_functions_head; func; func = func->funcnext) {
+            if (func->address == DF_NOT_IN_FUNCTION)
+                continue;
+
+            if (func->address != df_total_size_before_stripping)
+                compiler_error("DF: Address gap in function list");
+
+            df_total_size_before_stripping += func->length;
+            if (func->usage) {
+                func->newaddress = df_total_size_after_stripping;
+                df_total_size_after_stripping += func->length;
+            }
+
+            if (!glulx_mode && (df_total_size_after_stripping % scale_factor != 0))
+                compiler_error("DF: New function address is not aligned");
+
+            if (WARN_UNUSED_ROUTINES && !func->usage) {
+                if (!func->sysfile || WARN_UNUSED_ROUTINES >= 2)
+                    uncalled_routine_warning("Routine", func->name, func->source_line);
+            }
+        }
+    }
+
+    /* df_measure_hash_table_usage(); */
+}
+
+/* Given an original function address, return where it winds up after
+   unused-function stripping. The function must not itself be unused.
+
+   Both the input and output are offsets, and already scaled by
+   scale_factor.
+
+   This is used by the backpatching system.
+*/
+extern uint32 df_stripped_address_for_address(uint32 addr)
+{
+    df_function_t *func;
+
+    if (!track_unused_routines)
+        compiler_error("DF: df_stripped_address_for_address called, but function references have not been mapped");
+
+    if (!glulx_mode)
+        func = df_function_for_address(addr*scale_factor);
+    else
+        func = df_function_for_address(addr);
+
+    if (!func) {
+        compiler_error("DF: Unable to find function while backpatching");
+        return 0;
+    }
+    if (!func->usage)
+        compiler_error("DF: Tried to backpatch a function address which should be stripped");
+
+    if (!glulx_mode)
+        return func->newaddress / scale_factor;
+    else
+        return func->newaddress;
+}
+
+/* Given an address in the function area, return where it winds up after
+   unused-function stripping. The address can be a function or anywhere
+   within the function. If the address turns out to be in a stripped
+   function, returns 0 (and sets *stripped).
+
+   The input and output are offsets, but *not* scaled.
+
+   This is only used by the debug-file system.
+*/
+uint32 df_stripped_offset_for_code_offset(uint32 offset, int *stripped)
+{
+    df_function_t *func;
+    int count;
+
+    if (!track_unused_routines)
+        compiler_error("DF: df_stripped_offset_for_code_offset called, but function references have not been mapped");
+
+    if (!df_functions_sorted) {
+        /* To do this efficiently, we need a binary-searchable table. Fine,
+           we'll make one. Include both used and unused functions. */
+
+        for (func = df_functions_head, count = 0; func; func = func->funcnext) {
+            if (func->address == DF_NOT_IN_FUNCTION)
+                continue;
+            count++;
+        }
+        df_functions_sorted_count = count;
+
+        df_functions_sorted = my_calloc(sizeof(df_function_t *), df_functions_sorted_count, "df function sorted table");
+
+        for (func = df_functions_head, count = 0; func; func = func->funcnext) {
+            if (func->address == DF_NOT_IN_FUNCTION)
+                continue;
+            df_functions_sorted[count] = func;
+            count++;
+        }
+    }
+
+    /* Do a binary search. Maintain beg <= res < end, where res is the
+       function containing the desired address. */
+    int beg = 0;
+    int end = df_functions_sorted_count;
+
+    /* Set stripped flag until we decide on a non-stripped function. */
+    *stripped = TRUE;
+
+    while (1) {
+        if (beg >= end) {
+            error("DF: offset_for_code_offset: Could not locate address.");
+            return 0;
+        }
+        if (beg+1 == end) {
+            func = df_functions_sorted[beg];
+            if (func->usage == 0)
+                return 0;
+            *stripped = FALSE;
+            return func->newaddress + (offset - func->address);
+        }
+        int new = (beg + end) / 2;
+        if (new <= beg || new >= end)
+            compiler_error("DF: binary search went off the rails");
+
+        func = df_functions_sorted[new];
+        if (offset >= func->address) {
+            if (offset < func->address+func->length) {
+                /* We don't need to loop further; decide here. */
+                if (func->usage == 0)
+                    return 0;
+                *stripped = FALSE;
+                return func->newaddress + (offset - func->address);
+            }
+            beg = new;
+        }
+        else {
+            end = new;
+        }
+    }
+}
+
+/* The output_file() routines in files.c have to run down the list of
+   functions, deciding who is in and who is out. But I don't want to
+   export the df_function_t list structure. Instead, I provide this
+   silly iterator pair. Set it up with df_prepare_function_iterate();
+   then repeatedly call df_next_function_iterate().
+*/
+
+extern void df_prepare_function_iterate(void)
+{
+    df_iterator = df_functions_head;
+    if (!df_iterator || df_iterator->address != DF_NOT_IN_FUNCTION)
+        compiler_error("DF: Global namespace entry is not at the head of the chain.");
+    if (!df_iterator->funcnext || df_iterator->funcnext->address != 0)
+        compiler_error("DF: First function entry is not second in the chain.");
+}
+
+/* This returns the end of the next function, and whether the next function
+   is used (live).
+*/
+extern uint32 df_next_function_iterate(int *funcused)
+{
+    if (df_iterator)
+        df_iterator = df_iterator->funcnext;
+    if (!df_iterator) {
+        *funcused = TRUE;
+        return df_total_size_before_stripping+1;
+    }
+    *funcused = (df_iterator->usage != 0);
+    return df_iterator->address + df_iterator->length;
+}
+
+/* ========================================================================= */
+/*   Data structure management routines                                      */
+/* ------------------------------------------------------------------------- */
+
+extern void init_symbols_vars(void)
+{
+    symbs = NULL;
+    svals = NULL;
+    smarks = NULL;
+    stypes = NULL;
+    sflags = NULL;
+    next_entry = NULL;
+    start_of_list = NULL;
+
+    symbol_name_space_chunks = NULL;
+    no_symbol_name_space_chunks = 0;
+    symbols_free_space=NULL;
+    symbols_ceiling=symbols_free_space;
+
+    no_symbols = 0;
+
+    symbol_replacements = NULL;
+    symbol_replacements_count = 0;
+    symbol_replacements_size = 0;
+
+    make_case_conversion_grid();
+
+    track_unused_routines = (WARN_UNUSED_ROUTINES || OMIT_UNUSED_ROUTINES);
+    df_tables_closed = FALSE;
+    df_symbol_map = NULL;
+    df_functions = NULL;
+    df_functions_head = NULL;
+    df_functions_tail = NULL;
+    df_current_function = NULL;
+    df_functions_sorted = NULL;
+    df_functions_sorted_count = 0;
+}
+
+extern void symbols_begin_pass(void) 
+{
+    df_total_size_before_stripping = 0;
+    df_total_size_after_stripping = 0;
+    df_dont_note_global_symbols = FALSE;
+    df_iterator = NULL;
+}
+
+extern void symbols_allocate_arrays(void)
+{
+    symbs      = my_calloc(sizeof(char *),  MAX_SYMBOLS, "symbols");
+    svals      = my_calloc(sizeof(int32),   MAX_SYMBOLS, "symbol values");
+    if (glulx_mode)
+        smarks = my_calloc(sizeof(int),     MAX_SYMBOLS, "symbol markers");
+    slines     = my_calloc(sizeof(brief_location), MAX_SYMBOLS, "symbol lines");
+    stypes     = my_calloc(sizeof(char),    MAX_SYMBOLS, "symbol types");
+    sflags     = my_calloc(sizeof(int),     MAX_SYMBOLS, "symbol flags");
+    if (debugfile_switch)
+    {   symbol_debug_backpatch_positions =
+            my_calloc(sizeof(maybe_file_position), MAX_SYMBOLS,
+                      "symbol debug information backpatch positions");
+        replacement_debug_backpatch_positions =
+            my_calloc(sizeof(maybe_file_position), MAX_SYMBOLS,
+                      "replacement debug information backpatch positions");
+    }
+    next_entry = my_calloc(sizeof(int),     MAX_SYMBOLS,
+                     "symbol linked-list forward links");
+    start_of_list = my_calloc(sizeof(int32), HASH_TAB_SIZE,
+                     "hash code list beginnings");
+
+    symbol_name_space_chunks
+        = my_calloc(sizeof(char *), MAX_SYMBOL_CHUNKS, "symbol names chunk addresses");
+
+    if (track_unused_routines) {
+        df_tables_closed = FALSE;
+
+        df_symbol_map = my_calloc(sizeof(df_reference_t *), DF_SYMBOL_HASH_BUCKETS, "df symbol-map hash table");
+        memset(df_symbol_map, 0, sizeof(df_reference_t *) * DF_SYMBOL_HASH_BUCKETS);
+
+        df_functions = my_calloc(sizeof(df_function_t *), DF_FUNCTION_HASH_BUCKETS, "df function hash table");
+        memset(df_functions, 0, sizeof(df_function_t *) * DF_FUNCTION_HASH_BUCKETS);
+        df_functions_head = NULL;
+        df_functions_tail = NULL;
+
+        df_functions_sorted = NULL;
+        df_functions_sorted_count = 0;
+
+        df_note_function_start("<global namespace>", DF_NOT_IN_FUNCTION, FALSE, blank_brief_location);
+        df_note_function_end(DF_NOT_IN_FUNCTION);
+        /* Now df_current_function is df_functions_head. */
+    }
+
+    init_symbol_banks();
+    stockup_symbols();
+
+    /*  Allocated as needed  */
+    symbol_replacements = NULL;
+
+    /*  Allocated during story file construction, not now  */
+    individual_name_strings = NULL;
+    attribute_name_strings = NULL;
+    action_name_strings = NULL;
+    array_name_strings = NULL;
+}
+
+extern void symbols_free_arrays(void)
+{   int i;
+
+    for (i=0; i<no_symbol_name_space_chunks; i++)
+        my_free(&(symbol_name_space_chunks[i]),
+            "symbol names chunk");
+
+    my_free(&symbol_name_space_chunks, "symbol names chunk addresses");
+
+    my_free(&symbs, "symbols");
+    my_free(&svals, "symbol values");
+    my_free(&smarks, "symbol markers");
+    my_free(&slines, "symbol lines");
+    my_free(&stypes, "symbol types");
+    my_free(&sflags, "symbol flags");
+    if (debugfile_switch)
+    {   my_free
+            (&symbol_debug_backpatch_positions,
+             "symbol debug information backpatch positions");
+        my_free
+            (&replacement_debug_backpatch_positions,
+             "replacement debug information backpatch positions");
+    }
+    my_free(&next_entry, "symbol linked-list forward links");
+    my_free(&start_of_list, "hash code list beginnings");
+
+    if (symbol_replacements)
+        my_free(&symbol_replacements, "symbol replacement table");
+
+    if (df_symbol_map) {
+        for (i=0; i<DF_SYMBOL_HASH_BUCKETS; i++) {
+            df_reference_t *ent = df_symbol_map[i];
+            while (ent) {
+                df_reference_t *next = ent->next;
+                my_free(&ent, "df symbol map entry");
+                ent = next;
+            }
+        }
+        my_free(&df_symbol_map, "df symbol-map hash table");
+    }
+    if (df_functions_sorted) {
+        my_free(&df_functions, "df function sorted table");
+    }
+    if (df_functions) {
+        for (i=0; i<DF_FUNCTION_HASH_BUCKETS; i++) {
+            df_function_t *func = df_functions[i];
+            while (func) {
+                df_function_t *next = func->next;
+                my_free(&func, "df function entry");
+                func = next;
+            }
+        }
+        my_free(&df_functions, "df function hash table");
+    }
+    df_functions_head = NULL;
+    df_functions_tail = NULL;
+
+    if (individual_name_strings != NULL)
+        my_free(&individual_name_strings, "property name strings");
+    if (action_name_strings != NULL)
+        my_free(&action_name_strings,     "action name strings");
+    if (attribute_name_strings != NULL)
+        my_free(&attribute_name_strings,  "attribute name strings");
+    if (array_name_strings != NULL)
+        my_free(&array_name_strings,      "array name strings");
+}
+
+/* ========================================================================= */