OSDN Git Service

patch sys/unix
[jnethack/source.git] / sys / unix / unixmain.c
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. */
4
5 /* main.c - Unix NetHack */
6
7 #include "hack.h"
8 #include "dlb.h"
9
10 #include <ctype.h>
11 #include <sys/stat.h>
12 #include <signal.h>
13 #include <pwd.h>
14 #ifndef O_RDONLY
15 #include <fcntl.h>
16 #endif
17
18 #ifdef XI18N
19 #include <X11/Xlocale.h>
20 #endif
21
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));
26 #else
27 extern struct passwd *FDECL(getpwuid, (int));
28 #endif
29 #endif
30 #endif
31 extern struct passwd *FDECL(getpwnam, (const char *));
32 #ifdef CHDIR
33 static void FDECL(chdirx, (const char *, BOOLEAN_P));
34 #endif /* CHDIR */
35 static boolean NDECL(whoami);
36 static void FDECL(process_options, (int, char **));
37
38 #ifdef _M_UNIX
39 extern void NDECL(check_sco_console);
40 extern void NDECL(init_sco_cons);
41 #endif
42 #ifdef __linux__
43 extern void NDECL(check_linux_console);
44 extern void NDECL(init_linux_cons);
45 #endif
46
47 static void NDECL(wd_message);
48 static boolean wiz_error_flag = FALSE;
49 static struct passwd *NDECL(get_unix_pw);
50
51 int
52 main(argc, argv)
53 int argc;
54 char *argv[];
55 {
56     register int fd;
57 #ifdef CHDIR
58     register char *dir;
59 #endif
60     boolean exact_username;
61     boolean resuming = FALSE; /* assume new game */
62
63     sys_early_init();
64
65 #ifdef XI18N
66     setlocale(LC_ALL, "");
67 #endif
68 #if defined(__APPLE__)
69     {
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, '/')))
78                 mac_exe++;
79             else
80                 mac_exe = argv[0];
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)) {
86                     mac_lhs_len =
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';
92                     chdir(mac_tmp);
93                 }
94                 free(mac_tmp);
95             }
96         }
97     }
98 #endif
99
100     hname = argv[0];
101     hackpid = getpid();
102     (void) umask(0777 & ~FCMASK);
103
104     choose_windows(DEFAULT_WINDOW_SYS);
105
106 #ifdef CHDIR /* otherwise no chdir() */
107              /*
108               * See if we must change directory to the playground.
109               * (Perhaps hack runs suid and playground is inaccessible
110               *  for the player.)
111               * The environment variable HACKDIR is overridden by a
112               *  -d command line option (must be the first option given)
113               */
114     dir = nh_getenv("NETHACKDIR");
115     if (!dir)
116         dir = nh_getenv("HACKDIR");
117 #endif
118     if (argc > 1) {
119 #ifdef CHDIR
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
123              */
124             argc--;
125             argv++;
126             dir = argv[0] + 2;
127             if (*dir == '=' || *dir == ':')
128                 dir++;
129             if (!*dir && argc > 1) {
130                 argc--;
131                 argv++;
132                 dir = argv[0];
133             }
134             if (!*dir)
135                 error("Flag -d must be followed by a directory name.");
136         }
137         if (argc > 1)
138 #endif /* CHDIR */
139
140             /*
141              * Now we know the directory containing 'record' and
142              * may do a prscore().  Exclude `-style' - it's a Qt option.
143              */
144             if (!strncmp(argv[1], "-s", 2) && strncmp(argv[1], "-style", 6)) {
145 #ifdef CHDIR
146                 chdirx(dir, 0);
147 #endif
148 #ifdef SYSCF
149                 initoptions();
150 #endif
151 #ifdef PANICTRACE
152                 ARGV0 = argv[0]; /* save for possible stack trace */
153 #ifndef NO_SIGNAL
154                 panictrace_setsignals(TRUE);
155 #endif
156 #endif
157 #if 0 /*JP*/
158                 prscore(argc, argv);
159 #else
160                 setkcode('I');
161                 initoptions();
162                 prscore(argc, argv);
163                 jputchar('\0'); /* reset */
164 #endif
165                 exit(EXIT_SUCCESS);
166             }
167     }
168
169 /*
170  * Change directories before we initialize the window system so
171  * we can find the tile file.
172  */
173 #ifdef CHDIR
174     chdirx(dir, 1);
175 #endif
176
177 #ifdef _M_UNIX
178     check_sco_console();
179 #endif
180 #ifdef __linux__
181     check_linux_console();
182 #endif
183 #if 1 /*JP*/
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().
187      */
188     setrandom();
189 #endif
190     initoptions();
191 #ifdef PANICTRACE
192     ARGV0 = argv[0]; /* save for possible stack trace */
193 #ifndef NO_SIGNAL
194     panictrace_setsignals(TRUE);
195 #endif
196 #endif
197     exact_username = whoami();
198
199     /*
200      * It seems you really want to play.
201      */
202     u.uhp = 1; /* prevent RIP on early quits */
203     program_state.preserve_locks = 1;
204 #ifndef NO_SIGNAL
205     sethanguphandler((SIG_RET_TYPE) hangup);
206 #endif
207
208     process_options(argc, argv); /* command line options */
209 #ifdef WINCHAIN
210     commit_windowchain();
211 #endif
212     init_nhwindows(&argc, argv); /* now we can set up window system */
213 #ifdef _M_UNIX
214     init_sco_cons();
215 #endif
216 #ifdef __linux__
217     init_linux_cons();
218 #endif
219
220 #ifdef DEF_PAGER
221     if (!(catmore = nh_getenv("HACKPAGER"))
222         && !(catmore = nh_getenv("PAGER")))
223         catmore = DEF_PAGER;
224 #endif
225 #ifdef MAIL
226     getmailstatus();
227 #endif
228
229     /* wizard mode access is deferred until here */
230     set_playmode(); /* sets plname to "wizard" for wizard mode */
231     if (exact_username) {
232         /*
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.
239          */
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);
246     }
247     /* strip role,race,&c suffix; calls askname() if plname[] is empty
248        or holds a generic user name like "player" or "games" */
249     plnamesuffix();
250
251     if (wizard) {
252         /* use character name rather than lock letter for file names */
253         locknum = 0;
254     } else {
255         /* suppress interrupts while processing lock file */
256         (void) signal(SIGQUIT, SIG_IGN);
257         (void) signal(SIGINT, SIG_IGN);
258     }
259     /*
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
268      * (for locknum > 0).
269      */
270     getlock();
271     program_state.preserve_locks = 0; /* after getlock() */
272
273     dlb_init(); /* must be before newgame() */
274
275     /*
276      *  Initialize the vision system.  This must be before mklev() on a
277      *  new game or before a level restore on a saved game.
278      */
279     vision_init();
280
281     display_gamewindows();
282
283 /*
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.
286  */
287 attempt_restore:
288     if ((fd = restore_saved_game()) >= 0) {
289         const char *fq_save = fqname(SAVEF, SAVEPREFIX, 1);
290
291         (void) chmod(fq_save, 0); /* disallow parallel restores */
292 #ifndef NO_SIGNAL
293         (void) signal(SIGINT, (SIG_RET_TYPE) done1);
294 #endif
295 #ifdef NEWS
296         if (iflags.news) {
297             display_file(NEWS, FALSE);
298             iflags.news = FALSE; /* in case dorecover() fails */
299         }
300 #endif
301 /*JP
302         pline("Restoring save file...");
303 */
304         pline("\83Z\81[\83u\83t\83@\83C\83\8b\82ð\95\9c\8c³\92\86\81D\81D\81D");
305         mark_synch(); /* flush output */
306         if (dorecover(fd)) {
307             resuming = TRUE; /* not starting new game */
308             wd_message();
309             if (discover || wizard) {
310 /*JP
311                 if (yn("Do you want to keep the save file?") == 'n')
312 */
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();
315                 else {
316                     (void) chmod(fq_save, FCMASK); /* back to readable */
317                     nh_compress(fq_save);
318                 }
319             }
320         }
321     }
322
323     if (!resuming) {
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) {
329             player_selection();
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 */
335                 if (!locknum) {
336                     delete_levelfile(0); /* remove empty lock file */
337                     getlock();
338                 }
339                 goto attempt_restore;
340             }
341         }
342         newgame();
343         wd_message();
344     }
345
346     moveloop(resuming);
347     exit(EXIT_SUCCESS);
348     /*NOTREACHED*/
349     return (0);
350 }
351
352 static void
353 process_options(argc, argv)
354 int argc;
355 char *argv[];
356 {
357     int i, l;
358
359     /*
360      * Process options.
361      */
362     while (argc > 1 && argv[1][0] == '-') {
363         argv++;
364         argc--;
365         l = (int) strlen(*argv);
366         /* must supply at least 4 chars to match "-XXXgraphics" */
367         if (l < 4)
368             l = 4;
369
370         switch (argv[0][1]) {
371         case 'D':
372         case 'd':
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);
379             } else {
380                 raw_printf("Unknown option: %s", *argv);
381             }
382             break;
383         case 'X':
384
385             discover = TRUE, wizard = FALSE;
386             break;
387 #ifdef NEWS
388         case 'n':
389             iflags.news = FALSE;
390             break;
391 #endif
392         case 'u':
393             if (argv[0][2])
394                 (void) strncpy(plname, argv[0] + 2, sizeof(plname) - 1);
395             else if (argc > 1) {
396                 argc--;
397                 argv++;
398                 (void) strncpy(plname, argv[0], sizeof(plname) - 1);
399             } else
400                 raw_print("Player name expected after -u");
401             break;
402         case 'I':
403         case 'i':
404             if (!strncmpi(*argv, "-IBMgraphics", l)) {
405                 load_symset("IBMGraphics", PRIMARY);
406                 load_symset("RogueIBM", ROGUESET);
407                 switch_symbols(TRUE);
408             } else {
409                 raw_printf("Unknown option: %s", *argv);
410             }
411             break;
412         case 'p': /* profession (role) */
413             if (argv[0][2]) {
414                 if ((i = str2role(&argv[0][2])) >= 0)
415                     flags.initrole = i;
416             } else if (argc > 1) {
417                 argc--;
418                 argv++;
419                 if ((i = str2role(argv[0])) >= 0)
420                     flags.initrole = i;
421             }
422             break;
423         case 'r': /* race */
424             if (argv[0][2]) {
425                 if ((i = str2race(&argv[0][2])) >= 0)
426                     flags.initrace = i;
427             } else if (argc > 1) {
428                 argc--;
429                 argv++;
430                 if ((i = str2race(argv[0])) >= 0)
431                     flags.initrace = i;
432             }
433             break;
434         case 'w': /* windowtype */
435             choose_windows(&argv[0][2]);
436             break;
437         case '@':
438             flags.randomall = 1;
439             break;
440         default:
441             if ((i = str2role(&argv[0][1])) >= 0) {
442                 flags.initrole = i;
443                 break;
444             }
445             /* else raw_printf("Unknown option: %s", *argv); */
446         }
447     }
448
449     /* XXX This is deprecated in favor of SYSCF with MAXPLAYERS.  Make
450      * an error in next release. */
451     if (argc > 1)
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;
457 #endif
458 #ifdef SYSCF
459     /* let syscf override compile-time limit */
460     if (!locknum || (sysopt.maxplayers && locknum > sysopt.maxplayers))
461         locknum = sysopt.maxplayers;
462 #endif
463 }
464
465 #ifdef CHDIR
466 static void
467 chdirx(dir, wr)
468 const char *dir;
469 boolean wr;
470 {
471     if (dir /* User specified directory? */
472 #ifdef HACKDIR
473         && strcmp(dir, HACKDIR) /* and not the default? */
474 #endif
475         ) {
476 #ifdef SECURE
477         (void) setgid(getgid());
478         (void) setuid(getuid()); /* Ron Wessels */
479 #endif
480     } else {
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.
484  */
485 #ifdef VAR_PLAYGROUND
486         int len = strlen(VAR_PLAYGROUND);
487
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';
493         }
494 #endif
495     }
496
497 #ifdef HACKDIR
498     if (dir == (const char *) 0)
499         dir = HACKDIR;
500 #endif
501
502     if (dir && chdir(dir) < 0) {
503         perror(dir);
504         error("Cannot chdir to %s.", dir);
505     }
506
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 */
510     if (wr) {
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];
517 #endif
518         check_recordfile(dir);
519     }
520 }
521 #endif /* CHDIR */
522
523 /* returns True iff we set plname[] to username which contains a hyphen */
524 static boolean
525 whoami()
526 {
527     /*
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
534      * we'll ask him.
535      * Note that we trust the user here; it is possible to play under
536      * somebody else's name.
537      */
538     if (!*plname) {
539         register const char *s;
540
541         s = nh_getenv("USER");
542         if (!s || !*s)
543             s = nh_getenv("LOGNAME");
544         if (!s || !*s)
545             s = getlogin();
546
547         if (s && *s) {
548             (void) strncpy(plname, s, sizeof plname - 1);
549             if (index(plname, '-'))
550                 return TRUE;
551         }
552     }
553     return FALSE;
554 }
555
556 void
557 sethanguphandler(handler)
558 void FDECL((*handler), (int));
559 {
560 #ifdef SA_RESTART
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;
568
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);
572 #ifdef SIGXCPU
573     (void) sigaction(SIGXCPU, &sact, (struct sigaction *) 0);
574 #endif
575 #else /* !SA_RESTART */
576     (void) signal(SIGHUP, (SIG_RET_TYPE) handler);
577 #ifdef SIGXCPU
578     (void) signal(SIGXCPU, (SIG_RET_TYPE) handler);
579 #endif
580 #endif /* ?SA_RESTART */
581 }
582
583 #ifdef PORT_HELP
584 void
585 port_help()
586 {
587     /*
588      * Display unix-specific help.   Just show contents of the helpfile
589      * named by PORT_HELP.
590      */
591     display_file(PORT_HELP, TRUE);
592 }
593 #endif
594
595 /* validate wizard mode if player has requested access to it */
596 boolean
597 authorize_wizard_mode()
598 {
599     struct passwd *pw = get_unix_pw();
600     if (pw && sysopt.wizards && sysopt.wizards[0]) {
601         if (check_user_string(sysopt.wizards))
602             return TRUE;
603     }
604     wiz_error_flag = TRUE; /* not being allowed into wizard mode */
605     return FALSE;
606 }
607
608 static void
609 wd_message()
610 {
611     if (wiz_error_flag) {
612         if (sysopt.wizards && sysopt.wizards[0]) {
613             char *tmp = build_english_list(sysopt.wizards);
614 #if 0 /*JP*/
615             pline("Only user%s %s may access debug (wizard) mode.",
616                   index(sysopt.wizards, ' ') ? "s" : "", tmp);
617 #else
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",
619                   tmp);
620 #endif
621             free(tmp);
622         } else
623 /*JP
624             pline("Entering explore/discovery mode instead.");
625 */
626             pline("\82©\82í\82è\82É\94­\8c©\83\82\81[\83h\82Ö\88Ú\8ds\82·\82é\81D");
627         wizard = 0, discover = 1; /* (paranoia) */
628     } else if (discover)
629 /*JP
630         You("are in non-scoring explore/discovery mode.");
631 */
632         You("\83X\83R\83A\82Ì\8dÚ\82ç\82È\82¢\94­\8c©\83\82\81[\83h\82Å\8bN\93®\82µ\82½\81D");
633 }
634
635 /*
636  * Add a slash to any name not ending in /. There must
637  * be room for the /
638  */
639 void
640 append_slash(name)
641 char *name;
642 {
643     char *ptr;
644
645     if (!*name)
646         return;
647     ptr = name + (strlen(name) - 1);
648     if (*ptr != '/') {
649         *++ptr = '/';
650         *++ptr = '\0';
651     }
652     return;
653 }
654
655 boolean
656 check_user_string(optstr)
657 char *optstr;
658 {
659     struct passwd *pw = get_unix_pw();
660     int pwlen;
661     char *eop, *w;
662     if (optstr[0] == '*')
663         return TRUE; /* allow any user */
664     if (!pw)
665         return FALSE;
666     pwlen = strlen(pw->pw_name);
667     eop = eos(optstr);
668     w = optstr;
669     while (w + pwlen <= eop) {
670         if (!*w)
671             break;
672         if (isspace(*w)) {
673             w++;
674             continue;
675         }
676         if (!strncmp(w, pw->pw_name, pwlen)) {
677             if (!w[pwlen] || isspace(w[pwlen]))
678                 return TRUE;
679         }
680         while (*w && !isspace(*w))
681             w++;
682     }
683     return FALSE;
684 }
685
686 static struct passwd *
687 get_unix_pw()
688 {
689     char *user;
690     unsigned uid;
691     static struct passwd *pw = (struct passwd *) 0;
692
693     if (pw)
694         return pw; /* cache answer */
695
696     uid = (unsigned) getuid();
697     user = getlogin();
698     if (user) {
699         pw = getpwnam(user);
700         if (pw && ((unsigned) pw->pw_uid != uid))
701             pw = 0;
702     }
703     if (pw == 0) {
704         user = nh_getenv("USER");
705         if (user) {
706             pw = getpwnam(user);
707             if (pw && ((unsigned) pw->pw_uid != uid))
708                 pw = 0;
709         }
710         if (pw == 0) {
711             pw = getpwuid(uid);
712         }
713     }
714     return pw;
715 }
716
717 /*unixmain.c*/