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