1 /*	SCCS Id: @(#)vmsmain.c	3.3	1997/01/22	*/
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed.  See license for details. */
4 /* main.c - VMS NetHack */
5 
6 #include "hack.h"
7 #include "dlb.h"
8 
9 #include <signal.h>
10 
11 static void NDECL(whoami);
12 static void FDECL(process_options, (int, char **));
13 static void NDECL(byebye);
14 #ifndef SAVE_ON_FATAL_ERROR
15 # ifndef __DECC
16 #  define vms_handler_type int
17 # else
18 #  define vms_handler_type unsigned int
19 # endif
20 extern void FDECL(VAXC$ESTABLISH, (vms_handler_type (*)(genericptr_t,genericptr_t)));
21 static vms_handler_type FDECL(vms_handler, (genericptr_t,genericptr_t));
22 #include <ssdef.h>	/* system service status codes */
23 #endif
24 
25 static void NDECL(wd_message);
26 #ifdef WIZARD
27 static boolean wiz_error_flag = FALSE;
28 #endif
29 
30 int
main(argc,argv)31 main(argc,argv)
32 int argc;
33 char *argv[];
34 {
35 	register int fd;
36 #ifdef CHDIR
37 	register char *dir;
38 #endif
39 
40 #ifdef SECURE	/* this should be the very first code executed */
41 	privoff();
42 	fflush((FILE *)0);	/* force stdio to init itself */
43 	privon();
44 #endif
45 
46 	atexit(byebye);
47 	hname = argv[0];
48 	gethdate(hname);		/* find executable's creation date */
49 	hname = vms_basename(hname);	/* name used in 'usage' type messages */
50 	hackpid = getpid();
51 	(void) umask(0);
52 
53 	choose_windows(DEFAULT_WINDOW_SYS);
54 
55 #ifdef CHDIR			/* otherwise no chdir() */
56 	/*
57 	 * See if we must change directory to the playground.
58 	 * (Perhaps hack is installed with privs and playground is
59 	 *  inaccessible for the player.)
60 	 * The logical name HACKDIR is overridden by a
61 	 *  -d command line option (must be the first option given)
62 	 */
63 	dir = nh_getenv("NETHACKDIR");
64 	if (!dir) dir = nh_getenv("HACKDIR");
65 #endif
66 	if(argc > 1) {
67 #ifdef CHDIR
68 	    if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') {
69 		/* avoid matching "-dec" for DECgraphics; since the man page
70 		 * says -d directory, hope nobody's using -desomething_else
71 		 */
72 		argc--;
73 		argv++;
74 		dir = argv[0]+2;
75 		if(*dir == '=' || *dir == ':') dir++;
76 		if(!*dir && argc > 1) {
77 			argc--;
78 			argv++;
79 			dir = argv[0];
80 		}
81 		if(!*dir)
82 		    error("Flag -d must be followed by a directory name.");
83 	    }
84 	    if (argc > 1)
85 #endif /* CHDIR */
86 
87 	    /*
88 	     * Now we know the directory containing 'record' and
89 	     * may do a prscore().
90 	     */
91 	    if (!strncmp(argv[1], "-s", 2)) {
92 #ifdef CHDIR
93 		chdirx(dir, FALSE);
94 #endif
95 		prscore(argc, argv);
96 		exit(EXIT_SUCCESS);
97 	    }
98 	}
99 
100 #ifdef CHDIR
101 	/* move to the playground directory; 'termcap' might be found there */
102 	chdirx(dir, TRUE);
103 #endif
104 
105 #ifdef SECURE
106 	/* disable installed privs while loading nethack.cnf and termcap,
107 	   and also while initializing terminal [$assign("TT:")]. */
108 	privoff();
109 #endif
110 	initoptions();
111 	init_nhwindows(&argc, argv);
112 	whoami();
113 #ifdef SECURE
114 	privon();
115 #endif
116 
117 	/*
118 	 * It seems you really want to play.
119 	 */
120 	u.uhp = 1;	/* prevent RIP on early quits */
121 #ifndef SAVE_ON_FATAL_ERROR
122 	/* used to clear hangup stuff while still giving standard traceback */
123 	VAXC$ESTABLISH(vms_handler);
124 #endif
125 	(void) signal(SIGHUP, (SIG_RET_TYPE) hangup);
126 
127 	process_options(argc, argv);	/* command line options */
128 
129 #ifdef WIZARD
130 	if (wizard)
131 		Strcpy(plname, "wizard");
132 	else
133 #endif
134 	if (!*plname || !strncmpi(plname, "games", 4) ||
135 	    !strcmpi(plname, "nethack"))
136 		askname();
137 	plnamesuffix();		/* strip suffix from name; calls askname() */
138 				/* again if suffix was whole name */
139 				/* accepts any suffix */
140 #ifdef WIZARD
141 	if(!wizard) {
142 #endif
143 		/*
144 		 * check for multiple games under the same name
145 		 * (if !locknum) or check max nr of players (otherwise)
146 		 */
147 		(void) signal(SIGQUIT,SIG_IGN);
148 		(void) signal(SIGINT,SIG_IGN);
149 		if(!locknum)
150 			Sprintf(lock, "_%u%s", (unsigned)getuid(), plname);
151 		getlock();
152 #ifdef WIZARD
153 	} else {
154 		Sprintf(lock, "_%u%s", (unsigned)getuid(), plname);
155 		getlock();
156 	}
157 #endif /* WIZARD */
158 
159 	dlb_init();	/* must be before newgame() */
160 
161 	/*
162 	 * Initialization of the boundaries of the mazes
163 	 * Both boundaries have to be even.
164 	 */
165 	x_maze_max = COLNO-1;
166 	if (x_maze_max % 2)
167 		x_maze_max--;
168 	y_maze_max = ROWNO-1;
169 	if (y_maze_max % 2)
170 		y_maze_max--;
171 
172 	/*
173 	 *  Initialize the vision system.  This must be before mklev() on a
174 	 *  new game or before a level restore on a saved game.
175 	 */
176 	vision_init();
177 
178 	display_gamewindows();
179 
180 	if ((fd = restore_saved_game()) >= 0) {
181 #ifdef WIZARD
182 		/* Since wizard is actually flags.debug, restoring might
183 		 * overwrite it.
184 		 */
185 		boolean remember_wiz_mode = wizard;
186 #endif
187 		const char *fq_save = fqname(SAVEF, SAVEPREFIX, 0);
188 
189 		(void) chmod(fq_save,0);	/* disallow parallel restores */
190 		(void) signal(SIGINT, (SIG_RET_TYPE) done1);
191 #ifdef NEWS
192 		if(iflags.news) {
193 		    display_file(NEWS, FALSE);
194 		    iflags.news = FALSE; /* in case dorecover() fails */
195 		}
196 #endif
197 		pline("Restoring save file...");
198 		mark_synch();	/* flush output */
199 		if(!dorecover(fd))
200 			goto not_recovered;
201 #ifdef WIZARD
202 		if(!wizard && remember_wiz_mode) wizard = TRUE;
203 #endif
204 		check_special_room(FALSE);
205 		wd_message();
206 
207 		if (discover || wizard) {
208 			if (yn("Do you want to keep the save file?") == 'n')
209 			    (void) delete_savefile();
210 			else
211 			    (void) chmod(fq_save,FCMASK); /* back to readable */
212 		}
213 
214 		flags.move = 0;
215 	} else {
216 not_recovered:
217 		player_selection();
218 		newgame();
219 		wd_message();
220 
221 		flags.move = 0;
222 		set_wear();
223 		(void) pickup(1);
224 	}
225 
226 	moveloop();
227 	exit(EXIT_SUCCESS);
228 	/*NOTREACHED*/
229 	return(0);
230 }
231 
232 static void
process_options(argc,argv)233 process_options(argc, argv)
234 int argc;
235 char *argv[];
236 {
237 	int i;
238 
239 
240 	/*
241 	 * Process options.
242 	 */
243 	while(argc > 1 && argv[1][0] == '-'){
244 		argv++;
245 		argc--;
246 		switch(argv[0][1]){
247 		case 'D':
248 #ifdef WIZARD
249 			if(!strcmpi(nh_getenv("USER"), WIZARD_NAME)) {
250 				wizard = TRUE;
251 				break;
252 			}
253 			/* otherwise fall thru to discover */
254 			wiz_error_flag = TRUE;
255 #endif /* WIZARD */
256 		case 'X':
257 		case 'x':
258 			discover = TRUE;
259 			break;
260 #ifdef NEWS
261 		case 'n':
262 			iflags.news = FALSE;
263 			break;
264 #endif
265 		case 'u':
266 			if(argv[0][2])
267 			  (void) strncpy(plname, argv[0]+2, sizeof(plname)-1);
268 			else if(argc > 1) {
269 			  argc--;
270 			  argv++;
271 			  (void) strncpy(plname, argv[0], sizeof(plname)-1);
272 			} else
273 				raw_print("Player name expected after -u");
274 			break;
275 		case 'I':
276 		case 'i':
277 			if (!strncmpi(argv[0]+1, "IBM", 3))
278 				switch_graphics(IBM_GRAPHICS);
279 			break;
280 	    /*  case 'D': */
281 		case 'd':
282 			if (!strncmpi(argv[0]+1, "DEC", 3))
283 				switch_graphics(DEC_GRAPHICS);
284 			break;
285 		case 'p': /* profession (role) */
286 			if (argv[0][2]) {
287 			    if ((i = str2role(&argv[0][2])) >= 0)
288 			    	flags.initrole = i;
289 			} else if (argc > 1) {
290 				argc--;
291 				argv++;
292 			    if ((i = str2role(argv[0])) >= 0)
293 			    	flags.initrole = i;
294 			}
295 			break;
296 		case 'r': /* race */
297 			if (argv[0][2]) {
298 			    if ((i = str2race(&argv[0][2])) >= 0)
299 			    	flags.initrace = i;
300 			} else if (argc > 1) {
301 				argc--;
302 				argv++;
303 			    if ((i = str2race(argv[0])) >= 0)
304 			    	flags.initrace = i;
305 			}
306 			break;
307 		default:
308 			if ((i = str2role(&argv[0][1])) >= 0) {
309 			    flags.initrole = i;
310 				break;
311 			}
312 			/* else raw_printf("Unknown option: %s", *argv); */
313 		}
314 	}
315 
316 	if(argc > 1)
317 		locknum = atoi(argv[1]);
318 #ifdef MAX_NR_OF_PLAYERS
319 	if(!locknum || locknum > MAX_NR_OF_PLAYERS)
320 		locknum = MAX_NR_OF_PLAYERS;
321 #endif
322 }
323 
324 #ifdef CHDIR
325 void
chdirx(dir,wr)326 chdirx(dir, wr)
327 const char *dir;
328 boolean wr;
329 {
330 # ifndef HACKDIR
331 	static const char *defdir = ".";
332 # else
333 	static const char *defdir = HACKDIR;
334 
335 	if(dir == (const char *)0)
336 		dir = defdir;
337 	else if (wr && !same_dir(HACKDIR, dir))
338 		/* If we're playing anywhere other than HACKDIR, turn off any
339 		   privs we may have been installed with. */
340 		privoff();
341 # endif
342 
343 	if(dir && chdir(dir) < 0) {
344 		perror(dir);
345 		error("Cannot chdir to %s.", dir);
346 	}
347 
348 	/* warn the player if we can't write the record file */
349 	if (wr) check_recordfile(dir);
350 
351 	defdir = dir;
352 }
353 #endif /* CHDIR */
354 
355 static void
whoami()356 whoami()
357 {
358 	/*
359 	 * Who am i? Algorithm: 1. Use name as specified in NETHACKOPTIONS
360 	 *			2. Use lowercase of $USER  (if 1. fails)
361 	 * The resulting name is overridden by command line options.
362 	 * If everything fails, or if the resulting name is some generic
363 	 * account like "games" then eventually we'll ask him.
364 	 * Note that we trust the user here; it is possible to play under
365 	 * somebody else's name.
366 	 */
367 	register char *s;
368 
369 	if (!*plname && (s = nh_getenv("USER")))
370 		(void) lcase(strncpy(plname, s, sizeof(plname)-1));
371 }
372 
373 static void
byebye()374 byebye()
375 {
376     /*	Different versions of both VAX C and GNU C use different return types
377 	for signal functions.  Return type 'int' along with the explicit casts
378 	below satisfy the most combinations of compiler vs <signal.h>.
379      */
380     int (*hup)();
381 #ifdef SHELL
382     extern unsigned long dosh_pid, mail_pid;
383     extern unsigned long FDECL(sys$delprc,(unsigned long *,const genericptr_t));
384 
385     /* clean up any subprocess we've spawned that may still be hanging around */
386     if (dosh_pid) (void) sys$delprc(&dosh_pid, 0), dosh_pid = 0;
387     if (mail_pid) (void) sys$delprc(&mail_pid, 0), mail_pid = 0;
388 #endif
389 
390     /* SIGHUP doesn't seem to do anything on VMS, so we fudge it here... */
391     hup = (int(*)()) signal(SIGHUP, SIG_IGN);
392     if (!program_state.exiting++ &&
393 	hup != (int(*)()) SIG_DFL && hup != (int(*)()) SIG_IGN)
394 	(void) (*hup)();
395 
396 #ifdef CHDIR
397     (void) chdir(getenv("PATH"));
398 #endif
399 }
400 
401 #ifndef SAVE_ON_FATAL_ERROR
402 /* Condition handler to prevent byebye's hangup simulation
403    from saving the game after a fatal error has occurred.  */
404 /*ARGSUSED*/
405 static vms_handler_type		/* should be `unsigned long', but the -*/
vms_handler(sigargs,mechargs)406 vms_handler(sigargs, mechargs)	/*+ prototype in <signal.h> is screwed */
407 genericptr_t sigargs, mechargs;	/* [0] is argc, [1..argc] are the real args */
408 {
409     unsigned long condition = ((unsigned long *)sigargs)[1];
410 
411     if (condition == SS$_ACCVIO		/* access violation */
412      || (condition >= SS$_ASTFLT && condition <= SS$_TBIT)
413      || (condition >= SS$_ARTRES && condition <= SS$_INHCHME)) {
414 	program_state.done_hup = TRUE;	/* pretend hangup has been attempted */
415 # if defined(WIZARD) && !defined(BETA)
416 	if (wizard)
417 # endif /*WIZARD && !BETA*/
418 # if defined(WIZARD) ||  defined(BETA)
419 	    abort();	/* enter the debugger */
420 # endif /*WIZARD || BETA*/
421     }
422     return SS$_RESIGNAL;
423 }
424 #endif
425 
426 #ifdef PORT_HELP
427 void
port_help()428 port_help()
429 {
430 	/*
431 	 * Display VMS-specific help.   Just show contents of the helpfile
432 	 * named by PORT_HELP.
433 	 */
434 	display_file(PORT_HELP, TRUE);
435 }
436 #endif /* PORT_HELP */
437 
438 static void
wd_message()439 wd_message()
440 {
441 #ifdef WIZARD
442 	if (wiz_error_flag) {
443 		pline("Only user \"%s\" may access debug (wizard) mode.",
444 			WIZARD_NAME);
445 		pline("Entering discovery mode instead.");
446 	} else
447 #endif
448 	if (discover)
449 		You("are in non-scoring discovery mode.");
450 }
451 
452 /*vmsmain.c*/
453