text: /* NetHack 3.6  makedefs.c  $NHDT-Date: 1582403492 2020/02/22 20:31:32 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.177 $ */
  /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  /*-Copyright (c) Kenneth Lorber, Kensington, Maryland, 2015. */
  /* Copyright (c) M. Stephenson, 1990, 1991.                       */
  /* Copyright (c) Dean Luick, 1990.                                */
  /* NetHack may be freely redistributed.  See license for details. */
  
  #define MAKEDEFS_C /* use to conditionally include file sections */
  
  #include "config.h"
  #ifdef MONITOR_HEAP
  #undef free /* makedefs doesn't use the alloc and free in src/alloc.c */
  #endif
  #include "permonst.h"
  #include "objclass.h"
  #include "monsym.h"
  #include "artilist.h"
  #include "dungeon.h"
  #include "obj.h"
  #include "monst.h"
  #include "you.h"
  #include "context.h"
  #include "flag.h"
  #include "dlb.h"
  
  /* version information */
  #ifdef SHORT_FILENAMES
  #include "patchlev.h"
  #else
  #include "patchlevel.h"
  #endif
  
  #include <ctype.h>
  #ifdef MAC
  #if defined(__SC__) || defined(__MRC__) /* MPW compilers */
  #define MPWTOOL
  #include <CursorCtl.h>
  #include <string.h>
  #else /* MAC without MPWTOOL */
  #define MACsansMPWTOOL
  #endif
  #endif /* MAC */
  
  #ifndef MPWTOOL
  #define SpinCursor(x)
  #endif
  
  #define Fprintf (void) fprintf
  #define Fclose (void) fclose
  #define Unlink (void) unlink
  #if !defined(AMIGA) || defined(AZTEC_C)
  #define rewind(fp) fseek((fp), 0L, SEEK_SET) /* guarantee a return value */
  #endif
  
  #if defined(UNIX) && !defined(LINT) && !defined(GCC_WARN)
  static const char SCCS_Id[] UNUSED = "@(#)makedefs.c\t3.6\t2020/03/04";
  #endif
  
  /* names of files to be generated */
  #define DATE_FILE "date.h"
  #define MONST_FILE "pm.h"
  #define ONAME_FILE "onames.h"
  #ifndef OPTIONS_FILE
  #define OPTIONS_FILE "options"
  #endif
  #define ORACLE_FILE "oracles"
  #define DATA_FILE "data"
  #define RUMOR_FILE "rumors"
  #define DGN_I_FILE "dungeon.def"
  #define DGN_O_FILE "dungeon.pdf"
  #define MON_STR_C "monstr.c"
  #define QTXT_I_FILE "quest.txt"
  #define QTXT_O_FILE "quest.dat"
  #define VIS_TAB_H "vis_tab.h"
  #define VIS_TAB_C "vis_tab.c"
  #define GITINFO_FILE "gitinfo.txt"
  /* locations for those files */
  #ifdef AMIGA
  #define FILE_PREFIX
  #define INCLUDE_TEMPLATE "NH:include/t.%s"
  #define SOURCE_TEMPLATE "NH:src/%s"
  #define DGN_TEMPLATE "NH:dat/%s" /* where dungeon.pdf file goes */
  #define DATA_TEMPLATE "NH:slib/%s"
  #define DATA_IN_TEMPLATE "NH:dat/%s"
  #else /* not AMIGA */
  #if defined(MAC) && !defined(__MACH__)
  /* MacOS 9 or earlier */
  #define INCLUDE_TEMPLATE ":include:%s"
  #define SOURCE_TEMPLATE ":src:%s"
  #define DGN_TEMPLATE ":dat:%s" /* where dungeon.pdf file goes */
  #if __SC__ || __MRC__
  #define DATA_TEMPLATE ":Dungeon:%s"
  #else
  #define DATA_TEMPLATE ":lib:%s"
  #endif /* __SC__ || __MRC__ */
  #define DATA_IN_TEMPLATE ":dat:%s"
  #else /* neither AMIGA nor MAC */
  #ifdef OS2
  #define INCLUDE_TEMPLATE "..\\include\\%s"
  #define SOURCE_TEMPLATE "..\\src\\%s"
  #define DGN_TEMPLATE "..\\dat\\%s" /* where dungeon.pdf file goes */
  #define DATA_TEMPLATE "..\\dat\\%s"
  #define DATA_IN_TEMPLATE "..\\dat\\%s"
  #else /* not AMIGA, MAC, or OS2 */
  #define INCLUDE_TEMPLATE "../include/%s"
  #define SOURCE_TEMPLATE "../src/%s"
  #define DGN_TEMPLATE "../dat/%s" /* where dungeon.pdf file goes */
  #define DATA_TEMPLATE "../dat/%s"
  #define DATA_IN_TEMPLATE "../dat/%s"
  #endif /* else !OS2 */
  #endif /* else !MAC */
  #endif /* else !AMIGA */
  
  static const char
      *Dont_Edit_Code =
          "/* This source file is generated by 'makedefs'.  Do not edit. */\n",
      *Dont_Edit_Data =
          "#\tThis data file is generated by 'makedefs'.  Do not edit. \n";
  
  static struct version_info version;
  
  /* definitions used for vision tables */
  #define TEST_WIDTH COLNO
  #define TEST_HEIGHT ROWNO
  #define BLOCK_WIDTH (TEST_WIDTH + 10)
  #define BLOCK_HEIGHT TEST_HEIGHT /* don't need extra spaces */
  #define MAX_ROW (BLOCK_HEIGHT + TEST_HEIGHT)
  #define MAX_COL (BLOCK_WIDTH + TEST_WIDTH)
  /* Use this as an out-of-bound value in the close table.  */
  #define CLOSE_OFF_TABLE_STRING "99" /* for the close table */
  #define FAR_OFF_TABLE_STRING "0xff" /* for the far table */
  
  #define sign(z) ((z) < 0 ? -1 : ((z) ? 1 : 0))
  #ifdef VISION_TABLES
  static char xclear[MAX_ROW][MAX_COL];
  #endif
  /*-end of vision defs-*/
  
  #define MAXFNAMELEN 600
  
  static char filename[MAXFNAMELEN];
  
  #ifdef FILE_PREFIX
  /* if defined, a first argument not starting with - is
   * taken as a text string to be prepended to any
   * output filename generated */
  char *file_prefix = "";
  #endif
  
  #ifdef MACsansMPWTOOL
  int FDECL(main, (void));
  #else
  int FDECL(main, (int, char **));
  #endif
  void FDECL(do_makedefs, (char *));
  void NDECL(do_objs);
  void NDECL(do_data);
  void NDECL(do_dungeon);
  void NDECL(do_date);
  void NDECL(do_options);
  void NDECL(do_monstr);
  void NDECL(do_permonst);
  void NDECL(do_questtxt);
  void NDECL(do_rumors);
  void NDECL(do_oracles);
  void NDECL(do_vision);
  
  extern void NDECL(monst_init);   /* monst.c */
  extern void NDECL(objects_init); /* objects.c */
  
  static void NDECL(link_sanity_check);
  static char *FDECL(name_file, (const char *, const char *));
  static void FDECL(delete_file, (const char *template, const char *));
  static FILE *FDECL(getfp, (const char *, const char *, const char *));
  static void FDECL(do_ext_makedefs, (int, char **));
  
  static void NDECL(make_version);
  static char *FDECL(version_string, (char *, const char *));
  static char *FDECL(version_id_string, (char *, const char *));
  static char *FDECL(bannerc_string, (char *, const char *));
  static char *FDECL(xcrypt, (const char *));
  static unsigned long FDECL(read_rumors_file,
                             (const char *, int *, long *, unsigned long));
  static boolean FDECL(get_gitinfo, (char *, char *));
  static void FDECL(do_rnd_access_file, (const char *, const char *));
  static boolean FDECL(d_filter, (char *));
  static boolean FDECL(h_filter, (char *));
  static void NDECL(build_savebones_compat_string);
  static void NDECL(windowing_sanity);
  static void FDECL(opt_out_words, (char *, int *));
  
  static boolean FDECL(qt_comment, (char *));
  static boolean FDECL(qt_control, (char *));
  static int FDECL(get_hdr, (char *));
  static boolean FDECL(new_id, (char *));
  static boolean FDECL(known_msg, (int, int));
  static void FDECL(new_msg, (char *, int, int));
  static char *FDECL(valid_qt_summary, (char *, BOOLEAN_P));
  static void FDECL(do_qt_control, (char *));
  static void FDECL(do_qt_text, (char *));
  static void NDECL(adjust_qt_hdrs);
  static void NDECL(put_qt_hdrs);
  
  #ifdef VISION_TABLES
  static void NDECL(H_close_gen);
  static void NDECL(H_far_gen);
  static void NDECL(C_close_gen);
  static void NDECL(C_far_gen);
  static int FDECL(clear_path, (int, int, int, int));
  #endif
  
  static char *FDECL(fgetline, (FILE*));
  static char *FDECL(tmpdup, (const char *));
  static char *FDECL(limit, (char *, int));
  static char *FDECL(eos, (char *));
  static int FDECL(case_insensitive_comp, (const char *, const char *));
  
  /* input, output, tmp */
  static FILE *ifp, *ofp, *tfp;
  
  static boolean use_enum =
  #ifdef ENUM_PM
      TRUE;
  #else
      FALSE;
  #endif
  
  #if defined(__BORLANDC__) && !defined(_WIN32)
  extern unsigned _stklen = STKSIZ;
  #endif
  
  #ifdef MACsansMPWTOOL
  int
  main(void)
  {
      const char *def_options = "odemvpqrshz";
      char buf[100];
      int len;
  
      printf("Enter options to run: [%s] ", def_options);
      fflush(stdout);
      fgets(buf, 100, stdin);
      len = strlen(buf);
      if (len <= 1)
          Strcpy(buf, def_options);
      else
          buf[len - 1] = 0; /* remove return */
  
      if (buf[0] == '-' && buf[1] == '-') {
  #if 0
          split up buf into words
          do_ext_makedefs(fakeargc, fakeargv);
  #else
          printf("extended makedefs not implemented for Mac OS9\n");
          exit(EXIT_FAILURE);
  #endif
      }
  
      do_makedefs(buf);
      exit(EXIT_SUCCESS);
      return 0;
  }
  
  #else /* ! MAC */
  
  int
  main(argc, argv)
  int argc;
  char *argv[];
  {
      if ((argc == 1) ||
          ((argc != 2)
  #ifdef FILE_PREFIX
          && (argc != 3)
  #endif
          && !(argv[1][0] == '-' && argv[1][1] == '-'))) {
          Fprintf(stderr, "Bad arg count (%d).\n", argc - 1);
          (void) fflush(stderr);
          return 1;
      }
  
  #ifdef FILE_PREFIX
      if (argc >= 2 && argv[1][0] != '-') {
          file_prefix = argv[1];
          argc--;
          argv++;
      }
  #endif
  
      if (argv[1][0] == '-' && argv[1][1] == '-') {
          do_ext_makedefs(argc, argv);
      } else {
          do_makedefs(&argv[1][1]);
      }
      exit(EXIT_SUCCESS);
      /*NOTREACHED*/
      return 0;
  }
  
  #endif
  
  static void
  link_sanity_check()
  {
      /* Note:  these initializers don't do anything except guarantee that
              we're linked properly.
      */
      monst_init();
      objects_init();
  
  }
  
  void
  do_makedefs(options)
  char *options;
  {
      boolean more_than_one;
  
      link_sanity_check();
  
      /* construct the current version number */
      make_version();
  
      more_than_one = strlen(options) > 1;
      while (*options) {
          if (more_than_one)
              Fprintf(stderr, "makedefs -%c\n", *options);
  
          switch (*options) {
          case 'o':
          case 'O':
              do_objs();
              break;
          case 'd':
          case 'D':
              do_data();
              break;
          case 'e':
          case 'E':
              do_dungeon();
              break;
          case 'm':
          case 'M':
              do_monstr();
              break;
          case 'v':
          case 'V':
              do_date();
              do_options();
              break;
          case 'p':
          case 'P':
              do_permonst();
              break;
          case 'q':
          case 'Q':
              do_questtxt();
              break;
          case 'r':
          case 'R':
              do_rumors();
              break;
          case 's':
          case 'S':
              /*
               * post-3.6.5:
               *  File must not be empty to avoid divide by 0
               *  in core's rn2(), so provide a default entry.
               */
              do_rnd_access_file(EPITAPHFILE,
                  /* default epitaph:  parody of the default engraving */
                                 "No matter where I went, here I am.");
              do_rnd_access_file(ENGRAVEFILE,
                  /* default engraving:  popularized by "The Adventures of
                     Buckaroo Bonzai Across the 8th Dimenstion" but predates
                     that 1984 movie; some attribute it to Confucius */
                                 "No matter where you go, there you are.");
              do_rnd_access_file(BOGUSMONFILE,
                  /* default bogusmon:  iconic monster that isn't in nethack */
                                 "grue");
              break;
          case 'h':
          case 'H':
              do_oracles();
              break;
          case 'z':
          case 'Z':
              do_vision();
              break;
  
          default:
              Fprintf(stderr, "Unknown option '%c'.\n", *options);
              (void) fflush(stderr);
              exit(EXIT_FAILURE);
          }
          options++;
      }
      if (more_than_one)
          Fprintf(stderr, "Completed.\n"); /* feedback */
  }
  
  static char namebuf[1000];
  
  static char *
  name_file(template, tag)
  const char *template;
  const char *tag;
  {
      Sprintf(namebuf, template, tag);
      return namebuf;
  }
  
  static void
  delete_file(template, tag)
  const char *template;
  const char *tag;
  {
      char *name = name_file(template, tag);
  
      Unlink(name);
  }
  
  static FILE *
  getfp(template, tag, mode)
  const char *template;
  const char *tag;
  const char *mode;
  {
      char *name = name_file(template, tag);
      FILE *rv = fopen(name, mode);
  
      if (!rv) {
          Fprintf(stderr, "Can't open '%s'.\n", name);
          exit(EXIT_FAILURE);
      }
      return rv;
  }
  
  static boolean debug = FALSE;
  
  static FILE *inputfp;
  static FILE *outputfp;
  
  struct grep_var {
      const char *name;
      int is_defined; /* 0 undef; 1 defined */
  };
  /* struct grep_var grep_vars[] and TODO_* constants in include file: */
  #include "mdgrep.h"
  
  static void NDECL(do_grep_showvars);
  static struct grep_var *FDECL(grepsearch, (const char *));
  static int FDECL(grep_check_id, (const char *));
  static void FDECL(grep_show_wstack, (const char *));
  static char *FDECL(do_grep_control, (char *));
  static void NDECL(do_grep);
  static void FDECL(grep0, (FILE *, FILE *));
  
  static int grep_trace = 0;
  
  #define IS_OPTION(str) if (!strcmp(&argv[0][2], str))
  #define CONTINUE    \
      argv++, argc--; \
      continue
  #define CONSUME                              \
      argv++, argc--;                          \
      if (argc == 0) {                         \
          Fprintf(stderr, "missing option\n"); \
          exit(EXIT_FAILURE);                  \
      }
  
  static void
  do_ext_makedefs(int argc, char **argv)
  {
      int todo = 0;
  
      link_sanity_check();
  
      argc--;
      argv++; /* skip program name */
  
      while (argc) {
          if (argv[0][0] != '-')
              break;
          if (argv[0][1] != '-') {
              Fprintf(stderr, "Can't mix - and -- options.\n");
              exit(EXIT_FAILURE);
          }
          IS_OPTION("svs") {
              /* short version string for packaging - note no \n */
              char buf[100];
              char delim[10];
  
              argv++; /* not CONSUME */
              delim[0] = '\0';
              if (argv[0])
                  strcpy(delim, argv[0]);
              Fprintf(stdout, "%s", version_string(buf, delim));
              exit(EXIT_SUCCESS);
          }
          IS_OPTION("debug") {
              debug = TRUE;
              CONTINUE;
          }
          IS_OPTION("make") {
              CONSUME;
              do_makedefs(argv[0]);
              exit(EXIT_SUCCESS);
          }
          IS_OPTION("input") {
              CONSUME;
              if (!strcmp(argv[0], "-")) {
                  inputfp = stdin;
              } else {
                  inputfp = fopen(argv[0], RDTMODE);
                  if (!inputfp) {
                      Fprintf(stderr, "Can't open '%s'.\n", argv[0]);
                      exit(EXIT_FAILURE);
                  }
              }
              CONTINUE;
          }
          IS_OPTION("output") {
              CONSUME;
              if (!strcmp(argv[0], "-")) {
                  outputfp = stdout;
              } else {
                  outputfp = fopen(argv[0], WRTMODE);
                  if (!outputfp) {
                      Fprintf(stderr, "Can't open '%s'.\n", argv[0]);
                      exit(EXIT_FAILURE);
                  }
              }
              CONTINUE;
          }
          IS_OPTION("grep") {
              if (todo) {
                  Fprintf(stderr, "Can't do grep and something else.\n");
                  exit(EXIT_FAILURE);
              }
              todo = TODO_GREP;
              CONTINUE;
          }
          IS_OPTION("grep-showvars") {
              do_grep_showvars();
              exit(EXIT_SUCCESS);
          }
          IS_OPTION("grep-trace") {
              grep_trace = 1;
              CONTINUE;
          }
          IS_OPTION("grep-define") {
              struct grep_var *p;
  
              CONSUME;
              p = grepsearch(argv[0]);
              if (p) {
                  p->is_defined = 1;
              } else {
                  Fprintf(stderr, "Unknown symbol '%s'\n", argv[0]);
                  exit(EXIT_FAILURE);
              }
              CONTINUE;
          }
          IS_OPTION("grep-undef") {
              struct grep_var *p;
  
              CONSUME;
              p = grepsearch(argv[0]);
              if (p) {
                  p->is_defined = 0;
              } else {
                  Fprintf(stderr, "Unknown symbol '%s'\n", argv[0]);
                  exit(EXIT_FAILURE);
              }
              CONTINUE;
          }
  #ifdef notyet
          IS_OPTION("help") {
          }
  #endif
          Fprintf(stderr, "Unknown option '%s'.\n", argv[0]);
          exit(EXIT_FAILURE);
      }
      if (argc) {
          Fprintf(stderr, "unexpected argument '%s'.\n", argv[0]);
          exit(EXIT_FAILURE);
      }
  
      switch (todo) {
      default:
          Fprintf(stderr, "Confused about what to do?\n");
          exit(EXIT_FAILURE);
      case 0:
          Fprintf(stderr, "Nothing to do?\n");
          exit(EXIT_FAILURE);
      case TODO_GREP:
          do_grep();
          break;
      }
  }
  
  #undef IS_OPTION
  #undef CONTINUE
  #undef CONSUME
  
  /*
   * Filtering syntax:
   * Any line NOT starting with a caret is either suppressed or passed
   * through unchanged depending on the current conditional state.
   *
   * The default conditional state is printing on.
   *
   * Conditionals may be nested.
   *
   * makedefs will exit with a EXIT_FAILURE if any errors are detected;
   * as many errors as possible are detected before giving up.
   *
   * Unknown identifiers are treated as TRUE and also as an error to
   * allow processing to continue past the unknown identifier (note
   * that "#undef" is different than unknown).
   *
   * Any line starting with a caret is a control line; as in C, zero or
   * more spaces may be embedded in the line almost anywhere; the caret
   * MUST be in column 1.
   * (XXX for the moment, no white space is allowed after the caret because
   * existing lines in the docs look like that.)
   *
   * Control lines:
   *      ^^      a line starting with a (single) literal caret
   *      ^#      a comment - the line is ignored
   *      ^?ID    if defined(ID)
   *      ^!ID    if !defined(ID)
   *      ^:      else
   *      ^.      endif
   */
  #define GREP_MAGIC '^'
  #define GREP_STACK_SIZE 100
  #ifdef notyet
  static int grep_rewrite = 0; /* need to (possibly) rewrite lines */
  #endif
  static int grep_writing = 1; /* need to copy lines to output */
  static int grep_errors = 0;
  static int grep_sp = 0;
  #define ST_LD(old, opp) (((old) ? 1 : 0) | ((opp) ? 2 : 0))
  #define ST_OLD(v) (((v) & 1) != 0)
  #define ST_OPP(v) (((v) & 2) != 0)
  #define ST_ELSE 4
  static int grep_stack[GREP_STACK_SIZE] = { ST_LD(1, 0) };
  static int grep_lineno = 0;
  
  static void
  do_grep_showvars()
  {
      int x;
  
      for (x = 0; x < SIZE(grep_vars) - 1; x++) {
          printf("%d\t%s\n", grep_vars[x].is_defined, grep_vars[x].name);
      }
  }
  
  static struct grep_var *
  grepsearch(name)
  const char *name;
  {
      /* XXX make into binary search */
      int x = 0;
  
      while (x < SIZE(grep_vars) - 1) {
          if (!strcmp(grep_vars[x].name, name))
              return &grep_vars[x];
          x++;
      }
      return 0;
  }
  
  static int
  grep_check_id(id)
  const char *id;
  {
      struct grep_var *rv;
  
      while (*id && isspace((uchar) *id))
          id++;
      if (!*id) {
          Fprintf(stderr, "missing identifier in line %d", grep_lineno);
          grep_errors++;
          return 0;
      }
      rv = grepsearch(id);
      if (rv) {
          if (grep_trace) {
              Fprintf(outputfp, "ID %d %s\n", rv->is_defined, id);
          }
          return rv->is_defined;
      }
  
      if (grep_trace) {
          Fprintf(outputfp, "ID U %s\n", id);
      }
      Fprintf(stderr, "unknown identifier '%s' in line %d.\n", id, grep_lineno);
      grep_errors++;
      return 2; /* So new features can be checked before makedefs
                 * is rebuilt. */
  }
  
  static void
  grep_show_wstack(tag)
  const char *tag;
  {
      int x;
  
      if (!grep_trace)
          return;
  
      Fprintf(outputfp, "%s w=%d sp=%d\t", tag, grep_writing, grep_sp);
      for (x = grep_sp; x >= 0 && x > grep_sp - 6; x--) {
          Fprintf(outputfp, "[%d]=%d ", x, grep_stack[x]);
      }
      Fprintf(outputfp, "\n");
  }
  
  static char *
  do_grep_control(buf)
  char *buf;
  {
      int isif = 1;
      char *buf0 = buf;
  #if 1
      if (isspace((uchar) buf[0]))
          return &buf[-1]; /* XXX see docs above */
  #else
      while (buf[0] && isspace((uchar) buf[0]))
          buf++;
  #endif
      switch (buf[0]) {
      case '#': /* comment */
          break;
      case '.': /* end of if level */
          if (grep_sp == 0) {
              Fprintf(stderr, "unmatched ^. (endif) at line %d.\n",
                      grep_lineno);
              grep_errors++;
          } else {
              grep_writing = ST_OLD(grep_stack[grep_sp--]);
              grep_show_wstack("pop");
          }
          break;
      case '!': /* if not ID */
          isif = 0;
      /* FALLTHROUGH */
      case '?': /* if ID */
          if (grep_sp == GREP_STACK_SIZE - 2) {
              Fprintf(stderr, "stack overflow at line %d.", grep_lineno);
              exit(EXIT_FAILURE);
          }
          if (grep_writing) {
              isif = grep_check_id(&buf[1]) ? isif : !isif;
              grep_stack[++grep_sp] = ST_LD(grep_writing, !isif);
              grep_writing = isif;
          } else {
              grep_stack[++grep_sp] = ST_LD(0, 0);
              /* grep_writing = 0; */
          }
          grep_show_wstack("push");
          break;
      case ':': /* else */
          if (ST_ELSE & grep_stack[grep_sp]) {
              Fprintf(stderr, "multiple : for same conditional at line %d.\n",
                      grep_lineno);
              grep_errors++;
          }
          grep_writing = ST_OPP(grep_stack[grep_sp]);
          grep_stack[grep_sp] |= ST_ELSE;
          break;
  #if defined(notyet)
      case '(': /* start of expression */
  #endif
      case GREP_MAGIC: /* ^^ -> ^ */
          return buf0;
      default: {
          char str[10];
  
          if (isprint((uchar) buf[0])) {
              str[0] = buf[0];
              str[1] = '\0';
          } else {
              sprintf(str, "0x%02x", buf[0]);
          }
          Fprintf(stderr, "unknown control ^%s at line %d.\n", str,
                  grep_lineno);
          grep_errors++;
      } break;
      }
      return NULL;
  }
  
  #ifdef notyet
  static void
  do_grep_rewrite(buf)
  char *buf;
  {
      /* no language features use this yet */
      return;
  }
  #endif
  
  static void grep0(FILE *, FILE *);
  
  static void
  do_grep()
  {
      if (!inputfp) {
          Fprintf(stderr, "--grep requires --input\n");
      }
      if (!outputfp) {
          Fprintf(stderr, "--grep requires --output\n");
      }
      if (!inputfp || !outputfp) {
          exit(EXIT_FAILURE);
      }
  
      grep0(inputfp, outputfp);
  }
  
  static void
  grep0(inputfp0, outputfp0)
  FILE *inputfp0;
  FILE *outputfp0;
  {
      char buf[16384]; /* looong, just in case */
  
      while (!feof(inputfp0) && !ferror(inputfp0)) {
          char *tmp;
          char *buf1;
  
          if (fgets(buf, sizeof(buf), inputfp0) == 0)
              break;
          if ((tmp = strchr(buf, '\n')))
              *tmp = '\0';
          grep_lineno++;
          if (grep_trace) {
              Fprintf(outputfp0, "%04d %c >%s\n", grep_lineno,
                      grep_writing ? ' ' : '#', buf);
          }
  
          if (buf[0] == GREP_MAGIC) {
              buf1 = do_grep_control(&buf[1]);
              if (!buf1)
                  continue;
          } else {
              buf1 = buf;
          }
  #ifdef notyet
          if (grep_rewrite)
              do_grep_rewrite(buf1);
  #endif
          if (grep_writing)
              Fprintf(outputfp0, "%s\n", buf1);
      }
      if (ferror(inputfp0)) {
          Fprintf(stderr, "read error!\n");
          exit(EXIT_FAILURE);
      }
      if (ferror(outputfp0)) {
          Fprintf(stderr, "write error!\n");
          exit(EXIT_FAILURE);
      }
      fclose(inputfp0);
      fclose(outputfp0);
      if (grep_sp) {
          Fprintf(stderr, "%d unterminated conditional level%s\n", grep_sp,
                  grep_sp == 1 ? "" : "s");
          grep_errors++;
      }
      if (grep_errors) {
          Fprintf(stderr, "%d error%s detected.\n", grep_errors,
                  grep_errors == 1 ? "" : "s");
          exit(EXIT_FAILURE);
      }
  }
  
  /* trivial text encryption routine which can't be broken with `tr' */
  static char *
  xcrypt(str)
  const char *str;
  { /* duplicated in src/hacklib.c */
      static char buf[BUFSZ];
      register const char *p;
      register char *q;
      register int bitmask;
  
      for (bitmask = 1, p = str, q = buf; *p; q++) {
          *q = *p++;
          if (*q & (32 | 64))
              *q ^= bitmask;
          if ((bitmask <<= 1) >= 32)
              bitmask = 1;
      }
      *q = '\0';
      return buf;
  }
  
  #define PAD_RUMORS_TO 60
  /* common code for do_rumors().  Return 0 on error. */
  static unsigned long
  read_rumors_file(file_ext, rumor_count, rumor_size, old_rumor_offset)
  const char *file_ext;
  int *rumor_count;
  long *rumor_size;
  unsigned long old_rumor_offset;
  {
      char infile[MAXFNAMELEN];
      char *line;
      unsigned long rumor_offset;
  
      Sprintf(infile, DATA_IN_TEMPLATE, RUMOR_FILE);
      Strcat(infile, file_ext);
      if (!(ifp = fopen(infile, RDTMODE))) {
          perror(infile);
          return 0L;
      }
  
      /* copy the rumors */
      while ((line = fgetline(ifp)) != 0) {
  #ifdef PAD_RUMORS_TO
          /* rumor selection is accomplished by seeking to a random
             position in the file, advancing to newline, and taking
             the next line; therefore, rumors which follow long-line
             rumors are most likely to be chosen and rumors which
             follow short-line rumors are least likely to be chosen;
             we ameliorate the latter by padding the shortest lines,
             increasing the chance of the random seek landing in them */
          int len = (int) strlen(line);
  
          if (len <= PAD_RUMORS_TO) {
              char *base = index(line, '\n');
              /* this is only safe because fgetline() overallocates */
              while (len++ < PAD_RUMORS_TO) {
                  *base++ = '_';
              }
              *base++ = '\n';
              *base = '\0';
          }
  #endif
          (*rumor_count)++;
  #if 0
          /*[if we forced binary output, this would be sufficient]*/
          *rumor_size += strlen(line); /* includes newline */
  #endif
          (void) fputs(xcrypt(line), tfp);
          free(line);
      }
      /* record the current position; next rumors section will start here */
      rumor_offset = (unsigned long) ftell(tfp);
      Fclose(ifp); /* all done with rumors.file_ext */
  
      /* the calculated value for *_rumor_count assumes that
         a single-byte line terminator is in use; for platforms
         which use two byte CR+LF, we need to override that value
         [it's much simpler to do so unconditionally, rendering
         the loop's accumulation above obsolete] */
      *rumor_size = (long) (rumor_offset - old_rumor_offset);
      return rumor_offset;
  }
  
  static void
  do_rnd_access_file(fname, deflt_content)
  const char *fname;
  const char *deflt_content;
  {
      char *line;
  
      Sprintf(filename, DATA_IN_TEMPLATE, fname);
      Strcat(filename, ".txt");
      if (!(ifp = fopen(filename, RDTMODE))) {
          perror(filename);
          exit(EXIT_FAILURE);
      }
      filename[0] = '\0';
  #ifdef FILE_PREFIX
      Strcat(filename, file_prefix);
  #endif
      Sprintf(eos(filename), DATA_TEMPLATE, fname);
      if (!(ofp = fopen(filename, WRTMODE))) {
          perror(filename);
          exit(EXIT_FAILURE);
      }
      Fprintf(ofp, "%s", Dont_Edit_Data);
      /* write out the default content entry unconditionally instead of
         waiting to see whether there are no regular output lines; if it
         matches a regular entry (bogusmon "grue"), that entry will become
         more likely to be picked than normal but it's nothing to worry about */
      (void) fputs(xcrypt(deflt_content), ofp);
  
      tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE);
      grep0(ifp, tfp);
      ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE);
  
      while ((line = fgetline(ifp)) != 0) {
          if (line[0] != '#' && line[0] != '\n')
              (void) fputs(xcrypt(line), ofp);
          free((genericptr_t) line);
      }
      Fclose(ifp);
      Fclose(ofp);
  
      delete_file(DATA_TEMPLATE, "grep.tmp");
      return;
  }
  
  void
  do_rumors()
  {
      char *line;
      static const char rumors_header[] =
          "%s%04d,%06ld,%06lx;%04d,%06ld,%06lx;0,0,%06lx\n";
      char tempfile[MAXFNAMELEN];
      int true_rumor_count, false_rumor_count;
      long true_rumor_size, false_rumor_size;
      unsigned long true_rumor_offset, false_rumor_offset, eof_offset;
  
      Sprintf(tempfile, DATA_TEMPLATE, "rumors.tmp");
      filename[0] = '\0';
  #ifdef FILE_PREFIX
      Strcat(filename, file_prefix);
  #endif
      Sprintf(eos(filename), DATA_TEMPLATE, RUMOR_FILE);
      if (!(ofp = fopen(filename, WRTMODE))) {
          perror(filename);
          exit(EXIT_FAILURE);
      }
      if (!(tfp = fopen(tempfile, WRTMODE))) {
          perror(tempfile);
          Fclose(ofp);
          exit(EXIT_FAILURE);
      }
  
      true_rumor_count = false_rumor_count = 0;
      true_rumor_size = false_rumor_size = 0L;
      true_rumor_offset = false_rumor_offset = eof_offset = 0L;
  
      /* output a dummy header record; we'll replace it in final output */
      Fprintf(tfp, rumors_header, Dont_Edit_Data, true_rumor_count,
              true_rumor_size, true_rumor_offset, false_rumor_count,
              false_rumor_size, false_rumor_offset, eof_offset);
      /* record the current position; true rumors will start here */
      true_rumor_offset = ftell(tfp);
  
      false_rumor_offset = read_rumors_file(
          ".tru", &true_rumor_count, &true_rumor_size, true_rumor_offset);
      if (!false_rumor_offset)
          goto rumors_failure;
  
      eof_offset = read_rumors_file(".fal", &false_rumor_count,
                                    &false_rumor_size, false_rumor_offset);
      if (!eof_offset)
          goto rumors_failure;
  
      /* get ready to transfer the contents of temp file to output file */
      line = malloc(BUFSZ + MAXFNAMELEN);
      Sprintf(line, "rewind of \"%s\"", tempfile);
      if (rewind(tfp) != 0) {
          perror(line);
          free(line);
          goto rumors_failure;
      }
      free(line);
  
      /* output the header record */
      Fprintf(ofp, rumors_header, Dont_Edit_Data, true_rumor_count,
              true_rumor_size, true_rumor_offset, false_rumor_count,
              false_rumor_size, false_rumor_offset, eof_offset);
      /* skip the temp file's dummy header */
      if (!(line = fgetline(tfp))) { /* "Don't Edit" */
          perror(tempfile);
          goto rumors_failure;
      }
      free(line);
      if (!(line = fgetline(tfp))) { /* count,size,offset */
          perror(tempfile);
          goto rumors_failure;
      }
      free(line);
      /* copy the rest of the temp file into the final output file */
      while ((line = fgetline(tfp)) != 0) {
          (void) fputs(line, ofp);
          free(line);
      }
      /* all done; delete temp file */
      Fclose(tfp);
      Unlink(tempfile);
      Fclose(ofp);
      return;
  
  rumors_failure:
      Fclose(ofp);
      Unlink(filename); /* kill empty or incomplete output file */
      Fclose(tfp);
      Unlink(tempfile); /* and temporary file */
      exit(EXIT_FAILURE);
  }
  
  /*
   * Use this to explicitly mask out features during version checks.
   *
   * ZEROCOMP, RLECOMP, and ZLIB_COMP describe compression features
   * that the port/plaform which wrote the savefile was capable of
   * dealing with. Don't reject a savefile just because the port
   * reading the savefile doesn't match on all/some of them.
   * The actual compression features used to produce the savefile are
   * recorded in the savefile_info structure immediately following the
   * version_info, and that is what needs to be checked against the
   * feature set of the port that is reading the savefile back in.
   * That check is done in src/restore.c now.
   *
   */
  #define IGNORED_FEATURES                 \
      (0L | (1L << 19) /* SCORE_ON_BOTL */ \
       | (1L << 27)    /* ZEROCOMP */      \
       | (1L << 28)    /* RLECOMP */       \
       )
  
  static void
  make_version()
  {
      register int i;
  
      /*
       * integer version number
       */
      version.incarnation = ((unsigned long) VERSION_MAJOR << 24)
                            | ((unsigned long) VERSION_MINOR << 16)
                            | ((unsigned long) PATCHLEVEL << 8)
                            | ((unsigned long) EDITLEVEL);
      /*
       * encoded feature list
       * Note:  if any of these magic numbers are changed or reassigned,
       * EDITLEVEL in patchlevel.h should be incremented at the same time.
       * The actual values have no special meaning, and the category
       * groupings are just for convenience.
       */
      version.feature_set = (unsigned long) (0L
  /* levels and/or topology (0..4) */
  /* monsters (5..9) */
  #ifdef MAIL
                                             | (1L << 6)
  #endif
  /* objects (10..14) */
  /* flag bits and/or other global variables (15..26) */
  #ifdef TEXTCOLOR
                                             | (1L << 17)
  #endif
  #ifdef INSURANCE
                                             | (1L << 18)
  #endif
  #ifdef SCORE_ON_BOTL
                                             | (1L << 19)
  #endif
  /* data format (27..31)
   * External compression methods such as COMPRESS and ZLIB_COMP
   * do not affect the contents and are thus excluded from here */
  #ifdef ZEROCOMP
                                             | (1L << 27)
  #endif
  #ifdef RLECOMP
                                             | (1L << 28)
  #endif
                                                 );
      /*
       * Value used for object & monster sanity check.
       *    (NROFARTIFACTS<<24) | (NUM_OBJECTS<<12) | (NUMMONS<<0)
       */
      for (i = 1; artifact_names[i]; i++)
          continue;
      version.entity_count = (unsigned long) (i - 1);
      for (i = 1; objects[i].oc_class != ILLOBJ_CLASS; i++)
          continue;
      version.entity_count = (version.entity_count << 12) | (unsigned long) i;
      for (i = 0; mons[i].mlet; i++)
          continue;
      version.entity_count = (version.entity_count << 12) | (unsigned long) i;
      /*
       * Value used for compiler (word size/field alignment/padding) check.
       */
      version.struct_sizes1 =
          (((unsigned long) sizeof(struct context_info) << 24)
           | ((unsigned long) sizeof(struct obj) << 17)
           | ((unsigned long) sizeof(struct monst) << 10)
           | ((unsigned long) sizeof(struct you)));
      version.struct_sizes2 = (((unsigned long) sizeof(struct flag) << 10) |
  /* free bits in here */
  #ifdef SYSFLAGS
                               ((unsigned long) sizeof(struct sysflag)));
  #else
                               ((unsigned long) 0L));
  #endif
      return;
  }
  
  /* REPRODUCIBLE_BUILD will change this to TRUE */
  static boolean date_via_env = FALSE;
  
  static char *
  version_string(outbuf, delim)
  char *outbuf;
  const char *delim;
  {
      Sprintf(outbuf, "%d%s%d%s%d", VERSION_MAJOR, delim, VERSION_MINOR, delim,
              PATCHLEVEL);
  #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
      Sprintf(eos(outbuf), "-%d", EDITLEVEL);
  #endif
      return outbuf;
  }
  
  static char *
  version_id_string(outbuf, build_date)
  char *outbuf;
  const char *build_date;
  {
      char subbuf[64], versbuf[64];
      char statusbuf[64];
  
  #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
  #if (NH_DEVEL_STATUS == NH_STATUS_BETA)
      Strcpy(statusbuf, " Beta");
  #else
  #if (NH_DEVEL_STATUS == NH_STATUS_WIP)
      Strcpy(statusbuf, " Work-in-progress");
  #else
      Strcpy(statusbuf, " post-release");
  #endif
  #endif
  #else
      statusbuf[0] = '\0';
  #endif
  
      subbuf[0] = '\0';
  #ifdef PORT_SUB_ID
      subbuf[0] = ' ';
      Strcpy(&subbuf[1], PORT_SUB_ID);
  #endif
  
      Sprintf(outbuf, "%s NetHack%s Version %s%s - last %s %s.", PORT_ID,
              subbuf, version_string(versbuf, "."), statusbuf,
              date_via_env ? "revision" : "build", build_date);
      return outbuf;
  }
  
  static char *
  bannerc_string(outbuf, build_date)
  char *outbuf;
  const char *build_date;
  {
      char subbuf[64], versbuf[64];
  
      subbuf[0] = '\0';
  #ifdef PORT_SUB_ID
      subbuf[0] = ' ';
      Strcpy(&subbuf[1], PORT_SUB_ID);
  #endif
  #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
  #if (NH_DEVEL_STATUS == NH_STATUS_BETA)
      Strcat(subbuf, " Beta");
  #else
  #if (NH_DEVEL_STATUS == NH_STATUS_WIP)
      Strcat(subbuf, " Work-in-progress");
  #else
      Strcat(subbuf, " post-release");
  #endif
  #endif
  #endif
  
      Sprintf(outbuf, "         Version %s %s%s, %s %s.",
              version_string(versbuf, "."), PORT_ID, subbuf,
              date_via_env ? "revised" : "built", &build_date[4]);
  #if 0
      Sprintf(outbuf, "%s NetHack%s %s Copyright 1985-%s (built %s)",
              PORT_ID, subbuf, version_string(versbuf,"."), RELEASE_YEAR,
              &build_date[4]);
  #endif
      return outbuf;
  }
  
  void
  do_date()
  {
  #ifdef KR1ED
      long clocktim = 0;
  #else
      time_t clocktim = 0;
  #endif
      char githash[BUFSZ], gitbranch[BUFSZ];
      char *c, cbuf[60], buf[BUFSZ];
      const char *ul_sfx;
  
      /* before creating date.h, make sure that xxx_GRAPHICS and
         DEFAULT_WINDOW_SYS have been set up in a viable fashion */
      windowing_sanity();
  
      filename[0] = '\0';
  #ifdef FILE_PREFIX
      Strcat(filename, file_prefix);
  #endif
      Sprintf(eos(filename), INCLUDE_TEMPLATE, DATE_FILE);
      if (!(ofp = fopen(filename, WRTMODE))) {
          perror(filename);
          exit(EXIT_FAILURE);
      }
      /* NB: We've moved on from SCCS, but this way this line
       * won't get clobbered when downstream projects import
       * this file into something more modern. */
      Fprintf(ofp, "%s", Dont_Edit_Code);
  
      (void) time(&clocktim);
  #ifdef REPRODUCIBLE_BUILD
      {
          /*
           * Use date+time of latest source file revision (set up in
           * our environment rather than derived by scanning sources)
           * instead of current date+time, so that later rebuilds of
           * the same sources specifying the same configuration will
           * produce the same result.
           *
           * Changing the configuration should be done by modifying
           * config.h or <port>conf.h and setting SOURCE_DATE_EPOCH
           * based on whichever changed most recently, not by using
           *   make CFLAGS='-Dthis -Dthat'
           * to make alterations on the fly.
           *
           * Limited validation is performed to prevent dates in the
           * future (beyond a leeway of 24 hours) or distant past.
           *
           * Assumes the value of time_t is in seconds, which is
           * fundamental for Unix and mandated by POSIX.  For any ports
           * where that isn't true, leaving REPRODUCIBLE_BUILD disabled
           * is probably preferrable to hacking this code....
           */
          static struct tm nh360; /* static init should yield UTC timezone */
          unsigned long sd_num, sd_earliest, sd_latest;
          const char *sd_str = getenv("SOURCE_DATE_EPOCH");
  
          if (sd_str) {
              sd_num = strtoul(sd_str, (char **) 0, 10);
              /*
               * Note:  this does not need to be updated for future
               * releases.  It serves as a sanity check for potentially
               * mis-set environment, not a hard baseline for when the
               * current version could have first been built.
               */
              /* oldest date we'll accept: 7-Dec-2015 (release of 3.6.0) */
              nh360.tm_mday = 7;
              nh360.tm_mon  = 12 - 1;
              nh360.tm_year = 2015 - 1900;
              sd_earliest = (unsigned long) mktime(&nh360);
              /* 'youngest' date we'll accept: 24 hours in the future */
              sd_latest = (unsigned long) clocktim + 24L * 60L * 60L;
  
              if (sd_num >= sd_earliest && sd_num <= sd_latest) {
                  /* use SOURCE_DATE_EPOCH value */
                  clocktim = (time_t) sd_num;
                  date_via_env = TRUE;
              } else {
                  Fprintf(stderr, "? Invalid value for SOURCE_DATE_EPOCH (%lu)",
                          sd_num);
                  if (sd_num > 0L && sd_num < sd_earliest)
                      Fprintf(stderr, ", older than %lu", sd_earliest);
                  else if (sd_num > sd_latest)
                      Fprintf(stderr, ", newer than %lu", sd_latest);
                  Fprintf(stderr, ".\n");
                  Fprintf(stderr, ": Reverting to current date+time (%lu).\n",
                          (unsigned long) clocktim);
                  (void) fflush(stderr);
              }
          } else {
              /* REPRODUCIBLE_BUILD enabled but SOURCE_DATE_EPOCH is missing */
              Fprintf(stderr, "? No value for SOURCE_DATE_EPOCH.\n");
              Fprintf(stderr, ": Using current date+time (%lu).\n",
                      (unsigned long) clocktim);
              (void) fflush(stderr);
          }
          Strcpy(cbuf, asctime(gmtime(&clocktim)));
      }
  #else
      /* ordinary build: use current date+time */
      Strcpy(cbuf, ctime(&clocktim));
  #endif
  
      if ((c = index(cbuf, '\n')) != 0)
          *c = '\0'; /* strip off the '\n' */
  #ifdef NHSTDC
      ul_sfx = "UL";
  #else
      ul_sfx = "L";
  #endif
      if (date_via_env)
          Fprintf(ofp, "#define SOURCE_DATE_EPOCH (%lu%s) /* via getenv() */\n",
                  (unsigned long) clocktim, ul_sfx);
      Fprintf(ofp, "#define BUILD_DATE \"%s\"\n", cbuf);
      if (date_via_env)
          Fprintf(ofp, "#define BUILD_TIME SOURCE_DATE_EPOCH\n");
      else
          Fprintf(ofp, "#define BUILD_TIME (%lu%s)\n",
                  (unsigned long) clocktim, ul_sfx);
      Fprintf(ofp, "\n");
      Fprintf(ofp, "#define VERSION_NUMBER 0x%08lx%s\n", version.incarnation,
              ul_sfx);
      Fprintf(ofp, "#define VERSION_FEATURES 0x%08lx%s\n", version.feature_set,
              ul_sfx);
  #ifdef IGNORED_FEATURES
      Fprintf(ofp, "#define IGNORED_FEATURES 0x%08lx%s\n",
              (unsigned long) IGNORED_FEATURES, ul_sfx);
  #endif
      Fprintf(ofp, "#define VERSION_SANITY1 0x%08lx%s\n", version.entity_count,
              ul_sfx);
      Fprintf(ofp, "#define VERSION_SANITY2 0x%08lx%s\n", version.struct_sizes1,
              ul_sfx);
      Fprintf(ofp, "#define VERSION_SANITY3 0x%08lx%s\n", version.struct_sizes2,
              ul_sfx);
      Fprintf(ofp, "\n");
      Fprintf(ofp, "#define VERSION_STRING \"%s\"\n", version_string(buf, "."));
      Fprintf(ofp, "#define VERSION_ID \\\n \"%s\"\n",
              version_id_string(buf, cbuf));
      Fprintf(ofp, "#define COPYRIGHT_BANNER_C \\\n \"%s\"\n",
              bannerc_string(buf, cbuf));
      Fprintf(ofp, "\n");
      if (get_gitinfo(githash, gitbranch)) {
          Fprintf(ofp, "#define NETHACK_GIT_SHA \"%s\"\n", githash);
          Fprintf(ofp, "#define NETHACK_GIT_BRANCH \"%s\"\n", gitbranch);
      }
  #ifdef AMIGA
      {
          struct tm *tm = localtime((time_t *) &clocktim);
  
          Fprintf(ofp, "#define AMIGA_VERSION_STRING ");
          Fprintf(ofp, "\"\\0$VER: NetHack %d.%d.%d (%d.%d.%d)\"\n",
                  VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL,
                  tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900);
      }
  #endif
      Fclose(ofp);
      return;
  }
  
  static boolean
  get_gitinfo(githash, gitbranch)
  char *githash, *gitbranch;
  {
      FILE *gifp;
      size_t len;
      char infile[MAXFNAMELEN];
      char *line, *strval, *opt, *c, *end;
      boolean havebranch = FALSE, havehash = FALSE;
  
      if (!githash || !gitbranch) return FALSE;
  
      Sprintf(infile, DATA_IN_TEMPLATE, GITINFO_FILE);
      if (!(gifp = fopen(infile, RDTMODE))) {
          /* perror(infile); */
          return FALSE;
      }
  
      /* read the gitinfo file */
      while ((line = fgetline(gifp)) != 0) {
          strval = index(line, '=');
          if (strval && strlen(strval) < (BUFSZ-1)) {
              opt = line;
              *strval++ = '\0';
              /* strip off the '\n' */
              if ((c = index(strval, '\n')) != 0)
                  *c = '\0'; 
              if ((c = index(opt, '\n')) != 0)
                  *c = '\0';
              /* strip leading and trailing white space */
              while (*strval == ' ' || *strval == '\t')
                  strval++;
              end = eos(strval);
              while (--end >= strval && (*end == ' ' || *end == '\t'))
              *end = '\0';
              while (*opt == ' ' || *opt == '\t')
                  opt++;
              end = eos(opt);
              while (--end >= opt && (*end == ' ' || *end == '\t'))
              *end = '\0';
  
              len = strlen(opt);
              if ((len >= strlen("gitbranch")) && !case_insensitive_comp(opt, "gitbranch")) {
                  Strcpy(gitbranch, strval);
                  havebranch = TRUE;
              }
              if ((len >= strlen("githash")) && !case_insensitive_comp(opt, "githash")) {
                  Strcpy(githash, strval);
                  havehash = TRUE;
              }
  	}
          free(line);
      }
      Fclose(gifp);
      if (havebranch && havehash)
          return TRUE;
      return FALSE;
  }
  
  static int
  case_insensitive_comp(s1, s2)
  const char *s1;
  const char *s2;
  {
      uchar u1, u2;
  
      for (;; s1++, s2++) {
          u1 = (uchar) *s1;
          if (isupper(u1))
              u1 = tolower(u1);
          u2 = (uchar) *s2;
          if (isupper(u2))
              u2 = tolower(u2);
          if (u1 == '\0' || u1 != u2)
              break;
      }
      return u1 - u2;
  }
  
  static char save_bones_compat_buf[BUFSZ];
  
  static void
  build_savebones_compat_string()
  {
  #ifdef VERSION_COMPATIBILITY
      unsigned long uver = VERSION_COMPATIBILITY;
  #endif
      Strcpy(save_bones_compat_buf,
             "save and bones files accepted from version");
  #ifdef VERSION_COMPATIBILITY
      Sprintf(eos(save_bones_compat_buf), "s %lu.%lu.%lu through %d.%d.%d",
              ((uver & 0xFF000000L) >> 24), ((uver & 0x00FF0000L) >> 16),
              ((uver & 0x0000FF00L) >> 8), VERSION_MAJOR, VERSION_MINOR,
              PATCHLEVEL);
  #else
      Sprintf(eos(save_bones_compat_buf), " %d.%d.%d only", VERSION_MAJOR,
              VERSION_MINOR, PATCHLEVEL);
  #endif
  }
  
  static const char *build_opts[] = {
  #ifdef AMIGA_WBENCH
      "Amiga WorkBench support",
  #endif
  #ifdef ANSI_DEFAULT
      "ANSI default terminal",
  #endif
  #ifdef TEXTCOLOR
      "color",
  #endif
  #ifdef TTY_GRAPHICS
  #ifdef TTY_TILES_ESCCODES
      "console escape codes for tile hinting",
  #endif
  #endif
  #ifdef COM_COMPL
      "command line completion",
  #endif
  #ifdef LIFE
      "Conway's Game of Life",
  #endif
  #ifdef COMPRESS
      "data file compression",
  #endif
  #ifdef ZLIB_COMP
      "ZLIB data file compression",
  #endif
  #ifdef DLB
  #ifndef VERSION_IN_DLB_FILENAME
      "data librarian",
  #else
      "data librarian with a version-dependent name",
  #endif
  #endif
  #ifdef DUMPLOG
      "end-of-game dumplogs",
  #endif
  #ifdef HOLD_LOCKFILE_OPEN
      "exclusive lock on level 0 file",
  #endif
  #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
      "external program as a message handler",
  #endif
  #ifdef MFLOPPY
      "floppy drive support",
  #endif
  #ifdef INSURANCE
      "insurance files for recovering from crashes",
  #endif
  #ifdef LOGFILE
      "log file",
  #endif
  #ifdef XLOGFILE
      "extended log file",
  #endif
  #ifdef PANICLOG
      "errors and warnings log file",
  #endif
  #ifdef MAIL
      "mail daemon",
  #endif
  #ifdef GNUDOS
      "MSDOS protected mode",
  #endif
  #ifdef NEWS
      "news file",
  #endif
  #ifdef OVERLAY
  #ifdef MOVERLAY
      "MOVE overlays",
  #else
  #ifdef VROOMM
      "VROOMM overlays",
  #else
      "overlays",
  #endif
  #endif
  #endif
      /* pattern matching method will be substituted by nethack at run time */
      "pattern matching via :PATMATCH:",
  #ifdef USE_ISAAC64
      "pseudo random numbers generated by ISAAC64",
  #ifdef DEV_RANDOM
  #ifdef NHSTDC
      /* include which specific one */
      "strong PRNG seed available from " DEV_RANDOM,
  #else
      "strong PRNG seed available from DEV_RANDOM",
  #endif
  #else
  #ifdef WIN32
      "strong PRNG seed available from CNG BCryptGenRandom()",
  #endif
  #endif  /* DEV_RANDOM */    
  #else   /* ISAAC64 */
  #ifdef RANDOM
      "pseudo random numbers generated by random()",
  #else
      "pseudo random numbers generated by C rand()",
  #endif
  #endif
  #ifdef SELECTSAVED
      "restore saved games via menu",
  #endif
  #ifdef SCORE_ON_BOTL
      "score on status line",
  #endif
  #ifdef CLIPPING
      "screen clipping",
  #endif
  #ifdef NO_TERMS
  #ifdef MAC
      "screen control via mactty",
  #endif
  #ifdef SCREEN_BIOS
      "screen control via BIOS",
  #endif
  #ifdef SCREEN_DJGPPFAST
      "screen control via DJGPP fast",
  #endif
  #ifdef SCREEN_VGA
      "screen control via VGA graphics",
  #endif
  #ifdef WIN32CON
      "screen control via WIN32 console I/O",
  #endif
  #endif
  #ifdef SHELL
      "shell command",
  #endif
      "traditional status display",
  #ifdef STATUS_HILITES
      "status via windowport with highlighting",
  #else
      "status via windowport without highlighting",
  #endif
  #ifdef SUSPEND
      "suspend command",
  #endif
  #ifdef TTY_GRAPHICS
  #ifdef TERMINFO
      "terminal info library",
  #else
  #if defined(TERMLIB) || (!defined(MICRO) && !defined(WIN32))
      "terminal capability library",
  #endif
  #endif
  #endif /*TTY_GRAPHICS*/
  /*#ifdef X11_GRAPHICS*/
  #ifdef USE_XPM
      "tiles file in XPM format",
  #endif
  /*#endif*/
  /*#if (defined(QT_GRAPHICS) || defined(X11_GRAPHICS)*/
  #ifdef GRAPHIC_TOMBSTONE
      "graphical RIP screen",
  #endif
  /*#endif*/
  #ifdef TIMED_DELAY
      "timed wait for display effects",
  #endif
  #ifdef USER_SOUNDS
      "user sounds",
  #endif
  #ifdef PREFIXES_IN_USE
      "variable playground",
  #endif
  #ifdef VISION_TABLES
      "vision tables",
  #endif
  #ifdef ZEROCOMP
      "zero-compressed save files",
  #endif
  #ifdef RLECOMP
      "run-length compression of map in save files",
  #endif
  #ifdef SYSCF
      "system configuration at run-time",
  #endif
      save_bones_compat_buf,
      "and basic NetHack features"
  };
  
  struct win_info {
      const char *id, /* DEFAULT_WINDOW_SYS string */
          *name;      /* description, often same as id */
  };
  static struct win_info window_opts[] = {
  #ifdef TTY_GRAPHICS
      { "tty",
        /* testing 'USE_TILES' here would bring confusion because it could
           apply to another interface such as X11, so check MSDOS explicitly
           instead; even checking TTY_TILES_ESCCODES would probably be
           confusing to most users (and it will already be listed separately
           in the compiled options section so users aware of it can find it) */
  #ifdef MSDOS
        "traditional text with optional 'tiles' graphics"
  #else
        /* assume that one or more of IBMgraphics, DECgraphics, or MACgraphics
           can be enabled; we can't tell from here whether that is accurate */
        "traditional text with optional line-drawing"
  #endif
      },
  #endif
  #ifdef CURSES_GRAPHICS
      { "curses", "terminal-based graphics" },
  #endif
  #ifdef X11_GRAPHICS
      { "X11", "X11" },
  #endif
  #ifdef QT_GRAPHICS /* too vague; there are multiple incompatible versions */
      { "Qt", "Qt" },
  #endif
  #ifdef GNOME_GRAPHICS /* unmaintained/defunct */
      { "Gnome", "Gnome" },
  #endif
  #ifdef MAC /* defunct OS 9 interface */
      { "mac", "Mac" },
  #endif
  #ifdef AMIGA_INTUITION /* unmaintained/defunct */
      { "amii", "Amiga Intuition" },
  #endif
  #ifdef GEM_GRAPHICS /* defunct Atari interface */
      { "Gem", "Gem" },
  #endif
  #ifdef MSWIN_GRAPHICS /* win32 */
      { "mswin", "mswin" },
  #endif
  #ifdef BEOS_GRAPHICS /* unmaintained/defunct */
      { "BeOS", "BeOS InterfaceKit" },
  #endif
      { 0, 0 }
  };
  
  static void
  windowing_sanity()
  {
  #ifndef DEFAULT_WINDOW_SYS
      /* pre-standard compilers didn't support #error; wait til run-time */
      Fprintf(stderr,
              "Configuration error: DEFAULT_WINDOW_SYS is not defined.\n");
      exit(EXIT_FAILURE);
  /*NOTREACHED*/
  
  /* put in a dummy value so that do_options() will compile and makedefs
     will build, otherwise the message above won't ever get delivered */
  #define DEFAULT_WINDOW_SYS "<undefined>"
  #else  /*DEFAULT_WINDOW_SYS*/
  
      if (!window_opts[0].id) {
          Fprintf(stderr, "Configuration error: no windowing systems "
                          "(TTY_GRAPHICS, &c) enabled.\n");
          exit(EXIT_FAILURE);
      }
  
      {
          int i;
  
          for (i = 0; window_opts[i].id; ++i)
              if (!strcmp(window_opts[i].id, DEFAULT_WINDOW_SYS))
                  break;
          if (!window_opts[i].id) { /* went through whole list without a match */
              Fprintf(stderr, "Configuration error: DEFAULT_WINDOW_SYS (%s)\n",
                      DEFAULT_WINDOW_SYS);
              Fprintf(stderr,
                      " does not match any enabled windowing system (%s%s).\n",
                      window_opts[0].id, window_opts[1].id ? ", &c" : "");
              exit(EXIT_FAILURE);
          }
      }
  #endif /*DEFAULT_WINDOW_SYS*/
  }
  
  static const char opt_indent[] = "    ";
  
  static void
  opt_out_words(str, length_p)
  char *str; /* input, but modified during processing */
  int *length_p; /* in/out */
  {
      char *word;
  
      while (*str) {
          word = index(str, ' ');
  #if 0
          /* treat " (" as unbreakable space */
          if (word && *(word + 1) == '(')
              word = index(word + 1,  ' ');
  #endif
          if (word)
              *word = '\0';
          if (*length_p + (int) strlen(str) > COLNO - 5)
              Fprintf(ofp, "\n%s", opt_indent),
                  *length_p = (int) strlen(opt_indent);
          else
              Fprintf(ofp, " "), (*length_p)++;
          Fprintf(ofp, "%s", str), *length_p += (int) strlen(str);
          str += strlen(str) + (word ? 1 : 0);
      }
  }
  
  void
  do_options()
  {
      char buf[BUFSZ];
      int i, length, winsyscnt;
  
      windowing_sanity();
  
      filename[0] = '\0';
  #ifdef FILE_PREFIX
      Strcat(filename, file_prefix);
  #endif
      Sprintf(eos(filename), DATA_TEMPLATE, OPTIONS_FILE);
      if (!(ofp = fopen(filename, WRTMODE))) {
          perror(filename);
          exit(EXIT_FAILURE);
      }
  
      build_savebones_compat_string();
      Fprintf(ofp, "\n%sNetHack version %d.%d.%d%s\n",
              opt_indent,
              VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL,
  #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
  #if (NH_DEVEL_STATUS == NH_STATUS_BETA)
              " [beta]"
  #else
  #if (NH_DEVEL_STATUS == NH_STATUS_WIP)
              " [work-in-progress]"
  #else
              " [post-release]"
  #endif
  #endif
  #else
              ""
  #endif
              );
  
      Fprintf(ofp, "\nOptions compiled into this edition:\n");
      length = COLNO + 1; /* force 1st item onto new line */
      for (i = 0; i < SIZE(build_opts); i++) {
          opt_out_words(strcat(strcpy(buf, build_opts[i]),
                               (i < SIZE(build_opts) - 1) ? "," : "."),
                        &length);
      }
      Fprintf(ofp, "\n"); /* terminate last line of words */
  
      winsyscnt = SIZE(window_opts) - 1;
      Fprintf(ofp, "\nSupported windowing system%s:\n",
              (winsyscnt > 1) ? "s" : "");
      length = COLNO + 1; /* force 1st item onto new line */
      for (i = 0; i < winsyscnt; i++) {
          Sprintf(buf, "\"%s\"", window_opts[i].id);
          if (strcmp(window_opts[i].name, window_opts[i].id))
              Sprintf(eos(buf), " (%s)", window_opts[i].name);
          /*
           * 1 : foo.
           * 2 : foo and bar  (note no period; comes from 'with default' below)
           * 3+: for, bar, and quux
           */
          opt_out_words(strcat(buf, (winsyscnt == 1) ? "." /* no 'default' */
                                    : (winsyscnt == 2 && i == 0) ? " and"
                                      : (i == winsyscnt - 2) ? ", and"
                                        : ","),
                        &length);
      }
      if (winsyscnt > 1) {
          Sprintf(buf, "with a default of \"%s\".", DEFAULT_WINDOW_SYS);
          opt_out_words(buf, &length);
      }
      Fprintf(ofp, "\n"); /* terminate last line of words */
  
      /* end with a blank line */
      Fprintf(ofp, "\n");
      Fclose(ofp);
      return;
  }
  
  /* routine to decide whether to discard something from data.base */
  static boolean
  d_filter(line)
  char *line;
  {
      if (*line == '#')
          return TRUE; /* ignore comment lines */
      return FALSE;
  }
  
  /*
   *
       New format (v3.1) of 'data' file which allows much faster lookups [pr]
  "do not edit"           first record is a comment line
  01234567                hexadecimal formatted offset to text area
  name-a                  first name of interest
  123,4                   offset to name's text, and number of lines for it
  name-b                  next name of interest
  name-c                  multiple names which share same description also
  456,7                   share a single offset,count line
  .                       sentinel to mark end of names
  789,0                   dummy record containing offset, count of EOF
  text-a                  4 lines of descriptive text for name-a
  text-a                  at file position 0x01234567L + 123L
  text-a
  text-a
  text-b/text-c           7 lines of text for names-b and -c
  text-b/text-c           at fseek(0x01234567L + 456L)
  ...
   *
   */
  
  void
  do_data()
  {
      char infile[60], tempfile[60];
      boolean ok;
      long txt_offset;
      int entry_cnt, line_cnt;
      char *line;
  
      Sprintf(tempfile, DATA_TEMPLATE, "database.tmp");
      filename[0] = '\0';
  #ifdef FILE_PREFIX
      Strcat(filename, file_prefix);
  #endif
      Sprintf(eos(filename), DATA_TEMPLATE, DATA_FILE);
      Sprintf(infile, DATA_IN_TEMPLATE, DATA_FILE);
  #ifdef SHORT_FILENAMES
      Strcat(infile, ".bas");
  #else
      Strcat(infile, ".base");
  #endif
      if (!(ifp = fopen(infile, RDTMODE))) { /* data.base */
          perror(infile);
          exit(EXIT_FAILURE);
      }
      if (!(ofp = fopen(filename, WRTMODE))) { /* data */
          perror(filename);
          Fclose(ifp);
          exit(EXIT_FAILURE);
      }
      if (!(tfp = fopen(tempfile, WRTMODE))) { /* database.tmp */
          perror(tempfile);
          Fclose(ifp);
          Fclose(ofp);
          Unlink(filename);
          exit(EXIT_FAILURE);
      }
  
      /* output a dummy header record; we'll rewind and overwrite it later */
      Fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, 0L);
  
      entry_cnt = line_cnt = 0;
      /* read through the input file and split it into two sections */
      while ((line = fgetline(ifp)) != 0) {
          if (d_filter(line)) {
              free(line);
              continue;
          }
          if (*line > ' ') { /* got an entry name */
              /* first finish previous entry */
              if (line_cnt)
                  Fprintf(ofp, "%d\n", line_cnt), line_cnt = 0;
              /* output the entry name */
              (void) fputs(line, ofp);
              entry_cnt++;        /* update number of entries */
          } else if (entry_cnt) { /* got some descriptive text */
              /* update previous entry with current text offset */
              if (!line_cnt)
                  Fprintf(ofp, "%ld,", ftell(tfp));
              /* save the text line in the scratch file */
              (void) fputs(line, tfp);
              line_cnt++; /* update line counter */
          }
          free(line);
      }
      /* output an end marker and then record the current position */
      if (line_cnt)
          Fprintf(ofp, "%d\n", line_cnt);
      Fprintf(ofp, ".\n%ld,%d\n", ftell(tfp), 0);
      txt_offset = ftell(ofp);
      Fclose(ifp); /* all done with original input file */
  
      /* reprocess the scratch file; 1st format an error msg, just in case */
      line = malloc(BUFSZ + MAXFNAMELEN);
      Sprintf(line, "rewind of \"%s\"", tempfile);
      if (rewind(tfp) != 0)
          goto dead_data;
      free(line);
      /* copy all lines of text from the scratch file into the output file */
      while ((line = fgetline(tfp)) != 0) {
          (void) fputs(line, ofp);
          free(line);
      }
  
      /* finished with scratch file */
      Fclose(tfp);
      Unlink(tempfile); /* remove it */
  
      /* update the first record of the output file; prepare error msg 1st */
      line = malloc(BUFSZ + MAXFNAMELEN);
      Sprintf(line, "rewind of \"%s\"", filename);
      ok = (rewind(ofp) == 0);
      if (ok) {
          Sprintf(line, "header rewrite of \"%s\"", filename);
          ok = (fprintf(ofp, "%s%08lx\n", Dont_Edit_Data,
                        (unsigned long) txt_offset) >= 0);
      }
      if (!ok) {
      dead_data:
          perror(line); /* report the problem */
          free(line);
          /* close and kill the aborted output file, then give up */
          Fclose(ofp);
          Unlink(filename);
          exit(EXIT_FAILURE);
      }
      free(line);
  
      /* all done */
      Fclose(ofp);
  
      return;
  }
  
  /* routine to decide whether to discard something from oracles.txt */
  static boolean
  h_filter(line)
  char *line;
  {
      static boolean skip = FALSE;
      char *tag;
  
      SpinCursor(3);
  
      if (*line == '#')
          return TRUE; /* ignore comment lines */
  
      tag = malloc(strlen(line));
      if (sscanf(line, "----- %s", tag) == 1) {
          skip = FALSE;
      } else if (skip && !strncmp(line, "-----", 5))
          skip = FALSE;
      free(tag);
      return skip;
  }
  
  static const char *special_oracle[] = {
      "\"...it is rather disconcerting to be confronted with the",
      "following theorem from [Baker, Gill, and Solovay, 1975].", "",
      "Theorem 7.18  There exist recursive languages A and B such that",
      "  (1)  P(A) == NP(A), and", "  (2)  P(B) != NP(B)", "",
      "This provides impressive evidence that the techniques that are",
      "currently available will not suffice for proving that P != NP or        "
      "  ",
      "that P == NP.\"  [Garey and Johnson, p. 185.]"
  };
  
  /*
     The oracle file consists of a "do not edit" comment, a decimal count N
     and set of N+1 hexadecimal fseek offsets, followed by N multiple-line
     records, separated by "---" lines.  The first oracle is a special case.
     The input data contains just those multi-line records, separated by
     "-----" lines.
   */
  
  void
  do_oracles()
  {
      char infile[60], tempfile[60];
      boolean in_oracle, ok;
      long fpos;
      unsigned long txt_offset, offset;
      int oracle_cnt;
      register int i;
      char *line;
  
      Sprintf(tempfile, DATA_TEMPLATE, "oracles.tmp");
      filename[0] = '\0';
  #ifdef FILE_PREFIX
      Strcat(filename, file_prefix);
  #endif
      Sprintf(eos(filename), DATA_TEMPLATE, ORACLE_FILE);
      Sprintf(infile, DATA_IN_TEMPLATE, ORACLE_FILE);
      Strcat(infile, ".txt");
      if (!(ifp = fopen(infile, RDTMODE))) {
          perror(infile);
          exit(EXIT_FAILURE);
      }
      if (!(ofp = fopen(filename, WRTMODE))) {
          perror(filename);
          Fclose(ifp);
          exit(EXIT_FAILURE);
      }
      if (!(tfp = fopen(tempfile, WRTMODE))) { /* oracles.tmp */
          perror(tempfile);
          Fclose(ifp);
          Fclose(ofp);
          Unlink(filename);
          exit(EXIT_FAILURE);
      }
  
      /* output a dummy header record; we'll rewind and overwrite it later */
      Fprintf(ofp, "%s%5d\n", Dont_Edit_Data, 0);
  
      /* handle special oracle; it must come first */
      (void) fputs("---\n", tfp);
      offset = (unsigned long) ftell(tfp);
      Fprintf(ofp, "%05lx\n", offset); /* start pos of special oracle */
      for (i = 0; i < SIZE(special_oracle); i++) {
          (void) fputs(xcrypt(special_oracle[i]), tfp);
          (void) fputc('\n', tfp);
      }
      SpinCursor(3);
  
      oracle_cnt = 1;
      (void) fputs("---\n", tfp);
      offset = (unsigned long) ftell(tfp);
      Fprintf(ofp, "%05lx\n", offset); /* start pos of first oracle */
      in_oracle = FALSE;
  
      while ((line = fgetline(ifp)) != 0) {
          SpinCursor(3);
  
          if (h_filter(line)) {
              free(line);
              continue;
          }
          if (!strncmp(line, "-----", 5)) {
              if (!in_oracle) {
                  free(line);
                  continue;
              }
              in_oracle = FALSE;
              oracle_cnt++;
              (void) fputs("---\n", tfp);
              offset = (unsigned long) ftell(tfp);
              Fprintf(ofp, "%05lx\n", offset); /* start pos of this oracle */
          } else {
              in_oracle = TRUE;
              (void) fputs(xcrypt(line), tfp);
          }
          free(line);
      }
  
      if (in_oracle) { /* need to terminate last oracle */
          oracle_cnt++;
          (void) fputs("---\n", tfp);
          offset = (unsigned long) ftell(tfp);
          Fprintf(ofp, "%05lx\n", offset); /* eof position */
      }
  
      /* record the current position */
      txt_offset = (unsigned long) ftell(ofp);
      Fclose(ifp); /* all done with original input file */
  
      /* reprocess the scratch file; 1st format an error msg, just in case */
      line = malloc(BUFSZ + MAXFNAMELEN);
      Sprintf(line, "rewind of \"%s\"", tempfile);
      if (rewind(tfp) != 0)
          goto dead_data;
      free(line);
      /* copy all lines of text from the scratch file into the output file */
      while ((line = fgetline(tfp)) != 0) {
          (void) fputs(line, ofp);
          free(line);
      }
  
      /* finished with scratch file */
      Fclose(tfp);
      Unlink(tempfile); /* remove it */
  
      /* update the first record of the output file; prepare error msg 1st */
      line = malloc(BUFSZ + MAXFNAMELEN);
      Sprintf(line, "rewind of \"%s\"", filename);
      ok = (rewind(ofp) == 0);
      if (ok) {
          Sprintf(line, "header rewrite of \"%s\"", filename);
          ok = (fprintf(ofp, "%s%5d\n", Dont_Edit_Data, oracle_cnt) >= 0);
      }
      if (ok) {
          Sprintf(line, "data rewrite of \"%s\"", filename);
          for (i = 0; i <= oracle_cnt; i++) {
  #ifndef VMS /* alpha/vms v1.0; this fflush seems to confuse ftell */
              if (!(ok = (fflush(ofp) == 0)))
                  break;
  #endif
              if (!(ok = (fpos = ftell(ofp)) >= 0))
                  break;
              if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
                  break;
              if (!(ok = (fscanf(ofp, "%5lx", &offset) == 1)))
                  break;
  #ifdef MAC
  #ifdef __MWERKS__
              /*
              MetroWerks CodeWarrior Pro 1's (AKA CW12) version of MSL
              (ANSI C Libraries) needs this rewind or else the fprintf
              stops working.  This may also be true for CW11, but has
              never been checked.
              */
              rewind(ofp);
  #endif
  #endif
              if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
                  break;
              offset += txt_offset;
              if (!(ok = (fprintf(ofp, "%05lx\n", offset) >= 0)))
                  break;
          }
      }
      if (!ok) {
      dead_data:
          perror(line); /* report the problem */
          free(line);
          /* close and kill the aborted output file, then give up */
          Fclose(ofp);
          Unlink(filename);
          exit(EXIT_FAILURE);
      }
      free(line);
  
      /* all done */
      Fclose(ofp);
  
      return;
  }
  
  void
  do_dungeon()
  {
      char *line;
  
      Sprintf(filename, DATA_IN_TEMPLATE, DGN_I_FILE);
      if (!(ifp = fopen(filename, RDTMODE))) {
          perror(filename);
          exit(EXIT_FAILURE);
      }
      filename[0] = '\0';
  #ifdef FILE_PREFIX
      Strcat(filename, file_prefix);
  #endif
      Sprintf(eos(filename), DGN_TEMPLATE, DGN_O_FILE);
      if (!(ofp = fopen(filename, WRTMODE))) {
          perror(filename);
          exit(EXIT_FAILURE);
      }
      Fprintf(ofp, "%s", Dont_Edit_Data);
  
      tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE);
      grep0(ifp, tfp);
      ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE);
  
      while ((line = fgetline(ifp)) != 0) {
          SpinCursor(3);
  
          if (line[0] == '#') {
              free(line);
              continue; /* discard comments */
          }
          (void) fputs(line, ofp);
          free(line);
      }
      Fclose(ifp);
      Fclose(ofp);
  
      delete_file(DATA_TEMPLATE, "grep.tmp");
      return;
  }
  
  void
  do_monstr()
  {
      /* Don't break anything for ports that haven't been updated. */
      printf("DEPRECATION WARNINGS:\n");
      printf("'makedefs -m' is deprecated.  Remove all references\n");
      printf("  to it from the build process.\n");
      printf("'monstr.c' is deprecated.  Remove all references to\n");
      printf("  it from the build process.\n");
      printf("monstr[] is deprecated.  Replace monstr[x] with\n");
      printf("  mons[x].difficulty\n");
      printf("monstr_init() is deprecated.  Remove all references to it.\n");
  
      /*
       * create the source file, "monstr.c"
       */
      filename[0] = '\0';
  #ifdef FILE_PREFIX
      Strcat(filename, file_prefix);
  #endif
      Sprintf(eos(filename), SOURCE_TEMPLATE, MON_STR_C);
      if (!(ofp = fopen(filename, WRTMODE))) {
          perror(filename);
          exit(EXIT_FAILURE);
      }
      Fprintf(ofp, "%s", Dont_Edit_Code);
      Fprintf(ofp, "#include \"config.h\"\n");
      Fprintf(ofp, "\nconst int monstrXXX[] = {\n");
      Fprintf(ofp, "0};\n");
      Fprintf(ofp, "/*\n");
      Fprintf(ofp, "DEPRECATION WARNINGS:\n");
      Fprintf(ofp, "'makedefs -m' is deprecated.  Remove all references\n");
      Fprintf(ofp, "  to it from the build process.\n");
      Fprintf(ofp, "'monstr.c' is deprecated.  Remove all references to\n");
      Fprintf(ofp, "  it from the build process.\n");
      Fprintf(ofp, "monstr[] is deprecated.  Replace monstr[x] with\n");
      Fprintf(ofp, "  mons[x].difficulty\n");
      Fprintf(ofp, "monstr_init() is deprecated.  Remove all references to it.\n");
      Fprintf(ofp, "*/\n");
  
      Fprintf(ofp, "\nvoid NDECL(monstr_init);\n");
      Fprintf(ofp, "\nvoid\n");
      Fprintf(ofp, "monstr_init()\n");
      Fprintf(ofp, "{\n");
      Fprintf(ofp, "    return;\n");
      Fprintf(ofp, "}\n");
      Fprintf(ofp, "\n/*monstr.c*/\n");
  
      Fclose(ofp);
      return;
  }
  
  void
  do_permonst()
  {
      int i;
      char *c, *nam;
  
      filename[0] = '\0';
  #ifdef FILE_PREFIX
      Strcat(filename, file_prefix);
  #endif
      Sprintf(eos(filename), INCLUDE_TEMPLATE, MONST_FILE);
      if (!(ofp = fopen(filename, WRTMODE))) {
          perror(filename);
          exit(EXIT_FAILURE);
      }
      Fprintf(ofp, "%s", Dont_Edit_Code);
      Fprintf(ofp, "#ifndef PM_H\n#define PM_H\n");
  
      if (use_enum) {
          Fprintf(ofp, "\nenum monnums {");
  #if 0
          /* need #define ENUM_PM for the full NetHack build to include these */
          Fprintf(ofp, "\n        NON_PM = -1,");
          Fprintf(ofp, "\n        LOW_PM = 0,");
  #endif
      }
      for (i = 0; mons[i].mlet; i++) {
          SpinCursor(3);
          if (use_enum)
              Fprintf(ofp, "\n        PM_");
          else
              Fprintf(ofp, "\n#define\tPM_");
          if (mons[i].mlet == S_HUMAN && !strncmp(mons[i].mname, "were", 4))
              Fprintf(ofp, "HUMAN_");
          for (nam = c = tmpdup(mons[i].mname); *c; c++)
              if (*c >= 'a' && *c <= 'z')
                  *c -= (char) ('a' - 'A');
              else if (*c < 'A' || *c > 'Z')
                  *c = '_';
          if (use_enum)
              Fprintf(ofp, "%s = %d,", nam, i);
          else
              Fprintf(ofp, "%s\t%d", nam, i);
      }
      if (use_enum) {
          Fprintf(ofp, "\n\n        NUMMONS = %d", i);
          Fprintf(ofp, "\n};\n");
      } else {
          Fprintf(ofp, "\n\n#define\tNUMMONS\t%d\n", i);
      }
      Fprintf(ofp, "\n#endif /* PM_H */\n");
      Fclose(ofp);
      return;
  }
  
  /*      Start of Quest text file processing. */
  #include "qtext.h"
  
  static struct qthdr qt_hdr;
  static struct msghdr msg_hdr[N_HDR];
  static struct qtmsg *curr_msg;
  
  static int qt_line;
  
  static boolean in_msg;
  #define NO_MSG 1 /* strlen of a null line returned by fgets() */
  
  static boolean
  qt_comment(s)
  char *s;
  {
      if (s[0] == '#')
          return  TRUE;
      return (boolean) (!in_msg && strlen(s) == NO_MSG);
  }
  
  static boolean
  qt_control(s)
  char *s;
  {
      return (boolean) (s[0] == '%' && (s[1] == 'C' || s[1] == 'E'));
  }
  
  static int
  get_hdr(code)
  char *code;
  {
      int i;
  
      for (i = 0; i < qt_hdr.n_hdr; i++)
          if (!strncmp(code, qt_hdr.id[i], LEN_HDR))
              return ++i;
  
      return 0;
  }
  
  static boolean
  new_id(code)
  char *code;
  {
      if (qt_hdr.n_hdr >= N_HDR) {
          Fprintf(stderr, OUT_OF_HEADERS, qt_line);
          return FALSE;
      }
  
      strncpy(&qt_hdr.id[qt_hdr.n_hdr][0], code, LEN_HDR);
      msg_hdr[qt_hdr.n_hdr].n_msg = 0;
      qt_hdr.offset[qt_hdr.n_hdr++] = 0L;
      return TRUE;
  }
  
  static boolean
  known_msg(num, id)
  int num, id;
  {
      int i;
  
      for (i = 0; i < msg_hdr[num].n_msg; i++)
          if (msg_hdr[num].qt_msg[i].msgnum == id)
              return TRUE;
  
      return FALSE;
  }
  
  static void
  new_msg(s, num, id)
  char *s;
  int num, id;
  {
      struct qtmsg *qt_msg;
  
      if (msg_hdr[num].n_msg >= N_MSG) {
          Fprintf(stderr, OUT_OF_MESSAGES, qt_line);
      } else {
          qt_msg = &(msg_hdr[num].qt_msg[msg_hdr[num].n_msg++]);
          qt_msg->msgnum = id;
          qt_msg->delivery = s[2];
          qt_msg->offset = qt_msg->size = qt_msg->summary_size = 0L;
  
          curr_msg = qt_msg;
      }
  }
  
  /* check %E record for "[summary text]" that nethack can stuff into the
     message history buffer when delivering text via window instead of pline */
  static char *
  valid_qt_summary(s, parsing)
  char *s;         /* end record: "%E" optionally followed by " [summary]" */
  boolean parsing; /* curr_msg is valid iff this is True */
  {
      static char summary[BUFSZ];
      char *p;
  
      if (*s != '%' || *(s + 1) != 'E')
          return (char *) 0;
      if ((p = index(s, '[')) == 0)
          return (char *) 0;
      /* note: opening '[' and closing ']' will be retained in the output;
         anything after ']' will be discarded by putting a newline there */
      Strcpy(summary, p);
  
      /* have an opening bracket; summary[] holds it and all text that follows
       */
      p = eos(summary);
      /* find closing bracket */
      while (p > summary && *(p - 1) != ']')
          --p;
  
      if (p == summary) {
          /* we backed up all the way to the start without finding a bracket */
          if (parsing) /* malformed summary */
              Fprintf(stderr, MAL_SUM, qt_line);
      } else if (p == summary + 1) {
          ;    /* ignore empty [] */
      } else { /* got something */
               /* p points one spot past ']', usually to '\n';
                  we need to include the \n as part of the size */
          if (parsing) {
              /* during the writing pass we won't be able to recheck
                 delivery, so any useless summary for a pline mode
                 message has to be carried along to the output file */
              if (curr_msg->delivery == 'p')
                  Fprintf(stderr, DUMB_SUM, qt_line);
              /* +1 is for terminating newline */
              curr_msg->summary_size = (long) (p - summary) + 1L;
          } else {
              /* caller is writing rather than just parsing;
                 force newline after the closing bracket */
              Strcpy(p, "\n");
          }
          return summary;
      }
      return (char *) 0;
  }
  
  static void
  do_qt_control(s)
  char *s;
  {
      char code[BUFSZ];
      int num, id = 0;
  
      if (!index(s, '\n'))
          Fprintf(stderr, CTRL_TRUNC, qt_line);
  
      switch (s[1]) {
      case 'C':
          if (in_msg) {
              Fprintf(stderr, CREC_IN_MSG, qt_line);
              break;
          } else {
              in_msg = TRUE;
              if (sscanf(&s[4], "%s %5d", code, &id) != 2) {
                  Fprintf(stderr, UNREC_CREC, qt_line);
                  break;
              }
              num = get_hdr(code);
              if (!num && !new_id(code))
                  break;
              num = get_hdr(code) - 1;
              if (known_msg(num, id))
                  Fprintf(stderr, DUP_MSG, qt_line);
              else
                  new_msg(s, num, id);
          }
          break;
  
      case 'E':
          if (!in_msg) {
              Fprintf(stderr, END_NOT_IN_MSG, qt_line);
          } else {
              /* sets curr_msg->summary_size if applicable */
              (void) valid_qt_summary(s, TRUE);
              in_msg = FALSE;
          }
          break;
  
      default:
          Fprintf(stderr, UNREC_CREC, qt_line);
          break;
      }
  }
  
  static void
  do_qt_text(s)
  char *s;
  {
      if (!in_msg) {
          Fprintf(stderr, TEXT_NOT_IN_MSG, qt_line);
      } else if (!index(s, '\n')) {
          Fprintf(stderr, TEXT_TRUNC, qt_line);
      }
  
      curr_msg->size += strlen(s);
      return;
  }
  
  static void
  adjust_qt_hdrs()
  {
      int i, j;
      long count = 0L, hdr_offset = sizeof(int)
                                    + (sizeof(char) * LEN_HDR + sizeof(long))
                                          * qt_hdr.n_hdr;
  
      for (i = 0; i < qt_hdr.n_hdr; i++) {
          qt_hdr.offset[i] = hdr_offset;
          hdr_offset += sizeof(int) + sizeof(struct qtmsg) * msg_hdr[i].n_msg;
      }
  
      for (i = 0; i < qt_hdr.n_hdr; i++)
          for (j = 0; j < msg_hdr[i].n_msg; j++) {
              msg_hdr[i].qt_msg[j].offset = hdr_offset + count;
              count +=
                  msg_hdr[i].qt_msg[j].size + msg_hdr[i].qt_msg[j].summary_size;
          }
      return;
  }
  
  static void
  put_qt_hdrs()
  {
      int i;
  
      /*
       *  The main header record.
       */
      if (debug)
          Fprintf(stderr, "%ld: header info.\n", ftell(ofp));
      (void) fwrite((genericptr_t) & (qt_hdr.n_hdr), sizeof(int), 1, ofp);
      (void) fwrite((genericptr_t) & (qt_hdr.id[0][0]), sizeof(char) * LEN_HDR,
                    qt_hdr.n_hdr, ofp);
      (void) fwrite((genericptr_t) & (qt_hdr.offset[0]), sizeof(long),
                    qt_hdr.n_hdr, ofp);
      if (debug) {
          for (i = 0; i < qt_hdr.n_hdr; i++)
              Fprintf(stderr, "%s @ %ld, ", qt_hdr.id[i], qt_hdr.offset[i]);
          Fprintf(stderr, "\n");
      }
  
      /*
       *  The individual class headers.
       */
      for (i = 0; i < qt_hdr.n_hdr; i++) {
          if (debug)
              Fprintf(stderr, "%ld: %s header info.\n", ftell(ofp),
                      qt_hdr.id[i]);
          (void) fwrite((genericptr_t) & (msg_hdr[i].n_msg), sizeof(int), 1,
                        ofp);
          (void) fwrite((genericptr_t) & (msg_hdr[i].qt_msg[0]),
                        sizeof(struct qtmsg), msg_hdr[i].n_msg, ofp);
          if (debug) {
              int j;
  
              for (j = 0; j < msg_hdr[i].n_msg; j++) {
                  Fprintf(stderr, "msg %d @ %ld (%ld)",
                          msg_hdr[i].qt_msg[j].msgnum,
                          msg_hdr[i].qt_msg[j].offset,
                          msg_hdr[i].qt_msg[j].size);
                  if (msg_hdr[i].qt_msg[j].summary_size)
                      Fprintf(stderr, " [%ld]",
                              msg_hdr[i].qt_msg[j].summary_size);
                  Fprintf(stderr, "\n");
              }
          }
      }
  }
  
  void
  do_questtxt()
  {
      char *line;
  
      Sprintf(filename, DATA_IN_TEMPLATE, QTXT_I_FILE);
      if (!(ifp = fopen(filename, RDTMODE))) {
          perror(filename);
          exit(EXIT_FAILURE);
      }
  
      filename[0] = '\0';
  #ifdef FILE_PREFIX
      Strcat(filename, file_prefix);
  #endif
      Sprintf(eos(filename), DATA_TEMPLATE, QTXT_O_FILE);
      if (!(ofp = fopen(filename, WRBMODE))) {
          perror(filename);
          Fclose(ifp);
          exit(EXIT_FAILURE);
      }
  
      qt_hdr.n_hdr = 0;
      qt_line = 0;
      in_msg = FALSE;
  
      while ((line = fgetline(ifp)) != 0) {
          SpinCursor(3);
  
          qt_line++;
          if (qt_control(line))
              do_qt_control(line);
          else if (qt_comment(line)) {
              free(line);
              continue;
          } else
              do_qt_text(line);
          free(line);
      }
  
      (void) rewind(ifp);
      in_msg = FALSE;
      adjust_qt_hdrs();
      put_qt_hdrs();
      while ((line = fgetline(ifp)) != 0) {
          if (qt_control(line)) {
              char *summary_p = 0;
  
              in_msg = (line[1] == 'C');
              if (!in_msg)
                  summary_p = valid_qt_summary(line, FALSE);
              /* don't write anything unless we've got a summary */
              if (!summary_p) {
                  free(line);
                  continue;
              }
              /* we have summary text; replace raw %E record with it */
              Strcpy(line, summary_p); /* (guaranteed to fit) */
          } else if (qt_comment(line)) {
              free(line);
              continue;
          }
          if (debug)
              Fprintf(stderr, "%ld: %s", ftell(stdout), line);
          (void) fputs(xcrypt(line), ofp);
          free(line);
      }
      Fclose(ifp);
      Fclose(ofp);
      return;
  }
  
  static char temp[32];
  
  static char *limit(name, pref) /* limit a name to 30 characters length */
  char *name;
  int pref;
  {
      (void) strncpy(temp, name, pref ? 26 : 30);
      temp[pref ? 26 : 30] = 0;
      return temp;
  }
  
  void
  do_objs()
  {
      int i, sum = 0;
      char *c, *objnam;
      int nspell = 0;
      int prefix = 0;
      char class = '\0';
      boolean sumerr = FALSE;
  
      filename[0] = '\0';
  #ifdef FILE_PREFIX
      Strcat(filename, file_prefix);
  #endif
      Sprintf(eos(filename), INCLUDE_TEMPLATE, ONAME_FILE);
      if (!(ofp = fopen(filename, WRTMODE))) {
          perror(filename);
          exit(EXIT_FAILURE);
      }
      Fprintf(ofp, "%s", Dont_Edit_Code);
      Fprintf(ofp, "#ifndef ONAMES_H\n#define ONAMES_H\n\n");
  
      for (i = 0; !i || objects[i].oc_class != ILLOBJ_CLASS; i++) {
          SpinCursor(3);
  
          objects[i].oc_name_idx = objects[i].oc_descr_idx = i; /* init */
          if (!(objnam = tmpdup(OBJ_NAME(objects[i]))))
              continue;
  
          /* make sure probabilities add up to 1000 */
          if (objects[i].oc_class != class) {
              if (sum && sum != 1000) {
                  Fprintf(stderr, "prob error for class %d (%d%%)", class, sum);
                  (void) fflush(stderr);
                  sumerr = TRUE;
              }
              class = objects[i].oc_class;
              sum = 0;
          }
  
          for (c = objnam; *c; c++)
              if (*c >= 'a' && *c <= 'z')
                  *c -= (char) ('a' - 'A');
              else if (*c < 'A' || *c > 'Z')
                  *c = '_';
  
          switch (class) {
          case WAND_CLASS:
              Fprintf(ofp, "#define\tWAN_");
              prefix = 1;
              break;
          case RING_CLASS:
              Fprintf(ofp, "#define\tRIN_");
              prefix = 1;
              break;
          case POTION_CLASS:
              Fprintf(ofp, "#define\tPOT_");
              prefix = 1;
              break;
          case SPBOOK_CLASS:
              Fprintf(ofp, "#define\tSPE_");
              prefix = 1;
              nspell++;
              break;
          case SCROLL_CLASS:
              Fprintf(ofp, "#define\tSCR_");
              prefix = 1;
              break;
          case AMULET_CLASS:
              /* avoid trouble with stupid C preprocessors */
              Fprintf(ofp, "#define\t");
              if (objects[i].oc_material == PLASTIC) {
                  Fprintf(ofp, "FAKE_AMULET_OF_YENDOR\t%d\n", i);
                  prefix = -1;
                  break;
              }
              break;
          case GEM_CLASS:
              /* avoid trouble with stupid C preprocessors */
              if (objects[i].oc_material == GLASS) {
                  Fprintf(ofp, "/* #define\t%s\t%d */\n", objnam, i);
                  prefix = -1;
                  break;
              }
              /*FALLTHRU*/
          default:
              Fprintf(ofp, "#define\t");
          }
          if (prefix >= 0)
              Fprintf(ofp, "%s\t%d\n", limit(objnam, prefix), i);
          prefix = 0;
  
          sum += objects[i].oc_prob;
      }
  
      /* check last set of probabilities */
      if (sum && sum != 1000) {
          Fprintf(stderr, "prob error for class %d (%d%%)", class, sum);
          (void) fflush(stderr);
          sumerr = TRUE;
      }
  
      Fprintf(ofp, "#define\tLAST_GEM\t(JADE)\n");
      Fprintf(ofp, "#define\tMAXSPELL\t%d\n", nspell + 1);
      Fprintf(ofp, "#define\tNUM_OBJECTS\t%d\n", i);
  
      Fprintf(ofp, "\n/* Artifacts (unique objects) */\n\n");
  
      for (i = 1; artifact_names[i]; i++) {
          SpinCursor(3);
  
          for (c = objnam = tmpdup(artifact_names[i]); *c; c++)
              if (*c >= 'a' && *c <= 'z')
                  *c -= (char) ('a' - 'A');
              else if (*c < 'A' || *c > 'Z')
                  *c = '_';
  
          if (!strncmp(objnam, "THE_", 4))
              objnam += 4;
          /* fudge _platinum_ YENDORIAN EXPRESS CARD */
          if (!strncmp(objnam, "PLATINUM_", 9))
              objnam += 9;
          Fprintf(ofp, "#define\tART_%s\t%d\n", limit(objnam, 1), i);
      }
  
      Fprintf(ofp, "#define\tNROFARTIFACTS\t%d\n", i - 1);
      Fprintf(ofp, "\n#endif /* ONAMES_H */\n");
      Fclose(ofp);
      if (sumerr)
          exit(EXIT_FAILURE);
      return;
  }
  
  /* Read one line from input, up to and including the next newline
   * character. Returns a pointer to the heap-allocated string, or a
   * null pointer if no characters were read.
   */
  static char *
  fgetline(fd)
  FILE *fd;
  {
      static const int inc = 256;
      int len = inc;
      char *c = malloc(len), *ret;
  
      for (;;) {
          ret = fgets(c + len - inc, inc, fd);
          if (!ret) {
              free(c);
              c = NULL;
              break;
          } else if (index(c, '\n')) {
              /* normal case: we have a full line */
              break;
          }
          len += inc;
          c = realloc(c, len);
      }
      return c;
  }
  
  static char *
  tmpdup(str)
  const char *str;
  {
      static char buf[128];
  
      if (!str)
          return (char *) 0;
      (void) strncpy(buf, str, 127);
      return buf;
  }
  
  static char *
  eos(str)
  char *str;
  {
      while (*str)
          str++;
      return str;
  }
  
  /*
   * macro used to control vision algorithms:
   *      VISION_TABLES => generate tables
   */
  
  void
  do_vision()
  {
  #ifdef VISION_TABLES
      int i, j;
  
      /* Everything is clear.  xclear may be malloc'ed.
       * Block the upper left corner (BLOCK_HEIGHTxBLOCK_WIDTH)
       */
      for (i = 0; i < MAX_ROW; i++)
          for (j = 0; j < MAX_COL; j++)
              if (i < BLOCK_HEIGHT && j < BLOCK_WIDTH)
                  xclear[i][j] = '\000';
              else
                  xclear[i][j] = '\001';
  #endif /* VISION_TABLES */
  
      SpinCursor(3);
  
      /*
       * create the include file, "vis_tab.h"
       */
      filename[0] = '\0';
  #ifdef FILE_PREFIX
      Strcat(filename, file_prefix);
  #endif
      Sprintf(eos(filename), INCLUDE_TEMPLATE, VIS_TAB_H);
      if (!(ofp = fopen(filename, WRTMODE))) {
          perror(filename);
          exit(EXIT_FAILURE);
      }
      Fprintf(ofp, "%s", Dont_Edit_Code);
      Fprintf(ofp, "#ifdef VISION_TABLES\n");
  #ifdef VISION_TABLES
      H_close_gen();
      H_far_gen();
  #endif /* VISION_TABLES */
      Fprintf(ofp, "\n#endif /* VISION_TABLES */\n");
      Fclose(ofp);
  
      SpinCursor(3);
  
      /*
       * create the source file, "vis_tab.c"
       */
      filename[0] = '\0';
  #ifdef FILE_PREFIX
      Strcat(filename, file_prefix);
  #endif
      Sprintf(eos(filename), SOURCE_TEMPLATE, VIS_TAB_C);
      if (!(ofp = fopen(filename, WRTMODE))) {
          perror(filename);
          /* creating vis_tab.c failed; remove the vis_tab.h we just made */
          filename[0] = '\0';
  #ifdef FILE_PREFIX
          Strcat(filename, file_prefix);
  #endif
          Sprintf(eos(filename), INCLUDE_TEMPLATE, VIS_TAB_H);
          Unlink(filename);
          exit(EXIT_FAILURE);
      }
      Fprintf(ofp, "%s", Dont_Edit_Code);
      Fprintf(ofp, "#include \"config.h\"\n");
      Fprintf(ofp, "#ifdef VISION_TABLES\n");
      Fprintf(ofp, "#include \"vis_tab.h\"\n");
  
      SpinCursor(3);
  
  #ifdef VISION_TABLES
      C_close_gen();
      C_far_gen();
      Fprintf(ofp, "\nvoid vis_tab_init() { return; }\n");
  #endif /* VISION_TABLES */
  
      SpinCursor(3);
  
      Fprintf(ofp, "\n#endif /* VISION_TABLES */\n");
      Fprintf(ofp, "\n/*vis_tab.c*/\n");
  
      Fclose(ofp);
      return;
  }
  
  #ifdef VISION_TABLES
  
  /*--------------  vision tables  --------------*\
   *
   *  Generate the close and far tables.  This is done by setting up a
   *  fake dungeon and moving our source to different positions relative
   *  to a block and finding the first/last visible position.  The fake
   *  dungeon is all clear execpt for the upper left corner (BLOCK_HEIGHT
   *  by BLOCK_WIDTH) is blocked.  Then we move the source around relative
   *  to the corner of the block.  For each new position of the source
   *  we check positions on rows "kittycorner" from the source.  We check
   *  positions until they are either in sight or out of sight (depends on
   *  which table we are generating).  The picture below shows the setup
   *  for the generation of the close table.  The generation of the far
   *  table would switch the quadrants of the '@' and the "Check rows
   *  here".
   *
   *
   *  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
   *  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
   *  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,, Check rows here ,,,,,,,,,,,,
   *  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
   *  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXB,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
   *  ...............................
   *  ...............................
   *  .........@.....................
   *  ...............................
   *
   *      Table generation figure (close_table).  The 'X's are blocked points.
   *      The 'B' is a special blocked point.  The '@' is the source.  The ','s
   *      are the target area.  The '.' are just open areas.
   *
   *
   *  Example usage of close_table[][][].
   *
   *  The table is as follows:
   *
   *      dy = |row of '@' - row of 'B'|  - 1
   *      dx = |col of '@' - col of 'B'|
   *
   *  The first indices are the deltas from the source '@' and the block 'B'.
   *  You must check for the value inside the abs value bars being zero.  If
   *  so then the block is on the same row and you don't need to do a table
   *  lookup.  The last value:
   *
   *      dcy = |row of block - row to be checked|
   *
   *  Is the value of the first visible spot on the check row from the
   *  block column.  So
   *
   *  first visible col = close_table[dy][dx][dcy] + col of 'B'
   *
  \*--------------  vision tables  --------------*/
  
  static void
  H_close_gen()
  {
      Fprintf(ofp, "\n/* Close */\n");
      Fprintf(ofp,
              "#define CLOSE_MAX_SB_DY %2d\t/* |src row - block row| - 1\t*/\n",
              TEST_HEIGHT - 1);
      Fprintf(ofp,
              "#define CLOSE_MAX_SB_DX %2d\t/* |src col - block col|\t*/\n",
              TEST_WIDTH);
      Fprintf(ofp,
              "#define CLOSE_MAX_BC_DY %2d\t/* |block row - check row|\t*/\n",
              TEST_HEIGHT);
      Fprintf(ofp, "typedef struct {\n");
      Fprintf(ofp,
              "    unsigned char close[CLOSE_MAX_SB_DX][CLOSE_MAX_BC_DY];\n");
      Fprintf(ofp, "} close2d;\n");
      Fprintf(ofp, "extern close2d close_table[CLOSE_MAX_SB_DY];\n");
      return;
  }
  
  static void
  H_far_gen()
  {
      Fprintf(ofp, "\n/* Far */\n");
      Fprintf(ofp, "#define FAR_MAX_SB_DY %2d\t/* |src row - block row|\t*/\n",
              TEST_HEIGHT);
      Fprintf(ofp,
              "#define FAR_MAX_SB_DX %2d\t/* |src col - block col| - 1\t*/\n",
              TEST_WIDTH - 1);
      Fprintf(ofp,
              "#define FAR_MAX_BC_DY %2d\t/* |block row - check row| - 1\t*/\n",
              TEST_HEIGHT - 1);
      Fprintf(ofp, "typedef struct {\n");
      Fprintf(ofp, "    unsigned char far_q[FAR_MAX_SB_DX][FAR_MAX_BC_DY];\n");
      Fprintf(ofp, "} far2d;\n");
      Fprintf(ofp, "extern far2d far_table[FAR_MAX_SB_DY];\n");
      return;
  }
  
  static void
  C_close_gen()
  {
      int i, dx, dy;
      int src_row, src_col;     /* source */
      int block_row, block_col; /* block */
      int this_row;
      int no_more;
      const char *delim;
  
      block_row = BLOCK_HEIGHT - 1;
      block_col = BLOCK_WIDTH - 1;
  
      Fprintf(ofp, "\n#ifndef FAR_TABLE_ONLY\n");
      Fprintf(ofp, "\nclose2d close_table[CLOSE_MAX_SB_DY] = {\n");
  #ifndef no_vision_progress
      Fprintf(stderr, "\nclose:");
  #endif
  
      for (dy = 1; dy < TEST_HEIGHT; dy++) {
          src_row = block_row + dy;
          Fprintf(ofp, "/* DY = %2d (- 1)*/\n  {{\n", dy);
  #ifndef no_vision_progress
          Fprintf(stderr, " %2d", dy), (void) fflush(stderr);
  #endif
          for (dx = 0; dx < TEST_WIDTH; dx++) {
              src_col = block_col - dx;
              Fprintf(ofp, "  /*%2d*/ {", dx);
  
              no_more = 0;
              for (this_row = 0; this_row < TEST_HEIGHT; this_row++) {
                  delim = (this_row < TEST_HEIGHT - 1) ? "," : "";
                  if (no_more) {
                      Fprintf(ofp, "%s%s", CLOSE_OFF_TABLE_STRING, delim);
                      continue;
                  }
                  SpinCursor(3);
  
                  /* Find the first column that we can see. */
                  for (i = block_col + 1; i < MAX_COL; i++) {
                      if (clear_path(src_row, src_col, block_row - this_row, i))
                          break;
                  }
  
                  if (i == MAX_COL)
                      no_more = 1;
                  Fprintf(ofp, "%2d%s", i - block_col, delim);
              }
              Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n");
          }
          Fprintf(ofp, "  }},\n");
      }
  
      Fprintf(ofp, "}; /* close_table[] */\n"); /* closing brace for table */
      Fprintf(ofp, "#endif /* !FAR_TABLE_ONLY */\n");
  #ifndef no_vision_progress
      Fprintf(stderr, "\n");
  #endif
      return;
  }
  
  static void
  C_far_gen()
  {
      int i, dx, dy;
      int src_row, src_col;     /* source */
      int block_row, block_col; /* block */
      int this_row;
      const char *delim;
  
      block_row = BLOCK_HEIGHT - 1;
      block_col = BLOCK_WIDTH - 1;
  
      Fprintf(ofp, "\n#ifndef CLOSE_TABLE_ONLY\n");
      Fprintf(ofp, "\nfar2d far_table[FAR_MAX_SB_DY] = {\n");
  #ifndef no_vision_progress
      Fprintf(stderr, "\n_far_:");
  #endif
  
      for (dy = 0; dy < TEST_HEIGHT; dy++) {
          src_row = block_row - dy;
          Fprintf(ofp, "/* DY = %2d */\n  {{\n", dy);
  #ifndef no_vision_progress
          Fprintf(stderr, " %2d", dy), (void) fflush(stderr);
  #endif
          for (dx = 1; dx < TEST_WIDTH; dx++) {
              src_col = block_col + dx;
              Fprintf(ofp, "  /*%2d(-1)*/ {", dx);
  
              for (this_row = block_row + 1; this_row < block_row + TEST_HEIGHT;
                   this_row++) {
                  delim = (this_row < block_row + TEST_HEIGHT - 1) ? "," : "";
  
                  SpinCursor(3);
                  /* Find first col that we can see. */
                  for (i = 0; i <= block_col; i++) {
                      if (clear_path(src_row, src_col, this_row, i))
                          break;
                  }
  
                  if (block_col - i < 0)
                      Fprintf(ofp, "%s%s", FAR_OFF_TABLE_STRING, delim);
                  else
                      Fprintf(ofp, "%2d%s", block_col - i, delim);
              }
              Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n");
          }
          Fprintf(ofp, "  }},\n");
      }
  
      Fprintf(ofp, "}; /* far_table[] */\n"); /* closing brace for table */
      Fprintf(ofp, "#endif /* !CLOSE_TABLE_ONLY */\n");
  #ifndef no_vision_progress
      Fprintf(stderr, "\n");
  #endif
      return;
  }
  
  /*
   *  "Draw" a line from the hero to the given location.  Stop if we hit a
   *  wall.
   *
   *  Generalized integer Bresenham's algorithm (fast line drawing) for
   *  all quadrants.  From _Procedural Elements for Computer Graphics_, by
   *  David F. Rogers.  McGraw-Hill, 1985.
   *
   *  I have tried a little bit of optimization by pulling compares out of
   *  the inner loops.
   *
   *  NOTE:  This had better *not* be called from a position on the
   *  same row as the hero.
   */
  static int
  clear_path(you_row, you_col, y2, x2)
  int you_row, you_col, y2, x2;
  {
      int dx, dy, s1, s2;
      register int i, error, x, y, dxs, dys;
  
      x = you_col;
      y = you_row;
      dx = abs(x2 - you_col);
      dy = abs(y2 - you_row);
      s1 = sign(x2 - you_col);
      s2 = sign(y2 - you_row);
  
      if (s1 == 0) {     /* same column */
          if (s2 == 1) { /* below (larger y2 value) */
              for (i = you_row + 1; i < y2; i++)
                  if (!xclear[i][you_col])
                      return 0;
          } else { /* above (smaller y2 value) */
              for (i = y2 + 1; i < you_row; i++)
                  if (!xclear[i][you_col])
                      return 0;
          }
          return 1;
      }
  
      /*
       *  Lines at 0 and 90 degrees have been weeded out.
       */
      if (dy > dx) {
          error = dx;
          dx = dy;
          dy = error;    /* swap the values */
          dxs = dx << 1; /* save the shifted values */
          dys = dy << 1;
          error = dys - dx; /* NOTE: error is used as a temporary above */
  
          for (i = 0; i < dx; i++) {
              if (!xclear[y][x])
                  return 0; /* plot point */
  
              while (error >= 0) {
                  x += s1;
                  error -= dxs;
              }
              y += s2;
              error += dys;
          }
      } else {
          dxs = dx << 1; /* save the shifted values */
          dys = dy << 1;
          error = dys - dx;
  
          for (i = 0; i < dx; i++) {
              if (!xclear[y][x])
                  return 0; /* plot point */
  
              while (error >= 0) {
                  y += s2;
                  error -= dxs;
              }
              x += s1;
              error += dys;
          }
      }
      return 1;
  }
  #endif /* VISION_TABLES */
  
  #ifdef STRICT_REF_DEF
  NEARDATA struct flag flags;
  #ifdef ATTRIB_H
  struct attribs attrmax, attrmin;
  #endif
  #endif /* STRICT_REF_DEF */
  
  /*makedefs.c*/
  