OSDN Git Service

upgrade to 3.6.2
[jnethack/source.git] / sys / vms / vmsmain.c
1 /* NetHack 3.6  vmsmain.c       $NHDT-Date: 1449801742 2015/12/11 02:42:22 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.32 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2011. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 /* main.c - VMS NetHack */
6
7 #include "hack.h"
8 #include "dlb.h"
9
10 #include <signal.h>
11
12 static void NDECL(whoami);
13 static void FDECL(process_options, (int, char **));
14 static void NDECL(byebye);
15 #ifndef SAVE_ON_FATAL_ERROR
16 #ifndef __DECC
17 #define vms_handler_type int
18 #else
19 #define vms_handler_type unsigned int
20 #endif
21 extern void FDECL(VAXC$ESTABLISH,
22                          (vms_handler_type (*) (genericptr_t, genericptr_t)));
23 static vms_handler_type FDECL(vms_handler, (genericptr_t, genericptr_t));
24 #include <ssdef.h> /* system service status codes */
25 #endif
26
27 static void NDECL(wd_message);
28 static boolean wiz_error_flag = FALSE;
29
30 int
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     boolean resuming = FALSE; /* assume new game */
40
41 #ifdef SECURE /* this should be the very first code executed */
42     privoff();
43     fflush((FILE *) 0); /* force stdio to init itself */
44     privon();
45 #endif
46
47     sys_early_init();
48
49     atexit(byebye);
50     hname = argv[0];
51     hname = vms_basename(hname); /* name used in 'usage' type messages */
52     hackpid = getpid();
53     (void) umask(0);
54
55     choose_windows(DEFAULT_WINDOW_SYS);
56
57 #ifdef CHDIR /* otherwise no chdir() */
58     /*
59      * See if we must change directory to the playground.
60      * (Perhaps hack is installed with privs and playground is
61      *  inaccessible for the player.)
62      * The logical name HACKDIR is overridden by a
63      *  -d command line option (must be the first option given)
64      */
65     dir = nh_getenv("NETHACKDIR");
66     if (!dir)
67         dir = nh_getenv("HACKDIR");
68 #endif
69     if (argc > 1) {
70 #ifdef CHDIR
71         if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') {
72             /* avoid matching "-dec" for DECgraphics; since the man page
73              * says -d directory, hope nobody's using -desomething_else
74              */
75             argc--;
76             argv++;
77             dir = argv[0] + 2;
78             if (*dir == '=' || *dir == ':')
79                 dir++;
80             if (!*dir && argc > 1) {
81                 argc--;
82                 argv++;
83                 dir = argv[0];
84             }
85             if (!*dir)
86                 error("Flag -d must be followed by a directory name.");
87         }
88         if (argc > 1)
89 #endif /* CHDIR */
90
91             /*
92              * Now we know the directory containing 'record' and
93              * may do a prscore().
94              */
95             if (!strncmp(argv[1], "-s", 2)) {
96 #ifdef CHDIR
97                 chdirx(dir, FALSE);
98 #endif
99 #ifdef SYSCF
100                 initoptions();
101 #endif
102                 prscore(argc, argv);
103                 exit(EXIT_SUCCESS);
104             }
105     }
106
107 #ifdef CHDIR
108     /* move to the playground directory; 'termcap' might be found there */
109     chdirx(dir, TRUE);
110 #endif
111
112 #ifdef SECURE
113     /* disable installed privs while loading nethack.cnf and termcap,
114        and also while initializing terminal [$assign("TT:")]. */
115     privoff();
116 #endif
117     initoptions();
118     init_nhwindows(&argc, argv);
119     whoami();
120 #ifdef SECURE
121     privon();
122 #endif
123
124     /*
125      * It seems you really want to play.
126      */
127     u.uhp = 1; /* prevent RIP on early quits */
128 #ifndef SAVE_ON_FATAL_ERROR
129     /* used to clear hangup stuff while still giving standard traceback */
130     VAXC$ESTABLISH(vms_handler);
131 #endif
132     sethanguphandler(hangup);
133
134     process_options(argc, argv); /* command line options */
135
136     /* wizard mode access is deferred until here */
137     set_playmode(); /* sets plname to "wizard" for wizard mode */
138     /* strip role,race,&c suffix; calls askname() if plname[] is empty
139        or holds a generic user name like "player" or "games" */
140     plnamesuffix();
141
142     if (wizard) {
143         /* use character name rather than lock letter for file names */
144         locknum = 0;
145     } else {
146         /* suppress interrupts while processing lock file */
147         (void) signal(SIGQUIT, SIG_IGN);
148         (void) signal(SIGINT, SIG_IGN);
149     }
150     /*
151      * getlock() complains and quits if there is already a game
152      * in progress for current character name (when locknum == 0)
153      * or if there are too many active games (when locknum > 0).
154      * When proceeding, it creates an empty <lockname>.0 file to
155      * designate the current game.
156      * getlock() constructs <lockname> based on the character
157      * name (for !locknum) or on first available of alock, block,
158      * clock, &c not currently in use in the playground directory
159      * (for locknum > 0).
160      */
161     getlock();
162
163     dlb_init(); /* must be before newgame() */
164
165     /*
166      *  Initialize the vision system.  This must be before mklev() on a
167      *  new game or before a level restore on a saved game.
168      */
169     vision_init();
170
171     display_gamewindows();
172
173 /*
174  * First, try to find and restore a save file for specified character.
175  * We'll return here if new game player_selection() renames the hero.
176  */
177 attempt_restore:
178     if ((fd = restore_saved_game()) >= 0) {
179         const char *fq_save = fqname(SAVEF, SAVEPREFIX, 1);
180
181         (void) chmod(fq_save, 0); /* disallow parallel restores */
182         (void) signal(SIGINT, (SIG_RET_TYPE) done1);
183 #ifdef NEWS
184         if (iflags.news) {
185             display_file(NEWS, FALSE);
186             iflags.news = FALSE; /* in case dorecover() fails */
187         }
188 #endif
189         pline("Restoring save file...");
190         mark_synch(); /* flush output */
191         if (dorecover(fd)) {
192             resuming = TRUE; /* not starting new game */
193             wd_message();
194             if (discover || wizard) {
195                 if (yn("Do you want to keep the save file?") == 'n')
196                     (void) delete_savefile();
197                 else
198                     (void) chmod(fq_save, FCMASK); /* back to readable */
199             }
200         }
201     }
202
203     if (!resuming) {
204         /* new game:  start by choosing role, race, etc;
205            player might change the hero's name while doing that,
206            in which case we try to restore under the new name
207            and skip selection this time if that didn't succeed */
208         if (!iflags.renameinprogress) {
209             player_selection();
210             if (iflags.renameinprogress) {
211                 /* player has renamed the hero while selecting role;
212                    if locking alphabetically, the existing lock file
213                    can still be used; otherwise, discard current one
214                    and create another for the new character name */
215                 if (!locknum) {
216                     delete_levelfile(0); /* remove empty lock file */
217                     getlock();
218                 }
219                 goto attempt_restore;
220             }
221         }
222         newgame();
223         wd_message();
224     }
225
226     moveloop(resuming);
227     exit(EXIT_SUCCESS);
228     /*NOTREACHED*/
229     return 0;
230 }
231
232 static void
233 process_options(argc, argv)
234 int argc;
235 char *argv[];
236 {
237     int i;
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             wizard = TRUE, discover = FALSE;
248             break;
249         case 'X':
250         case 'x':
251             discover = TRUE, wizard = FALSE;
252             break;
253 #ifdef NEWS
254         case 'n':
255             iflags.news = FALSE;
256             break;
257 #endif
258         case 'u':
259             if (argv[0][2])
260                 (void) strncpy(plname, argv[0] + 2, sizeof(plname) - 1);
261             else if (argc > 1) {
262                 argc--;
263                 argv++;
264                 (void) strncpy(plname, argv[0], sizeof(plname) - 1);
265             } else
266                 raw_print("Player name expected after -u");
267             break;
268         case 'I':
269         case 'i':
270             if (!strncmpi(argv[0] + 1, "IBM", 3)) {
271                 load_symset("IBMGraphics", PRIMARY);
272                 load_symset("RogueIBM", ROGUESET);
273                 switch_symbols(TRUE);
274             }
275             break;
276         /*  case 'D': */
277         case 'd':
278             if (!strncmpi(argv[0] + 1, "DEC", 3)) {
279                 load_symset("DECGraphics", PRIMARY);
280                 switch_symbols(TRUE);
281             }
282             break;
283         case 'p': /* profession (role) */
284             if (argv[0][2]) {
285                 if ((i = str2role(&argv[0][2])) >= 0)
286                     flags.initrole = i;
287             } else if (argc > 1) {
288                 argc--;
289                 argv++;
290                 if ((i = str2role(argv[0])) >= 0)
291                     flags.initrole = i;
292             }
293             break;
294         case 'r': /* race */
295             if (argv[0][2]) {
296                 if ((i = str2race(&argv[0][2])) >= 0)
297                     flags.initrace = i;
298             } else if (argc > 1) {
299                 argc--;
300                 argv++;
301                 if ((i = str2race(argv[0])) >= 0)
302                     flags.initrace = i;
303             }
304             break;
305         case '@':
306             flags.randomall = 1;
307             break;
308         default:
309             if ((i = str2role(&argv[0][1])) >= 0) {
310                 flags.initrole = i;
311                 break;
312             }
313             /* else raw_printf("Unknown option: %s", *argv); */
314         }
315     }
316
317     if (argc > 1)
318         locknum = atoi(argv[1]);
319 #ifdef MAX_NR_OF_PLAYERS
320     if (!locknum || locknum > MAX_NR_OF_PLAYERS)
321         locknum = MAX_NR_OF_PLAYERS;
322 #endif
323 }
324
325 #ifdef CHDIR
326 void
327 chdirx(dir, wr)
328 const char *dir;
329 boolean wr;
330 {
331 #ifndef HACKDIR
332     static const char *defdir = ".";
333 #else
334     static const char *defdir = HACKDIR;
335
336     if (dir == (const char *) 0)
337         dir = defdir;
338     else if (wr && !same_dir(HACKDIR, dir))
339         /* If we're playing anywhere other than HACKDIR, turn off any
340            privs we may have been installed with. */
341         privoff();
342 #endif
343
344     if (dir && chdir(dir) < 0) {
345         perror(dir);
346         error("Cannot chdir to %s.", dir);
347     }
348
349     /* warn the player if we can't write the record file */
350     if (wr)
351         check_recordfile(dir);
352
353     defdir = dir;
354 }
355 #endif /* CHDIR */
356
357 static void
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
376 byebye()
377 {
378     void FDECL((*hup), (int) );
379 #ifdef SHELL
380     extern unsigned long dosh_pid, mail_pid;
381     extern unsigned long FDECL(sys$delprc,
382                                (unsigned long *, const genericptr_t));
383
384     /* clean up any subprocess we've spawned that may still be hanging around
385      */
386     if (dosh_pid)
387         (void) sys$delprc(&dosh_pid, (genericptr_t) 0), dosh_pid = 0;
388     if (mail_pid)
389         (void) sys$delprc(&mail_pid, (genericptr_t) 0), mail_pid = 0;
390 #endif
391
392     /* SIGHUP doesn't seem to do anything on VMS, so we fudge it here... */
393     hup = (void FDECL((*), (int) )) signal(SIGHUP, SIG_IGN);
394     if (!program_state.exiting++ && hup != (void FDECL((*), (int) )) SIG_DFL
395         && hup != (void FDECL((*), (int) )) SIG_IGN) {
396         (*hup)(SIGHUP);
397     }
398
399 #ifdef CHDIR
400     (void) chdir(getenv("PATH"));
401 #endif
402 }
403
404 #ifndef SAVE_ON_FATAL_ERROR
405 /* Condition handler to prevent byebye's hangup simulation
406    from saving the game after a fatal error has occurred.  */
407 /*ARGSUSED*/
408 static vms_handler_type         /* should be `unsigned long', but the -*/
409 vms_handler(sigargs, mechargs)  /*+ prototype in <signal.h> is screwed */
410 genericptr_t sigargs, mechargs; /* [0] is argc, [1..argc] are the real args */
411 {
412     unsigned long condition = ((unsigned long *) sigargs)[1];
413
414     if (condition == SS$_ACCVIO /* access violation */
415         || (condition >= SS$_ASTFLT && condition <= SS$_TBIT)
416         || (condition >= SS$_ARTRES && condition <= SS$_INHCHME)) {
417         program_state.done_hup = TRUE; /* pretend hangup has been attempted */
418 #ifndef BETA
419         if (wizard)
420 #endif
421             abort(); /* enter the debugger */
422     }
423     return SS$_RESIGNAL;
424 }
425 #endif
426
427 void
428 sethanguphandler(handler)
429 void FDECL((*handler), (int));
430 {
431     (void) signal(SIGHUP, (SIG_RET_TYPE) handler);
432 }
433
434 #ifdef PORT_HELP
435 void
436 port_help()
437 {
438     /*
439      * Display VMS-specific help.   Just show contents of the helpfile
440      * named by PORT_HELP.
441      */
442     display_file(PORT_HELP, TRUE);
443 }
444 #endif /* PORT_HELP */
445
446 /* validate wizard mode if player has requested access to it */
447 boolean
448 authorize_wizard_mode()
449 {
450     if (!strcmpi(nh_getenv("USER"), WIZARD_NAME))
451         return TRUE;
452     wiz_error_flag = TRUE; /* not being allowed into wizard mode */
453     return FALSE;
454 }
455
456 static void
457 wd_message()
458 {
459     if (wiz_error_flag) {
460         pline("Only user \"%s\" may access debug (wizard) mode.",
461               WIZARD_NAME);
462         pline("Entering explore/discovery mode instead.");
463         wizard = 0, discover = 1; /* (paranoia) */
464     } else if (discover)
465         You("are in non-scoring explore/discovery mode.");
466 }
467
468 unsigned long
469 sys_random_seed()
470 {
471     unsigned long seed;
472     unsigned long pid = (unsigned long) getpid();
473
474     seed = (unsigned long) getnow(); /* time((TIME_type) 0) */
475     /* Quick dirty band-aid to prevent PRNG prediction */
476     if (pid) {
477         if (!(pid & 3L))
478             pid -= 1L;
479         seed *= pid;
480     }
481     return seed;
482 }
483
484 /*vmsmain.c*/