o Add support for zlib compression via ZLIB_COMP in config.h (ZLIB_COMP and COMPRESS are mutually exclusive). o rlecomp and zerocomp are run time options available if RLECOMP and ZEROCOMP are defined, but not turned on by default if either COMPRESS or ZLIB_COMP are defined. o Add information to the save file about internal compression options used when writing the save file, particularly rlecomp and zerocomp support. o Automatically adjust rlecomp and zerocomp (if support compiled in) when reading in an existing savefile that was saved with those options turned on. Still allows writing out of savefile in preferred format. o In order to support zlib and not conflict with compress and uncompress routines there, the NetHack internal functions were changed to nh_uncompress and nh_compress as done in the zlib contribution received in 1999 from <Someone>. I tagged the sources NETHACK_3_5_0_PREZLIB prior to applying these changes.
568 lines
13 KiB
C
568 lines
13 KiB
C
/* SCCS Id: @(#)unixmain.c 3.5 1997/01/22 */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
/* main.c - Unix NetHack */
|
|
|
|
#include "hack.h"
|
|
#include "dlb.h"
|
|
|
|
#include <sys/stat.h>
|
|
#include <signal.h>
|
|
#include <pwd.h>
|
|
#ifndef O_RDONLY
|
|
#include <fcntl.h>
|
|
#endif
|
|
|
|
#if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX)
|
|
# if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__))
|
|
# if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX)
|
|
extern struct passwd *FDECL(getpwuid,(uid_t));
|
|
# else
|
|
extern struct passwd *FDECL(getpwuid,(int));
|
|
# endif
|
|
# endif
|
|
#endif
|
|
extern struct passwd *FDECL(getpwnam,(const char *));
|
|
#ifdef CHDIR
|
|
static void FDECL(chdirx, (const char *,BOOLEAN_P));
|
|
#endif /* CHDIR */
|
|
static boolean NDECL(whoami);
|
|
static void FDECL(process_options, (int, char **));
|
|
|
|
#ifdef _M_UNIX
|
|
extern void NDECL(check_sco_console);
|
|
extern void NDECL(init_sco_cons);
|
|
#endif
|
|
#ifdef __linux__
|
|
extern void NDECL(check_linux_console);
|
|
extern void NDECL(init_linux_cons);
|
|
#endif
|
|
|
|
static void NDECL(wd_message);
|
|
#ifdef WIZARD
|
|
static boolean wiz_error_flag = FALSE;
|
|
#endif
|
|
|
|
int
|
|
main(argc,argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
register int fd;
|
|
#ifdef CHDIR
|
|
register char *dir;
|
|
#endif
|
|
boolean exact_username;
|
|
|
|
#if defined(__APPLE__)
|
|
/* special hack to change working directory to a resource fork when
|
|
running from finder --sam */
|
|
#define MAC_PATH_VALUE ".app/Contents/MacOS/"
|
|
char mac_cwd[1024], *mac_exe = argv[0], *mac_tmp;
|
|
int arg0_len = strlen(mac_exe), mac_tmp_len, mac_lhs_len=0;
|
|
getcwd(mac_cwd, 1024);
|
|
if(mac_exe[0] == '/' && !strcmp(mac_cwd, "/")) {
|
|
if((mac_exe = strrchr(mac_exe, '/')))
|
|
mac_exe++;
|
|
else
|
|
mac_exe = argv[0];
|
|
mac_tmp_len = (strlen(mac_exe) * 2) + strlen(MAC_PATH_VALUE);
|
|
if(mac_tmp_len <= arg0_len) {
|
|
mac_tmp = malloc(mac_tmp_len + 1);
|
|
sprintf(mac_tmp, "%s%s%s", mac_exe, MAC_PATH_VALUE, mac_exe);
|
|
if(!strcmp(argv[0] + (arg0_len - mac_tmp_len), mac_tmp)) {
|
|
mac_lhs_len = (arg0_len - mac_tmp_len) + strlen(mac_exe) + 5;
|
|
if(mac_lhs_len > mac_tmp_len - 1)
|
|
mac_tmp = realloc(mac_tmp, mac_lhs_len);
|
|
strncpy(mac_tmp, argv[0], mac_lhs_len);
|
|
mac_tmp[mac_lhs_len] = '\0';
|
|
chdir(mac_tmp);
|
|
}
|
|
free(mac_tmp);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
hname = argv[0];
|
|
hackpid = getpid();
|
|
(void) umask(0777 & ~FCMASK);
|
|
|
|
choose_windows(DEFAULT_WINDOW_SYS);
|
|
|
|
#ifdef CHDIR /* otherwise no chdir() */
|
|
/*
|
|
* See if we must change directory to the playground.
|
|
* (Perhaps hack runs suid and playground is inaccessible
|
|
* for the player.)
|
|
* The environment variable HACKDIR is overridden by a
|
|
* -d command line option (must be the first option given)
|
|
*/
|
|
dir = nh_getenv("NETHACKDIR");
|
|
if (!dir) dir = nh_getenv("HACKDIR");
|
|
#endif
|
|
if(argc > 1) {
|
|
#ifdef CHDIR
|
|
if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') {
|
|
/* avoid matching "-dec" for DECgraphics; since the man page
|
|
* says -d directory, hope nobody's using -desomething_else
|
|
*/
|
|
argc--;
|
|
argv++;
|
|
dir = argv[0]+2;
|
|
if(*dir == '=' || *dir == ':') dir++;
|
|
if(!*dir && argc > 1) {
|
|
argc--;
|
|
argv++;
|
|
dir = argv[0];
|
|
}
|
|
if(!*dir)
|
|
error("Flag -d must be followed by a directory name.");
|
|
}
|
|
if (argc > 1)
|
|
#endif /* CHDIR */
|
|
|
|
/*
|
|
* Now we know the directory containing 'record' and
|
|
* may do a prscore(). Exclude `-style' - it's a Qt option.
|
|
*/
|
|
if (!strncmp(argv[1], "-s", 2) && strncmp(argv[1], "-style", 6)) {
|
|
#ifdef CHDIR
|
|
chdirx(dir,0);
|
|
#endif
|
|
prscore(argc, argv);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Change directories before we initialize the window system so
|
|
* we can find the tile file.
|
|
*/
|
|
#ifdef CHDIR
|
|
chdirx(dir,1);
|
|
#endif
|
|
|
|
#ifdef _M_UNIX
|
|
check_sco_console();
|
|
#endif
|
|
#ifdef __linux__
|
|
check_linux_console();
|
|
#endif
|
|
initoptions();
|
|
exact_username = whoami();
|
|
|
|
/*
|
|
* It seems you really want to play.
|
|
*/
|
|
u.uhp = 1; /* prevent RIP on early quits */
|
|
#ifdef SA_RESTART
|
|
/* don't want reads to restart. If SA_RESTART is defined, we know
|
|
* sigaction exists and can be used to ensure reads won't restart.
|
|
* If it's not defined, assume reads do not restart. If reads restart
|
|
* and a signal occurs, the game won't do anything until the read
|
|
* succeeds (or the stream returns EOF, which might not happen if
|
|
* reading from, say, a window manager). */
|
|
{
|
|
struct sigaction sact;
|
|
|
|
(void) memset((char*) &sact, 0, sizeof(struct sigaction));
|
|
sact.sa_handler = (SIG_RET_TYPE)hangup;
|
|
(void) sigaction(SIGHUP, &sact, (struct sigaction*)0);
|
|
#ifdef SIGXCPU
|
|
(void) sigaction(SIGXCPU, &sact, (struct sigaction*)0);
|
|
#endif
|
|
}
|
|
#else
|
|
(void) signal(SIGHUP, (SIG_RET_TYPE) hangup);
|
|
#ifdef SIGXCPU
|
|
(void) signal(SIGXCPU, (SIG_RET_TYPE) hangup);
|
|
#endif
|
|
#endif
|
|
|
|
process_options(argc, argv); /* command line options */
|
|
init_nhwindows(&argc, argv); /* now we can set up window system */
|
|
#ifdef _M_UNIX
|
|
init_sco_cons();
|
|
#endif
|
|
#ifdef __linux__
|
|
init_linux_cons();
|
|
#endif
|
|
|
|
#ifdef DEF_PAGER
|
|
if(!(catmore = nh_getenv("HACKPAGER")) && !(catmore = nh_getenv("PAGER")))
|
|
catmore = DEF_PAGER;
|
|
#endif
|
|
#ifdef MAIL
|
|
getmailstatus();
|
|
#endif
|
|
#ifdef WIZARD
|
|
if (wizard)
|
|
Strcpy(plname, "wizard");
|
|
else
|
|
#endif
|
|
if(!*plname || !strncmp(plname, "player", 4)
|
|
|| !strncmp(plname, "games", 4)) {
|
|
askname();
|
|
} else if (exact_username) {
|
|
/* guard against user names with hyphens in them */
|
|
int len = strlen(plname);
|
|
/* append the current role, if any, so that last dash is ours */
|
|
if (++len < sizeof plname)
|
|
(void)strncat(strcat(plname, "-"),
|
|
pl_character, sizeof plname - len - 1);
|
|
}
|
|
plnamesuffix(); /* strip suffix from name; calls askname() */
|
|
/* again if suffix was whole name */
|
|
/* accepts any suffix */
|
|
#ifdef WIZARD
|
|
if(!wizard) {
|
|
#endif
|
|
/*
|
|
* check for multiple games under the same name
|
|
* (if !locknum) or check max nr of players (otherwise)
|
|
*/
|
|
(void) signal(SIGQUIT,SIG_IGN);
|
|
(void) signal(SIGINT,SIG_IGN);
|
|
if(!locknum)
|
|
Sprintf(lock, "%d%s", (int)getuid(), plname);
|
|
getlock();
|
|
#ifdef WIZARD
|
|
} else {
|
|
Sprintf(lock, "%d%s", (int)getuid(), plname);
|
|
getlock();
|
|
}
|
|
#endif /* WIZARD */
|
|
|
|
dlb_init(); /* must be before newgame() */
|
|
|
|
/*
|
|
* Initialization of the boundaries of the mazes
|
|
* Both boundaries have to be even.
|
|
*/
|
|
x_maze_max = COLNO-1;
|
|
if (x_maze_max % 2)
|
|
x_maze_max--;
|
|
y_maze_max = ROWNO-1;
|
|
if (y_maze_max % 2)
|
|
y_maze_max--;
|
|
|
|
/*
|
|
* Initialize the vision system. This must be before mklev() on a
|
|
* new game or before a level restore on a saved game.
|
|
*/
|
|
vision_init();
|
|
|
|
display_gamewindows();
|
|
|
|
if ((fd = restore_saved_game()) >= 0) {
|
|
#ifdef WIZARD
|
|
/* Since wizard is actually flags.debug, restoring might
|
|
* overwrite it.
|
|
*/
|
|
boolean remember_wiz_mode = wizard;
|
|
#endif
|
|
const char *fq_save = fqname(SAVEF, SAVEPREFIX, 1);
|
|
|
|
(void) chmod(fq_save,0); /* disallow parallel restores */
|
|
(void) signal(SIGINT, (SIG_RET_TYPE) done1);
|
|
#ifdef NEWS
|
|
if(iflags.news) {
|
|
display_file(NEWS, FALSE);
|
|
iflags.news = FALSE; /* in case dorecover() fails */
|
|
}
|
|
#endif
|
|
pline("Restoring save file...");
|
|
mark_synch(); /* flush output */
|
|
if(!dorecover(fd))
|
|
goto not_recovered;
|
|
#ifdef WIZARD
|
|
if(!wizard && remember_wiz_mode) wizard = TRUE;
|
|
#endif
|
|
check_special_room(FALSE);
|
|
wd_message();
|
|
|
|
if (discover || wizard) {
|
|
if(yn("Do you want to keep the save file?") == 'n')
|
|
(void) delete_savefile();
|
|
else {
|
|
(void) chmod(fq_save,FCMASK); /* back to readable */
|
|
nh_compress(fq_save);
|
|
}
|
|
}
|
|
context.move = 0;
|
|
} else {
|
|
not_recovered:
|
|
player_selection();
|
|
newgame();
|
|
wd_message();
|
|
|
|
context.move = 0;
|
|
set_wear();
|
|
(void) pickup(1);
|
|
}
|
|
|
|
moveloop();
|
|
exit(EXIT_SUCCESS);
|
|
/*NOTREACHED*/
|
|
return(0);
|
|
}
|
|
|
|
static void
|
|
process_options(argc, argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* Process options.
|
|
*/
|
|
while(argc > 1 && argv[1][0] == '-'){
|
|
argv++;
|
|
argc--;
|
|
switch(argv[0][1]){
|
|
case 'D':
|
|
#ifdef WIZARD
|
|
{
|
|
char *user;
|
|
int uid;
|
|
struct passwd *pw = (struct passwd *)0;
|
|
|
|
uid = getuid();
|
|
user = getlogin();
|
|
if (user) {
|
|
pw = getpwnam(user);
|
|
if (pw && (pw->pw_uid != uid)) pw = 0;
|
|
}
|
|
if (pw == 0) {
|
|
user = nh_getenv("USER");
|
|
if (user) {
|
|
pw = getpwnam(user);
|
|
if (pw && (pw->pw_uid != uid)) pw = 0;
|
|
}
|
|
if (pw == 0) {
|
|
pw = getpwuid(uid);
|
|
}
|
|
}
|
|
if (pw && !strcmp(pw->pw_name,WIZARD)) {
|
|
wizard = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
/* otherwise fall thru to discover */
|
|
wiz_error_flag = TRUE;
|
|
#endif
|
|
case 'X':
|
|
discover = TRUE;
|
|
break;
|
|
#ifdef NEWS
|
|
case 'n':
|
|
iflags.news = FALSE;
|
|
break;
|
|
#endif
|
|
case 'u':
|
|
if(argv[0][2])
|
|
(void) strncpy(plname, argv[0]+2, sizeof(plname)-1);
|
|
else if(argc > 1) {
|
|
argc--;
|
|
argv++;
|
|
(void) strncpy(plname, argv[0], sizeof(plname)-1);
|
|
} else
|
|
raw_print("Player name expected after -u");
|
|
break;
|
|
case 'I':
|
|
case 'i':
|
|
if (!strncmpi(argv[0]+1, "IBM", 3))
|
|
switch_graphics(IBM_GRAPHICS);
|
|
break;
|
|
/* case 'D': */
|
|
case 'd':
|
|
if (!strncmpi(argv[0]+1, "DEC", 3))
|
|
switch_graphics(DEC_GRAPHICS);
|
|
break;
|
|
case 'p': /* profession (role) */
|
|
if (argv[0][2]) {
|
|
if ((i = str2role(&argv[0][2])) >= 0)
|
|
flags.initrole = i;
|
|
} else if (argc > 1) {
|
|
argc--;
|
|
argv++;
|
|
if ((i = str2role(argv[0])) >= 0)
|
|
flags.initrole = i;
|
|
}
|
|
break;
|
|
case 'r': /* race */
|
|
if (argv[0][2]) {
|
|
if ((i = str2race(&argv[0][2])) >= 0)
|
|
flags.initrace = i;
|
|
} else if (argc > 1) {
|
|
argc--;
|
|
argv++;
|
|
if ((i = str2race(argv[0])) >= 0)
|
|
flags.initrace = i;
|
|
}
|
|
break;
|
|
case 'w': /* windowtype */
|
|
choose_windows(&argv[0][2]);
|
|
break;
|
|
case '@':
|
|
flags.randomall = 1;
|
|
break;
|
|
default:
|
|
if ((i = str2role(&argv[0][1])) >= 0) {
|
|
flags.initrole = i;
|
|
break;
|
|
}
|
|
/* else raw_printf("Unknown option: %s", *argv); */
|
|
}
|
|
}
|
|
|
|
if(argc > 1)
|
|
locknum = atoi(argv[1]);
|
|
#ifdef MAX_NR_OF_PLAYERS
|
|
if(!locknum || locknum > MAX_NR_OF_PLAYERS)
|
|
locknum = MAX_NR_OF_PLAYERS;
|
|
#endif
|
|
}
|
|
|
|
#ifdef CHDIR
|
|
static void
|
|
chdirx(dir, wr)
|
|
const char *dir;
|
|
boolean wr;
|
|
{
|
|
if (dir /* User specified directory? */
|
|
# ifdef HACKDIR
|
|
&& strcmp(dir, HACKDIR) /* and not the default? */
|
|
# endif
|
|
) {
|
|
# ifdef SECURE
|
|
(void) setgid(getgid());
|
|
(void) setuid(getuid()); /* Ron Wessels */
|
|
# endif
|
|
} else {
|
|
/* non-default data files is a sign that scores may not be
|
|
* compatible, or perhaps that a binary not fitting this
|
|
* system's layout is being used.
|
|
*/
|
|
# ifdef VAR_PLAYGROUND
|
|
int len = strlen(VAR_PLAYGROUND);
|
|
|
|
fqn_prefix[SCOREPREFIX] = (char *)alloc(len+2);
|
|
Strcpy(fqn_prefix[SCOREPREFIX], VAR_PLAYGROUND);
|
|
if (fqn_prefix[SCOREPREFIX][len-1] != '/') {
|
|
fqn_prefix[SCOREPREFIX][len] = '/';
|
|
fqn_prefix[SCOREPREFIX][len+1] = '\0';
|
|
}
|
|
# endif
|
|
}
|
|
|
|
# ifdef HACKDIR
|
|
if (dir == (const char *)0)
|
|
dir = HACKDIR;
|
|
# endif
|
|
|
|
if (dir && chdir(dir) < 0) {
|
|
perror(dir);
|
|
error("Cannot chdir to %s.", dir);
|
|
}
|
|
|
|
/* warn the player if we can't write the record file */
|
|
/* perhaps we should also test whether . is writable */
|
|
/* unfortunately the access system-call is worthless */
|
|
if (wr) {
|
|
# ifdef VAR_PLAYGROUND
|
|
fqn_prefix[LEVELPREFIX] = fqn_prefix[SCOREPREFIX];
|
|
fqn_prefix[SAVEPREFIX] = fqn_prefix[SCOREPREFIX];
|
|
fqn_prefix[BONESPREFIX] = fqn_prefix[SCOREPREFIX];
|
|
fqn_prefix[LOCKPREFIX] = fqn_prefix[SCOREPREFIX];
|
|
fqn_prefix[TROUBLEPREFIX] = fqn_prefix[SCOREPREFIX];
|
|
# endif
|
|
check_recordfile(dir);
|
|
}
|
|
}
|
|
#endif /* CHDIR */
|
|
|
|
static boolean
|
|
whoami() {
|
|
/*
|
|
* Who am i? Algorithm: 1. Use name as specified in NETHACKOPTIONS
|
|
* 2. Use $USER or $LOGNAME (if 1. fails)
|
|
* 3. Use getlogin() (if 2. fails)
|
|
* The resulting name is overridden by command line options.
|
|
* If everything fails, or if the resulting name is some generic
|
|
* account like "games", "play", "player", "hack" then eventually
|
|
* we'll ask him.
|
|
* Note that we trust the user here; it is possible to play under
|
|
* somebody else's name.
|
|
*/
|
|
#if defined(__APPLE__)
|
|
/* Unixisms just confuse the user */
|
|
(void) strncpy(plname, "player", sizeof(plname)-1);
|
|
#else
|
|
register char *s;
|
|
|
|
if (*plname) return FALSE;
|
|
if(/* !*plname && */ (s = nh_getenv("USER")))
|
|
(void) strncpy(plname, s, sizeof(plname)-1);
|
|
if(!*plname && (s = nh_getenv("LOGNAME")))
|
|
(void) strncpy(plname, s, sizeof(plname)-1);
|
|
if(!*plname && (s = getlogin()))
|
|
(void) strncpy(plname, s, sizeof(plname)-1);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef PORT_HELP
|
|
void
|
|
port_help()
|
|
{
|
|
/*
|
|
* Display unix-specific help. Just show contents of the helpfile
|
|
* named by PORT_HELP.
|
|
*/
|
|
display_file(PORT_HELP, TRUE);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
wd_message()
|
|
{
|
|
#ifdef WIZARD
|
|
if (wiz_error_flag) {
|
|
pline("Only user \"%s\" may access debug (wizard) mode.",
|
|
# ifndef KR1ED
|
|
WIZARD);
|
|
# else
|
|
WIZARD_NAME);
|
|
# endif
|
|
pline("Entering discovery mode instead.");
|
|
} else
|
|
#endif
|
|
if (discover)
|
|
You("are in non-scoring discovery mode.");
|
|
}
|
|
|
|
/*
|
|
* Add a slash to any name not ending in /. There must
|
|
* be room for the /
|
|
*/
|
|
void
|
|
append_slash(name)
|
|
char *name;
|
|
{
|
|
char *ptr;
|
|
|
|
if (!*name)
|
|
return;
|
|
ptr = name + (strlen(name) - 1);
|
|
if (*ptr != '/') {
|
|
*++ptr = '/';
|
|
*++ptr = '\0';
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*unixmain.c*/
|