OSDN Git Service

upgrade to 3.6.2
[jnethack/source.git] / util / makedefs.c
1 /* NetHack 3.6  makedefs.c  $NHDT-Date: 1557254354 2019/05/07 18:39:14 $  $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.145 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Kenneth Lorber, Kensington, Maryland, 2015. */
4 /* Copyright (c) M. Stephenson, 1990, 1991.                       */
5 /* Copyright (c) Dean Luick, 1990.                                */
6 /* NetHack may be freely redistributed.  See license for details. */
7
8 #define MAKEDEFS_C /* use to conditionally include file sections */
9
10 #include "config.h"
11 #ifdef MONITOR_HEAP
12 #undef free /* makedefs doesn't use the alloc and free in src/alloc.c */
13 #endif
14 #include "permonst.h"
15 #include "objclass.h"
16 #include "monsym.h"
17 #include "artilist.h"
18 #include "dungeon.h"
19 #include "obj.h"
20 #include "monst.h"
21 #include "you.h"
22 #include "context.h"
23 #include "flag.h"
24 #include "dlb.h"
25
26 /* version information */
27 #ifdef SHORT_FILENAMES
28 #include "patchlev.h"
29 #if 1 /*JP*/
30 #include "../japanese/jpatchle.h"
31 #endif
32 #else
33 #include "patchlevel.h"
34 #if 1 /*JP*/
35 #include "../japanese/jpatchlevel.h"
36 #endif
37 #endif
38
39 #include <ctype.h>
40 #ifdef MAC
41 #if defined(__SC__) || defined(__MRC__) /* MPW compilers */
42 #define MPWTOOL
43 #include <CursorCtl.h>
44 #include <string.h>
45 #else /* MAC without MPWTOOL */
46 #define MACsansMPWTOOL
47 #endif
48 #endif /* MAC */
49
50 #ifndef MPWTOOL
51 #define SpinCursor(x)
52 #endif
53
54 #define Fprintf (void) fprintf
55 #define Fclose (void) fclose
56 #define Unlink (void) unlink
57 #if !defined(AMIGA) || defined(AZTEC_C)
58 #define rewind(fp) fseek((fp), 0L, SEEK_SET) /* guarantee a return value */
59 #endif
60
61 #if defined(UNIX) && !defined(LINT) && !defined(GCC_WARN)
62 static const char SCCS_Id[] UNUSED = "@(#)makedefs.c\t3.6\t2019/05/07";
63 #endif
64
65 /* names of files to be generated */
66 #define DATE_FILE "date.h"
67 #define MONST_FILE "pm.h"
68 #define ONAME_FILE "onames.h"
69 #ifndef OPTIONS_FILE
70 #define OPTIONS_FILE "options"
71 #endif
72 #define ORACLE_FILE "oracles"
73 #define DATA_FILE "data"
74 #define RUMOR_FILE "rumors"
75 #define DGN_I_FILE "dungeon.def"
76 #define DGN_O_FILE "dungeon.pdf"
77 #define MON_STR_C "monstr.c"
78 #define QTXT_I_FILE "quest.txt"
79 #define QTXT_O_FILE "quest.dat"
80 #define VIS_TAB_H "vis_tab.h"
81 #define VIS_TAB_C "vis_tab.c"
82 #define GITINFO_FILE "gitinfo.txt"
83 /* locations for those files */
84 #ifdef AMIGA
85 #define FILE_PREFIX
86 #define INCLUDE_TEMPLATE "NH:include/t.%s"
87 #define SOURCE_TEMPLATE "NH:src/%s"
88 #define DGN_TEMPLATE "NH:dat/%s" /* where dungeon.pdf file goes */
89 #define DATA_TEMPLATE "NH:slib/%s"
90 #define DATA_IN_TEMPLATE "NH:dat/%s"
91 #else /* not AMIGA */
92 #if defined(MAC) && !defined(__MACH__)
93 /* MacOS 9 or earlier */
94 #define INCLUDE_TEMPLATE ":include:%s"
95 #define SOURCE_TEMPLATE ":src:%s"
96 #define DGN_TEMPLATE ":dat:%s" /* where dungeon.pdf file goes */
97 #if __SC__ || __MRC__
98 #define DATA_TEMPLATE ":Dungeon:%s"
99 #else
100 #define DATA_TEMPLATE ":lib:%s"
101 #endif /* __SC__ || __MRC__ */
102 #define DATA_IN_TEMPLATE ":dat:%s"
103 #else /* neither AMIGA nor MAC */
104 #ifdef OS2
105 #define INCLUDE_TEMPLATE "..\\include\\%s"
106 #define SOURCE_TEMPLATE "..\\src\\%s"
107 #define DGN_TEMPLATE "..\\dat\\%s" /* where dungeon.pdf file goes */
108 #define DATA_TEMPLATE "..\\dat\\%s"
109 #define DATA_IN_TEMPLATE "..\\dat\\%s"
110 #else /* not AMIGA, MAC, or OS2 */
111 #define INCLUDE_TEMPLATE "../include/%s"
112 #define SOURCE_TEMPLATE "../src/%s"
113 #define DGN_TEMPLATE "../dat/%s" /* where dungeon.pdf file goes */
114 #define DATA_TEMPLATE "../dat/%s"
115 #define DATA_IN_TEMPLATE "../dat/%s"
116 #endif /* else !OS2 */
117 #endif /* else !MAC */
118 #endif /* else !AMIGA */
119
120 static const char
121     *Dont_Edit_Code =
122         "/* This source file is generated by 'makedefs'.  Do not edit. */\n",
123     *Dont_Edit_Data =
124         "#\tThis data file is generated by 'makedefs'.  Do not edit. \n";
125
126 static struct version_info version;
127
128 /* definitions used for vision tables */
129 #define TEST_WIDTH COLNO
130 #define TEST_HEIGHT ROWNO
131 #define BLOCK_WIDTH (TEST_WIDTH + 10)
132 #define BLOCK_HEIGHT TEST_HEIGHT /* don't need extra spaces */
133 #define MAX_ROW (BLOCK_HEIGHT + TEST_HEIGHT)
134 #define MAX_COL (BLOCK_WIDTH + TEST_WIDTH)
135 /* Use this as an out-of-bound value in the close table.  */
136 #define CLOSE_OFF_TABLE_STRING "99" /* for the close table */
137 #define FAR_OFF_TABLE_STRING "0xff" /* for the far table */
138
139 #define sign(z) ((z) < 0 ? -1 : ((z) ? 1 : 0))
140 #ifdef VISION_TABLES
141 static char xclear[MAX_ROW][MAX_COL];
142 #endif
143 /*-end of vision defs-*/
144
145 #define MAXFNAMELEN 600
146
147 static char filename[MAXFNAMELEN];
148
149 #ifdef FILE_PREFIX
150 /* if defined, a first argument not starting with - is
151  * taken as a text string to be prepended to any
152  * output filename generated */
153 char *file_prefix = "";
154 #endif
155
156 #ifdef MACsansMPWTOOL
157 int FDECL(main, (void));
158 #else
159 int FDECL(main, (int, char **));
160 #endif
161 void FDECL(do_makedefs, (char *));
162 void NDECL(do_objs);
163 void NDECL(do_data);
164 void NDECL(do_dungeon);
165 void NDECL(do_date);
166 void NDECL(do_options);
167 void NDECL(do_monstr);
168 void NDECL(do_permonst);
169 void NDECL(do_questtxt);
170 void NDECL(do_rumors);
171 void NDECL(do_oracles);
172 void NDECL(do_vision);
173
174 extern void NDECL(monst_init);   /* monst.c */
175 extern void NDECL(objects_init); /* objects.c */
176
177 static void NDECL(link_sanity_check);
178 static char *FDECL(name_file, (const char *, const char *));
179 static void FDECL(delete_file, (const char *template, const char *));
180 static FILE *FDECL(getfp, (const char *, const char *, const char *));
181 static void FDECL(do_ext_makedefs, (int, char **));
182
183 static void NDECL(make_version);
184 static char *FDECL(version_string, (char *, const char *));
185 static char *FDECL(version_id_string, (char *, const char *));
186 static char *FDECL(bannerc_string, (char *, const char *));
187 static char *FDECL(xcrypt, (const char *));
188 static unsigned long FDECL(read_rumors_file,
189                            (const char *, int *, long *, unsigned long));
190 static boolean FDECL(get_gitinfo, (char *, char *));
191 static void FDECL(do_rnd_access_file, (const char *));
192 static boolean FDECL(d_filter, (char *));
193 static boolean FDECL(h_filter, (char *));
194 static void NDECL(build_savebones_compat_string);
195 static void NDECL(windowing_sanity);
196 static void FDECL(opt_out_words, (char *, int *));
197
198 static boolean FDECL(qt_comment, (char *));
199 static boolean FDECL(qt_control, (char *));
200 static int FDECL(get_hdr, (char *));
201 static boolean FDECL(new_id, (char *));
202 static boolean FDECL(known_msg, (int, int));
203 static void FDECL(new_msg, (char *, int, int));
204 static char *FDECL(valid_qt_summary, (char *, BOOLEAN_P));
205 static void FDECL(do_qt_control, (char *));
206 static void FDECL(do_qt_text, (char *));
207 static void NDECL(adjust_qt_hdrs);
208 static void NDECL(put_qt_hdrs);
209
210 #ifdef VISION_TABLES
211 static void NDECL(H_close_gen);
212 static void NDECL(H_far_gen);
213 static void NDECL(C_close_gen);
214 static void NDECL(C_far_gen);
215 static int FDECL(clear_path, (int, int, int, int));
216 #endif
217
218 static char *FDECL(fgetline, (FILE*));
219 static char *FDECL(tmpdup, (const char *));
220 static char *FDECL(limit, (char *, int));
221 static char *FDECL(eos, (char *));
222 static int FDECL(case_insensitive_comp, (const char *, const char *));
223
224 /* input, output, tmp */
225 static FILE *ifp, *ofp, *tfp;
226
227 #if defined(__BORLANDC__) && !defined(_WIN32)
228 extern unsigned _stklen = STKSIZ;
229 #endif
230
231 #ifdef MACsansMPWTOOL
232 int
233 main(void)
234 {
235     const char *def_options = "odemvpqrshz";
236     char buf[100];
237     int len;
238
239     printf("Enter options to run: [%s] ", def_options);
240     fflush(stdout);
241     fgets(buf, 100, stdin);
242     len = strlen(buf);
243     if (len <= 1)
244         Strcpy(buf, def_options);
245     else
246         buf[len - 1] = 0; /* remove return */
247
248     if (buf[0] == '-' && buf[1] == '-') {
249 #if 0
250         split up buf into words
251         do_ext_makedefs(fakeargc, fakeargv);
252 #else
253         printf("extended makedefs not implemented for Mac OS9\n");
254         exit(EXIT_FAILURE);
255 #endif
256     }
257
258     do_makedefs(buf);
259     exit(EXIT_SUCCESS);
260     return 0;
261 }
262
263 #else /* ! MAC */
264
265 int
266 main(argc, argv)
267 int argc;
268 char *argv[];
269 {
270     if ((argc == 1) ||
271         ((argc != 2)
272 #ifdef FILE_PREFIX
273         && (argc != 3)
274 #endif
275         && !(argv[1][0] == '-' && argv[1][1] == '-'))) {
276         Fprintf(stderr, "Bad arg count (%d).\n", argc - 1);
277         (void) fflush(stderr);
278         return 1;
279     }
280
281 #ifdef FILE_PREFIX
282     if (argc >= 2 && argv[1][0] != '-') {
283         file_prefix = argv[1];
284         argc--;
285         argv++;
286     }
287 #endif
288
289     if (argv[1][0] == '-' && argv[1][1] == '-') {
290         do_ext_makedefs(argc, argv);
291     } else {
292         do_makedefs(&argv[1][1]);
293     }
294     exit(EXIT_SUCCESS);
295     /*NOTREACHED*/
296     return 0;
297 }
298
299 #endif
300
301 static void
302 link_sanity_check()
303 {
304     /* Note:  these initializers don't do anything except guarantee that
305             we're linked properly.
306     */
307     monst_init();
308     objects_init();
309
310 }
311
312 void
313 do_makedefs(options)
314 char *options;
315 {
316     boolean more_than_one;
317
318     link_sanity_check();
319
320     /* construct the current version number */
321     make_version();
322
323     more_than_one = strlen(options) > 1;
324     while (*options) {
325         if (more_than_one)
326             Fprintf(stderr, "makedefs -%c\n", *options);
327
328         switch (*options) {
329         case 'o':
330         case 'O':
331             do_objs();
332             break;
333         case 'd':
334         case 'D':
335             do_data();
336             break;
337         case 'e':
338         case 'E':
339             do_dungeon();
340             break;
341         case 'm':
342         case 'M':
343             do_monstr();
344             break;
345         case 'v':
346         case 'V':
347             do_date();
348             do_options();
349             break;
350         case 'p':
351         case 'P':
352             do_permonst();
353             break;
354         case 'q':
355         case 'Q':
356             do_questtxt();
357             break;
358         case 'r':
359         case 'R':
360             do_rumors();
361             break;
362         case 's':
363         case 'S':
364             do_rnd_access_file(EPITAPHFILE);
365             do_rnd_access_file(ENGRAVEFILE);
366             do_rnd_access_file(BOGUSMONFILE);
367             break;
368         case 'h':
369         case 'H':
370             do_oracles();
371             break;
372         case 'z':
373         case 'Z':
374             do_vision();
375             break;
376
377         default:
378             Fprintf(stderr, "Unknown option '%c'.\n", *options);
379             (void) fflush(stderr);
380             exit(EXIT_FAILURE);
381         }
382         options++;
383     }
384     if (more_than_one)
385         Fprintf(stderr, "Completed.\n"); /* feedback */
386 }
387
388 static char namebuf[1000];
389
390 static char *
391 name_file(template, tag)
392 const char *template;
393 const char *tag;
394 {
395     Sprintf(namebuf, template, tag);
396     return namebuf;
397 }
398
399 static void
400 delete_file(template, tag)
401 const char *template;
402 const char *tag;
403 {
404     char *name = name_file(template, tag);
405
406     Unlink(name);
407 }
408
409 static FILE *
410 getfp(template, tag, mode)
411 const char *template;
412 const char *tag;
413 const char *mode;
414 {
415     char *name = name_file(template, tag);
416     FILE *rv = fopen(name, mode);
417
418     if (!rv) {
419         Fprintf(stderr, "Can't open '%s'.\n", name);
420         exit(EXIT_FAILURE);
421     }
422     return rv;
423 }
424
425 static boolean debug = FALSE;
426
427 static FILE *inputfp;
428 static FILE *outputfp;
429
430 struct grep_var {
431     const char *name;
432     int is_defined; /* 0 undef; 1 defined */
433 };
434 /* struct grep_var grep_vars[] and TODO_* constants in include file: */
435 #include "mdgrep.h"
436
437 static void NDECL(do_grep_showvars);
438 static struct grep_var *FDECL(grepsearch, (const char *));
439 static int FDECL(grep_check_id, (const char *));
440 static void FDECL(grep_show_wstack, (const char *));
441 static char *FDECL(do_grep_control, (char *));
442 static void NDECL(do_grep);
443 static void FDECL(grep0, (FILE *, FILE *));
444
445 static int grep_trace = 0;
446
447 #define IS_OPTION(str) if (!strcmp(&argv[0][2], str))
448 #define CONTINUE    \
449     argv++, argc--; \
450     continue
451 #define CONSUME                              \
452     argv++, argc--;                          \
453     if (argc == 0) {                         \
454         Fprintf(stderr, "missing option\n"); \
455         exit(EXIT_FAILURE);                  \
456     }
457
458 static void
459 do_ext_makedefs(int argc, char **argv)
460 {
461     int todo = 0;
462
463     link_sanity_check();
464
465     argc--;
466     argv++; /* skip program name */
467
468     while (argc) {
469         if (argv[0][0] != '-')
470             break;
471         if (argv[0][1] != '-') {
472             Fprintf(stderr, "Can't mix - and -- options.\n");
473             exit(EXIT_FAILURE);
474         }
475         IS_OPTION("svs") {
476             /* short version string for packaging - note no \n */
477             char buf[100];
478             char delim[10];
479
480             argv++; /* not CONSUME */
481             delim[0] = '\0';
482             if (argv[0])
483                 strcpy(delim, argv[0]);
484             Fprintf(stdout, "%s", version_string(buf, delim));
485             exit(EXIT_SUCCESS);
486         }
487         IS_OPTION("debug") {
488             debug = TRUE;
489             CONTINUE;
490         }
491         IS_OPTION("make") {
492             CONSUME;
493             do_makedefs(argv[0]);
494             exit(EXIT_SUCCESS);
495         }
496         IS_OPTION("input") {
497             CONSUME;
498             if (!strcmp(argv[0], "-")) {
499                 inputfp = stdin;
500             } else {
501                 inputfp = fopen(argv[0], RDTMODE);
502                 if (!inputfp) {
503                     Fprintf(stderr, "Can't open '%s'.\n", argv[0]);
504                     exit(EXIT_FAILURE);
505                 }
506             }
507             CONTINUE;
508         }
509         IS_OPTION("output") {
510             CONSUME;
511             if (!strcmp(argv[0], "-")) {
512                 outputfp = stdout;
513             } else {
514                 outputfp = fopen(argv[0], WRTMODE);
515                 if (!outputfp) {
516                     Fprintf(stderr, "Can't open '%s'.\n", argv[0]);
517                     exit(EXIT_FAILURE);
518                 }
519             }
520             CONTINUE;
521         }
522         IS_OPTION("grep") {
523             if (todo) {
524                 Fprintf(stderr, "Can't do grep and something else.\n");
525                 exit(EXIT_FAILURE);
526             }
527             todo = TODO_GREP;
528             CONTINUE;
529         }
530         IS_OPTION("grep-showvars") {
531             do_grep_showvars();
532             exit(EXIT_SUCCESS);
533         }
534         IS_OPTION("grep-trace") {
535             grep_trace = 1;
536             CONTINUE;
537         }
538         IS_OPTION("grep-define") {
539             struct grep_var *p;
540
541             CONSUME;
542             p = grepsearch(argv[0]);
543             if (p) {
544                 p->is_defined = 1;
545             } else {
546                 Fprintf(stderr, "Unknown symbol '%s'\n", argv[0]);
547                 exit(EXIT_FAILURE);
548             }
549             CONTINUE;
550         }
551         IS_OPTION("grep-undef") {
552             struct grep_var *p;
553
554             CONSUME;
555             p = grepsearch(argv[0]);
556             if (p) {
557                 p->is_defined = 0;
558             } else {
559                 Fprintf(stderr, "Unknown symbol '%s'\n", argv[0]);
560                 exit(EXIT_FAILURE);
561             }
562             CONTINUE;
563         }
564 #ifdef notyet
565         IS_OPTION("help") {
566         }
567 #endif
568         Fprintf(stderr, "Unknown option '%s'.\n", argv[0]);
569         exit(EXIT_FAILURE);
570     }
571     if (argc) {
572         Fprintf(stderr, "unexpected argument '%s'.\n", argv[0]);
573         exit(EXIT_FAILURE);
574     }
575
576     switch (todo) {
577     default:
578         Fprintf(stderr, "Confused about what to do?\n");
579         exit(EXIT_FAILURE);
580     case 0:
581         Fprintf(stderr, "Nothing to do?\n");
582         exit(EXIT_FAILURE);
583     case TODO_GREP:
584         do_grep();
585         break;
586     }
587 }
588
589 #undef IS_OPTION
590 #undef CONTINUE
591 #undef CONSUME
592
593 /*
594  * Filtering syntax:
595  * Any line NOT starting with a caret is either suppressed or passed
596  * through unchanged depending on the current conditional state.
597  *
598  * The default conditional state is printing on.
599  *
600  * Conditionals may be nested.
601  *
602  * makedefs will exit with a EXIT_FAILURE if any errors are detected;
603  * as many errors as possible are detected before giving up.
604  *
605  * Unknown identifiers are treated as TRUE and also as an error to
606  * allow processing to continue past the unknown identifier (note
607  * that "#undef" is different than unknown).
608  *
609  * Any line starting with a caret is a control line; as in C, zero or
610  * more spaces may be embedded in the line almost anywhere; the caret
611  * MUST be in column 1.
612  * (XXX for the moment, no white space is allowed after the caret because
613  * existing lines in the docs look like that.)
614  *
615  * Control lines:
616  *      ^^      a line starting with a (single) literal caret
617  *      ^#      a comment - the line is ignored
618  *      ^?ID    if defined(ID)
619  *      ^!ID    if !defined(ID)
620  *      ^:      else
621  *      ^.      endif
622  */
623 #define GREP_MAGIC '^'
624 #define GREP_STACK_SIZE 100
625 #ifdef notyet
626 static int grep_rewrite = 0; /* need to (possibly) rewrite lines */
627 #endif
628 static int grep_writing = 1; /* need to copy lines to output */
629 static int grep_errors = 0;
630 static int grep_sp = 0;
631 #define ST_LD(old, opp) (((old) ? 1 : 0) | ((opp) ? 2 : 0))
632 #define ST_OLD(v) (((v) & 1) != 0)
633 #define ST_OPP(v) (((v) & 2) != 0)
634 #define ST_ELSE 4
635 static int grep_stack[GREP_STACK_SIZE] = { ST_LD(1, 0) };
636 static int grep_lineno = 0;
637
638 static void
639 do_grep_showvars()
640 {
641     int x;
642
643     for (x = 0; x < SIZE(grep_vars) - 1; x++) {
644         printf("%d\t%s\n", grep_vars[x].is_defined, grep_vars[x].name);
645     }
646 }
647
648 static struct grep_var *
649 grepsearch(name)
650 const char *name;
651 {
652     /* XXX make into binary search */
653     int x = 0;
654
655     while (x < SIZE(grep_vars) - 1) {
656         if (!strcmp(grep_vars[x].name, name))
657             return &grep_vars[x];
658         x++;
659     }
660     return 0;
661 }
662
663 static int
664 grep_check_id(id)
665 const char *id;
666 {
667     struct grep_var *rv;
668
669     while (*id && isspace((uchar) *id))
670         id++;
671     if (!*id) {
672         Fprintf(stderr, "missing identifier in line %d", grep_lineno);
673         grep_errors++;
674         return 0;
675     }
676     rv = grepsearch(id);
677     if (rv) {
678         if (grep_trace) {
679             Fprintf(outputfp, "ID %d %s\n", rv->is_defined, id);
680         }
681         return rv->is_defined;
682     }
683
684     if (grep_trace) {
685         Fprintf(outputfp, "ID U %s\n", id);
686     }
687     Fprintf(stderr, "unknown identifier '%s' in line %d.\n", id, grep_lineno);
688     grep_errors++;
689     return 2; /* So new features can be checked before makedefs
690                * is rebuilt. */
691 }
692
693 static void
694 grep_show_wstack(tag)
695 const char *tag;
696 {
697     int x;
698
699     if (!grep_trace)
700         return;
701
702     Fprintf(outputfp, "%s w=%d sp=%d\t", tag, grep_writing, grep_sp);
703     for (x = grep_sp; x >= 0 && x > grep_sp - 6; x--) {
704         Fprintf(outputfp, "[%d]=%d ", x, grep_stack[x]);
705     }
706     Fprintf(outputfp, "\n");
707 }
708
709 static char *
710 do_grep_control(buf)
711 char *buf;
712 {
713     int isif = 1;
714     char *buf0 = buf;
715 #if 1
716     if (isspace((uchar) buf[0]))
717         return &buf[-1]; /* XXX see docs above */
718 #else
719     while (buf[0] && isspace((uchar) buf[0]))
720         buf++;
721 #endif
722     switch (buf[0]) {
723     case '#': /* comment */
724         break;
725     case '.': /* end of if level */
726         if (grep_sp == 0) {
727             Fprintf(stderr, "unmatched ^. (endif) at line %d.\n",
728                     grep_lineno);
729             grep_errors++;
730         } else {
731             grep_writing = ST_OLD(grep_stack[grep_sp--]);
732             grep_show_wstack("pop");
733         }
734         break;
735     case '!': /* if not ID */
736         isif = 0;
737     /* FALLTHROUGH */
738     case '?': /* if ID */
739         if (grep_sp == GREP_STACK_SIZE - 2) {
740             Fprintf(stderr, "stack overflow at line %d.", grep_lineno);
741             exit(EXIT_FAILURE);
742         }
743         if (grep_writing) {
744             isif = grep_check_id(&buf[1]) ? isif : !isif;
745             grep_stack[++grep_sp] = ST_LD(grep_writing, !isif);
746             grep_writing = isif;
747         } else {
748             grep_stack[++grep_sp] = ST_LD(0, 0);
749             /* grep_writing = 0; */
750         }
751         grep_show_wstack("push");
752         break;
753     case ':': /* else */
754         if (ST_ELSE & grep_stack[grep_sp]) {
755             Fprintf(stderr, "multiple : for same conditional at line %d.\n",
756                     grep_lineno);
757             grep_errors++;
758         }
759         grep_writing = ST_OPP(grep_stack[grep_sp]);
760         grep_stack[grep_sp] |= ST_ELSE;
761         break;
762 #if defined(notyet)
763     case '(': /* start of expression */
764 #endif
765     case GREP_MAGIC: /* ^^ -> ^ */
766         return buf0;
767     default: {
768         char str[10];
769
770         if (isprint((uchar) buf[0])) {
771             str[0] = buf[0];
772             str[1] = '\0';
773         } else {
774             sprintf(str, "0x%02x", buf[0]);
775         }
776         Fprintf(stderr, "unknown control ^%s at line %d.\n", str,
777                 grep_lineno);
778         grep_errors++;
779     } break;
780     }
781     return NULL;
782 }
783
784 #ifdef notyet
785 static void
786 do_grep_rewrite(buf)
787 char *buf;
788 {
789     /* no language features use this yet */
790     return;
791 }
792 #endif
793
794 static void grep0(FILE *, FILE *);
795
796 static void
797 do_grep()
798 {
799     if (!inputfp) {
800         Fprintf(stderr, "--grep requires --input\n");
801     }
802     if (!outputfp) {
803         Fprintf(stderr, "--grep requires --output\n");
804     }
805     if (!inputfp || !outputfp) {
806         exit(EXIT_FAILURE);
807     }
808
809     grep0(inputfp, outputfp);
810 }
811
812 static void
813 grep0(inputfp0, outputfp0)
814 FILE *inputfp0;
815 FILE *outputfp0;
816 {
817     char buf[16384]; /* looong, just in case */
818
819     while (!feof(inputfp0) && !ferror(inputfp0)) {
820         char *tmp;
821         char *buf1;
822
823         if (fgets(buf, sizeof(buf), inputfp0) == 0)
824             break;
825         if ((tmp = strchr(buf, '\n')))
826             *tmp = '\0';
827         grep_lineno++;
828         if (grep_trace) {
829             Fprintf(outputfp0, "%04d %c >%s\n", grep_lineno,
830                     grep_writing ? ' ' : '#', buf);
831         }
832
833         if (buf[0] == GREP_MAGIC) {
834             buf1 = do_grep_control(&buf[1]);
835             if (!buf1)
836                 continue;
837         } else {
838             buf1 = buf;
839         }
840 #ifdef notyet
841         if (grep_rewrite)
842             do_grep_rewrite(buf1);
843 #endif
844         if (grep_writing)
845             Fprintf(outputfp0, "%s\n", buf1);
846     }
847     if (ferror(inputfp0)) {
848         Fprintf(stderr, "read error!\n");
849         exit(EXIT_FAILURE);
850     }
851     if (ferror(outputfp0)) {
852         Fprintf(stderr, "write error!\n");
853         exit(EXIT_FAILURE);
854     }
855     fclose(inputfp0);
856     fclose(outputfp0);
857     if (grep_sp) {
858         Fprintf(stderr, "%d unterminated conditional level%s\n", grep_sp,
859                 grep_sp == 1 ? "" : "s");
860         grep_errors++;
861     }
862     if (grep_errors) {
863         Fprintf(stderr, "%d error%s detected.\n", grep_errors,
864                 grep_errors == 1 ? "" : "s");
865         exit(EXIT_FAILURE);
866     }
867 }
868
869 /* trivial text encryption routine which can't be broken with `tr' */
870 static char *
871 xcrypt(str)
872 const char *str;
873 { /* duplicated in src/hacklib.c */
874     static char buf[BUFSZ];
875     register const char *p;
876     register char *q;
877     register int bitmask;
878
879     for (bitmask = 1, p = str, q = buf; *p; q++) {
880         *q = *p++;
881         if (*q & (32 | 64))
882             *q ^= bitmask;
883         if ((bitmask <<= 1) >= 32)
884             bitmask = 1;
885     }
886     *q = '\0';
887     return buf;
888 }
889
890 #define PAD_RUMORS_TO 60
891 /* common code for do_rumors().  Return 0 on error. */
892 static unsigned long
893 read_rumors_file(file_ext, rumor_count, rumor_size, old_rumor_offset)
894 const char *file_ext;
895 int *rumor_count;
896 long *rumor_size;
897 unsigned long old_rumor_offset;
898 {
899     char infile[MAXFNAMELEN];
900     char *line;
901     unsigned long rumor_offset;
902
903     Sprintf(infile, DATA_IN_TEMPLATE, RUMOR_FILE);
904     Strcat(infile, file_ext);
905     if (!(ifp = fopen(infile, RDTMODE))) {
906         perror(infile);
907         return 0L;
908     }
909
910     /* copy the rumors */
911     while ((line = fgetline(ifp)) != 0) {
912 #ifdef PAD_RUMORS_TO
913         /* rumor selection is accomplished by seeking to a random
914            position in the file, advancing to newline, and taking
915            the next line; therefore, rumors which follow long-line
916            rumors are most likely to be chosen and rumors which
917            follow short-line rumors are least likely to be chosen;
918            we ameliorate the latter by padding the shortest lines,
919            increasing the chance of the random seek landing in them */
920         int len = (int) strlen(line);
921
922         if (len <= PAD_RUMORS_TO) {
923             char *base = index(line, '\n');
924             /* this is only safe because fgetline() overallocates */
925             while (len++ < PAD_RUMORS_TO) {
926                 *base++ = '_';
927             }
928             *base++ = '\n';
929             *base = '\0';
930         }
931 #endif
932         (*rumor_count)++;
933 #if 0
934         /*[if we forced binary output, this would be sufficient]*/
935         *rumor_size += strlen(line); /* includes newline */
936 #endif
937         (void) fputs(xcrypt(line), tfp);
938         free(line);
939     }
940     /* record the current position; next rumors section will start here */
941     rumor_offset = (unsigned long) ftell(tfp);
942     Fclose(ifp); /* all done with rumors.file_ext */
943
944     /* the calculated value for *_rumor_count assumes that
945        a single-byte line terminator is in use; for platforms
946        which use two byte CR+LF, we need to override that value
947        [it's much simpler to do so unconditionally, rendering
948        the loop's accumulation above obsolete] */
949     *rumor_size = (long) (rumor_offset - old_rumor_offset);
950     return rumor_offset;
951 }
952
953 void
954 do_rnd_access_file(fname)
955 const char *fname;
956 {
957     char *line;
958
959     Sprintf(filename, DATA_IN_TEMPLATE, fname);
960     Strcat(filename, ".txt");
961     if (!(ifp = fopen(filename, RDTMODE))) {
962         perror(filename);
963         exit(EXIT_FAILURE);
964     }
965     filename[0] = '\0';
966 #ifdef FILE_PREFIX
967     Strcat(filename, file_prefix);
968 #endif
969     Sprintf(eos(filename), DATA_TEMPLATE, fname);
970     if (!(ofp = fopen(filename, WRTMODE))) {
971         perror(filename);
972         exit(EXIT_FAILURE);
973     }
974     Fprintf(ofp, "%s", Dont_Edit_Data);
975
976     tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE);
977     grep0(ifp, tfp);
978     ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE);
979
980     while ((line = fgetline(ifp)) != 0) {
981         if (line[0] != '#' && line[0] != '\n')
982             (void) fputs(xcrypt(line), ofp);
983         free(line);
984     }
985     Fclose(ifp);
986     Fclose(ofp);
987
988     delete_file(DATA_TEMPLATE, "grep.tmp");
989     return;
990 }
991
992 void
993 do_rumors()
994 {
995     char *line;
996     static const char rumors_header[] =
997         "%s%04d,%06ld,%06lx;%04d,%06ld,%06lx;0,0,%06lx\n";
998     char tempfile[MAXFNAMELEN];
999     int true_rumor_count, false_rumor_count;
1000     long true_rumor_size, false_rumor_size;
1001     unsigned long true_rumor_offset, false_rumor_offset, eof_offset;
1002
1003     Sprintf(tempfile, DATA_TEMPLATE, "rumors.tmp");
1004     filename[0] = '\0';
1005 #ifdef FILE_PREFIX
1006     Strcat(filename, file_prefix);
1007 #endif
1008     Sprintf(eos(filename), DATA_TEMPLATE, RUMOR_FILE);
1009     if (!(ofp = fopen(filename, WRTMODE))) {
1010         perror(filename);
1011         exit(EXIT_FAILURE);
1012     }
1013     if (!(tfp = fopen(tempfile, WRTMODE))) {
1014         perror(tempfile);
1015         Fclose(ofp);
1016         exit(EXIT_FAILURE);
1017     }
1018
1019     true_rumor_count = false_rumor_count = 0;
1020     true_rumor_size = false_rumor_size = 0L;
1021     true_rumor_offset = false_rumor_offset = eof_offset = 0L;
1022
1023     /* output a dummy header record; we'll replace it in final output */
1024     Fprintf(tfp, rumors_header, Dont_Edit_Data, true_rumor_count,
1025             true_rumor_size, true_rumor_offset, false_rumor_count,
1026             false_rumor_size, false_rumor_offset, eof_offset);
1027     /* record the current position; true rumors will start here */
1028     true_rumor_offset = ftell(tfp);
1029
1030     false_rumor_offset = read_rumors_file(
1031         ".tru", &true_rumor_count, &true_rumor_size, true_rumor_offset);
1032     if (!false_rumor_offset)
1033         goto rumors_failure;
1034
1035     eof_offset = read_rumors_file(".fal", &false_rumor_count,
1036                                   &false_rumor_size, false_rumor_offset);
1037     if (!eof_offset)
1038         goto rumors_failure;
1039
1040     /* get ready to transfer the contents of temp file to output file */
1041     line = malloc(BUFSZ + MAXFNAMELEN);
1042     Sprintf(line, "rewind of \"%s\"", tempfile);
1043     if (rewind(tfp) != 0) {
1044         perror(line);
1045         free(line);
1046         goto rumors_failure;
1047     }
1048     free(line);
1049
1050     /* output the header record */
1051     Fprintf(ofp, rumors_header, Dont_Edit_Data, true_rumor_count,
1052             true_rumor_size, true_rumor_offset, false_rumor_count,
1053             false_rumor_size, false_rumor_offset, eof_offset);
1054     /* skip the temp file's dummy header */
1055     if (!(line = fgetline(tfp))) { /* "Don't Edit" */
1056         perror(tempfile);
1057         goto rumors_failure;
1058     }
1059     free(line);
1060     if (!(line = fgetline(tfp))) { /* count,size,offset */
1061         perror(tempfile);
1062         goto rumors_failure;
1063     }
1064     free(line);
1065     /* copy the rest of the temp file into the final output file */
1066     while ((line = fgetline(tfp)) != 0) {
1067         (void) fputs(line, ofp);
1068         free(line);
1069     }
1070     /* all done; delete temp file */
1071     Fclose(tfp);
1072     Unlink(tempfile);
1073     Fclose(ofp);
1074     return;
1075
1076 rumors_failure:
1077     Fclose(ofp);
1078     Unlink(filename); /* kill empty or incomplete output file */
1079     Fclose(tfp);
1080     Unlink(tempfile); /* and temporary file */
1081     exit(EXIT_FAILURE);
1082 }
1083
1084 /*
1085  * Use this to explicitly mask out features during version checks.
1086  *
1087  * ZEROCOMP, RLECOMP, and ZLIB_COMP describe compression features
1088  * that the port/plaform which wrote the savefile was capable of
1089  * dealing with. Don't reject a savefile just because the port
1090  * reading the savefile doesn't match on all/some of them.
1091  * The actual compression features used to produce the savefile are
1092  * recorded in the savefile_info structure immediately following the
1093  * version_info, and that is what needs to be checked against the
1094  * feature set of the port that is reading the savefile back in.
1095  * That check is done in src/restore.c now.
1096  *
1097  */
1098 #define IGNORED_FEATURES                 \
1099     (0L | (1L << 19) /* SCORE_ON_BOTL */ \
1100      | (1L << 27)    /* ZEROCOMP */      \
1101      | (1L << 28)    /* RLECOMP */       \
1102      )
1103
1104 static void
1105 make_version()
1106 {
1107     register int i;
1108
1109     /*
1110      * integer version number
1111      */
1112     version.incarnation = ((unsigned long) VERSION_MAJOR << 24)
1113                           | ((unsigned long) VERSION_MINOR << 16)
1114                           | ((unsigned long) PATCHLEVEL << 8)
1115                           | ((unsigned long) EDITLEVEL);
1116     /*
1117      * encoded feature list
1118      * Note:  if any of these magic numbers are changed or reassigned,
1119      * EDITLEVEL in patchlevel.h should be incremented at the same time.
1120      * The actual values have no special meaning, and the category
1121      * groupings are just for convenience.
1122      */
1123     version.feature_set = (unsigned long) (0L
1124 /* levels and/or topology (0..4) */
1125 /* monsters (5..9) */
1126 #ifdef MAIL
1127                                            | (1L << 6)
1128 #endif
1129 /* objects (10..14) */
1130 /* flag bits and/or other global variables (15..26) */
1131 #ifdef TEXTCOLOR
1132                                            | (1L << 17)
1133 #endif
1134 #ifdef INSURANCE
1135                                            | (1L << 18)
1136 #endif
1137 #ifdef SCORE_ON_BOTL
1138                                            | (1L << 19)
1139 #endif
1140 /* data format (27..31)
1141  * External compression methods such as COMPRESS and ZLIB_COMP
1142  * do not affect the contents and are thus excluded from here */
1143 #ifdef ZEROCOMP
1144                                            | (1L << 27)
1145 #endif
1146 #ifdef RLECOMP
1147                                            | (1L << 28)
1148 #endif
1149                                                );
1150     /*
1151      * Value used for object & monster sanity check.
1152      *    (NROFARTIFACTS<<24) | (NUM_OBJECTS<<12) | (NUMMONS<<0)
1153      */
1154     for (i = 1; artifact_names[i]; i++)
1155         continue;
1156     version.entity_count = (unsigned long) (i - 1);
1157     for (i = 1; objects[i].oc_class != ILLOBJ_CLASS; i++)
1158         continue;
1159     version.entity_count = (version.entity_count << 12) | (unsigned long) i;
1160     for (i = 0; mons[i].mlet; i++)
1161         continue;
1162     version.entity_count = (version.entity_count << 12) | (unsigned long) i;
1163     /*
1164      * Value used for compiler (word size/field alignment/padding) check.
1165      */
1166     version.struct_sizes1 =
1167         (((unsigned long) sizeof(struct context_info) << 24)
1168          | ((unsigned long) sizeof(struct obj) << 17)
1169          | ((unsigned long) sizeof(struct monst) << 10)
1170          | ((unsigned long) sizeof(struct you)));
1171     version.struct_sizes2 = (((unsigned long) sizeof(struct flag) << 10) |
1172 /* free bits in here */
1173 #ifdef SYSFLAGS
1174                              ((unsigned long) sizeof(struct sysflag)));
1175 #else
1176                              ((unsigned long) 0L));
1177 #endif
1178     return;
1179 }
1180
1181 /* REPRODUCIBLE_BUILD will change this to TRUE */
1182 static boolean date_via_env = FALSE;
1183
1184 static char *
1185 version_string(outbuf, delim)
1186 char *outbuf;
1187 const char *delim;
1188 {
1189     Sprintf(outbuf, "%d%s%d%s%d", VERSION_MAJOR, delim, VERSION_MINOR, delim,
1190             PATCHLEVEL);
1191 #ifdef BETA
1192     Sprintf(eos(outbuf), "-%d", EDITLEVEL);
1193 #endif
1194     return outbuf;
1195 }
1196
1197 static char *
1198 version_id_string(outbuf, build_date)
1199 char *outbuf;
1200 const char *build_date;
1201 {
1202     char subbuf[64], versbuf[64];
1203     char betabuf[64];
1204
1205 #ifdef BETA
1206     Strcpy(betabuf, " Beta");
1207 #else
1208     betabuf[0] = '\0';
1209 #endif
1210
1211     subbuf[0] = '\0';
1212 #ifdef PORT_SUB_ID
1213     subbuf[0] = ' ';
1214     Strcpy(&subbuf[1], PORT_SUB_ID);
1215 #endif
1216
1217     Sprintf(outbuf, "%s NetHack%s Version %s%s - last %s %s.", PORT_ID,
1218             subbuf, version_string(versbuf, "."), betabuf,
1219             date_via_env ? "revision" : "build", build_date);
1220     return outbuf;
1221 }
1222
1223 #if 1 /*JP*/
1224 static char *
1225 jversion_id_string(outbuf, build_date)
1226 char *outbuf;
1227 const char *build_date;
1228 {
1229     char subbuf[64], versbuf[64];
1230
1231     subbuf[0] = '\0';
1232 #ifdef BETA
1233     Strcat(subbuf, " Beta");
1234 #endif
1235
1236     Sprintf(outbuf, "%s JNetHack%s Version %s-%d.%d.", PORT_ID,
1237             subbuf, version_string(versbuf, "."), JVERSION_MAJOR, JVERSION_MINOR);
1238         return outbuf;
1239 }
1240 #endif
1241
1242 static char *
1243 bannerc_string(outbuf, build_date)
1244 char *outbuf;
1245 const char *build_date;
1246 {
1247     char subbuf[64], versbuf[64];
1248
1249     subbuf[0] = '\0';
1250 #ifdef PORT_SUB_ID
1251     subbuf[0] = ' ';
1252     Strcpy(&subbuf[1], PORT_SUB_ID);
1253 #endif
1254 #ifdef BETA
1255     Strcat(subbuf, " Beta");
1256 #endif
1257
1258     Sprintf(outbuf, "         Version %s %s%s, %s %s.",
1259             version_string(versbuf, "."), PORT_ID, subbuf,
1260             date_via_env ? "revised" : "built", &build_date[4]);
1261 #if 0
1262     Sprintf(outbuf, "%s NetHack%s %s Copyright 1985-%s (built %s)",
1263             PORT_ID, subbuf, version_string(versbuf,"."), RELEASE_YEAR,
1264             &build_date[4]);
1265 #endif
1266     return outbuf;
1267 }
1268
1269 void
1270 do_date()
1271 {
1272 #ifdef KR1ED
1273     long clocktim = 0;
1274 #else
1275     time_t clocktim = 0;
1276 #endif
1277     char githash[BUFSZ], gitbranch[BUFSZ];
1278     char *c, cbuf[60], buf[BUFSZ];
1279     const char *ul_sfx;
1280
1281     /* before creating date.h, make sure that xxx_GRAPHICS and
1282        DEFAULT_WINDOW_SYS have been set up in a viable fashion */
1283     windowing_sanity();
1284
1285     filename[0] = '\0';
1286 #ifdef FILE_PREFIX
1287     Strcat(filename, file_prefix);
1288 #endif
1289     Sprintf(eos(filename), INCLUDE_TEMPLATE, DATE_FILE);
1290     if (!(ofp = fopen(filename, WRTMODE))) {
1291         perror(filename);
1292         exit(EXIT_FAILURE);
1293     }
1294     /* NB: We've moved on from SCCS, but this way this line
1295      * won't get clobbered when downstream projects import
1296      * this file into something more modern. */
1297     Fprintf(ofp, "%s", Dont_Edit_Code);
1298
1299     (void) time(&clocktim);
1300 #ifdef REPRODUCIBLE_BUILD
1301     {
1302         /*
1303          * Use date+time of latest source file revision (set up in
1304          * our environment rather than derived by scanning sources)
1305          * instead of current date+time, so that later rebuilds of
1306          * the same sources specifying the same configuration will
1307          * produce the same result.
1308          *
1309          * Changing the configuration should be done by modifying
1310          * config.h or <port>conf.h and setting SOURCE_DATE_EPOCH
1311          * based on whichever changed most recently, not by using
1312          *   make CFLAGS='-Dthis -Dthat'
1313          * to make alterations on the fly.
1314          *
1315          * Limited validation is performed to prevent dates in the
1316          * future (beyond a leeway of 24 hours) or distant past.
1317          *
1318          * Assumes the value of time_t is in seconds, which is
1319          * fundamental for Unix and mandated by POSIX.  For any ports
1320          * where that isn't true, leaving REPRODUCIBLE_BUILD disabled
1321          * is probably preferrable to hacking this code....
1322          */
1323         static struct tm nh360; /* static init should yield UTC timezone */
1324         unsigned long sd_num, sd_earliest, sd_latest;
1325         const char *sd_str = getenv("SOURCE_DATE_EPOCH");
1326
1327         if (sd_str) {
1328             sd_num = strtoul(sd_str, (char **) 0, 10);
1329             /*
1330              * Note:  this does not need to be updated for future
1331              * releases.  It serves as a sanity check for potentially
1332              * mis-set environment, not a hard baseline for when the
1333              * current version could have first been built.
1334              */
1335             /* oldest date we'll accept: 7-Dec-2015 (release of 3.6.0) */
1336             nh360.tm_mday = 7;
1337             nh360.tm_mon  = 12 - 1;
1338             nh360.tm_year = 2015 - 1900;
1339             sd_earliest = (unsigned long) mktime(&nh360);
1340             /* 'youngest' date we'll accept: 24 hours in the future */
1341             sd_latest = (unsigned long) clocktim + 24L * 60L * 60L;
1342
1343             if (sd_num >= sd_earliest && sd_num <= sd_latest) {
1344                 /* use SOURCE_DATE_EPOCH value */
1345                 clocktim = (time_t) sd_num;
1346                 date_via_env = TRUE;
1347             } else {
1348                 Fprintf(stderr, "? Invalid value for SOURCE_DATE_EPOCH (%lu)",
1349                         sd_num);
1350                 if (sd_num > 0L && sd_num < sd_earliest)
1351                     Fprintf(stderr, ", older than %lu", sd_earliest);
1352                 else if (sd_num > sd_latest)
1353                     Fprintf(stderr, ", newer than %lu", sd_latest);
1354                 Fprintf(stderr, ".\n");
1355                 Fprintf(stderr, ": Reverting to current date+time (%lu).\n",
1356                         (unsigned long) clocktim);
1357                 (void) fflush(stderr);
1358             }
1359         } else {
1360             /* REPRODUCIBLE_BUILD enabled but SOURCE_DATE_EPOCH is missing */
1361             Fprintf(stderr, "? No value for SOURCE_DATE_EPOCH.\n");
1362             Fprintf(stderr, ": Using current date+time (%lu).\n",
1363                     (unsigned long) clocktim);
1364             (void) fflush(stderr);
1365         }
1366         Strcpy(cbuf, asctime(gmtime(&clocktim)));
1367     }
1368 #else
1369     /* ordinary build: use current date+time */
1370     Strcpy(cbuf, ctime(&clocktim));
1371 #endif
1372
1373     if ((c = index(cbuf, '\n')) != 0)
1374         *c = '\0'; /* strip off the '\n' */
1375 #ifdef NHSTDC
1376     ul_sfx = "UL";
1377 #else
1378     ul_sfx = "L";
1379 #endif
1380     if (date_via_env)
1381         Fprintf(ofp, "#define SOURCE_DATE_EPOCH (%lu%s) /* via getenv() */\n",
1382                 (unsigned long) clocktim, ul_sfx);
1383     Fprintf(ofp, "#define BUILD_DATE \"%s\"\n", cbuf);
1384     if (date_via_env)
1385         Fprintf(ofp, "#define BUILD_TIME SOURCE_DATE_EPOCH\n");
1386     else
1387         Fprintf(ofp, "#define BUILD_TIME (%lu%s)\n",
1388                 (unsigned long) clocktim, ul_sfx);
1389     Fprintf(ofp, "\n");
1390     Fprintf(ofp, "#define VERSION_NUMBER 0x%08lx%s\n", version.incarnation,
1391             ul_sfx);
1392     Fprintf(ofp, "#define VERSION_FEATURES 0x%08lx%s\n", version.feature_set,
1393             ul_sfx);
1394 #ifdef IGNORED_FEATURES
1395     Fprintf(ofp, "#define IGNORED_FEATURES 0x%08lx%s\n",
1396             (unsigned long) IGNORED_FEATURES, ul_sfx);
1397 #endif
1398     Fprintf(ofp, "#define VERSION_SANITY1 0x%08lx%s\n", version.entity_count,
1399             ul_sfx);
1400     Fprintf(ofp, "#define VERSION_SANITY2 0x%08lx%s\n", version.struct_sizes1,
1401             ul_sfx);
1402     Fprintf(ofp, "#define VERSION_SANITY3 0x%08lx%s\n", version.struct_sizes2,
1403             ul_sfx);
1404     Fprintf(ofp, "\n");
1405     Fprintf(ofp, "#define VERSION_STRING \"%s\"\n", version_string(buf, "."));
1406     Fprintf(ofp, "#define VERSION_ID \\\n \"%s\"\n",
1407             version_id_string(buf, cbuf));
1408 #if 1 /*JP*/
1409     Fprintf(ofp,"#define JVERSION_ID \\\n \"%s\"\n",
1410             jversion_id_string(buf, cbuf));
1411 #endif
1412     Fprintf(ofp, "#define COPYRIGHT_BANNER_C \\\n \"%s\"\n",
1413             bannerc_string(buf, cbuf));
1414     Fprintf(ofp, "\n");
1415     if (get_gitinfo(githash, gitbranch)) {
1416         Fprintf(ofp, "#define NETHACK_GIT_SHA \"%s\"\n", githash);
1417         Fprintf(ofp, "#define NETHACK_GIT_BRANCH \"%s\"\n", gitbranch);
1418     }
1419 #ifdef AMIGA
1420     {
1421         struct tm *tm = localtime((time_t *) &clocktim);
1422
1423         Fprintf(ofp, "#define AMIGA_VERSION_STRING ");
1424         Fprintf(ofp, "\"\\0$VER: NetHack %d.%d.%d (%d.%d.%d)\"\n",
1425                 VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL,
1426                 tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900);
1427     }
1428 #endif
1429     Fclose(ofp);
1430     return;
1431 }
1432
1433 boolean
1434 get_gitinfo(githash, gitbranch)
1435 char *githash, *gitbranch;
1436 {
1437     FILE *gifp;
1438     size_t len;
1439     char infile[MAXFNAMELEN];
1440     char *line, *strval, *opt, *c, *end;
1441     boolean havebranch = FALSE, havehash = FALSE;
1442
1443     if (!githash || !gitbranch) return FALSE;
1444
1445     Sprintf(infile, DATA_IN_TEMPLATE, GITINFO_FILE);
1446     if (!(gifp = fopen(infile, RDTMODE))) {
1447         /* perror(infile); */
1448         return FALSE;
1449     }
1450
1451     /* read the gitinfo file */
1452     while ((line = fgetline(gifp)) != 0) {
1453         strval = index(line, '=');
1454         if (strval && strlen(strval) < (BUFSZ-1)) {
1455             opt = line;
1456             *strval++ = '\0';
1457             /* strip off the '\n' */
1458             if ((c = index(strval, '\n')) != 0)
1459                 *c = '\0'; 
1460             if ((c = index(opt, '\n')) != 0)
1461                 *c = '\0';
1462             /* strip leading and trailing white space */
1463             while (*strval == ' ' || *strval == '\t')
1464                 strval++;
1465             end = eos(strval);
1466             while (--end >= strval && (*end == ' ' || *end == '\t'))
1467             *end = '\0';
1468             while (*opt == ' ' || *opt == '\t')
1469                 opt++;
1470             end = eos(opt);
1471             while (--end >= opt && (*end == ' ' || *end == '\t'))
1472             *end = '\0';
1473
1474             len = strlen(opt);
1475             if ((len >= strlen("gitbranch")) && !case_insensitive_comp(opt, "gitbranch")) {
1476                 Strcpy(gitbranch, strval);
1477                 havebranch = TRUE;
1478             }
1479             if ((len >= strlen("githash")) && !case_insensitive_comp(opt, "githash")) {
1480                 Strcpy(githash, strval);
1481                 havehash = TRUE;
1482             }
1483         }
1484         free(line);
1485     }
1486     Fclose(gifp);
1487     if (havebranch && havehash)
1488         return TRUE;
1489     return FALSE;
1490 }
1491
1492 static int
1493 case_insensitive_comp(s1, s2)
1494 const char *s1;
1495 const char *s2;
1496 {
1497     uchar u1, u2;
1498
1499     for (;; s1++, s2++) {
1500         u1 = (uchar) *s1;
1501         if (isupper(u1))
1502             u1 = tolower(u1);
1503         u2 = (uchar) *s2;
1504         if (isupper(u2))
1505             u2 = tolower(u2);
1506         if (u1 == '\0' || u1 != u2)
1507             break;
1508     }
1509     return u1 - u2;
1510 }
1511
1512 static char save_bones_compat_buf[BUFSZ];
1513
1514 static void
1515 build_savebones_compat_string()
1516 {
1517 #ifdef VERSION_COMPATIBILITY
1518     unsigned long uver = VERSION_COMPATIBILITY;
1519 #endif
1520     Strcpy(save_bones_compat_buf,
1521            "save and bones files accepted from version");
1522 #ifdef VERSION_COMPATIBILITY
1523     Sprintf(eos(save_bones_compat_buf), "s %lu.%lu.%lu through %d.%d.%d",
1524             ((uver & 0xFF000000L) >> 24), ((uver & 0x00FF0000L) >> 16),
1525             ((uver & 0x0000FF00L) >> 8), VERSION_MAJOR, VERSION_MINOR,
1526             PATCHLEVEL);
1527 #else
1528     Sprintf(eos(save_bones_compat_buf), " %d.%d.%d only", VERSION_MAJOR,
1529             VERSION_MINOR, PATCHLEVEL);
1530 #endif
1531 }
1532
1533 static const char *build_opts[] = {
1534 #ifdef AMIGA_WBENCH
1535     "Amiga WorkBench support",
1536 #endif
1537 #ifdef ANSI_DEFAULT
1538     "ANSI default terminal",
1539 #endif
1540 #ifdef TEXTCOLOR
1541     "color",
1542 #endif
1543 #ifdef TTY_TILES_ESCCODES
1544     "console escape codes for tile hinting",
1545 #endif
1546 #ifdef COM_COMPL
1547     "command line completion",
1548 #endif
1549 #ifdef LIFE
1550     "Conway's Game of Life",
1551 #endif
1552 #ifdef COMPRESS
1553     "data file compression",
1554 #endif
1555 #ifdef ZLIB_COMP
1556     "ZLIB data file compression",
1557 #endif
1558 #ifdef DLB
1559     "data librarian",
1560 #endif
1561 #ifdef DUMPLOG
1562     "end-of-game dumplogs",
1563 #endif
1564 #ifdef HOLD_LOCKFILE_OPEN
1565     "exclusive lock on level 0 file",
1566 #endif
1567 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
1568     "external program as a message handler",
1569 #endif
1570 #ifdef MFLOPPY
1571     "floppy drive support",
1572 #endif
1573 #ifdef INSURANCE
1574     "insurance files for recovering from crashes",
1575 #endif
1576 #ifdef LOGFILE
1577     "log file",
1578 #endif
1579 #ifdef XLOGFILE
1580     "extended log file",
1581 #endif
1582 #ifdef PANICLOG
1583     "errors and warnings log file",
1584 #endif
1585 #ifdef MAIL
1586     "mail daemon",
1587 #endif
1588 #ifdef GNUDOS
1589     "MSDOS protected mode",
1590 #endif
1591 #ifdef NEWS
1592     "news file",
1593 #endif
1594 #ifdef OVERLAY
1595 #ifdef MOVERLAY
1596     "MOVE overlays",
1597 #else
1598 #ifdef VROOMM
1599     "VROOMM overlays",
1600 #else
1601     "overlays",
1602 #endif
1603 #endif
1604 #endif
1605     /* pattern matching method will be substituted by nethack at run time */
1606     "pattern matching via :PATMATCH:",
1607 #ifdef USE_ISAAC64
1608     "pseudo random numbers generated by ISAAC64",
1609 #ifdef DEV_RANDOM
1610 #ifdef NHSTDC
1611     /* include which specific one */
1612     "strong PRNG seed available from " DEV_RANDOM,
1613 #else
1614     "strong PRNG seed available from DEV_RANDOM",
1615 #endif
1616 #else
1617 #ifdef WIN32
1618     "strong PRNG seed available from CNG BCryptGenRandom()",
1619 #endif
1620 #endif  /* DEV_RANDOM */    
1621 #else   /* ISAAC64 */
1622 #ifdef RANDOM
1623     "pseudo random numbers generated by random()",
1624 #else
1625     "pseudo random numbers generated by C rand()",
1626 #endif
1627 #endif
1628 #ifdef SELECTSAVED
1629     "restore saved games via menu",
1630 #endif
1631 #ifdef SCORE_ON_BOTL
1632     "score on status line",
1633 #endif
1634 #ifdef CLIPPING
1635     "screen clipping",
1636 #endif
1637 #ifdef NO_TERMS
1638 #ifdef MAC
1639     "screen control via mactty",
1640 #endif
1641 #ifdef SCREEN_BIOS
1642     "screen control via BIOS",
1643 #endif
1644 #ifdef SCREEN_DJGPPFAST
1645     "screen control via DJGPP fast",
1646 #endif
1647 #ifdef SCREEN_VGA
1648     "screen control via VGA graphics",
1649 #endif
1650 #ifdef WIN32CON
1651     "screen control via WIN32 console I/O",
1652 #endif
1653 #endif
1654 #ifdef SHELL
1655     "shell command",
1656 #endif
1657     "traditional status display",
1658 #ifdef STATUS_HILITES
1659     "status via windowport with highlighting",
1660 #else
1661     "status via windowport without highlighting",
1662 #endif
1663 #ifdef SUSPEND
1664     "suspend command",
1665 #endif
1666 #ifdef TERMINFO
1667     "terminal info library",
1668 #else
1669 #if defined(TERMLIB) \
1670     || ((!defined(MICRO) && !defined(WIN32)) && defined(TTY_GRAPHICS))
1671     "terminal capability library",
1672 #endif
1673 #endif
1674 #ifdef USE_XPM
1675     "tile_file in XPM format",
1676 #endif
1677 #ifdef GRAPHIC_TOMBSTONE
1678     "graphical RIP screen",
1679 #endif
1680 #ifdef TIMED_DELAY
1681     "timed wait for display effects",
1682 #endif
1683 #ifdef USER_SOUNDS
1684     "user sounds",
1685 #endif
1686 #ifdef PREFIXES_IN_USE
1687     "variable playground",
1688 #endif
1689 #ifdef VISION_TABLES
1690     "vision tables",
1691 #endif
1692 #ifdef ZEROCOMP
1693     "zero-compressed save files",
1694 #endif
1695 #ifdef RLECOMP
1696     "run-length compression of map in save files",
1697 #endif
1698 #ifdef SYSCF
1699     "system configuration at run-time",
1700 #endif
1701     save_bones_compat_buf,
1702     "and basic NetHack features"
1703 };
1704
1705 struct win_info {
1706     const char *id, /* DEFAULT_WINDOW_SYS string */
1707         *name;      /* description, often same as id */
1708 };
1709 static struct win_info window_opts[] = {
1710 #ifdef TTY_GRAPHICS
1711     { "tty",
1712       /* testing 'USE_TILES' here would bring confusion because it could
1713          apply to another interface such as X11, so check MSDOS explicitly
1714          instead; even checking TTY_TILES_ESCCODES would probably be
1715          confusing to most users (and it will already be listed separately
1716          in the compiled options section so users aware of it can find it) */
1717 #ifdef MSDOS
1718       "traditional text with optional 'tiles' graphics"
1719 #else
1720       /* assume that one or more of IBMgraphics, DECgraphics, or MACgraphics
1721          can be enabled; we can't tell from here whether that is accurate */
1722       "traditional text with optional line-drawing"
1723 #endif
1724     },
1725 #endif
1726 #ifdef CURSES_GRAPHICS
1727     { "curses", "terminal-based graphics" },
1728 #endif
1729 #ifdef X11_GRAPHICS
1730     { "X11", "X11" },
1731 #endif
1732 #ifdef QT_GRAPHICS
1733     { "Qt", "Qt" },
1734 #endif
1735 #ifdef GNOME_GRAPHICS
1736     { "Gnome", "Gnome" },
1737 #endif
1738 #ifdef MAC
1739     { "mac", "Mac" },
1740 #endif
1741 #ifdef AMIGA_INTUITION
1742     { "amii", "Amiga Intuition" },
1743 #endif
1744 #ifdef GEM_GRAPHICS
1745     { "Gem", "Gem" },
1746 #endif
1747 #ifdef MSWIN_GRAPHICS
1748     { "mswin", "mswin" },
1749 #endif
1750 #ifdef BEOS_GRAPHICS
1751     { "BeOS", "BeOS InterfaceKit" },
1752 #endif
1753     { 0, 0 }
1754 };
1755
1756 static void
1757 windowing_sanity()
1758 {
1759 #ifndef DEFAULT_WINDOW_SYS
1760     /* pre-standard compilers didn't support #error; wait til run-time */
1761     Fprintf(stderr,
1762             "Configuration error: DEFAULT_WINDOW_SYS is not defined.\n");
1763     exit(EXIT_FAILURE);
1764 /*NOTREACHED*/
1765
1766 /* put in a dummy value so that do_options() will compile and makedefs
1767    will build, otherwise the message above won't ever get delivered */
1768 #define DEFAULT_WINDOW_SYS "<undefined>"
1769 #else  /*DEFAULT_WINDOW_SYS*/
1770
1771     if (!window_opts[0].id) {
1772         Fprintf(stderr, "Configuration error: no windowing systems "
1773                         "(TTY_GRAPHICS, &c) enabled.\n");
1774         exit(EXIT_FAILURE);
1775     }
1776
1777     {
1778         int i;
1779
1780         for (i = 0; window_opts[i].id; ++i)
1781             if (!strcmp(window_opts[i].id, DEFAULT_WINDOW_SYS))
1782                 break;
1783         if (!window_opts[i].id) { /* went through whole list without a match */
1784             Fprintf(stderr, "Configuration error: DEFAULT_WINDOW_SYS (%s)\n",
1785                     DEFAULT_WINDOW_SYS);
1786             Fprintf(stderr,
1787                     " does not match any enabled windowing system (%s%s).\n",
1788                     window_opts[0].id, window_opts[1].id ? ", &c" : "");
1789             exit(EXIT_FAILURE);
1790         }
1791     }
1792 #endif /*DEFAULT_WINDOW_SYS*/
1793 }
1794
1795 static const char opt_indent[] = "    ";
1796
1797 static void
1798 opt_out_words(str, length_p)
1799 char *str; /* input, but modified during processing */
1800 int *length_p; /* in/out */
1801 {
1802     char *word;
1803
1804     while (*str) {
1805         word = index(str, ' ');
1806 #if 0
1807         /* treat " (" as unbreakable space */
1808         if (word && *(word + 1) == '(')
1809             word = index(word + 1,  ' ');
1810 #endif
1811         if (word)
1812             *word = '\0';
1813         if (*length_p + (int) strlen(str) > COLNO - 5)
1814             Fprintf(ofp, "\n%s", opt_indent),
1815                 *length_p = (int) strlen(opt_indent);
1816         else
1817             Fprintf(ofp, " "), (*length_p)++;
1818         Fprintf(ofp, "%s", str), *length_p += (int) strlen(str);
1819         str += strlen(str) + (word ? 1 : 0);
1820     }
1821 }
1822
1823 void
1824 do_options()
1825 {
1826     char buf[BUFSZ];
1827     int i, length, winsyscnt;
1828
1829     windowing_sanity();
1830
1831     filename[0] = '\0';
1832 #ifdef FILE_PREFIX
1833     Strcat(filename, file_prefix);
1834 #endif
1835     Sprintf(eos(filename), DATA_TEMPLATE, OPTIONS_FILE);
1836     if (!(ofp = fopen(filename, WRTMODE))) {
1837         perror(filename);
1838         exit(EXIT_FAILURE);
1839     }
1840
1841     build_savebones_compat_string();
1842     Fprintf(ofp, "\n%sNetHack version %d.%d.%d%s\n",
1843             opt_indent,
1844             VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL,
1845 #ifdef BETA
1846             " [beta]"
1847 #else
1848             ""
1849 #endif
1850             );
1851
1852     Fprintf(ofp, "\nOptions compiled into this edition:\n");
1853     length = COLNO + 1; /* force 1st item onto new line */
1854     for (i = 0; i < SIZE(build_opts); i++) {
1855         opt_out_words(strcat(strcpy(buf, build_opts[i]),
1856                              (i < SIZE(build_opts) - 1) ? "," : "."),
1857                       &length);
1858     }
1859     Fprintf(ofp, "\n"); /* terminate last line of words */
1860
1861     winsyscnt = SIZE(window_opts) - 1;
1862     Fprintf(ofp, "\nSupported windowing system%s:\n",
1863             (winsyscnt > 1) ? "s" : "");
1864     length = COLNO + 1; /* force 1st item onto new line */
1865     for (i = 0; i < winsyscnt; i++) {
1866         Sprintf(buf, "\"%s\"", window_opts[i].id);
1867         if (strcmp(window_opts[i].name, window_opts[i].id))
1868             Sprintf(eos(buf), " (%s)", window_opts[i].name);
1869         /*
1870          * 1 : foo.
1871          * 2 : foo and bar  (note no period; comes from 'with default' below)
1872          * 3+: for, bar, and quux
1873          */
1874         opt_out_words(strcat(buf, (winsyscnt == 1) ? "." /* no 'default' */
1875                                   : (winsyscnt == 2 && i == 0) ? " and"
1876                                     : (i == winsyscnt - 2) ? ", and"
1877                                       : ","),
1878                       &length);
1879     }
1880     if (winsyscnt > 1) {
1881         Sprintf(buf, "with a default of \"%s\".", DEFAULT_WINDOW_SYS);
1882         opt_out_words(buf, &length);
1883     }
1884     Fprintf(ofp, "\n"); /* terminate last line of words */
1885
1886     /* end with a blank line */
1887     Fprintf(ofp, "\n");
1888     Fclose(ofp);
1889     return;
1890 }
1891
1892 /* routine to decide whether to discard something from data.base */
1893 static boolean
1894 d_filter(line)
1895 char *line;
1896 {
1897     if (*line == '#')
1898         return TRUE; /* ignore comment lines */
1899     return FALSE;
1900 }
1901
1902 /*
1903  *
1904      New format (v3.1) of 'data' file which allows much faster lookups [pr]
1905 "do not edit"           first record is a comment line
1906 01234567                hexadecimal formatted offset to text area
1907 name-a                  first name of interest
1908 123,4                   offset to name's text, and number of lines for it
1909 name-b                  next name of interest
1910 name-c                  multiple names which share same description also
1911 456,7                   share a single offset,count line
1912 .                       sentinel to mark end of names
1913 789,0                   dummy record containing offset, count of EOF
1914 text-a                  4 lines of descriptive text for name-a
1915 text-a                  at file position 0x01234567L + 123L
1916 text-a
1917 text-a
1918 text-b/text-c           7 lines of text for names-b and -c
1919 text-b/text-c           at fseek(0x01234567L + 456L)
1920 ...
1921  *
1922  */
1923
1924 void
1925 do_data()
1926 {
1927     char infile[60], tempfile[60];
1928     boolean ok;
1929     long txt_offset;
1930     int entry_cnt, line_cnt;
1931     char *line;
1932
1933     Sprintf(tempfile, DATA_TEMPLATE, "database.tmp");
1934     filename[0] = '\0';
1935 #ifdef FILE_PREFIX
1936     Strcat(filename, file_prefix);
1937 #endif
1938     Sprintf(eos(filename), DATA_TEMPLATE, DATA_FILE);
1939     Sprintf(infile, DATA_IN_TEMPLATE, DATA_FILE);
1940 #ifdef SHORT_FILENAMES
1941     Strcat(infile, ".bas");
1942 #else
1943     Strcat(infile, ".base");
1944 #endif
1945     if (!(ifp = fopen(infile, RDTMODE))) { /* data.base */
1946         perror(infile);
1947         exit(EXIT_FAILURE);
1948     }
1949     if (!(ofp = fopen(filename, WRTMODE))) { /* data */
1950         perror(filename);
1951         Fclose(ifp);
1952         exit(EXIT_FAILURE);
1953     }
1954     if (!(tfp = fopen(tempfile, WRTMODE))) { /* database.tmp */
1955         perror(tempfile);
1956         Fclose(ifp);
1957         Fclose(ofp);
1958         Unlink(filename);
1959         exit(EXIT_FAILURE);
1960     }
1961
1962     /* output a dummy header record; we'll rewind and overwrite it later */
1963     Fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, 0L);
1964
1965     entry_cnt = line_cnt = 0;
1966     /* read through the input file and split it into two sections */
1967     while ((line = fgetline(ifp)) != 0) {
1968 #if 0 /*JP*/
1969         if (d_filter(line)) {
1970             free(line);
1971             continue;
1972         }
1973         if (*line > ' ') { /* got an entry name */
1974 #else
1975         unsigned char uc;
1976         if (d_filter(line)) {
1977             free(line);
1978             continue;
1979         }
1980         uc = *((unsigned char *)line);
1981         if (uc > ' ') { /* got an entry name */
1982 #endif
1983             /* first finish previous entry */
1984             if (line_cnt)
1985                 Fprintf(ofp, "%d\n", line_cnt), line_cnt = 0;
1986             /* output the entry name */
1987             (void) fputs(line, ofp);
1988             entry_cnt++;        /* update number of entries */
1989         } else if (entry_cnt) { /* got some descriptive text */
1990             /* update previous entry with current text offset */
1991             if (!line_cnt)
1992                 Fprintf(ofp, "%ld,", ftell(tfp));
1993             /* save the text line in the scratch file */
1994             (void) fputs(line, tfp);
1995             line_cnt++; /* update line counter */
1996         }
1997         free(line);
1998     }
1999     /* output an end marker and then record the current position */
2000     if (line_cnt)
2001         Fprintf(ofp, "%d\n", line_cnt);
2002     Fprintf(ofp, ".\n%ld,%d\n", ftell(tfp), 0);
2003     txt_offset = ftell(ofp);
2004     Fclose(ifp); /* all done with original input file */
2005
2006     /* reprocess the scratch file; 1st format an error msg, just in case */
2007     line = malloc(BUFSZ + MAXFNAMELEN);
2008     Sprintf(line, "rewind of \"%s\"", tempfile);
2009     if (rewind(tfp) != 0)
2010         goto dead_data;
2011     free(line);
2012     /* copy all lines of text from the scratch file into the output file */
2013     while ((line = fgetline(tfp)) != 0) {
2014         (void) fputs(line, ofp);
2015         free(line);
2016     }
2017
2018     /* finished with scratch file */
2019     Fclose(tfp);
2020     Unlink(tempfile); /* remove it */
2021
2022     /* update the first record of the output file; prepare error msg 1st */
2023     line = malloc(BUFSZ + MAXFNAMELEN);
2024     Sprintf(line, "rewind of \"%s\"", filename);
2025     ok = (rewind(ofp) == 0);
2026     if (ok) {
2027         Sprintf(line, "header rewrite of \"%s\"", filename);
2028         ok = (fprintf(ofp, "%s%08lx\n", Dont_Edit_Data,
2029                       (unsigned long) txt_offset) >= 0);
2030     }
2031     if (!ok) {
2032     dead_data:
2033         perror(line); /* report the problem */
2034         free(line);
2035         /* close and kill the aborted output file, then give up */
2036         Fclose(ofp);
2037         Unlink(filename);
2038         exit(EXIT_FAILURE);
2039     }
2040     free(line);
2041
2042     /* all done */
2043     Fclose(ofp);
2044
2045     return;
2046 }
2047
2048 /* routine to decide whether to discard something from oracles.txt */
2049 static boolean
2050 h_filter(line)
2051 char *line;
2052 {
2053     static boolean skip = FALSE;
2054     char *tag;
2055
2056     SpinCursor(3);
2057
2058     if (*line == '#')
2059         return TRUE; /* ignore comment lines */
2060
2061     tag = malloc(strlen(line));
2062     if (sscanf(line, "----- %s", tag) == 1) {
2063         skip = FALSE;
2064     } else if (skip && !strncmp(line, "-----", 5))
2065         skip = FALSE;
2066     free(tag);
2067     return skip;
2068 }
2069
2070 static const char *special_oracle[] = {
2071 #if 0 /*JP*/
2072     "\"...it is rather disconcerting to be confronted with the",
2073     "following theorem from [Baker, Gill, and Solovay, 1975].", "",
2074     "Theorem 7.18  There exist recursive languages A and B such that",
2075     "  (1)  P(A) == NP(A), and", "  (2)  P(B) != NP(B)", "",
2076     "This provides impressive evidence that the techniques that are",
2077     "currently available will not suffice for proving that P != NP or        "
2078     "  ",
2079     "that P == NP.\"  [Garey and Johnson, p. 185.]"
2080 #else
2081     "\81u\8e\9f\82Ì\92è\97\9d[Baker, Gill, and Solovay, 1975]\82É\92¼\96Ê\82·\82é\82±\82Æ\82Í",
2082     "\82Þ\82µ\82ë\8d¢\98f\82·\82é\82±\82Æ\82Å\82 \82é\81D",
2083     "",
2084     "\92è\97\9d 7.18 \8e\9f\82Ì\82æ\82¤\82È\8dÄ\8bA\93I\8c¾\8cê A\81CB\82ª\91\8dÝ\82·\82é",
2085     "  (1)  P(A) == NP(A)\81C\82©\82Â",
2086     "  (2)  P(B) != NP(B)",
2087     "",
2088     "\82±\82ê\82Í\8c»\8dÝ P != NP\82Å\82 \82é\82©\82Ü\82½\82Í P == NP\82Å\82 \82é\82©\82ð\8fØ\96¾\82·\82é",
2089     "\97L\8cø\82È\8eè\96@\82ª\82È\82¢\82±\82Æ\82ð\8b­\82­\8e¦\82µ\82Ä\82¢\82é\81D\81v",
2090     "[Garey and Johnson, p. 185.]"
2091 #endif
2092 };
2093
2094 /*
2095    The oracle file consists of a "do not edit" comment, a decimal count N
2096    and set of N+1 hexadecimal fseek offsets, followed by N multiple-line
2097    records, separated by "---" lines.  The first oracle is a special case.
2098    The input data contains just those multi-line records, separated by
2099    "-----" lines.
2100  */
2101
2102 void
2103 do_oracles()
2104 {
2105     char infile[60], tempfile[60];
2106     boolean in_oracle, ok;
2107     long fpos;
2108     unsigned long txt_offset, offset;
2109     int oracle_cnt;
2110     register int i;
2111     char *line;
2112
2113     Sprintf(tempfile, DATA_TEMPLATE, "oracles.tmp");
2114     filename[0] = '\0';
2115 #ifdef FILE_PREFIX
2116     Strcat(filename, file_prefix);
2117 #endif
2118     Sprintf(eos(filename), DATA_TEMPLATE, ORACLE_FILE);
2119     Sprintf(infile, DATA_IN_TEMPLATE, ORACLE_FILE);
2120     Strcat(infile, ".txt");
2121     if (!(ifp = fopen(infile, RDTMODE))) {
2122         perror(infile);
2123         exit(EXIT_FAILURE);
2124     }
2125     if (!(ofp = fopen(filename, WRTMODE))) {
2126         perror(filename);
2127         Fclose(ifp);
2128         exit(EXIT_FAILURE);
2129     }
2130     if (!(tfp = fopen(tempfile, WRTMODE))) { /* oracles.tmp */
2131         perror(tempfile);
2132         Fclose(ifp);
2133         Fclose(ofp);
2134         Unlink(filename);
2135         exit(EXIT_FAILURE);
2136     }
2137
2138     /* output a dummy header record; we'll rewind and overwrite it later */
2139     Fprintf(ofp, "%s%5d\n", Dont_Edit_Data, 0);
2140
2141     /* handle special oracle; it must come first */
2142     (void) fputs("---\n", tfp);
2143     offset = (unsigned long) ftell(tfp);
2144     Fprintf(ofp, "%05lx\n", offset); /* start pos of special oracle */
2145     for (i = 0; i < SIZE(special_oracle); i++) {
2146         (void) fputs(xcrypt(special_oracle[i]), tfp);
2147         (void) fputc('\n', tfp);
2148     }
2149     SpinCursor(3);
2150
2151     oracle_cnt = 1;
2152     (void) fputs("---\n", tfp);
2153     offset = (unsigned long) ftell(tfp);
2154     Fprintf(ofp, "%05lx\n", offset); /* start pos of first oracle */
2155     in_oracle = FALSE;
2156
2157     while ((line = fgetline(ifp)) != 0) {
2158         SpinCursor(3);
2159
2160         if (h_filter(line)) {
2161             free(line);
2162             continue;
2163         }
2164         if (!strncmp(line, "-----", 5)) {
2165             if (!in_oracle) {
2166                 free(line);
2167                 continue;
2168             }
2169             in_oracle = FALSE;
2170             oracle_cnt++;
2171             (void) fputs("---\n", tfp);
2172             offset = (unsigned long) ftell(tfp);
2173             Fprintf(ofp, "%05lx\n", offset); /* start pos of this oracle */
2174         } else {
2175             in_oracle = TRUE;
2176             (void) fputs(xcrypt(line), tfp);
2177         }
2178         free(line);
2179     }
2180
2181     if (in_oracle) { /* need to terminate last oracle */
2182         oracle_cnt++;
2183         (void) fputs("---\n", tfp);
2184         offset = (unsigned long) ftell(tfp);
2185         Fprintf(ofp, "%05lx\n", offset); /* eof position */
2186     }
2187
2188     /* record the current position */
2189     txt_offset = (unsigned long) ftell(ofp);
2190     Fclose(ifp); /* all done with original input file */
2191
2192     /* reprocess the scratch file; 1st format an error msg, just in case */
2193     line = malloc(BUFSZ + MAXFNAMELEN);
2194     Sprintf(line, "rewind of \"%s\"", tempfile);
2195     if (rewind(tfp) != 0)
2196         goto dead_data;
2197     free(line);
2198     /* copy all lines of text from the scratch file into the output file */
2199     while ((line = fgetline(tfp)) != 0) {
2200         (void) fputs(line, ofp);
2201         free(line);
2202     }
2203
2204     /* finished with scratch file */
2205     Fclose(tfp);
2206     Unlink(tempfile); /* remove it */
2207
2208     /* update the first record of the output file; prepare error msg 1st */
2209     line = malloc(BUFSZ + MAXFNAMELEN);
2210     Sprintf(line, "rewind of \"%s\"", filename);
2211     ok = (rewind(ofp) == 0);
2212     if (ok) {
2213         Sprintf(line, "header rewrite of \"%s\"", filename);
2214         ok = (fprintf(ofp, "%s%5d\n", Dont_Edit_Data, oracle_cnt) >= 0);
2215     }
2216     if (ok) {
2217         Sprintf(line, "data rewrite of \"%s\"", filename);
2218         for (i = 0; i <= oracle_cnt; i++) {
2219 #ifndef VMS /* alpha/vms v1.0; this fflush seems to confuse ftell */
2220             if (!(ok = (fflush(ofp) == 0)))
2221                 break;
2222 #endif
2223             if (!(ok = (fpos = ftell(ofp)) >= 0))
2224                 break;
2225             if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
2226                 break;
2227             if (!(ok = (fscanf(ofp, "%5lx", &offset) == 1)))
2228                 break;
2229 #ifdef MAC
2230 #ifdef __MWERKS__
2231             /*
2232             MetroWerks CodeWarrior Pro 1's (AKA CW12) version of MSL
2233             (ANSI C Libraries) needs this rewind or else the fprintf
2234             stops working.  This may also be true for CW11, but has
2235             never been checked.
2236             */
2237             rewind(ofp);
2238 #endif
2239 #endif
2240             if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
2241                 break;
2242             offset += txt_offset;
2243             if (!(ok = (fprintf(ofp, "%05lx\n", offset) >= 0)))
2244                 break;
2245         }
2246     }
2247     if (!ok) {
2248     dead_data:
2249         perror(line); /* report the problem */
2250         free(line);
2251         /* close and kill the aborted output file, then give up */
2252         Fclose(ofp);
2253         Unlink(filename);
2254         exit(EXIT_FAILURE);
2255     }
2256     free(line);
2257
2258     /* all done */
2259     Fclose(ofp);
2260
2261     return;
2262 }
2263
2264 void
2265 do_dungeon()
2266 {
2267     char *line;
2268
2269     Sprintf(filename, DATA_IN_TEMPLATE, DGN_I_FILE);
2270     if (!(ifp = fopen(filename, RDTMODE))) {
2271         perror(filename);
2272         exit(EXIT_FAILURE);
2273     }
2274     filename[0] = '\0';
2275 #ifdef FILE_PREFIX
2276     Strcat(filename, file_prefix);
2277 #endif
2278     Sprintf(eos(filename), DGN_TEMPLATE, DGN_O_FILE);
2279     if (!(ofp = fopen(filename, WRTMODE))) {
2280         perror(filename);
2281         exit(EXIT_FAILURE);
2282     }
2283     Fprintf(ofp, "%s", Dont_Edit_Data);
2284
2285     tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE);
2286     grep0(ifp, tfp);
2287     ifp = getfp(DATA_TEMPLATE, "grep.tmp", RDTMODE);
2288
2289     while ((line = fgetline(ifp)) != 0) {
2290         SpinCursor(3);
2291
2292         if (line[0] == '#') {
2293             free(line);
2294             continue; /* discard comments */
2295         }
2296         (void) fputs(line, ofp);
2297         free(line);
2298     }
2299     Fclose(ifp);
2300     Fclose(ofp);
2301
2302     delete_file(DATA_TEMPLATE, "grep.tmp");
2303     return;
2304 }
2305
2306 void
2307 do_monstr()
2308 {
2309     /* Don't break anything for ports that haven't been updated. */
2310     printf("DEPRECATION WARNINGS:\n");
2311     printf("'makedefs -m' is deprecated.  Remove all references\n");
2312     printf("  to it from the build process.\n");
2313     printf("'monstr.c' is deprecated.  Remove all references to\n");
2314     printf("  it from the build process.\n");
2315     printf("monstr[] is deprecated.  Replace monstr[x] with\n");
2316     printf("  mons[x].difficulty\n");
2317     printf("monstr_init() is deprecated.  Remove all references to it.\n");
2318
2319     /*
2320      * create the source file, "monstr.c"
2321      */
2322     filename[0] = '\0';
2323 #ifdef FILE_PREFIX
2324     Strcat(filename, file_prefix);
2325 #endif
2326     Sprintf(eos(filename), SOURCE_TEMPLATE, MON_STR_C);
2327     if (!(ofp = fopen(filename, WRTMODE))) {
2328         perror(filename);
2329         exit(EXIT_FAILURE);
2330     }
2331     Fprintf(ofp, "%s", Dont_Edit_Code);
2332     Fprintf(ofp, "#include \"config.h\"\n");
2333     Fprintf(ofp, "\nconst int monstrXXX[] = {\n");
2334     Fprintf(ofp, "0};\n");
2335     Fprintf(ofp, "/*\n");
2336     Fprintf(ofp, "DEPRECATION WARNINGS:\n");
2337     Fprintf(ofp, "'makedefs -m' is deprecated.  Remove all references\n");
2338     Fprintf(ofp, "  to it from the build process.\n");
2339     Fprintf(ofp, "'monstr.c' is deprecated.  Remove all references to\n");
2340     Fprintf(ofp, "  it from the build process.\n");
2341     Fprintf(ofp, "monstr[] is deprecated.  Replace monstr[x] with\n");
2342     Fprintf(ofp, "  mons[x].difficulty\n");
2343     Fprintf(ofp, "monstr_init() is deprecated.  Remove all references to it.\n");
2344     Fprintf(ofp, "*/\n");
2345
2346     Fprintf(ofp, "\nvoid NDECL(monstr_init);\n");
2347     Fprintf(ofp, "\nvoid\n");
2348     Fprintf(ofp, "monstr_init()\n");
2349     Fprintf(ofp, "{\n");
2350     Fprintf(ofp, "    return;\n");
2351     Fprintf(ofp, "}\n");
2352     Fprintf(ofp, "\n/*monstr.c*/\n");
2353
2354     Fclose(ofp);
2355     return;
2356 }
2357
2358 void
2359 do_permonst()
2360 {
2361     int i;
2362     char *c, *nam;
2363
2364     filename[0] = '\0';
2365 #ifdef FILE_PREFIX
2366     Strcat(filename, file_prefix);
2367 #endif
2368     Sprintf(eos(filename), INCLUDE_TEMPLATE, MONST_FILE);
2369     if (!(ofp = fopen(filename, WRTMODE))) {
2370         perror(filename);
2371         exit(EXIT_FAILURE);
2372     }
2373     Fprintf(ofp, "%s", Dont_Edit_Code);
2374     Fprintf(ofp, "#ifndef PM_H\n#define PM_H\n");
2375
2376     for (i = 0; mons[i].mlet; i++) {
2377         SpinCursor(3);
2378
2379         Fprintf(ofp, "\n#define\tPM_");
2380         if (mons[i].mlet == S_HUMAN && !strncmp(mons[i].mname, "were", 4))
2381             Fprintf(ofp, "HUMAN_");
2382         for (nam = c = tmpdup(mons[i].mname); *c; c++)
2383             if (*c >= 'a' && *c <= 'z')
2384                 *c -= (char) ('a' - 'A');
2385             else if (*c < 'A' || *c > 'Z')
2386                 *c = '_';
2387         Fprintf(ofp, "%s\t%d", nam, i);
2388     }
2389     Fprintf(ofp, "\n\n#define\tNUMMONS\t%d\n", i);
2390     Fprintf(ofp, "\n#endif /* PM_H */\n");
2391     Fclose(ofp);
2392     return;
2393 }
2394
2395 /*      Start of Quest text file processing. */
2396 #include "qtext.h"
2397
2398 static struct qthdr qt_hdr;
2399 static struct msghdr msg_hdr[N_HDR];
2400 static struct qtmsg *curr_msg;
2401
2402 static int qt_line;
2403
2404 static boolean in_msg;
2405 #define NO_MSG 1 /* strlen of a null line returned by fgets() */
2406
2407 static boolean
2408 qt_comment(s)
2409 char *s;
2410 {
2411     if (s[0] == '#')
2412         return  TRUE;
2413     return (boolean) (!in_msg && strlen(s) == NO_MSG);
2414 }
2415
2416 static boolean
2417 qt_control(s)
2418 char *s;
2419 {
2420     return (boolean) (s[0] == '%' && (s[1] == 'C' || s[1] == 'E'));
2421 }
2422
2423 static int
2424 get_hdr(code)
2425 char *code;
2426 {
2427     int i;
2428
2429     for (i = 0; i < qt_hdr.n_hdr; i++)
2430         if (!strncmp(code, qt_hdr.id[i], LEN_HDR))
2431             return ++i;
2432
2433     return 0;
2434 }
2435
2436 static boolean
2437 new_id(code)
2438 char *code;
2439 {
2440     if (qt_hdr.n_hdr >= N_HDR) {
2441         Fprintf(stderr, OUT_OF_HEADERS, qt_line);
2442         return FALSE;
2443     }
2444
2445     strncpy(&qt_hdr.id[qt_hdr.n_hdr][0], code, LEN_HDR);
2446     msg_hdr[qt_hdr.n_hdr].n_msg = 0;
2447     qt_hdr.offset[qt_hdr.n_hdr++] = 0L;
2448     return TRUE;
2449 }
2450
2451 static boolean
2452 known_msg(num, id)
2453 int num, id;
2454 {
2455     int i;
2456
2457     for (i = 0; i < msg_hdr[num].n_msg; i++)
2458         if (msg_hdr[num].qt_msg[i].msgnum == id)
2459             return TRUE;
2460
2461     return FALSE;
2462 }
2463
2464 static void
2465 new_msg(s, num, id)
2466 char *s;
2467 int num, id;
2468 {
2469     struct qtmsg *qt_msg;
2470
2471     if (msg_hdr[num].n_msg >= N_MSG) {
2472         Fprintf(stderr, OUT_OF_MESSAGES, qt_line);
2473     } else {
2474         qt_msg = &(msg_hdr[num].qt_msg[msg_hdr[num].n_msg++]);
2475         qt_msg->msgnum = id;
2476         qt_msg->delivery = s[2];
2477         qt_msg->offset = qt_msg->size = qt_msg->summary_size = 0L;
2478
2479         curr_msg = qt_msg;
2480     }
2481 }
2482
2483 /* check %E record for "[summary text]" that nethack can stuff into the
2484    message history buffer when delivering text via window instead of pline */
2485 static char *
2486 valid_qt_summary(s, parsing)
2487 char *s;         /* end record: "%E" optionally followed by " [summary]" */
2488 boolean parsing; /* curr_msg is valid iff this is True */
2489 {
2490     static char summary[BUFSZ];
2491     char *p;
2492
2493     if (*s != '%' || *(s + 1) != 'E')
2494         return (char *) 0;
2495     if ((p = index(s, '[')) == 0)
2496         return (char *) 0;
2497     /* note: opening '[' and closing ']' will be retained in the output;
2498        anything after ']' will be discarded by putting a newline there */
2499     Strcpy(summary, p);
2500
2501     /* have an opening bracket; summary[] holds it and all text that follows
2502      */
2503     p = eos(summary);
2504     /* find closing bracket */
2505     while (p > summary && *(p - 1) != ']')
2506         --p;
2507
2508     if (p == summary) {
2509         /* we backed up all the way to the start without finding a bracket */
2510         if (parsing) /* malformed summary */
2511             Fprintf(stderr, MAL_SUM, qt_line);
2512     } else if (p == summary + 1) {
2513         ;    /* ignore empty [] */
2514     } else { /* got something */
2515              /* p points one spot past ']', usually to '\n';
2516                 we need to include the \n as part of the size */
2517         if (parsing) {
2518             /* during the writing pass we won't be able to recheck
2519                delivery, so any useless summary for a pline mode
2520                message has to be carried along to the output file */
2521             if (curr_msg->delivery == 'p')
2522                 Fprintf(stderr, DUMB_SUM, qt_line);
2523             /* +1 is for terminating newline */
2524             curr_msg->summary_size = (long) (p - summary) + 1L;
2525         } else {
2526             /* caller is writing rather than just parsing;
2527                force newline after the closing bracket */
2528             Strcpy(p, "\n");
2529         }
2530         return summary;
2531     }
2532     return (char *) 0;
2533 }
2534
2535 static void
2536 do_qt_control(s)
2537 char *s;
2538 {
2539     char code[BUFSZ];
2540     int num, id = 0;
2541
2542     if (!index(s, '\n'))
2543         Fprintf(stderr, CTRL_TRUNC, qt_line);
2544
2545     switch (s[1]) {
2546     case 'C':
2547         if (in_msg) {
2548             Fprintf(stderr, CREC_IN_MSG, qt_line);
2549             break;
2550         } else {
2551             in_msg = TRUE;
2552             if (sscanf(&s[4], "%s %5d", code, &id) != 2) {
2553                 Fprintf(stderr, UNREC_CREC, qt_line);
2554                 break;
2555             }
2556             num = get_hdr(code);
2557             if (!num && !new_id(code))
2558                 break;
2559             num = get_hdr(code) - 1;
2560             if (known_msg(num, id))
2561                 Fprintf(stderr, DUP_MSG, qt_line);
2562             else
2563                 new_msg(s, num, id);
2564         }
2565         break;
2566
2567     case 'E':
2568         if (!in_msg) {
2569             Fprintf(stderr, END_NOT_IN_MSG, qt_line);
2570         } else {
2571             /* sets curr_msg->summary_size if applicable */
2572             (void) valid_qt_summary(s, TRUE);
2573             in_msg = FALSE;
2574         }
2575         break;
2576
2577     default:
2578         Fprintf(stderr, UNREC_CREC, qt_line);
2579         break;
2580     }
2581 }
2582
2583 static void
2584 do_qt_text(s)
2585 char *s;
2586 {
2587     if (!in_msg) {
2588         Fprintf(stderr, TEXT_NOT_IN_MSG, qt_line);
2589     } else if (!index(s, '\n')) {
2590         Fprintf(stderr, TEXT_TRUNC, qt_line);
2591     }
2592
2593     curr_msg->size += strlen(s);
2594     return;
2595 }
2596
2597 static void
2598 adjust_qt_hdrs()
2599 {
2600     int i, j;
2601     long count = 0L, hdr_offset = sizeof(int)
2602                                   + (sizeof(char) * LEN_HDR + sizeof(long))
2603                                         * qt_hdr.n_hdr;
2604
2605     for (i = 0; i < qt_hdr.n_hdr; i++) {
2606         qt_hdr.offset[i] = hdr_offset;
2607         hdr_offset += sizeof(int) + sizeof(struct qtmsg) * msg_hdr[i].n_msg;
2608     }
2609
2610     for (i = 0; i < qt_hdr.n_hdr; i++)
2611         for (j = 0; j < msg_hdr[i].n_msg; j++) {
2612             msg_hdr[i].qt_msg[j].offset = hdr_offset + count;
2613             count +=
2614                 msg_hdr[i].qt_msg[j].size + msg_hdr[i].qt_msg[j].summary_size;
2615         }
2616     return;
2617 }
2618
2619 static void
2620 put_qt_hdrs()
2621 {
2622     int i;
2623
2624     /*
2625      *  The main header record.
2626      */
2627     if (debug)
2628         Fprintf(stderr, "%ld: header info.\n", ftell(ofp));
2629     (void) fwrite((genericptr_t) & (qt_hdr.n_hdr), sizeof(int), 1, ofp);
2630     (void) fwrite((genericptr_t) & (qt_hdr.id[0][0]), sizeof(char) * LEN_HDR,
2631                   qt_hdr.n_hdr, ofp);
2632     (void) fwrite((genericptr_t) & (qt_hdr.offset[0]), sizeof(long),
2633                   qt_hdr.n_hdr, ofp);
2634     if (debug) {
2635         for (i = 0; i < qt_hdr.n_hdr; i++)
2636             Fprintf(stderr, "%s @ %ld, ", qt_hdr.id[i], qt_hdr.offset[i]);
2637         Fprintf(stderr, "\n");
2638     }
2639
2640     /*
2641      *  The individual class headers.
2642      */
2643     for (i = 0; i < qt_hdr.n_hdr; i++) {
2644         if (debug)
2645             Fprintf(stderr, "%ld: %s header info.\n", ftell(ofp),
2646                     qt_hdr.id[i]);
2647         (void) fwrite((genericptr_t) & (msg_hdr[i].n_msg), sizeof(int), 1,
2648                       ofp);
2649         (void) fwrite((genericptr_t) & (msg_hdr[i].qt_msg[0]),
2650                       sizeof(struct qtmsg), msg_hdr[i].n_msg, ofp);
2651         if (debug) {
2652             int j;
2653
2654             for (j = 0; j < msg_hdr[i].n_msg; j++) {
2655                 Fprintf(stderr, "msg %d @ %ld (%ld)",
2656                         msg_hdr[i].qt_msg[j].msgnum,
2657                         msg_hdr[i].qt_msg[j].offset,
2658                         msg_hdr[i].qt_msg[j].size);
2659                 if (msg_hdr[i].qt_msg[j].summary_size)
2660                     Fprintf(stderr, " [%ld]",
2661                             msg_hdr[i].qt_msg[j].summary_size);
2662                 Fprintf(stderr, "\n");
2663             }
2664         }
2665     }
2666 }
2667
2668 void
2669 do_questtxt()
2670 {
2671     char *line;
2672
2673     Sprintf(filename, DATA_IN_TEMPLATE, QTXT_I_FILE);
2674     if (!(ifp = fopen(filename, RDTMODE))) {
2675         perror(filename);
2676         exit(EXIT_FAILURE);
2677     }
2678
2679     filename[0] = '\0';
2680 #ifdef FILE_PREFIX
2681     Strcat(filename, file_prefix);
2682 #endif
2683     Sprintf(eos(filename), DATA_TEMPLATE, QTXT_O_FILE);
2684     if (!(ofp = fopen(filename, WRBMODE))) {
2685         perror(filename);
2686         Fclose(ifp);
2687         exit(EXIT_FAILURE);
2688     }
2689
2690     qt_hdr.n_hdr = 0;
2691     qt_line = 0;
2692     in_msg = FALSE;
2693
2694     while ((line = fgetline(ifp)) != 0) {
2695         SpinCursor(3);
2696
2697         qt_line++;
2698         if (qt_control(line))
2699             do_qt_control(line);
2700         else if (qt_comment(line)) {
2701             free(line);
2702             continue;
2703         } else
2704             do_qt_text(line);
2705         free(line);
2706     }
2707
2708     (void) rewind(ifp);
2709     in_msg = FALSE;
2710     adjust_qt_hdrs();
2711     put_qt_hdrs();
2712     while ((line = fgetline(ifp)) != 0) {
2713         if (qt_control(line)) {
2714             char *summary_p = 0;
2715
2716             in_msg = (line[1] == 'C');
2717             if (!in_msg)
2718                 summary_p = valid_qt_summary(line, FALSE);
2719             /* don't write anything unless we've got a summary */
2720             if (!summary_p) {
2721                 free(line);
2722                 continue;
2723             }
2724             /* we have summary text; replace raw %E record with it */
2725             Strcpy(line, summary_p); /* (guaranteed to fit) */
2726         } else if (qt_comment(line)) {
2727             free(line);
2728             continue;
2729         }
2730         if (debug)
2731             Fprintf(stderr, "%ld: %s", ftell(stdout), line);
2732         (void) fputs(xcrypt(line), ofp);
2733         free(line);
2734     }
2735     Fclose(ifp);
2736     Fclose(ofp);
2737     return;
2738 }
2739
2740 static char temp[32];
2741
2742 static char *limit(name, pref) /* limit a name to 30 characters length */
2743 char *name;
2744 int pref;
2745 {
2746     (void) strncpy(temp, name, pref ? 26 : 30);
2747     temp[pref ? 26 : 30] = 0;
2748     return temp;
2749 }
2750
2751 void
2752 do_objs()
2753 {
2754     int i, sum = 0;
2755     char *c, *objnam;
2756     int nspell = 0;
2757     int prefix = 0;
2758     char class = '\0';
2759     boolean sumerr = FALSE;
2760
2761     filename[0] = '\0';
2762 #ifdef FILE_PREFIX
2763     Strcat(filename, file_prefix);
2764 #endif
2765     Sprintf(eos(filename), INCLUDE_TEMPLATE, ONAME_FILE);
2766     if (!(ofp = fopen(filename, WRTMODE))) {
2767         perror(filename);
2768         exit(EXIT_FAILURE);
2769     }
2770     Fprintf(ofp, "%s", Dont_Edit_Code);
2771     Fprintf(ofp, "#ifndef ONAMES_H\n#define ONAMES_H\n\n");
2772
2773     for (i = 0; !i || objects[i].oc_class != ILLOBJ_CLASS; i++) {
2774         SpinCursor(3);
2775
2776         objects[i].oc_name_idx = objects[i].oc_descr_idx = i; /* init */
2777         if (!(objnam = tmpdup(OBJ_NAME(objects[i]))))
2778             continue;
2779
2780         /* make sure probabilities add up to 1000 */
2781         if (objects[i].oc_class != class) {
2782             if (sum && sum != 1000) {
2783                 Fprintf(stderr, "prob error for class %d (%d%%)", class, sum);
2784                 (void) fflush(stderr);
2785                 sumerr = TRUE;
2786             }
2787             class = objects[i].oc_class;
2788             sum = 0;
2789         }
2790
2791         for (c = objnam; *c; c++)
2792             if (*c >= 'a' && *c <= 'z')
2793                 *c -= (char) ('a' - 'A');
2794             else if (*c < 'A' || *c > 'Z')
2795                 *c = '_';
2796
2797         switch (class) {
2798         case WAND_CLASS:
2799             Fprintf(ofp, "#define\tWAN_");
2800             prefix = 1;
2801             break;
2802         case RING_CLASS:
2803             Fprintf(ofp, "#define\tRIN_");
2804             prefix = 1;
2805             break;
2806         case POTION_CLASS:
2807             Fprintf(ofp, "#define\tPOT_");
2808             prefix = 1;
2809             break;
2810         case SPBOOK_CLASS:
2811             Fprintf(ofp, "#define\tSPE_");
2812             prefix = 1;
2813             nspell++;
2814             break;
2815         case SCROLL_CLASS:
2816             Fprintf(ofp, "#define\tSCR_");
2817             prefix = 1;
2818             break;
2819         case AMULET_CLASS:
2820             /* avoid trouble with stupid C preprocessors */
2821             Fprintf(ofp, "#define\t");
2822             if (objects[i].oc_material == PLASTIC) {
2823                 Fprintf(ofp, "FAKE_AMULET_OF_YENDOR\t%d\n", i);
2824                 prefix = -1;
2825                 break;
2826             }
2827             break;
2828         case GEM_CLASS:
2829             /* avoid trouble with stupid C preprocessors */
2830             if (objects[i].oc_material == GLASS) {
2831                 Fprintf(ofp, "/* #define\t%s\t%d */\n", objnam, i);
2832                 prefix = -1;
2833                 break;
2834             }
2835             /*FALLTHRU*/
2836         default:
2837             Fprintf(ofp, "#define\t");
2838         }
2839         if (prefix >= 0)
2840             Fprintf(ofp, "%s\t%d\n", limit(objnam, prefix), i);
2841         prefix = 0;
2842
2843         sum += objects[i].oc_prob;
2844     }
2845
2846     /* check last set of probabilities */
2847     if (sum && sum != 1000) {
2848         Fprintf(stderr, "prob error for class %d (%d%%)", class, sum);
2849         (void) fflush(stderr);
2850         sumerr = TRUE;
2851     }
2852
2853     Fprintf(ofp, "#define\tLAST_GEM\t(JADE)\n");
2854     Fprintf(ofp, "#define\tMAXSPELL\t%d\n", nspell + 1);
2855     Fprintf(ofp, "#define\tNUM_OBJECTS\t%d\n", i);
2856
2857     Fprintf(ofp, "\n/* Artifacts (unique objects) */\n\n");
2858
2859     for (i = 1; artifact_names[i]; i++) {
2860         SpinCursor(3);
2861
2862         for (c = objnam = tmpdup(artifact_names[i]); *c; c++)
2863             if (*c >= 'a' && *c <= 'z')
2864                 *c -= (char) ('a' - 'A');
2865             else if (*c < 'A' || *c > 'Z')
2866                 *c = '_';
2867
2868         if (!strncmp(objnam, "THE_", 4))
2869             objnam += 4;
2870         /* fudge _platinum_ YENDORIAN EXPRESS CARD */
2871         if (!strncmp(objnam, "PLATINUM_", 9))
2872             objnam += 9;
2873         Fprintf(ofp, "#define\tART_%s\t%d\n", limit(objnam, 1), i);
2874     }
2875
2876     Fprintf(ofp, "#define\tNROFARTIFACTS\t%d\n", i - 1);
2877     Fprintf(ofp, "\n#endif /* ONAMES_H */\n");
2878     Fclose(ofp);
2879     if (sumerr)
2880         exit(EXIT_FAILURE);
2881     return;
2882 }
2883
2884 /* Read one line from input, up to and including the next newline
2885  * character. Returns a pointer to the heap-allocated string, or a
2886  * null pointer if no characters were read.
2887  */
2888 static char *
2889 fgetline(fd)
2890 FILE *fd;
2891 {
2892     static const int inc = 256;
2893     int len = inc;
2894     char *c = malloc(len), *ret;
2895
2896     for (;;) {
2897         ret = fgets(c + len - inc, inc, fd);
2898         if (!ret) {
2899             free(c);
2900             c = NULL;
2901             break;
2902         } else if (index(c, '\n')) {
2903             /* normal case: we have a full line */
2904             break;
2905         }
2906         len += inc;
2907         c = realloc(c, len);
2908     }
2909     return c;
2910 }
2911
2912 static char *
2913 tmpdup(str)
2914 const char *str;
2915 {
2916     static char buf[128];
2917
2918     if (!str)
2919         return (char *) 0;
2920     (void) strncpy(buf, str, 127);
2921     return buf;
2922 }
2923
2924 static char *
2925 eos(str)
2926 char *str;
2927 {
2928     while (*str)
2929         str++;
2930     return str;
2931 }
2932
2933 /*
2934  * macro used to control vision algorithms:
2935  *      VISION_TABLES => generate tables
2936  */
2937
2938 void
2939 do_vision()
2940 {
2941 #ifdef VISION_TABLES
2942     int i, j;
2943
2944     /* Everything is clear.  xclear may be malloc'ed.
2945      * Block the upper left corner (BLOCK_HEIGHTxBLOCK_WIDTH)
2946      */
2947     for (i = 0; i < MAX_ROW; i++)
2948         for (j = 0; j < MAX_COL; j++)
2949             if (i < BLOCK_HEIGHT && j < BLOCK_WIDTH)
2950                 xclear[i][j] = '\000';
2951             else
2952                 xclear[i][j] = '\001';
2953 #endif /* VISION_TABLES */
2954
2955     SpinCursor(3);
2956
2957     /*
2958      * create the include file, "vis_tab.h"
2959      */
2960     filename[0] = '\0';
2961 #ifdef FILE_PREFIX
2962     Strcat(filename, file_prefix);
2963 #endif
2964     Sprintf(eos(filename), INCLUDE_TEMPLATE, VIS_TAB_H);
2965     if (!(ofp = fopen(filename, WRTMODE))) {
2966         perror(filename);
2967         exit(EXIT_FAILURE);
2968     }
2969     Fprintf(ofp, "%s", Dont_Edit_Code);
2970     Fprintf(ofp, "#ifdef VISION_TABLES\n");
2971 #ifdef VISION_TABLES
2972     H_close_gen();
2973     H_far_gen();
2974 #endif /* VISION_TABLES */
2975     Fprintf(ofp, "\n#endif /* VISION_TABLES */\n");
2976     Fclose(ofp);
2977
2978     SpinCursor(3);
2979
2980     /*
2981      * create the source file, "vis_tab.c"
2982      */
2983     filename[0] = '\0';
2984 #ifdef FILE_PREFIX
2985     Strcat(filename, file_prefix);
2986 #endif
2987     Sprintf(eos(filename), SOURCE_TEMPLATE, VIS_TAB_C);
2988     if (!(ofp = fopen(filename, WRTMODE))) {
2989         perror(filename);
2990         /* creating vis_tab.c failed; remove the vis_tab.h we just made */
2991         filename[0] = '\0';
2992 #ifdef FILE_PREFIX
2993         Strcat(filename, file_prefix);
2994 #endif
2995         Sprintf(eos(filename), INCLUDE_TEMPLATE, VIS_TAB_H);
2996         Unlink(filename);
2997         exit(EXIT_FAILURE);
2998     }
2999     Fprintf(ofp, "%s", Dont_Edit_Code);
3000     Fprintf(ofp, "#include \"config.h\"\n");
3001     Fprintf(ofp, "#ifdef VISION_TABLES\n");
3002     Fprintf(ofp, "#include \"vis_tab.h\"\n");
3003
3004     SpinCursor(3);
3005
3006 #ifdef VISION_TABLES
3007     C_close_gen();
3008     C_far_gen();
3009     Fprintf(ofp, "\nvoid vis_tab_init() { return; }\n");
3010 #endif /* VISION_TABLES */
3011
3012     SpinCursor(3);
3013
3014     Fprintf(ofp, "\n#endif /* VISION_TABLES */\n");
3015     Fprintf(ofp, "\n/*vis_tab.c*/\n");
3016
3017     Fclose(ofp);
3018     return;
3019 }
3020
3021 #ifdef VISION_TABLES
3022
3023 /*--------------  vision tables  --------------*\
3024  *
3025  *  Generate the close and far tables.  This is done by setting up a
3026  *  fake dungeon and moving our source to different positions relative
3027  *  to a block and finding the first/last visible position.  The fake
3028  *  dungeon is all clear execpt for the upper left corner (BLOCK_HEIGHT
3029  *  by BLOCK_WIDTH) is blocked.  Then we move the source around relative
3030  *  to the corner of the block.  For each new position of the source
3031  *  we check positions on rows "kittycorner" from the source.  We check
3032  *  positions until they are either in sight or out of sight (depends on
3033  *  which table we are generating).  The picture below shows the setup
3034  *  for the generation of the close table.  The generation of the far
3035  *  table would switch the quadrants of the '@' and the "Check rows
3036  *  here".
3037  *
3038  *
3039  *  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
3040  *  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
3041  *  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,, Check rows here ,,,,,,,,,,,,
3042  *  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
3043  *  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXB,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
3044  *  ...............................
3045  *  ...............................
3046  *  .........@.....................
3047  *  ...............................
3048  *
3049  *      Table generation figure (close_table).  The 'X's are blocked points.
3050  *      The 'B' is a special blocked point.  The '@' is the source.  The ','s
3051  *      are the target area.  The '.' are just open areas.
3052  *
3053  *
3054  *  Example usage of close_table[][][].
3055  *
3056  *  The table is as follows:
3057  *
3058  *      dy = |row of '@' - row of 'B'|  - 1
3059  *      dx = |col of '@' - col of 'B'|
3060  *
3061  *  The first indices are the deltas from the source '@' and the block 'B'.
3062  *  You must check for the value inside the abs value bars being zero.  If
3063  *  so then the block is on the same row and you don't need to do a table
3064  *  lookup.  The last value:
3065  *
3066  *      dcy = |row of block - row to be checked|
3067  *
3068  *  Is the value of the first visible spot on the check row from the
3069  *  block column.  So
3070  *
3071  *  first visible col = close_table[dy][dx][dcy] + col of 'B'
3072  *
3073 \*--------------  vision tables  --------------*/
3074
3075 static void
3076 H_close_gen()
3077 {
3078     Fprintf(ofp, "\n/* Close */\n");
3079     Fprintf(ofp,
3080             "#define CLOSE_MAX_SB_DY %2d\t/* |src row - block row| - 1\t*/\n",
3081             TEST_HEIGHT - 1);
3082     Fprintf(ofp,
3083             "#define CLOSE_MAX_SB_DX %2d\t/* |src col - block col|\t*/\n",
3084             TEST_WIDTH);
3085     Fprintf(ofp,
3086             "#define CLOSE_MAX_BC_DY %2d\t/* |block row - check row|\t*/\n",
3087             TEST_HEIGHT);
3088     Fprintf(ofp, "typedef struct {\n");
3089     Fprintf(ofp,
3090             "    unsigned char close[CLOSE_MAX_SB_DX][CLOSE_MAX_BC_DY];\n");
3091     Fprintf(ofp, "} close2d;\n");
3092     Fprintf(ofp, "extern close2d close_table[CLOSE_MAX_SB_DY];\n");
3093     return;
3094 }
3095
3096 static void
3097 H_far_gen()
3098 {
3099     Fprintf(ofp, "\n/* Far */\n");
3100     Fprintf(ofp, "#define FAR_MAX_SB_DY %2d\t/* |src row - block row|\t*/\n",
3101             TEST_HEIGHT);
3102     Fprintf(ofp,
3103             "#define FAR_MAX_SB_DX %2d\t/* |src col - block col| - 1\t*/\n",
3104             TEST_WIDTH - 1);
3105     Fprintf(ofp,
3106             "#define FAR_MAX_BC_DY %2d\t/* |block row - check row| - 1\t*/\n",
3107             TEST_HEIGHT - 1);
3108     Fprintf(ofp, "typedef struct {\n");
3109     Fprintf(ofp, "    unsigned char far_q[FAR_MAX_SB_DX][FAR_MAX_BC_DY];\n");
3110     Fprintf(ofp, "} far2d;\n");
3111     Fprintf(ofp, "extern far2d far_table[FAR_MAX_SB_DY];\n");
3112     return;
3113 }
3114
3115 static void
3116 C_close_gen()
3117 {
3118     int i, dx, dy;
3119     int src_row, src_col;     /* source */
3120     int block_row, block_col; /* block */
3121     int this_row;
3122     int no_more;
3123     const char *delim;
3124
3125     block_row = BLOCK_HEIGHT - 1;
3126     block_col = BLOCK_WIDTH - 1;
3127
3128     Fprintf(ofp, "\n#ifndef FAR_TABLE_ONLY\n");
3129     Fprintf(ofp, "\nclose2d close_table[CLOSE_MAX_SB_DY] = {\n");
3130 #ifndef no_vision_progress
3131     Fprintf(stderr, "\nclose:");
3132 #endif
3133
3134     for (dy = 1; dy < TEST_HEIGHT; dy++) {
3135         src_row = block_row + dy;
3136         Fprintf(ofp, "/* DY = %2d (- 1)*/\n  {{\n", dy);
3137 #ifndef no_vision_progress
3138         Fprintf(stderr, " %2d", dy), (void) fflush(stderr);
3139 #endif
3140         for (dx = 0; dx < TEST_WIDTH; dx++) {
3141             src_col = block_col - dx;
3142             Fprintf(ofp, "  /*%2d*/ {", dx);
3143
3144             no_more = 0;
3145             for (this_row = 0; this_row < TEST_HEIGHT; this_row++) {
3146                 delim = (this_row < TEST_HEIGHT - 1) ? "," : "";
3147                 if (no_more) {
3148                     Fprintf(ofp, "%s%s", CLOSE_OFF_TABLE_STRING, delim);
3149                     continue;
3150                 }
3151                 SpinCursor(3);
3152
3153                 /* Find the first column that we can see. */
3154                 for (i = block_col + 1; i < MAX_COL; i++) {
3155                     if (clear_path(src_row, src_col, block_row - this_row, i))
3156                         break;
3157                 }
3158
3159                 if (i == MAX_COL)
3160                     no_more = 1;
3161                 Fprintf(ofp, "%2d%s", i - block_col, delim);
3162             }
3163             Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n");
3164         }
3165         Fprintf(ofp, "  }},\n");
3166     }
3167
3168     Fprintf(ofp, "}; /* close_table[] */\n"); /* closing brace for table */
3169     Fprintf(ofp, "#endif /* !FAR_TABLE_ONLY */\n");
3170 #ifndef no_vision_progress
3171     Fprintf(stderr, "\n");
3172 #endif
3173     return;
3174 }
3175
3176 static void
3177 C_far_gen()
3178 {
3179     int i, dx, dy;
3180     int src_row, src_col;     /* source */
3181     int block_row, block_col; /* block */
3182     int this_row;
3183     const char *delim;
3184
3185     block_row = BLOCK_HEIGHT - 1;
3186     block_col = BLOCK_WIDTH - 1;
3187
3188     Fprintf(ofp, "\n#ifndef CLOSE_TABLE_ONLY\n");
3189     Fprintf(ofp, "\nfar2d far_table[FAR_MAX_SB_DY] = {\n");
3190 #ifndef no_vision_progress
3191     Fprintf(stderr, "\n_far_:");
3192 #endif
3193
3194     for (dy = 0; dy < TEST_HEIGHT; dy++) {
3195         src_row = block_row - dy;
3196         Fprintf(ofp, "/* DY = %2d */\n  {{\n", dy);
3197 #ifndef no_vision_progress
3198         Fprintf(stderr, " %2d", dy), (void) fflush(stderr);
3199 #endif
3200         for (dx = 1; dx < TEST_WIDTH; dx++) {
3201             src_col = block_col + dx;
3202             Fprintf(ofp, "  /*%2d(-1)*/ {", dx);
3203
3204             for (this_row = block_row + 1; this_row < block_row + TEST_HEIGHT;
3205                  this_row++) {
3206                 delim = (this_row < block_row + TEST_HEIGHT - 1) ? "," : "";
3207
3208                 SpinCursor(3);
3209                 /* Find first col that we can see. */
3210                 for (i = 0; i <= block_col; i++) {
3211                     if (clear_path(src_row, src_col, this_row, i))
3212                         break;
3213                 }
3214
3215                 if (block_col - i < 0)
3216                     Fprintf(ofp, "%s%s", FAR_OFF_TABLE_STRING, delim);
3217                 else
3218                     Fprintf(ofp, "%2d%s", block_col - i, delim);
3219             }
3220             Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n");
3221         }
3222         Fprintf(ofp, "  }},\n");
3223     }
3224
3225     Fprintf(ofp, "}; /* far_table[] */\n"); /* closing brace for table */
3226     Fprintf(ofp, "#endif /* !CLOSE_TABLE_ONLY */\n");
3227 #ifndef no_vision_progress
3228     Fprintf(stderr, "\n");
3229 #endif
3230     return;
3231 }
3232
3233 /*
3234  *  "Draw" a line from the hero to the given location.  Stop if we hit a
3235  *  wall.
3236  *
3237  *  Generalized integer Bresenham's algorithm (fast line drawing) for
3238  *  all quadrants.  From _Procedural Elements for Computer Graphics_, by
3239  *  David F. Rogers.  McGraw-Hill, 1985.
3240  *
3241  *  I have tried a little bit of optimization by pulling compares out of
3242  *  the inner loops.
3243  *
3244  *  NOTE:  This had better *not* be called from a position on the
3245  *  same row as the hero.
3246  */
3247 static int
3248 clear_path(you_row, you_col, y2, x2)
3249 int you_row, you_col, y2, x2;
3250 {
3251     int dx, dy, s1, s2;
3252     register int i, error, x, y, dxs, dys;
3253
3254     x = you_col;
3255     y = you_row;
3256     dx = abs(x2 - you_col);
3257     dy = abs(y2 - you_row);
3258     s1 = sign(x2 - you_col);
3259     s2 = sign(y2 - you_row);
3260
3261     if (s1 == 0) {     /* same column */
3262         if (s2 == 1) { /* below (larger y2 value) */
3263             for (i = you_row + 1; i < y2; i++)
3264                 if (!xclear[i][you_col])
3265                     return 0;
3266         } else { /* above (smaller y2 value) */
3267             for (i = y2 + 1; i < you_row; i++)
3268                 if (!xclear[i][you_col])
3269                     return 0;
3270         }
3271         return 1;
3272     }
3273
3274     /*
3275      *  Lines at 0 and 90 degrees have been weeded out.
3276      */
3277     if (dy > dx) {
3278         error = dx;
3279         dx = dy;
3280         dy = error;    /* swap the values */
3281         dxs = dx << 1; /* save the shifted values */
3282         dys = dy << 1;
3283         error = dys - dx; /* NOTE: error is used as a temporary above */
3284
3285         for (i = 0; i < dx; i++) {
3286             if (!xclear[y][x])
3287                 return 0; /* plot point */
3288
3289             while (error >= 0) {
3290                 x += s1;
3291                 error -= dxs;
3292             }
3293             y += s2;
3294             error += dys;
3295         }
3296     } else {
3297         dxs = dx << 1; /* save the shifted values */
3298         dys = dy << 1;
3299         error = dys - dx;
3300
3301         for (i = 0; i < dx; i++) {
3302             if (!xclear[y][x])
3303                 return 0; /* plot point */
3304
3305             while (error >= 0) {
3306                 y += s2;
3307                 error -= dxs;
3308             }
3309             x += s1;
3310             error += dys;
3311         }
3312     }
3313     return 1;
3314 }
3315 #endif /* VISION_TABLES */
3316
3317 #ifdef STRICT_REF_DEF
3318 NEARDATA struct flag flags;
3319 #ifdef ATTRIB_H
3320 struct attribs attrmax, attrmin;
3321 #endif
3322 #endif /* STRICT_REF_DEF */
3323
3324 /*makedefs.c*/