Add an optional paniclog file, controlled by a new PANICLOG macro that can be used to log all panic and impossible messages. Helpful when people forget to send, or didn't see, the message.
1808 lines
43 KiB
C
1808 lines
43 KiB
C
/* SCCS Id: @(#)files.c 3.4 2002/02/23 */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "hack.h"
|
|
#include "dlb.h"
|
|
|
|
#ifdef TTY_GRAPHICS
|
|
#include "wintty.h" /* more() */
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
|
|
#if !defined(MAC) && !defined(O_WRONLY) && !defined(AZTEC_C)
|
|
#include <fcntl.h>
|
|
#endif
|
|
#if defined(UNIX) || defined(VMS)
|
|
#include <errno.h>
|
|
# ifndef SKIP_ERRNO
|
|
extern int errno;
|
|
# endif
|
|
#include <signal.h>
|
|
#endif
|
|
|
|
#if defined(MSDOS) || defined(OS2) || defined(TOS) || defined(WIN32)
|
|
# ifndef GNUDOS
|
|
#include <sys\stat.h>
|
|
# else
|
|
#include <sys/stat.h>
|
|
# endif
|
|
#endif
|
|
#ifndef O_BINARY /* used for micros, no-op for others */
|
|
# define O_BINARY 0
|
|
#endif
|
|
|
|
#ifdef PREFIXES_IN_USE
|
|
#define FQN_NUMBUF 3
|
|
static char fqn_filename_buffer[FQN_NUMBUF][FQN_MAX_FILENAME];
|
|
#endif
|
|
|
|
#if !defined(MFLOPPY) && !defined(VMS) && !defined(WIN32)
|
|
char bones[] = "bonesnn.xxx";
|
|
char lock[PL_NSIZ+14] = "1lock"; /* long enough for uid+name+.99 */
|
|
#else
|
|
# if defined(MFLOPPY)
|
|
char bones[FILENAME]; /* pathname of bones files */
|
|
char lock[FILENAME]; /* pathname of level files */
|
|
# endif
|
|
# if defined(VMS)
|
|
char bones[] = "bonesnn.xxx;1";
|
|
char lock[PL_NSIZ+17] = "1lock"; /* long enough for _uid+name+.99;1 */
|
|
# endif
|
|
# if defined(WIN32)
|
|
char bones[] = "bonesnn.xxx";
|
|
char lock[PL_NSIZ+25]; /* long enough for username+-+name+.99 */
|
|
# endif
|
|
#endif
|
|
|
|
#if defined(UNIX) || defined(__BEOS__)
|
|
#define SAVESIZE (PL_NSIZ + 13) /* save/99999player.e */
|
|
#else
|
|
# ifdef VMS
|
|
#define SAVESIZE (PL_NSIZ + 22) /* [.save]<uid>player.e;1 */
|
|
# else
|
|
# if defined(WIN32)
|
|
#define SAVESIZE (PL_NSIZ + 40) /* username-player.NetHack-saved-game */
|
|
# else
|
|
#define SAVESIZE FILENAME /* from macconf.h or pcconf.h */
|
|
# endif
|
|
# endif
|
|
#endif
|
|
|
|
char SAVEF[SAVESIZE]; /* holds relative path of save file from playground */
|
|
#ifdef MICRO
|
|
char SAVEP[SAVESIZE]; /* holds path of directory for save file */
|
|
#endif
|
|
|
|
#ifdef WIZARD
|
|
#define WIZKIT_MAX 128
|
|
static char wizkit[WIZKIT_MAX];
|
|
STATIC_DCL FILE *NDECL(fopen_wizkit_file);
|
|
#endif
|
|
|
|
#ifdef AMIGA
|
|
extern char PATH[]; /* see sys/amiga/amidos.c */
|
|
extern char bbs_id[];
|
|
static int lockptr;
|
|
# ifdef __SASC_60
|
|
#include <proto/dos.h>
|
|
# endif
|
|
|
|
#include <libraries/dos.h>
|
|
extern void FDECL(amii_set_text_font, ( char *, int ));
|
|
#endif
|
|
|
|
#if defined(WIN32) || defined(MSDOS)
|
|
static int lockptr;
|
|
# ifdef MSDOS
|
|
#define Delay(a) msleep(a)
|
|
# endif
|
|
#define Close close
|
|
#define DeleteFile unlink
|
|
#endif
|
|
|
|
#ifdef USER_SOUNDS
|
|
extern char *sounddir;
|
|
#endif
|
|
|
|
extern int n_dgns; /* from dungeon.c */
|
|
|
|
STATIC_DCL char *FDECL(set_bonesfile_name, (char *,d_level*));
|
|
STATIC_DCL char *NDECL(set_bonestemp_name);
|
|
#ifdef COMPRESS
|
|
STATIC_DCL void FDECL(redirect, (const char *,const char *,FILE *,BOOLEAN_P));
|
|
STATIC_DCL void FDECL(docompress_file, (const char *,BOOLEAN_P));
|
|
#endif
|
|
STATIC_DCL char *FDECL(make_lockname, (const char *,char *));
|
|
STATIC_DCL FILE *FDECL(fopen_config_file, (const char *));
|
|
STATIC_DCL int FDECL(get_uchars, (FILE *,char *,char *,uchar *,int,const char *));
|
|
int FDECL(parse_config_line, (FILE *,char *,char *,char *));
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
STATIC_DCL void FDECL(adjust_prefix, (char *, int));
|
|
#endif
|
|
|
|
#ifndef PREFIXES_IN_USE
|
|
/*ARGSUSED*/
|
|
#endif
|
|
const char *
|
|
fqname(basename, whichprefix, buffnum)
|
|
const char *basename;
|
|
int whichprefix, buffnum;
|
|
{
|
|
#ifndef PREFIXES_IN_USE
|
|
return basename;
|
|
#else
|
|
if (!basename || whichprefix < 0 || whichprefix >= PREFIX_COUNT)
|
|
return basename;
|
|
if (!fqn_prefix[whichprefix])
|
|
return basename;
|
|
if (buffnum < 0 || buffnum >= FQN_NUMBUF) {
|
|
impossible("Invalid fqn_filename_buffer specified: %d",
|
|
buffnum);
|
|
buffnum = 0;
|
|
}
|
|
if (strlen(fqn_prefix[whichprefix]) + strlen(basename) >=
|
|
FQN_MAX_FILENAME) {
|
|
impossible("fqname too long: %s + %s", fqn_prefix[whichprefix],
|
|
basename);
|
|
return basename; /* XXX */
|
|
}
|
|
Strcpy(fqn_filename_buffer[buffnum], fqn_prefix[whichprefix]);
|
|
return strcat(fqn_filename_buffer[buffnum], basename);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* fopen a file, with OS-dependent bells and whistles */
|
|
/* NOTE: a simpler version of this routine also exists in util/dlb_main.c */
|
|
FILE *
|
|
fopen_datafile(filename, mode, use_scoreprefix)
|
|
const char *filename, *mode;
|
|
boolean use_scoreprefix;
|
|
{
|
|
FILE *fp;
|
|
|
|
filename = fqname(filename,
|
|
use_scoreprefix ? SCOREPREFIX : DATAPREFIX, 0);
|
|
#ifdef VMS /* essential to have punctuation, to avoid logical names */
|
|
{
|
|
char tmp[BUFSIZ];
|
|
|
|
if (!index(filename, '.') && !index(filename, ';'))
|
|
filename = strcat(strcpy(tmp, filename), ";0");
|
|
fp = fopen(filename, mode, "mbc=16");
|
|
}
|
|
#else
|
|
fp = fopen(filename, mode);
|
|
#endif
|
|
return fp;
|
|
}
|
|
|
|
/* ---------- BEGIN LEVEL FILE HANDLING ----------- */
|
|
|
|
#ifdef MFLOPPY
|
|
/* Set names for bones[] and lock[] */
|
|
void
|
|
set_lock_and_bones()
|
|
{
|
|
if (!ramdisk) {
|
|
Strcpy(levels, permbones);
|
|
Strcpy(bones, permbones);
|
|
}
|
|
append_slash(permbones);
|
|
append_slash(levels);
|
|
#ifdef AMIGA
|
|
strncat(levels, bbs_id, PATHLEN);
|
|
#endif
|
|
append_slash(bones);
|
|
Strcat(bones, "bonesnn.*");
|
|
Strcpy(lock, levels);
|
|
#ifndef AMIGA
|
|
Strcat(lock, alllevels);
|
|
#endif
|
|
return;
|
|
}
|
|
#endif /* MFLOPPY */
|
|
|
|
|
|
/* Construct a file name for a level-type file, which is of the form
|
|
* something.level (with any old level stripped off).
|
|
* This assumes there is space on the end of 'file' to append
|
|
* a two digit number. This is true for 'level'
|
|
* but be careful if you use it for other things -dgk
|
|
*/
|
|
void
|
|
set_levelfile_name(file, lev)
|
|
char *file;
|
|
int lev;
|
|
{
|
|
char *tf;
|
|
|
|
tf = rindex(file, '.');
|
|
if (!tf) tf = eos(file);
|
|
Sprintf(tf, ".%d", lev);
|
|
#ifdef VMS
|
|
Strcat(tf, ";1");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
int
|
|
create_levelfile(lev)
|
|
int lev;
|
|
{
|
|
int fd;
|
|
const char *fq_lock;
|
|
|
|
set_levelfile_name(lock, lev);
|
|
fq_lock = fqname(lock, LEVELPREFIX, 0);
|
|
|
|
#if defined(MICRO) || defined(WIN32)
|
|
/* Use O_TRUNC to force the file to be shortened if it already
|
|
* exists and is currently longer.
|
|
*/
|
|
fd = open(fq_lock, O_WRONLY |O_CREAT | O_TRUNC | O_BINARY, FCMASK);
|
|
#else
|
|
# ifdef MAC
|
|
fd = maccreat(fq_lock, LEVL_TYPE);
|
|
# else
|
|
fd = creat(fq_lock, FCMASK);
|
|
# endif
|
|
#endif /* MICRO || WIN32 */
|
|
|
|
if (fd >= 0)
|
|
level_info[lev].flags |= LFILE_EXISTS;
|
|
|
|
return fd;
|
|
}
|
|
|
|
|
|
int
|
|
open_levelfile(lev)
|
|
int lev;
|
|
{
|
|
int fd;
|
|
const char *fq_lock;
|
|
|
|
set_levelfile_name(lock, lev);
|
|
fq_lock = fqname(lock, LEVELPREFIX, 0);
|
|
#ifdef MFLOPPY
|
|
/* If not currently accessible, swap it in. */
|
|
if (level_info[lev].where != ACTIVE)
|
|
swapin_file(lev);
|
|
#endif
|
|
#ifdef MAC
|
|
fd = macopen(fq_lock, O_RDONLY | O_BINARY, LEVL_TYPE);
|
|
#else
|
|
fd = open(fq_lock, O_RDONLY | O_BINARY, 0);
|
|
#endif
|
|
return fd;
|
|
}
|
|
|
|
|
|
void
|
|
delete_levelfile(lev)
|
|
int lev;
|
|
{
|
|
/*
|
|
* Level 0 might be created by port specific code that doesn't
|
|
* call create_levfile(), so always assume that it exists.
|
|
*/
|
|
if (lev == 0 || (level_info[lev].flags & LFILE_EXISTS)) {
|
|
set_levelfile_name(lock, lev);
|
|
(void) unlink(fqname(lock, LEVELPREFIX, 0));
|
|
level_info[lev].flags &= ~LFILE_EXISTS;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
clearlocks()
|
|
{
|
|
#if !defined(PC_LOCKING) && defined(MFLOPPY) && !defined(AMIGA)
|
|
eraseall(levels, alllevels);
|
|
if (ramdisk)
|
|
eraseall(permbones, alllevels);
|
|
#else
|
|
register int x;
|
|
|
|
# if defined(UNIX) || defined(VMS)
|
|
(void) signal(SIGHUP, SIG_IGN);
|
|
# endif
|
|
/* can't access maxledgerno() before dungeons are created -dlc */
|
|
for (x = (n_dgns ? maxledgerno() : 0); x >= 0; x--)
|
|
delete_levelfile(x); /* not all levels need be present */
|
|
#endif
|
|
}
|
|
|
|
/* ---------- END LEVEL FILE HANDLING ----------- */
|
|
|
|
|
|
/* ---------- BEGIN BONES FILE HANDLING ----------- */
|
|
|
|
/* set up "file" to be file name for retrieving bones, and return a
|
|
* bonesid to be read/written in the bones file.
|
|
*/
|
|
STATIC_OVL char *
|
|
set_bonesfile_name(file, lev)
|
|
char *file;
|
|
d_level *lev;
|
|
{
|
|
s_level *sptr;
|
|
char *dptr;
|
|
|
|
Sprintf(file, "bon%c%s", dungeons[lev->dnum].boneid,
|
|
In_quest(lev) ? urole.filecode : "0");
|
|
dptr = eos(file);
|
|
if ((sptr = Is_special(lev)) != 0)
|
|
Sprintf(dptr, ".%c", sptr->boneid);
|
|
else
|
|
Sprintf(dptr, ".%d", lev->dlevel);
|
|
#ifdef VMS
|
|
Strcat(dptr, ";1");
|
|
#endif
|
|
return(dptr-2);
|
|
}
|
|
|
|
/* set up temporary file name for writing bones, to avoid another game's
|
|
* trying to read from an uncompleted bones file. we want an uncontentious
|
|
* name, so use one in the namespace reserved for this game's level files.
|
|
* (we are not reading or writing level files while writing bones files, so
|
|
* the same array may be used instead of copying.)
|
|
*/
|
|
STATIC_OVL char *
|
|
set_bonestemp_name()
|
|
{
|
|
char *tf;
|
|
|
|
tf = rindex(lock, '.');
|
|
if (!tf) tf = eos(lock);
|
|
Sprintf(tf, ".bn");
|
|
#ifdef VMS
|
|
Strcat(tf, ";1");
|
|
#endif
|
|
return lock;
|
|
}
|
|
|
|
int
|
|
create_bonesfile(lev, bonesid)
|
|
d_level *lev;
|
|
char **bonesid;
|
|
{
|
|
const char *file;
|
|
int fd;
|
|
|
|
*bonesid = set_bonesfile_name(bones, lev);
|
|
file = set_bonestemp_name();
|
|
file = fqname(file, BONESPREFIX, 0);
|
|
|
|
#if defined(MICRO) || defined(WIN32)
|
|
/* Use O_TRUNC to force the file to be shortened if it already
|
|
* exists and is currently longer.
|
|
*/
|
|
fd = open(file, O_WRONLY |O_CREAT | O_TRUNC | O_BINARY, FCMASK);
|
|
#else
|
|
# ifdef MAC
|
|
fd = maccreat(file, BONE_TYPE);
|
|
# else
|
|
fd = creat(file, FCMASK);
|
|
# endif
|
|
# if defined(VMS) && !defined(SECURE)
|
|
/*
|
|
Re-protect bones file with world:read+write+execute+delete access.
|
|
umask() doesn't seem very reliable; also, vaxcrtl won't let us set
|
|
delete access without write access, which is what's really wanted.
|
|
Can't simply create it with the desired protection because creat
|
|
ANDs the mask with the user's default protection, which usually
|
|
denies some or all access to world.
|
|
*/
|
|
(void) chmod(file, FCMASK | 007); /* allow other users full access */
|
|
# endif /* VMS && !SECURE */
|
|
#endif /* MICRO || WIN32*/
|
|
|
|
return fd;
|
|
}
|
|
|
|
#ifdef MFLOPPY
|
|
/* remove partial bonesfile in process of creation */
|
|
void
|
|
cancel_bonesfile()
|
|
{
|
|
const char *tempname;
|
|
|
|
tempname = set_bonestemp_name();
|
|
tempname = fqname(tempname, BONESPREFIX, 0);
|
|
(void) unlink(tempname);
|
|
}
|
|
#endif /* MFLOPPY */
|
|
|
|
/* move completed bones file to proper name */
|
|
void
|
|
commit_bonesfile(lev)
|
|
d_level *lev;
|
|
{
|
|
const char *fq_bones, *tempname;
|
|
int ret;
|
|
|
|
(void) set_bonesfile_name(bones, lev);
|
|
fq_bones = fqname(bones, BONESPREFIX, 0);
|
|
tempname = set_bonestemp_name();
|
|
tempname = fqname(tempname, BONESPREFIX, 1);
|
|
|
|
#if (defined(SYSV) && !defined(SVR4)) || defined(GENIX)
|
|
/* old SYSVs don't have rename. Some SVR3's may, but since they
|
|
* also have link/unlink, it doesn't matter. :-)
|
|
*/
|
|
(void) unlink(fq_bones);
|
|
ret = link(tempname, fq_bones);
|
|
ret += unlink(tempname);
|
|
#else
|
|
ret = rename(tempname, fq_bones);
|
|
#endif
|
|
#ifdef WIZARD
|
|
if (wizard && ret != 0)
|
|
pline("couldn't rename %s to %s", tempname, fq_bones);
|
|
#endif
|
|
}
|
|
|
|
|
|
int
|
|
open_bonesfile(lev, bonesid)
|
|
d_level *lev;
|
|
char **bonesid;
|
|
{
|
|
const char *fq_bones;
|
|
int fd;
|
|
|
|
*bonesid = set_bonesfile_name(bones, lev);
|
|
fq_bones = fqname(bones, BONESPREFIX, 0);
|
|
uncompress(fq_bones); /* no effect if nonexistent */
|
|
#ifdef MAC
|
|
fd = macopen(fq_bones, O_RDONLY | O_BINARY, BONE_TYPE);
|
|
#else
|
|
fd = open(fq_bones, O_RDONLY | O_BINARY, 0);
|
|
#endif
|
|
return fd;
|
|
}
|
|
|
|
|
|
int
|
|
delete_bonesfile(lev)
|
|
d_level *lev;
|
|
{
|
|
(void) set_bonesfile_name(bones, lev);
|
|
return !(unlink(fqname(bones, BONESPREFIX, 0)) < 0);
|
|
}
|
|
|
|
|
|
/* assume we're compressing the recently read or created bonesfile, so the
|
|
* file name is already set properly */
|
|
void
|
|
compress_bonesfile()
|
|
{
|
|
compress(fqname(bones, BONESPREFIX, 0));
|
|
}
|
|
|
|
/* ---------- END BONES FILE HANDLING ----------- */
|
|
|
|
|
|
/* ---------- BEGIN SAVE FILE HANDLING ----------- */
|
|
|
|
/* set savefile name in OS-dependent manner from pre-existing plname,
|
|
* avoiding troublesome characters */
|
|
void
|
|
set_savefile_name()
|
|
{
|
|
#ifdef VMS
|
|
Sprintf(SAVEF, "[.save]%d%s", getuid(), plname);
|
|
regularize(SAVEF+7);
|
|
Strcat(SAVEF, ";1");
|
|
#else
|
|
# if defined(MICRO)
|
|
Strcpy(SAVEF, SAVEP);
|
|
# ifdef AMIGA
|
|
strncat(SAVEF, bbs_id, PATHLEN);
|
|
# endif
|
|
{
|
|
int i = strlen(SAVEP);
|
|
# ifdef AMIGA
|
|
/* plname has to share space with SAVEP and ".sav" */
|
|
(void)strncat(SAVEF, plname, FILENAME - i - 4);
|
|
# else
|
|
(void)strncat(SAVEF, plname, 8);
|
|
# endif
|
|
regularize(SAVEF+i);
|
|
}
|
|
Strcat(SAVEF, ".sav");
|
|
# else
|
|
# if defined(WIN32)
|
|
Sprintf(SAVEF,"%s-%s.NetHack-saved-game",get_username(0), plname);
|
|
# else
|
|
Sprintf(SAVEF, "save/%d%s", (int)getuid(), plname);
|
|
regularize(SAVEF+5); /* avoid . or / in name */
|
|
# endif /* WIN32 */
|
|
# endif /* MICRO */
|
|
#endif /* VMS */
|
|
}
|
|
|
|
#ifdef INSURANCE
|
|
void
|
|
save_savefile_name(fd)
|
|
int fd;
|
|
{
|
|
(void) write(fd, (genericptr_t) SAVEF, sizeof(SAVEF));
|
|
}
|
|
#endif
|
|
|
|
|
|
#if defined(WIZARD) && !defined(MICRO)
|
|
/* change pre-existing savefile name to indicate an error savefile */
|
|
void
|
|
set_error_savefile()
|
|
{
|
|
# ifdef VMS
|
|
{
|
|
char *semi_colon = rindex(SAVEF, ';');
|
|
if (semi_colon) *semi_colon = '\0';
|
|
}
|
|
Strcat(SAVEF, ".e;1");
|
|
# else
|
|
# ifdef MAC
|
|
Strcat(SAVEF, "-e");
|
|
# else
|
|
Strcat(SAVEF, ".e");
|
|
# endif
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
|
|
/* create save file, overwriting one if it already exists */
|
|
int
|
|
create_savefile()
|
|
{
|
|
const char *fq_save;
|
|
int fd;
|
|
|
|
fq_save = fqname(SAVEF, SAVEPREFIX, 0);
|
|
#if defined(MICRO) || defined(WIN32)
|
|
fd = open(fq_save, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK);
|
|
#else
|
|
# ifdef MAC
|
|
fd = maccreat(fq_save, SAVE_TYPE);
|
|
# else
|
|
fd = creat(fq_save, FCMASK);
|
|
# endif
|
|
# if defined(VMS) && !defined(SECURE)
|
|
/*
|
|
Make sure the save file is owned by the current process. That's
|
|
the default for non-privileged users, but for priv'd users the
|
|
file will be owned by the directory's owner instead of the user.
|
|
*/
|
|
# ifdef getuid /*(see vmsunix.c)*/
|
|
# undef getuid
|
|
# endif
|
|
(void) chown(fq_save, getuid(), getgid());
|
|
# endif /* VMS && !SECURE */
|
|
#endif /* MICRO */
|
|
|
|
return fd;
|
|
}
|
|
|
|
|
|
/* open savefile for reading */
|
|
int
|
|
open_savefile()
|
|
{
|
|
const char *fq_save;
|
|
int fd;
|
|
|
|
fq_save = fqname(SAVEF, SAVEPREFIX, 0);
|
|
#ifdef MAC
|
|
fd = macopen(fq_save, O_RDONLY | O_BINARY, SAVE_TYPE);
|
|
#else
|
|
fd = open(fq_save, O_RDONLY | O_BINARY, 0);
|
|
#endif
|
|
return fd;
|
|
}
|
|
|
|
|
|
/* delete savefile */
|
|
int
|
|
delete_savefile()
|
|
{
|
|
(void) unlink(fqname(SAVEF, SAVEPREFIX, 0));
|
|
return 0; /* for restore_saved_game() (ex-xxxmain.c) test */
|
|
}
|
|
|
|
|
|
/* try to open up a save file and prepare to restore it */
|
|
int
|
|
restore_saved_game()
|
|
{
|
|
const char *fq_save;
|
|
int fd;
|
|
|
|
set_savefile_name();
|
|
#ifdef MFLOPPY
|
|
if (!saveDiskPrompt(1))
|
|
return -1;
|
|
#endif /* MFLOPPY */
|
|
fq_save = fqname(SAVEF, SAVEPREFIX, 0);
|
|
|
|
uncompress(fq_save);
|
|
if ((fd = open_savefile()) < 0) return fd;
|
|
|
|
if (!uptodate(fd, fq_save)) {
|
|
(void) close(fd), fd = -1;
|
|
(void) delete_savefile();
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
/* ---------- END SAVE FILE HANDLING ----------- */
|
|
|
|
|
|
/* ---------- BEGIN FILE COMPRESSION HANDLING ----------- */
|
|
|
|
#ifdef COMPRESS
|
|
|
|
STATIC_OVL void
|
|
redirect(filename, mode, stream, uncomp)
|
|
const char *filename, *mode;
|
|
FILE *stream;
|
|
boolean uncomp;
|
|
{
|
|
if (freopen(filename, mode, stream) == (FILE *)0) {
|
|
(void) fprintf(stderr, "freopen of %s for %scompress failed\n",
|
|
filename, uncomp ? "un" : "");
|
|
terminate(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* using system() is simpler, but opens up security holes and causes
|
|
* problems on at least Interactive UNIX 3.0.1 (SVR3.2), where any
|
|
* setuid is renounced by /bin/sh, so the files cannot be accessed.
|
|
*
|
|
* cf. child() in unixunix.c.
|
|
*/
|
|
STATIC_OVL void
|
|
docompress_file(filename, uncomp)
|
|
const char *filename;
|
|
boolean uncomp;
|
|
{
|
|
char cfn[80];
|
|
FILE *cf;
|
|
const char *args[10];
|
|
# ifdef COMPRESS_OPTIONS
|
|
char opts[80];
|
|
# endif
|
|
int i = 0;
|
|
int f;
|
|
# ifdef TTY_GRAPHICS
|
|
boolean istty = !strncmpi(windowprocs.name, "tty", 3);
|
|
# endif
|
|
|
|
Strcpy(cfn, filename);
|
|
# ifdef COMPRESS_EXTENSION
|
|
Strcat(cfn, COMPRESS_EXTENSION);
|
|
# endif
|
|
/* when compressing, we know the file exists */
|
|
if (uncomp) {
|
|
if ((cf = fopen(cfn, RDBMODE)) == (FILE *)0)
|
|
return;
|
|
(void) fclose(cf);
|
|
}
|
|
|
|
args[0] = COMPRESS;
|
|
if (uncomp) args[++i] = "-d"; /* uncompress */
|
|
# ifdef COMPRESS_OPTIONS
|
|
{
|
|
/* we can't guarantee there's only one additional option, sigh */
|
|
char *opt;
|
|
boolean inword = FALSE;
|
|
|
|
Strcpy(opts, COMPRESS_OPTIONS);
|
|
opt = opts;
|
|
while (*opt) {
|
|
if ((*opt == ' ') || (*opt == '\t')) {
|
|
if (inword) {
|
|
*opt = '\0';
|
|
inword = FALSE;
|
|
}
|
|
} else if (!inword) {
|
|
args[++i] = opt;
|
|
inword = TRUE;
|
|
}
|
|
opt++;
|
|
}
|
|
}
|
|
# endif
|
|
args[++i] = (char *)0;
|
|
|
|
f = fork();
|
|
# ifdef TTY_GRAPHICS
|
|
/* If we don't do this and we are right after a y/n question *and*
|
|
* there is an error message from the compression, the 'y' or 'n' can
|
|
* end up being displayed after the error message.
|
|
*/
|
|
if (istty)
|
|
mark_synch();
|
|
# endif
|
|
if (f == 0) { /* child */
|
|
# ifdef TTY_GRAPHICS
|
|
/* any error messages from the compression must come out after
|
|
* the first line, because the more() to let the user read
|
|
* them will have to clear the first line. This should be
|
|
* invisible if there are no error messages.
|
|
*/
|
|
if (istty)
|
|
raw_print("");
|
|
# endif
|
|
/* run compressor without privileges, in case other programs
|
|
* have surprises along the line of gzip once taking filenames
|
|
* in GZIP.
|
|
*/
|
|
/* assume all compressors will compress stdin to stdout
|
|
* without explicit filenames. this is true of at least
|
|
* compress and gzip, those mentioned in config.h.
|
|
*/
|
|
if (uncomp) {
|
|
redirect(cfn, RDBMODE, stdin, uncomp);
|
|
redirect(filename, WRBMODE, stdout, uncomp);
|
|
} else {
|
|
redirect(filename, RDBMODE, stdin, uncomp);
|
|
redirect(cfn, WRBMODE, stdout, uncomp);
|
|
}
|
|
(void) setgid(getgid());
|
|
(void) setuid(getuid());
|
|
(void) execv(args[0], (char *const *) args);
|
|
perror((char *)0);
|
|
(void) fprintf(stderr, "Exec to %scompress %s failed.\n",
|
|
uncomp ? "un" : "", filename);
|
|
terminate(EXIT_FAILURE);
|
|
} else if (f == -1) {
|
|
perror((char *)0);
|
|
pline("Fork to %scompress %s failed.",
|
|
uncomp ? "un" : "", filename);
|
|
return;
|
|
}
|
|
(void) signal(SIGINT, SIG_IGN);
|
|
(void) signal(SIGQUIT, SIG_IGN);
|
|
(void) wait((int *)&i);
|
|
(void) signal(SIGINT, (SIG_RET_TYPE) done1);
|
|
# ifdef WIZARD
|
|
if (wizard) (void) signal(SIGQUIT, SIG_DFL);
|
|
# endif
|
|
if (i == 0) {
|
|
/* (un)compress succeeded: remove file left behind */
|
|
if (uncomp)
|
|
(void) unlink(cfn);
|
|
else
|
|
(void) unlink(filename);
|
|
} else {
|
|
/* (un)compress failed; remove the new, bad file */
|
|
if (uncomp) {
|
|
raw_printf("Unable to uncompress %s", filename);
|
|
(void) unlink(filename);
|
|
} else {
|
|
/* no message needed for compress case; life will go on */
|
|
(void) unlink(cfn);
|
|
}
|
|
#ifdef TTY_GRAPHICS
|
|
/* Give them a chance to read any error messages from the
|
|
* compression--these would go to stdout or stderr and would get
|
|
* overwritten only in tty mode. It's still ugly, since the
|
|
* messages are being written on top of the screen, but at least
|
|
* the user can read them.
|
|
*/
|
|
if (istty)
|
|
{
|
|
clear_nhwindow(WIN_MESSAGE);
|
|
more();
|
|
/* No way to know if this is feasible */
|
|
/* doredraw(); */
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#endif /* COMPRESS */
|
|
|
|
/* compress file */
|
|
void
|
|
compress(filename)
|
|
const char *filename;
|
|
{
|
|
#ifndef COMPRESS
|
|
#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__)
|
|
# pragma unused(filename)
|
|
#endif
|
|
#else
|
|
docompress_file(filename, FALSE);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* uncompress file if it exists */
|
|
void
|
|
uncompress(filename)
|
|
const char *filename;
|
|
{
|
|
#ifndef COMPRESS
|
|
#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__)
|
|
# pragma unused(filename)
|
|
#endif
|
|
#else
|
|
docompress_file(filename, TRUE);
|
|
#endif
|
|
}
|
|
|
|
/* ---------- END FILE COMPRESSION HANDLING ----------- */
|
|
|
|
|
|
/* ---------- BEGIN FILE LOCKING HANDLING ----------- */
|
|
|
|
static int nesting = 0;
|
|
|
|
#ifdef NO_FILE_LINKS /* implies UNIX */
|
|
static int lockfd; /* for lock_file() to pass to unlock_file() */
|
|
#endif
|
|
|
|
#define HUP if (!program_state.done_hup)
|
|
|
|
STATIC_OVL char *
|
|
make_lockname(filename, lockname)
|
|
const char *filename;
|
|
char *lockname;
|
|
{
|
|
#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__)
|
|
# pragma unused(filename,lockname)
|
|
return (char*)0;
|
|
#else
|
|
# if defined(UNIX) || defined(VMS) || defined(AMIGA) || defined(WIN32) || defined(MSDOS)
|
|
# ifdef NO_FILE_LINKS
|
|
Strcpy(lockname, LOCKDIR);
|
|
Strcat(lockname, "/");
|
|
Strcat(lockname, filename);
|
|
# else
|
|
Strcpy(lockname, filename);
|
|
# endif
|
|
# ifdef VMS
|
|
{
|
|
char *semi_colon = rindex(lockname, ';');
|
|
if (semi_colon) *semi_colon = '\0';
|
|
}
|
|
Strcat(lockname, ".lock;1");
|
|
# else
|
|
Strcat(lockname, "_lock");
|
|
# endif
|
|
return lockname;
|
|
# else
|
|
lockname[0] = '\0';
|
|
return (char*)0;
|
|
# endif /* UNIX || VMS || AMIGA || WIN32 || MSDOS */
|
|
#endif
|
|
}
|
|
|
|
|
|
/* lock a file */
|
|
boolean
|
|
lock_file(filename, whichprefix, retryct)
|
|
const char *filename;
|
|
int whichprefix;
|
|
int retryct;
|
|
{
|
|
#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__)
|
|
# pragma unused(filename, retryct)
|
|
#endif
|
|
char locknambuf[BUFSZ];
|
|
const char *lockname;
|
|
|
|
nesting++;
|
|
if (nesting > 1) {
|
|
impossible("TRIED TO NEST LOCKS");
|
|
return TRUE;
|
|
}
|
|
|
|
lockname = make_lockname(filename, locknambuf);
|
|
filename = fqname(filename, whichprefix, 0);
|
|
#ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
|
|
lockname = fqname(lockname, LOCKPREFIX, 2);
|
|
#endif
|
|
|
|
#if defined(UNIX) || defined(VMS)
|
|
# ifdef NO_FILE_LINKS
|
|
while ((lockfd = open(lockname, O_RDWR|O_CREAT|O_EXCL, 0666)) == -1) {
|
|
# else
|
|
while (link(filename, lockname) == -1) {
|
|
# endif
|
|
register int errnosv = errno;
|
|
|
|
switch (errnosv) { /* George Barbanis */
|
|
case EEXIST:
|
|
if (retryct--) {
|
|
HUP raw_printf(
|
|
"Waiting for access to %s. (%d retries left).",
|
|
filename, retryct);
|
|
# if defined(SYSV) || defined(ULTRIX) || defined(VMS)
|
|
(void)
|
|
# endif
|
|
sleep(1);
|
|
} else {
|
|
HUP (void) raw_print("I give up. Sorry.");
|
|
HUP raw_printf("Perhaps there is an old %s around?",
|
|
lockname);
|
|
nesting--;
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
case ENOENT:
|
|
HUP raw_printf("Can't find file %s to lock!", filename);
|
|
nesting--;
|
|
return FALSE;
|
|
case EACCES:
|
|
HUP raw_printf("No write permission to lock %s!", filename);
|
|
nesting--;
|
|
return FALSE;
|
|
# ifdef VMS /* c__translate(vmsfiles.c) */
|
|
case EPERM:
|
|
/* could be misleading, but usually right */
|
|
HUP raw_printf("Can't lock %s due to directory protection.",
|
|
filename);
|
|
nesting--;
|
|
return FALSE;
|
|
# endif
|
|
default:
|
|
HUP perror(lockname);
|
|
HUP raw_printf(
|
|
"Cannot lock %s for unknown reason (%d).",
|
|
filename, errnosv);
|
|
nesting--;
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
#endif /* UNIX || VMS */
|
|
|
|
#if defined(AMIGA) || defined(WIN32) || defined(MSDOS)
|
|
lockptr = 0;
|
|
while (retryct-- && !lockptr) {
|
|
# ifdef AMIGA
|
|
(void)DeleteFile(lockname); /* in case dead process was here first */
|
|
lockptr = Open(lockname,MODE_NEWFILE);
|
|
# else
|
|
lockptr = open(lockname, O_RDWR|O_CREAT|O_EXCL, S_IWRITE);
|
|
# endif
|
|
if (!lockptr) {
|
|
raw_printf("Waiting for access to %s. (%d retries left).",
|
|
filename, retryct);
|
|
Delay(50);
|
|
}
|
|
}
|
|
if (!retryct) {
|
|
raw_printf("I give up. Sorry.");
|
|
nesting--;
|
|
return FALSE;
|
|
}
|
|
#endif /* AMIGA || WIN32 || MSDOS */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifdef VMS /* for unlock_file, use the unlink() routine in vmsunix.c */
|
|
# ifdef unlink
|
|
# undef unlink
|
|
# endif
|
|
# define unlink(foo) vms_unlink(foo)
|
|
#endif
|
|
|
|
/* unlock file, which must be currently locked by lock_file */
|
|
void
|
|
unlock_file(filename)
|
|
const char *filename;
|
|
#if defined(macintosh) && (defined(__SC__) || defined(__MRC__))
|
|
# pragma unused(filename)
|
|
#endif
|
|
{
|
|
char locknambuf[BUFSZ];
|
|
const char *lockname;
|
|
|
|
if (nesting == 1) {
|
|
lockname = make_lockname(filename, locknambuf);
|
|
#ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */
|
|
lockname = fqname(lockname, LOCKPREFIX, 2);
|
|
#endif
|
|
|
|
#if defined(UNIX) || defined(VMS)
|
|
if (unlink(lockname) < 0)
|
|
HUP raw_printf("Can't unlink %s.", lockname);
|
|
# ifdef NO_FILE_LINKS
|
|
(void) close(lockfd);
|
|
# endif
|
|
|
|
#endif /* UNIX || VMS */
|
|
|
|
#if defined(AMIGA) || defined(WIN32) || defined(MSDOS)
|
|
if (lockptr) Close(lockptr);
|
|
DeleteFile(lockname);
|
|
lockptr = 0;
|
|
#endif /* AMIGA || WIN32 || MSDOS */
|
|
}
|
|
|
|
nesting--;
|
|
}
|
|
|
|
/* ---------- END FILE LOCKING HANDLING ----------- */
|
|
|
|
|
|
/* ---------- BEGIN CONFIG FILE HANDLING ----------- */
|
|
|
|
const char *configfile =
|
|
#ifdef UNIX
|
|
".nethackrc";
|
|
#else
|
|
# if defined(MAC) || defined(__BEOS__)
|
|
"NetHack Defaults";
|
|
# else
|
|
# if defined(MSDOS) || defined(WIN32)
|
|
"defaults.nh";
|
|
# else
|
|
"NetHack.cnf";
|
|
# endif
|
|
# endif
|
|
#endif
|
|
|
|
|
|
#ifdef MSDOS
|
|
/* conflict with speed-dial under windows
|
|
* for XXX.cnf file so support of NetHack.cnf
|
|
* is for backward compatibility only.
|
|
* Preferred name (and first tried) is now defaults.nh but
|
|
* the game will try the old name if there
|
|
* is no defaults.nh.
|
|
*/
|
|
const char *backward_compat_configfile = "nethack.cnf";
|
|
#endif
|
|
|
|
#ifndef MFLOPPY
|
|
#define fopenp fopen
|
|
#endif
|
|
|
|
STATIC_OVL FILE *
|
|
fopen_config_file(filename)
|
|
const char *filename;
|
|
{
|
|
FILE *fp;
|
|
#if defined(UNIX) || defined(VMS)
|
|
char tmp_config[BUFSZ];
|
|
char *envp;
|
|
#endif
|
|
|
|
/* "filename" is an environment variable, so it should hang around */
|
|
/* if set, it is expected to be a full path name (if relevant) */
|
|
if (filename) {
|
|
#ifdef UNIX
|
|
if (access(filename, 4) == -1) {
|
|
/* 4 is R_OK on newer systems */
|
|
/* nasty sneaky attempt to read file through
|
|
* NetHack's setuid permissions -- this is the only
|
|
* place a file name may be wholly under the player's
|
|
* control
|
|
*/
|
|
raw_printf("Access to %s denied (%d).",
|
|
filename, errno);
|
|
wait_synch();
|
|
/* fall through to standard names */
|
|
} else
|
|
#endif
|
|
if ((fp = fopenp(filename, "r")) != (FILE *)0) {
|
|
configfile = filename;
|
|
return(fp);
|
|
#if defined(UNIX) || defined(VMS)
|
|
} else {
|
|
/* access() above probably caught most problems for UNIX */
|
|
raw_printf("Couldn't open requested config file %s (%d).",
|
|
filename, errno);
|
|
wait_synch();
|
|
/* fall through to standard names */
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
|
|
if ((fp = fopenp(fqname(configfile, CONFIGPREFIX, 0), "r"))
|
|
!= (FILE *)0)
|
|
return(fp);
|
|
# ifdef MSDOS
|
|
else if ((fp = fopenp(fqname(backward_compat_configfile,
|
|
CONFIGPREFIX, 0), "r")) != (FILE *)0)
|
|
return(fp);
|
|
# endif
|
|
#else
|
|
/* constructed full path names don't need fqname() */
|
|
# ifdef VMS
|
|
if ((fp = fopenp(fqname("nethackini", CONFIGPREFIX, 0), "r"))
|
|
!= (FILE *)0) {
|
|
configfile = "nethackini";
|
|
return(fp);
|
|
}
|
|
if ((fp = fopenp("sys$login:nethack.ini", "r")) != (FILE *)0) {
|
|
configfile = "nethack.ini";
|
|
return(fp);
|
|
}
|
|
|
|
envp = nh_getenv("HOME");
|
|
if (!envp)
|
|
Strcpy(tmp_config, "NetHack.cnf");
|
|
else
|
|
Sprintf(tmp_config, "%s%s", envp, "NetHack.cnf");
|
|
if ((fp = fopenp(tmp_config, "r")) != (FILE *)0)
|
|
return(fp);
|
|
# else /* should be only UNIX left */
|
|
envp = nh_getenv("HOME");
|
|
if (!envp)
|
|
Strcpy(tmp_config, ".nethackrc");
|
|
else
|
|
Sprintf(tmp_config, "%s/%s", envp, ".nethackrc");
|
|
if ((fp = fopenp(tmp_config, "r")) != (FILE *)0)
|
|
return(fp);
|
|
else if (errno != ENOENT) {
|
|
/* e.g., problems when setuid NetHack can't search home
|
|
* directory restricted to user */
|
|
raw_printf("Couldn't open default config file %s (%d).",
|
|
tmp_config, errno);
|
|
wait_synch();
|
|
}
|
|
# endif
|
|
#endif
|
|
return (FILE *)0;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Retrieve a list of integers from a file into a uchar array.
|
|
*
|
|
* NOTE: This routine is unable to read a value of 0.
|
|
*/
|
|
STATIC_OVL int
|
|
get_uchars(fp, buf, bufp, list, size, name)
|
|
FILE *fp; /* input file pointer */
|
|
char *buf; /* read buffer, must be of size BUFSZ */
|
|
char *bufp; /* current pointer */
|
|
uchar *list; /* return list */
|
|
int size; /* return list size */
|
|
const char *name; /* name of option for error message */
|
|
{
|
|
unsigned int num = 0;
|
|
int count = 0;
|
|
|
|
while (1) {
|
|
switch(*bufp) {
|
|
case ' ': case '\0':
|
|
case '\t': case '\n':
|
|
if (num) {
|
|
list[count++] = num;
|
|
num = 0;
|
|
}
|
|
if (count == size || !*bufp) return count;
|
|
bufp++;
|
|
break;
|
|
|
|
case '0': case '1': case '2': case '3':
|
|
case '4': case '5': case '6': case '7':
|
|
case '8': case '9':
|
|
num = num*10 + (*bufp-'0');
|
|
bufp++;
|
|
break;
|
|
|
|
case '\\':
|
|
if (fp == (FILE *)0)
|
|
goto gi_error;
|
|
do {
|
|
if (!fgets(buf, BUFSZ, fp)) goto gi_error;
|
|
} while (buf[0] == '#');
|
|
bufp = buf;
|
|
break;
|
|
|
|
default:
|
|
gi_error:
|
|
raw_printf("Syntax error in %s", name);
|
|
wait_synch();
|
|
return count;
|
|
}
|
|
}
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
STATIC_OVL void
|
|
adjust_prefix(bufp, prefixid)
|
|
char *bufp;
|
|
int prefixid;
|
|
{
|
|
char *ptr;
|
|
|
|
if (!bufp) return;
|
|
/* Backward compatibility, ignore trailing ;n */
|
|
if ((ptr = index(bufp, ';')) != 0) *ptr = '\0';
|
|
if (strlen(bufp) > 0) {
|
|
fqn_prefix[prefixid] = (char *)alloc(strlen(bufp)+2);
|
|
Strcpy(fqn_prefix[prefixid], bufp);
|
|
append_slash(fqn_prefix[prefixid]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#define match_varname(INP,NAM,LEN) match_optname(INP, NAM, LEN, TRUE)
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
parse_config_line(fp, buf, tmp_ramdisk, tmp_levels)
|
|
FILE *fp;
|
|
char *buf;
|
|
char *tmp_ramdisk;
|
|
char *tmp_levels;
|
|
{
|
|
#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__)
|
|
# pragma unused(tmp_ramdisk,tmp_levels)
|
|
#endif
|
|
char *bufp, *altp;
|
|
uchar translate[MAXPCHARS];
|
|
int len;
|
|
|
|
if (*buf == '#')
|
|
return 1;
|
|
|
|
/* remove trailing whitespace */
|
|
bufp = eos(buf);
|
|
while (--bufp > buf && isspace(*bufp))
|
|
continue;
|
|
|
|
if (bufp <= buf)
|
|
return 1; /* skip all-blank lines */
|
|
else
|
|
*(bufp + 1) = '\0'; /* terminate line */
|
|
|
|
/* find the '=' or ':' */
|
|
bufp = index(buf, '=');
|
|
altp = index(buf, ':');
|
|
if (!bufp || (altp && altp < bufp)) bufp = altp;
|
|
if (!bufp) return 0;
|
|
|
|
/* skip whitespace between '=' and value */
|
|
do { ++bufp; } while (isspace(*bufp));
|
|
|
|
/* Go through possible variables */
|
|
/* some of these (at least LEVELS and SAVE) should now set the
|
|
* appropriate fqn_prefix[] rather than specialized variables
|
|
*/
|
|
if (match_varname(buf, "OPTIONS", 4)) {
|
|
parseoptions(bufp, TRUE, TRUE);
|
|
if (plname[0]) /* If a name was given */
|
|
plnamesuffix(); /* set the character class */
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
} else if (match_varname(buf, "HACKDIR", 4)) {
|
|
adjust_prefix(bufp, HACKPREFIX);
|
|
} else if (match_varname(buf, "LEVELDIR", 4) ||
|
|
match_varname(buf, "LEVELS", 4)) {
|
|
adjust_prefix(bufp, LEVELPREFIX);
|
|
} else if (match_varname(buf, "SAVE", 4)) {
|
|
adjust_prefix(bufp, SAVEPREFIX);
|
|
} else if (match_varname(buf, "BONESDIR", 5)) {
|
|
adjust_prefix(bufp, BONESPREFIX);
|
|
} else if (match_varname(buf, "DATADIR", 4)) {
|
|
adjust_prefix(bufp, DATAPREFIX);
|
|
} else if (match_varname(buf, "SCOREDIR", 4)) {
|
|
adjust_prefix(bufp, SCOREPREFIX);
|
|
} else if (match_varname(buf, "LOCKDIR", 4)) {
|
|
adjust_prefix(bufp, LOCKPREFIX);
|
|
} else if (match_varname(buf, "CONFIGDIR", 4)) {
|
|
adjust_prefix(bufp, CONFIGPREFIX);
|
|
#else /*NOCWD_ASSUMPTIONS*/
|
|
# ifdef MICRO
|
|
} else if (match_varname(buf, "HACKDIR", 4)) {
|
|
(void) strncpy(hackdir, bufp, PATHLEN-1);
|
|
# ifdef MFLOPPY
|
|
} else if (match_varname(buf, "RAMDISK", 3)) {
|
|
/* The following ifdef is NOT in the wrong
|
|
* place. For now, we accept and silently
|
|
* ignore RAMDISK */
|
|
# ifndef AMIGA
|
|
(void) strncpy(tmp_ramdisk, bufp, PATHLEN-1);
|
|
# endif
|
|
# endif
|
|
} else if (match_varname(buf, "LEVELS", 4)) {
|
|
(void) strncpy(tmp_levels, bufp, PATHLEN-1);
|
|
|
|
} else if (match_varname(buf, "SAVE", 4)) {
|
|
# ifdef MFLOPPY
|
|
extern int saveprompt;
|
|
# endif
|
|
char *ptr;
|
|
if ((ptr = index(bufp, ';')) != 0) {
|
|
*ptr = '\0';
|
|
# ifdef MFLOPPY
|
|
if (*(ptr+1) == 'n' || *(ptr+1) == 'N') {
|
|
saveprompt = FALSE;
|
|
}
|
|
# endif
|
|
}
|
|
# ifdef MFLOPPY
|
|
else
|
|
saveprompt = flags.asksavedisk;
|
|
# endif
|
|
|
|
(void) strncpy(SAVEP, bufp, SAVESIZE-1);
|
|
append_slash(SAVEP);
|
|
# endif /* MICRO */
|
|
#endif /*NOCWD_ASSUMPTIONS*/
|
|
|
|
} else if (match_varname(buf, "NAME", 4)) {
|
|
(void) strncpy(plname, bufp, PL_NSIZ-1);
|
|
plnamesuffix();
|
|
} else if (match_varname(buf, "ROLE", 4) ||
|
|
match_varname(buf, "CHARACTER", 4)) {
|
|
if ((len = str2role(bufp)) >= 0)
|
|
flags.initrole = len;
|
|
} else if (match_varname(buf, "DOGNAME", 3)) {
|
|
(void) strncpy(dogname, bufp, PL_PSIZ-1);
|
|
} else if (match_varname(buf, "CATNAME", 3)) {
|
|
(void) strncpy(catname, bufp, PL_PSIZ-1);
|
|
|
|
} else if (match_varname(buf, "BOULDER", 3)) {
|
|
(void) get_uchars(fp, buf, bufp, &iflags.bouldersym, 1, "BOULDER");
|
|
} else if (match_varname(buf, "GRAPHICS", 4)) {
|
|
len = get_uchars(fp, buf, bufp, translate, MAXPCHARS, "GRAPHICS");
|
|
assign_graphics(translate, len, MAXPCHARS, 0);
|
|
} else if (match_varname(buf, "DUNGEON", 4)) {
|
|
len = get_uchars(fp, buf, bufp, translate, MAXDCHARS, "DUNGEON");
|
|
assign_graphics(translate, len, MAXDCHARS, 0);
|
|
} else if (match_varname(buf, "TRAPS", 4)) {
|
|
len = get_uchars(fp, buf, bufp, translate, MAXTCHARS, "TRAPS");
|
|
assign_graphics(translate, len, MAXTCHARS, MAXDCHARS);
|
|
} else if (match_varname(buf, "EFFECTS", 4)) {
|
|
len = get_uchars(fp, buf, bufp, translate, MAXECHARS, "EFFECTS");
|
|
assign_graphics(translate, len, MAXECHARS, MAXDCHARS+MAXTCHARS);
|
|
|
|
} else if (match_varname(buf, "OBJECTS", 3)) {
|
|
/* oc_syms[0] is the RANDOM object, unused */
|
|
(void) get_uchars(fp, buf, bufp, &(oc_syms[1]),
|
|
MAXOCLASSES-1, "OBJECTS");
|
|
} else if (match_varname(buf, "MONSTERS", 3)) {
|
|
/* monsyms[0] is unused */
|
|
(void) get_uchars(fp, buf, bufp, &(monsyms[1]),
|
|
MAXMCLASSES-1, "MONSTERS");
|
|
} else if (match_varname(buf, "WARNINGS", 5)) {
|
|
(void) get_uchars(fp, buf, bufp, translate,
|
|
WARNCOUNT, "WARNINGS");
|
|
assign_warnings(translate);
|
|
#ifdef WIZARD
|
|
} else if (match_varname(buf, "WIZKIT", 6)) {
|
|
(void) strncpy(wizkit, bufp, WIZKIT_MAX-1);
|
|
#endif
|
|
#ifdef AMIGA
|
|
} else if (match_varname(buf, "FONT", 4)) {
|
|
char *t;
|
|
|
|
if( t = strchr( buf+5, ':' ) )
|
|
{
|
|
*t = 0;
|
|
amii_set_text_font( buf+5, atoi( t + 1 ) );
|
|
*t = ':';
|
|
}
|
|
} else if (match_varname(buf, "PATH", 4)) {
|
|
(void) strncpy(PATH, bufp, PATHLEN-1);
|
|
} else if (match_varname(buf, "DEPTH", 5)) {
|
|
extern int amii_numcolors;
|
|
int val = atoi( bufp );
|
|
amii_numcolors = 1L << min( DEPTH, val );
|
|
} else if (match_varname(buf, "DRIPENS", 7)) {
|
|
int i, val;
|
|
char *t;
|
|
for (i = 0, t = strtok(bufp, ",/"); t != (char *)0;
|
|
i < 20 && (t = strtok((char*)0, ",/")), ++i) {
|
|
sscanf(t, "%d", &val );
|
|
flags.amii_dripens[i] = val;
|
|
}
|
|
} else if (match_varname(buf, "SCREENMODE", 10 )) {
|
|
extern long amii_scrnmode;
|
|
if (!stricmp(bufp,"req"))
|
|
amii_scrnmode = 0xffffffff; /* Requester */
|
|
else if( sscanf(bufp, "%x", &amii_scrnmode) != 1 )
|
|
amii_scrnmode = 0;
|
|
} else if (match_varname(buf, "MSGPENS", 7)) {
|
|
extern int amii_msgAPen, amii_msgBPen;
|
|
char *t = strtok(bufp, ",/");
|
|
if( t )
|
|
{
|
|
sscanf(t, "%d", &amii_msgAPen);
|
|
if( t = strtok((char*)0, ",/") )
|
|
sscanf(t, "%d", &amii_msgBPen);
|
|
}
|
|
} else if (match_varname(buf, "TEXTPENS", 8)) {
|
|
extern int amii_textAPen, amii_textBPen;
|
|
char *t = strtok(bufp, ",/");
|
|
if( t )
|
|
{
|
|
sscanf(t, "%d", &amii_textAPen);
|
|
if( t = strtok((char*)0, ",/") )
|
|
sscanf(t, "%d", &amii_textBPen);
|
|
}
|
|
} else if (match_varname(buf, "MENUPENS", 8)) {
|
|
extern int amii_menuAPen, amii_menuBPen;
|
|
char *t = strtok(bufp, ",/");
|
|
if( t )
|
|
{
|
|
sscanf(t, "%d", &amii_menuAPen);
|
|
if( t = strtok((char*)0, ",/") )
|
|
sscanf(t, "%d", &amii_menuBPen);
|
|
}
|
|
} else if (match_varname(buf, "STATUSPENS", 10)) {
|
|
extern int amii_statAPen, amii_statBPen;
|
|
char *t = strtok(bufp, ",/");
|
|
if( t )
|
|
{
|
|
sscanf(t, "%d", &amii_statAPen);
|
|
if( t = strtok((char*)0, ",/") )
|
|
sscanf(t, "%d", &amii_statBPen);
|
|
}
|
|
} else if (match_varname(buf, "OTHERPENS", 9)) {
|
|
extern int amii_otherAPen, amii_otherBPen;
|
|
char *t = strtok(bufp, ",/");
|
|
if( t )
|
|
{
|
|
sscanf(t, "%d", &amii_otherAPen);
|
|
if( t = strtok((char*)0, ",/") )
|
|
sscanf(t, "%d", &amii_otherBPen);
|
|
}
|
|
} else if (match_varname(buf, "PENS", 4)) {
|
|
extern unsigned short amii_init_map[ AMII_MAXCOLORS ];
|
|
int i;
|
|
char *t;
|
|
|
|
for (i = 0, t = strtok(bufp, ",/");
|
|
i < AMII_MAXCOLORS && t != (char *)0;
|
|
t = strtok((char *)0, ",/"), ++i)
|
|
{
|
|
sscanf(t, "%hx", &amii_init_map[i]);
|
|
}
|
|
amii_setpens( amii_numcolors = i );
|
|
} else if (match_varname(buf, "FGPENS", 6)) {
|
|
extern int foreg[ AMII_MAXCOLORS ];
|
|
int i;
|
|
char *t;
|
|
|
|
for (i = 0, t = strtok(bufp, ",/");
|
|
i < AMII_MAXCOLORS && t != (char *)0;
|
|
t = strtok((char *)0, ",/"), ++i)
|
|
{
|
|
sscanf(t, "%d", &foreg[i]);
|
|
}
|
|
} else if (match_varname(buf, "BGPENS", 6)) {
|
|
extern int backg[ AMII_MAXCOLORS ];
|
|
int i;
|
|
char *t;
|
|
|
|
for (i = 0, t = strtok(bufp, ",/");
|
|
i < AMII_MAXCOLORS && t != (char *)0;
|
|
t = strtok((char *)0, ",/"), ++i)
|
|
{
|
|
sscanf(t, "%d", &backg[i]);
|
|
}
|
|
#endif
|
|
#ifdef USER_SOUNDS
|
|
} else if (match_varname(buf, "SOUNDDIR", 8)) {
|
|
sounddir = (char *)strdup(bufp);
|
|
} else if (match_varname(buf, "SOUND", 5)) {
|
|
add_sound_mapping(bufp);
|
|
#endif
|
|
#ifdef QT_GRAPHICS
|
|
} else if (match_varname(buf, "QT_TILEWIDTH", 12)) {
|
|
extern char *qt_tilewidth;
|
|
if (qt_tilewidth == NULL)
|
|
qt_tilewidth=(char *)strdup(bufp);
|
|
} else if (match_varname(buf, "QT_TILEHEIGHT", 13)) {
|
|
extern char *qt_tileheight;
|
|
if (qt_tileheight == NULL)
|
|
qt_tileheight=(char *)strdup(bufp);
|
|
} else if (match_varname(buf, "QT_FONTSIZE", 11)) {
|
|
extern char *qt_fontsize;
|
|
if (qt_fontsize == NULL)
|
|
qt_fontsize=(char *)strdup(bufp);
|
|
} else if (match_varname(buf, "QT_COMPACT", 10)) {
|
|
extern int qt_compact_mode;
|
|
qt_compact_mode = atoi(bufp);
|
|
#endif
|
|
} else
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
#ifdef USER_SOUNDS
|
|
boolean
|
|
can_read_file(filename)
|
|
const char *filename;
|
|
{
|
|
return (access(filename, 4) == 0);
|
|
}
|
|
#endif /* USER_SOUNDS */
|
|
|
|
void
|
|
read_config_file(filename)
|
|
const char *filename;
|
|
{
|
|
#define tmp_levels (char *)0
|
|
#define tmp_ramdisk (char *)0
|
|
|
|
#if defined(MICRO) || defined(WIN32)
|
|
#undef tmp_levels
|
|
char tmp_levels[PATHLEN];
|
|
# ifdef MFLOPPY
|
|
# ifndef AMIGA
|
|
#undef tmp_ramdisk
|
|
char tmp_ramdisk[PATHLEN];
|
|
# endif
|
|
# endif
|
|
#endif
|
|
char buf[4*BUFSZ];
|
|
FILE *fp;
|
|
|
|
if (!(fp = fopen_config_file(filename))) return;
|
|
|
|
#if defined(MICRO) || defined(WIN32)
|
|
# ifdef MFLOPPY
|
|
# ifndef AMIGA
|
|
tmp_ramdisk[0] = 0;
|
|
# endif
|
|
# endif
|
|
tmp_levels[0] = 0;
|
|
#endif
|
|
/* begin detection of duplicate configfile options */
|
|
set_duplicate_opt_detection(1);
|
|
|
|
while (fgets(buf, 4*BUFSZ, fp)) {
|
|
if (!parse_config_line(fp, buf, tmp_ramdisk, tmp_levels)) {
|
|
raw_printf("Bad option line: \"%.50s\"", buf);
|
|
wait_synch();
|
|
}
|
|
}
|
|
(void) fclose(fp);
|
|
|
|
/* turn off detection of duplicate configfile options */
|
|
set_duplicate_opt_detection(0);
|
|
|
|
#if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS)
|
|
/* should be superseded by fqn_prefix[] */
|
|
# ifdef MFLOPPY
|
|
Strcpy(permbones, tmp_levels);
|
|
# ifndef AMIGA
|
|
if (tmp_ramdisk[0]) {
|
|
Strcpy(levels, tmp_ramdisk);
|
|
if (strcmp(permbones, levels)) /* if not identical */
|
|
ramdisk = TRUE;
|
|
} else
|
|
# endif /* AMIGA */
|
|
Strcpy(levels, tmp_levels);
|
|
|
|
Strcpy(bones, levels);
|
|
# endif /* MFLOPPY */
|
|
#endif /* MICRO */
|
|
return;
|
|
}
|
|
|
|
#ifdef WIZARD
|
|
STATIC_OVL FILE *
|
|
fopen_wizkit_file()
|
|
{
|
|
FILE *fp;
|
|
#if defined(VMS) || defined(UNIX)
|
|
char tmp_wizkit[BUFSZ];
|
|
#endif
|
|
char *envp;
|
|
|
|
envp = nh_getenv("WIZKIT");
|
|
if (envp && *envp) (void) strncpy(wizkit, envp, WIZKIT_MAX - 1);
|
|
if (!wizkit[0]) return (FILE *)0;
|
|
|
|
#ifdef UNIX
|
|
if (access(wizkit, 4) == -1) {
|
|
/* 4 is R_OK on newer systems */
|
|
/* nasty sneaky attempt to read file through
|
|
* NetHack's setuid permissions -- this is a
|
|
* place a file name may be wholly under the player's
|
|
* control
|
|
*/
|
|
raw_printf("Access to %s denied (%d).",
|
|
wizkit, errno);
|
|
wait_synch();
|
|
/* fall through to standard names */
|
|
} else
|
|
#endif
|
|
if ((fp = fopenp(wizkit, "r")) != (FILE *)0) {
|
|
return(fp);
|
|
#if defined(UNIX) || defined(VMS)
|
|
} else {
|
|
/* access() above probably caught most problems for UNIX */
|
|
raw_printf("Couldn't open requested config file %s (%d).",
|
|
wizkit, errno);
|
|
wait_synch();
|
|
#endif
|
|
}
|
|
|
|
#if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
|
|
if ((fp = fopenp(fqname(wizkit, CONFIGPREFIX, 0), "r"))
|
|
!= (FILE *)0)
|
|
return(fp);
|
|
#else
|
|
# ifdef VMS
|
|
envp = nh_getenv("HOME");
|
|
if (envp)
|
|
Sprintf(tmp_wizkit, "%s%s", envp, wizkit);
|
|
else
|
|
Sprintf(tmp_wizkit, "%s%s", "sys$login:", wizkit);
|
|
if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *)0)
|
|
return(fp);
|
|
# else /* should be only UNIX left */
|
|
envp = nh_getenv("HOME");
|
|
if (envp)
|
|
Sprintf(tmp_wizkit, "%s/%s", envp, wizkit);
|
|
else Strcpy(tmp_wizkit, wizkit);
|
|
if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *)0)
|
|
return(fp);
|
|
else if (errno != ENOENT) {
|
|
/* e.g., problems when setuid NetHack can't search home
|
|
* directory restricted to user */
|
|
raw_printf("Couldn't open default wizkit file %s (%d).",
|
|
tmp_wizkit, errno);
|
|
wait_synch();
|
|
}
|
|
# endif
|
|
#endif
|
|
return (FILE *)0;
|
|
}
|
|
|
|
void
|
|
read_wizkit()
|
|
{
|
|
FILE *fp;
|
|
char *ep, buf[BUFSZ];
|
|
struct obj *otmp;
|
|
boolean bad_items = FALSE;
|
|
|
|
if (!wizard || !(fp = fopen_wizkit_file())) return;
|
|
|
|
while (fgets(buf, 4*BUFSZ, fp)) {
|
|
if ((ep = index(buf, '\n'))) *ep = '\0';
|
|
if (buf[0]) {
|
|
otmp = readobjnam(buf, (struct obj *)0, FALSE);
|
|
if (otmp) {
|
|
if (otmp != &zeroobj)
|
|
otmp = addinv(otmp);
|
|
} else {
|
|
raw_printf("Bad wizkit item: \"%.50s\"", buf);
|
|
bad_items = TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (bad_items)
|
|
wait_synch();
|
|
(void) fclose(fp);
|
|
return;
|
|
}
|
|
|
|
#endif /*WIZARD*/
|
|
|
|
/* ---------- END CONFIG FILE HANDLING ----------- */
|
|
|
|
/* ---------- BEGIN SCOREBOARD CREATION ----------- */
|
|
|
|
/* verify that we can write to the scoreboard file; if not, try to create one */
|
|
void
|
|
check_recordfile(dir)
|
|
const char *dir;
|
|
{
|
|
#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__)
|
|
# pragma unused(dir)
|
|
#endif
|
|
const char *fq_record;
|
|
int fd;
|
|
|
|
#if defined(UNIX) || defined(VMS)
|
|
fq_record = fqname(RECORD, SCOREPREFIX, 0);
|
|
fd = open(fq_record, O_RDWR, 0);
|
|
if (fd >= 0) {
|
|
# ifdef VMS /* must be stream-lf to use UPDATE_RECORD_IN_PLACE */
|
|
if (!file_is_stmlf(fd)) {
|
|
raw_printf(
|
|
"Warning: scoreboard file %s is not in stream_lf format",
|
|
fq_record);
|
|
wait_synch();
|
|
}
|
|
# endif
|
|
(void) close(fd); /* RECORD is accessible */
|
|
} else if ((fd = open(fq_record, O_CREAT|O_RDWR, FCMASK)) >= 0) {
|
|
(void) close(fd); /* RECORD newly created */
|
|
# if defined(VMS) && !defined(SECURE)
|
|
/* Re-protect RECORD with world:read+write+execute+delete access. */
|
|
(void) chmod(fq_record, FCMASK | 007);
|
|
# endif /* VMS && !SECURE */
|
|
} else {
|
|
raw_printf("Warning: cannot write scoreboard file %s", fq_record);
|
|
wait_synch();
|
|
}
|
|
#endif /* !UNIX && !VMS */
|
|
#if defined(MICRO) || defined(WIN32)
|
|
char tmp[PATHLEN];
|
|
|
|
# ifdef OS2_CODEVIEW /* explicit path on opening for OS/2 */
|
|
/* how does this work when there isn't an explicit path or fopenp
|
|
* for later access to the file via fopen_datafile? ? */
|
|
(void) strncpy(tmp, dir, PATHLEN - 1);
|
|
tmp[PATHLEN-1] = '\0';
|
|
if ((strlen(tmp) + 1 + strlen(RECORD)) < (PATHLEN - 1)) {
|
|
append_slash(tmp);
|
|
Strcat(tmp, RECORD);
|
|
}
|
|
fq_record = tmp;
|
|
# else
|
|
Strcpy(tmp, RECORD);
|
|
fq_record = fqname(RECORD, SCOREPREFIX, 0);
|
|
# endif
|
|
|
|
if ((fd = open(fq_record, O_RDWR)) < 0) {
|
|
/* try to create empty record */
|
|
# if defined(AZTEC_C) || defined(_DCC) || (defined(__GNUC__) && defined(__AMIGA__))
|
|
/* Aztec doesn't use the third argument */
|
|
/* DICE doesn't like it */
|
|
if ((fd = open(fq_record, O_CREAT|O_RDWR)) < 0) {
|
|
# else
|
|
if ((fd = open(fq_record, O_CREAT|O_RDWR, S_IREAD|S_IWRITE)) < 0) {
|
|
# endif
|
|
raw_printf("Warning: cannot write record %s", tmp);
|
|
wait_synch();
|
|
} else
|
|
(void) close(fd);
|
|
} else /* open succeeded */
|
|
(void) close(fd);
|
|
#else /* MICRO || WIN32*/
|
|
|
|
# ifdef MAC
|
|
/* Create the "record" file, if necessary */
|
|
fq_record = fqname(RECORD, SCOREPREFIX, 0);
|
|
fd = macopen (fq_record, O_RDWR | O_CREAT, TEXT_TYPE);
|
|
if (fd != -1) macclose (fd);
|
|
# endif /* MAC */
|
|
|
|
#endif /* MICRO || WIN32*/
|
|
}
|
|
|
|
/* ---------- END SCOREBOARD CREATION ----------- */
|
|
|
|
/* ---------- BEGIN PANIC/IMPOSSIBLE LOG ----------- */
|
|
|
|
/*ARGSUSED*/
|
|
void
|
|
paniclog(why, s)
|
|
const char* why;
|
|
const char* s;
|
|
{
|
|
#ifdef PANICLOG
|
|
FILE *lfile;
|
|
|
|
lfile = fopen_datafile(PANICLOG, "a", TRUE);
|
|
if (lfile) {
|
|
(void) fprintf(lfile, "%08ld: %s %s\n",
|
|
yyyymmdd((time_t)0L), why, s);
|
|
(void) fclose(lfile);
|
|
}
|
|
#endif /* PANICLOG */
|
|
}
|
|
|
|
/* ---------- END PANIC/IMPOSSIBLE LOG ----------- */
|
|
|
|
/*files.c*/
|