OSDN Git Service

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