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 (void) setgid(getgid());
478 (void) setuid(getuid()); /* Ron Wessels */
481 /* non-default data files is a sign that scores may not be
482 * compatible, or perhaps that a binary not fitting this
483 * system's layout is being used.
485 #ifdef VAR_PLAYGROUND
486 int len = strlen(VAR_PLAYGROUND);
488 fqn_prefix[SCOREPREFIX] = (char *) alloc(len + 2);
489 Strcpy(fqn_prefix[SCOREPREFIX], VAR_PLAYGROUND);
490 if (fqn_prefix[SCOREPREFIX][len - 1] != '/') {
491 fqn_prefix[SCOREPREFIX][len] = '/';
492 fqn_prefix[SCOREPREFIX][len + 1] = '\0';
498 if (dir == (const char *) 0)
502 if (dir && chdir(dir) < 0) {
504 error("Cannot chdir to %s.", dir);
507 /* warn the player if we can't write the record file */
508 /* perhaps we should also test whether . is writable */
509 /* unfortunately the access system-call is worthless */
511 #ifdef VAR_PLAYGROUND
512 fqn_prefix[LEVELPREFIX] = fqn_prefix[SCOREPREFIX];
513 fqn_prefix[SAVEPREFIX] = fqn_prefix[SCOREPREFIX];
514 fqn_prefix[BONESPREFIX] = fqn_prefix[SCOREPREFIX];
515 fqn_prefix[LOCKPREFIX] = fqn_prefix[SCOREPREFIX];
516 fqn_prefix[TROUBLEPREFIX] = fqn_prefix[SCOREPREFIX];
518 check_recordfile(dir);
523 /* returns True iff we set plname[] to username which contains a hyphen */
528 * Who am i? Algorithm: 1. Use name as specified in NETHACKOPTIONS
529 * 2. Use $USER or $LOGNAME (if 1. fails)
530 * 3. Use getlogin() (if 2. fails)
531 * The resulting name is overridden by command line options.
532 * If everything fails, or if the resulting name is some generic
533 * account like "games", "play", "player", "hack" then eventually
535 * Note that we trust the user here; it is possible to play under
536 * somebody else's name.
539 register const char *s;
541 s = nh_getenv("USER");
543 s = nh_getenv("LOGNAME");
548 (void) strncpy(plname, s, sizeof plname - 1);
549 if (index(plname, '-'))
557 sethanguphandler(handler)
558 void FDECL((*handler), (int));
561 /* don't want reads to restart. If SA_RESTART is defined, we know
562 * sigaction exists and can be used to ensure reads won't restart.
563 * If it's not defined, assume reads do not restart. If reads restart
564 * and a signal occurs, the game won't do anything until the read
565 * succeeds (or the stream returns EOF, which might not happen if
566 * reading from, say, a window manager). */
567 struct sigaction sact;
569 (void) memset((genericptr_t) &sact, 0, sizeof sact);
570 sact.sa_handler = (SIG_RET_TYPE) handler;
571 (void) sigaction(SIGHUP, &sact, (struct sigaction *) 0);
573 (void) sigaction(SIGXCPU, &sact, (struct sigaction *) 0);
575 #else /* !SA_RESTART */
576 (void) signal(SIGHUP, (SIG_RET_TYPE) handler);
578 (void) signal(SIGXCPU, (SIG_RET_TYPE) handler);
580 #endif /* ?SA_RESTART */
588 * Display unix-specific help. Just show contents of the helpfile
589 * named by PORT_HELP.
591 display_file(PORT_HELP, TRUE);
595 /* validate wizard mode if player has requested access to it */
597 authorize_wizard_mode()
599 struct passwd *pw = get_unix_pw();
600 if (pw && sysopt.wizards && sysopt.wizards[0]) {
601 if (check_user_string(sysopt.wizards))
604 wiz_error_flag = TRUE; /* not being allowed into wizard mode */
611 if (wiz_error_flag) {
612 if (sysopt.wizards && sysopt.wizards[0]) {
613 char *tmp = build_english_list(sysopt.wizards);
615 pline("Only user%s %s may access debug (wizard) mode.",
616 index(sysopt.wizards, ' ') ? "s" : "", tmp);
618 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",
624 pline("Entering explore/discovery mode instead.");
626 pline("
\82©
\82í
\82è
\82É
\94
\8c©
\83\82\81[
\83h
\82Ö
\88Ú
\8ds
\82·
\82é
\81D");
627 wizard = 0, discover = 1; /* (paranoia) */
630 You("are in non-scoring explore/discovery mode.");
632 You("
\83X
\83R
\83A
\82Ì
\8dÚ
\82ç
\82È
\82¢
\94
\8c©
\83\82\81[
\83h
\82Å
\8bN
\93®
\82µ
\82½
\81D");
636 * Add a slash to any name not ending in /. There must
647 ptr = name + (strlen(name) - 1);
656 check_user_string(optstr)
659 struct passwd *pw = get_unix_pw();
662 if (optstr[0] == '*')
663 return TRUE; /* allow any user */
666 pwlen = strlen(pw->pw_name);
669 while (w + pwlen <= eop) {
676 if (!strncmp(w, pw->pw_name, pwlen)) {
677 if (!w[pwlen] || isspace(w[pwlen]))
680 while (*w && !isspace(*w))
686 static struct passwd *
691 static struct passwd *pw = (struct passwd *) 0;
694 return pw; /* cache answer */
696 uid = (unsigned) getuid();
700 if (pw && ((unsigned) pw->pw_uid != uid))
704 user = nh_getenv("USER");
707 if (pw && ((unsigned) pw->pw_uid != uid))