Added Nitfol and Frotz source code.
[rodin/chimara.git] / interpreters / nitfol / infix.c
diff --git a/interpreters/nitfol/infix.c b/interpreters/nitfol/infix.c
new file mode 100644 (file)
index 0000000..47b208d
--- /dev/null
@@ -0,0 +1,1170 @@
+/*  Nitfol - z-machine interpreter using Glk for output.
+    Copyright (C) 1999  Evin Robertson
+
+    This program 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 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+
+    The author can be reached at nitfol@deja.com
+*/
+#include "nitfol.h"
+#ifdef STDOUT_DEBUG
+#include <stdio.h>
+#endif
+
+
+#ifdef DEBUGGING
+
+
+#ifdef HEADER
+
+typedef enum { Z_UNKNOWN, Z_BOOLEAN, Z_NUMBER, Z_OBJECT, Z_ROUTINE, Z_STRING, Z_GLOBAL, Z_LOCAL, Z_BYTEARRAY, Z_WORDARRAY, Z_OBJPROP, Z_ATTR, Z_PROP, Z_ARRAY } z_type;
+
+typedef struct z_typed z_typed;
+struct z_typed {
+  zword v;      /* The value stored */
+  z_type t;
+
+  zword o, p;   /* location of value (if t is appropriate) */
+};
+
+
+typedef struct {
+  const char *filename;
+  strid_t stream;
+  int num_lines;
+  glui32 *line_locations;
+} infix_file;
+
+typedef struct {
+  infix_file *file;
+  int line_num;
+  int line_x;
+  const char *func_name;
+  unsigned func_num;
+  offset thisPC;
+} infix_location;
+
+#endif
+
+
+#define EOF_DBR          0
+#define FILE_DBR         1
+#define CLASS_DBR        2
+#define OBJECT_DBR       3
+#define GLOBAL_DBR       4
+#define ATTR_DBR         5
+#define PROP_DBR         6
+#define FAKE_ACTION_DBR  7
+#define ACTION_DBR       8
+#define HEADER_DBR       9
+#define LINEREF_DBR     10
+#define ROUTINE_DBR     11
+#define ARRAY_DBR       12
+#define MAP_DBR         13
+#define ROUTINE_END_DBR 14
+#define MAX_DBR 14
+
+static unsigned record_info[][9] = {
+  {      0,      0,      0, 0, 0,      0, 0, 0, 0 }, /* eof_dbr */
+  {      1, 0x8000, 0x8000, 0, 0,      0, 0, 0, 0 }, /* file_dbr */
+  { 0x8000,      1,      2, 1, 1,      2, 1, 0, 0 }, /* class_dbr */
+  {      2, 0x8000,      1, 2, 1,      1, 2, 1, 0 }, /* object_dbr */
+  {      1, 0x8000,      0, 0, 0,      0, 0, 0, 0 }, /* global_dbr */
+  {      2, 0x8000,      0, 0, 0,      0, 0, 0, 0 }, /* attr_dbr */
+  {      2, 0x8000,      0, 0, 0,      0, 0, 0, 0 }, /* prop_dbr */
+  {      2, 0x8000,      0, 0, 0,      0, 0, 0, 0 }, /* fake_action_dbr */
+  {      2, 0x8000,      0, 0, 0,      0, 0, 0, 0 }, /* action_dbr */
+  {     64,      0,      0, 0, 0,      0, 0, 0, 0 }, /* header_dbr */
+  {      2,      2,      0, 0, 0,      0, 0, 0, 0 }, /* lineref_dbr */
+  {      2,      1,      2, 1, 3, 0x8000, 0, 0, 0 }, /* routine_dbr */
+  {      2, 0x8000,      0, 0, 0,      0, 0, 0, 0 }, /* array_dbr */
+  {      0,      0,      0, 0, 0,      0, 0, 0, 0 }, /* map_dbr */
+  {      2,      1,      2, 1, 3,      0, 0, 0, 0 }  /* routine_end_dbr */
+};
+
+
+offset infix_get_routine_PC(zword routine)
+{
+  offset destPC = UNPACKR(routine);
+  unsigned num_local = HIBYTE(destPC);
+  destPC++;
+  if(zversion < 5)
+    destPC += num_local * ZWORD_SIZE;
+  return destPC;
+}
+
+
+static infix_file infix_load_file_info(const char *filename)
+{
+  infix_file r;
+  glsi32 c;
+  int lines_allocated = 0;
+                                         
+  r.filename = filename;
+  r.num_lines = 0;
+  r.line_locations = NULL;
+
+  r.stream = n_file_name(fileusage_Data | fileusage_TextMode,
+                        filemode_Read, filename);
+
+  if(!r.stream)
+    return r;
+
+  lines_allocated = 256;
+  r.line_locations = (glui32 *) n_malloc(lines_allocated * sizeof(*r.line_locations));
+  r.line_locations[r.num_lines] = 0;
+  r.num_lines++;
+
+  while((c = glk_get_char_stream(r.stream)) != GLK_EOF) {
+    if(c == 10) {
+      if(r.num_lines >= lines_allocated) {
+       glui32 *new_locations;
+
+       lines_allocated *= 2;
+       new_locations = (glui32 *) n_realloc(r.line_locations,
+                                 lines_allocated * sizeof(*r.line_locations));
+       r.line_locations = new_locations;
+      }
+      r.line_locations[r.num_lines] = glk_stream_get_position(r.stream);
+      r.num_lines++;
+    }
+  }
+  return r;
+}
+
+
+static void infix_unload_file_info(infix_file *f)
+{
+  if(f->line_locations)
+    n_free(f->line_locations);
+  f->line_locations = 0;
+  return;
+}
+
+
+void infix_file_print_line(infix_file *f, int line)
+{
+  glsi32 c;
+
+  if(!(f && f->stream && f->num_lines && f->line_locations))
+    return;
+
+  if(line <= 0)
+    line = 1;
+  if(line > f->num_lines)
+    line = f->num_lines;
+
+  if(fullname) {  /* Running as a subprocess under emacs */
+    infix_print_char(26);
+    infix_print_char(26);
+    infix_print_string(f->filename);
+    infix_print_char(':');
+    infix_print_number(line);
+    infix_print_char(':');
+    infix_print_number(f->line_locations[line-1] + 0);
+    infix_print_string(":beg:0x00000000");
+  } else {
+    infix_print_number(line);
+    infix_print_char('\t');
+    
+    glk_stream_set_position(f->stream, f->line_locations[line-1], seekmode_Start);
+
+    while((c = glk_get_char_stream(f->stream)) != GLK_EOF) {
+      if(c == 10)
+       break;
+      infix_print_char(c);
+    }
+  }
+
+  infix_print_char(10);
+}
+
+
+static unsigned local_names_info[] = { 0x8000, 0 };
+static unsigned sequence_point_info[] ={ 1, 2, 1, 2, 0 };
+static unsigned map_info[] = { 0x8000, 3, 0 };
+
+static glui32 infix_strlen;
+static char *infix_stringdata;
+
+static glui32 infix_add_string(strid_t infix)
+{
+  glui32 ch;
+  glui32 return_offset = infix_strlen;
+
+  while((ch = glk_get_char_stream(infix)) != 0) {
+    if(infix_stringdata)
+      infix_stringdata[infix_strlen] = ch;
+    infix_strlen++;
+  }
+
+  if(infix_stringdata)
+    infix_stringdata[infix_strlen] = 0;
+  infix_strlen++;
+
+  return return_offset;
+}
+
+static void infix_add_stringchar(int c)
+{
+  /* Really inefficient to call realloc for every character, but oh well... */
+  infix_stringdata = n_realloc(infix_stringdata, infix_strlen+1);
+  infix_stringdata[infix_strlen++] = c;
+}
+
+static glui32 infix_add_zstring(zword paddr)
+{
+  if(paddr) {
+    glui32 return_offset = infix_strlen;
+    offset addr = UNPACKS(paddr);
+    if(addr+2 < total_size) {
+      decodezscii(addr, infix_add_stringchar);
+      infix_add_stringchar(0);
+      return return_offset;
+    }
+  }
+  return 0xffffffffL;
+}
+
+
+static infix_file *infix_files;
+static unsigned infix_filescount;
+
+static char **infix_objects;
+static unsigned infix_objectscount;
+
+static char **infix_globals;
+static unsigned infix_globalscount;
+
+typedef struct {
+  zword address;
+  const char *name;
+} infix_arrayref;
+
+static infix_arrayref *infix_arrays;
+static unsigned infix_arrayscount;
+
+static char **infix_attrs;
+static unsigned infix_attrscount;
+
+static char **infix_props;
+static unsigned infix_propscount;
+
+typedef struct {
+  const char *name;
+  unsigned filenum;
+  unsigned startline;
+  unsigned start_x;
+  offset start_PC;
+  const char *localnames[15];
+  unsigned end_line;
+  unsigned end_x;
+  offset end_PC;
+} routineref;
+
+static routineref *infix_routines;
+static unsigned infix_routinescount;
+
+typedef struct {
+  unsigned routine;
+  unsigned filenum;
+  unsigned line;
+  unsigned x;
+  offset PC;
+} infix_sequence;
+
+static infix_sequence *infix_linerefs;
+static unsigned infix_linerefscount;
+
+
+static offset code_start = 0;
+static offset string_start = 0;
+
+static int infix_compare_linerefs(const void *a, const void *b)
+{
+  const infix_sequence *A = (const infix_sequence *) a;
+  const infix_sequence *B = (const infix_sequence *) b;
+  if(A->PC < B->PC)
+    return -1;
+  if(A->PC > B->PC)
+    return 1;
+
+  if(A->line < B->line)
+    return -1;
+  if(A->line > B->line)
+    return 1;
+  return 0;
+}
+
+static BOOL infix_load_records(strid_t infix)
+{
+  unsigned n;
+
+  for(;;) {
+    glui32 record_type = glk_get_char_stream(infix);
+    glui32 record_data[64];
+    glui32 more_data[4];
+    if(record_type > MAX_DBR) {
+      glk_stream_close(infix, NULL);
+      n_show_error(E_DEBUG, "unknown record type", record_type);
+      return FALSE;
+    }
+
+    fillstruct(infix, record_info[record_type], record_data, infix_add_string);
+    
+    switch(record_type) {
+    case EOF_DBR:
+      if(infix_linerefs && infix_routines && code_start) {
+       for(n = 0; n < infix_routinescount; n++) {
+         infix_routines[n].start_PC += code_start;
+         infix_routines[n].end_PC += code_start;
+       }
+
+       n_qsort(infix_linerefs, infix_linerefscount, sizeof(*infix_linerefs),
+               infix_compare_linerefs);
+
+       for(n = 0; n < infix_linerefscount; n++)
+         infix_linerefs[n].PC += code_start;
+      }
+      return TRUE;
+    case FILE_DBR:
+      if(infix_files) {
+       infix_files[record_data[0]] = infix_load_file_info(infix_stringdata + record_data[2]);
+      }
+      if(record_data[0] >= infix_filescount)
+       infix_filescount = record_data[0] + 1;
+      break;
+    case CLASS_DBR:
+      break;
+    case OBJECT_DBR:
+      if(infix_objects) {
+       infix_objects[record_data[0]] = infix_stringdata + record_data[1];
+      }
+      if(record_data[0] >= infix_objectscount)
+       infix_objectscount = record_data[0] + 1;
+      break;
+    case GLOBAL_DBR:
+      if(infix_globals) {
+       infix_globals[record_data[0]] = infix_stringdata + record_data[1];
+      }
+      if(record_data[0] >= infix_globalscount)
+       infix_globalscount = record_data[0] + 1;
+      break;
+    case ARRAY_DBR:
+      if(infix_arrays) {
+      }
+      infix_arrayscount++;
+      break;
+    case ATTR_DBR:
+      if(infix_attrs) {
+       infix_attrs[record_data[0]] = infix_stringdata + record_data[1];
+      }
+      if(record_data[0] >= infix_attrscount)
+       infix_attrscount = record_data[0] + 1;
+      break;
+    case PROP_DBR:
+      if(infix_props) {
+       infix_props[record_data[0]] = infix_stringdata + record_data[1];
+      }
+      if(record_data[0] >= infix_propscount)
+       infix_propscount = record_data[0] + 1;
+      break;
+    case FAKE_ACTION_DBR:
+      break;
+    case ACTION_DBR:
+      break;
+    case HEADER_DBR:
+      glk_stream_set_position(current_zfile, 0, seekmode_Start);
+      for(n = 0; n < 64; n++) {
+       unsigned c = (unsigned char) glk_get_char_stream(current_zfile);
+       if(record_data[n] != c)  {
+         n_show_error(E_DEBUG, "infix file does not match current file", n);
+         return FALSE;
+       }
+      }
+      break;
+    case ROUTINE_DBR:
+      if(infix_routines) {
+       infix_routines[record_data[0]].filenum   = record_data[1];
+       infix_routines[record_data[0]].startline = record_data[2];
+       infix_routines[record_data[0]].start_x   = record_data[3];
+       infix_routines[record_data[0]].start_PC  = record_data[4];
+       infix_routines[record_data[0]].name = infix_stringdata +
+                                                               record_data[5];
+      }
+
+      if(infix_routines)
+       for(n = 0; n < 15; n++)
+         infix_routines[record_data[0]].localnames[n] = NULL;
+
+      for(n = 0; n < 16; n++) {
+       if(!glk_get_char_stream(infix))
+         break;
+       if(n == 15)
+         return FALSE;
+       glk_stream_set_position(infix, -1, seekmode_Current);
+       fillstruct(infix, local_names_info, more_data, infix_add_string);
+       if(infix_routines) {
+         infix_routines[record_data[0]].localnames[n] = infix_stringdata +
+                                                                 more_data[0];
+       }
+      }
+      if(record_data[0] >= infix_routinescount)
+       infix_routinescount = record_data[0] + 1;
+      break;
+    case LINEREF_DBR:
+      for(n = 0; n < record_data[1]; n++) {
+       fillstruct(infix, sequence_point_info, more_data, NULL);
+       if(infix_linerefs) {
+         infix_linerefs[infix_linerefscount].routine = record_data[0];
+         infix_linerefs[infix_linerefscount].filenum = more_data[0];
+         infix_linerefs[infix_linerefscount].line    = more_data[1];
+         infix_linerefs[infix_linerefscount].x       = more_data[2];
+         infix_linerefs[infix_linerefscount].PC      = more_data[3] +
+                                      infix_routines[record_data[0]].start_PC;
+       }
+       infix_linerefscount++;
+      }
+      break;
+    case ROUTINE_END_DBR:
+      if(infix_routines) {
+       infix_routines[record_data[0]].end_line = record_data[2];
+       infix_routines[record_data[0]].end_x    = record_data[3];
+       infix_routines[record_data[0]].end_PC   = record_data[4];
+      }
+      if(record_data[0] >= infix_routinescount)
+       infix_routinescount = record_data[0] + 1;
+      break;
+    case MAP_DBR:
+      for(;;) {
+       if(!glk_get_char_stream(infix))
+         break;
+       glk_stream_set_position(infix, -1, seekmode_Current);
+       fillstruct(infix, map_info, more_data, infix_add_string);
+       if(infix_stringdata) {
+         if(n_strcmp(infix_stringdata + more_data[0], "code area") == 0)
+           code_start = more_data[1];
+         if(n_strcmp(infix_stringdata + more_data[0], "strings area") == 0)
+           string_start = more_data[1];
+       }
+      }
+      break;
+    }     
+  }
+}
+
+
+BOOL init_infix(strid_t infix)
+{
+  if(!infix && (infix_props || infix_attrs))
+    return FALSE;
+
+  kill_infix();
+
+  /* Inform 6.10+ has run-time symbols (techman 9.6) */
+  if(!infix && z_memory && prop_table_end
+     && (z_memory[HD_INFORMVER] == '6' && z_memory[HD_INFORMVER + 2] >= '1')) {
+    zword addr;
+    unsigned i;
+    glui32 *prop_names, *attr_names;
+
+    /* Assume no strings before end of property table */
+    string_start = prop_table_end;
+    
+    for(addr = prop_table_end+1; LOWORD(addr); addr+=ZWORD_SIZE)
+      ;
+    addr+=ZWORD_SIZE;
+
+    infix_propscount = LOWORD(addr) - 1; addr+=ZWORD_SIZE;
+    prop_names = (glui32 *) n_calloc(sizeof(*prop_names), infix_propscount+1);
+    for(i = 1; i <= infix_propscount; i++) {
+      prop_names[i] = infix_add_zstring(LOWORD(addr));
+      addr += ZWORD_SIZE;
+    }
+    
+    infix_attrscount = 48;
+    attr_names = (glui32 *) n_calloc(sizeof(*prop_names), infix_propscount);
+    for(i = 0; i <= 47; i++) {
+      attr_names[i] = infix_add_zstring(LOWORD(addr));
+      addr += ZWORD_SIZE;
+    }
+
+    infix_props = (char **) n_calloc(sizeof(*infix_props), infix_propscount+1);
+    infix_attrs = (char **) n_calloc(sizeof(*infix_attrs), infix_attrscount);
+    for(i = 1; i <= infix_propscount; i++) {
+      if(prop_names[i] != 0xffffffffL)
+       infix_props[i] = infix_stringdata + prop_names[i];
+    }
+    for(i = 0; i <= 47; i++) {
+      if(attr_names[i] != 0xffffffffL)
+       infix_attrs[i] = infix_stringdata + attr_names[i];
+    }
+
+    n_free(prop_names);
+    n_free(attr_names);
+    
+    return TRUE;
+  }
+
+  if(!infix)
+    return FALSE;
+  
+  glk_stream_set_position(infix, 0, seekmode_Start);
+  if((glk_get_char_stream(infix) != 0xDE) ||
+     (glk_get_char_stream(infix) != 0xBF) ||
+     (glk_get_char_stream(infix) != 0x00) ||
+     (glk_get_char_stream(infix) != 0x00)) {
+    glk_stream_close(infix, NULL);
+    n_show_error(E_DEBUG, "unknown version or not an infix file", 0);
+    return FALSE;
+  }
+
+  /* ignore inform version number */
+  glk_stream_set_position(infix, 6, seekmode_Start);
+
+  /* Calculate space requirements */
+  infix_load_records(infix);
+
+  /* Malloc required memory */
+  infix_stringdata   = (char *) n_calloc(sizeof(*infix_stringdata),
+                                        infix_strlen);
+  infix_strlen       = 0;
+  infix_files        = (infix_file *) n_calloc(sizeof(*infix_files),
+                                              infix_filescount);
+  infix_filescount   = 0;
+  infix_objects      = (char **) n_calloc(sizeof(*infix_objects),
+                                         infix_objectscount);
+  infix_objectscount = 0;
+  infix_globals      = (char **) n_calloc(sizeof(*infix_globals),
+                                         infix_globalscount);
+  infix_globalscount = 0;
+  infix_attrs        = (char **) n_calloc(sizeof(*infix_attrs),
+                                         infix_attrscount);
+  infix_attrscount   = 0;
+  infix_props        = (char **) n_calloc(sizeof(*infix_props),
+                                         infix_propscount);
+  infix_propscount   = 0;
+  infix_routines     = (routineref *) n_calloc(sizeof(*infix_routines),
+                                              infix_routinescount);
+  infix_routinescount= 0;
+  infix_linerefs     = (infix_sequence *) n_calloc(sizeof(*infix_linerefs),
+                                                  infix_linerefscount);
+  infix_linerefscount= 0;
+
+  glk_stream_set_position(infix, 6, seekmode_Start);
+  infix_load_records(infix);
+  
+  return TRUE;
+}
+
+
+void kill_infix(void)
+{
+  unsigned i;
+
+  n_free(infix_stringdata);
+  infix_stringdata = 0;
+  infix_strlen = 0;
+
+  if(infix_files) {
+    for(i = 0; i < infix_filescount; i++)
+      infix_unload_file_info(&infix_files[i]);
+    n_free(infix_files);
+  }
+  infix_files = 0;
+  infix_filescount = 0;
+
+  n_free(infix_objects);
+  infix_objects = 0;
+  infix_objectscount = 0;
+
+  n_free(infix_globals);
+  infix_globals = 0;
+  infix_globalscount = 0;
+
+  n_free(infix_attrs);
+  infix_attrs = 0;
+  infix_attrscount = 0;
+
+  n_free(infix_props);
+  infix_props = 0;
+  infix_propscount = 0;
+
+  n_free(infix_routines);
+  infix_routines = 0;
+  infix_routinescount = 0;
+
+  n_free(infix_linerefs);
+  infix_linerefs = 0;
+  infix_linerefscount = 0;
+}
+
+
+void infix_print_znumber(zword blah)
+{
+  set_glk_stream_current();
+  g_print_znumber(blah);
+}
+
+void infix_print_offset(zword blah)
+{
+  set_glk_stream_current();
+  g_print_number(blah);
+}
+
+void infix_print_number(zword blah)
+{
+  set_glk_stream_current();
+  g_print_number(blah);
+}
+
+void infix_print_char(int blah)
+{
+  if(blah == 13)
+    blah = 10;
+#ifdef STDOUT_DEBUG
+  fputc(blah, stdout);
+#else
+  set_glk_stream_current();
+  /* We don't do a style-set because of bugs in zarf Glks */
+  glk_put_char((unsigned char) blah);
+#endif
+}
+
+void infix_print_fixed_char(int blah)
+{
+  if(blah == 13)
+    blah = 10;
+#ifdef STDOUT_DEBUG
+  fputc(blah, stdout);
+#else
+  set_glk_stream_current();
+  glk_set_style(style_Preformatted);
+  glk_put_char((unsigned char) blah);
+  glk_set_style(style_Normal);
+#endif
+}
+
+void infix_print_string(const char *blah)
+{
+  while(*blah)
+    infix_print_char(*blah++);
+}
+
+void infix_print_fixed_string(const char *blah)
+{
+  while(*blah)
+    infix_print_fixed_char(*blah++);
+}
+
+void infix_get_string(char *dest, int maxlen)
+{
+#ifdef STDOUT_DEBUG
+  fgets(dest, maxlen, stdin);
+#else
+  unsigned char t;
+  int len;
+  len = z_read(0, dest, maxlen - 1, 0, 0, 0, 0, &t);
+  dest[len] = 0;
+#endif
+}
+
+void infix_get_val(z_typed *val)
+{
+  switch(val->t) {
+  case Z_LOCAL:     val->v = frame_get_var(val->o, val->p + 1); break;
+  case Z_GLOBAL:    val->v = get_var(val->o + 0x10); break;
+  case Z_BYTEARRAY: val->v = LOBYTE(val->o + val->p); break;
+  case Z_WORDARRAY: val->v = LOWORD(val->o + ZWORD_SIZE * val->p); break;
+  case Z_OBJPROP:   val->v = infix_get_prop(val->o, val->p); break;
+  default: ;
+  }
+}
+
+void infix_assign(z_typed *dest, zword val)
+{
+  switch(dest->t) {
+  case Z_LOCAL:     frame_set_var(dest->o, dest->p + 1, val); break;
+  case Z_BYTEARRAY: LOBYTEwrite(dest->o + dest->p, val); break;
+  case Z_WORDARRAY: LOWORDwrite(dest->o + ZWORD_SIZE * dest->p, val); break;
+  case Z_OBJPROP:   infix_put_prop(dest->o, dest->p, val); break;
+  default:          infix_print_string("assigning to non-lvalue\n"); break;
+  }
+  dest->v = val;
+}
+
+void infix_display(z_typed val)
+{
+  unsigned n, i;
+  const char *name;
+
+  switch(val.t) {
+  default:
+    infix_print_znumber(val.v);
+    break;
+
+  case Z_BOOLEAN:
+    if(val.v)
+      infix_print_string("true");
+    else
+      infix_print_string("false");
+    break;
+
+  case Z_UNKNOWN:
+    val.t = Z_NUMBER;
+    infix_display(val);
+
+    name = debug_decode_number(val.v);
+
+    if(name) {
+      infix_print_char(' ');
+      infix_print_char('(');
+      infix_print_string(name);
+      infix_print_char(')');
+    }
+    break;
+
+  case Z_OBJECT:
+    infix_object_display(val.v);
+    break;
+
+  case Z_STRING:
+    infix_print_char('\"');
+    decodezscii(UNPACKS(val.v), infix_print_char);
+    infix_print_char('\"');
+    break;
+
+  case Z_ROUTINE:
+    if(!infix_routines)
+      return;
+
+    for(i = 0; i < infix_routinescount; i++) {
+      if(infix_routines[i].start_PC == UNPACKR(val.v)) {
+
+       infix_print_char('{');
+       for(n = 0; n < 15; n++) {
+         if(infix_routines[i].localnames[n]) {
+           infix_print_string(infix_routines[i].localnames[n]);
+           if(n < 14 && infix_routines[i].localnames[n+1])
+             infix_print_string(", ");
+         }
+       }
+       infix_print_string("} ");
+
+       infix_print_offset(infix_routines[i].start_PC);
+       infix_print_string(" <");
+       infix_print_string(infix_routines[i].name);
+       infix_print_char('>');
+       break;
+      }
+    }
+  }
+  infix_print_char('\n');
+}
+
+
+int infix_find_file(infix_file **dest, const char *name)
+{
+  unsigned n;
+  if(infix_files) {
+    for(n = 0; n < infix_filescount; n++) {
+      if(infix_files[n].filename) {
+       unsigned len = n_strlen(infix_files[n].filename);
+       if(len &&
+          n_strncasecmp(infix_files[n].filename, name, len) == 0 &&
+          (name[len] == ' ' || name[len] == ':' || name[len] == 0)) {
+         *dest = &infix_files[n];
+         return len;
+       }
+      }
+    }
+  }
+  return 0;
+}
+
+
+BOOL infix_find_symbol(z_typed *val, const char *name, int len)
+{
+  unsigned n;
+  if(infix_routines) {
+    infix_location location;
+    if(infix_decode_PC(&location, frame_get_PC(infix_selected_frame))) {
+      for(n = 0; n < 15; n++) {
+       if(n_strmatch(infix_routines[location.func_num].localnames[n], name, len)) {
+         val->t = Z_LOCAL;
+         val->o = infix_selected_frame;
+         val->p = n;
+         infix_get_val(val);
+         return TRUE;
+       }
+      }
+    }
+  }
+  if(infix_objects)
+    for(n = 0; n < infix_objectscount; n++) {
+      if(n_strmatch(infix_objects[n], name, len)) {
+       val->t = Z_OBJECT;
+       val->v = n;
+       return TRUE;
+      }
+    }
+  if(infix_globals) 
+    for(n = 0; n < infix_globalscount; n++) {
+      if(n_strmatch(infix_globals[n], name, len)) {
+       val->t = Z_WORDARRAY;
+       val->o = z_globaltable;
+       val->p = n;
+       infix_get_val(val);
+       return TRUE;
+      }
+  }
+  if(infix_arrays)
+    for(n = 0; n < infix_arrayscount; n++) {
+      if(n_strmatch(infix_arrays[n].name, name, len)) {
+       val->t = Z_NUMBER;
+       val->v = n;
+       return TRUE;
+      }
+    }
+  if(infix_attrs)
+    for(n = 0; n < infix_attrscount; n++) {
+      if(n_strmatch(infix_attrs[n], name, len)) {
+       val->t = Z_NUMBER;
+       val->v = n;
+       return TRUE;
+      }
+    }
+  if(infix_props)
+    for(n = 0; n < infix_propscount; n++) {
+      if(n_strmatch(infix_props[n], name, len)) {
+       val->t = Z_NUMBER;
+       val->v = n;
+       return TRUE;
+      }
+    }
+  if(infix_routines)
+    for(n = 0; n < infix_routinescount; n++) {
+      if(n_strmatch(infix_routines[n].name, name, len)) {
+       val->t = Z_ROUTINE;
+       val->v = PACKR(infix_routines[n].start_PC);
+       return TRUE;
+      }
+    }
+  return FALSE;
+}
+
+static char infix_temp_string_buffer[45];
+static unsigned infix_temp_strlen;
+
+static void infix_temp_string_build(int ch)
+{
+  infix_temp_string_buffer[infix_temp_strlen] = ch;
+  infix_temp_strlen++;
+  if(infix_temp_strlen > 40) {
+    infix_temp_strlen = 43;
+    infix_temp_string_buffer[40] = '.';
+    infix_temp_string_buffer[41] = '.';
+    infix_temp_string_buffer[42] = '.';
+  }
+}
+
+
+const char *infix_get_name(z_typed val)
+{
+  unsigned i;
+  if(!infix_stringdata)
+    return NULL;
+  switch(val.t) {
+  case Z_GLOBAL:
+    if(val.o < infix_globalscount)
+      return infix_globals[val.o];
+    break;
+  case Z_OBJECT:
+    if(val.v < infix_objectscount)
+      return infix_objects[val.v];
+    break;
+  case Z_ATTR:
+    if(val.v < infix_attrscount)
+      return infix_attrs[val.v];
+    break;
+  case Z_PROP:
+    if(val.v < infix_propscount)
+      return infix_props[val.v];
+    break;
+  case Z_ROUTINE:
+    for(i = 0; i < infix_routinescount; i++) {
+      if(infix_routines[i].start_PC == UNPACKR(val.v))
+       return infix_routines[i].name;
+    }
+    break;
+  case Z_STRING:
+    if(UNPACKS(val.v) < string_start)
+      break;
+    if(string_start < code_start && UNPACKS(val.v) >= code_start)
+      break;
+    if(UNPACKS(val.v) >= total_size)
+      break;
+
+    /* Assume every string except the first is preceded by zeroed padding until
+       an end-of-string marker */
+    if(UNPACKS(val.v) != string_start) {
+      offset addr = UNPACKS(val.v) - 2;
+      zword s;
+      while((s = HIWORD(addr)) == 0)
+       addr -= 2;
+      if(!(s & 0x8000))
+       break;
+    }
+
+    infix_temp_string_buffer[0] = '\"';
+    infix_temp_strlen = 1;
+    testing_string = TRUE; string_bad = FALSE;
+    decodezscii(UNPACKS(val.v), infix_temp_string_build);
+    testing_string = FALSE;
+    if(string_bad)
+      break;
+    infix_temp_string_buffer[infix_temp_strlen] = '\"';
+    infix_temp_string_buffer[infix_temp_strlen + 1] = 0;
+    return infix_temp_string_buffer;
+  case Z_ARRAY:
+    if(val.v < infix_arrayscount)
+      return infix_arrays[val.v].name;
+    break;
+
+  default: ;
+  }
+  return NULL;
+}
+
+
+/* We search through linerefs very often so use binary search for speed */
+
+static infix_sequence *infix_search_linerefs(offset thisPC)
+{
+  unsigned n;
+  int top = 0;
+  int bottom = infix_linerefscount-1;
+  int middle = (top + bottom) / 2;
+
+  if(!infix_linerefs)
+    return NULL;
+
+  do {
+    middle = (top + bottom) / 2;
+    if(thisPC < infix_linerefs[middle].PC)
+      bottom = middle - 1;
+    else if(thisPC > infix_linerefs[middle].PC)
+      top = middle + 1;
+    else
+      break;
+  } while(top <= bottom);
+
+  /* If the PC is in the middle of a line, we want to display that line. In
+     this case, PC will be greater than infix_linerefs[middle].PC, so just let
+     it go. Otherwise, we want to look at the previous lineref, so subtract
+     one (or more)
+  */
+
+  while(middle && thisPC < infix_linerefs[middle].PC)
+    middle--;
+
+
+  /* Make sure PC is inside the function the lineref says it is; if so, then
+     we're done */
+
+  n = infix_linerefs[middle].routine;
+  if(thisPC >= infix_routines[n].start_PC &&
+     thisPC <= infix_routines[n].end_PC)
+    return &infix_linerefs[middle];
+
+  return NULL;
+}
+
+
+BOOL infix_decode_PC(infix_location *dest, offset thisPC)
+{
+  infix_sequence *n = infix_search_linerefs(thisPC);
+
+  if(!n) {     /* No sequence point found - return a function */
+    unsigned i;
+
+    if(!infix_routines)
+      return FALSE;
+
+    for(i = 0; i < infix_routinescount; i++) {
+      if(thisPC >= infix_routines[i].start_PC &&
+        thisPC <= infix_routines[i].end_PC) {
+       
+       routineref *r = &infix_routines[i];
+       dest->file = &infix_files[r->filenum];
+       dest->line_num = r->startline;
+       dest->line_x = r->start_x;
+       dest->func_name = r->name;
+       dest->func_num = i;
+       dest->thisPC = r->start_PC;
+
+       return TRUE;
+      }
+    }
+
+    /* Not in a function. Give up. */
+    return FALSE;
+  }
+
+
+  dest->file      = &infix_files[n->filenum];
+  dest->line_num  = n->line;
+  dest->line_x    = n->x;
+  dest->func_name = infix_routines[n->routine].name;
+  dest->func_num  = n->routine;
+  dest->thisPC    = n->PC;
+
+  return TRUE;
+}
+
+
+BOOL infix_decode_fileloc(infix_location *dest, const char *filename,
+                         unsigned line_num)
+{
+  unsigned n;
+  if(!infix_linerefs)
+    return FALSE;
+  for(n = 0; n < infix_linerefscount; n++) {
+    if(infix_linerefs[n].line == line_num &&
+       n_strcmp(infix_files[infix_linerefs[n].filenum].filename, filename) == 0) {
+
+      dest->file      = &infix_files[infix_linerefs[n].filenum];
+      dest->line_num  = infix_linerefs[n].line;
+      dest->line_x    = infix_linerefs[n].x;
+      dest->func_name = infix_routines[infix_linerefs[n].routine].name;
+      dest->func_num  = infix_linerefs[n].routine;
+      dest->thisPC    = infix_linerefs[n].PC;
+      return TRUE;
+    }
+  }
+  dest->thisPC = 0;
+  return FALSE;
+}
+
+
+BOOL infix_decode_func_name(infix_location *dest, const char *file_name,
+                           const char *func_name)
+{
+  unsigned n;
+  if(!infix_linerefs)
+    return FALSE;
+  for(n = 0; n < infix_linerefscount; n++) {
+    if(n_strcmp(infix_files[infix_linerefs[n].filenum].filename, file_name) == 0) {
+      if(!file_name || n_strcmp(infix_routines[infix_linerefs[n].filenum].name,
+                               func_name) == 0) {
+
+       dest->file      = &infix_files[infix_linerefs[n].filenum];
+       dest->line_num  = infix_linerefs[n].line;
+       dest->line_x    = infix_linerefs[n].x;
+       dest->func_name = infix_routines[infix_linerefs[n].routine].name;
+       dest->func_num  = infix_linerefs[n].routine;
+       dest->thisPC    = infix_linerefs[n].PC;
+       return TRUE;
+      }
+    }
+  }
+  return FALSE;
+}
+
+
+void infix_gprint_loc(int frame, offset thisPC)
+{
+  infix_location boo;
+  offset loc;
+  BOOL found_frame;
+  unsigned numlocals;
+  unsigned n;
+
+  if(!thisPC) {
+    loc = frame_get_PC(frame);
+    numlocals = stack_get_numlocals(frame);
+  } else {
+    loc = thisPC;
+    numlocals = 0;
+  }
+
+  found_frame = infix_decode_PC(&boo, loc);
+
+  infix_print_offset(loc);
+
+  if(found_frame) {
+    infix_print_string(" in ");
+    infix_print_string(boo.func_name);
+  }
+
+  if(!thisPC) {
+    infix_print_string(" (");
+    
+    for(n = 0; n < numlocals; n++) {
+      const char *name;
+      if(n)
+       infix_print_string(", ");
+      if(found_frame) {
+       infix_print_string(infix_routines[boo.func_num].localnames[n]);
+       infix_print_char('=');
+      }
+      infix_print_znumber(frame_get_var(frame, n + 1));
+      name = debug_decode_number(frame_get_var(frame, n + 1));
+      if(name) {
+       infix_print_char(' ');
+       infix_print_string(name);
+      }
+    }
+
+    infix_print_string(")");
+  }
+
+  if(found_frame) {
+    infix_print_string(" at ");
+    if(boo.file->filename)
+      infix_print_string(boo.file->filename);
+    else
+      infix_print_string("<No file>");
+    infix_print_char(':');
+    infix_print_number(boo.line_num);
+  }
+  
+  infix_print_char('\n');
+
+  if(found_frame && !thisPC)
+    infix_file_print_line(boo.file, boo.line_num);
+}
+
+
+void infix_list_files(void)
+{
+  unsigned i;
+  for(i = 0; i < infix_filescount; i++) {
+    if(i)
+      infix_print_string(", ");
+    infix_print_string(infix_files[i].filename);
+  }
+}
+
+#else
+
+BOOL init_infix(strid_t unused)
+{
+  n_show_error(E_DEBUG, "debugging code not compiled in", 0);
+  return FALSE;
+}
+
+
+#endif
+