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