1 /* NetHack 3.6 unixmain.c $NHDT-Date: 1432512788 2015/05/25 00:13:08 $ $NHDT-Branch: master $:$NHDT-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 */
19 #include <X11/Xlocale.h>
22 #if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX)
23 #if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__))
24 #if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX)
25 extern struct passwd *FDECL(getpwuid, (uid_t));
27 extern struct passwd *FDECL(getpwuid, (int));
31 extern struct passwd *FDECL(getpwnam, (const char *));
33 static void FDECL(chdirx, (const char *, BOOLEAN_P));
35 static boolean NDECL(whoami);
36 static void FDECL(process_options, (int, char **));
39 extern void NDECL(check_sco_console);
40 extern void NDECL(init_sco_cons);
43 extern void NDECL(check_linux_console);
44 extern void NDECL(init_linux_cons);
47 static void NDECL(wd_message);
48 static boolean wiz_error_flag = FALSE;
49 static struct passwd *NDECL(get_unix_pw);
60 boolean exact_username;
61 boolean resuming = FALSE; /* assume new game */
66 setlocale(LC_ALL, "");
68 #if defined(__APPLE__)
70 /* special hack to change working directory to a resource fork when
71 running from finder --sam */
72 #define MAC_PATH_VALUE ".app/Contents/MacOS/"
73 char mac_cwd[1024], *mac_exe = argv[0], *mac_tmp;
74 int arg0_len = strlen(mac_exe), mac_tmp_len, mac_lhs_len = 0;
75 getcwd(mac_cwd, 1024);
76 if (mac_exe[0] == '/' && !strcmp(mac_cwd, "/")) {
77 if ((mac_exe = strrchr(mac_exe, '/')))
81 mac_tmp_len = (strlen(mac_exe) * 2) + strlen(MAC_PATH_VALUE);
82 if (mac_tmp_len <= arg0_len) {
83 mac_tmp = malloc(mac_tmp_len + 1);
84 sprintf(mac_tmp, "%s%s%s", mac_exe, MAC_PATH_VALUE, mac_exe);
85 if (!strcmp(argv[0] + (arg0_len - mac_tmp_len), mac_tmp)) {
87 (arg0_len - mac_tmp_len) + strlen(mac_exe) + 5;
88 if (mac_lhs_len > mac_tmp_len - 1)
89 mac_tmp = realloc(mac_tmp, mac_lhs_len);
90 strncpy(mac_tmp, argv[0], mac_lhs_len);
91 mac_tmp[mac_lhs_len] = '\0';
102 (void) umask(0777 & ~FCMASK);
104 choose_windows(DEFAULT_WINDOW_SYS);
106 #ifdef CHDIR /* otherwise no chdir() */
108 * See if we must change directory to the playground.
109 * (Perhaps hack runs suid and playground is inaccessible
111 * The environment variable HACKDIR is overridden by a
112 * -d command line option (must be the first option given)
114 dir = nh_getenv("NETHACKDIR");
116 dir = nh_getenv("HACKDIR");
120 if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') {
121 /* avoid matching "-dec" for DECgraphics; since the man page
122 * says -d directory, hope nobody's using -desomething_else
127 if (*dir == '=' || *dir == ':')
129 if (!*dir && argc > 1) {
135 error("Flag -d must be followed by a directory name.");
141 * Now we know the directory containing 'record' and
142 * may do a prscore(). Exclude `-style' - it's a Qt option.
144 if (!strncmp(argv[1], "-s", 2) && strncmp(argv[1], "-style", 6)) {
152 ARGV0 = argv[0]; /* save for possible stack trace */
154 panictrace_setsignals(TRUE);
163 jputchar('\0'); /* reset */
170 * Change directories before we initialize the window system so
171 * we can find the tile file.
181 check_linux_console();
184 /* Line like "OPTIONS=name:foo-@" may exist in config file.
185 * In this case, need to select random class,
186 * so must call setrandom() before initoptions().
192 ARGV0 = argv[0]; /* save for possible stack trace */
194 panictrace_setsignals(TRUE);
197 exact_username = whoami();
200 * It seems you really want to play.
202 u.uhp = 1; /* prevent RIP on early quits */
203 program_state.preserve_locks = 1;
205 sethanguphandler((SIG_RET_TYPE) hangup);
208 process_options(argc, argv); /* command line options */
210 commit_windowchain();
212 init_nhwindows(&argc, argv); /* now we can set up window system */
221 if (!(catmore = nh_getenv("HACKPAGER"))
222 && !(catmore = nh_getenv("PAGER")))
229 /* wizard mode access is deferred until here */
230 set_playmode(); /* sets plname to "wizard" for wizard mode */
231 if (exact_username) {
233 * FIXME: this no longer works, ever since 3.3.0
234 * when plnamesuffix() was changed to find
235 * Name-Role-Race-Gender-Alignment. It removes
236 * all dashes rather than just the last one,
237 * regardless of whether whatever follows each
238 * dash matches role, race, gender, or alignment.
240 /* guard against user names with hyphens in them */
241 int len = strlen(plname);
242 /* append the current role, if any, so that last dash is ours */
243 if (++len < (int) sizeof plname)
244 (void) strncat(strcat(plname, "-"), pl_character,
245 sizeof plname - len - 1);
247 /* strip role,race,&c suffix; calls askname() if plname[] is empty
248 or holds a generic user name like "player" or "games" */
252 /* use character name rather than lock letter for file names */
255 /* suppress interrupts while processing lock file */
256 (void) signal(SIGQUIT, SIG_IGN);
257 (void) signal(SIGINT, SIG_IGN);
260 * getlock() complains and quits if there is already a game
261 * in progress for current character name (when locknum == 0)
262 * or if there are too many active games (when locknum > 0).
263 * When proceeding, it creates an empty <lockname>.0 file to
264 * designate the current game.
265 * getlock() constructs <lockname> based on the character
266 * name (for !locknum) or on first available of alock, block,
267 * clock, &c not currently in use in the playground directory
271 program_state.preserve_locks = 0; /* after getlock() */
273 dlb_init(); /* must be before newgame() */
276 * Initialize the vision system. This must be before mklev() on a
277 * new game or before a level restore on a saved game.
281 display_gamewindows();
284 * First, try to find and restore a save file for specified character.
285 * We'll return here if new game player_selection() renames the hero.
288 if ((fd = restore_saved_game()) >= 0) {
289 const char *fq_save = fqname(SAVEF, SAVEPREFIX, 1);
291 (void) chmod(fq_save, 0); /* disallow parallel restores */
293 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
297 display_file(NEWS, FALSE);
298 iflags.news = FALSE; /* in case dorecover() fails */
302 pline("Restoring save file...");
304 pline("
\83Z
\81[
\83u
\83t
\83@
\83C
\83\8b\82ð
\95\9c\8c³
\92\86\81D
\81D
\81D");
305 mark_synch(); /* flush output */
307 resuming = TRUE; /* not starting new game */
309 if (discover || wizard) {
311 if (yn("Do you want to keep the save file?") == 'n')
313 if (yn("
\83Z
\81[
\83u
\83t
\83@
\83C
\83\8b\82ð
\8ec
\82µ
\82Ä
\82¨
\82«
\82Ü
\82·
\82©
\81H") == 'n')
314 (void) delete_savefile();
316 (void) chmod(fq_save, FCMASK); /* back to readable */
317 nh_compress(fq_save);
324 /* new game: start by choosing role, race, etc;
325 player might change the hero's name while doing that,
326 in which case we try to restore under the new name
327 and skip selection this time if that didn't succeed */
328 if (!iflags.renameinprogress) {
330 if (iflags.renameinprogress) {
331 /* player has renamed the hero while selecting role;
332 if locking alphabetically, the existing lock file
333 can still be used; otherwise, discard current one
334 and create another for the new character name */
336 delete_levelfile(0); /* remove empty lock file */
339 goto attempt_restore;
353 process_options(argc, argv)
362 while (argc > 1 && argv[1][0] == '-') {
365 l = (int) strlen(*argv);
366 /* must supply at least 4 chars to match "-XXXgraphics" */
370 switch (argv[0][1]) {
373 if ((argv[0][1] == 'D' && !argv[0][2])
374 || !strcmpi(*argv, "-debug")) {
375 wizard = TRUE, discover = FALSE;
376 } else if (!strncmpi(*argv, "-DECgraphics", l)) {
377 load_symset("DECGraphics", PRIMARY);
378 switch_symbols(TRUE);
380 raw_printf("Unknown option: %s", *argv);
385 discover = TRUE, wizard = FALSE;
394 (void) strncpy(plname, argv[0] + 2, sizeof(plname) - 1);
398 (void) strncpy(plname, argv[0], sizeof(plname) - 1);
400 raw_print("Player name expected after -u");
404 if (!strncmpi(*argv, "-IBMgraphics", l)) {
405 load_symset("IBMGraphics", PRIMARY);
406 load_symset("RogueIBM", ROGUESET);
407 switch_symbols(TRUE);
409 raw_printf("Unknown option: %s", *argv);
412 case 'p': /* profession (role) */
414 if ((i = str2role(&argv[0][2])) >= 0)
416 } else if (argc > 1) {
419 if ((i = str2role(argv[0])) >= 0)
425 if ((i = str2race(&argv[0][2])) >= 0)
427 } else if (argc > 1) {
430 if ((i = str2race(argv[0])) >= 0)
434 case 'w': /* windowtype */
435 choose_windows(&argv[0][2]);
441 if ((i = str2role(&argv[0][1])) >= 0) {
445 /* else raw_printf("Unknown option: %s", *argv); */
449 /* XXX This is deprecated in favor of SYSCF with MAXPLAYERS. Make
450 * an error in next release. */
452 locknum = atoi(argv[1]);
453 #ifdef MAX_NR_OF_PLAYERS
454 /* limit to compile-time limit */
455 if (!locknum || locknum > MAX_NR_OF_PLAYERS)
456 locknum = MAX_NR_OF_PLAYERS;
459 /* let syscf override compile-time limit */
460 if (!locknum || (sysopt.maxplayers && locknum > sysopt.maxplayers))
461 locknum = sysopt.maxplayers;
471 if (dir /* User specified directory? */
473 && strcmp(dir, HACKDIR) /* and not the default? */
477 #pragma GCC diagnostic push
478 #pragma GCC diagnostic ignored "-Wunused-result"
479 (void) setgid(getgid());
480 (void) setuid(getuid()); /* Ron Wessels */
481 #pragma GCC diagnostic pop
484 /* non-default data files is a sign that scores may not be
485 * compatible, or perhaps that a binary not fitting this
486 * system's layout is being used.
488 #ifdef VAR_PLAYGROUND
489 int len = strlen(VAR_PLAYGROUND);
491 fqn_prefix[SCOREPREFIX] = (char *) alloc(len + 2);
492 Strcpy(fqn_prefix[SCOREPREFIX], VAR_PLAYGROUND);
493 if (fqn_prefix[SCOREPREFIX][len - 1] != '/') {
494 fqn_prefix[SCOREPREFIX][len] = '/';
495 fqn_prefix[SCOREPREFIX][len + 1] = '\0';
501 if (dir == (const char *) 0)
505 if (dir && chdir(dir) < 0) {
507 error("Cannot chdir to %s.", dir);
510 /* warn the player if we can't write the record file */
511 /* perhaps we should also test whether . is writable */
512 /* unfortunately the access system-call is worthless */
514 #ifdef VAR_PLAYGROUND
515 fqn_prefix[LEVELPREFIX] = fqn_prefix[SCOREPREFIX];
516 fqn_prefix[SAVEPREFIX] = fqn_prefix[SCOREPREFIX];
517 fqn_prefix[BONESPREFIX] = fqn_prefix[SCOREPREFIX];
518 fqn_prefix[LOCKPREFIX] = fqn_prefix[SCOREPREFIX];
519 fqn_prefix[TROUBLEPREFIX] = fqn_prefix[SCOREPREFIX];
521 check_recordfile(dir);
526 /* returns True iff we set plname[] to username which contains a hyphen */
531 * Who am i? Algorithm: 1. Use name as specified in NETHACKOPTIONS
532 * 2. Use $USER or $LOGNAME (if 1. fails)
533 * 3. Use getlogin() (if 2. fails)
534 * The resulting name is overridden by command line options.
535 * If everything fails, or if the resulting name is some generic
536 * account like "games", "play", "player", "hack" then eventually
538 * Note that we trust the user here; it is possible to play under
539 * somebody else's name.
542 register const char *s;
544 s = nh_getenv("USER");
546 s = nh_getenv("LOGNAME");
551 (void) strncpy(plname, s, sizeof plname - 1);
552 if (index(plname, '-'))
560 sethanguphandler(handler)
561 void FDECL((*handler), (int));
564 /* don't want reads to restart. If SA_RESTART is defined, we know
565 * sigaction exists and can be used to ensure reads won't restart.
566 * If it's not defined, assume reads do not restart. If reads restart
567 * and a signal occurs, the game won't do anything until the read
568 * succeeds (or the stream returns EOF, which might not happen if
569 * reading from, say, a window manager). */
570 struct sigaction sact;
572 (void) memset((genericptr_t) &sact, 0, sizeof sact);
573 sact.sa_handler = (SIG_RET_TYPE) handler;
574 (void) sigaction(SIGHUP, &sact, (struct sigaction *) 0);
576 (void) sigaction(SIGXCPU, &sact, (struct sigaction *) 0);
578 #else /* !SA_RESTART */
579 (void) signal(SIGHUP, (SIG_RET_TYPE) handler);
581 (void) signal(SIGXCPU, (SIG_RET_TYPE) handler);
583 #endif /* ?SA_RESTART */
591 * Display unix-specific help. Just show contents of the helpfile
592 * named by PORT_HELP.
594 display_file(PORT_HELP, TRUE);
598 /* validate wizard mode if player has requested access to it */
600 authorize_wizard_mode()
602 struct passwd *pw = get_unix_pw();
603 if (pw && sysopt.wizards && sysopt.wizards[0]) {
604 if (check_user_string(sysopt.wizards))
607 wiz_error_flag = TRUE; /* not being allowed into wizard mode */
614 if (wiz_error_flag) {
615 if (sysopt.wizards && sysopt.wizards[0]) {
616 char *tmp = build_english_list(sysopt.wizards);
618 pline("Only user%s %s may access debug (wizard) mode.",
619 index(sysopt.wizards, ' ') ? "s" : "", tmp);
621 pline("
\81u%s
\81v
\82Ì
\82Ý
\82ª
\83f
\83o
\83b
\83O(
\83E
\83C
\83U
\81[
\83h)
\83\82\81[
\83h
\82ð
\8eg
\97p
\82Å
\82«
\82é
\81D",
627 pline("Entering explore/discovery mode instead.");
629 pline("
\82©
\82í
\82è
\82É
\94
\8c©
\83\82\81[
\83h
\82Ö
\88Ú
\8ds
\82·
\82é
\81D");
630 wizard = 0, discover = 1; /* (paranoia) */
633 You("are in non-scoring explore/discovery mode.");
635 You("
\83X
\83R
\83A
\82Ì
\8dÚ
\82ç
\82È
\82¢
\94
\8c©
\83\82\81[
\83h
\82Å
\8bN
\93®
\82µ
\82½
\81D");
639 * Add a slash to any name not ending in /. There must
650 ptr = name + (strlen(name) - 1);
659 check_user_string(optstr)
662 struct passwd *pw = get_unix_pw();
665 if (optstr[0] == '*')
666 return TRUE; /* allow any user */
669 pwlen = strlen(pw->pw_name);
672 while (w + pwlen <= eop) {
679 if (!strncmp(w, pw->pw_name, pwlen)) {
680 if (!w[pwlen] || isspace(w[pwlen]))
683 while (*w && !isspace(*w))
689 static struct passwd *
694 static struct passwd *pw = (struct passwd *) 0;
697 return pw; /* cache answer */
699 uid = (unsigned) getuid();
703 if (pw && ((unsigned) pw->pw_uid != uid))
707 user = nh_getenv("USER");
710 if (pw && ((unsigned) pw->pw_uid != uid))