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