1 /* NetHack 3.6 unixmain.c $ANH-Date: 1432512788 2015/05/25 00:13:08 $ $ANH-Branch: master $:$ANH-Revision: 1.52 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* main.c - Unix NetHack */
18 #if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX)
19 #if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__))
20 #if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX)
21 extern struct passwd
*FDECL(getpwuid
, (uid_t
));
23 extern struct passwd
*FDECL(getpwuid
, (int));
27 extern struct passwd
*FDECL(getpwnam
, (const char *));
29 static void FDECL(chdirx
, (const char *, BOOLEAN_P
));
31 static boolean
NDECL(whoami
);
32 static void FDECL(process_options
, (int, char **));
35 extern void NDECL(check_sco_console
);
36 extern void NDECL(init_sco_cons
);
39 extern void NDECL(check_linux_console
);
40 extern void NDECL(init_linux_cons
);
43 static void NDECL(wd_message
);
44 static boolean wiz_error_flag
= FALSE
;
45 static struct passwd
*NDECL(get_unix_pw
);
56 boolean exact_username
;
57 boolean resuming
= FALSE
; /* assume new game */
61 #if defined(__APPLE__)
63 /* special hack to change working directory to a resource fork when
64 running from finder --sam */
65 #define MAC_PATH_VALUE ".app/Contents/MacOS/"
66 char mac_cwd
[1024], *mac_exe
= argv
[0], *mac_tmp
;
67 int arg0_len
= strlen(mac_exe
), mac_tmp_len
, mac_lhs_len
= 0;
68 getcwd(mac_cwd
, 1024);
69 if (mac_exe
[0] == '/' && !strcmp(mac_cwd
, "/")) {
70 if ((mac_exe
= strrchr(mac_exe
, '/')))
74 mac_tmp_len
= (strlen(mac_exe
) * 2) + strlen(MAC_PATH_VALUE
);
75 if (mac_tmp_len
<= arg0_len
) {
76 mac_tmp
= malloc(mac_tmp_len
+ 1);
77 sprintf(mac_tmp
, "%s%s%s", mac_exe
, MAC_PATH_VALUE
, mac_exe
);
78 if (!strcmp(argv
[0] + (arg0_len
- mac_tmp_len
), mac_tmp
)) {
80 (arg0_len
- mac_tmp_len
) + strlen(mac_exe
) + 5;
81 if (mac_lhs_len
> mac_tmp_len
- 1)
82 mac_tmp
= realloc(mac_tmp
, mac_lhs_len
);
83 strncpy(mac_tmp
, argv
[0], mac_lhs_len
);
84 mac_tmp
[mac_lhs_len
] = '\0';
95 (void) umask(0777 & ~FCMASK
);
97 choose_windows(DEFAULT_WINDOW_SYS
);
99 #ifdef CHDIR /* otherwise no chdir() */
101 * See if we must change directory to the playground.
102 * (Perhaps hack runs suid and playground is inaccessible
104 * The environment variable HACKDIR is overridden by a
105 * -d command line option (must be the first option given)
107 dir
= nh_getenv("NETHACKDIR");
109 dir
= nh_getenv("HACKDIR");
113 if (!strncmp(argv
[1], "-d", 2) && argv
[1][2] != 'e') {
114 /* avoid matching "-dec" for DECgraphics; since the man page
115 * says -d directory, hope nobody's using -desomething_else
120 if (*dir
== '=' || *dir
== ':')
122 if (!*dir
&& argc
> 1) {
128 error("Flag -d must be followed by a directory name.");
134 * Now we know the directory containing 'record' and
135 * may do a prscore(). Exclude `-style' - it's a Qt option.
137 if (!strncmp(argv
[1], "-s", 2) && strncmp(argv
[1], "-style", 6)) {
145 ARGV0
= argv
[0]; /* save for possible stack trace */
147 panictrace_setsignals(TRUE
);
156 * Change directories before we initialize the window system so
157 * we can find the tile file.
167 check_linux_console();
171 ARGV0
= argv
[0]; /* save for possible stack trace */
173 panictrace_setsignals(TRUE
);
176 exact_username
= whoami();
179 * It seems you really want to play.
181 u
.uhp
= 1; /* prevent RIP on early quits */
182 program_state
.preserve_locks
= 1;
184 sethanguphandler((SIG_RET_TYPE
) hangup
);
187 process_options(argc
, argv
); /* command line options */
189 commit_windowchain();
191 init_nhwindows(&argc
, argv
); /* now we can set up window system */
200 if (!(catmore
= nh_getenv("HACKPAGER"))
201 && !(catmore
= nh_getenv("PAGER")))
208 /* wizard mode access is deferred until here */
209 set_playmode(); /* sets plname to "wizard" for wizard mode */
210 if (exact_username
) {
212 * FIXME: this no longer works, ever since 3.3.0
213 * when plnamesuffix() was changed to find
214 * Name-Role-Race-Gender-Alignment. It removes
215 * all dashes rather than just the last one,
216 * regardless of whether whatever follows each
217 * dash matches role, race, gender, or alignment.
219 /* guard against user names with hyphens in them */
220 int len
= strlen(plname
);
221 /* append the current role, if any, so that last dash is ours */
222 if (++len
< (int) sizeof plname
)
223 (void) strncat(strcat(plname
, "-"), pl_character
,
224 sizeof plname
- len
- 1);
226 /* strip role,race,&c suffix; calls askname() if plname[] is empty
227 or holds a generic user name like "player" or "games" */
231 /* use character name rather than lock letter for file names */
234 /* suppress interrupts while processing lock file */
235 (void) signal(SIGQUIT
, SIG_IGN
);
236 (void) signal(SIGINT
, SIG_IGN
);
239 * getlock() complains and quits if there is already a game
240 * in progress for current character name (when locknum == 0)
241 * or if there are too many active games (when locknum > 0).
242 * When proceeding, it creates an empty <lockname>.0 file to
243 * designate the current game.
244 * getlock() constructs <lockname> based on the character
245 * name (for !locknum) or on first available of alock, block,
246 * clock, &c not currently in use in the playground directory
250 program_state
.preserve_locks
= 0; /* after getlock() */
252 dlb_init(); /* must be before newgame() */
255 * Initialize the vision system. This must be before mklev() on a
256 * new game or before a level restore on a saved game.
260 display_gamewindows();
263 * First, try to find and restore a save file for specified character.
264 * We'll return here if new game player_selection() renames the hero.
267 if ((fd
= restore_saved_game()) >= 0) {
268 const char *fq_save
= fqname(SAVEF
, SAVEPREFIX
, 1);
270 (void) chmod(fq_save
, 0); /* disallow parallel restores */
272 (void) signal(SIGINT
, (SIG_RET_TYPE
) done1
);
276 display_file(NEWS
, FALSE
);
277 iflags
.news
= FALSE
; /* in case dorecover() fails */
280 pline("Restoring save file...");
281 mark_synch(); /* flush output */
283 resuming
= TRUE
; /* not starting new game */
285 if (discover
|| wizard
) {
286 if (yn("Do you want to keep the save file?") == 'n')
287 (void) delete_savefile();
289 (void) chmod(fq_save
, FCMASK
); /* back to readable */
290 nh_compress(fq_save
);
297 /* new game: start by choosing role, race, etc;
298 player might change the hero's name while doing that,
299 in which case we try to restore under the new name
300 and skip selection this time if that didn't succeed */
301 if (!iflags
.renameinprogress
) {
303 if (iflags
.renameinprogress
) {
304 /* player has renamed the hero while selecting role;
305 if locking alphabetically, the existing lock file
306 can still be used; otherwise, discard current one
307 and create another for the new character name */
309 delete_levelfile(0); /* remove empty lock file */
312 goto attempt_restore
;
326 process_options(argc
, argv
)
335 while (argc
> 1 && argv
[1][0] == '-') {
338 l
= (int) strlen(*argv
);
339 /* must supply at least 4 chars to match "-XXXgraphics" */
343 switch (argv
[0][1]) {
346 if ((argv
[0][1] == 'D' && !argv
[0][2])
347 || !strcmpi(*argv
, "-debug")) {
348 wizard
= TRUE
, discover
= FALSE
;
349 } else if (!strncmpi(*argv
, "-DECgraphics", l
)) {
350 load_symset("DECGraphics", PRIMARY
);
351 switch_symbols(TRUE
);
353 raw_printf("Unknown option: %s", *argv
);
358 discover
= TRUE
, wizard
= FALSE
;
367 (void) strncpy(plname
, argv
[0] + 2, sizeof(plname
) - 1);
371 (void) strncpy(plname
, argv
[0], sizeof(plname
) - 1);
373 raw_print("Player name expected after -u");
377 if (!strncmpi(*argv
, "-IBMgraphics", l
)) {
378 load_symset("IBMGraphics", PRIMARY
);
379 load_symset("RogueIBM", ROGUESET
);
380 switch_symbols(TRUE
);
382 raw_printf("Unknown option: %s", *argv
);
385 case 'p': /* profession (role) */
387 if ((i
= str2role(&argv
[0][2])) >= 0)
389 } else if (argc
> 1) {
392 if ((i
= str2role(argv
[0])) >= 0)
398 if ((i
= str2race(&argv
[0][2])) >= 0)
400 } else if (argc
> 1) {
403 if ((i
= str2race(argv
[0])) >= 0)
407 case 'w': /* windowtype */
408 choose_windows(&argv
[0][2]);
414 if ((i
= str2role(&argv
[0][1])) >= 0) {
418 /* else raw_printf("Unknown option: %s", *argv); */
424 raw_printf("MAXPLAYERS are set in sysconf file.\n");
426 /* XXX This is deprecated in favor of SYSCF with MAXPLAYERS */
428 locknum
= atoi(argv
[1]);
430 #ifdef MAX_NR_OF_PLAYERS
431 /* limit to compile-time limit */
432 if (!locknum
|| locknum
> MAX_NR_OF_PLAYERS
)
433 locknum
= MAX_NR_OF_PLAYERS
;
436 /* let syscf override compile-time limit */
437 if (!locknum
|| (sysopt
.maxplayers
&& locknum
> sysopt
.maxplayers
))
438 locknum
= sysopt
.maxplayers
;
448 if (dir
/* User specified directory? */
450 && strcmp(dir
, HACKDIR
) /* and not the default? */
454 (void) setgid(getgid());
455 (void) setuid(getuid()); /* Ron Wessels */
458 /* non-default data files is a sign that scores may not be
459 * compatible, or perhaps that a binary not fitting this
460 * system's layout is being used.
462 #ifdef VAR_PLAYGROUND
463 int len
= strlen(VAR_PLAYGROUND
);
465 fqn_prefix
[SCOREPREFIX
] = (char *) alloc(len
+ 2);
466 Strcpy(fqn_prefix
[SCOREPREFIX
], VAR_PLAYGROUND
);
467 if (fqn_prefix
[SCOREPREFIX
][len
- 1] != '/') {
468 fqn_prefix
[SCOREPREFIX
][len
] = '/';
469 fqn_prefix
[SCOREPREFIX
][len
+ 1] = '\0';
475 if (dir
== (const char *) 0)
479 if (dir
&& chdir(dir
) < 0) {
481 error("Cannot chdir to %s.", dir
);
484 /* warn the player if we can't write the record file */
485 /* perhaps we should also test whether . is writable */
486 /* unfortunately the access system-call is worthless */
488 #ifdef VAR_PLAYGROUND
489 fqn_prefix
[LEVELPREFIX
] = fqn_prefix
[SCOREPREFIX
];
490 fqn_prefix
[SAVEPREFIX
] = fqn_prefix
[SCOREPREFIX
];
491 fqn_prefix
[BONESPREFIX
] = fqn_prefix
[SCOREPREFIX
];
492 fqn_prefix
[LOCKPREFIX
] = fqn_prefix
[SCOREPREFIX
];
493 fqn_prefix
[TROUBLEPREFIX
] = fqn_prefix
[SCOREPREFIX
];
495 check_recordfile(dir
);
500 /* returns True iff we set plname[] to username which contains a hyphen */
505 * Who am i? Algorithm: 1. Use name as specified in NETHACKOPTIONS
506 * 2. Use $USER or $LOGNAME (if 1. fails)
507 * 3. Use getlogin() (if 2. fails)
508 * The resulting name is overridden by command line options.
509 * If everything fails, or if the resulting name is some generic
510 * account like "games", "play", "player", "hack" then eventually
512 * Note that we trust the user here; it is possible to play under
513 * somebody else's name.
516 register const char *s
;
518 s
= nh_getenv("USER");
520 s
= nh_getenv("LOGNAME");
525 (void) strncpy(plname
, s
, sizeof plname
- 1);
526 if (index(plname
, '-'))
534 sethanguphandler(handler
)
535 void FDECL((*handler
), (int));
538 /* don't want reads to restart. If SA_RESTART is defined, we know
539 * sigaction exists and can be used to ensure reads won't restart.
540 * If it's not defined, assume reads do not restart. If reads restart
541 * and a signal occurs, the game won't do anything until the read
542 * succeeds (or the stream returns EOF, which might not happen if
543 * reading from, say, a window manager). */
544 struct sigaction sact
;
546 (void) memset((genericptr_t
) &sact
, 0, sizeof sact
);
547 sact
.sa_handler
= (SIG_RET_TYPE
) handler
;
548 (void) sigaction(SIGHUP
, &sact
, (struct sigaction
*) 0);
550 (void) sigaction(SIGXCPU
, &sact
, (struct sigaction
*) 0);
552 #else /* !SA_RESTART */
553 (void) signal(SIGHUP
, (SIG_RET_TYPE
) handler
);
555 (void) signal(SIGXCPU
, (SIG_RET_TYPE
) handler
);
557 #endif /* ?SA_RESTART */
565 * Display unix-specific help. Just show contents of the helpfile
566 * named by PORT_HELP.
568 display_file(PORT_HELP
, TRUE
);
572 /* validate wizard mode if player has requested access to it */
574 authorize_wizard_mode()
576 struct passwd
*pw
= get_unix_pw();
577 if (pw
&& sysopt
.wizards
&& sysopt
.wizards
[0]) {
578 if (check_user_string(sysopt
.wizards
))
581 wiz_error_flag
= TRUE
; /* not being allowed into wizard mode */
588 if (wiz_error_flag
) {
589 if (sysopt
.wizards
&& sysopt
.wizards
[0]) {
590 char *tmp
= build_english_list(sysopt
.wizards
);
591 pline("Only user%s %s may access debug (wizard) mode.",
592 index(sysopt
.wizards
, ' ') ? "s" : "", tmp
);
595 pline("Entering explore/discovery mode instead.");
596 wizard
= 0, discover
= 1; /* (paranoia) */
598 You("are in non-scoring explore/discovery mode.");
602 * Add a slash to any name not ending in /. There must
613 ptr
= name
+ (strlen(name
) - 1);
622 check_user_string(optstr
)
625 struct passwd
*pw
= get_unix_pw();
629 if (optstr
[0] == '*')
630 return TRUE
; /* allow any user */
633 if (sysopt
.check_plname
)
636 pwname
= pw
->pw_name
;
637 pwlen
= strlen(pwname
);
640 while (w
+ pwlen
<= eop
) {
647 if (!strncmp(w
, pwname
, pwlen
)) {
648 if (!w
[pwlen
] || isspace(w
[pwlen
]))
651 while (*w
&& !isspace(*w
))
657 static struct passwd
*
662 static struct passwd
*pw
= (struct passwd
*) 0;
665 return pw
; /* cache answer */
667 uid
= (unsigned) getuid();
671 if (pw
&& ((unsigned) pw
->pw_uid
!= uid
))
675 user
= nh_getenv("USER");
678 if (pw
&& ((unsigned) pw
->pw_uid
!= uid
))