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