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