OSDN Git Service

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