1 /* NetHack 3.6	version.c	$NHDT-Date: 1552353060 2019/03/12 01:11:00 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.52 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Michael Allison, 2018. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 
6 #include "hack.h"
7 #include "dlb.h"
8 #include "date.h"
9 /*
10  * All the references to the contents of patchlevel.h have been moved
11  * into makedefs....
12  */
13 #ifdef SHORT_FILENAMES
14 #include "patchlev.h"
15 #else
16 #include "patchlevel.h"
17 #endif
18 
19 #if defined(NETHACK_GIT_SHA)
20 const char *NetHack_git_sha = NETHACK_GIT_SHA;
21 #endif
22 #if defined(NETHACK_GIT_BRANCH)
23 const char *NetHack_git_branch = NETHACK_GIT_BRANCH;
24 #endif
25 
26 STATIC_DCL void FDECL(insert_rtoption, (char *));
27 
28 /* fill buffer with short version (so caller can avoid including date.h) */
29 char *
version_string(buf)30 version_string(buf)
31 char *buf;
32 {
33     return strcpy(buf, VERSION_STRING);
34 }
35 
36 /* fill and return the given buffer with the long nethack version string */
37 char *
getversionstring(buf)38 getversionstring(buf)
39 char *buf;
40 {
41     Strcpy(buf, VERSION_ID);
42 
43 #if defined(RUNTIME_PORT_ID) \
44     || defined(NETHACK_GIT_SHA) || defined(NETHACK_GIT_BRANCH)
45     {
46         int c = 0;
47 #if defined(RUNTIME_PORT_ID)
48         char tmpbuf[BUFSZ], *tmp;
49 #endif
50         char *p = eos(buf);
51         boolean dotoff = (p > buf && p[-1] == '.');
52 
53         if (dotoff)
54             --p;
55         Strcpy(p, " (");
56 #if defined(RUNTIME_PORT_ID)
57         tmp = get_port_id(tmpbuf);
58         if (tmp)
59             Sprintf(eos(buf), "%s%s", c++ ? "," : "", tmp);
60 #endif
61 #if defined(NETHACK_GIT_SHA)
62         if (NetHack_git_sha)
63             Sprintf(eos(buf), "%s%s", c++ ? "," : "", NetHack_git_sha);
64 #endif
65 #if defined(NETHACK_GIT_BRANCH)
66 #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
67         if (NetHack_git_branch)
68             Sprintf(eos(buf), "%sbranch:%s",
69                     c++ ? "," : "", NetHack_git_branch);
70 #endif
71 #endif
72         if (c)
73             Strcat(buf, ")");
74         else /* if nothing has been added, strip " (" back off */
75             *p = '\0';
76         if (dotoff)
77             Strcat(buf, ".");
78     }
79 #endif /* RUNTIME_PORT_ID || NETHACK_GIT_SHA || NETHACK_GIT_BRANCH */
80 
81     return buf;
82 }
83 
84 /* the 'v' command */
85 int
doversion()86 doversion()
87 {
88     char buf[BUFSZ];
89 
90     pline("%s", getversionstring(buf));
91     return 0;
92 }
93 
94 /* the '#version' command; also a choice for '?' */
95 int
doextversion()96 doextversion()
97 {
98     dlb *f;
99     char buf[BUFSZ], *p = 0;
100     winid win = create_nhwindow(NHW_TEXT);
101 
102     /* instead of using ``display_file(OPTIONS_USED,TRUE)'' we handle
103        the file manually so we can include dynamic version info */
104 
105     (void) getversionstring(buf);
106     /* if extra text (git info) is present, put it on separate line */
107     if (strlen(buf) >= COLNO)
108         p = rindex(buf, '(');
109     if (p && p > buf && p[-1] == ' ')
110         p[-1] = '\0';
111     else
112         p = 0;
113     putstr(win, 0, buf);
114     if (p) {
115         *--p = ' ';
116         putstr(win, 0, p);
117     }
118 
119     f = dlb_fopen(OPTIONS_USED, "r");
120     if (!f) {
121         putstr(win, 0, "");
122         Sprintf(buf, "[Configuration '%s' not available?]", OPTIONS_USED);
123         putstr(win, 0, buf);
124     } else {
125         /*
126          * already inserted above:
127          * + outdented program name and version plus build date and time
128          * dat/options; display contents with lines prefixed by '-' deleted:
129          * - blank-line
130          * -     indented program name and version
131          *   blank-line
132          *   outdented feature header
133          * - blank-line
134          *       indented feature list
135          *       spread over multiple lines
136          *   blank-line
137          *   outdented windowing header
138          * - blank-line
139          *       indented windowing choices with
140          *       optional second line for default
141          * - blank-line
142          * - EOF
143          */
144         boolean prolog = TRUE; /* to skip indented program name */
145 
146         while (dlb_fgets(buf, BUFSZ, f)) {
147             (void) strip_newline(buf);
148             if (index(buf, '\t') != 0)
149                 (void) tabexpand(buf);
150 
151             if (*buf && *buf != ' ') {
152                 /* found outdented header; insert a separator since we'll
153                    have skipped corresponding blank line inside the file */
154                 putstr(win, 0, "");
155                 prolog = FALSE;
156             }
157             /* skip blank lines and prolog (progame name plus version) */
158             if (prolog || !*buf)
159                 continue;
160 
161             if (index(buf, ':'))
162                 insert_rtoption(buf);
163 
164             if (*buf)
165                 putstr(win, 0, buf);
166         }
167         (void) dlb_fclose(f);
168         display_nhwindow(win, FALSE);
169         destroy_nhwindow(win);
170     }
171     return 0;
172 }
173 
early_version_info(pastebuf)174 void early_version_info(pastebuf)
175 boolean pastebuf;
176 {
177     char buf[BUFSZ], buf2[BUFSZ];
178     char *tmp = getversionstring(buf);
179 
180     /* this is early enough that we have to do
181        our own line-splitting */
182     if (tmp) {
183         tmp = strstri(buf," (");
184         if (tmp) *tmp++ = '\0';
185     }
186 
187     Sprintf(buf2, "%s\n", buf);
188     if (tmp) Sprintf(eos(buf2), "%s\n", tmp);
189     raw_printf("%s", buf2);
190 
191     if (pastebuf) {
192 #ifdef RUNTIME_PASTEBUF_SUPPORT
193         /*
194          * Call a platform/port-specific routine to insert the
195          * version information into a paste buffer. Useful for
196          * easy inclusion in bug reports.
197          */
198         port_insert_pastebuf(buf2);
199 #else
200         raw_printf("%s", "Paste buffer copy is not available.\n");
201 #endif
202     }
203 }
204 
205 extern const char regex_id[];
206 
207 /*
208  * makedefs should put the first token into dat/options; we'll substitute
209  * the second value for it.  The token must contain at least one colon
210  * so that we can spot it, and should not contain spaces so that makedefs
211  * won't split it across lines.  Ideally the length should be close to
212  * that of the substituted value since we don't do phrase-splitting/line-
213  * wrapping when displaying it.
214  */
215 static struct rt_opt {
216     const char *token, *value;
217 } rt_opts[] = {
218     { ":PATMATCH:", regex_id },
219 };
220 
221 /*
222  * 3.6.0
223  * Some optional stuff is no longer available to makedefs because
224  * it depends which of several object files got linked into the
225  * game image, so we insert those options here.
226  */
227 STATIC_OVL void
insert_rtoption(buf)228 insert_rtoption(buf)
229 char *buf;
230 {
231     int i;
232 
233     for (i = 0; i < SIZE(rt_opts); ++i) {
234         if (strstri(buf, rt_opts[i].token))
235             (void) strsubst(buf, rt_opts[i].token, rt_opts[i].value);
236         /* we don't break out of the loop after a match; there might be
237            other matches on the same line */
238     }
239 }
240 
241 #ifdef MICRO
242 boolean
comp_times(filetime)243 comp_times(filetime)
244 long filetime;
245 {
246     /* BUILD_TIME is constant but might have L suffix rather than UL;
247        'filetime' is historically signed but ought to have been unsigned */
248     return (boolean) ((unsigned long) filetime < (unsigned long) BUILD_TIME);
249 }
250 #endif
251 
252 boolean
check_version(version_data,filename,complain)253 check_version(version_data, filename, complain)
254 struct version_info *version_data;
255 const char *filename;
256 boolean complain;
257 {
258     if (
259 #ifdef VERSION_COMPATIBILITY
260         version_data->incarnation < VERSION_COMPATIBILITY
261         || version_data->incarnation > VERSION_NUMBER
262 #else
263         version_data->incarnation != VERSION_NUMBER
264 #endif
265         ) {
266         if (complain)
267             pline("Version mismatch for file \"%s\".", filename);
268         return FALSE;
269     } else if (
270 #ifndef IGNORED_FEATURES
271         version_data->feature_set != VERSION_FEATURES
272 #else
273         (version_data->feature_set & ~IGNORED_FEATURES)
274             != (VERSION_FEATURES & ~IGNORED_FEATURES)
275 #endif
276         || version_data->entity_count != VERSION_SANITY1
277         || version_data->struct_sizes1 != VERSION_SANITY2
278         || version_data->struct_sizes2 != VERSION_SANITY3) {
279         if (complain)
280             pline("Configuration incompatibility for file \"%s\".", filename);
281         return FALSE;
282     }
283     return TRUE;
284 }
285 
286 /* this used to be based on file date and somewhat OS-dependant,
287    but now examines the initial part of the file's contents */
288 boolean
uptodate(fd,name)289 uptodate(fd, name)
290 int fd;
291 const char *name;
292 {
293     int rlen;
294     struct version_info vers_info;
295     boolean verbose = name ? TRUE : FALSE;
296 
297     rlen = read(fd, (genericptr_t) &vers_info, sizeof vers_info);
298     minit(); /* ZEROCOMP */
299     if (rlen == 0) {
300         if (verbose) {
301             pline("File \"%s\" is empty?", name);
302             wait_synch();
303         }
304         return FALSE;
305     }
306     if (!check_version(&vers_info, name, verbose)) {
307         if (verbose)
308             wait_synch();
309         return FALSE;
310     }
311     return TRUE;
312 }
313 
314 void
store_version(fd)315 store_version(fd)
316 int fd;
317 {
318     static const struct version_info version_data = {
319         VERSION_NUMBER, VERSION_FEATURES,
320         VERSION_SANITY1, VERSION_SANITY2, VERSION_SANITY3
321     };
322 
323     bufoff(fd);
324     /* bwrite() before bufon() uses plain write() */
325     bwrite(fd, (genericptr_t) &version_data,
326            (unsigned) (sizeof version_data));
327     bufon(fd);
328     return;
329 }
330 
331 #ifdef AMIGA
332 const char amiga_version_string[] = AMIGA_VERSION_STRING;
333 #endif
334 
335 unsigned long
get_feature_notice_ver(str)336 get_feature_notice_ver(str)
337 char *str;
338 {
339     char buf[BUFSZ];
340     int ver_maj, ver_min, patch;
341     char *istr[3];
342     int j = 0;
343 
344     if (!str)
345         return 0L;
346     str = strcpy(buf, str);
347     istr[j] = str;
348     while (*str) {
349         if (*str == '.') {
350             *str++ = '\0';
351             j++;
352             istr[j] = str;
353             if (j == 2)
354                 break;
355         } else if (index("0123456789", *str) != 0) {
356             str++;
357         } else
358             return 0L;
359     }
360     if (j != 2)
361         return 0L;
362     ver_maj = atoi(istr[0]);
363     ver_min = atoi(istr[1]);
364     patch = atoi(istr[2]);
365     return FEATURE_NOTICE_VER(ver_maj, ver_min, patch);
366     /* macro from hack.h */
367 }
368 
369 unsigned long
get_current_feature_ver()370 get_current_feature_ver()
371 {
372     return FEATURE_NOTICE_VER(VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL);
373 }
374 
375 /*ARGUSED*/
376 const char *
copyright_banner_line(indx)377 copyright_banner_line(indx)
378 int indx;
379 {
380 #ifdef COPYRIGHT_BANNER_A
381     if (indx == 1)
382         return COPYRIGHT_BANNER_A;
383 #endif
384 #ifdef COPYRIGHT_BANNER_B
385     if (indx == 2)
386         return COPYRIGHT_BANNER_B;
387 #endif
388 #ifdef COPYRIGHT_BANNER_C
389     if (indx == 3)
390         return COPYRIGHT_BANNER_C;
391 #endif
392 #ifdef COPYRIGHT_BANNER_D
393     if (indx == 4)
394         return COPYRIGHT_BANNER_D;
395 #endif
396     return "";
397 }
398 
399 /*version.c*/
400