OSDN Git Service

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