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