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
main(argc,argv)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
process_options(argc,argv)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
chdirx(dir,wr)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
whoami()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
port_help()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
wd_message()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*/
495