1 /* NetHack 3.7  makedefs.c  $NHDT-Date: 1600855420 2020/09/23 10:03:40 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.188 $ */
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 #include <ctype.h>
27 #ifdef MAC
28 #if defined(__SC__) || defined(__MRC__) /* MPW compilers */
29 #define MPWTOOL
30 #include <CursorCtl.h>
31 #include <string.h>
32 #else /* MAC without MPWTOOL */
33 #define MACsansMPWTOOL
34 #endif
35 #endif /* MAC */
36 
37 #ifndef MPWTOOL
38 #define SpinCursor(x)
39 #endif
40 
41 #define Fprintf (void) fprintf
42 #define Fclose (void) fclose
43 #define Unlink (void) unlink
44 #if !defined(AMIGA) || defined(AZTEC_C)
45 #define rewind(fp) fseek((fp), 0L, SEEK_SET) /* guarantee a return value */
46 #endif
47 
48 #if defined(UNIX) && !defined(LINT) && !defined(GCC_WARN)
49 static const char SCCS_Id[] UNUSED = "@(#)makedefs.c\t3.7\t2020/01/18";
50 #endif
51 
52 /* names of files to be generated */
53 #define DATE_FILE "date.h"
54 #define MONST_FILE "pm.h"
55 #define ONAME_FILE "onames.h"
56 #ifndef OPTIONS_FILE
57 #define OPTIONS_FILE "options"
58 #endif
59 #define ORACLE_FILE "oracles"
60 #define DATA_FILE "data"
61 #define RUMOR_FILE "rumors"
62 #define DGN_I_FILE "dungeon.def"
63 #define DGN_O_FILE "dungeon.pdf"
64 #define MON_STR_C "monstr.c"
65 #if 0
66 #define QTXT_I_FILE "quest.txt"
67 #define QTXT_O_FILE "quest.dat"
68 #endif
69 #define GITINFO_FILE "gitinfo.txt"
70 /* locations for those files */
71 #ifdef AMIGA
72 #define FILE_PREFIX
73 #define INCLUDE_TEMPLATE "NH:include/t.%s"
74 #define SOURCE_TEMPLATE "NH:src/%s"
75 #define DGN_TEMPLATE "NH:dat/%s" /* where dungeon.pdf file goes */
76 #define DATA_TEMPLATE "NH:slib/%s"
77 #define DATA_IN_TEMPLATE "NH:dat/%s"
78 #else /* not AMIGA */
79 #if defined(MAC) && !defined(__MACH__)
80 /* MacOS 9 or earlier */
81 #define INCLUDE_TEMPLATE ":include:%s"
82 #define SOURCE_TEMPLATE ":src:%s"
83 #define DGN_TEMPLATE ":dat:%s" /* where dungeon.pdf file goes */
84 #if __SC__ || __MRC__
85 #define DATA_TEMPLATE ":Dungeon:%s"
86 #else
87 #define DATA_TEMPLATE ":lib:%s"
88 #endif /* __SC__ || __MRC__ */
89 #define DATA_IN_TEMPLATE ":dat:%s"
90 #else /* neither AMIGA nor MAC */
91 #ifdef OS2
92 #define INCLUDE_TEMPLATE "..\\include\\%s"
93 #define SOURCE_TEMPLATE "..\\src\\%s"
94 #define DGN_TEMPLATE "..\\dat\\%s" /* where dungeon.pdf file goes */
95 #define DATA_TEMPLATE "..\\dat\\%s"
96 #define DATA_IN_TEMPLATE "..\\dat\\%s"
97 #else /* not AMIGA, MAC, or OS2 */
98 #define INCLUDE_TEMPLATE "../include/%s"
99 #define SOURCE_TEMPLATE "../src/%s"
100 #define DGN_TEMPLATE "../dat/%s" /* where dungeon.pdf file goes */
101 #define DATA_TEMPLATE "../dat/%s"
102 #define DATA_IN_TEMPLATE "../dat/%s"
103 #endif /* else !OS2 */
104 #endif /* else !MAC */
105 #endif /* else !AMIGA */
106 
107 static const char
108     *Dont_Edit_Code =
109         "/* This source file is generated by 'makedefs'.  Do not edit. */\n",
110     *Dont_Edit_Data =
111         "#\tThis data file is generated by 'makedefs'.  Do not edit. \n";
112 
113 static struct version_info version;
114 
115 #define FLG_TEMPFILE  0x01              /* flag for temp file */
116 #define MAXFNAMELEN 600
117 
118 static char filename[MAXFNAMELEN];
119 static char tempfilename[MAXFNAMELEN];
120 
121 #ifdef FILE_PREFIX
122 /* if defined, a first argument not starting with - is
123  * taken as a text string to be prepended to any
124  * output filename generated */
125 char *file_prefix = "";
126 #endif
127 
128 #ifdef MACsansMPWTOOL
129 int main(void);
130 #else
131 int main(int, char **);
132 #endif
133 void do_makedefs(char *);
134 void do_objs(void);
135 void do_data(void);
136 void do_dungeon(void);
137 void do_options(void);
138 void do_monstr(void);
139 void do_permonst(void);
140 void do_questtxt(void);
141 void do_rumors(void);
142 void do_oracles(void);
143 void do_date(void);
144 
145 extern void monst_globals_init(void);   /* monst.c */
146 extern void objects_globals_init(void); /* objects.c */
147 
148 static char *name_file(const char *, const char *);
149 static FILE *getfp(const char *, const char *, const char *, int);
150 static void do_ext_makedefs(int, char **);
151 static char *xcrypt(const char *);
152 static unsigned long read_rumors_file(const char *, int *,
153                                       long *, unsigned long);
154 static void do_rnd_access_file(const char *, const char *);
155 static boolean d_filter(char *);
156 static boolean h_filter(char *);
157 static void opt_out_words(char *, int *);
158 
159 static char *fgetline(FILE*);
160 static char *tmpdup(const char *);
161 static char *limit(char *, int);
162 static void windowing_sanity(void);
163 static boolean get_gitinfo(char *, char *);
164 
165 /* input, output, tmp */
166 static FILE *ifp, *ofp, *tfp;
167 
168 static boolean use_enum = TRUE;
169 
170 #if defined(__BORLANDC__) && !defined(_WIN32)
171 extern unsigned _stklen = STKSIZ;
172 #endif
173 
174 /*
175  * Some of the routines in this source file were moved into .../src/mdlib
176  * to facilitate the use of a cross-compiler generation of some of the
177  * information for the target environment during the game compile portion
178  * under the cross-compiler and/or at runtime in some cases.
179  */
180 
181 #include "../src/mdlib.c"
182 
183 #ifdef MACsansMPWTOOL
184 int
main(void)185 main(void)
186 {
187     const char *def_options = "odemvpqrshz";
188     char buf[100];
189     int len;
190 
191     printf("Enter options to run: [%s] ", def_options);
192     fflush(stdout);
193     fgets(buf, 100, stdin);
194     len = strlen(buf);
195     if (len <= 1)
196         Strcpy(buf, def_options);
197     else
198         buf[len - 1] = 0; /* remove return */
199 
200     if (buf[0] == '-' && buf[1] == '-') {
201 #if 0
202         split up buf into words
203         do_ext_makedefs(fakeargc, fakeargv);
204 #else
205         printf("extended makedefs not implemented for Mac OS9\n");
206         exit(EXIT_FAILURE);
207 #endif
208     }
209 
210     do_makedefs(buf);
211     exit(EXIT_SUCCESS);
212     return 0;
213 }
214 
215 #else /* ! MAC */
216 
217 DISABLE_WARNING_UNREACHABLE_CODE
218 
219 int
main(int argc,char * argv[])220 main(int argc, char *argv[])
221 {
222     if ((argc == 1) ||
223         ((argc != 2)
224 #ifdef FILE_PREFIX
225         && (argc != 3)
226 #endif
227         && !(argv[1][0] == '-' && argv[1][1] == '-'))) {
228         Fprintf(stderr, "Bad arg count (%d).\n", argc - 1);
229         (void) fflush(stderr);
230         return 1;
231     }
232 
233     if (snprintf(tempfilename, sizeof(tempfilename), "%s.%d", "grep.tmp", getpid()) >= sizeof(tempfilename)) {
234         Fprintf(stderr, "Cannot create temporary filename.");
235         (void) fflush(stderr);
236         return 1;
237     }
238 
239 #ifdef FILE_PREFIX
240     if (argc >= 2 && argv[1][0] != '-') {
241         file_prefix = argv[1];
242         argc--;
243         argv++;
244     }
245 #endif
246 
247     if (argv[1][0] == '-' && argv[1][1] == '-') {
248         do_ext_makedefs(argc, argv);
249     } else {
250         do_makedefs(&argv[1][1]);
251     }
252     exit(EXIT_SUCCESS);
253     /*NOTREACHED*/
254     return 0;
255 }
256 #endif
257 
258 RESTORE_WARNINGS
259 
260 void
do_makedefs(char * options)261 do_makedefs(char *options)
262 {
263     boolean more_than_one;
264 
265     objects_globals_init();
266     monst_globals_init();
267 
268     /* construct the current version number */
269     make_version();
270 
271     more_than_one = strlen(options) > 1;
272     while (*options) {
273         if (more_than_one)
274             Fprintf(stderr, "makedefs -%c\n", *options);
275 
276         switch (*options) {
277         case 'o':
278         case 'O':
279             do_objs();
280             break;
281         case 'd':
282         case 'D':
283             do_data();
284             break;
285         case 'e':
286         case 'E':
287             do_dungeon();
288             break;
289         case 'm':
290         case 'M':
291             do_monstr();
292             break;
293         case 'v':
294         case 'V':
295             do_date();
296             do_options();
297             break;
298         case 'p':
299         case 'P':
300             do_permonst();
301             break;
302         case 'q':
303         case 'Q':
304             do_questtxt();
305             break;
306         case 'r':
307         case 'R':
308             do_rumors();
309             break;
310         case 's':
311         case 'S':
312 
313             /*
314              * post-3.6.5:
315              *  File must not be empty to avoid divide by 0
316              *  in core's rn2(), so provide a default entry.
317              */
318             do_rnd_access_file(EPITAPHFILE,
319                 /* default epitaph:  parody of the default engraving */
320                                "No matter where I went, here I am.");
321             do_rnd_access_file(ENGRAVEFILE,
322                 /* default engraving:  popularized by "The Adventures of
323                    Buckaroo Bonzai Across the 8th Dimenstion" but predates
324                    that 1984 movie; some attribute it to Confucius */
325                                "No matter where you go, there you are.");
326             do_rnd_access_file(BOGUSMONFILE,
327                 /* default bogusmon:  iconic monster that isn't in nethack */
328                                "grue");
329             do_rnd_access_file(SHIRTFILE,
330                 /* default shirt:  announcing you're a character in nethack */
331                                "@");
332             break;
333         case 'h':
334         case 'H':
335             do_oracles();
336             break;
337 
338         default:
339             Fprintf(stderr, "Unknown option '%c'.\n", *options);
340             (void) fflush(stderr);
341             exit(EXIT_FAILURE);
342         }
343         options++;
344     }
345     if (more_than_one)
346         Fprintf(stderr, "Completed.\n"); /* feedback */
347 }
348 
349 static char namebuf[1000];
350 
351 DISABLE_WARNING_FORMAT_NONLITERAL
352 
353 static char *
name_file(const char * template,const char * tag)354 name_file(const char* template, const char* tag)
355 {
356     Sprintf(namebuf, template, tag);
357     return namebuf;
358 }
359 
360 #ifdef HAS_NO_MKSTEMP
361 static void delete_file(const char *template, const char *);
362 
363 static void
delete_file(const char * template,const char * tag)364 delete_file(const char *template, const char *tag)
365 {
366     char *name = name_file(template, tag);
367 
368     Unlink(name);
369 }
370 #endif
371 
372 static FILE *
getfp(const char * template,const char * tag,const char * mode,int flg)373 getfp(const char* template, const char* tag, const char* mode, int flg)
374 {
375     char *name = name_file(template, tag);
376     FILE *rv = (FILE *) 0;
377 #ifndef HAS_NO_MKSTEMP
378     boolean istemp = (flg & FLG_TEMPFILE) != 0;
379     char tmpfbuf[MAXFNAMELEN];
380     int tmpfd;
381 
382     if (istemp) {
383         (void) snprintf(tmpfbuf, sizeof tmpfbuf, DATA_TEMPLATE, "mdXXXXXX");
384         tmpfd = mkstemp(tmpfbuf);
385         if (tmpfd >= 0) {
386             rv = fdopen(tmpfd, WRTMODE);   /* temp file is always read+write */
387             Unlink(tmpfbuf);
388         }
389     }
390     else
391 #else
392         flg; // unused
393 #endif
394     rv = fopen(name, mode);
395     if (!rv) {
396         Fprintf(stderr, "Can't open '%s'.\n",
397 #ifndef HAS_NO_MKSTEMP
398                 istemp ? tmpfbuf :
399 #endif
400                  name);
401             exit(EXIT_FAILURE);
402     }
403     return rv;
404 }
405 
406 RESTORE_WARNING_FORMAT_NONLITERAL
407 
408 static boolean debug = FALSE;
409 
410 static FILE *inputfp;
411 static FILE *outputfp;
412 
413 struct grep_var {
414     const char *name;
415     int is_defined; /* 0 undef; 1 defined */
416 };
417 /* struct grep_var grep_vars[] and TODO_* constants in include file: */
418 #include "mdgrep.h"
419 
420 static void do_grep_showvars(void);
421 static struct grep_var *grepsearch(const char *);
422 static int grep_check_id(const char *);
423 static void grep_show_wstack(const char *);
424 static char *do_grep_control(char *);
425 static void do_grep(void);
426 static void grep0(FILE *, FILE *, int);
427 
428 static int grep_trace = 0;
429 
430 #define IS_OPTION(str) if (!strcmp(&argv[0][2], str))
431 #define CONTINUE    \
432     argv++, argc--; \
433     continue
434 #define CONSUME                              \
435     argv++, argc--;                          \
436     if (argc == 0) {                         \
437         Fprintf(stderr, "missing option\n"); \
438         exit(EXIT_FAILURE);                  \
439     }
440 
441 static void
do_ext_makedefs(int argc,char ** argv)442 do_ext_makedefs(int argc, char **argv)
443 {
444     int todo = 0;
445 
446     argc--;
447     argv++; /* skip program name */
448 
449     while (argc) {
450         if (argv[0][0] != '-')
451             break;
452         if (argv[0][1] != '-') {
453             Fprintf(stderr, "Can't mix - and -- options.\n");
454             exit(EXIT_FAILURE);
455         }
456         IS_OPTION("svs") {
457             /* short version string for packaging - note no \n */
458             char buf[100];
459             char delim[10];
460 
461             argv++; /* not CONSUME */
462             delim[0] = '\0';
463             if (argv[0])
464                 strcpy(delim, argv[0]);
465             Fprintf(stdout, "%s", version_string(buf, delim));
466             exit(EXIT_SUCCESS);
467         }
468         IS_OPTION("debug") {
469             debug = TRUE;
470             CONTINUE;
471         }
472         IS_OPTION("make") {
473             CONSUME;
474             do_makedefs(argv[0]);
475             exit(EXIT_SUCCESS);
476         }
477         IS_OPTION("input") {
478             CONSUME;
479             if (!strcmp(argv[0], "-")) {
480                 inputfp = stdin;
481             } else {
482                 inputfp = fopen(argv[0], RDTMODE);
483                 if (!inputfp) {
484                     Fprintf(stderr, "Can't open '%s'.\n", argv[0]);
485                     exit(EXIT_FAILURE);
486                 }
487             }
488             CONTINUE;
489         }
490         IS_OPTION("output") {
491             CONSUME;
492             if (!strcmp(argv[0], "-")) {
493                 outputfp = stdout;
494             } else {
495                 outputfp = fopen(argv[0], WRTMODE);
496                 if (!outputfp) {
497                     Fprintf(stderr, "Can't open '%s'.\n", argv[0]);
498                     exit(EXIT_FAILURE);
499                 }
500             }
501             CONTINUE;
502         }
503         IS_OPTION("grep") {
504             if (todo) {
505                 Fprintf(stderr, "Can't do grep and something else.\n");
506                 exit(EXIT_FAILURE);
507             }
508             todo = TODO_GREP;
509             CONTINUE;
510         }
511         IS_OPTION("grep-showvars") {
512             do_grep_showvars();
513             exit(EXIT_SUCCESS);
514         }
515         IS_OPTION("grep-trace") {
516             grep_trace = 1;
517             CONTINUE;
518         }
519         IS_OPTION("grep-define") {
520             struct grep_var *p;
521 
522             CONSUME;
523             p = grepsearch(argv[0]);
524             if (p) {
525                 p->is_defined = 1;
526             } else {
527                 Fprintf(stderr, "Unknown symbol '%s'\n", argv[0]);
528                 exit(EXIT_FAILURE);
529             }
530             CONTINUE;
531         }
532         IS_OPTION("grep-undef") {
533             struct grep_var *p;
534 
535             CONSUME;
536             p = grepsearch(argv[0]);
537             if (p) {
538                 p->is_defined = 0;
539             } else {
540                 Fprintf(stderr, "Unknown symbol '%s'\n", argv[0]);
541                 exit(EXIT_FAILURE);
542             }
543             CONTINUE;
544         }
545 #ifdef notyet
546         IS_OPTION("help") {
547         }
548 #endif
549         Fprintf(stderr, "Unknown option '%s'.\n", argv[0]);
550         exit(EXIT_FAILURE);
551     }
552     if (argc) {
553         Fprintf(stderr, "unexpected argument '%s'.\n", argv[0]);
554         exit(EXIT_FAILURE);
555     }
556 
557     switch (todo) {
558     default:
559         Fprintf(stderr, "Confused about what to do?\n");
560         exit(EXIT_FAILURE);
561     case 0:
562         Fprintf(stderr, "Nothing to do?\n");
563         exit(EXIT_FAILURE);
564     case TODO_GREP:
565         do_grep();
566         break;
567     }
568 }
569 
570 #undef IS_OPTION
571 #undef CONTINUE
572 #undef CONSUME
573 
574 /*
575  * Filtering syntax:
576  * Any line NOT starting with a caret is either suppressed or passed
577  * through unchanged depending on the current conditional state.
578  *
579  * The default conditional state is printing on.
580  *
581  * Conditionals may be nested.
582  *
583  * makedefs will exit with a EXIT_FAILURE if any errors are detected;
584  * as many errors as possible are detected before giving up.
585  *
586  * Unknown identifiers are treated as TRUE and also as an error to
587  * allow processing to continue past the unknown identifier (note
588  * that "#undef" is different than unknown).
589  *
590  * Any line starting with a caret is a control line; as in C, zero or
591  * more spaces may be embedded in the line almost anywhere; the caret
592  * MUST be in column 1.
593  * (XXX for the moment, no white space is allowed after the caret because
594  * existing lines in the docs look like that.)
595  *
596  * Control lines:
597  *      ^^      a line starting with a (single) literal caret
598  *      ^#      a comment - the line is ignored
599  *      ^?ID    if defined(ID)
600  *      ^!ID    if !defined(ID)
601  *      ^:      else
602  *      ^.      endif
603  */
604 #define GREP_MAGIC '^'
605 #define GREP_STACK_SIZE 100
606 #ifdef notyet
607 static int grep_rewrite = 0; /* need to (possibly) rewrite lines */
608 #endif
609 static int grep_writing = 1; /* need to copy lines to output */
610 static int grep_errors = 0;
611 static int grep_sp = 0;
612 #define ST_LD(old, opp) (((old) ? 1 : 0) | ((opp) ? 2 : 0))
613 #define ST_OLD(v) (((v) & 1) != 0)
614 #define ST_OPP(v) (((v) & 2) != 0)
615 #define ST_ELSE 4
616 static int grep_stack[GREP_STACK_SIZE] = { ST_LD(1, 0) };
617 static int grep_lineno = 0;
618 
619 static void
do_grep_showvars(void)620 do_grep_showvars(void)
621 {
622     int x;
623 
624     for (x = 0; x < SIZE(grep_vars) - 1; x++) {
625         printf("%d\t%s\n", grep_vars[x].is_defined, grep_vars[x].name);
626     }
627 }
628 
629 static struct grep_var *
grepsearch(const char * name)630 grepsearch(const char* name)
631 {
632     /* XXX make into binary search */
633     int x = 0;
634 
635     while (x < SIZE(grep_vars) - 1) {
636         if (!strcmp(grep_vars[x].name, name))
637             return &grep_vars[x];
638         x++;
639     }
640     return 0;
641 }
642 
643 static int
grep_check_id(const char * id)644 grep_check_id(const char* id)
645 {
646     struct grep_var *rv;
647 
648     while (*id && isspace((uchar) *id))
649         id++;
650     if (!*id) {
651         Fprintf(stderr, "missing identifier in line %d", grep_lineno);
652         grep_errors++;
653         return 0;
654     }
655     rv = grepsearch(id);
656     if (rv) {
657         if (grep_trace) {
658             Fprintf(outputfp, "ID %d %s\n", rv->is_defined, id);
659         }
660         return rv->is_defined;
661     }
662 
663     if (grep_trace) {
664         Fprintf(outputfp, "ID U %s\n", id);
665     }
666     Fprintf(stderr, "unknown identifier '%s' in line %d.\n", id, grep_lineno);
667     grep_errors++;
668     return 2; /* So new features can be checked before makedefs
669                * is rebuilt. */
670 }
671 
672 static void
grep_show_wstack(const char * tag)673 grep_show_wstack(const char* tag)
674 {
675     int x;
676 
677     if (!grep_trace)
678         return;
679 
680     Fprintf(outputfp, "%s w=%d sp=%d\t", tag, grep_writing, grep_sp);
681     for (x = grep_sp; x >= 0 && x > grep_sp - 6; x--) {
682         Fprintf(outputfp, "[%d]=%d ", x, grep_stack[x]);
683     }
684     Fprintf(outputfp, "\n");
685 }
686 
687 static char *
do_grep_control(char * buf)688 do_grep_control(char *buf)
689 {
690     int isif = 1;
691     char *buf0 = buf;
692 #if 1
693     if (isspace((uchar) buf[0]))
694         return &buf[-1]; /* XXX see docs above */
695 #else
696     while (buf[0] && isspace((uchar) buf[0]))
697         buf++;
698 #endif
699     switch (buf[0]) {
700     case '#': /* comment */
701         break;
702     case '.': /* end of if level */
703         if (grep_sp == 0) {
704             Fprintf(stderr, "unmatched ^. (endif) at line %d.\n",
705                     grep_lineno);
706             grep_errors++;
707         } else {
708             grep_writing = ST_OLD(grep_stack[grep_sp--]);
709             grep_show_wstack("pop");
710         }
711         break;
712     case '!': /* if not ID */
713         isif = 0;
714     /* FALLTHROUGH */
715     case '?': /* if ID */
716         if (grep_sp == GREP_STACK_SIZE - 2) {
717             Fprintf(stderr, "stack overflow at line %d.", grep_lineno);
718             exit(EXIT_FAILURE);
719         }
720         if (grep_writing) {
721             isif = grep_check_id(&buf[1]) ? isif : !isif;
722             grep_stack[++grep_sp] = ST_LD(grep_writing, !isif);
723             grep_writing = isif;
724         } else {
725             grep_stack[++grep_sp] = ST_LD(0, 0);
726             /* grep_writing = 0; */
727         }
728         grep_show_wstack("push");
729         break;
730     case ':': /* else */
731         if (ST_ELSE & grep_stack[grep_sp]) {
732             Fprintf(stderr, "multiple : for same conditional at line %d.\n",
733                     grep_lineno);
734             grep_errors++;
735         }
736         grep_writing = ST_OPP(grep_stack[grep_sp]);
737         grep_stack[grep_sp] |= ST_ELSE;
738         break;
739 #if defined(notyet)
740     case '(': /* start of expression */
741 #endif
742     case GREP_MAGIC: /* ^^ -> ^ */
743         return buf0;
744     default: {
745         char str[10];
746 
747         if (isprint((uchar) buf[0])) {
748             str[0] = buf[0];
749             str[1] = '\0';
750         } else {
751             sprintf(str, "0x%02x", buf[0]);
752         }
753         Fprintf(stderr, "unknown control ^%s at line %d.\n", str,
754                 grep_lineno);
755         grep_errors++;
756     } break;
757     }
758     return NULL;
759 }
760 
761 #ifdef notyet
762 static void
do_grep_rewrite(buf)763 do_grep_rewrite(buf)
764 char *buf;
765 {
766     /* no language features use this yet */
767     return;
768 }
769 #endif
770 
771 static void grep0(FILE *, FILE *, int);
772 
773 static void
do_grep(void)774 do_grep(void)
775 {
776     if (!inputfp) {
777         Fprintf(stderr, "--grep requires --input\n");
778     }
779     if (!outputfp) {
780         Fprintf(stderr, "--grep requires --output\n");
781     }
782     if (!inputfp || !outputfp) {
783         exit(EXIT_FAILURE);
784     }
785 
786     grep0(inputfp, outputfp, 0);
787 }
788 
789 static void
grep0(FILE * inputfp0,FILE * outputfp0,int flg)790 grep0(FILE *inputfp0, FILE* outputfp0, int flg)
791 {
792 #ifndef HAS_NO_MKSTEMP
793     /* if grep0 is passed FLG_TEMPFILE flag, it will
794        leave the output file open when it returns.
795        The caller will have to take care of calling
796        fclose() when it is done with the file */
797     boolean istemp = (flg & FLG_TEMPFILE) != 0;
798 #else
799     flg; // unused
800 #endif
801     char buf[16384]; /* looong, just in case */
802 
803     while (!feof(inputfp0) && !ferror(inputfp0)) {
804         char *tmp;
805         char *buf1;
806 
807         if (fgets(buf, sizeof(buf), inputfp0) == 0)
808             break;
809         if ((tmp = strchr(buf, '\n')))
810             *tmp = '\0';
811         grep_lineno++;
812         if (grep_trace) {
813             Fprintf(outputfp0, "%04d %c >%s\n", grep_lineno,
814                     grep_writing ? ' ' : '#', buf);
815         }
816 
817         if (buf[0] == GREP_MAGIC) {
818             buf1 = do_grep_control(&buf[1]);
819             if (!buf1)
820                 continue;
821         } else {
822             buf1 = buf;
823         }
824 #ifdef notyet
825         if (grep_rewrite)
826             do_grep_rewrite(buf1);
827 #endif
828         if (grep_writing)
829             Fprintf(outputfp0, "%s\n", buf1);
830     }
831     if (ferror(inputfp0)) {
832         Fprintf(stderr, "read error!\n");
833         exit(EXIT_FAILURE);
834     }
835     if (ferror(outputfp0)) {
836         Fprintf(stderr, "write error!\n");
837         exit(EXIT_FAILURE);
838     }
839     fclose(inputfp0);
840 #ifndef HAS_NO_MKSTEMP
841     if (istemp)
842         rewind(outputfp0);
843     else
844 #endif
845         fclose(outputfp0);
846     if (grep_sp) {
847         Fprintf(stderr, "%d unterminated conditional level%s\n", grep_sp,
848                 grep_sp == 1 ? "" : "s");
849         grep_errors++;
850     }
851     if (grep_errors) {
852         Fprintf(stderr, "%d error%s detected.\n", grep_errors,
853                 grep_errors == 1 ? "" : "s");
854         exit(EXIT_FAILURE);
855     }
856 }
857 
858 /* trivial text encryption routine which can't be broken with `tr' */
859 static char *
xcrypt(const char * str)860 xcrypt(const char* str)
861 { /* duplicated in src/hacklib.c */
862     static char buf[BUFSZ];
863     register const char *p;
864     register char *q;
865     register int bitmask;
866 
867     for (bitmask = 1, p = str, q = buf; *p; q++) {
868         *q = *p++;
869         if (*q & (32 | 64))
870             *q ^= bitmask;
871         if ((bitmask <<= 1) >= 32)
872             bitmask = 1;
873     }
874     *q = '\0';
875     return buf;
876 }
877 
878 #define PAD_RUMORS_TO 60
879 /* common code for do_rumors().  Return 0 on error. */
880 static unsigned long
read_rumors_file(const char * file_ext,int * rumor_count,long * rumor_size,unsigned long old_rumor_offset)881 read_rumors_file(const char* file_ext, int* rumor_count,
882                  long* rumor_size, unsigned long old_rumor_offset)
883 {
884     char infile[MAXFNAMELEN];
885     char *line;
886     unsigned long rumor_offset;
887 
888     Sprintf(infile, DATA_IN_TEMPLATE, RUMOR_FILE);
889     Strcat(infile, file_ext);
890     if (!(ifp = fopen(infile, RDTMODE))) {
891         perror(infile);
892         return 0L;
893     }
894 
895     /* copy the rumors */
896     while ((line = fgetline(ifp)) != 0) {
897         /* skip comments and blank lines */
898         if (line[0] == '#' || line[0] == '\n') {
899             free(line);
900             continue;
901         }
902 #ifdef PAD_RUMORS_TO
903         /* rumor selection is accomplished by seeking to a random
904            position in the file, advancing to newline, and taking
905            the next line; therefore, rumors which follow long-line
906            rumors are most likely to be chosen and rumors which
907            follow short-line rumors are least likely to be chosen;
908            we ameliorate the latter by padding the shortest lines,
909            increasing the chance of the random seek landing in them */
910         int len = (int) strlen(line);
911 
912         if (len <= PAD_RUMORS_TO) {
913             char *base = index(line, '\n');
914             /* this is only safe because fgetline() overallocates */
915             while (len++ < PAD_RUMORS_TO) {
916                 *base++ = '_';
917             }
918             *base++ = '\n';
919             *base = '\0';
920         }
921 #endif
922         (*rumor_count)++;
923 #if 0
924         /*[if we forced binary output, this would be sufficient]*/
925         *rumor_size += strlen(line); /* includes newline */
926 #endif
927         (void) fputs(xcrypt(line), tfp);
928         free(line);
929     }
930     /* record the current position; next rumors section will start here */
931     rumor_offset = (unsigned long) ftell(tfp);
932     Fclose(ifp); /* all done with rumors.file_ext */
933 
934     /* the calculated value for *_rumor_count assumes that
935        a single-byte line terminator is in use; for platforms
936        which use two byte CR+LF, we need to override that value
937        [it's much simpler to do so unconditionally, rendering
938        the loop's accumulation above obsolete] */
939     *rumor_size = (long) (rumor_offset - old_rumor_offset);
940     return rumor_offset;
941 }
942 
943 static void
do_rnd_access_file(const char * fname,const char * deflt_content)944 do_rnd_access_file(const char* fname, const char* deflt_content)
945 {
946     char *line, buf[BUFSZ];
947 
948     Sprintf(filename, DATA_IN_TEMPLATE, fname);
949     Strcat(filename, ".txt");
950     if (!(ifp = fopen(filename, RDTMODE))) {
951         perror(filename);
952         exit(EXIT_FAILURE);
953     }
954     filename[0] = '\0';
955 #ifdef FILE_PREFIX
956     Strcat(filename, file_prefix);
957 #endif
958     Sprintf(eos(filename), DATA_TEMPLATE, fname);
959     if (!(ofp = fopen(filename, WRTMODE))) {
960         perror(filename);
961         exit(EXIT_FAILURE);
962     }
963     Fprintf(ofp, "%s", Dont_Edit_Data);
964     /* lines from the file include trailing newline so make sure that the
965        default one does too */
966     if (!index(deflt_content, '\n'))
967         deflt_content = strcat(strcpy(buf, deflt_content), "\n");
968     /* write out the default content entry unconditionally instead of
969        waiting to see whether there are no regular output lines; if it
970        matches a regular entry (bogusmon "grue"), that entry will become
971        more likely to be picked than normal but it's nothing to worry about */
972     (void) fputs(xcrypt(deflt_content), ofp);
973 
974     tfp = getfp(DATA_TEMPLATE, tempfilename, WRTMODE, FLG_TEMPFILE);
975     grep0(ifp, tfp, FLG_TEMPFILE);
976 #ifndef HAS_NO_MKSTEMP
977     ifp = tfp;
978 #else
979     ifp = getfp(DATA_TEMPLATE, tempfilename, RDTMODE, 0);
980 #endif
981     while ((line = fgetline(ifp)) != 0) {
982         if (line[0] != '#' && line[0] != '\n')
983             (void) fputs(xcrypt(line), ofp);
984         free((genericptr_t) line);
985     }
986     Fclose(ifp);
987     Fclose(ofp);
988 
989 #ifdef HAS_NO_MKSTEMP
990     delete_file(DATA_TEMPLATE, templfilename);
991 #endif
992     return;
993 }
994 
995 DISABLE_WARNING_FORMAT_NONLITERAL
996 
997 void
do_rumors(void)998 do_rumors(void)
999 {
1000     char *line;
1001     static const char rumors_header[] =
1002         "%s%04d,%06ld,%06lx;%04d,%06ld,%06lx;0,0,%06lx\n";
1003     char tempfile[MAXFNAMELEN];
1004     int true_rumor_count, false_rumor_count;
1005     long true_rumor_size, false_rumor_size;
1006     unsigned long true_rumor_offset, false_rumor_offset, eof_offset;
1007 
1008     Sprintf(tempfile, DATA_TEMPLATE, "rumors.tmp");
1009     filename[0] = '\0';
1010 #ifdef FILE_PREFIX
1011     Strcat(filename, file_prefix);
1012 #endif
1013     Sprintf(eos(filename), DATA_TEMPLATE, RUMOR_FILE);
1014     if (!(ofp = fopen(filename, WRTMODE))) {
1015         perror(filename);
1016         exit(EXIT_FAILURE);
1017     }
1018     if (!(tfp = fopen(tempfile, WRTMODE))) {
1019         perror(tempfile);
1020         Fclose(ofp);
1021         exit(EXIT_FAILURE);
1022     }
1023 
1024     true_rumor_count = false_rumor_count = 0;
1025     true_rumor_size = false_rumor_size = 0L;
1026     true_rumor_offset = false_rumor_offset = eof_offset = 0L;
1027 
1028     /* output a dummy header record; we'll replace it in final output */
1029     Fprintf(tfp, rumors_header, Dont_Edit_Data, true_rumor_count,
1030             true_rumor_size, true_rumor_offset, false_rumor_count,
1031             false_rumor_size, false_rumor_offset, eof_offset);
1032     /* record the current position; true rumors will start here */
1033     true_rumor_offset = ftell(tfp);
1034 
1035     false_rumor_offset = read_rumors_file(
1036         ".tru", &true_rumor_count, &true_rumor_size, true_rumor_offset);
1037     if (!false_rumor_offset)
1038         goto rumors_failure;
1039 
1040     eof_offset = read_rumors_file(".fal", &false_rumor_count,
1041                                   &false_rumor_size, false_rumor_offset);
1042     if (!eof_offset)
1043         goto rumors_failure;
1044 
1045     /* get ready to transfer the contents of temp file to output file */
1046     line = malloc(BUFSZ + MAXFNAMELEN);
1047     Sprintf(line, "rewind of \"%s\"", tempfile);
1048     if (rewind(tfp) != 0) {
1049         perror(line);
1050         free(line);
1051         goto rumors_failure;
1052     }
1053     free(line);
1054 
1055     /* output the header record */
1056     Fprintf(ofp, rumors_header, Dont_Edit_Data, true_rumor_count,
1057             true_rumor_size, true_rumor_offset, false_rumor_count,
1058             false_rumor_size, false_rumor_offset, eof_offset);
1059     /* skip the temp file's dummy header */
1060     if (!(line = fgetline(tfp))) { /* "Don't Edit" */
1061         perror(tempfile);
1062         goto rumors_failure;
1063     }
1064     free(line);
1065     if (!(line = fgetline(tfp))) { /* count,size,offset */
1066         perror(tempfile);
1067         goto rumors_failure;
1068     }
1069     free(line);
1070     /* copy the rest of the temp file into the final output file */
1071     while ((line = fgetline(tfp)) != 0) {
1072         (void) fputs(line, ofp);
1073         free(line);
1074     }
1075     /* all done; delete temp file */
1076     Fclose(tfp);
1077     Unlink(tempfile);
1078     Fclose(ofp);
1079     return;
1080 
1081 rumors_failure:
1082     Fclose(ofp);
1083     Unlink(filename); /* kill empty or incomplete output file */
1084     Fclose(tfp);
1085     Unlink(tempfile); /* and temporary file */
1086     exit(EXIT_FAILURE);
1087 }
1088 
1089 RESTORE_WARNING_FORMAT_NONLITERAL
1090 
1091 void
do_date(void)1092 do_date(void)
1093 {
1094 #ifdef KR1ED
1095     long clocktim = 0;
1096 #else
1097     time_t clocktim = 0;
1098 #endif
1099     char githash[BUFSZ], gitbranch[BUFSZ];
1100     char *c, cbuf[60], buf[BUFSZ];
1101     const char *ul_sfx;
1102 #if defined(CROSSCOMPILE) && !defined(CROSSCOMPILE_TARGET)
1103     const char *xpref = "HOST_";
1104 #else
1105     const char *xpref = (const char *) 0;
1106 #endif /* CROSSCOMPILE && !CROSSCOMPILE_TARGET */
1107 
1108     /* before creating date.h, make sure that xxx_GRAPHICS and
1109        DEFAULT_WINDOW_SYS have been set up in a viable fashion */
1110     windowing_sanity();
1111 
1112     filename[0] = '\0';
1113 #ifdef FILE_PREFIX
1114     Strcat(filename, file_prefix);
1115 #endif
1116     Sprintf(eos(filename), INCLUDE_TEMPLATE, DATE_FILE);
1117     if (!(ofp = fopen(filename, WRTMODE))) {
1118         perror(filename);
1119         exit(EXIT_FAILURE);
1120     }
1121     /* NB: We've moved on from SCCS, but this way this line
1122      * won't get clobbered when downstream projects import
1123      * this file into something more modern. */
1124     Fprintf(ofp, "%s", Dont_Edit_Code);
1125 
1126     (void) time(&clocktim);
1127 #ifdef REPRODUCIBLE_BUILD
1128     {
1129         /*
1130          * Use date+time of latest source file revision (set up in
1131          * our environment rather than derived by scanning sources)
1132          * instead of current date+time, so that later rebuilds of
1133          * the same sources specifying the same configuration will
1134          * produce the same result.
1135          *
1136          * Changing the configuration should be done by modifying
1137          * config.h or <port>conf.h and setting SOURCE_DATE_EPOCH
1138          * based on whichever changed most recently, not by using
1139          *   make CFLAGS='-Dthis -Dthat'
1140          * to make alterations on the fly.
1141          *
1142          * Limited validation is performed to prevent dates in the
1143          * future (beyond a leeway of 24 hours) or distant past.
1144          *
1145          * Assumes the value of time_t is in seconds, which is
1146          * fundamental for Unix and mandated by POSIX.  For any ports
1147          * where that isn't true, leaving REPRODUCIBLE_BUILD disabled
1148          * is probably preferrable to hacking this code....
1149          */
1150         static struct tm nh360; /* static init should yield UTC timezone */
1151         unsigned long sd_num, sd_earliest, sd_latest;
1152         const char *sd_str = getenv("SOURCE_DATE_EPOCH");
1153 
1154         if (sd_str) {
1155             sd_num = strtoul(sd_str, (char **) 0, 10);
1156             /*
1157              * Note:  this does not need to be updated for future
1158              * releases.  It serves as a sanity check for potentially
1159              * mis-set environment, not a hard baseline for when the
1160              * current version could have first been built.
1161              */
1162             /* oldest date we'll accept: 7-Dec-2015 (release of 3.6.0) */
1163             nh360.tm_mday = 7;
1164             nh360.tm_mon  = 12 - 1;
1165             nh360.tm_year = 2015 - 1900;
1166             sd_earliest = (unsigned long) mktime(&nh360);
1167             /* 'youngest' date we'll accept: 24 hours in the future */
1168             sd_latest = (unsigned long) clocktim + 24L * 60L * 60L;
1169 
1170             if (sd_num >= sd_earliest && sd_num <= sd_latest) {
1171                 /* use SOURCE_DATE_EPOCH value */
1172                 clocktim = (time_t) sd_num;
1173                 date_via_env = TRUE;
1174             } else {
1175                 Fprintf(stderr, "? Invalid value for SOURCE_DATE_EPOCH (%lu)",
1176                         sd_num);
1177                 if (sd_num > 0L && sd_num < sd_earliest)
1178                     Fprintf(stderr, ", older than %lu", sd_earliest);
1179                 else if (sd_num > sd_latest)
1180                     Fprintf(stderr, ", newer than %lu", sd_latest);
1181                 Fprintf(stderr, ".\n");
1182                 Fprintf(stderr, ": Reverting to current date+time (%lu).\n",
1183                         (unsigned long) clocktim);
1184                 (void) fflush(stderr);
1185             }
1186         } else {
1187             /* REPRODUCIBLE_BUILD enabled but SOURCE_DATE_EPOCH is missing */
1188             Fprintf(stderr, "? No value for SOURCE_DATE_EPOCH.\n");
1189             Fprintf(stderr, ": Using current date+time (%lu).\n",
1190                     (unsigned long) clocktim);
1191             (void) fflush(stderr);
1192         }
1193         Strcpy(cbuf, asctime(gmtime(&clocktim)));
1194     }
1195 #else
1196     /* ordinary build: use current date+time */
1197     Strcpy(cbuf, ctime(&clocktim));
1198 #endif /* REPRODUCIBLE_BUILD */
1199 
1200     if ((c = index(cbuf, '\n')) != 0)
1201         *c = '\0'; /* strip off the '\n' */
1202 #ifdef NHSTDC
1203     ul_sfx = "UL";
1204 #else
1205     ul_sfx = "L";
1206 #endif
1207 
1208 #if !defined(CROSSCOMPILE) || !defined(CROSSCOMPILE_TARGET)
1209     Fprintf(ofp,
1210             "\n#if !defined(CROSSCOMPILE) || !defined(CROSSCOMPILE_TARGET)\n");
1211 #endif /* CROSSCOMPILE || !CROSSCOMPILE_TARGET */
1212     if (date_via_env)
1213         Fprintf(ofp, "#define SOURCE_DATE_EPOCH (%lu%s) /* via getenv() */\n",
1214                 (unsigned long) clocktim, ul_sfx);
1215     Fprintf(ofp, "#define BUILD_DATE \"%s\"\n", cbuf);
1216     if (date_via_env)
1217         Fprintf(ofp, "#define BUILD_TIME SOURCE_DATE_EPOCH\n");
1218     else
1219         Fprintf(ofp, "#define BUILD_TIME (%lu%s)\n",
1220                 (unsigned long) clocktim, ul_sfx);
1221     Fprintf(ofp, "\n");
1222     Fprintf(ofp, "#define VERSION_NUMBER 0x%08lx%s\n", version.incarnation,
1223             ul_sfx);
1224     Fprintf(ofp, "#define VERSION_FEATURES 0x%08lx%s\n", version.feature_set,
1225             ul_sfx);
1226 #ifdef MD_IGNORED_FEATURES
1227     Fprintf(ofp, "#define IGNORED_FEATURES 0x%08lx%s\n",
1228             (unsigned long) MD_IGNORED_FEATURES, ul_sfx);
1229 #endif
1230     Fprintf(ofp, "#define VERSION_SANITY1 0x%08lx%s\n", version.entity_count,
1231             ul_sfx);
1232 #ifndef __EMSCRIPTEN__
1233     Fprintf(ofp, "#define VERSION_SANITY2 0x%08lx%s\n", version.struct_sizes1,
1234             ul_sfx);
1235     Fprintf(ofp, "#define VERSION_SANITY3 0x%08lx%s\n", version.struct_sizes2,
1236             ul_sfx);
1237 #else /* __EMSCRIPTEN__ */
1238     Fprintf(ofp, "#define VERSION_SANITY2 0x%08llx%s\n", version.struct_sizes1,
1239             ul_sfx);
1240     Fprintf(ofp, "#define VERSION_SANITY3 0x%08llx%s\n", version.struct_sizes2,
1241             ul_sfx);
1242 #endif /* !__EMSCRIPTEN__ */
1243 
1244     Fprintf(ofp, "\n");
1245     Fprintf(ofp, "#define VERSION_STRING \"%s\"\n", version_string(buf, "."));
1246     Fprintf(ofp, "#define VERSION_ID \\\n \"%s\"\n",
1247             version_id_string(buf, cbuf));
1248     Fprintf(ofp, "#define COPYRIGHT_BANNER_C \\\n \"%s\"\n",
1249             bannerc_string(buf, cbuf));
1250     if (get_gitinfo(githash, gitbranch)) {
1251         Fprintf(ofp, "#define NETHACK_GIT_SHA \"%s\"\n", githash);
1252         Fprintf(ofp, "#define NETHACK_GIT_BRANCH \"%s\"\n", gitbranch);
1253     }
1254     if (xpref && get_gitinfo(githash, gitbranch)) {
1255         Fprintf(ofp, "#else /* !CROSSCOMPILE || !CROSSCOMPILE_TARGET */\n");
1256         Fprintf(ofp, "#define NETHACK_%sGIT_SHA \"%s\"\n",
1257                 xpref, githash);
1258         Fprintf(ofp, "#define NETHACK_%sGIT_BRANCH \"%s\"\n",
1259                 xpref, gitbranch);
1260     }
1261 #if !defined(CROSSCOMPILE) || !defined(CROSSCOMPILE_TARGET)
1262     Fprintf(ofp, "#endif /* !CROSSCOMPILE || !CROSSCOMPILE_TARGET */\n");
1263 #endif
1264     Fprintf(ofp, "\n");
1265 #ifdef AMIGA
1266     {
1267         struct tm *tm = localtime((time_t *) &clocktim);
1268 
1269         Fprintf(ofp, "#define AMIGA_VERSION_STRING ");
1270         Fprintf(ofp, "\"\\0$VER: NetHack %d.%d.%d (%d.%d.%d)\"\n",
1271                 VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL,
1272                 tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900);
1273     }
1274 #endif
1275     Fclose(ofp);
1276     return;
1277 }
1278 
1279 static boolean
get_gitinfo(char * githash,char * gitbranch)1280 get_gitinfo(char *githash, char *gitbranch)
1281 {
1282     FILE *gifp;
1283     size_t len;
1284     char infile[MAXFNAMELEN];
1285     char *line, *strval, *opt, *c, *end;
1286     boolean havebranch = FALSE, havehash = FALSE;
1287 
1288     if (!githash || !gitbranch) return FALSE;
1289 
1290     Sprintf(infile, DATA_IN_TEMPLATE, GITINFO_FILE);
1291     if (!(gifp = fopen(infile, RDTMODE))) {
1292         /* perror(infile); */
1293         return FALSE;
1294     }
1295 
1296     /* read the gitinfo file */
1297     while ((line = fgetline(gifp)) != 0) {
1298         strval = index(line, '=');
1299         if (strval && strlen(strval) < (BUFSZ-1)) {
1300             opt = line;
1301             *strval++ = '\0';
1302             /* strip off the '\n' */
1303             if ((c = index(strval, '\n')) != 0)
1304                 *c = '\0';
1305             if ((c = index(opt, '\n')) != 0)
1306                 *c = '\0';
1307             /* strip leading and trailing white space */
1308             while (*strval == ' ' || *strval == '\t')
1309                 strval++;
1310             end = eos(strval);
1311             while (--end >= strval && (*end == ' ' || *end == '\t'))
1312             *end = '\0';
1313             while (*opt == ' ' || *opt == '\t')
1314                 opt++;
1315             end = eos(opt);
1316             while (--end >= opt && (*end == ' ' || *end == '\t'))
1317             *end = '\0';
1318 
1319             len = strlen(opt);
1320             if ((len >= strlen("gitbranch"))
1321                 && !case_insensitive_comp(opt, "gitbranch")) {
1322                 Strcpy(gitbranch, strval);
1323                 havebranch = TRUE;
1324             }
1325             if ((len >= strlen("githash"))
1326                 && !case_insensitive_comp(opt, "githash")) {
1327                 Strcpy(githash, strval);
1328                 havehash = TRUE;
1329             }
1330 	}
1331         free(line);
1332     }
1333     Fclose(gifp);
1334     if (havebranch && havehash)
1335         return TRUE;
1336     return FALSE;
1337 }
1338 
1339 void
do_options(void)1340 do_options(void)
1341 {
1342     const char *optline;
1343     int infocontext = 0;
1344 
1345     windowing_sanity();
1346     filename[0] = '\0';
1347 #ifdef FILE_PREFIX
1348     Strcat(filename, file_prefix);
1349 #endif
1350     Sprintf(eos(filename), DATA_TEMPLATE, OPTIONS_FILE);
1351     if (!(ofp = fopen(filename, WRTMODE))) {
1352         perror(filename);
1353         exit(EXIT_FAILURE);
1354     }
1355     while ((optline = do_runtime_info(&infocontext)) != 0)
1356         Fprintf(ofp, "%s\n", optline);
1357     Fclose(ofp);
1358     return;
1359 }
1360 
1361 static void
windowing_sanity(void)1362 windowing_sanity(void)
1363 {
1364 #ifndef DEFAULT_WINDOW_SYS
1365     /* pre-standard compilers didn't support #error; wait til run-time */
1366     Fprintf(stderr,
1367             "Configuration error: DEFAULT_WINDOW_SYS is not defined.\n");
1368     exit(EXIT_FAILURE);
1369 /*NOTREACHED*/
1370 
1371 /* put in a dummy value so that do_options() will compile and makedefs
1372    will build, otherwise the message above won't ever get delivered */
1373 #define DEFAULT_WINDOW_SYS "<undefined>"
1374 #else  /*DEFAULT_WINDOW_SYS*/
1375 
1376     if (!window_opts[0].id) {
1377         Fprintf(stderr, "Configuration error: no windowing systems "
1378                         "(TTY_GRAPHICS, &c) enabled.\n");
1379         exit(EXIT_FAILURE);
1380     }
1381 
1382     {
1383         int i;
1384 
1385         for (i = 0; window_opts[i].id; ++i)
1386             if (!strcmp(window_opts[i].id, DEFAULT_WINDOW_SYS))
1387                 break;
1388         if (!window_opts[i].id) { /* went through whole list without a match */
1389             Fprintf(stderr, "Configuration error: DEFAULT_WINDOW_SYS (%s)\n",
1390                     DEFAULT_WINDOW_SYS);
1391             Fprintf(stderr,
1392                     " does not match any enabled windowing system (%s%s).\n",
1393                     window_opts[0].id, window_opts[1].id ? ", &c" : "");
1394             exit(EXIT_FAILURE);
1395         }
1396     }
1397 #endif /*DEFAULT_WINDOW_SYS*/
1398 }
1399 
1400 /* routine to decide whether to discard something from data.base */
1401 static boolean
d_filter(char * line)1402 d_filter(char *line)
1403 {
1404     if (*line == '#')
1405         return TRUE; /* ignore comment lines */
1406     return FALSE;
1407 }
1408 
1409 /*
1410  *
1411      New format (v3.1) of 'data' file which allows much faster lookups [pr]
1412 "do not edit"           first record is a comment line
1413 01234567                hexadecimal formatted offset to text area
1414 name-a                  first name of interest
1415 123,4                   offset to name's text, and number of lines for it
1416 name-b                  next name of interest
1417 name-c                  multiple names which share same description also
1418 456,7                   share a single offset,count line
1419 .                       sentinel to mark end of names
1420 789,0                   dummy record containing offset, count of EOF
1421 text-a                  4 lines of descriptive text for name-a
1422 text-a                  at file position 0x01234567L + 123L
1423 text-a
1424 text-a
1425 text-b/text-c           7 lines of text for names-b and -c
1426 text-b/text-c           at fseek(0x01234567L + 456L)
1427 ...
1428  *
1429  */
1430 
1431 void
do_data(void)1432 do_data(void)
1433 {
1434     char infile[60], tempfile[60];
1435     boolean ok;
1436     long txt_offset;
1437     int entry_cnt, line_cnt;
1438     char *line;
1439 
1440     Sprintf(tempfile, DATA_TEMPLATE, "database.tmp");
1441     filename[0] = '\0';
1442 #ifdef FILE_PREFIX
1443     Strcat(filename, file_prefix);
1444 #endif
1445     Sprintf(eos(filename), DATA_TEMPLATE, DATA_FILE);
1446     Sprintf(infile, DATA_IN_TEMPLATE, DATA_FILE);
1447 #ifdef SHORT_FILENAMES
1448     Strcat(infile, ".bas");
1449 #else
1450     Strcat(infile, ".base");
1451 #endif
1452     if (!(ifp = fopen(infile, RDTMODE))) { /* data.base */
1453         perror(infile);
1454         exit(EXIT_FAILURE);
1455     }
1456     if (!(ofp = fopen(filename, WRTMODE))) { /* data */
1457         perror(filename);
1458         Fclose(ifp);
1459         exit(EXIT_FAILURE);
1460     }
1461     if (!(tfp = fopen(tempfile, WRTMODE))) { /* database.tmp */
1462         perror(tempfile);
1463         Fclose(ifp);
1464         Fclose(ofp);
1465         Unlink(filename);
1466         exit(EXIT_FAILURE);
1467     }
1468 
1469     /* output a dummy header record; we'll rewind and overwrite it later */
1470     Fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, 0L);
1471 
1472     entry_cnt = line_cnt = 0;
1473     /* read through the input file and split it into two sections */
1474     while ((line = fgetline(ifp)) != 0) {
1475         if (d_filter(line)) {
1476             free(line);
1477             continue;
1478         }
1479         if (*line > ' ') { /* got an entry name */
1480             /* first finish previous entry */
1481             if (line_cnt)
1482                 Fprintf(ofp, "%d\n", line_cnt), line_cnt = 0;
1483             /* output the entry name */
1484             (void) fputs(line, ofp);
1485             entry_cnt++;        /* update number of entries */
1486         } else if (entry_cnt) { /* got some descriptive text */
1487             /* update previous entry with current text offset */
1488             if (!line_cnt)
1489                 Fprintf(ofp, "%ld,", ftell(tfp));
1490             /* save the text line in the scratch file */
1491             (void) fputs(line, tfp);
1492             line_cnt++; /* update line counter */
1493         }
1494         free(line);
1495     }
1496     /* output an end marker and then record the current position */
1497     if (line_cnt)
1498         Fprintf(ofp, "%d\n", line_cnt);
1499     Fprintf(ofp, ".\n%ld,%d\n", ftell(tfp), 0);
1500     txt_offset = ftell(ofp);
1501     Fclose(ifp); /* all done with original input file */
1502 
1503     /* reprocess the scratch file; 1st format an error msg, just in case */
1504     line = malloc(BUFSZ + MAXFNAMELEN);
1505     Sprintf(line, "rewind of \"%s\"", tempfile);
1506     if (rewind(tfp) != 0)
1507         goto dead_data;
1508     free(line);
1509     /* copy all lines of text from the scratch file into the output file */
1510     while ((line = fgetline(tfp)) != 0) {
1511         (void) fputs(line, ofp);
1512         free(line);
1513     }
1514 
1515     /* finished with scratch file */
1516     Fclose(tfp);
1517     Unlink(tempfile); /* remove it */
1518 
1519     /* update the first record of the output file; prepare error msg 1st */
1520     line = malloc(BUFSZ + MAXFNAMELEN);
1521     Sprintf(line, "rewind of \"%s\"", filename);
1522     ok = (rewind(ofp) == 0);
1523     if (ok) {
1524         Sprintf(line, "header rewrite of \"%s\"", filename);
1525         ok = (fprintf(ofp, "%s%08lx\n", Dont_Edit_Data,
1526                       (unsigned long) txt_offset) >= 0);
1527     }
1528     if (!ok) {
1529     dead_data:
1530         perror(line); /* report the problem */
1531         free(line);
1532         /* close and kill the aborted output file, then give up */
1533         Fclose(ofp);
1534         Unlink(filename);
1535         exit(EXIT_FAILURE);
1536     }
1537     free(line);
1538 
1539     /* all done */
1540     Fclose(ofp);
1541 
1542     return;
1543 }
1544 
1545 /* routine to decide whether to discard something from oracles.txt */
1546 static boolean
h_filter(char * line)1547 h_filter(char *line)
1548 {
1549     static boolean skip = FALSE;
1550     char *tag;
1551 
1552     SpinCursor(3);
1553 
1554     if (*line == '#')
1555         return TRUE; /* ignore comment lines */
1556 
1557     tag = malloc(strlen(line));
1558     if (sscanf(line, "----- %s", tag) == 1) {
1559         skip = FALSE;
1560     } else if (skip && !strncmp(line, "-----", 5))
1561         skip = FALSE;
1562     free(tag);
1563     return skip;
1564 }
1565 
1566 static const char *special_oracle[] = {
1567     "\"...it is rather disconcerting to be confronted with the",
1568     "following theorem from [Baker, Gill, and Solovay, 1975].", "",
1569     "Theorem 7.18  There exist recursive languages A and B such that",
1570     "  (1)  P(A) == NP(A), and", "  (2)  P(B) != NP(B)", "",
1571     "This provides impressive evidence that the techniques that are",
1572     "currently available will not suffice for proving that P != NP or        "
1573     "  ",
1574     "that P == NP.\"  [Garey and Johnson, p. 185.]"
1575 };
1576 
1577 /*
1578    The oracle file consists of a "do not edit" comment, a decimal count N
1579    and set of N+1 hexadecimal fseek offsets, followed by N multiple-line
1580    records, separated by "---" lines.  The first oracle is a special case.
1581    The input data contains just those multi-line records, separated by
1582    "-----" lines.
1583  */
1584 
1585 void
do_oracles(void)1586 do_oracles(void)
1587 {
1588     char infile[60], tempfile[60];
1589     boolean in_oracle, ok;
1590     long fpos;
1591     unsigned long txt_offset, offset;
1592     int oracle_cnt;
1593     register int i;
1594     char *line;
1595 
1596     Sprintf(tempfile, DATA_TEMPLATE, "oracles.tmp");
1597     filename[0] = '\0';
1598 #ifdef FILE_PREFIX
1599     Strcat(filename, file_prefix);
1600 #endif
1601     Sprintf(eos(filename), DATA_TEMPLATE, ORACLE_FILE);
1602     Sprintf(infile, DATA_IN_TEMPLATE, ORACLE_FILE);
1603     Strcat(infile, ".txt");
1604     if (!(ifp = fopen(infile, RDTMODE))) {
1605         perror(infile);
1606         exit(EXIT_FAILURE);
1607     }
1608     if (!(ofp = fopen(filename, WRTMODE))) {
1609         perror(filename);
1610         Fclose(ifp);
1611         exit(EXIT_FAILURE);
1612     }
1613     if (!(tfp = fopen(tempfile, WRTMODE))) { /* oracles.tmp */
1614         perror(tempfile);
1615         Fclose(ifp);
1616         Fclose(ofp);
1617         Unlink(filename);
1618         exit(EXIT_FAILURE);
1619     }
1620 
1621     /* output a dummy header record; we'll rewind and overwrite it later */
1622     Fprintf(ofp, "%s%5d\n", Dont_Edit_Data, 0);
1623 
1624     /* handle special oracle; it must come first */
1625     (void) fputs("---\n", tfp);
1626     offset = (unsigned long) ftell(tfp);
1627     Fprintf(ofp, "%05lx\n", offset); /* start pos of special oracle */
1628     for (i = 0; i < SIZE(special_oracle); i++) {
1629         (void) fputs(xcrypt(special_oracle[i]), tfp);
1630         (void) fputc('\n', tfp);
1631     }
1632     SpinCursor(3);
1633 
1634     oracle_cnt = 1;
1635     (void) fputs("---\n", tfp);
1636     offset = (unsigned long) ftell(tfp);
1637     Fprintf(ofp, "%05lx\n", offset); /* start pos of first oracle */
1638     in_oracle = FALSE;
1639 
1640     while ((line = fgetline(ifp)) != 0) {
1641         SpinCursor(3);
1642 
1643         if (h_filter(line)) {
1644             free(line);
1645             continue;
1646         }
1647         if (!strncmp(line, "-----", 5)) {
1648             if (!in_oracle) {
1649                 free(line);
1650                 continue;
1651             }
1652             in_oracle = FALSE;
1653             oracle_cnt++;
1654             (void) fputs("---\n", tfp);
1655             offset = (unsigned long) ftell(tfp);
1656             Fprintf(ofp, "%05lx\n", offset); /* start pos of this oracle */
1657         } else {
1658             in_oracle = TRUE;
1659             (void) fputs(xcrypt(line), tfp);
1660         }
1661         free(line);
1662     }
1663 
1664     if (in_oracle) { /* need to terminate last oracle */
1665         oracle_cnt++;
1666         (void) fputs("---\n", tfp);
1667         offset = (unsigned long) ftell(tfp);
1668         Fprintf(ofp, "%05lx\n", offset); /* eof position */
1669     }
1670 
1671     /* record the current position */
1672     txt_offset = (unsigned long) ftell(ofp);
1673     Fclose(ifp); /* all done with original input file */
1674 
1675     /* reprocess the scratch file; 1st format an error msg, just in case */
1676     line = malloc(BUFSZ + MAXFNAMELEN);
1677     Sprintf(line, "rewind of \"%s\"", tempfile);
1678     if (rewind(tfp) != 0)
1679         goto dead_data;
1680     free(line);
1681     /* copy all lines of text from the scratch file into the output file */
1682     while ((line = fgetline(tfp)) != 0) {
1683         (void) fputs(line, ofp);
1684         free(line);
1685     }
1686 
1687     /* finished with scratch file */
1688     Fclose(tfp);
1689     Unlink(tempfile); /* remove it */
1690 
1691     /* update the first record of the output file; prepare error msg 1st */
1692     line = malloc(BUFSZ + MAXFNAMELEN);
1693     Sprintf(line, "rewind of \"%s\"", filename);
1694     ok = (rewind(ofp) == 0);
1695     if (ok) {
1696         Sprintf(line, "header rewrite of \"%s\"", filename);
1697         ok = (fprintf(ofp, "%s%5d\n", Dont_Edit_Data, oracle_cnt) >= 0);
1698     }
1699     if (ok) {
1700         Sprintf(line, "data rewrite of \"%s\"", filename);
1701         for (i = 0; i <= oracle_cnt; i++) {
1702 #ifndef VMS /* alpha/vms v1.0; this fflush seems to confuse ftell */
1703             if (!(ok = (fflush(ofp) == 0)))
1704                 break;
1705 #endif
1706             if (!(ok = (fpos = ftell(ofp)) >= 0))
1707                 break;
1708             if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
1709                 break;
1710             if (!(ok = (fscanf(ofp, "%5lx", &offset) == 1)))
1711                 break;
1712 #ifdef MAC
1713 #ifdef __MWERKS__
1714             /*
1715             MetroWerks CodeWarrior Pro 1's (AKA CW12) version of MSL
1716             (ANSI C Libraries) needs this rewind or else the fprintf
1717             stops working.  This may also be true for CW11, but has
1718             never been checked.
1719             */
1720             rewind(ofp);
1721 #endif
1722 #endif
1723             if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0)))
1724                 break;
1725             offset += txt_offset;
1726             if (!(ok = (fprintf(ofp, "%05lx\n", offset) >= 0)))
1727                 break;
1728         }
1729     }
1730     if (!ok) {
1731     dead_data:
1732         perror(line); /* report the problem */
1733         free(line);
1734         /* close and kill the aborted output file, then give up */
1735         Fclose(ofp);
1736         Unlink(filename);
1737         exit(EXIT_FAILURE);
1738     }
1739     free(line);
1740 
1741     /* all done */
1742     Fclose(ofp);
1743 
1744     return;
1745 }
1746 
1747 void
do_dungeon(void)1748 do_dungeon(void)
1749 {
1750     char *line;
1751 
1752     Sprintf(filename, DATA_IN_TEMPLATE, DGN_I_FILE);
1753     if (!(ifp = fopen(filename, RDTMODE))) {
1754         perror(filename);
1755         exit(EXIT_FAILURE);
1756     }
1757     filename[0] = '\0';
1758 #ifdef FILE_PREFIX
1759     Strcat(filename, file_prefix);
1760 #endif
1761     Sprintf(eos(filename), DGN_TEMPLATE, DGN_O_FILE);
1762     if (!(ofp = fopen(filename, WRTMODE))) {
1763         perror(filename);
1764         exit(EXIT_FAILURE);
1765     }
1766     Fprintf(ofp, "%s", Dont_Edit_Data);
1767 
1768     tfp = getfp(DATA_TEMPLATE, tempfilename, WRTMODE, FLG_TEMPFILE);
1769     grep0(ifp, tfp, FLG_TEMPFILE);
1770 #ifndef HAS_NO_MKSTEMP
1771     ifp = tfp;
1772 #else
1773     ifp = getfp(DATA_TEMPLATE, tempfilename, RDTMODE, 0);
1774 #endif
1775     while ((line = fgetline(ifp)) != 0) {
1776         SpinCursor(3);
1777 
1778         if (line[0] == '#') {
1779             free(line);
1780             continue; /* discard comments */
1781         }
1782         (void) fputs(line, ofp);
1783         free(line);
1784     }
1785     Fclose(ifp);
1786     Fclose(ofp);
1787 
1788 #ifdef HAS_NO_MKSTEMP
1789     delete_file(DATA_TEMPLATE, tempfilename);
1790 #endif
1791     return;
1792 }
1793 
1794 /*
1795  * In 3.4.3 and earlier, this code was used to construct monstr[] array
1796  * in generated file src/monstr.c.  It wasn't used in 3.6.  For 3.7 it
1797  * has been reincarnated as a way to generate default monster strength
1798  * values:
1799  *      add new monster(s) to src/monst.c with placeholder value for
1800  *          the monstr field;
1801  *      run 'makedefs -m' to create src/monstr.c; ignore the complaints
1802  *          about it being deprecated;
1803  *      transfer relevant generated monstr values to src/monst.c;
1804  *      delete src/monstr.c.
1805  */
1806 static int mstrength(struct permonst *);
1807 static boolean ranged_attk(struct permonst *);
1808 
1809  /*
1810  * This routine is designed to return an integer value which represents
1811  * an approximation of monster strength.  It uses a similar method of
1812  * determination as "experience()" to arrive at the strength.
1813  */
1814 static int
mstrength(struct permonst * ptr)1815 mstrength(struct permonst* ptr)
1816 {
1817     int	i, tmp2, n, tmp = ptr->mlevel;
1818 
1819     if (tmp > 49)		/* special fixed hp monster */
1820         tmp = 2 * (tmp - 6) / 4;
1821 
1822     /*	For creation in groups */
1823     n = (!!(ptr->geno & G_SGROUP));
1824     n += (!!(ptr->geno & G_LGROUP)) << 1;
1825 
1826     /*	For ranged attacks */
1827     if (ranged_attk(ptr))
1828         n++;
1829 
1830     /*	For higher ac values */
1831     n += (ptr->ac < 4);
1832     n += (ptr->ac < 0);
1833 
1834     /*	For very fast monsters */
1835     n += (ptr->mmove >= 18);
1836 
1837     /*	For each attack and "special" attack */
1838     for (i = 0; i < NATTK; i++) {
1839         tmp2 = ptr->mattk[i].aatyp;
1840         n += (tmp2 > 0);
1841         n += (tmp2 == AT_MAGC);
1842         n += (tmp2 == AT_WEAP && (ptr->mflags2 & M2_STRONG));
1843     }
1844 
1845     /*	For each "special" damage type */
1846     for (i = 0; i < NATTK; i++) {
1847         tmp2 = ptr->mattk[i].adtyp;
1848         if ((tmp2 == AD_DRLI) || (tmp2 == AD_STON) || (tmp2 == AD_DRST)
1849             || (tmp2 == AD_DRDX) || (tmp2 == AD_DRCO) || (tmp2 == AD_WERE))
1850             n += 2;
1851         else if (strcmp(ptr->pmnames[NEUTRAL], "grid bug"))
1852             n += (tmp2 != AD_PHYS);
1853         n += ((int) (ptr->mattk[i].damd * ptr->mattk[i].damn) > 23);
1854     }
1855 
1856     /*	Leprechauns are special cases.  They have many hit dice so they
1857 	can hit and are hard to kill, but they don't really do much damage. */
1858     if (!strcmp(ptr->pmnames[NEUTRAL], "leprechaun"))
1859         n -= 2;
1860 
1861     /*	Finally, adjust the monster level  0 <= n <= 24 (approx.) */
1862     if (n == 0)
1863         tmp--;
1864     else if (n >= 6)
1865         tmp += (n / 2);
1866     else
1867         tmp += (n / 3 + 1);
1868 
1869     return (tmp >= 0) ? tmp : 0;
1870 }
1871 
1872 /* returns True if monster can attack at range */
1873 static boolean
ranged_attk(register struct permonst * ptr)1874 ranged_attk(register struct permonst* ptr)
1875 {
1876     register int i, j;
1877     register int atk_mask = (1 << AT_BREA) | (1 << AT_SPIT) | (1 << AT_GAZE);
1878 
1879     for (i = 0; i < NATTK; i++) {
1880         if ((j = ptr->mattk[i].aatyp) >= AT_WEAP
1881             || (j < 32 && (atk_mask & (1 << j)) != 0))
1882             return TRUE;
1883     }
1884     return FALSE;
1885 }
1886 
1887 void
do_monstr(void)1888 do_monstr(void)
1889 {
1890     struct permonst *ptr;
1891     int i;
1892 
1893     /* Don't break anything for ports that haven't been updated. */
1894     printf("DEPRECATION WARNINGS:\n");
1895     printf("'makedefs -m' is deprecated.  Remove all references\n");
1896     printf("  to it from the build process.\n");
1897     printf("'monstr.c' is deprecated.  Remove all references to\n");
1898     printf("  it from the build process.\n");
1899     printf("monstr[] is deprecated.  Replace monstr[x] with\n");
1900     printf("  mons[x].difficulty\n");
1901     printf("monstr_init() is deprecated.  Remove all references to it.\n");
1902 
1903     /*
1904      * create the source file, "monstr.c"
1905      */
1906     filename[0] = '\0';
1907 #ifdef FILE_PREFIX
1908     Strcat(filename, file_prefix);
1909 #endif
1910     Sprintf(eos(filename), SOURCE_TEMPLATE, MON_STR_C);
1911     if (!(ofp = fopen(filename, WRTMODE))) {
1912         perror(filename);
1913         exit(EXIT_FAILURE);
1914     }
1915     Fprintf(ofp, "%s", Dont_Edit_Code);
1916     Fprintf(ofp, "#include \"config.h\"\n");
1917     Fprintf(ofp, "\nconst int monstrXXX[] = {\n");
1918     Fprintf(ofp, "0};\n");
1919     Fprintf(ofp, "/*\n");
1920     Fprintf(ofp, "DEPRECATION WARNINGS:\n");
1921     Fprintf(ofp, "'makedefs -m' is deprecated.  Remove all references\n");
1922     Fprintf(ofp, "  to it from the build process.\n");
1923     Fprintf(ofp, "'monstr.c' is deprecated.  Remove all references to\n");
1924     Fprintf(ofp, "  it from the build process.\n");
1925     Fprintf(ofp, "monstr[] is deprecated.  Replace monstr[x] with\n");
1926     Fprintf(ofp, "  mons[x].difficulty\n");
1927     Fprintf(ofp,
1928             "monstr_init() is deprecated.  Remove all references to it.\n");
1929     Fprintf(ofp, "*/\n");
1930 
1931     /* output derived monstr values as a comment */
1932     Fprintf(ofp, "\n\n/*\n * default mons[].difficulty values\n *\n");
1933     for (ptr = &mons[0]; ptr->mlet; ptr++) {
1934         i = mstrength(ptr);
1935         Fprintf(ofp, "%-24s %2u\n", ptr->pmnames[NEUTRAL], (unsigned int) (uchar) i);
1936     }
1937     Fprintf(ofp, " *\n */\n\n");
1938 
1939     Fprintf(ofp, "\nvoid monstr_init(void);\n");
1940     Fprintf(ofp, "\nvoid\n");
1941     Fprintf(ofp, "monstr_init(void)\n");
1942     Fprintf(ofp, "{\n");
1943     Fprintf(ofp, "    return;\n");
1944     Fprintf(ofp, "}\n");
1945     Fprintf(ofp, "\n/*monstr.c*/\n");
1946 
1947     Fclose(ofp);
1948     return;
1949 }
1950 
1951 void
do_permonst(void)1952 do_permonst(void)
1953 {
1954     int i;
1955     char *c, *nam;
1956 
1957     filename[0] = '\0';
1958 #ifdef FILE_PREFIX
1959     Strcat(filename, file_prefix);
1960 #endif
1961     Sprintf(eos(filename), INCLUDE_TEMPLATE, MONST_FILE);
1962     if (!(ofp = fopen(filename, WRTMODE))) {
1963         perror(filename);
1964         exit(EXIT_FAILURE);
1965     }
1966     Fprintf(ofp, "%s", Dont_Edit_Code);
1967     Fprintf(ofp, "#ifndef PM_H\n#define PM_H\n");
1968 
1969     if (use_enum) {
1970         Fprintf(ofp, "\nenum monnums {");
1971 #if 0
1972         /* need #define ENUM_PM for the full NetHack build to include these */
1973         Fprintf(ofp, "\n        NON_PM = -1,");
1974         Fprintf(ofp, "\n        LOW_PM = 0,");
1975 #endif
1976     }
1977     for (i = 0; mons[i].mlet; i++) {
1978         SpinCursor(3);
1979         if (use_enum)
1980             Fprintf(ofp, "\n        PM_");
1981         else
1982             Fprintf(ofp, "\n#define\tPM_");
1983         if (mons[i].mlet == S_HUMAN && !strncmp(mons[i].pmnames[NEUTRAL], "were", 4))
1984             Fprintf(ofp, "HUMAN_");
1985         for (nam = c = tmpdup(mons[i].pmnames[NEUTRAL]); *c; c++)
1986             if (*c >= 'a' && *c <= 'z')
1987                 *c -= (char) ('a' - 'A');
1988             else if (*c < 'A' || *c > 'Z')
1989                 *c = '_';
1990         if (use_enum)
1991             Fprintf(ofp, "%s = %d,", nam, i);
1992         else
1993             Fprintf(ofp, "%s\t%d", nam, i);
1994     }
1995     if (use_enum) {
1996         Fprintf(ofp, "\n\n        NUMMONS = %d", i);
1997         Fprintf(ofp, "\n};\n");
1998     } else {
1999         Fprintf(ofp, "\n\n#define\tNUMMONS\t%d\n", i);
2000     }
2001     Fprintf(ofp, "\n#endif /* PM_H */\n");
2002     Fclose(ofp);
2003     return;
2004 }
2005 
2006 /*      Start of Quest text file processing. */
2007 
2008 void
do_questtxt(void)2009 do_questtxt(void)
2010 {
2011     printf("DEPRECATION WARNINGS:\n");
2012     printf("'makedefs -q' is no longer required.  Remove all references\n");
2013     printf("  to it from the build process.\n");
2014     printf("'dat/quest.txt' is no longer part of the source tree.\n");
2015 
2016     return;
2017 }
2018 
2019 static char temp[32];
2020 
limit(char * name,int pref)2021 static char *limit(char* name, int pref) /* limit a name to 30 characters length */
2022 {
2023     (void) strncpy(temp, name, pref ? 26 : 30);
2024     temp[pref ? 26 : 30] = 0;
2025     return temp;
2026 }
2027 
2028 void
do_objs(void)2029 do_objs(void)
2030 {
2031     int i;
2032     char *c, *objnam;
2033     int nspell = 0;
2034     int prefix = 0;
2035     char class = '\0';
2036 
2037     filename[0] = '\0';
2038 #ifdef FILE_PREFIX
2039     Strcat(filename, file_prefix);
2040 #endif
2041     Sprintf(eos(filename), INCLUDE_TEMPLATE, ONAME_FILE);
2042     if (!(ofp = fopen(filename, WRTMODE))) {
2043         perror(filename);
2044         exit(EXIT_FAILURE);
2045     }
2046     Fprintf(ofp, "%s", Dont_Edit_Code);
2047     Fprintf(ofp, "#ifndef ONAMES_H\n#define ONAMES_H\n\n");
2048 
2049     for (i = 0; !i || objects[i].oc_class != ILLOBJ_CLASS; i++) {
2050         SpinCursor(3);
2051 
2052         objects[i].oc_name_idx = objects[i].oc_descr_idx = i; /* init */
2053         if (!(objnam = tmpdup(OBJ_NAME(objects[i]))))
2054             continue;
2055 
2056         if (objects[i].oc_class != class) {
2057             class = objects[i].oc_class;
2058         }
2059 
2060         for (c = objnam; *c; c++)
2061             if (*c >= 'a' && *c <= 'z')
2062                 *c -= (char) ('a' - 'A');
2063             else if (*c < 'A' || *c > 'Z')
2064                 *c = '_';
2065 
2066         switch (class) {
2067         case WAND_CLASS:
2068             Fprintf(ofp, "#define\tWAN_");
2069             prefix = 1;
2070             break;
2071         case RING_CLASS:
2072             Fprintf(ofp, "#define\tRIN_");
2073             prefix = 1;
2074             break;
2075         case POTION_CLASS:
2076             Fprintf(ofp, "#define\tPOT_");
2077             prefix = 1;
2078             break;
2079         case SPBOOK_CLASS:
2080             Fprintf(ofp, "#define\tSPE_");
2081             prefix = 1;
2082             nspell++;
2083             break;
2084         case SCROLL_CLASS:
2085             Fprintf(ofp, "#define\tSCR_");
2086             prefix = 1;
2087             break;
2088         case AMULET_CLASS:
2089             /* avoid trouble with stupid C preprocessors */
2090             Fprintf(ofp, "#define\t");
2091             if (objects[i].oc_material == PLASTIC) {
2092                 Fprintf(ofp, "FAKE_AMULET_OF_YENDOR\t%d\n", i);
2093                 prefix = -1;
2094                 break;
2095             }
2096             break;
2097         case GEM_CLASS:
2098             /* avoid trouble with stupid C preprocessors */
2099             if (objects[i].oc_material == GLASS) {
2100                 Fprintf(ofp, "/* #define\t%s\t%d */\n", objnam, i);
2101                 prefix = -1;
2102                 break;
2103             }
2104             /*FALLTHRU*/
2105         case VENOM_CLASS:
2106             /* fall-through from gem class is ok; objects[] used to have
2107                 { "{acid,blinding} venom", "splash of venom" }
2108                but those have been changed to
2109                 { "splash of {acid,blinding} venom", "splash of venom" }
2110                so strip the extra "splash of " off to keep same macros */
2111             if (!strncmp(objnam, "SPLASH_OF_", 10))
2112                 objnam += 10;
2113             /*FALLTHRU*/
2114         default:
2115             Fprintf(ofp, "#define\t");
2116             break;
2117         }
2118         if (prefix >= 0)
2119             Fprintf(ofp, "%s\t%d\n", limit(objnam, prefix), i);
2120         prefix = 0;
2121 
2122     }
2123 
2124     Fprintf(ofp, "#define\tLAST_GEM\t(JADE)\n");
2125     Fprintf(ofp, "#define\tMAXSPELL\t%d\n", nspell + 1);
2126     Fprintf(ofp, "#define\tNUM_OBJECTS\t%d\n", i);
2127 
2128     Fprintf(ofp, "\n/* Artifacts (unique objects) */\n\n");
2129 
2130     for (i = 1; artifact_names[i]; i++) {
2131         SpinCursor(3);
2132 
2133         for (c = objnam = tmpdup(artifact_names[i]); *c; c++)
2134             if (*c >= 'a' && *c <= 'z')
2135                 *c -= (char) ('a' - 'A');
2136             else if (*c < 'A' || *c > 'Z')
2137                 *c = '_';
2138 
2139         if (!strncmp(objnam, "THE_", 4))
2140             objnam += 4;
2141         /* fudge _platinum_ YENDORIAN EXPRESS CARD */
2142         if (!strncmp(objnam, "PLATINUM_", 9))
2143             objnam += 9;
2144         Fprintf(ofp, "#define\tART_%s\t%d\n", limit(objnam, 1), i);
2145     }
2146 
2147     Fprintf(ofp, "#define\tNROFARTIFACTS\t%d\n", i - 1);
2148     Fprintf(ofp, "\n#endif /* ONAMES_H */\n");
2149     Fclose(ofp);
2150     return;
2151 }
2152 
2153 /* Read one line from input, up to and including the next newline
2154  * character. Returns a pointer to the heap-allocated string, or a
2155  * null pointer if no characters were read.
2156  */
2157 static char *
fgetline(FILE * fd)2158 fgetline(FILE *fd)
2159 {
2160     static const int inc = 256;
2161     int len = inc;
2162     char *c = malloc(len), *ret;
2163 
2164     for (;;) {
2165         ret = fgets(c + len - inc, inc, fd);
2166         if (!ret) {
2167             free(c);
2168             c = NULL;
2169             break;
2170         } else if (index(c, '\n')) {
2171             /* normal case: we have a full line */
2172             break;
2173         }
2174         len += inc;
2175         c = realloc(c, len);
2176     }
2177     return c;
2178 }
2179 
2180 static char *
tmpdup(const char * str)2181 tmpdup(const char* str)
2182 {
2183     static char buf[128];
2184 
2185     if (!str)
2186         return (char *) 0;
2187     (void) strncpy(buf, str, 127);
2188     return buf;
2189 }
2190 
2191 #ifdef STRICT_REF_DEF
2192 NEARDATA struct flag flags;
2193 #ifdef ATTRIB_H
2194 struct attribs attrmax, attrmin;
2195 #endif
2196 #endif /* STRICT_REF_DEF */
2197 
2198 /*makedefs.c*/
2199