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 /*-Copyright (c) Robert Patrick Rankin, 2011. */
4 /* NetHack may be freely redistributed. See license for details. */
6 /* main.c - Unix NetHack */
20 #include <X11/Xlocale.h>
23 #if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX)
24 #if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__))
25 #if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX)
26 extern struct passwd *FDECL(getpwuid, (uid_t));
28 extern struct passwd *FDECL(getpwuid, (int));
32 extern struct passwd *FDECL(getpwnam, (const char *));
34 static void FDECL(chdirx, (const char *, BOOLEAN_P));
36 static boolean NDECL(whoami);
37 static void FDECL(process_options, (int, char **));
40 extern void NDECL(check_sco_console);
41 extern void NDECL(init_sco_cons);
44 extern void NDECL(check_linux_console);
45 extern void NDECL(init_linux_cons);
48 static void NDECL(wd_message);
49 static boolean wiz_error_flag = FALSE;
50 static struct passwd *NDECL(get_unix_pw);
61 boolean exact_username;
62 boolean resuming = FALSE; /* assume new game */
63 boolean plsel_once = FALSE;
68 setlocale(LC_ALL, "");
70 #if defined(__APPLE__)
72 /* special hack to change working directory to a resource fork when
73 running from finder --sam */
74 #define MAC_PATH_VALUE ".app/Contents/MacOS/"
75 char mac_cwd[1024], *mac_exe = argv[0], *mac_tmp;
76 int arg0_len = strlen(mac_exe), mac_tmp_len, mac_lhs_len = 0;
77 getcwd(mac_cwd, 1024);
78 if (mac_exe[0] == '/' && !strcmp(mac_cwd, "/")) {
79 if ((mac_exe = strrchr(mac_exe, '/')))
83 mac_tmp_len = (strlen(mac_exe) * 2) + strlen(MAC_PATH_VALUE);
84 if (mac_tmp_len <= arg0_len) {
85 mac_tmp = malloc(mac_tmp_len + 1);
86 sprintf(mac_tmp, "%s%s%s", mac_exe, MAC_PATH_VALUE, mac_exe);
87 if (!strcmp(argv[0] + (arg0_len - mac_tmp_len), mac_tmp)) {
89 (arg0_len - mac_tmp_len) + strlen(mac_exe) + 5;
90 if (mac_lhs_len > mac_tmp_len - 1)
91 mac_tmp = realloc(mac_tmp, mac_lhs_len);
92 strncpy(mac_tmp, argv[0], mac_lhs_len);
93 mac_tmp[mac_lhs_len] = '\0';
104 (void) umask(0777 & ~FCMASK);
106 choose_windows(DEFAULT_WINDOW_SYS);
108 #ifdef CHDIR /* otherwise no chdir() */
110 * See if we must change directory to the playground.
111 * (Perhaps hack runs suid and playground is inaccessible
113 * The environment variable HACKDIR is overridden by a
114 * -d command line option (must be the first option given).
116 dir = nh_getenv("NETHACKDIR");
118 dir = nh_getenv("HACKDIR");
121 if (argcheck(argc, argv, ARG_VERSION))
124 if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') {
125 /* avoid matching "-dec" for DECgraphics; since the man page
126 * says -d directory, hope nobody's using -desomething_else
131 if (*dir == '=' || *dir == ':')
133 if (!*dir && argc > 1) {
139 error("Flag -d must be followed by a directory name.");
146 * Now we know the directory containing 'record' and
147 * may do a prscore(). Exclude `-style' - it's a Qt option.
149 if (!strncmp(argv[1], "-s", 2) && strncmp(argv[1], "-style", 6)) {
157 ARGV0 = hname; /* save for possible stack trace */
159 panictrace_setsignals(TRUE);
168 jputchar('\0'); /* reset */
170 /* FIXME: shouldn't this be using nh_terminate() to free
171 up any memory allocated by initoptions() */
177 * Change directories before we initialize the window system so
178 * we can find the tile file.
188 check_linux_console();
191 /* Line like "OPTIONS=name:foo-@" may exist in config file.
192 * In this case, need to select random class,
193 * so must call setrandom() before initoptions().
199 ARGV0 = hname; /* save for possible stack trace */
201 panictrace_setsignals(TRUE);
204 exact_username = whoami();
207 * It seems you really want to play.
209 u.uhp = 1; /* prevent RIP on early quits */
210 program_state.preserve_locks = 1;
212 sethanguphandler((SIG_RET_TYPE) hangup);
215 process_options(argc, argv); /* command line options */
217 commit_windowchain();
219 init_nhwindows(&argc, argv); /* now we can set up window system */
228 if (!(catmore = nh_getenv("HACKPAGER"))
229 && !(catmore = nh_getenv("PAGER")))
236 /* wizard mode access is deferred until here */
237 set_playmode(); /* sets plname to "wizard" for wizard mode */
238 if (exact_username) {
240 * FIXME: this no longer works, ever since 3.3.0
241 * when plnamesuffix() was changed to find
242 * Name-Role-Race-Gender-Alignment. It removes
243 * all dashes rather than just the last one,
244 * regardless of whether whatever follows each
245 * dash matches role, race, gender, or alignment.
247 /* guard against user names with hyphens in them */
248 int len = (int) strlen(plname);
249 /* append the current role, if any, so that last dash is ours */
250 if (++len < (int) sizeof plname)
251 (void) strncat(strcat(plname, "-"), pl_character,
252 sizeof plname - len - 1);
254 /* strip role,race,&c suffix; calls askname() if plname[] is empty
255 or holds a generic user name like "player" or "games" */
259 /* use character name rather than lock letter for file names */
262 /* suppress interrupts while processing lock file */
263 (void) signal(SIGQUIT, SIG_IGN);
264 (void) signal(SIGINT, SIG_IGN);
267 dlb_init(); /* must be before newgame() */
270 * Initialize the vision system. This must be before mklev() on a
271 * new game or before a level restore on a saved game.
275 display_gamewindows();
278 * First, try to find and restore a save file for specified character.
279 * We'll return here if new game player_selection() renames the hero.
284 * getlock() complains and quits if there is already a game
285 * in progress for current character name (when locknum == 0)
286 * or if there are too many active games (when locknum > 0).
287 * When proceeding, it creates an empty <lockname>.0 file to
288 * designate the current game.
289 * getlock() constructs <lockname> based on the character
290 * name (for !locknum) or on first available of alock, block,
291 * clock, &c not currently in use in the playground directory
296 program_state.preserve_locks = 0; /* after getlock() */
299 if (*plname && (fd = restore_saved_game()) >= 0) {
300 const char *fq_save = fqname(SAVEF, SAVEPREFIX, 1);
302 (void) chmod(fq_save, 0); /* disallow parallel restores */
304 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
308 display_file(NEWS, FALSE);
309 iflags.news = FALSE; /* in case dorecover() fails */
313 pline("Restoring save file...");
315 pline("
\83Z
\81[
\83u
\83t
\83@
\83C
\83\8b\82ð
\95\9c\8c³
\92\86\81D
\81D
\81D");
316 mark_synch(); /* flush output */
318 resuming = TRUE; /* not starting new game */
320 if (discover || wizard) {
321 /* this seems like a candidate for paranoid_confirmation... */
323 if (yn("Do you want to keep the save file?") == 'n') {
325 if (yn("
\83Z
\81[
\83u
\83t
\83@
\83C
\83\8b\82ð
\8ec
\82µ
\82Ä
\82¨
\82«
\82Ü
\82·
\82©
\81H") == 'n') {
326 (void) delete_savefile();
328 (void) chmod(fq_save, FCMASK); /* back to readable */
329 nh_compress(fq_save);
336 boolean neednewlock = (!*plname);
337 /* new game: start by choosing role, race, etc;
338 player might change the hero's name while doing that,
339 in which case we try to restore under the new name
340 and skip selection this time if that didn't succeed */
341 if (!iflags.renameinprogress || iflags.defer_plname || neednewlock) {
345 if (neednewlock && *plname)
346 goto attempt_restore;
347 if (iflags.renameinprogress) {
348 /* player has renamed the hero while selecting role;
349 if locking alphabetically, the existing lock file
350 can still be used; otherwise, discard current one
351 and create another for the new character name */
353 delete_levelfile(0); /* remove empty lock file */
356 goto attempt_restore;
363 /* moveloop() never returns but isn't flagged NORETURN */
372 process_options(argc, argv)
381 while (argc > 1 && argv[1][0] == '-') {
384 l = (int) strlen(*argv);
385 /* must supply at least 4 chars to match "-XXXgraphics" */
389 switch (argv[0][1]) {
392 if ((argv[0][1] == 'D' && !argv[0][2])
393 || !strcmpi(*argv, "-debug")) {
394 wizard = TRUE, discover = FALSE;
395 } else if (!strncmpi(*argv, "-DECgraphics", l)) {
396 load_symset("DECGraphics", PRIMARY);
397 switch_symbols(TRUE);
399 raw_printf("Unknown option: %s", *argv);
404 discover = TRUE, wizard = FALSE;
414 (void) strncpy(plname, argv[0] + 2, sizeof plname - 1);
416 (void) strncpy(plname, str2ic(argv[0] + 2), sizeof(plname) - 1);
418 } else if (argc > 1) {
422 (void) strncpy(plname, argv[0], sizeof plname - 1);
424 (void) strncpy(plname, str2ic(argv[0]), sizeof(plname) - 1);
427 raw_print("Player name expected after -u");
432 if (!strncmpi(*argv, "-IBMgraphics", l)) {
433 load_symset("IBMGraphics", PRIMARY);
434 load_symset("RogueIBM", ROGUESET);
435 switch_symbols(TRUE);
437 raw_printf("Unknown option: %s", *argv);
440 case 'p': /* profession (role) */
442 if ((i = str2role(&argv[0][2])) >= 0)
444 } else if (argc > 1) {
447 if ((i = str2role(argv[0])) >= 0)
453 if ((i = str2race(&argv[0][2])) >= 0)
455 } else if (argc > 1) {
458 if ((i = str2race(argv[0])) >= 0)
462 case 'w': /* windowtype */
463 config_error_init(FALSE, "command line", FALSE);
464 choose_windows(&argv[0][2]);
471 if ((i = str2role(&argv[0][1])) >= 0) {
475 /* else raw_printf("Unknown option: %s", *argv); */
481 raw_printf("MAXPLAYERS are set in sysconf file.\n");
483 /* XXX This is deprecated in favor of SYSCF with MAXPLAYERS */
485 locknum = atoi(argv[1]);
487 #ifdef MAX_NR_OF_PLAYERS
488 /* limit to compile-time limit */
489 if (!locknum || locknum > MAX_NR_OF_PLAYERS)
490 locknum = MAX_NR_OF_PLAYERS;
493 /* let syscf override compile-time limit */
494 if (!locknum || (sysopt.maxplayers && locknum > sysopt.maxplayers))
495 locknum = sysopt.maxplayers;
505 if (dir /* User specified directory? */
507 && strcmp(dir, HACKDIR) /* and not the default? */
511 #pragma GCC diagnostic push
512 #pragma GCC diagnostic ignored "-Wunused-result"
513 (void) setgid(getgid());
514 (void) setuid(getuid()); /* Ron Wessels */
515 #pragma GCC diagnostic pop
518 /* non-default data files is a sign that scores may not be
519 * compatible, or perhaps that a binary not fitting this
520 * system's layout is being used.
522 #ifdef VAR_PLAYGROUND
523 int len = strlen(VAR_PLAYGROUND);
525 fqn_prefix[SCOREPREFIX] = (char *) alloc(len + 2);
526 Strcpy(fqn_prefix[SCOREPREFIX], VAR_PLAYGROUND);
527 if (fqn_prefix[SCOREPREFIX][len - 1] != '/') {
528 fqn_prefix[SCOREPREFIX][len] = '/';
529 fqn_prefix[SCOREPREFIX][len + 1] = '\0';
535 if (dir == (const char *) 0)
539 if (dir && chdir(dir) < 0) {
541 error("Cannot chdir to %s.", dir);
544 /* warn the player if we can't write the record file
545 * perhaps we should also test whether . is writable
546 * unfortunately the access system-call is worthless.
549 #ifdef VAR_PLAYGROUND
550 fqn_prefix[LEVELPREFIX] = fqn_prefix[SCOREPREFIX];
551 fqn_prefix[SAVEPREFIX] = fqn_prefix[SCOREPREFIX];
552 fqn_prefix[BONESPREFIX] = fqn_prefix[SCOREPREFIX];
553 fqn_prefix[LOCKPREFIX] = fqn_prefix[SCOREPREFIX];
554 fqn_prefix[TROUBLEPREFIX] = fqn_prefix[SCOREPREFIX];
556 check_recordfile(dir);
561 /* returns True iff we set plname[] to username which contains a hyphen */
566 * Who am i? Algorithm: 1. Use name as specified in NETHACKOPTIONS
567 * 2. Use $USER or $LOGNAME (if 1. fails)
568 * 3. Use getlogin() (if 2. fails)
569 * The resulting name is overridden by command line options.
570 * If everything fails, or if the resulting name is some generic
571 * account like "games", "play", "player", "hack" then eventually
573 * Note that we trust the user here; it is possible to play under
574 * somebody else's name.
577 register const char *s;
579 s = nh_getenv("USER");
581 s = nh_getenv("LOGNAME");
586 (void) strncpy(plname, s, sizeof plname - 1);
587 if (index(plname, '-'))
595 sethanguphandler(handler)
596 void FDECL((*handler), (int));
599 /* don't want reads to restart. If SA_RESTART is defined, we know
600 * sigaction exists and can be used to ensure reads won't restart.
601 * If it's not defined, assume reads do not restart. If reads restart
602 * and a signal occurs, the game won't do anything until the read
603 * succeeds (or the stream returns EOF, which might not happen if
604 * reading from, say, a window manager). */
605 struct sigaction sact;
607 (void) memset((genericptr_t) &sact, 0, sizeof sact);
608 sact.sa_handler = (SIG_RET_TYPE) handler;
609 (void) sigaction(SIGHUP, &sact, (struct sigaction *) 0);
611 (void) sigaction(SIGXCPU, &sact, (struct sigaction *) 0);
613 #else /* !SA_RESTART */
614 (void) signal(SIGHUP, (SIG_RET_TYPE) handler);
616 (void) signal(SIGXCPU, (SIG_RET_TYPE) handler);
618 #endif /* ?SA_RESTART */
626 * Display unix-specific help. Just show contents of the helpfile
627 * named by PORT_HELP.
629 display_file(PORT_HELP, TRUE);
633 /* validate wizard mode if player has requested access to it */
635 authorize_wizard_mode()
637 struct passwd *pw = get_unix_pw();
639 if (pw && sysopt.wizards && sysopt.wizards[0]) {
640 if (check_user_string(sysopt.wizards))
643 wiz_error_flag = TRUE; /* not being allowed into wizard mode */
650 if (wiz_error_flag) {
651 if (sysopt.wizards && sysopt.wizards[0]) {
652 char *tmp = build_english_list(sysopt.wizards);
654 pline("Only user%s %s may access debug (wizard) mode.",
655 index(sysopt.wizards, ' ') ? "s" : "", tmp);
657 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",
663 pline("Entering explore/discovery mode instead.");
665 pline("
\82©
\82í
\82è
\82É
\94
\8c©
\83\82\81[
\83h
\82Ö
\88Ú
\8ds
\82·
\82é
\81D");
666 wizard = 0, discover = 1; /* (paranoia) */
669 You("are in non-scoring explore/discovery mode.");
671 You("
\83X
\83R
\83A
\82Ì
\8dÚ
\82ç
\82È
\82¢
\94
\8c©
\83\82\81[
\83h
\82Å
\8bN
\93®
\82µ
\82½
\81D");
675 * Add a slash to any name not ending in /. There must
686 ptr = name + (strlen(name) - 1);
695 check_user_string(optstr)
698 struct passwd *pw = get_unix_pw();
703 if (optstr[0] == '*')
704 return TRUE; /* allow any user */
707 if (sysopt.check_plname)
710 pwname = pw->pw_name;
711 pwlen = strlen(pwname);
714 while (w + pwlen <= eop) {
721 if (!strncmp(w, pwname, pwlen)) {
722 if (!w[pwlen] || isspace(w[pwlen]))
725 while (*w && !isspace(*w))
731 static struct passwd *
736 static struct passwd *pw = (struct passwd *) 0;
739 return pw; /* cache answer */
741 uid = (unsigned) getuid();
745 if (pw && ((unsigned) pw->pw_uid != uid))
749 user = nh_getenv("USER");
752 if (pw && ((unsigned) pw->pw_uid != uid))
765 static char buf[BUFSZ];
766 struct passwd *pw = get_unix_pw();
771 (void)strcpy(buf, pw->pw_name);
780 port_insert_pastebuf(buf)
783 /* This should be replaced when there is a Cocoa port. */
786 FILE *PB = popen("/usr/bin/pbcopy","w");
788 errfmt = "Unable to start pbcopy (%d)\n";
793 /* Remove the trailing \n, carefully. */
794 if(buf[len-1] == '\n') len--;
796 /* XXX Sorry, I'm too lazy to write a loop for output this short. */
797 if(len!=fwrite(buf,1,len,PB)){
798 errfmt = "Error sending data to pbcopy (%d)\n";
805 errfmt = "Error finishing pbcopy (%d)\n";
808 raw_printf(errfmt,strerror(errno));