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;
67 #if 1 /*JP*//* iconv
\82Ì
\8f\89\8aú
\89»
\82Ì
\82½
\82ß
\82É
\88ê
\93x
\82Í
\8cÄ
\82Ñ
\8fo
\82·
\95K
\97v
\82ª
\82 \82é */
72 setlocale(LC_ALL, "");
74 #if defined(__APPLE__)
76 /* special hack to change working directory to a resource fork when
77 running from finder --sam */
78 #define MAC_PATH_VALUE ".app/Contents/MacOS/"
79 char mac_cwd[1024], *mac_exe = argv[0], *mac_tmp;
80 int arg0_len = strlen(mac_exe), mac_tmp_len, mac_lhs_len = 0;
81 getcwd(mac_cwd, 1024);
82 if (mac_exe[0] == '/' && !strcmp(mac_cwd, "/")) {
83 if ((mac_exe = strrchr(mac_exe, '/')))
87 mac_tmp_len = (strlen(mac_exe) * 2) + strlen(MAC_PATH_VALUE);
88 if (mac_tmp_len <= arg0_len) {
89 mac_tmp = malloc(mac_tmp_len + 1);
90 sprintf(mac_tmp, "%s%s%s", mac_exe, MAC_PATH_VALUE, mac_exe);
91 if (!strcmp(argv[0] + (arg0_len - mac_tmp_len), mac_tmp)) {
93 (arg0_len - mac_tmp_len) + strlen(mac_exe) + 5;
94 if (mac_lhs_len > mac_tmp_len - 1)
95 mac_tmp = realloc(mac_tmp, mac_lhs_len);
96 strncpy(mac_tmp, argv[0], mac_lhs_len);
97 mac_tmp[mac_lhs_len] = '\0';
108 (void) umask(0777 & ~FCMASK);
110 choose_windows(DEFAULT_WINDOW_SYS);
112 #ifdef CHDIR /* otherwise no chdir() */
114 * See if we must change directory to the playground.
115 * (Perhaps hack runs suid and playground is inaccessible
117 * The environment variable HACKDIR is overridden by a
118 * -d command line option (must be the first option given).
120 dir = nh_getenv("NETHACKDIR");
122 dir = nh_getenv("HACKDIR");
125 if (argcheck(argc, argv, ARG_VERSION))
128 if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') {
129 /* avoid matching "-dec" for DECgraphics; since the man page
130 * says -d directory, hope nobody's using -desomething_else
135 if (*dir == '=' || *dir == ':')
137 if (!*dir && argc > 1) {
143 error("Flag -d must be followed by a directory name.");
150 * Now we know the directory containing 'record' and
151 * may do a prscore(). Exclude `-style' - it's a Qt option.
153 if (!strncmp(argv[1], "-s", 2) && strncmp(argv[1], "-style", 6)) {
161 ARGV0 = hname; /* save for possible stack trace */
163 panictrace_setsignals(TRUE);
172 jputchar('\0'); /* reset */
174 /* FIXME: shouldn't this be using nh_terminate() to free
175 up any memory allocated by initoptions() */
181 * Change directories before we initialize the window system so
182 * we can find the tile file.
192 check_linux_console();
195 /* Line like "OPTIONS=name:foo-@" may exist in config file.
196 * In this case, need to select random class,
197 * so must call setrandom() before initoptions().
203 ARGV0 = hname; /* save for possible stack trace */
205 panictrace_setsignals(TRUE);
208 exact_username = whoami();
211 * It seems you really want to play.
213 u.uhp = 1; /* prevent RIP on early quits */
214 program_state.preserve_locks = 1;
216 sethanguphandler((SIG_RET_TYPE) hangup);
219 process_options(argc, argv); /* command line options */
221 commit_windowchain();
223 init_nhwindows(&argc, argv); /* now we can set up window system */
232 if (!(catmore = nh_getenv("HACKPAGER"))
233 && !(catmore = nh_getenv("PAGER")))
240 /* wizard mode access is deferred until here */
241 set_playmode(); /* sets plname to "wizard" for wizard mode */
242 if (exact_username) {
244 * FIXME: this no longer works, ever since 3.3.0
245 * when plnamesuffix() was changed to find
246 * Name-Role-Race-Gender-Alignment. It removes
247 * all dashes rather than just the last one,
248 * regardless of whether whatever follows each
249 * dash matches role, race, gender, or alignment.
251 /* guard against user names with hyphens in them */
252 int len = (int) strlen(plname);
253 /* append the current role, if any, so that last dash is ours */
254 if (++len < (int) sizeof plname)
255 (void) strncat(strcat(plname, "-"), pl_character,
256 sizeof plname - len - 1);
258 /* strip role,race,&c suffix; calls askname() if plname[] is empty
259 or holds a generic user name like "player" or "games" */
263 /* use character name rather than lock letter for file names */
266 /* suppress interrupts while processing lock file */
267 (void) signal(SIGQUIT, SIG_IGN);
268 (void) signal(SIGINT, SIG_IGN);
271 dlb_init(); /* must be before newgame() */
274 * Initialize the vision system. This must be before mklev() on a
275 * new game or before a level restore on a saved game.
279 display_gamewindows();
282 * First, try to find and restore a save file for specified character.
283 * We'll return here if new game player_selection() renames the hero.
288 * getlock() complains and quits if there is already a game
289 * in progress for current character name (when locknum == 0)
290 * or if there are too many active games (when locknum > 0).
291 * When proceeding, it creates an empty <lockname>.0 file to
292 * designate the current game.
293 * getlock() constructs <lockname> based on the character
294 * name (for !locknum) or on first available of alock, block,
295 * clock, &c not currently in use in the playground directory
300 program_state.preserve_locks = 0; /* after getlock() */
303 if (*plname && (fd = restore_saved_game()) >= 0) {
304 const char *fq_save = fqname(SAVEF, SAVEPREFIX, 1);
306 (void) chmod(fq_save, 0); /* disallow parallel restores */
308 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
312 display_file(NEWS, FALSE);
313 iflags.news = FALSE; /* in case dorecover() fails */
317 pline("Restoring save file...");
319 pline("
\83Z
\81[
\83u
\83t
\83@
\83C
\83\8b\82ð
\95\9c\8c³
\92\86\81D
\81D
\81D");
320 mark_synch(); /* flush output */
322 resuming = TRUE; /* not starting new game */
324 if (discover || wizard) {
325 /* this seems like a candidate for paranoid_confirmation... */
327 if (yn("Do you want to keep the save file?") == 'n') {
329 if (yn("
\83Z
\81[
\83u
\83t
\83@
\83C
\83\8b\82ð
\8ec
\82µ
\82Ä
\82¨
\82«
\82Ü
\82·
\82©
\81H") == 'n') {
330 (void) delete_savefile();
332 (void) chmod(fq_save, FCMASK); /* back to readable */
333 nh_compress(fq_save);
340 boolean neednewlock = (!*plname);
341 /* new game: start by choosing role, race, etc;
342 player might change the hero's name while doing that,
343 in which case we try to restore under the new name
344 and skip selection this time if that didn't succeed */
345 if (!iflags.renameinprogress || iflags.defer_plname || neednewlock) {
349 if (neednewlock && *plname)
350 goto attempt_restore;
351 if (iflags.renameinprogress) {
352 /* player has renamed the hero while selecting role;
353 if locking alphabetically, the existing lock file
354 can still be used; otherwise, discard current one
355 and create another for the new character name */
357 delete_levelfile(0); /* remove empty lock file */
360 goto attempt_restore;
367 /* moveloop() never returns but isn't flagged NORETURN */
376 process_options(argc, argv)
385 while (argc > 1 && argv[1][0] == '-') {
388 l = (int) strlen(*argv);
389 /* must supply at least 4 chars to match "-XXXgraphics" */
393 switch (argv[0][1]) {
396 if ((argv[0][1] == 'D' && !argv[0][2])
397 || !strcmpi(*argv, "-debug")) {
398 wizard = TRUE, discover = FALSE;
399 } else if (!strncmpi(*argv, "-DECgraphics", l)) {
400 load_symset("DECGraphics", PRIMARY);
401 switch_symbols(TRUE);
403 raw_printf("Unknown option: %s", *argv);
408 discover = TRUE, wizard = FALSE;
418 (void) strncpy(plname, argv[0] + 2, sizeof plname - 1);
420 (void) strncpy(plname, str2ic(argv[0] + 2), sizeof(plname) - 1);
422 } else if (argc > 1) {
426 (void) strncpy(plname, argv[0], sizeof plname - 1);
428 (void) strncpy(plname, str2ic(argv[0]), sizeof(plname) - 1);
431 raw_print("Player name expected after -u");
436 if (!strncmpi(*argv, "-IBMgraphics", l)) {
437 load_symset("IBMGraphics", PRIMARY);
438 load_symset("RogueIBM", ROGUESET);
439 switch_symbols(TRUE);
441 raw_printf("Unknown option: %s", *argv);
444 case 'p': /* profession (role) */
446 if ((i = str2role(&argv[0][2])) >= 0)
448 } else if (argc > 1) {
451 if ((i = str2role(argv[0])) >= 0)
457 if ((i = str2race(&argv[0][2])) >= 0)
459 } else if (argc > 1) {
462 if ((i = str2race(argv[0])) >= 0)
466 case 'w': /* windowtype */
467 config_error_init(FALSE, "command line", FALSE);
468 choose_windows(&argv[0][2]);
475 if ((i = str2role(&argv[0][1])) >= 0) {
479 /* else raw_printf("Unknown option: %s", *argv); */
485 raw_printf("MAXPLAYERS are set in sysconf file.\n");
487 /* XXX This is deprecated in favor of SYSCF with MAXPLAYERS */
489 locknum = atoi(argv[1]);
491 #ifdef MAX_NR_OF_PLAYERS
492 /* limit to compile-time limit */
493 if (!locknum || locknum > MAX_NR_OF_PLAYERS)
494 locknum = MAX_NR_OF_PLAYERS;
497 /* let syscf override compile-time limit */
498 if (!locknum || (sysopt.maxplayers && locknum > sysopt.maxplayers))
499 locknum = sysopt.maxplayers;
509 if (dir /* User specified directory? */
511 && strcmp(dir, HACKDIR) /* and not the default? */
515 _pragma_ignore(-Wunused-result)
516 (void) setgid(getgid());
517 (void) setuid(getuid()); /* Ron Wessels */
521 /* non-default data files is a sign that scores may not be
522 * compatible, or perhaps that a binary not fitting this
523 * system's layout is being used.
525 #ifdef VAR_PLAYGROUND
526 int len = strlen(VAR_PLAYGROUND);
528 fqn_prefix[SCOREPREFIX] = (char *) alloc(len + 2);
529 Strcpy(fqn_prefix[SCOREPREFIX], VAR_PLAYGROUND);
530 if (fqn_prefix[SCOREPREFIX][len - 1] != '/') {
531 fqn_prefix[SCOREPREFIX][len] = '/';
532 fqn_prefix[SCOREPREFIX][len + 1] = '\0';
538 if (dir == (const char *) 0)
542 if (dir && chdir(dir) < 0) {
544 error("Cannot chdir to %s.", dir);
547 /* warn the player if we can't write the record file
548 * perhaps we should also test whether . is writable
549 * unfortunately the access system-call is worthless.
552 #ifdef VAR_PLAYGROUND
553 fqn_prefix[LEVELPREFIX] = fqn_prefix[SCOREPREFIX];
554 fqn_prefix[SAVEPREFIX] = fqn_prefix[SCOREPREFIX];
555 fqn_prefix[BONESPREFIX] = fqn_prefix[SCOREPREFIX];
556 fqn_prefix[LOCKPREFIX] = fqn_prefix[SCOREPREFIX];
557 fqn_prefix[TROUBLEPREFIX] = fqn_prefix[SCOREPREFIX];
559 check_recordfile(dir);
564 /* returns True iff we set plname[] to username which contains a hyphen */
569 * Who am i? Algorithm: 1. Use name as specified in NETHACKOPTIONS
570 * 2. Use $USER or $LOGNAME (if 1. fails)
571 * 3. Use getlogin() (if 2. fails)
572 * The resulting name is overridden by command line options.
573 * If everything fails, or if the resulting name is some generic
574 * account like "games", "play", "player", "hack" then eventually
576 * Note that we trust the user here; it is possible to play under
577 * somebody else's name.
580 register const char *s;
582 s = nh_getenv("USER");
584 s = nh_getenv("LOGNAME");
589 (void) strncpy(plname, s, sizeof plname - 1);
590 if (index(plname, '-'))
598 sethanguphandler(handler)
599 void FDECL((*handler), (int));
602 /* don't want reads to restart. If SA_RESTART is defined, we know
603 * sigaction exists and can be used to ensure reads won't restart.
604 * If it's not defined, assume reads do not restart. If reads restart
605 * and a signal occurs, the game won't do anything until the read
606 * succeeds (or the stream returns EOF, which might not happen if
607 * reading from, say, a window manager). */
608 struct sigaction sact;
610 (void) memset((genericptr_t) &sact, 0, sizeof sact);
611 sact.sa_handler = (SIG_RET_TYPE) handler;
612 (void) sigaction(SIGHUP, &sact, (struct sigaction *) 0);
614 (void) sigaction(SIGXCPU, &sact, (struct sigaction *) 0);
616 #else /* !SA_RESTART */
617 (void) signal(SIGHUP, (SIG_RET_TYPE) handler);
619 (void) signal(SIGXCPU, (SIG_RET_TYPE) handler);
621 #endif /* ?SA_RESTART */
629 * Display unix-specific help. Just show contents of the helpfile
630 * named by PORT_HELP.
632 display_file(PORT_HELP, TRUE);
636 /* validate wizard mode if player has requested access to it */
638 authorize_wizard_mode()
640 struct passwd *pw = get_unix_pw();
642 if (pw && sysopt.wizards && sysopt.wizards[0]) {
643 if (check_user_string(sysopt.wizards))
646 wiz_error_flag = TRUE; /* not being allowed into wizard mode */
653 if (wiz_error_flag) {
654 if (sysopt.wizards && sysopt.wizards[0]) {
655 char *tmp = build_english_list(sysopt.wizards);
657 pline("Only user%s %s may access debug (wizard) mode.",
658 index(sysopt.wizards, ' ') ? "s" : "", tmp);
660 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",
666 pline("Entering explore/discovery mode instead.");
668 pline("
\82©
\82í
\82è
\82É
\94
\8c©
\83\82\81[
\83h
\82Ö
\88Ú
\8ds
\82·
\82é
\81D");
669 wizard = 0, discover = 1; /* (paranoia) */
672 You("are in non-scoring explore/discovery mode.");
674 You("
\83X
\83R
\83A
\82Ì
\8dÚ
\82ç
\82È
\82¢
\94
\8c©
\83\82\81[
\83h
\82Å
\8bN
\93®
\82µ
\82½
\81D");
678 * Add a slash to any name not ending in /. There must
689 ptr = name + (strlen(name) - 1);
698 check_user_string(optstr)
701 struct passwd *pw = get_unix_pw();
706 if (optstr[0] == '*')
707 return TRUE; /* allow any user */
710 if (sysopt.check_plname)
713 pwname = pw->pw_name;
714 pwlen = strlen(pwname);
717 while (w + pwlen <= eop) {
724 if (!strncmp(w, pwname, pwlen)) {
725 if (!w[pwlen] || isspace(w[pwlen]))
728 while (*w && !isspace(*w))
734 static struct passwd *
739 static struct passwd *pw = (struct passwd *) 0;
742 return pw; /* cache answer */
744 uid = (unsigned) getuid();
748 if (pw && ((unsigned) pw->pw_uid != uid))
752 user = nh_getenv("USER");
755 if (pw && ((unsigned) pw->pw_uid != uid))
768 static char buf[BUFSZ];
769 struct passwd *pw = get_unix_pw();
774 (void)strcpy(buf, pw->pw_name);
783 port_insert_pastebuf(buf)
786 /* This should be replaced when there is a Cocoa port. */
789 FILE *PB = popen("/usr/bin/pbcopy","w");
791 errfmt = "Unable to start pbcopy (%d)\n";
796 /* Remove the trailing \n, carefully. */
797 if(buf[len-1] == '\n') len--;
799 /* XXX Sorry, I'm too lazy to write a loop for output this short. */
800 if(len!=fwrite(buf,1,len,PB)){
801 errfmt = "Error sending data to pbcopy (%d)\n";
808 errfmt = "Error finishing pbcopy (%d)\n";
811 raw_printf(errfmt,strerror(errno));