1    | /*	SCCS Id: @(#)unixmain.c	3.3	97/01/22	*/
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 <sys/stat.h>
11   | #include <signal.h>
12   | #include <pwd.h>
13   | #ifndef O_RDONLY
14   | #include <fcntl.h>
15   | #endif
16   | 
17   | #if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX)
18   | # if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__))
19   | #  if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX)
20   | extern struct passwd *FDECL(getpwuid,(uid_t));
21   | #  else
22   | extern struct passwd *FDECL(getpwuid,(int));
23   | #  endif
24   | # endif
25   | #endif
26   | extern struct passwd *FDECL(getpwnam,(const char *));
27   | #ifdef CHDIR
28   | static void FDECL(chdirx, (const char *,BOOLEAN_P));
29   | #endif /* CHDIR */
30   | static boolean NDECL(whoami);
31   | static void FDECL(process_options, (int, char **));
32   | 
33   | #ifdef _M_UNIX
34   | extern void NDECL(check_sco_console);
35   | extern void NDECL(init_sco_cons);
36   | #endif
37   | #ifdef __linux__
38   | extern void NDECL(check_linux_console);
39   | extern void NDECL(init_linux_cons);
40   | #endif
41   | 
42   | static void NDECL(wd_message);
43   | #ifdef WIZARD
44   | static boolean wiz_error_flag = FALSE;
45   | #endif
46   | 
47   | int
48   | main(argc,argv)
49   | int argc;
50   | char *argv[];
51   | {
52   | 	register int fd;
53   | #ifdef CHDIR
54   | 	register char *dir;
55   | #endif
56   | 	boolean exact_username;
57   | 
58   | 	hname = argv[0];
59   | 	hackpid = getpid();
60   | 	(void) umask(0777 & ~FCMASK);
61   | 
62   | 	choose_windows(DEFAULT_WINDOW_SYS);
63   | 
64   | #ifdef CHDIR			/* otherwise no chdir() */
65   | 	/*
66   | 	 * See if we must change directory to the playground.
67   | 	 * (Perhaps hack runs suid and playground is inaccessible
68   | 	 *  for the player.)
69   | 	 * The environment variable HACKDIR is overridden by a
70   | 	 *  -d command line option (must be the first option given)
71   | 	 */
72   | 	dir = nh_getenv("NETHACKDIR");
73   | 	if (!dir) dir = nh_getenv("HACKDIR");
74   | #endif
75   | 	if(argc > 1) {
76   | #ifdef CHDIR
77   | 	    if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') {
78   | 		/* avoid matching "-dec" for DECgraphics; since the man page
79   | 		 * says -d directory, hope nobody's using -desomething_else
80   | 		 */
81   | 		argc--;
82   | 		argv++;
83   | 		dir = argv[0]+2;
84   | 		if(*dir == '=' || *dir == ':') dir++;
85   | 		if(!*dir && argc > 1) {
86   | 			argc--;
87   | 			argv++;
88   | 			dir = argv[0];
89   | 		}
90   | 		if(!*dir)
91   | 		    error("Flag -d must be followed by a directory name.");
92   | 	    }
93   | 	    if (argc > 1)
94   | #endif /* CHDIR */
95   | 
96   | 	    /*
97   | 	     * Now we know the directory containing 'record' and
98   | 	     * may do a prscore().  Exclude `-style' - it's a Qt option.
99   | 	     */
100  | 	    if (!strncmp(argv[1], "-s", 2) && strncmp(argv[1], "-style", 6)) {
101  | #ifdef CHDIR
102  | 		chdirx(dir,0);
103  | #endif
104  | 		prscore(argc, argv);
105  | 		exit(EXIT_SUCCESS);
106  | 	    }
107  | 	}
108  | 
109  | 	/*
110  | 	 * Find the creation date of this game,
111  | 	 * so as to avoid restoring outdated savefiles.
112  | 	 */
113  | 	gethdate(hname);
114  | 
115  | 	/*
116  | 	 * We cannot do chdir earlier, otherwise gethdate will fail.
117  | 	 * Change directories before we initialize the window system so
118  | 	 * we can find the tile file.
119  | 	 */
120  | #ifdef CHDIR
121  | 	chdirx(dir,1);
122  | #endif
123  | 
124  | #ifdef _M_UNIX
125  | 	check_sco_console();
126  | #endif
127  | #ifdef __linux__
128  | 	check_linux_console();
129  | #endif
130  | 	initoptions();
131  | 	init_nhwindows(&argc,argv);
132  | 	exact_username = whoami();
133  | #ifdef _M_UNIX
134  | 	init_sco_cons();
135  | #endif
136  | #ifdef __linux__
137  | 	init_linux_cons();
138  | #endif
139  | 
140  | 	/*
141  | 	 * It seems you really want to play.
142  | 	 */
143  | 	u.uhp = 1;	/* prevent RIP on early quits */
144  | 	(void) signal(SIGHUP, (SIG_RET_TYPE) hangup);
145  | #ifdef SIGXCPU
146  | 	(void) signal(SIGXCPU, (SIG_RET_TYPE) hangup);
147  | #endif
148  | 
149  | 	process_options(argc, argv);	/* command line options */
150  | 
151  | #ifdef DEF_PAGER
152  | 	if(!(catmore = nh_getenv("HACKPAGER")) && !(catmore = nh_getenv("PAGER")))
153  | 		catmore = DEF_PAGER;
154  | #endif
155  | #ifdef MAIL
156  | 	getmailstatus();
157  | #endif
158  | #ifdef WIZARD
159  | 	if (wizard)
160  | 		Strcpy(plname, "wizard");
161  | 	else
162  | #endif
163  | 	if(!*plname || !strncmp(plname, "player", 4)
164  | 		    || !strncmp(plname, "games", 4)) {
165  | 		askname();
166  | 	} else if (exact_username) {
167  | 		/* guard against user names with hyphens in them */
168  | 		int len = strlen(plname);
169  | 		/* append the current role, if any, so that last dash is ours */
170  | 		if (++len < sizeof plname)
171  | 			(void)strncat(strcat(plname, "-"),
172  | 				      pl_character, sizeof plname - len - 1);
173  | 	}
174  | 	plnamesuffix();		/* strip suffix from name; calls askname() */
175  | 				/* again if suffix was whole name */
176  | 				/* accepts any suffix */
177  | #ifdef WIZARD
178  | 	if(!wizard) {
179  | #endif
180  | 		/*
181  | 		 * check for multiple games under the same name
182  | 		 * (if !locknum) or check max nr of players (otherwise)
183  | 		 */
184  | 		(void) signal(SIGQUIT,SIG_IGN);
185  | 		(void) signal(SIGINT,SIG_IGN);
186  | 		if(!locknum)
187  | 			Sprintf(lock, "%d%s", (int)getuid(), plname);
188  | 		getlock();
189  | #ifdef WIZARD
190  | 	} else {
191  | 		Sprintf(lock, "%d%s", (int)getuid(), plname);
192  | 		getlock();
193  | 	}
194  | #endif /* WIZARD */
195  | 
196  | 	dlb_init();	/* must be before newgame() */
197  | 
198  | 	/*
199  | 	 * Initialization of the boundaries of the mazes
200  | 	 * Both boundaries have to be even.
201  | 	 */
202  | 	x_maze_max = COLNO-1;
203  | 	if (x_maze_max % 2)
204  | 		x_maze_max--;
205  | 	y_maze_max = ROWNO-1;
206  | 	if (y_maze_max % 2)
207  | 		y_maze_max--;
208  | 
209  | 	/*
210  | 	 *  Initialize the vision system.  This must be before mklev() on a
211  | 	 *  new game or before a level restore on a saved game.
212  | 	 */
213  | 	vision_init();
214  | 
215  | 	display_gamewindows();
216  | 
217  | 	if ((fd = restore_saved_game()) >= 0) {
218  | #ifdef WIZARD
219  | 		/* Since wizard is actually flags.debug, restoring might
220  | 		 * overwrite it.
221  | 		 */
222  | 		boolean remember_wiz_mode = wizard;
223  | #endif
224  | 		const char *fq_save = fqname(SAVEF, SAVEPREFIX, 0);
225  | 
226  | 		(void) chmod(fq_save,0);	/* disallow parallel restores */
227  | 		(void) signal(SIGINT, (SIG_RET_TYPE) done1);
228  | #ifdef NEWS
229  | 		if(iflags.news) {
230  | 		    display_file(NEWS, FALSE);
231  | 		    iflags.news = FALSE; /* in case dorecover() fails */
232  | 		}
233  | #endif
234  | 		pline("Restoring save file...");
235  | 		mark_synch();	/* flush output */
236  | 		if(!dorecover(fd))
237  | 			goto not_recovered;
238  | #ifdef WIZARD
239  | 		if(!wizard && remember_wiz_mode) wizard = TRUE;
240  | #endif
241  | 		check_special_room(FALSE);
242  | 		wd_message();
243  | 
244  | 		if (discover || wizard) {
245  | 			if(yn("Do you want to keep the save file?") == 'n')
246  | 			    (void) delete_savefile();
247  | 			else {
248  | 			    (void) chmod(fq_save,FCMASK); /* back to readable */
249  | 			    compress(fq_save);
250  | 			}
251  | 		}
252  | 		flags.move = 0;
253  | 	} else {
254  | not_recovered:
255  | 		player_selection();
256  | 		newgame();
257  | 		wd_message();
258  | 
259  | 		flags.move = 0;
260  | 		set_wear();
261  | 		(void) pickup(1);
262  | 	}
263  | 
264  | 	moveloop();
265  | 	exit(EXIT_SUCCESS);
266  | 	/*NOTREACHED*/
267  | 	return(0);
268  | }
269  | 
270  | static void
271  | process_options(argc, argv)
272  | int argc;
273  | char *argv[];
274  | {
275  | 	int i;
276  | 
277  | 
278  | 	/*
279  | 	 * Process options.
280  | 	 */
281  | 	while(argc > 1 && argv[1][0] == '-'){
282  | 		argv++;
283  | 		argc--;
284  | 		switch(argv[0][1]){
285  | 		case 'D':
286  | #ifdef WIZARD
287  | 			{
288  | 			  char *user;
289  | 			  int uid;
290  | 			  struct passwd *pw = (struct passwd *)0;
291  | 
292  | 			  uid = getuid();
293  | 			  user = getlogin();
294  | 			  if (user) {
295  | 			      pw = getpwnam(user);
296  | 			      if (pw && (pw->pw_uid != uid)) pw = 0;
297  | 			  }
298  | 			  if (pw == 0) {
299  | 			      user = nh_getenv("USER");
300  | 			      if (user) {
301  | 				  pw = getpwnam(user);
302  | 				  if (pw && (pw->pw_uid != uid)) pw = 0;
303  | 			      }
304  | 			      if (pw == 0) {
305  | 				  pw = getpwuid(uid);
306  | 			      }
307  | 			  }
308  | 			  if (pw && !strcmp(pw->pw_name,WIZARD)) {
309  | 			      wizard = TRUE;
310  | 			      break;
311  | 			  }
312  | 			}
313  | 			/* otherwise fall thru to discover */
314  | 			wiz_error_flag = TRUE;
315  | #endif
316  | 		case 'X':
317  | 			discover = TRUE;
318  | 			break;
319  | #ifdef NEWS
320  | 		case 'n':
321  | 			iflags.news = FALSE;
322  | 			break;
323  | #endif
324  | 		case 'u':
325  | 			if(argv[0][2])
326  | 			  (void) strncpy(plname, argv[0]+2, sizeof(plname)-1);
327  | 			else if(argc > 1) {
328  | 			  argc--;
329  | 			  argv++;
330  | 			  (void) strncpy(plname, argv[0], sizeof(plname)-1);
331  | 			} else
332  | 				raw_print("Player name expected after -u");
333  | 			break;
334  | 		case 'I':
335  | 		case 'i':
336  | 			if (!strncmpi(argv[0]+1, "IBM", 3))
337  | 				switch_graphics(IBM_GRAPHICS);
338  | 			break;
339  | 	    /*  case 'D': */
340  | 		case 'd':
341  | 			if (!strncmpi(argv[0]+1, "DEC", 3))
342  | 				switch_graphics(DEC_GRAPHICS);
343  | 			break;
344  | 		case 'p': /* profession (role) */
345  | 			if (argv[0][2]) {
346  | 			    if ((i = str2role(&argv[0][2])) >= 0)
347  | 			    	flags.initrole = i;
348  | 			} else if (argc > 1) {
349  | 				argc--;
350  | 				argv++;
351  | 			    if ((i = str2role(argv[0])) >= 0)
352  | 			    	flags.initrole = i;
353  | 			}
354  | 			break;
355  | 		case 'r': /* race */
356  | 			if (argv[0][2]) {
357  | 			    if ((i = str2race(&argv[0][2])) >= 0)
358  | 			    	flags.initrace = i;
359  | 			} else if (argc > 1) {
360  | 				argc--;
361  | 				argv++;
362  | 			    if ((i = str2race(argv[0])) >= 0)
363  | 			    	flags.initrace = i;
364  | 			}
365  | 			break;
366  | 		default:
367  | 			if ((i = str2role(&argv[0][1])) >= 0) {
368  | 			    flags.initrole = i;
369  | 				break;
370  | 			}
371  | 			/* else raw_printf("Unknown option: %s", *argv); */
372  | 		}
373  | 	}
374  | 
375  | 	if(argc > 1)
376  | 		locknum = atoi(argv[1]);
377  | #ifdef MAX_NR_OF_PLAYERS
378  | 	if(!locknum || locknum > MAX_NR_OF_PLAYERS)
379  | 		locknum = MAX_NR_OF_PLAYERS;
380  | #endif
381  | }
382  | 
383  | #ifdef CHDIR
384  | static void
385  | chdirx(dir, wr)
386  | const char *dir;
387  | boolean wr;
388  | {
389  | 	if (dir					/* User specified directory? */
390  | # ifdef HACKDIR
391  | 	       && strcmp(dir, HACKDIR)		/* and not the default? */
392  | # endif
393  | 		) {
394  | # ifdef SECURE
395  | 	    (void) setgid(getgid());
396  | 	    (void) setuid(getuid());		/* Ron Wessels */
397  | # endif
398  | 	} else {
399  | 	    /* non-default data files is a sign that scores may not be
400  | 	     * compatible, or perhaps that a binary not fitting this
401  | 	     * system's layout is being used.
402  | 	     */
403  | # ifdef VAR_PLAYGROUND
404  | 	    int len = strlen(VAR_PLAYGROUND);
405  | 
406  | 	    fqn_prefix[SCOREPREFIX] = (char *)alloc(len+2);
407  | 	    Strcpy(fqn_prefix[SCOREPREFIX], VAR_PLAYGROUND);
408  | 	    if (fqn_prefix[SCOREPREFIX][len-1] != '/') {
409  | 		fqn_prefix[SCOREPREFIX][len] = '/';
410  | 		fqn_prefix[SCOREPREFIX][len+1] = '\0';
411  | 	    }
412  | # endif
413  | 	}
414  | 
415  | # ifdef HACKDIR
416  | 	if (dir == (const char *)0)
417  | 	    dir = HACKDIR;
418  | # endif
419  | 
420  | 	if (dir && chdir(dir) < 0) {
421  | 	    perror(dir);
422  | 	    error("Cannot chdir to %s.", dir);
423  | 	}
424  | 
425  | 	/* warn the player if we can't write the record file */
426  | 	/* perhaps we should also test whether . is writable */
427  | 	/* unfortunately the access system-call is worthless */
428  | 	if (wr) {
429  | # ifdef VAR_PLAYGROUND
430  | 	    fqn_prefix[LEVELPREFIX] = fqn_prefix[SCOREPREFIX];
431  | 	    fqn_prefix[SAVEPREFIX] = fqn_prefix[SCOREPREFIX];
432  | 	    fqn_prefix[BONESPREFIX] = fqn_prefix[SCOREPREFIX];
433  | 	    fqn_prefix[LOCKPREFIX] = fqn_prefix[SCOREPREFIX];
434  | # endif
435  | 	    check_recordfile(dir);
436  | 	}
437  | }
438  | #endif /* CHDIR */
439  | 
440  | static boolean
441  | whoami() {
442  | 	/*
443  | 	 * Who am i? Algorithm: 1. Use name as specified in NETHACKOPTIONS
444  | 	 *			2. Use $USER or $LOGNAME	(if 1. fails)
445  | 	 *			3. Use getlogin()		(if 2. fails)
446  | 	 * The resulting name is overridden by command line options.
447  | 	 * If everything fails, or if the resulting name is some generic
448  | 	 * account like "games", "play", "player", "hack" then eventually
449  | 	 * we'll ask him.
450  | 	 * Note that we trust the user here; it is possible to play under
451  | 	 * somebody else's name.
452  | 	 */
453  | 	register char *s;
454  | 
455  | 	if (*plname) return FALSE;
456  | 	if(/* !*plname && */ (s = nh_getenv("USER")))
457  | 		(void) strncpy(plname, s, sizeof(plname)-1);
458  | 	if(!*plname && (s = nh_getenv("LOGNAME")))
459  | 		(void) strncpy(plname, s, sizeof(plname)-1);
460  | 	if(!*plname && (s = getlogin()))
461  | 		(void) strncpy(plname, s, sizeof(plname)-1);
462  | 	return TRUE;
463  | }
464  | 
465  | #ifdef PORT_HELP
466  | void
467  | port_help()
468  | {
469  | 	/*
470  | 	 * Display unix-specific help.   Just show contents of the helpfile
471  | 	 * named by PORT_HELP.
472  | 	 */
473  | 	display_file(PORT_HELP, TRUE);
474  | }
475  | #endif
476  | 
477  | static void
478  | wd_message()
479  | {
480  | #ifdef WIZARD
481  | 	if (wiz_error_flag) {
482  | 		pline("Only user \"%s\" may access debug (wizard) mode.",
483  | # ifndef KR1ED
484  | 			WIZARD);
485  | # else
486  | 			WIZARD_NAME);
487  | # endif
488  | 		pline("Entering discovery mode instead.");
489  | 	} else
490  | #endif
491  | 	if (discover)
492  | 		You("are in non-scoring discovery mode.");
493  | }
494  | /*unixmain.c*/