Change lev_comp's add_opvars() to be like pline(), where there's a
single visible opening brace and a second one hidden in VA_DECL2
that introduces a nested block, plus a single visible closing brace
with a hidden one in VA_END() to close the nested block. This
addresses the erroneous report (sent directly to devteam, so no #H
number, subject "missing '{' in util/lev_main.c:634") that the code
for !USE_STDARG/!USE_VARARGS in add_opvars() wouldn't compile.
Also, fix the part of "#H5778: file descriptor leaks" dealing with
util/recover.c -- an open file not being closed after various errors.
I didn't take responsibility for this entry in the bugzilla list
since the report includes similar problems in other code that's not
addressed here.
And a blast from the past: some reformatting fixups in recover.c.
The most interesting bit is for a block of dead code....
458 lines
11 KiB
C
458 lines
11 KiB
C
/* NetHack 3.6 recover.c $NHDT-Date: 1501461282 2017/07/31 00:34:42 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.16 $ */
|
|
/* Copyright (c) Janet Walz, 1992. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
/*
|
|
* Utility for reconstructing NetHack save file from a set of individual
|
|
* level files. Requires that the `checkpoint' option be enabled at the
|
|
* time NetHack creates those level files.
|
|
*/
|
|
#include "config.h"
|
|
#if !defined(O_WRONLY) && !defined(LSC) && !defined(AZTEC_C)
|
|
#include <fcntl.h>
|
|
#endif
|
|
#ifdef WIN32
|
|
#include <errno.h>
|
|
#include "win32api.h"
|
|
#endif
|
|
|
|
#ifdef VMS
|
|
extern int FDECL(vms_creat, (const char *, unsigned));
|
|
extern int FDECL(vms_open, (const char *, int, unsigned));
|
|
#endif /* VMS */
|
|
|
|
int FDECL(restore_savefile, (char *));
|
|
void FDECL(set_levelfile_name, (int));
|
|
int FDECL(open_levelfile, (int));
|
|
int NDECL(create_savefile);
|
|
void FDECL(copy_bytes, (int, int));
|
|
|
|
#ifndef WIN_CE
|
|
#define Fprintf (void) fprintf
|
|
#else
|
|
#define Fprintf (void) nhce_message
|
|
static void nhce_message(FILE *, const char *, ...);
|
|
#endif
|
|
|
|
#define Close (void) close
|
|
|
|
#ifdef UNIX
|
|
#define SAVESIZE (PL_NSIZ + 13) /* save/99999player.e */
|
|
#else
|
|
#ifdef VMS
|
|
#define SAVESIZE (PL_NSIZ + 22) /* [.save]<uid>player.e;1 */
|
|
#else
|
|
#ifdef WIN32
|
|
#define SAVESIZE (PL_NSIZ + 40) /* username-player.NetHack-saved-game */
|
|
#else
|
|
#define SAVESIZE FILENAME /* from macconf.h or pcconf.h */
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(EXEPATH)
|
|
char *FDECL(exepath, (char *));
|
|
#endif
|
|
|
|
#if defined(__BORLANDC__) && !defined(_WIN32)
|
|
extern unsigned _stklen = STKSIZ;
|
|
#endif
|
|
char
|
|
savename[SAVESIZE]; /* holds relative path of save file from playground */
|
|
|
|
int
|
|
main(argc, argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
int argno;
|
|
const char *dir = (char *) 0;
|
|
#ifdef AMIGA
|
|
char *startdir = (char *) 0;
|
|
#endif
|
|
|
|
if (!dir)
|
|
dir = getenv("NETHACKDIR");
|
|
if (!dir)
|
|
dir = getenv("HACKDIR");
|
|
#if defined(EXEPATH)
|
|
if (!dir)
|
|
dir = exepath(argv[0]);
|
|
#endif
|
|
if (argc == 1 || (argc == 2 && !strcmp(argv[1], "-"))) {
|
|
Fprintf(stderr, "Usage: %s [ -d directory ] base1 [ base2 ... ]\n",
|
|
argv[0]);
|
|
#if defined(WIN32) || defined(MSDOS)
|
|
if (dir) {
|
|
Fprintf(
|
|
stderr,
|
|
"\t(Unless you override it with -d, recover will look \n");
|
|
Fprintf(stderr, "\t in the %s directory on your system)\n", dir);
|
|
}
|
|
#endif
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
argno = 1;
|
|
if (!strncmp(argv[argno], "-d", 2)) {
|
|
dir = argv[argno] + 2;
|
|
if (*dir == '=' || *dir == ':')
|
|
dir++;
|
|
if (!*dir && argc > argno) {
|
|
argno++;
|
|
dir = argv[argno];
|
|
}
|
|
if (!*dir) {
|
|
Fprintf(stderr,
|
|
"%s: flag -d must be followed by a directory name.\n",
|
|
argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
argno++;
|
|
}
|
|
#if defined(SECURE) && !defined(VMS)
|
|
if (dir
|
|
#ifdef HACKDIR
|
|
&& strcmp(dir, HACKDIR)
|
|
#endif
|
|
) {
|
|
(void) setgid(getgid());
|
|
(void) setuid(getuid());
|
|
}
|
|
#endif /* SECURE && !VMS */
|
|
|
|
#ifdef HACKDIR
|
|
if (!dir)
|
|
dir = HACKDIR;
|
|
#endif
|
|
|
|
#ifdef AMIGA
|
|
startdir = getcwd(0, 255);
|
|
#endif
|
|
if (dir && chdir((char *) dir) < 0) {
|
|
Fprintf(stderr, "%s: cannot chdir to %s.\n", argv[0], dir);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
while (argc > argno) {
|
|
if (restore_savefile(argv[argno]) == 0)
|
|
Fprintf(stderr, "recovered \"%s\" to %s\n", argv[argno],
|
|
savename);
|
|
argno++;
|
|
}
|
|
#ifdef AMIGA
|
|
if (startdir)
|
|
(void) chdir(startdir);
|
|
#endif
|
|
exit(EXIT_SUCCESS);
|
|
/*NOTREACHED*/
|
|
return 0;
|
|
}
|
|
|
|
static char lock[256];
|
|
|
|
void
|
|
set_levelfile_name(lev)
|
|
int lev;
|
|
{
|
|
char *tf;
|
|
|
|
tf = rindex(lock, '.');
|
|
if (!tf)
|
|
tf = lock + strlen(lock);
|
|
(void) sprintf(tf, ".%d", lev);
|
|
#ifdef VMS
|
|
(void) strcat(tf, ";1");
|
|
#endif
|
|
}
|
|
|
|
int
|
|
open_levelfile(lev)
|
|
int lev;
|
|
{
|
|
int fd;
|
|
|
|
set_levelfile_name(lev);
|
|
#if defined(MICRO) || defined(WIN32) || defined(MSDOS)
|
|
fd = open(lock, O_RDONLY | O_BINARY);
|
|
#else
|
|
fd = open(lock, O_RDONLY, 0);
|
|
#endif
|
|
return fd;
|
|
}
|
|
|
|
int
|
|
create_savefile()
|
|
{
|
|
int fd;
|
|
|
|
#if defined(MICRO) || defined(WIN32) || defined(MSDOS)
|
|
fd = open(savename, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK);
|
|
#else
|
|
fd = creat(savename, FCMASK);
|
|
#endif
|
|
return fd;
|
|
}
|
|
|
|
void
|
|
copy_bytes(ifd, ofd)
|
|
int ifd, ofd;
|
|
{
|
|
char buf[BUFSIZ];
|
|
int nfrom, nto;
|
|
|
|
do {
|
|
nfrom = read(ifd, buf, BUFSIZ);
|
|
nto = write(ofd, buf, nfrom);
|
|
if (nto != nfrom) {
|
|
Fprintf(stderr, "file copy failed!\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
} while (nfrom == BUFSIZ);
|
|
}
|
|
|
|
int
|
|
restore_savefile(basename)
|
|
char *basename;
|
|
{
|
|
int gfd, lfd, sfd;
|
|
int lev, savelev, hpid, pltmpsiz;
|
|
xchar levc;
|
|
struct version_info version_data;
|
|
struct savefile_info sfi;
|
|
char plbuf[PL_NSIZ];
|
|
|
|
/* level 0 file contains:
|
|
* pid of creating process (ignored here)
|
|
* level number for current level of save file
|
|
* name of save file nethack would have created
|
|
* savefile info
|
|
* player name
|
|
* and game state
|
|
*/
|
|
(void) strcpy(lock, basename);
|
|
gfd = open_levelfile(0);
|
|
if (gfd < 0) {
|
|
#if defined(WIN32) && !defined(WIN_CE)
|
|
if (errno == EACCES) {
|
|
Fprintf(
|
|
stderr,
|
|
"\nThere are files from a game in progress under your name.");
|
|
Fprintf(stderr, "\nThe files are locked or inaccessible.");
|
|
Fprintf(stderr, "\nPerhaps the other game is still running?\n");
|
|
} else
|
|
Fprintf(stderr, "\nTrouble accessing level 0 (errno = %d).\n",
|
|
errno);
|
|
#endif
|
|
Fprintf(stderr, "Cannot open level 0 for %s.\n", basename);
|
|
return -1;
|
|
}
|
|
if (read(gfd, (genericptr_t) &hpid, sizeof hpid) != sizeof hpid) {
|
|
Fprintf(
|
|
stderr, "%s\n%s%s%s\n",
|
|
"Checkpoint data incompletely written or subsequently clobbered;",
|
|
"recovery for \"", basename, "\" impossible.");
|
|
Close(gfd);
|
|
return -1;
|
|
}
|
|
if (read(gfd, (genericptr_t) &savelev, sizeof(savelev))
|
|
!= sizeof(savelev)) {
|
|
Fprintf(stderr, "Checkpointing was not in effect for %s -- recovery "
|
|
"impossible.\n",
|
|
basename);
|
|
Close(gfd);
|
|
return -1;
|
|
}
|
|
if ((read(gfd, (genericptr_t) savename, sizeof savename)
|
|
!= sizeof savename)
|
|
|| (read(gfd, (genericptr_t) &version_data, sizeof version_data)
|
|
!= sizeof version_data)
|
|
|| (read(gfd, (genericptr_t) &sfi, sizeof sfi) != sizeof sfi)
|
|
|| (read(gfd, (genericptr_t) &pltmpsiz, sizeof pltmpsiz)
|
|
!= sizeof pltmpsiz) || (pltmpsiz > PL_NSIZ)
|
|
|| (read(gfd, (genericptr_t) &plbuf, pltmpsiz) != pltmpsiz)) {
|
|
Fprintf(stderr, "Error reading %s -- can't recover.\n", lock);
|
|
Close(gfd);
|
|
return -1;
|
|
}
|
|
|
|
/* save file should contain:
|
|
* version info
|
|
* savefile info
|
|
* player name
|
|
* current level (including pets)
|
|
* (non-level-based) game state
|
|
* other levels
|
|
*/
|
|
sfd = create_savefile();
|
|
if (sfd < 0) {
|
|
Fprintf(stderr, "Cannot create savefile %s.\n", savename);
|
|
Close(gfd);
|
|
return -1;
|
|
}
|
|
|
|
lfd = open_levelfile(savelev);
|
|
if (lfd < 0) {
|
|
Fprintf(stderr, "Cannot open level of save for %s.\n", basename);
|
|
Close(gfd);
|
|
Close(sfd);
|
|
return -1;
|
|
}
|
|
|
|
if (write(sfd, (genericptr_t) &version_data, sizeof version_data)
|
|
!= sizeof version_data) {
|
|
Fprintf(stderr, "Error writing %s; recovery failed.\n", savename);
|
|
Close(gfd);
|
|
Close(sfd);
|
|
Close(lfd);
|
|
return -1;
|
|
}
|
|
|
|
if (write(sfd, (genericptr_t) &sfi, sizeof sfi) != sizeof sfi) {
|
|
Fprintf(stderr,
|
|
"Error writing %s; recovery failed (savefile_info).\n",
|
|
savename);
|
|
Close(gfd);
|
|
Close(sfd);
|
|
Close(lfd);
|
|
return -1;
|
|
}
|
|
|
|
if (write(sfd, (genericptr_t) &pltmpsiz, sizeof pltmpsiz)
|
|
!= sizeof pltmpsiz) {
|
|
Fprintf(stderr,
|
|
"Error writing %s; recovery failed (player name size).\n",
|
|
savename);
|
|
Close(gfd);
|
|
Close(sfd);
|
|
Close(lfd);
|
|
return -1;
|
|
}
|
|
|
|
if (write(sfd, (genericptr_t) &plbuf, pltmpsiz) != pltmpsiz) {
|
|
Fprintf(stderr, "Error writing %s; recovery failed (player name).\n",
|
|
savename);
|
|
Close(gfd);
|
|
Close(sfd);
|
|
Close(lfd);
|
|
return -1;
|
|
}
|
|
|
|
copy_bytes(lfd, sfd);
|
|
Close(lfd);
|
|
(void) unlink(lock);
|
|
|
|
copy_bytes(gfd, sfd);
|
|
Close(gfd);
|
|
set_levelfile_name(0);
|
|
(void) unlink(lock);
|
|
|
|
for (lev = 1; lev < 256; lev++) {
|
|
/* level numbers are kept in xchars in save.c, so the
|
|
* maximum level number (for the endlevel) must be < 256
|
|
*/
|
|
if (lev != savelev) {
|
|
lfd = open_levelfile(lev);
|
|
if (lfd >= 0) {
|
|
/* any or all of these may not exist */
|
|
levc = (xchar) lev;
|
|
write(sfd, (genericptr_t) &levc, sizeof(levc));
|
|
copy_bytes(lfd, sfd);
|
|
Close(lfd);
|
|
(void) unlink(lock);
|
|
}
|
|
}
|
|
}
|
|
|
|
Close(sfd);
|
|
|
|
#if 0 /* OBSOLETE, HackWB is no longer in use */
|
|
#ifdef AMIGA
|
|
{
|
|
/* we need to create an icon for the saved game
|
|
* or HackWB won't notice the file.
|
|
*/
|
|
char iconfile[FILENAME];
|
|
int in, out;
|
|
|
|
(void) sprintf(iconfile, "%s.info", savename);
|
|
in = open("NetHack:default.icon", O_RDONLY);
|
|
out = open(iconfile, O_WRONLY | O_TRUNC | O_CREAT);
|
|
if (in > -1 && out > -1) {
|
|
copy_bytes(in, out);
|
|
}
|
|
if (in > -1)
|
|
close(in);
|
|
if (out > -1)
|
|
close(out);
|
|
}
|
|
#endif /*AMIGA*/
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#ifdef EXEPATH
|
|
#ifdef __DJGPP__
|
|
#define PATH_SEPARATOR '/'
|
|
#else
|
|
#define PATH_SEPARATOR '\\'
|
|
#endif
|
|
|
|
#define EXEPATHBUFSZ 256
|
|
char exepathbuf[EXEPATHBUFSZ];
|
|
|
|
char *
|
|
exepath(str)
|
|
char *str;
|
|
{
|
|
char *tmp, *tmp2;
|
|
int bsize;
|
|
|
|
if (!str)
|
|
return (char *) 0;
|
|
bsize = EXEPATHBUFSZ;
|
|
tmp = exepathbuf;
|
|
#if !defined(WIN32)
|
|
strcpy(tmp, str);
|
|
#else
|
|
#if defined(WIN_CE)
|
|
{
|
|
TCHAR wbuf[EXEPATHBUFSZ];
|
|
GetModuleFileName((HANDLE) 0, wbuf, EXEPATHBUFSZ);
|
|
NH_W2A(wbuf, tmp, bsize);
|
|
}
|
|
#else
|
|
*(tmp + GetModuleFileName((HANDLE) 0, tmp, bsize)) = '\0';
|
|
#endif
|
|
#endif
|
|
tmp2 = strrchr(tmp, PATH_SEPARATOR);
|
|
if (tmp2)
|
|
*tmp2 = '\0';
|
|
return tmp;
|
|
}
|
|
#endif /* EXEPATH */
|
|
|
|
#ifdef AMIGA
|
|
#include "date.h"
|
|
const char amiga_version_string[] = AMIGA_VERSION_STRING;
|
|
#endif
|
|
|
|
#ifdef WIN_CE
|
|
void
|
|
nhce_message(FILE *f, const char *str, ...)
|
|
{
|
|
va_list ap;
|
|
TCHAR wbuf[NHSTR_BUFSIZE];
|
|
char buf[NHSTR_BUFSIZE];
|
|
|
|
va_start(ap, str);
|
|
vsprintf(buf, str, ap);
|
|
va_end(ap);
|
|
|
|
MessageBox(NULL, NH_A2W(buf, wbuf, NHSTR_BUFSIZE), TEXT("Recover"),
|
|
MB_OK);
|
|
}
|
|
#endif
|
|
|
|
/*recover.c*/
|