The consolidation of global variables from scattered source
files into decl.c and declared in decl.h was begun in 3.7.0.
Their placement in common files was done for centralized
initialization and potential re-initialization during a
"play again" scenario.
It wasn't really necessary for all of them to be housed in a
single huge structure to meet the "play again" requirement,
and the single huge structure has been a little unwieldy when
it comes to maintenance.
Following this commit, instead of one single extremely large structure
named 'g' to house all of the relocated global variables, they
are distributed into several ga through gz.
To make things easy for the developer, each variable is placed
into the struct corresponding to the starting letter of the variable.
That way, no lookup is required in order to know which struct houses
a particular variable, it is a simple match to the starting letter
for all the centralized global variables.
A global variable named 'amulets', would be found in ga.
ga.amulets
^ ^
A global varable named 'move', would be found in gm.
gm.moves
^ ^
A global variable named 'val_for_n_or_more' would be found in gv.
gv.val_for_n_or_more
^ ^
A global variable named 'youmonst' would be found in gy.
gy.youmonst
^ ^
485 lines
14 KiB
C
485 lines
14 KiB
C
/* NetHack 3.7 version.c $NHDT-Date: 1655402415 2022/06/16 18:00:15 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.92 $ */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/*-Copyright (c) Michael Allison, 2018. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "hack.h"
|
|
#include "dlb.h"
|
|
|
|
#ifndef OPTIONS_AT_RUNTIME
|
|
#define OPTIONS_AT_RUNTIME
|
|
#endif
|
|
|
|
extern char *mdlib_version_string(char *, const char *);
|
|
static void insert_rtoption(char *);
|
|
|
|
/* fill buffer with short version (so caller can avoid including date.h) */
|
|
char *
|
|
version_string(char *buf, size_t bufsz)
|
|
{
|
|
Snprintf(buf, bufsz, "%s",
|
|
((nomakedefs.version_string && nomakedefs.version_string[0])
|
|
? nomakedefs.version_string
|
|
/* in case we try to write a paniclog entry after releasing
|
|
the 'nomakedefs' data */
|
|
: mdlib_version_string(buf, ".")));
|
|
return buf;
|
|
}
|
|
|
|
/* fill and return the given buffer with the long nethack version string */
|
|
char *
|
|
getversionstring(char *buf, size_t bufsz)
|
|
{
|
|
Strcpy(buf, nomakedefs.version_id);
|
|
|
|
{
|
|
int c = 0;
|
|
#if defined(RUNTIME_PORT_ID)
|
|
char tmpbuf[BUFSZ], *tmp;
|
|
#endif
|
|
char *p = eos(buf);
|
|
boolean dotoff = (p > buf && p[-1] == '.');
|
|
|
|
if (dotoff)
|
|
--p;
|
|
Strcpy(p, " (");
|
|
#if defined(RUNTIME_PORT_ID)
|
|
tmp = get_port_id(tmpbuf);
|
|
if (tmp)
|
|
Snprintf(eos(buf), (bufsz - strlen(buf)) - 1,
|
|
"%s%s", c++ ? "," : "", tmp);
|
|
#endif
|
|
if (nomakedefs.git_sha)
|
|
Snprintf(eos(buf), (bufsz - strlen(buf)) - 1,
|
|
"%s%s", c++ ? "," : "", nomakedefs.git_sha);
|
|
#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
|
|
if (nomakedefs.git_branch)
|
|
Snprintf(eos(buf), (bufsz - strlen(buf)) - 1,
|
|
"%sbranch:%s",
|
|
c++ ? "," : "", nomakedefs.git_branch);
|
|
#endif
|
|
if (c)
|
|
Snprintf(eos(buf), (bufsz - strlen(buf)) - 1,
|
|
"%s", ")");
|
|
else /* if nothing has been added, strip " (" back off */
|
|
*p = '\0';
|
|
if (dotoff)
|
|
Snprintf(eos(buf), (bufsz - strlen(buf)) - 1,
|
|
"%s", ".");
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
/* the #versionshort command */
|
|
int
|
|
doversion(void)
|
|
{
|
|
char buf[BUFSZ];
|
|
|
|
pline("%s", getversionstring(buf, sizeof buf));
|
|
return ECMD_OK;
|
|
}
|
|
|
|
/* the '#version' command; also a choice for '?' */
|
|
int
|
|
doextversion(void)
|
|
{
|
|
int rtcontext = 0;
|
|
const char *rtbuf;
|
|
dlb *f = (dlb *) 0;
|
|
char buf[BUFSZ], *p = 0;
|
|
winid win = create_nhwindow(NHW_TEXT);
|
|
boolean use_dlb = TRUE,
|
|
done_rt = FALSE,
|
|
done_dlb = FALSE,
|
|
prolog;
|
|
#if 0 /* moved to util/mdlib.c and rendered via do_runtime_info() */
|
|
const char *lua_info[] = {
|
|
"About Lua: Copyright (c) 1994-2017 Lua.org, PUC-Rio.",
|
|
/* 1 2 3 4 5 6 7
|
|
1234567890123456789012345678901234567890123456789012345678901234567890123456789
|
|
*/
|
|
" \"Permission is hereby granted, free of charge, to any person obtaining",
|
|
" a copy of this software and associated documentation files (the ",
|
|
" \"Software\"), to deal in the Software without restriction including",
|
|
" without limitation the rights to use, copy, modify, merge, publish,",
|
|
" distribute, sublicense, and/or sell copies of the Software, and to ",
|
|
" permit persons to whom the Software is furnished to do so, subject to",
|
|
" the following conditions:",
|
|
" The above copyright notice and this permission notice shall be",
|
|
" included in all copies or substantial portions of the Software.\"",
|
|
(const char *) 0
|
|
};
|
|
#endif /*0*/
|
|
#if defined(OPTIONS_AT_RUNTIME)
|
|
use_dlb = FALSE;
|
|
#else
|
|
done_rt = TRUE;
|
|
#endif
|
|
|
|
/* instead of using ``display_file(OPTIONS_USED,TRUE)'' we handle
|
|
the file manually so we can include dynamic version info */
|
|
|
|
(void) getversionstring(buf, sizeof buf);
|
|
/* if extra text (git info) is present, put it on separate line
|
|
but don't wrap on (x86) */
|
|
if (strlen(buf) >= COLNO)
|
|
p = strrchr(buf, '(');
|
|
if (p && p > buf && p[-1] == ' ' && p[1] != 'x')
|
|
p[-1] = '\0';
|
|
else
|
|
p = 0;
|
|
putstr(win, 0, buf);
|
|
if (p) {
|
|
*--p = ' ';
|
|
putstr(win, 0, p);
|
|
}
|
|
|
|
if (use_dlb) {
|
|
f = dlb_fopen(OPTIONS_USED, "r");
|
|
if (!f) {
|
|
putstr(win, 0, "");
|
|
Sprintf(buf, "[Configuration '%s' not available?]", OPTIONS_USED);
|
|
putstr(win, 0, buf);
|
|
done_dlb = TRUE;
|
|
}
|
|
}
|
|
/*
|
|
* already inserted above:
|
|
* + outdented program name and version plus build date and time
|
|
* dat/options; display contents with lines prefixed by '-' deleted:
|
|
* - blank-line
|
|
* - indented program name and version
|
|
* blank-line
|
|
* outdented feature header
|
|
* - blank-line
|
|
* indented feature list
|
|
* spread over multiple lines
|
|
* blank-line
|
|
* outdented windowing header
|
|
* - blank-line
|
|
* indented windowing choices with
|
|
* optional second line for default
|
|
* - blank-line
|
|
* - EOF
|
|
*/
|
|
|
|
prolog = TRUE; /* to skip indented program name */
|
|
for (;;) {
|
|
if (use_dlb && !done_dlb) {
|
|
if (!dlb_fgets(buf, BUFSZ, f)) {
|
|
done_dlb = TRUE;
|
|
continue;
|
|
}
|
|
} else if (!done_rt) {
|
|
if (!(rtbuf = do_runtime_info(&rtcontext))) {
|
|
done_rt = TRUE;
|
|
continue;
|
|
}
|
|
(void) strncpy(buf, rtbuf, BUFSZ - 1);
|
|
buf[BUFSZ - 1] = '\0';
|
|
} else {
|
|
break;
|
|
}
|
|
(void) strip_newline(buf);
|
|
if (strchr(buf, '\t') != 0)
|
|
(void) tabexpand(buf);
|
|
|
|
if (*buf && *buf != ' ') {
|
|
/* found outdented header; insert a separator since we'll
|
|
have skipped corresponding blank line inside the file */
|
|
putstr(win, 0, "");
|
|
prolog = FALSE;
|
|
}
|
|
/* skip blank lines and prolog (progame name plus version) */
|
|
if (prolog || !*buf)
|
|
continue;
|
|
|
|
if (strchr(buf, ':'))
|
|
insert_rtoption(buf);
|
|
|
|
if (*buf)
|
|
putstr(win, 0, buf);
|
|
}
|
|
if (use_dlb)
|
|
(void) dlb_fclose(f);
|
|
display_nhwindow(win, FALSE);
|
|
destroy_nhwindow(win);
|
|
return ECMD_OK;
|
|
}
|
|
|
|
void
|
|
early_version_info(boolean pastebuf)
|
|
{
|
|
char buf1[BUFSZ], buf2[BUFSZ];
|
|
char *buf, *tmp;
|
|
|
|
Snprintf(buf1, sizeof(buf1), "test");
|
|
/* this is early enough that we have to do our own line-splitting */
|
|
getversionstring(buf1, sizeof buf1);
|
|
tmp = strstri(buf1, " ("); /* split at start of version info */
|
|
if (tmp) {
|
|
/* retain one buffer so that it all goes into the paste buffer */
|
|
*tmp++ = '\0';
|
|
Snprintf(buf2, sizeof(buf2), "%s\n%s", buf1, tmp);
|
|
buf = buf2;
|
|
} else {
|
|
buf = buf1;
|
|
}
|
|
|
|
raw_printf("%s", buf);
|
|
|
|
if (pastebuf) {
|
|
#if defined(RUNTIME_PASTEBUF_SUPPORT) && !defined(LIBNH)
|
|
/*
|
|
* Call a platform/port-specific routine to insert the
|
|
* version information into a paste buffer. Useful for
|
|
* easy inclusion in bug reports.
|
|
*/
|
|
port_insert_pastebuf(buf);
|
|
#else
|
|
raw_printf("%s", "Paste buffer copy is not available.\n");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
extern const char regex_id[];
|
|
|
|
/*
|
|
* makedefs should put the first token into dat/options; we'll substitute
|
|
* the second value for it. The token must contain at least one colon
|
|
* so that we can spot it, and should not contain spaces so that makedefs
|
|
* won't split it across lines. Ideally the length should be close to
|
|
* that of the substituted value since we don't do phrase-splitting/line-
|
|
* wrapping when displaying it.
|
|
*/
|
|
static struct rt_opt {
|
|
const char *token, *value;
|
|
} rt_opts[] = {
|
|
{ ":PATMATCH:", regex_id },
|
|
{ ":LUAVERSION:", (const char *) gl.lua_ver },
|
|
{ ":LUACOPYRIGHT:", (const char *) gl.lua_copyright },
|
|
};
|
|
|
|
/*
|
|
* 3.6.0
|
|
* Some optional stuff is no longer available to makedefs because
|
|
* it depends which of several object files got linked into the
|
|
* game image, so we insert those options here.
|
|
*/
|
|
static void
|
|
insert_rtoption(char *buf)
|
|
{
|
|
int i;
|
|
|
|
if (!gl.lua_ver[0])
|
|
get_lua_version();
|
|
|
|
for (i = 0; i < SIZE(rt_opts); ++i) {
|
|
if (strstri(buf, rt_opts[i].token) && *rt_opts[i].value) {
|
|
(void) strsubst(buf, rt_opts[i].token, rt_opts[i].value);
|
|
}
|
|
/* we don't break out of the loop after a match; there might be
|
|
other matches on the same line */
|
|
}
|
|
}
|
|
|
|
#ifdef MICRO
|
|
boolean
|
|
comp_times(long filetime)
|
|
{
|
|
/* BUILD_TIME is constant but might have L suffix rather than UL;
|
|
'filetime' is historically signed but ought to have been unsigned */
|
|
return ((unsigned long) filetime < (unsigned long) nomakedefs.build_time);
|
|
}
|
|
#endif
|
|
|
|
boolean
|
|
check_version(
|
|
struct version_info *version_data,
|
|
const char *filename,
|
|
boolean complain,
|
|
unsigned long utdflags)
|
|
{
|
|
if (
|
|
#ifdef VERSION_COMPATIBILITY /* patchlevel.h */
|
|
version_data->incarnation < VERSION_COMPATIBILITY
|
|
|| version_data->incarnation > nomakedefs.version_number
|
|
#else
|
|
version_data->incarnation != nomakedefs.version_number
|
|
#endif
|
|
) {
|
|
if (complain) {
|
|
pline("Version mismatch for file \"%s\".", filename);
|
|
display_nhwindow(WIN_MESSAGE, TRUE);
|
|
}
|
|
return FALSE;
|
|
} else if (
|
|
(version_data->feature_set & ~nomakedefs.ignored_features)
|
|
!= (nomakedefs.version_features & ~nomakedefs.ignored_features)
|
|
|| ((utdflags & UTD_SKIP_SANITY1) == 0
|
|
&& version_data->entity_count != nomakedefs.version_sanity1)
|
|
|| ((utdflags & UTD_CHECKSIZES) != 0
|
|
&& version_data->struct_sizes1 != nomakedefs.version_sanity2)
|
|
|| ((utdflags & UTD_CHECKSIZES) != 0
|
|
&& version_data->struct_sizes2 != nomakedefs.version_sanity3)
|
|
) {
|
|
if (complain) {
|
|
pline("Configuration incompatibility for file \"%s\".", filename);
|
|
display_nhwindow(WIN_MESSAGE, TRUE);
|
|
}
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* this used to be based on file date and somewhat OS-dependant,
|
|
but now examines the initial part of the file's contents */
|
|
boolean
|
|
uptodate(NHFILE *nhfp, const char *name, unsigned long utdflags)
|
|
{
|
|
ssize_t rlen = 0;
|
|
int cmc = 0, filecmc = 0;
|
|
struct version_info vers_info;
|
|
boolean verbose = name ? TRUE : FALSE;
|
|
char indicator;
|
|
|
|
if (nhfp->structlevel) {
|
|
rlen = read(nhfp->fd, (genericptr_t) &indicator, sizeof indicator);
|
|
rlen = read(nhfp->fd, (genericptr_t) &filecmc, sizeof filecmc);
|
|
if (rlen == 0)
|
|
return FALSE;
|
|
}
|
|
if (cmc != filecmc)
|
|
return FALSE;
|
|
|
|
rlen = read(nhfp->fd, (genericptr_t) &vers_info, sizeof vers_info);
|
|
minit(); /* ZEROCOMP */
|
|
if (rlen == 0) {
|
|
if (verbose) {
|
|
pline("File \"%s\" is empty?", name);
|
|
wait_synch();
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
if (!check_version(&vers_info, name, verbose, utdflags)) {
|
|
if (verbose)
|
|
wait_synch();
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
store_formatindicator(NHFILE *nhfp)
|
|
{
|
|
char indicate = 'u';
|
|
int cmc = 0;
|
|
|
|
if (nhfp->mode & WRITING) {
|
|
if (nhfp->structlevel) {
|
|
indicate = 'h'; /* historical */
|
|
bwrite(nhfp->fd, (genericptr_t) &indicate, sizeof indicate);
|
|
bwrite(nhfp->fd, (genericptr_t) &cmc, sizeof cmc);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
store_version(NHFILE *nhfp)
|
|
{
|
|
struct version_info version_data = {
|
|
0UL, 0UL, 0UL, 0UL, 0UL
|
|
};
|
|
|
|
/* actual version number */
|
|
version_data.incarnation = nomakedefs.version_number;
|
|
/* bitmask of config settings */
|
|
version_data.feature_set = nomakedefs.version_features;
|
|
/* # of monsters and objects */
|
|
version_data.entity_count = nomakedefs.version_sanity1;
|
|
/* size of key structs */
|
|
version_data.struct_sizes1 = nomakedefs.version_sanity2;
|
|
/* size of more key structs */
|
|
version_data.struct_sizes2 = nomakedefs.version_sanity3;
|
|
|
|
if (nhfp->structlevel) {
|
|
bufoff(nhfp->fd);
|
|
/* bwrite() before bufon() uses plain write() */
|
|
store_formatindicator(nhfp);
|
|
bwrite(nhfp->fd, (genericptr_t) &version_data,
|
|
(unsigned) (sizeof version_data));
|
|
bufon(nhfp->fd);
|
|
}
|
|
return;
|
|
}
|
|
|
|
#ifdef AMIGA
|
|
const char amiga_version_string[] = AMIGA_VERSION_STRING;
|
|
#endif
|
|
|
|
unsigned long
|
|
get_feature_notice_ver(char *str)
|
|
{
|
|
char buf[BUFSZ];
|
|
int ver_maj, ver_min, patch;
|
|
char *istr[3];
|
|
int j = 0;
|
|
|
|
if (!str)
|
|
return 0L;
|
|
str = strcpy(buf, str);
|
|
istr[j] = str;
|
|
while (*str) {
|
|
if (*str == '.') {
|
|
*str++ = '\0';
|
|
j++;
|
|
istr[j] = str;
|
|
if (j == 2)
|
|
break;
|
|
} else if (strchr("0123456789", *str) != 0) {
|
|
str++;
|
|
} else
|
|
return 0L;
|
|
}
|
|
if (j != 2)
|
|
return 0L;
|
|
ver_maj = atoi(istr[0]);
|
|
ver_min = atoi(istr[1]);
|
|
patch = atoi(istr[2]);
|
|
return FEATURE_NOTICE_VER(ver_maj, ver_min, patch);
|
|
/* macro from hack.h */
|
|
}
|
|
|
|
unsigned long
|
|
get_current_feature_ver(void)
|
|
{
|
|
return FEATURE_NOTICE_VER(VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL);
|
|
}
|
|
|
|
/*ARGUSED*/
|
|
const char *
|
|
copyright_banner_line(int indx)
|
|
{
|
|
#ifdef COPYRIGHT_BANNER_A
|
|
if (indx == 1)
|
|
return COPYRIGHT_BANNER_A;
|
|
#endif
|
|
#ifdef COPYRIGHT_BANNER_B
|
|
if (indx == 2)
|
|
return COPYRIGHT_BANNER_B;
|
|
#endif
|
|
|
|
if (indx == 3)
|
|
return nomakedefs.copyright_banner_c;
|
|
|
|
#ifdef COPYRIGHT_BANNER_D
|
|
if (indx == 4)
|
|
return COPYRIGHT_BANNER_D;
|
|
#endif
|
|
return "";
|
|
}
|
|
|
|
/*version.c*/
|