1 /* NetHack 3.7  mdlib.c  $NHDT-Date: 1608933420 2020/12/25 21:57:00 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.17 $ */
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 /*
9  * This can be linked into a binary to provide the functionality
10  * via the contained functions, or it can be #included directly
11  * into util/makedefs.c to provide it there.
12  */
13 
14 #ifndef MAKEDEFS_C
15 #define MDLIB_C
16 #include "config.h"
17 #ifdef MONITOR_HEAP
18 #undef free /* makedefs, mdlib don't use the alloc and free in src/alloc.c */
19 #endif
20 #include "permonst.h"
21 #include "objclass.h"
22 #include "monsym.h"
23 #include "artilist.h"
24 #include "dungeon.h"
25 #include "obj.h"
26 #include "monst.h"
27 #include "you.h"
28 #include "context.h"
29 #include "flag.h"
30 #include "dlb.h"
31 #include <ctype.h>
32 /* version information */
33 #ifdef SHORT_FILENAMES
34 #include "patchlev.h"
35 #else
36 #include "patchlevel.h"
37 #endif
38 #define Fprintf (void) fprintf
39 #define Fclose (void) fclose
40 #define Unlink (void) unlink
41 #if !defined(AMIGA) || defined(AZTEC_C)
42 #define rewind(fp) fseek((fp), 0L, SEEK_SET) /* guarantee a return value */
43 #endif  /* AMIGA || AZTEC_C */
44 #else
45 #ifndef GLOBAL_H
46 #include "global.h"
47 #endif
48 #endif  /* !MAKEDEFS_C */
49 
50 /* shorten up some lines */
51 #if defined(CROSSCOMPILE_TARGET) || defined(OPTIONS_AT_RUNTIME)
52 #define FOR_RUNTIME
53 #endif
54 
55 #if defined(MAKEDEFS_C) || defined(FOR_RUNTIME)
56 /* REPRODUCIBLE_BUILD will change this to TRUE */
57 static boolean date_via_env = FALSE;
58 
59 static char *version_string(char *, const char *);
60 static char *version_id_string(char *, const char *);
61 static char *bannerc_string(char *, const char *);
62 
63 static void make_version(void);
64 static char *eos(char *);
65 #if 0
66 static char *mdlib_strsubst(char *, const char *, const char *);
67 #endif
68 
69 #ifndef HAS_NO_MKSTEMP
70 #ifdef _MSC_VER
71 static int mkstemp(char *);
72 #endif
73 #endif
74 #endif /* MAKEDEFS_C || FOR_RUNTIME */
75 
76 #if defined(MAKEDEFS_C) || defined(FOR_RUNTIME) || defined(WIN32) \
77     || (defined(CROSSCOMPILE_TARGET) && defined(__DATE__) && defined(__TIME__))
78 static int case_insensitive_comp(const char *, const char *);
79 #endif
80 
81 #if !defined(MAKEDEFS_C) && defined(WIN32)
82 extern int GUILaunched;
83 #endif
84 
85 /* these two are in extern.h but we don't include hack.h */
86 void runtime_info_init(void);
87 const char *do_runtime_info(int *);
88 
89 void build_options(void);
90 static int count_and_validate_winopts(void);
91 static void opt_out_words(char *, int *);
92 static void build_savebones_compat_string(void);
93 static int idxopttext, done_runtime_opt_init_once = 0;
94 #define MAXOPT 40
95 #if !defined(MAKEDEFS_C) && defined(CROSSCOMPILE_TARGET) \
96     && defined(__DATE__) && defined(__TIME__)
97 static char rttimebuf[MAXOPT];
98 #endif
99 static char *opttext[120] = { 0 };
100 char optbuf[BUFSZ];
101 static struct version_info version;
102 static const char opt_indent[] = "    ";
103 
104 struct win_info {
105     const char *id, /* DEFAULT_WINDOW_SYS string */
106         *name;      /* description, often same as id */
107     boolean valid;
108 };
109 
110 static struct win_info window_opts[] = {
111 #ifdef TTY_GRAPHICS
112     { "tty",
113       /* testing 'USE_TILES' here would bring confusion because it could
114          apply to another interface such as X11, so check MSDOS explicitly
115          instead; even checking TTY_TILES_ESCCODES would probably be
116          confusing to most users (and it will already be listed separately
117          in the compiled options section so users aware of it can find it) */
118 #ifdef MSDOS
119       "traditional text with optional 'tiles' graphics",
120 #else
121       /* assume that one or more of IBMgraphics, DECgraphics, or MACgraphics
122          can be enabled; we can't tell from here whether that is accurate */
123       "traditional text with optional line-drawing",
124 #endif
125       TRUE
126     },
127 #endif /*TTY_GRAPHICS */
128 #ifdef CURSES_GRAPHICS
129     { "curses", "terminal-based graphics", TRUE },
130 #endif
131 #ifdef X11_GRAPHICS
132     { "X11", "X11", TRUE },
133 #endif
134 #ifdef QT_GRAPHICS /* too vague; there are multiple incompatible versions */
135     { "Qt", "Qt", TRUE },
136 #endif
137 #ifdef MSWIN_GRAPHICS /* win32 */
138     { "mswin", "Windows GUI", TRUE },
139 #endif
140 #ifdef SHIM_GRAPHICS
141     { "shim", "NetHack Library Windowing Shim", TRUE },
142 #endif
143 
144 #if 0  /* remainder have been retired */
145 #ifdef GNOME_GRAPHICS /* unmaintained/defunct */
146     { "Gnome", "Gnome", TRUE },
147 #endif
148 #ifdef MAC /* defunct OS 9 interface */
149     { "mac", "Mac", TRUE },
150 #endif
151 #ifdef AMIGA_INTUITION /* unmaintained/defunct */
152     { "amii", "Amiga Intuition", TRUE },
153 #endif
154 #ifdef GEM_GRAPHICS /* defunct Atari interface */
155     { "Gem", "Gem", TRUE },
156 #endif
157 #ifdef BEOS_GRAPHICS /* unmaintained/defunct */
158     { "BeOS", "BeOS InterfaceKit", TRUE },
159 #endif
160 #endif  /* 0 => retired */
161     { 0, 0, FALSE }
162 };
163 
164 /*
165  * Use this to explicitly mask out features during version checks.
166  *
167  * ZEROCOMP, RLECOMP, and ZLIB_COMP describe compression features
168  * that the port/plaform which wrote the savefile was capable of
169  * dealing with. Don't reject a savefile just because the port
170  * reading the savefile doesn't match on all/some of them.
171  * The actual compression features used to produce the savefile are
172  * recorded in the savefile_info structure immediately following the
173  * version_info, and that is what needs to be checked against the
174  * feature set of the port that is reading the savefile back in.
175  * That check is done in src/restore.c now.
176  *
177  */
178 #ifndef MD_IGNORED_FEATURES
179 #define MD_IGNORED_FEATURES              \
180     (0L | (1L << 19) /* SCORE_ON_BOTL */ \
181      | (1L << 27)    /* ZEROCOMP */      \
182      | (1L << 28)    /* RLECOMP */       \
183      )
184 #endif /* MD_IGNORED_FEATUES */
185 
186 static void
make_version(void)187 make_version(void)
188 {
189     register int i;
190 
191     /*
192      * integer version number
193      */
194     version.incarnation = ((unsigned long) VERSION_MAJOR << 24)
195                           | ((unsigned long) VERSION_MINOR << 16)
196                           | ((unsigned long) PATCHLEVEL << 8)
197                           | ((unsigned long) EDITLEVEL);
198     /*
199      * encoded feature list
200      * Note:  if any of these magic numbers are changed or reassigned,
201      * EDITLEVEL in patchlevel.h should be incremented at the same time.
202      * The actual values have no special meaning, and the category
203      * groupings are just for convenience.
204      */
205     version.feature_set = (unsigned long) (0L
206 /* levels and/or topology (0..4) */
207 /* monsters (5..9) */
208 #ifdef MAIL_STRUCTURES
209                                            | (1L << 6)
210 #endif
211 /* objects (10..14) */
212 /* flag bits and/or other global variables (15..26) */
213 #ifdef TEXTCOLOR
214                                            | (1L << 17)
215 #endif
216 #ifdef INSURANCE
217                                            | (1L << 18)
218 #endif
219 #ifdef SCORE_ON_BOTL
220                                            | (1L << 19)
221 #endif
222 /* data format (27..31)
223  * External compression methods such as COMPRESS and ZLIB_COMP
224  * do not affect the contents and are thus excluded from here */
225 #ifdef ZEROCOMP
226                                            | (1L << 27)
227 #endif
228 #ifdef RLECOMP
229                                            | (1L << 28)
230 #endif
231                                                );
232     /*
233      * Value used for object & monster sanity check.
234      *    (NROFARTIFACTS<<24) | (NUM_OBJECTS<<12) | (NUMMONS<<0)
235      */
236     for (i = 1; artifact_names[i]; i++)
237         continue;
238     version.entity_count = (unsigned long) (i - 1);
239     for (i = 1; objects[i].oc_class != ILLOBJ_CLASS; i++)
240         continue;
241     version.entity_count = (version.entity_count << 12) | (unsigned long) i;
242     for (i = 0; mons[i].mlet; i++)
243         continue;
244     version.entity_count = (version.entity_count << 12) | (unsigned long) i;
245     /*
246      * Value used for compiler (word size/field alignment/padding) check.
247      */
248     version.struct_sizes1 =
249         (((unsigned long) sizeof(struct context_info) << 24)
250          | ((unsigned long) sizeof(struct obj) << 17)
251          | ((unsigned long) sizeof(struct monst) << 10)
252          | ((unsigned long) sizeof(struct you)));
253     version.struct_sizes2 = (((unsigned long) sizeof(struct flag) << 10));
254 /* free bits in here */
255     return;
256 }
257 
258 #if defined(MAKEDEFS_C) || defined(FOR_RUNTIME)
259 
260 static char *
version_string(char * outbuf,const char * delim)261 version_string(char *outbuf, const char *delim)
262 {
263     Sprintf(outbuf, "%d%s%d", VERSION_MAJOR, delim, VERSION_MINOR);
264 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
265     Sprintf(eos(outbuf), "-%d", EDITLEVEL);
266 #endif
267     return outbuf;
268 }
269 
270 static char *
version_id_string(char * outbuf,const char * build_date)271 version_id_string(char *outbuf, const char *build_date)
272 {
273     char subbuf[64], versbuf[64];
274     char statusbuf[64];
275 
276 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
277 #if (NH_DEVEL_STATUS == NH_STATUS_BETA)
278     Strcpy(statusbuf, " Beta");
279 #else
280 #if (NH_DEVEL_STATUS == NH_STATUS_WIP)
281     Strcpy(statusbuf, " Work-in-progress");
282 #else
283     Strcpy(statusbuf, " post-release");
284 #endif
285 #endif
286 #else
287     statusbuf[0] = '\0';
288 #endif
289     subbuf[0] = '\0';
290 #ifdef PORT_SUB_ID
291     subbuf[0] = ' ';
292     Strcpy(&subbuf[1], PORT_SUB_ID);
293 #endif
294 
295     Sprintf(outbuf, "%s xNetHack%s Version %s%s - last %s %s.", PORT_ID,
296             subbuf, version_string(versbuf, "."), statusbuf,
297             date_via_env ? "revision" : "build", build_date);
298     return outbuf;
299 }
300 
301 /* still within #if MAKDEFS_C || FOR_RUNTIME */
302 
303 static char *
bannerc_string(char * outbuf,const char * build_date)304 bannerc_string(char *outbuf, const char *build_date)
305 {
306     char subbuf[64], versbuf[64];
307 
308     subbuf[0] = '\0';
309 #ifdef PORT_SUB_ID
310     subbuf[0] = ' ';
311     Strcpy(&subbuf[1], PORT_SUB_ID);
312 #endif
313 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
314 #if (NH_DEVEL_STATUS == NH_STATUS_BETA)
315     Strcat(subbuf, " Beta");
316 #else
317     Strcat(subbuf, " Work-in-progress");
318 #endif
319 #endif
320 
321     Sprintf(outbuf, "         Version %s %s%s, %s %s.",
322             version_string(versbuf, "."), PORT_ID, subbuf,
323             date_via_env ? "revised" : "built", &build_date[4]);
324 #if 0
325     Sprintf(outbuf, "%s NetHack%s %s Copyright 1985-%s (built %s)",
326             PORT_ID, subbuf, version_string(versbuf,"."), RELEASE_YEAR,
327             &build_date[4]);
328 #endif
329     return outbuf;
330 }
331 
332 #ifndef HAS_NO_MKSTEMP
333 #ifdef _MSC_VER
334 int
mkstemp(char * template)335 mkstemp(char *template)
336 {
337     int err;
338 
339     err = _mktemp_s(template, strlen(template) + 1);
340     if( err != 0 )
341         return -1;
342     return _open(template,
343                  _O_RDWR | _O_BINARY | _O_TEMPORARY | _O_CREAT,
344                  _S_IREAD | _S_IWRITE);
345 }
346 #endif /* _MSC_VER */
347 #endif /* HAS_NO_MKSTEMP */
348 #endif /* MAKEDEFS_C || FOR_RUNTIME */
349 
350 #if defined(MAKEDEFS_C) || defined(FOR_RUNTIME) || defined(WIN32) \
351     || (defined(CROSSCOMPILE_TARGET) && defined(__DATE__) && defined(__TIME__))
352 static int
case_insensitive_comp(const char * s1,const char * s2)353 case_insensitive_comp(const char *s1, const char *s2)
354 {
355     uchar u1, u2;
356 
357     for (;; s1++, s2++) {
358         u1 = (uchar) *s1;
359         if (isupper(u1))
360             u1 = tolower(u1);
361         u2 = (uchar) *s2;
362         if (isupper(u2))
363             u2 = tolower(u2);
364         if (u1 == '\0' || u1 != u2)
365             break;
366     }
367     return u1 - u2;
368 }
369 #endif
370 
371 static char *
eos(char * str)372 eos(char *str)
373 {
374     while (*str)
375         str++;
376     return str;
377 }
378 
379 #if 0
380 static char *
381 mdlib_strsubst(char *bp, const char *orig, const char *replacement)
382 {
383     char *found, buf[BUFSZ];
384 
385     if (bp) {
386         /* [this could be replaced by strNsubst(bp, orig, replacement, 1)] */
387         found = strstr(bp, orig);
388         if (found) {
389             Strcpy(buf, found + strlen(orig));
390             Strcpy(found, replacement);
391             Strcat(bp, buf);
392         }
393     }
394     return bp;
395 }
396 #endif
397 
398 static char save_bones_compat_buf[BUFSZ];
399 
400 static void
build_savebones_compat_string(void)401 build_savebones_compat_string(void)
402 {
403 #ifdef VERSION_COMPATIBILITY
404     unsigned long uver = VERSION_COMPATIBILITY,
405                   cver  = (((unsigned long) VERSION_MAJOR << 24)
406                          | ((unsigned long) VERSION_MINOR << 16)
407                          | ((unsigned long) PATCHLEVEL    <<  8));
408 #endif
409 
410     Strcpy(save_bones_compat_buf,
411            "save and bones files accepted from version");
412 #ifdef VERSION_COMPATIBILITY
413     if (uver != cver)
414         Sprintf(eos(save_bones_compat_buf), "s %lu.%lu.%lu through %d.%d.%d",
415                 ((uver >> 24) & 0x0ffUL),
416                 ((uver >> 16) & 0x0ffUL),
417                 ((uver >>  8) & 0x0ffUL),
418                 VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL);
419     else
420 #endif
421         Sprintf(eos(save_bones_compat_buf), " %d.%d.%d only",
422                 VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL);
423 }
424 
425 static const char *build_opts[] = {
426 #ifdef AMIGA_WBENCH
427     "Amiga WorkBench support",
428 #endif
429 #ifdef ANSI_DEFAULT
430     "ANSI default terminal",
431 #endif
432 #ifdef TEXTCOLOR
433     "color",
434 #endif
435 #ifdef TTY_GRAPHICS
436 #ifdef TTY_TILES_ESCCODES
437     "console escape codes for tile hinting",
438 #endif
439 #endif
440 #ifdef COM_COMPL
441     "command line completion",
442 #endif
443 #ifdef LIFE
444     "Conway's Game of Life",
445 #endif
446 #ifdef COMPRESS
447     "data file compression",
448 #endif
449 #ifdef ZLIB_COMP
450     "ZLIB data file compression",
451 #endif
452 #ifdef DLB
453 #ifndef VERSION_IN_DLB_FILENAME
454     "data librarian",
455 #else
456     "data librarian with a version-dependent name",
457 #endif
458 #endif
459 #ifdef DUMPLOG
460     "end-of-game dumplogs",
461 #endif
462 #ifdef HOLD_LOCKFILE_OPEN
463     "exclusive lock on level 0 file",
464 #endif
465 #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__))
466     "external program as a message handler",
467 #endif
468 #if defined(HANGUPHANDLING) && !defined(NO_SIGNAL)
469 #ifdef SAFERHANGUP
470     "deferred handling of hangup signal",
471 #else
472     "immediate handling of hangup signal",
473 #endif
474 #endif
475 #ifdef INSURANCE
476     "insurance files for recovering from crashes",
477 #endif
478 #ifdef LOGFILE
479     "log file",
480 #endif
481 #ifdef XLOGFILE
482     "extended log file",
483 #endif
484 #ifdef PANICLOG
485     "errors and warnings log file",
486 #endif
487 #ifdef MAIL
488     "mail daemon",
489 #endif
490 #if defined(GNUDOS) || defined(__DJGPP__)
491     "MSDOS protected mode",
492 #endif
493 #ifdef NEWS
494     "news file",
495 #endif
496 #ifdef OVERLAY
497 #ifdef MOVERLAY
498     "MOVE overlays",
499 #else
500 #ifdef VROOMM
501     "VROOMM overlays",
502 #else
503     "overlays",
504 #endif
505 #endif
506 #endif
507     /* pattern matching method will be substituted by nethack at run time */
508     "pattern matching via :PATMATCH:",
509 #ifdef USE_ISAAC64
510     "pseudo random numbers generated by ISAAC64",
511 #ifdef DEV_RANDOM
512 #ifdef NHSTDC
513     /* include which specific one */
514     "strong PRNG seed available from " DEV_RANDOM,
515 #else
516     "strong PRNG seed available from DEV_RANDOM",
517 #endif
518 #else
519 #ifdef WIN32
520     "strong PRNG seed available from CNG BCryptGenRandom()",
521 #endif
522 #endif  /* DEV_RANDOM */
523 #else   /* ISAAC64 */
524 #ifdef RANDOM
525     "pseudo random numbers generated by random()",
526 #else
527     "pseudo random numbers generated by C rand()",
528 #endif
529 #endif /* ISAAC64 */
530 #ifdef SELECTSAVED
531     "restore saved games via menu",
532 #endif
533 #ifdef SCORE_ON_BOTL
534     "score on status line",
535 #endif
536 #ifdef CLIPPING
537     "screen clipping",
538 #endif
539 #ifdef NO_TERMS
540 #ifdef MAC
541     "screen control via mactty",
542 #endif
543 #ifdef SCREEN_BIOS
544     "screen control via BIOS",
545 #endif
546 #ifdef SCREEN_DJGPPFAST
547     "screen control via DJGPP fast",
548 #endif
549 #ifdef SCREEN_VGA
550     "screen control via VGA graphics",
551 #endif
552 #ifdef WIN32CON
553     "screen control via WIN32 console I/O",
554 #endif
555 #endif /* NO_TERMS */
556 #ifdef SHELL
557     "shell command",
558 #endif
559     "traditional status display",
560 #ifdef STATUS_HILITES
561     "status via windowport with highlighting",
562 #else
563     "status via windowport without highlighting",
564 #endif
565 #ifdef SUSPEND
566     "suspend command",
567 #endif
568 #ifdef TTY_GRAPHICS
569 #ifdef TERMINFO
570     "terminal info library",
571 #else
572 #if defined(TERMLIB) || (!defined(MICRO) && !defined(WIN32))
573     "terminal capability library",
574 #endif
575 #endif
576 #endif /*TTY_GRAPHICS*/
577 #ifdef USE_XPM
578     "tiles file in XPM format",
579 #endif
580 #ifdef GRAPHIC_TOMBSTONE
581     "graphical RIP screen",
582 #endif
583 #ifdef TIMED_DELAY
584     "timed wait for display effects",
585 #endif
586 #ifdef USER_SOUNDS
587     "user sounds",
588 #endif
589 #ifdef PREFIXES_IN_USE
590     "variable playground",
591 #endif
592 #ifdef VISION_TABLES
593     "vision tables",
594 #endif
595 #ifdef ZEROCOMP
596     "zero-compressed save files",
597 #endif
598 #ifdef RLECOMP
599     "run-length compression of map in save files",
600 #endif
601 #ifdef SYSCF
602     "system configuration at run-time",
603 #endif
604     save_bones_compat_buf,
605     "and basic NetHack features"
606 };
607 
608 int
count_and_validate_winopts(void)609 count_and_validate_winopts(void)
610 {
611     int i, cnt = 0;
612 
613     /* window_opts has a fencepost entry at the end */
614     for (i = 0; i < SIZE(window_opts) - 1; i++) {
615 #if !defined(MAKEDEFS_C) && defined(FOR_RUNTIME)
616 #ifdef WIN32
617         window_opts[i].valid = FALSE;
618         if ((GUILaunched
619              && case_insensitive_comp(window_opts[i].id, "mswin") != 0)
620             || (!GUILaunched
621                 && case_insensitive_comp(window_opts[i].id, "mswin") == 0))
622             continue;
623 #endif
624 #endif /* !MAKEDEFS_C && FOR_RUNTIME */
625         ++cnt;
626         window_opts[i].valid = TRUE;
627     }
628     return cnt;
629 }
630 
631 static void
opt_out_words(char * str,int * length_p)632 opt_out_words(char *str,     /* input, but modified during processing */
633               int *length_p) /* in/out */
634 {
635     char *word;
636 
637     while (*str) {
638         word = index(str, ' ');
639 #if 0
640         /* treat " (" as unbreakable space */
641         if (word && *(word + 1) == '(')
642             word = index(word + 1,  ' ');
643 #endif
644         if (word)
645             *word = '\0';
646         if (*length_p + (int) strlen(str) > COLNO - 5) {
647             opttext[idxopttext] = strdup(optbuf);
648             if (idxopttext < (MAXOPT - 1))
649                 idxopttext++;
650             Sprintf(optbuf, "%s", opt_indent),
651                     *length_p = (int) strlen(opt_indent);
652         } else {
653             Sprintf(eos(optbuf), " "), (*length_p)++;
654         }
655         Sprintf(eos(optbuf), "%s", str), *length_p += (int) strlen(str);
656         str += strlen(str) + (word ? 1 : 0);
657     }
658 }
659 
660 void
build_options(void)661 build_options(void)
662 {
663     char buf[BUFSZ];
664     int i, length, winsyscnt, cnt = 0;
665     const char *defwinsys = DEFAULT_WINDOW_SYS;
666 
667 #if !defined (MAKEDEFS_C) && defined(FOR_RUNTIME)
668 #ifdef WIN32
669     defwinsys = GUILaunched ? "mswin" : "tty";
670 #endif
671 #endif
672     build_savebones_compat_string();
673     opttext[idxopttext] = strdup(optbuf);
674     if (idxopttext < (MAXOPT - 1))
675         idxopttext++;
676 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
677 #if (NH_DEVEL_STATUS == NH_STATUS_BETA)
678 #define STATUS_ARG " [beta]"
679 #else
680 #define STATUS_ARG " [work-in-progress]"
681 #endif
682 #else
683 #define STATUS_ARG ""
684 #endif /* NH_DEVEL_STATUS == NH_STATUS_RELEASED */
685     Sprintf(optbuf, "%sNetHack version %d.%d%s\n",
686             opt_indent, VERSION_MAJOR, VERSION_MINOR, STATUS_ARG);
687     opttext[idxopttext] = strdup(optbuf);
688     if (idxopttext < (MAXOPT - 1))
689         idxopttext++;
690     Sprintf(optbuf, "Options compiled into this edition:");
691     opttext[idxopttext] = strdup(optbuf);
692     if (idxopttext < (MAXOPT - 1))
693         idxopttext++;
694     optbuf[0] = '\0';
695     length = COLNO + 1; /* force 1st item onto new line */
696     for (i = 0; i < SIZE(build_opts); i++) {
697 #if !defined(MAKEDEFS_C) && defined(FOR_RUNTIME)
698 #ifdef WIN32
699         /* ignore the console entry if GUI version */
700         if (GUILaunched
701             && !strcmp("screen control via WIN32 console I/O", build_opts[i]))
702             continue;
703 #endif
704 #endif /* !MAKEDEFS_C && FOR_RUNTIME */
705         Strcat(strcpy(buf, build_opts[i]),
706                (i < SIZE(build_opts) - 1) ? "," : ".");
707         opt_out_words(buf, &length);
708     }
709     opttext[idxopttext] = strdup(optbuf);
710     if (idxopttext < (MAXOPT - 1))
711         idxopttext++;
712     optbuf[0] = '\0';
713     winsyscnt = count_and_validate_winopts();
714     opttext[idxopttext] = strdup(optbuf);
715     if (idxopttext < (MAXOPT - 1))
716         idxopttext++;
717     Sprintf(optbuf, "Supported windowing system%s:",
718             (winsyscnt > 1) ? "s" : "");
719     opttext[idxopttext] = strdup(optbuf);
720     if (idxopttext < (MAXOPT - 1))
721         idxopttext++;
722     optbuf[0] = '\0';
723     length = COLNO + 1; /* force 1st item onto new line */
724 
725     for (i = 0; i < SIZE(window_opts) - 1; i++) {
726         if (!window_opts[i].valid)
727             continue;
728         Sprintf(buf, "\"%s\"", window_opts[i].id);
729         if (strcmp(window_opts[i].name, window_opts[i].id))
730             Sprintf(eos(buf), " (%s)", window_opts[i].name);
731         /*
732          * 1 : foo.
733          * 2 : foo and bar,
734          * 3+: for, bar, and quux,
735          *
736          * 2+ will be followed by " with a default of..."
737          */
738         Strcat(buf, (winsyscnt == 1) ? "." /* no 'default' */
739                     : (winsyscnt == 2 && cnt == 0) ? " and"
740                       : (cnt == winsyscnt - 2) ? ", and"
741                         : ",");
742         opt_out_words(buf, &length);
743         cnt++;
744     }
745     if (cnt > 1) {
746         /* loop ended with a comma; opt_out_words() will insert a space */
747         Sprintf(buf, "with a default of \"%s\".", defwinsys);
748         opt_out_words(buf, &length);
749     }
750 
751     opttext[idxopttext] = strdup(optbuf);
752     if (idxopttext < (MAXOPT - 1))
753         idxopttext++;
754     optbuf[0] = '\0';
755 
756 #if defined(MAKEDEFS_C) || defined(FOR_RUNTIME)
757     {
758         static const char *lua_info[] = {
759  "", "NetHack 3.7.* uses the 'Lua' interpreter to process some data:", "",
760  "    :LUACOPYRIGHT:", "",
761  /*        1         2         3         4         5         6         7
762   1234567890123456789012345678901234567890123456789012345678901234567890123456
763   */
764  "    \"Permission is hereby granted, free of charge, to any person obtaining",
765  "     a copy of this software and associated documentation files (the ",
766  "     \"Software\"), to deal in the Software without restriction including",
767  "     without limitation the rights to use, copy, modify, merge, publish,",
768  "     distribute, sublicense, and/or sell copies of the Software, and to ",
769  "     permit persons to whom the Software is furnished to do so, subject to",
770  "     the following conditions:",
771  "     The above copyright notice and this permission notice shall be",
772  "     included in all copies or substantial portions of the Software.\"",
773             (const char *) 0
774         };
775 
776         /* add lua copyright notice;
777            ":TAG:" substitutions are deferred to caller */
778         for (i = 0; lua_info[i]; ++i) {
779             opttext[idxopttext] = strdup(lua_info[i]);
780             if (idxopttext < (MAXOPT - 1))
781                 idxopttext++;
782         }
783     }
784 #endif /* MAKEDEFS_C || FOR_RUNTIME */
785 
786     /* end with a blank line */
787     opttext[idxopttext] = strdup("");
788     if (idxopttext < (MAXOPT - 1))
789         idxopttext++;
790     return;
791 }
792 
793 #if defined(__DATE__) && defined(__TIME__)
794 #define extract_field(t,s,n,z)    \
795     do {                          \
796         for (i = 0; i < n; ++i)   \
797             t[i] = s[i + z];      \
798         t[i] = '\0';              \
799     } while (0)
800 #endif
801 
802 void
runtime_info_init(void)803 runtime_info_init(void)
804 {
805 #if !defined(MAKEDEFS_C) && defined(CROSSCOMPILE_TARGET) \
806     && defined(__DATE__) && defined(__TIME__)
807     int i;
808     char tmpbuf[BUFSZ], *strp;
809     const char *mth[] = {
810         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
811         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
812     struct tm t = {0};
813     time_t timeresult;
814 #endif
815 
816     if (!done_runtime_opt_init_once) {
817         done_runtime_opt_init_once = 1;
818         build_savebones_compat_string();
819         /* construct the current version number */
820         make_version();
821 #if !defined(MAKEDEFS_C) && defined(CROSSCOMPILE_TARGET)
822 #if defined(__DATE__) && defined(__TIME__)
823         /*
824          * In a cross-compiled environment, you can't execute
825          * the target binaries during the build, so we can't
826          * use makedefs to write the values of the build
827          * date and time to a file for retrieval. Not for
828          * information meaningful to the target execution
829          * environment.
830          *
831          * How can we capture the build date/time of the target
832          * binaries in such a situation?  We need to rely on the
833          * cross-compiler itself to do it for us during the
834          * cross-compile.
835          *
836          * To that end, we are going to make use of the
837          * following pre-defined preprocessor macros for this:
838          *    gcc, msvc, clang   __DATE__  "Feb 12 1996"
839          *    gcc, msvc, clang   __TIME__  "23:59:01"
840          *
841          */
842         if (sizeof __DATE__ + sizeof __TIME__  + sizeof "123" <
843             sizeof rttimebuf)
844             Sprintf(rttimebuf, "%s %s", __DATE__, __TIME__);
845         /* "Feb 12 1996 23:59:01"
846             01234567890123456789  */
847         if ((int) strlen(rttimebuf) == 20) {
848             extract_field(tmpbuf, rttimebuf, 4, 7);   /* year */
849             t.tm_year = atoi(tmpbuf) - 1900;
850             extract_field(tmpbuf, rttimebuf, 3, 0);   /* mon */
851             for (i = 0; i < SIZE(mth); ++i)
852                 if (!case_insensitive_comp(tmpbuf, mth[i])) {
853                     t.tm_mon = i;
854                     break;
855                 }
856             extract_field(tmpbuf, rttimebuf, 2, 4);   /* mday */
857             strp = tmpbuf;
858             if (*strp == ' ')
859                 strp++;
860             t.tm_mday = atoi(strp);
861             extract_field(tmpbuf, rttimebuf, 2, 12);  /* hour */
862             t.tm_hour = atoi(tmpbuf);
863             extract_field(tmpbuf, rttimebuf, 2, 15);  /* min  */
864             t.tm_min = atoi(tmpbuf);
865             extract_field(tmpbuf, rttimebuf, 2, 18);  /* sec  */
866             t.tm_sec = atoi(tmpbuf);
867             timeresult = mktime(&t);
868             BUILD_TIME = (unsigned long) timeresult;
869             BUILD_DATE = rttimebuf;
870 	}
871 #endif /* __DATE__ && __TIME__ */
872         VERSION_NUMBER = version.incarnation;
873         VERSION_FEATURES = version.feature_set;
874 #ifdef MD_IGNORED_FEATURES
875         IGNORED_FEATURES = MD_IGNORED_FEATURES;
876 #endif
877         VERSION_SANITY1 = version.entity_count;
878         VERSION_SANITY2 = version.struct_sizes1;
879         VERSION_SANITY3 = version.struct_sizes2;
880         VERSION_STRING = strdup(version_string(tmpbuf, "."));
881         VERSION_ID = strdup(version_id_string(tmpbuf, BUILD_DATE));
882         COPYRIGHT_BANNER_C = strdup(bannerc_string(tmpbuf, BUILD_DATE));
883 #ifdef NETHACK_HOST_GIT_SHA
884         NETHACK_GIT_SHA = strdup(NETHACK_HOST_GIT_SHA);
885 #endif
886 #ifdef NETHACK_HOST_GIT_BRANCH
887         NETHACK_GIT_BRANCH = strdup(NETHACK_HOST_GIT_BRANCH);
888 #endif
889 #endif /* !MAKEDEFS_C  && CROSSCOMPILE_TARGET */
890         idxopttext = 0;
891         build_options();
892     }
893 }
894 
895 const char *
do_runtime_info(int * rtcontext)896 do_runtime_info(int *rtcontext)
897 {
898     const char *retval = (const char *) 0;
899 
900     if (!done_runtime_opt_init_once)
901         runtime_info_init();
902     if (idxopttext && rtcontext)
903         if (*rtcontext >= 0 && *rtcontext < (MAXOPT - 1)) {
904             retval = opttext[*rtcontext];
905             *rtcontext += 1;
906 	}
907     return retval;
908 }
909 
910 /*mdlib.c*/
911