NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / sys / vms / vmsunix.c
blob6446cd386279d3ba42f72606ef49fd4ea5c9a6a5
1 /* aNetHack 0.0.1 vmsunix.c $ANH-Date: 1449801743 2015/12/11 02:42:23 $ $ANH-Branch: aNetHack-3.6.0 $:$ANH-Revision: 1.15 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* aNetHack may be freely redistributed. See license for details. */
5 /* This file implements things from unixunix.c, plus related stuff */
7 #include "hack.h"
9 #include <descrip.h>
10 #include <dvidef.h>
11 #include <jpidef.h>
12 #include <ssdef.h>
13 #include <errno.h>
14 #include <signal.h>
15 #undef off_t
16 #ifdef GNUC
17 #include <sys/stat.h>
18 #else
19 #define umask hide_umask_dummy /* DEC C: avoid conflict with system.h */
20 #include <stat.h>
21 #undef umask
22 #endif
23 #include <ctype.h>
25 extern int debuggable; /* defined in vmsmisc.c */
27 extern void VDECL(lib$signal, (unsigned, ...));
28 extern unsigned long sys$setprv();
29 extern unsigned long lib$getdvi(), lib$getjpi(), lib$spawn(), lib$attach();
30 extern unsigned long smg$init_term_table_by_type(), smg$del_term_table();
31 #define vms_ok(sts) ((sts) & 1) /* odd => success */
33 /* this could be static; it's only used within this file;
34 it won't be used at all if C_LIB$INTIALIZE gets commented out below,
35 so make it global so that compiler won't complain that it's not used */
36 int FDECL(vmsexeini, (const void *, const void *, const unsigned char *));
38 static int FDECL(veryold, (int));
39 static char *NDECL(verify_term);
40 #if defined(SHELL) || defined(SUSPEND)
41 static void FDECL(hack_escape, (BOOLEAN_P, const char *));
42 static void FDECL(hack_resume, (BOOLEAN_P));
43 #endif
45 static int
46 veryold(fd)
47 int fd;
49 register int i;
50 time_t date;
51 struct stat buf;
53 if (fstat(fd, &buf))
54 return 0; /* cannot get status */
55 #ifndef INSURANCE
56 if (buf.st_size != sizeof(int))
57 return 0; /* not an xlock file */
58 #endif
59 (void) time(&date);
60 if (date - buf.st_mtime < 3L * 24L * 60L * 60L) { /* recent */
61 int lockedpid; /* should be the same size as hackpid */
62 unsigned long status, dummy, code = JPI$_PID;
64 if (read(fd, (genericptr_t) &lockedpid, sizeof(lockedpid))
65 != sizeof(lockedpid)) /* strange ... */
66 return 0;
67 status = lib$getjpi(&code, &lockedpid, 0, &dummy);
68 if (vms_ok(status) || status != SS$_NONEXPR)
69 return 0;
71 (void) close(fd);
73 /* cannot use maxledgerno() here, because we need to find a lock name
74 * before starting everything (including the dungeon initialization
75 * that sets astral_level, needed for maxledgerno()) up
77 for (i = 1; i <= MAXDUNGEON * MAXLEVEL + 1; i++) {
78 /* try to remove all */
79 set_levelfile_name(lock, i);
80 (void) delete (lock);
82 set_levelfile_name(lock, 0);
83 if (delete (lock))
84 return 0; /* cannot remove it */
85 return 1; /* success! */
88 void
89 getlock()
91 register int i = 0, fd;
93 /* idea from rpick%ucqais@uccba.uc.edu
94 * prevent automated rerolling of characters
95 * test input (fd0) so that tee'ing output to get a screen dump still
96 * works
97 * also incidentally prevents development of any hack-o-matic programs
99 if (isatty(0) <= 0)
100 error("You must play from a terminal.");
102 /* we ignore QUIT and INT at this point */
103 if (!lock_file(HLOCK, LOCKPREFIX, 10)) {
104 wait_synch();
105 error("Quitting.");
108 /* default value of lock[] is "1lock" where '1' gets changed to
109 'a','b',&c below; override the default and use <uid><charname>
110 if we aren't restricting the number of simultaneous games */
111 if (!locknum)
112 Sprintf(lock, "_%u%s", (unsigned) getuid(), plname);
114 regularize(lock);
115 set_levelfile_name(lock, 0);
116 if (locknum > 25)
117 locknum = 25;
119 do {
120 if (locknum)
121 lock[0] = 'a' + i++;
123 if ((fd = open(lock, 0, 0)) == -1) {
124 if (errno == ENOENT)
125 goto gotlock; /* no such file */
126 perror(lock);
127 unlock_file(HLOCK);
128 error("Cannot open %s", lock);
131 if (veryold(fd)) /* if true, this closes fd and unlinks lock */
132 goto gotlock;
133 (void) close(fd);
134 } while (i < locknum);
136 unlock_file(HLOCK);
137 error(locknum ? "Too many hacks running now."
138 : "There is a game in progress under your name.");
140 gotlock:
141 fd = creat(lock, FCMASK);
142 unlock_file(HLOCK);
143 if (fd == -1) {
144 error("cannot creat lock file.");
145 } else {
146 if (write(fd, (char *) &hackpid, sizeof(hackpid))
147 != sizeof(hackpid)) {
148 error("cannot write lock");
150 if (close(fd) == -1) {
151 error("cannot close lock");
156 void regularize(s) /* normalize file name */
157 register char *s;
159 register char *lp;
161 for (lp = s; *lp; lp++) /* note: '-' becomes '_' */
162 if (!(isalpha(*lp) || isdigit(*lp) || *lp == '$'))
163 *lp = '_';
166 #undef getuid
168 vms_getuid()
170 return ((getgid() << 16) | getuid());
173 #ifndef FAB$C_STMLF
174 #define FAB$C_STMLF 5
175 #endif
176 /* check whether the open file specified by `fd' is in stream-lf format */
177 boolean
178 file_is_stmlf(fd)
179 int fd;
181 int rfm;
182 struct stat buf;
184 if (fstat(fd, &buf))
185 return FALSE; /* cannot get status? */
187 #ifdef stat_alignment_fix /* gcc-vms alignment kludge */
188 rfm = stat_alignment_fix(&buf)->st_fab_rfm;
189 #else
190 rfm = buf.st_fab_rfm;
191 #endif
192 return (boolean) (rfm == FAB$C_STMLF);
195 /*------*/
196 #ifndef LNM$_STRING
197 #include <lnmdef.h> /* logical name definitions */
198 #endif
199 #define ENVSIZ LNM$C_NAMLENGTH /*255*/
201 #define ENV_USR 0 /* user-mode */
202 #define ENV_SUP 1 /* supervisor-mode */
203 #define ENV_JOB 2 /* job-wide entry */
205 /* vms_define() - assign a value to a logical name */
207 vms_define(name, value, flag)
208 const char *name;
209 const char *value;
210 int flag;
212 struct dsc {
213 unsigned short len, mbz;
214 const char *adr;
215 }; /* descriptor */
216 struct itm3 {
217 short buflen, itmcode;
218 const char *bufadr;
219 short *retlen;
221 static struct itm3 itm_lst[] = { { 0, LNM$_STRING, 0, 0 }, { 0, 0 } };
222 struct dsc nam_dsc, val_dsc, tbl_dsc;
223 unsigned long result, sys$crelnm(), lib$set_logical();
225 /* set up string descriptors */
226 nam_dsc.mbz = val_dsc.mbz = tbl_dsc.mbz = 0;
227 nam_dsc.len = strlen(nam_dsc.adr = name);
228 val_dsc.len = strlen(val_dsc.adr = value);
229 tbl_dsc.len = strlen(tbl_dsc.adr = "LNM$PROCESS");
231 switch (flag) {
232 case ENV_JOB: /* job logical name */
233 tbl_dsc.len = strlen(tbl_dsc.adr = "LNM$JOB");
234 /*FALLTHRU*/
235 case ENV_SUP: /* supervisor-mode process logical name */
236 result = lib$set_logical(&nam_dsc, &val_dsc, &tbl_dsc);
237 break;
238 case ENV_USR: /* user-mode process logical name */
239 itm_lst[0].buflen = val_dsc.len;
240 itm_lst[0].bufadr = val_dsc.adr;
241 result = sys$crelnm(0, &tbl_dsc, &nam_dsc, 0, itm_lst);
242 break;
243 default: /*[ bad input ]*/
244 result = 0;
245 break;
247 result &= 1; /* odd => success (== 1), even => failure (== 0) */
248 return !result; /* 0 == success, 1 == failure */
251 /* vms_putenv() - create or modify an environment value */
253 vms_putenv(string)
254 const char *string;
256 char name[ENVSIZ + 1], value[ENVSIZ + 1], *p; /* [255+1] */
258 p = strchr(string, '=');
259 if (p > string && p < string + sizeof name
260 && strlen(p + 1) < sizeof value) {
261 (void) strncpy(name, string, p - string), name[p - string] = '\0';
262 (void) strcpy(value, p + 1);
263 return vms_define(name, value, ENV_USR);
264 } else
265 return 1; /* failure */
269 Support for VT420 was added to VMS in version V5.4, but as of V5.5-2
270 VAXCRTL still doesn't handle it and puts TERM=undefined into the
271 environ[] array. getenv("TERM") will return "undefined" instead of
272 something sensible. Even though that's finally fixed in V6.0, site
273 defined terminals also return "undefined" so query SMG's TERMTABLE
274 instead of just checking VMS's device-type value for VT400_Series.
276 Called by verify_termcap() for convenience.
278 static char *
279 verify_term()
281 char *term = getenv("ANETHACK_TERM");
282 if (!term)
283 term = getenv("HACK_TERM");
284 if (!term)
285 term = getenv("EMACS_TERM");
286 if (!term)
287 term = getenv("TERM");
288 if (!term || !*term || !strcmpi(term, "undefined")
289 || !strcmpi(term, "unknown")) {
290 static char smgdevtyp[31 + 1]; /* size is somewhat arbitrary */
291 static char dev_tty[] = "TT:";
292 static $DESCRIPTOR(smgdsc, smgdevtyp);
293 static $DESCRIPTOR(tt, dev_tty);
294 unsigned short dvicode = DVI$_DEVTYPE;
295 unsigned long devtype = 0L, termtab = 0L;
297 (void) lib$getdvi(&dvicode, (unsigned short *) 0, &tt, &devtype,
298 (genericptr_t) 0, (unsigned short *) 0);
300 if (devtype && vms_ok(smg$init_term_table_by_type(&devtype, &termtab,
301 &smgdsc))) {
302 register char *p = &smgdevtyp[smgdsc.dsc$w_length];
303 /* strip trailing blanks */
304 while (p > smgdevtyp && *--p == ' ')
305 *p = '\0';
306 /* (void) smg$del_term_table(); */
307 term = smgdevtyp;
310 return term;
314 Figure out whether the termcap code will find a termcap file; if not,
315 try to help it out. This avoids modifying the GNU termcap sources and
316 can simplify configuration for sites which don't already use termcap.
318 #define GNU_DEFAULT_TERMCAP "emacs_library:[etc]termcap.dat"
319 #define ANETHACK_DEF_TERMCAP "anethackdir:termcap"
320 #define HACK_DEF_TERMCAP "hackdir:termcap"
322 char *verify_termcap() /* called from startup(src/termcap.c) */
324 struct stat dummy;
325 const char *tc = getenv("TERMCAP");
326 if (tc)
327 return verify_term(); /* no termcap fixups needed */
328 if (!tc && !stat(ANETHACK_DEF_TERMCAP, &dummy))
329 tc = ANETHACK_DEF_TERMCAP;
330 if (!tc && !stat(HACK_DEF_TERMCAP, &dummy))
331 tc = HACK_DEF_TERMCAP;
332 if (!tc && !stat(GNU_DEFAULT_TERMCAP, &dummy))
333 tc = GNU_DEFAULT_TERMCAP;
334 if (!tc && !stat("[]termcap", &dummy))
335 tc = "[]termcap"; /* current dir */
336 if (!tc && !stat("$TERMCAP", &dummy))
337 tc = "$TERMCAP"; /* alt environ */
338 if (tc) {
339 /* putenv(strcat(strcpy(buffer,"TERMCAP="),tc)); */
340 vms_define("TERMCAP", tc, ENV_USR);
341 } else {
342 /* perhaps someday we'll construct a termcap entry string */
344 return verify_term();
346 /*------*/
348 #ifdef SHELL
349 #ifndef CLI$M_NOWAIT
350 #define CLI$M_NOWAIT 1
351 #endif
352 #endif
354 #if defined(CHDIR) || defined(SHELL) || defined(SECURE)
355 static unsigned long oprv[2];
357 void
358 privoff()
360 unsigned long pid = 0, prv[2] = { ~0, ~0 };
361 unsigned short code = JPI$_PROCPRIV;
363 (void) sys$setprv(0, prv, 0, oprv);
364 (void) lib$getjpi(&code, &pid, (genericptr_t) 0, prv);
365 (void) sys$setprv(1, prv, 0, (unsigned long *) 0);
368 void
369 privon()
371 (void) sys$setprv(1, oprv, 0, (unsigned long *) 0);
373 #endif /* CHDIR || SHELL || SECURE */
375 #if defined(SHELL) || defined(SUSPEND)
376 static void
377 hack_escape(screen_manip, msg_str)
378 boolean screen_manip;
379 const char *msg_str;
381 if (screen_manip)
382 suspend_nhwindows(msg_str); /* clear screen, reset terminal, &c */
383 (void) signal(SIGQUIT, SIG_IGN); /* ignore ^Y */
384 (void) signal(SIGINT, SIG_DFL); /* don't trap ^C (implct cnvrs to ^Y) */
387 static void
388 hack_resume(screen_manip)
389 boolean screen_manip;
391 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
392 if (wizard)
393 (void) signal(SIGQUIT, SIG_DFL);
394 if (screen_manip)
395 resume_nhwindows(); /* setup terminal modes, redraw screen, &c */
397 #endif /* SHELL || SUSPEND */
399 #ifdef SHELL
400 unsigned long dosh_pid = 0, /* this should cover any interactive escape */
401 mail_pid = 0; /* this only covers the last mail or phone;
402 (mail & phone commands aren't expected to
403 leave any process hanging around) */
406 dosh()
408 return vms_doshell("", TRUE); /* call for interactive child process */
411 /* vms_doshell -- called by dosh() and readmail()
413 * If execstring is not a null string, then it will be executed in a spawned
414 * subprocess, which will then return. It is for handling mail or phone
415 * interactive commands, which are only available if both MAIL and SHELL are
416 * #defined, but we don't bother making the support code conditionalized on
417 * MAIL here, just on SHELL being enabled.
419 * Normally, all output from this interaction will be 'piped' to the user's
420 * screen (SYS$OUTPUT). However, if 'screenoutput' is set to FALSE, output
421 * will be piped into oblivion. Used for silent phone call rejection.
424 vms_doshell(execstring, screenoutput)
425 const char *execstring;
426 boolean screenoutput;
428 unsigned long status, new_pid, spawnflags = 0;
429 struct dsc$descriptor_s comstring, *command, *inoutfile = 0;
430 static char dev_null[] = "_NLA0:";
431 static $DESCRIPTOR(nulldevice, dev_null);
433 /* Is this an interactive shell spawn, or do we have a command to do? */
434 if (execstring && *execstring) {
435 comstring.dsc$w_length = strlen(execstring);
436 comstring.dsc$b_dtype = DSC$K_DTYPE_T;
437 comstring.dsc$b_class = DSC$K_CLASS_S;
438 comstring.dsc$a_pointer = (char *) execstring;
439 command = &comstring;
440 } else
441 command = 0;
443 /* use asynch subprocess and suppress output iff one-shot command */
444 if (!screenoutput) {
445 spawnflags = CLI$M_NOWAIT;
446 inoutfile = &nulldevice;
449 hack_escape(screenoutput,
450 command ? (const char *) 0
451 : " \"Escaping\" into a subprocess; LOGOUT to reconnect and resume play. ");
453 if (command || !dosh_pid || !vms_ok(status = lib$attach(&dosh_pid))) {
454 #ifdef CHDIR
455 (void) chdir(getenv("PATH"));
456 #endif
457 privoff();
458 new_pid = 0;
459 status = lib$spawn(command, inoutfile, inoutfile, &spawnflags,
460 (struct dsc$descriptor_s *) 0, &new_pid);
461 if (!command)
462 dosh_pid = new_pid;
463 else
464 mail_pid = new_pid;
465 privon();
466 #ifdef CHDIR
467 chdirx((char *) 0, 0);
468 #endif
471 hack_resume(screenoutput);
473 if (!vms_ok(status)) {
474 pline(" Spawn failed. (%%x%08lX) ", status);
475 mark_synch();
477 return 0;
479 #endif /* SHELL */
481 #ifdef SUSPEND
482 /* dosuspend() -- if we're a subprocess, attach to our parent;
483 * if not, there's nothing we can do.
486 dosuspend()
488 static long owner_pid = -1;
489 unsigned long status;
491 if (owner_pid == -1) /* need to check for parent */
492 owner_pid = getppid();
493 if (owner_pid == 0) {
494 pline(
495 " No parent process. Use '!' to Spawn, 'S' to Save, or '#quit' to Quit. ");
496 mark_synch();
497 return 0;
500 /* restore normal tty environment & clear screen */
501 hack_escape(1,
502 " Attaching to parent process; use the ATTACH command to resume play. ");
504 status = lib$attach(&owner_pid); /* connect to parent */
506 hack_resume(1); /* resume game tty environment & refresh screen */
508 if (!vms_ok(status)) {
509 pline(" Unable to attach to parent. (%%x%08lX) ", status);
510 mark_synch();
512 return 0;
514 #endif /* SUSPEND */
516 #ifdef SELECTSAVED
517 /* this would fit better in vmsfiles.c except that that gets linked
518 with the utility programs and we don't want this code there */
520 static void FDECL(savefile, (const char *, int, int *, char ***));
522 static void
523 savefile(name, indx, asize, array)
524 const char *name;
525 int indx, *asize;
526 char ***array;
528 char **newarray;
529 int i, oldsize;
531 /* (asize - 1) guarantees that [indx + 1] will exist and be set to null */
532 while (indx >= *asize - 1) {
533 oldsize = *asize;
534 *asize += 5;
535 newarray = (char **) alloc(*asize * sizeof (char *));
536 /* poor man's realloc() */
537 for (i = 0; i < *asize; ++i)
538 newarray[i] = (i < oldsize) ? (*array)[i] : 0;
539 if (*array)
540 free((genericptr_t) *array);
541 *array = newarray;
543 (*array)[indx] = dupstr(name);
546 struct dsc {
547 unsigned short len, mbz;
548 char *adr;
549 }; /* descriptor */
550 typedef unsigned long vmscond; /* vms condition value */
551 vmscond FDECL(lib$find_file, (const struct dsc *, struct dsc *, genericptr *));
552 vmscond FDECL(lib$find_file_end, (void **));
554 /* collect a list of character names from all save files for this player */
556 vms_get_saved_games(savetemplate, outarray)
557 const char *savetemplate; /* wildcarded save file name in native VMS format */
558 char ***outarray;
560 struct dsc in, out;
561 unsigned short l;
562 int count, asize;
563 char *charname, wildcard[255 + 1], filename[255 + 1];
564 genericptr_t context = 0;
566 Strcpy(wildcard, savetemplate); /* plname_from_file overwrites SAVEF */
567 in.mbz = 0; /* class and type; leave them unspecified */
568 in.len = (unsigned short) strlen(wildcard);
569 in.adr = wildcard;
570 out.mbz = 0;
571 out.len = (unsigned short) (sizeof filename - 1);
572 out.adr = filename;
574 *outarray = 0;
575 count = asize = 0;
576 /* note: only works as intended if savetemplate is a wildcard filespec */
577 while (lib$find_file(&in, &out, &context) & 1) {
578 /* strip trailing blanks */
579 for (l = out.len; l > 0; --l)
580 if (filename[l - 1] != ' ')
581 break;
582 filename[l] = '\0';
583 if ((charname = plname_from_file(filename)) != 0)
584 savefile(charname, count++, &asize, outarray);
586 (void) lib$find_file_end(&context);
588 return count;
590 #endif /* SELECTSAVED */
592 #ifdef PANICTRACE
593 /* anethack has detected an internal error; try to give a trace of call stack
595 void
596 vms_traceback(how)
597 int how; /* 1: exit after traceback; 2: stay in debugger */
599 /* assumes that a static initializer applies to the first union
600 field and that no padding will be placed between len and str */
601 union dbgcmd {
602 struct ascic {
603 unsigned char len; /* 8-bit length prefix */
604 char str[79]; /* could be up to 255, but we don't need so much */
605 } cmd_fields;
606 char cmd[1 + 79];
608 #define DBGCMD(arg) { (unsigned char) (sizeof arg - sizeof ""), arg }
609 static union dbgcmd dbg[3] = {
610 /* prologue for less verbose feedback (when combined with
611 $ define/User_mode dbg$output _NL: ) */
612 DBGCMD("set Log SYS$OUTPUT: ; set Output Log,noTerminal,noVerify"),
613 /* enable modules with calls present on stack, then show those calls;
614 limit traceback to 18 stack frames to avoid scrolling off screen
615 (could check termcap LI and maybe give more, but we're operating
616 in a last-gasp environment so apply the KISS principle...) */
617 DBGCMD("set Module/Calls ; show Calls 18"),
618 /* epilogue; "exit" ends the sequence it's part of, but it doesn't
619 seem able to cause program termination end when used separately;
620 instead of relying on it, we'll redirect debugger input to come
621 from the null device so that it'll get an end-of-input condition
622 when it tries to get a command from the user */
623 DBGCMD("exit"),
625 #undef DBGCMD
628 * If we've been linked /noTraceback then we can't provide any
629 * trace of the call stack. Linking that way is required if
630 * anethack.exe is going to be installed with privileges, so the
631 * SECURE configuration usually won't have any trace feedback.
633 if (!debuggable) {
634 ; /* debugger not available to catch lib$signal(SS$_DEBUG) */
635 } else if (how == 2) {
636 /* omit prologue and epilogue (dbg[0] and dbg[2]) */
637 (void) lib$signal(SS$_DEBUG, 1, dbg[1].cmd);
638 } else if (how == 1) {
640 * Suppress most of debugger's initial feedback to avoid scaring
641 * users (and scrolling panic message off the screen). Also control
642 * debugging environment to try to prevent unexpected complications.
644 /* start up with output going to /dev/null instead of stdout;
645 once started, output is sent to log file that's actually stdout */
646 (void) vms_define("DBG$OUTPUT", "_NL:", 0);
647 /* take input from null device so debugger will see end-on-input
648 and quit if/when it tries to get a command from the user */
649 (void) vms_define("DBG$INPUT", "_NL:", 0);
650 /* bypass any debugger initialization file the user might have */
651 (void) vms_define("DBG$INIT", "_NL:", 0);
652 /* force tty interface by suppressing DECwindows/Motif interface */
653 (void) vms_define("DBG$DECW$DISPLAY", " ", 0);
654 /* raise an exception for the debugger to catch */
655 (void) lib$signal(SS$_DEBUG, 3, dbg[0].cmd, dbg[1].cmd, dbg[2].cmd);
658 vms_exit(2); /* don't return to caller (2==arbitrary non-zero) */
659 /* NOT REACHED */
661 #endif /* PANICTRACE */
664 * Play Hunt the Wumpus to see whether the debugger lurks nearby.
665 * It all takes place before anethack even starts, and sets up
666 * `debuggable' to control possible use of lib$signal(SS$_DEBUG).
668 typedef unsigned FDECL((*condition_handler), (unsigned *, unsigned *));
669 extern condition_handler FDECL(lib$establish, (condition_handler));
670 extern unsigned FDECL(lib$sig_to_ret, (unsigned *, unsigned *));
672 /* SYS$IMGSTA() is not documented: if called at image startup, it controls
673 access to the debugger; fortunately, the linker knows now to find it
674 without needing to link against sys.stb (VAX) or use LINK/System (Alpha).
675 We won't be calling it, but we indirectly check whether it has already
676 been called by checking if anethack.exe has it as a transfer address. */
677 extern unsigned FDECL(sys$imgsta, ());
680 * These structures are in header files contained in sys$lib_c.tlb,
681 * but that isn't available on sufficiently old versions of VMS.
682 * Construct our own: partly stubs, with simpler field names and
683 * without ugly unions. Contents derived from Bliss32 definitions
684 * in lib.req and/or Macro32 definitions in lib.mlb.
686 struct ihd { /* (vax) image header, $IHDDEF */
687 unsigned short size, activoff;
688 unsigned char otherstuff[512 - 4];
690 struct eihd { /* extended image header, $EIHDDEF */
691 unsigned long majorid, minorid, size, isdoff, activoff;
692 unsigned char otherstuff[512 - 20];
694 struct iha { /* (vax) image header activation block, $IHADEF */
695 unsigned long trnadr1, trnadr2, trnadr3;
696 unsigned long fill_, inishr;
698 struct eiha { /* extended image header activation block, $EIHADEF */
699 unsigned long size, spare;
700 unsigned long trnadr1[2], trnadr2[2], trnadr3[2], trnadr4[2], inishr[2];
704 * We're going to use lib$initialize, not because we need or
705 * want to be called before main(), but because one of the
706 * arguments passed to a lib$initialize callback is a pointer
707 * to the image header (somewhat complex data structure which
708 * includes the memory location(s) of where to start executing)
709 * of the program being initialized. It comes in two flavors,
710 * one used by VAX and the other by Alpha and IA64.
712 * An image can have up to three transfer addresses; one of them
713 * decides whether to run under debugger control (RUN/Debug, or
714 * LINK/Debug + plain RUN), another handles lib$initialize calls
715 * if that's used, and the last is to start the program itself
716 * (a jacket built around main() for code compiled with DEC C).
717 * They aren't always all present; some might be zero/null.
718 * A shareable image (pre-linked library) usually won't have any,
719 * but can have a separate initializer (not of interest here).
721 * The transfer targets don't have fixed slots but do occur in a
722 * particular order:
723 * link link lib$initialize lib$initialize
724 * sharable /noTrace /Trace + /noTrace + /Traceback
725 * 1: (none) main debugger init-handler debugger
726 * 2: main main init-handler
727 * 3: main
729 * We check whether the first transfer address is SYS$IMGSTA().
730 * If it is, the debugger should be available to catch SS$_DEBUG
731 * exception even when we don't start up under debugger control.
732 * One extra complication: if we *do* start up under debugger
733 * control, the first address in the in-memory copy of the image
734 * header will be changed from sys$imgsta() to a value in system
735 * space. [I don't know how to reference that one symbolically,
736 * so I'm going to treat any address in system space as meaning
737 * that the debugger is available. pr]
740 /* called via lib$initialize during image activation: before main() and
741 with magic arguments; C run-time library won't be initialized yet */
742 /*ARGSUSED*/
744 vmsexeini(inirtn_unused, clirtn_unused, imghdr)
745 const void *inirtn_unused, *clirtn_unused;
746 const unsigned char *imghdr;
748 const struct ihd *vax_hdr;
749 const struct eihd *axp_hdr;
750 const struct iha *vax_xfr;
751 const struct eiha *axp_xfr;
752 unsigned long trnadr1;
754 (void) lib$establish(lib$sig_to_ret); /* set up condition handler */
757 * Check the first of three transfer addresses to see whether
758 * it is SYS$IMGSTA(). Note that they come from a file,
759 * where they reside as longword or quadword integers rather
760 * than function pointers. (Basically just a C type issue;
761 * casting back and forth between integer and pointer doesn't
762 * change any bits for the architectures VMS runs on.)
764 debuggable = 0;
765 /* start with a guess rather than bothering to figure out architecture */
766 vax_hdr = (struct ihd *) imghdr;
767 if (vax_hdr->size >= 512) {
768 /* this is a VAX-specific header; addresses are longwords */
769 vax_xfr = (struct iha *) (imghdr + vax_hdr->activoff);
770 trnadr1 = vax_xfr->trnadr1;
771 } else {
772 /* the guess above was wrong; imghdr's first word is not
773 the size field, it's a version number component */
774 axp_hdr = (struct eihd *) imghdr;
775 /* this is an Alpha or IA64 header; addresses are quadwords
776 but we ignore the upper half which will be all 0's or 0xF's
777 (we hope; if not, assume it still won't matter for this test) */
778 axp_xfr = (struct eiha *) (imghdr + axp_hdr->activoff);
779 trnadr1 = axp_xfr->trnadr1[0];
781 if ((unsigned (*) ()) trnadr1 == sys$imgsta ||
782 /* check whether first transfer address points to system space
783 [we want (trnadr1 >= 0x80000000UL) but really old compilers
784 don't support the UL suffix, so do a signed compare instead] */
785 (long) trnadr1 < 0L)
786 debuggable = 1;
787 return 1; /* success (return value here doesn't actually matter) */
791 * Setting up lib$initialize transfer block is trivial with Macro32,
792 * but we don't want to introduce use of assembler code. Doing it
793 * with C requires jiggery-pokery here and again when linking, and
794 * may not work with some compiler versions. The lib$initialize
795 * transfer block is an open-ended array of 32-bit routine addresses
796 * in a psect named "lib$initialize" with particular attributes (one
797 * being "concatenate" so that multiple instances of lib$initialize
798 * are appended rather than overwriting each other).
800 * VAX C made global variables become named program sections, to be
801 * compatable with Fortran COMMON blocks, simplifying mixed-language
802 * programs. GNU C for VAX/VMS did the same, to be compatable with
803 * VAX C. By default, DEC C makes global variables be global symbols
804 * instead, with its /Extern_Model=Relaxed_Ref_Def mode, but can be
805 * told to be VAX C compatable by using /Extern_Model=Common_Block.
807 * We don't want to force that for the whole program; occasional use
808 * of /Extern_Model=Strict_Ref_Def to find mistakes is too useful.
809 * Also, using symbols instead of psects is more robust when linking
810 * with an object library if the module defining the symbol contains
811 * only data. With a psect, any declaration is enough to become a
812 * definition and the linker won't bother hunting through a library
813 * to find another one unless explicitly told to do so. Bad news
814 * if that other one happens to include the intended initial value
815 * and someone bypasses `make' to link interactively but neglects
816 * to give the linker enough explicit directions. Linking like that
817 * would work, but the program wouldn't.
819 * So, we switch modes for this hack only. Besides, psect attributes
820 * for lib$initialize are different from the ones used for ordinary
821 * variables, so we'd need to resort to some linker magic anyway.
822 * (With assembly language, in addtion to having full control of the
823 * psect attributes in the source code, Macro32 would include enough
824 * information in its object file such that linker wouldn't need any
825 * extra instructions from us to make this work.) [If anyone links
826 * manually now and neglects the esoteric details, vmsexeini() won't
827 * get called and `debuggable' will stay 0, so lib$signal(SS$_DEBUG)
828 * will be avoided even when its use is viable. But the program will
829 * still work correctly.]
831 #define C_LIB$INITIALIZE /* comment out if this won't compile... */
832 /* (then `debuggable' will always stay 0) */
833 #ifdef C_LIB$INITIALIZE
834 #ifdef __DECC
835 #pragma extern_model save /* push current mode */
836 #pragma extern_model common_block /* set new mode */
837 #endif
838 /* values are 32-bit function addresses; pointers might be 64 so avoid them */
839 extern const unsigned long lib$initialize[1]; /* size is actually variable */
840 const unsigned long lib$initialize[] = { (unsigned long) (void *) vmsexeini };
841 #ifdef __DECC
842 #pragma extern_model restore /* pop previous mode */
843 #endif
844 /* We also need to link against a linker options file containing:
845 sys$library:starlet.olb/Include=(lib$initialize)
846 psect_attr=lib$initialize, Con,Usr,noPic,Rel,Gbl,noShr,noExe,Rd,noWrt,Long
848 #endif /* C_LIB$INITIALIZE */
849 /* End of debugger hackery. */
851 /*vmsunix.c*/