1 /* NetHack 3.6 vmsunix.c $NHDT-Date: 1432512790 2015/05/25 00:13:10 $ $NHDT-Branch: master $:$NHDT-Revision: 1.14 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* This file implements things from unixunix.c, plus related stuff */
19 #define umask hide_umask_dummy /* DEC C: avoid conflict with system.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));
54 return (0); /* cannot get status */
56 if (buf.st_size != sizeof(int))
57 return (0); /* not an xlock file */
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 ... */
67 status = lib$getjpi(&code, &lockedpid, 0, &dummy);
68 if (vms_ok(status) || status != SS$_NONEXPR)
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);
82 set_levelfile_name(lock, 0);
84 return (0); /* cannot remove it */
85 return (1); /* success! */
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
97 * also incidentally prevents development of any hack-o-matic programs
100 error("You must play from a terminal.");
102 /* we ignore QUIT and INT at this point */
103 if (!lock_file(HLOCK, LOCKPREFIX, 10)) {
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 */
112 Sprintf(lock, "_%u%s", (unsigned) getuid(), plname);
115 set_levelfile_name(lock, 0);
123 if ((fd = open(lock, 0, 0)) == -1) {
125 goto gotlock; /* no such file */
128 error("Cannot open %s", lock);
131 if (veryold(fd)) /* if true, this closes fd and unlinks lock */
134 } while (i < locknum);
137 error(locknum ? "Too many hacks running now."
138 : "There is a game in progress under your name.");
141 fd = creat(lock, FCMASK);
144 error("cannot creat lock file.");
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 */
161 for (lp = s; *lp; lp++) /* note: '-' becomes '_' */
162 if (!(isalpha(*lp) || isdigit(*lp) || *lp == '$'))
170 return (getgid() << 16) | getuid();
174 #define FAB$C_STMLF 5
176 /* check whether the open file specified by `fd' is in stream-lf format */
185 return FALSE; /* cannot get status? */
187 #ifdef stat_alignment_fix /* gcc-vms alignment kludge */
188 rfm = stat_alignment_fix(&buf)->st_fab_rfm;
190 rfm = buf.st_fab_rfm;
192 return rfm == FAB$C_STMLF;
197 #include <lnmdef.h> /* logical name definitions */
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)
213 unsigned short len, mbz;
217 short buflen, itmcode;
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");
232 case ENV_JOB: /* job logical name */
233 tbl_dsc.len = strlen(tbl_dsc.adr = "LNM$JOB");
235 case ENV_SUP: /* supervisor-mode process logical name */
236 result = lib$set_logical(&nam_dsc, &val_dsc, &tbl_dsc);
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);
243 default: /*[ bad input ]*/
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 */
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);
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.
281 char *term = getenv("NETHACK_TERM");
283 term = getenv("HACK_TERM");
285 term = getenv("EMACS_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,
302 register char *p = &smgdevtyp[smgdsc.dsc$w_length];
303 /* strip trailing blanks */
304 while (p > smgdevtyp && *--p == ' ')
306 /* (void)smg$del_term_table(); */
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 NETHACK_DEF_TERMCAP "nethackdir:termcap"
320 #define HACK_DEF_TERMCAP "hackdir:termcap"
322 char *verify_termcap() /* called from startup(src/termcap.c) */
325 const char *tc = getenv("TERMCAP");
327 return verify_term(); /* no termcap fixups needed */
328 if (!tc && !stat(NETHACK_DEF_TERMCAP, &dummy))
329 tc = NETHACK_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 */
339 /* putenv(strcat(strcpy(buffer,"TERMCAP="),tc)); */
340 vms_define("TERMCAP", tc, ENV_USR);
342 /* perhaps someday we'll construct a termcap entry string */
344 return verify_term();
350 #define CLI$M_NOWAIT 1
354 #if defined(CHDIR) || defined(SHELL) || defined(SECURE)
355 static unsigned long oprv[2];
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);
371 (void) sys$setprv(1, oprv, 0, (unsigned long *) 0);
373 #endif /* CHDIR || SHELL || SECURE */
375 #if defined(SHELL) || defined(SUSPEND)
377 hack_escape(screen_manip, msg_str)
378 boolean 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) */
388 hack_resume(screen_manip)
389 boolean screen_manip;
391 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
393 (void) signal(SIGQUIT, SIG_DFL);
395 resume_nhwindows(); /* setup terminal modes, redraw screen, &c */
397 #endif /* SHELL || SUSPEND */
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 leave any process hanging
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
415 /* subprocess, which will then return. It is for handling mail or phone */
416 /* interactive commands, which are only available if both MAIL and SHELL are
418 /* #defined, but we don't bother making the support code conditionalized on */
419 /* MAIL here, just on SHELL being enabled. */
421 /* Normally, all output from this interaction will be 'piped' to the user's */
422 /* screen (SYS$OUTPUT). However, if 'screenoutput' is set to FALSE, output */
423 /* will be piped into oblivion. Used for silent phone call rejection. */
426 vms_doshell(execstring, screenoutput)
427 const char *execstring;
428 boolean screenoutput;
430 unsigned long status, new_pid, spawnflags = 0;
431 struct dsc$descriptor_s comstring, *command, *inoutfile = 0;
432 static char dev_null[] = "_NLA0:";
433 static $DESCRIPTOR(nulldevice, dev_null);
435 /* Is this an interactive shell spawn, or do we have a command to do? */
436 if (execstring && *execstring) {
437 comstring.dsc$w_length = strlen(execstring);
438 comstring.dsc$b_dtype = DSC$K_DTYPE_T;
439 comstring.dsc$b_class = DSC$K_CLASS_S;
440 comstring.dsc$a_pointer = (char *) execstring;
441 command = &comstring;
445 /* use asynch subprocess and suppress output iff one-shot command */
447 spawnflags = CLI$M_NOWAIT;
448 inoutfile = &nulldevice;
451 hack_escape(screenoutput,
452 command ? (const char *) 0 : " \"Escaping\" into a "
453 "subprocess; LOGOUT to "
454 "reconnect and resume play. ");
456 if (command || !dosh_pid || !vms_ok(status = lib$attach(&dosh_pid))) {
458 (void) chdir(getenv("PATH"));
462 status = lib$spawn(command, inoutfile, inoutfile, &spawnflags,
463 (struct dsc$descriptor_s *) 0, &new_pid);
470 chdirx((char *) 0, 0);
474 hack_resume(screenoutput);
476 if (!vms_ok(status)) {
477 pline(" Spawn failed. (%%x%08lX) ", status);
485 /* dosuspend() -- if we're a subprocess, attach to our parent;
486 * if not, there's nothing we can do.
491 static long owner_pid = -1;
492 unsigned long status;
494 if (owner_pid == -1) /* need to check for parent */
495 owner_pid = getppid();
496 if (owner_pid == 0) {
497 pline(" No parent process. Use '!' to Spawn, 'S' to Save, or 'Q' "
503 /* restore normal tty environment & clear screen */
504 hack_escape(1, " Attaching to parent process; use the ATTACH command to "
507 status = lib$attach(&owner_pid); /* connect to parent */
509 hack_resume(1); /* resume game tty environment & refresh screen */
511 if (!vms_ok(status)) {
512 pline(" Unable to attach to parent. (%%x%08lX) ", status);
520 /* this would fit better in vmsfiles.c except that that gets linked
521 with the utility programs and we don't want this code there */
523 static void FDECL(savefile, (const char *, int, int *, char ***));
526 savefile(name, indx, asize, array)
534 /* (asize - 1) guarantees that [indx + 1] will exist and be set to null */
535 while (indx >= *asize - 1) {
538 newarray = (char **) alloc(*asize * sizeof(char *));
539 /* poor man's realloc() */
540 for (i = 0; i < *asize; ++i)
541 newarray[i] = (i < oldsize) ? (*array)[i] : 0;
543 free((genericptr_t) *array);
546 (*array)[indx] = strcpy((char *) alloc(strlen(name) + 1), name);
550 unsigned short len, mbz;
553 typedef unsigned long vmscond; /* vms condition value */
554 vmscond FDECL(lib$find_file,
555 (const struct dsc *, struct dsc *, genericptr *));
556 vmscond FDECL(lib$find_file_end, (void **));
558 /* collect a list of character names from all save files for this player */
560 vms_get_saved_games(savetemplate, outarray)
561 const char *savetemplate; /* wildcarded save file name in native VMS format */
567 char *charname, wildcard[255 + 1], filename[255 + 1];
568 genericptr_t context = 0;
570 Strcpy(wildcard, savetemplate); /* plname_from_file overwrites SAVEF */
571 in.mbz = 0; /* class and type; leave them unspecified */
572 in.len = (unsigned short) strlen(wildcard);
575 out.len = (unsigned short) (sizeof filename - 1);
580 /* note: only works as intended if savetemplate is a wildcard filespec */
581 while (lib$find_file(&in, &out, &context) & 1) {
582 /* strip trailing blanks */
583 for (l = out.len; l > 0; --l)
584 if (filename[l - 1] != ' ')
587 if ((charname = plname_from_file(filename)) != 0)
588 savefile(charname, count++, &asize, outarray);
590 (void) lib$find_file_end(&context);
594 #endif /* SELECTSAVED */
597 /* nethack has detected an internal error; try to give a trace of call stack
601 int how; /* 1: exit after traceback; 2: stay in debugger */
603 /* assumes that a static initializer applies to the first union
604 field and that no padding will be placed between len and str */
607 unsigned char len; /* 8-bit length prefix */
609 str[79]; /* could be up to 255, but we don't need that much */
613 #define DBGCMD(arg) \
615 (unsigned char)(sizeof arg - sizeof ""), arg \
617 static union dbgcmd dbg[3] = {
618 /* prologue for less verbose feedback (when combined with
619 $ define/User_mode dbg$output _NL: ) */
620 DBGCMD("set Log SYS$OUTPUT: ; set Output Log,noTerminal,noVerify"),
621 /* enable modules with calls present on stack, then show those calls;
622 limit traceback to 18 stack frames to avoid scrolling off screen
623 (could check termcap LI and maybe give more, but we're operating
624 in a last-gasp environment so apply the KISS principle...) */
625 DBGCMD("set Module/Calls ; show Calls 18"),
626 /* epilogue; "exit" ends the sequence it's part of, but it doesn't
627 seem able to cause program termination end when used separately;
628 instead of relying on it, we'll redirect debugger input to come
629 from the null device so that it'll get an end-of-input condition
630 when it tries to get a command from the user */
636 * If we've been linked /noTraceback then we can't provide any
637 * trace of the call stack. Linking that way is required if
638 * nethack.exe is going to be installed with privileges, so the
639 * SECURE configuration usually won't have any trace feedback.
642 ; /* debugger not available to catch lib$signal(SS$_DEBUG) */
643 } else if (how == 2) {
644 /* omit prologue and epilogue (dbg[0] and dbg[2]) */
645 (void) lib$signal(SS$_DEBUG, 1, dbg[1].cmd);
646 } else if (how == 1) {
648 * Suppress most of debugger's initial feedback to avoid scaring
649 * users (and scrolling panic message off the screen). Also control
650 * debugging environment to try to prevent unexpected complications.
652 /* start up with output going to /dev/null instead of stdout;
653 once started, output is sent to log file that's actually stdout */
654 (void) vms_define("DBG$OUTPUT", "_NL:", 0);
655 /* take input from null device so debugger will see end-on-input
656 and quit if/when it tries to get a command from the user */
657 (void) vms_define("DBG$INPUT", "_NL:", 0);
658 /* bypass any debugger initialization file the user might have */
659 (void) vms_define("DBG$INIT", "_NL:", 0);
660 /* force tty interface by suppressing DECwindows/Motif interface */
661 (void) vms_define("DBG$DECW$DISPLAY", " ", 0);
662 /* raise an exception for the debugger to catch */
663 (void) lib$signal(SS$_DEBUG, 3, dbg[0].cmd, dbg[1].cmd, dbg[2].cmd);
666 vms_exit(2); /* don't return to caller (2==arbitrary non-zero) */
669 #endif /* PANICTRACE */
672 * Play Hunt the Wumpus to see whether the debugger lurks nearby.
673 * It all takes place before nethack even starts, and sets up
674 * `debuggable' to control possible use of lib$signal(SS$_DEBUG).
676 typedef unsigned FDECL((*condition_handler), (unsigned *, unsigned *));
677 extern condition_handler FDECL(lib$establish, (condition_handler));
678 extern unsigned FDECL(lib$sig_to_ret, (unsigned *, unsigned *));
680 /* SYS$IMGSTA() is not documented: if called at image startup, it controls
681 access to the debugger; fortunately, the linker knows now to find it
682 without needing to link against sys.stb (VAX) or use LINK/System (Alpha).
683 We won't be calling it, but we indirectly check whether it has already
684 been called by checking if nethack.exe has it as a transfer address. */
685 extern unsigned FDECL(sys$imgsta, ());
688 * These structures are in header files contained in sys$lib_c.tlb,
689 * but that isn't available on sufficiently old versions of VMS.
690 * Construct our own: partly stubs, with simpler field names and
691 * without ugly unions. Contents derived from Bliss32 definitions
692 * in lib.req and/or Macro32 definitions in lib.mlb.
694 struct ihd { /* (vax) image header, $IHDDEF */
695 unsigned short size, activoff;
696 unsigned char otherstuff[512 - 4];
698 struct eihd { /* extended image header, $EIHDDEF */
699 unsigned long majorid, minorid, size, isdoff, activoff;
700 unsigned char otherstuff[512 - 20];
702 struct iha { /* (vax) image header activation block, $IHADEF */
703 unsigned long trnadr1, trnadr2, trnadr3;
704 unsigned long fill_, inishr;
706 struct eiha { /* extended image header activation block, $EIHADEF */
707 unsigned long size, spare;
708 unsigned long trnadr1[2], trnadr2[2], trnadr3[2], trnadr4[2], inishr[2];
712 * We're going to use lib$initialize, not because we need or
713 * want to be called before main(), but because one of the
714 * arguments passed to a lib$initialize callback is a pointer
715 * to the image header (somewhat complex data structure which
716 * includes the memory location(s) of where to start executing)
717 * of the program being initialized. It comes in two flavors,
718 * one used by VAX and the other by Alpha and IA64.
720 * An image can have up to three transfer addresses; one of them
721 * decides whether to run under debugger control (RUN/Debug, or
722 * LINK/Debug + plain RUN), another handles lib$initialize calls
723 * if that's used, and the last is to start the program itself
724 * (a jacket built around main() for code compiled with DEC C).
725 * They aren't always all present; some might be zero/null.
726 * A shareable image (pre-linked library) usually won't have any,
727 * but can have a separate initializer (not of interest here).
729 * The transfer targets don't have fixed slots but do occur in a
731 * link link lib$initialize lib$initialize
732 * sharable /noTrace /Trace + /noTrace + /Traceback
733 * 1: (none) main debugger init-handler debugger
734 * 2: main main init-handler
737 * We check whether the first transfer address is SYS$IMGSTA().
738 * If it is, the debugger should be available to catch SS$_DEBUG
739 * exception even when we don't start up under debugger control.
740 * One extra complication: if we *do* start up under debugger
741 * control, the first address in the in-memory copy of the image
742 * header will be changed from sys$imgsta() to a value in system
743 * space. [I don't know how to reference that one symbolically,
744 * so I'm going to treat any address in system space as meaning
745 * that the debugger is available. pr]
748 /* called via lib$initialize during image activation: before main() and
749 with magic arguments; C run-time library won't be initialized yet */
752 vmsexeini(inirtn_unused, clirtn_unused, imghdr)
753 const void *inirtn_unused, *clirtn_unused;
754 const unsigned char *imghdr;
756 const struct ihd *vax_hdr;
757 const struct eihd *axp_hdr;
758 const struct iha *vax_xfr;
759 const struct eiha *axp_xfr;
760 unsigned long trnadr1;
762 (void) lib$establish(lib$sig_to_ret); /* set up condition handler */
764 * Check the first of three transfer addresses to see whether
765 * it is SYS$IMGSTA(). Note that they come from a file,
766 * where they reside as longword or quadword integers rather
767 * than function pointers. (Basically just a C type issue;
768 * casting back and forth between integer and pointer doesn't
769 * change any bits for the architectures VMS runs on.)
772 /* start with a guess rather than bothering to figure out architecture */
773 vax_hdr = (struct ihd *) imghdr;
774 if (vax_hdr->size >= 512) {
775 /* this is a VAX-specific header; addresses are longwords */
776 vax_xfr = (struct iha *) (imghdr + vax_hdr->activoff);
777 trnadr1 = vax_xfr->trnadr1;
779 /* the guess above was wrong; imghdr's first word is not
780 the size field, it's a version number component */
781 axp_hdr = (struct eihd *) imghdr;
782 /* this is an Alpha or IA64 header; addresses are quadwords
783 but we ignore the upper half which will be all 0's or 0xF's
784 (we hope; if not, assume it still won't matter for this test) */
785 axp_xfr = (struct eiha *) (imghdr + axp_hdr->activoff);
786 trnadr1 = axp_xfr->trnadr1[0];
788 if ((unsigned (*) ()) trnadr1 == sys$imgsta ||
789 /* check whether first transfer address points to system space
790 [we want (trnadr1 >= 0x80000000UL) but really old compilers
791 don't support the UL suffix, so do a signed compare instead] */
794 return 1; /* success (return value here doesn't actually matter) */
798 * Setting up lib$initialize transfer block is trivial with Macro32,
799 * but we don't want to introduce use of assembler code. Doing it
800 * with C requires jiggery-pokery here and again when linking, and
801 * may not work with some compiler versions. The lib$initialize
802 * transfer block is an open-ended array of 32-bit routine addresses
803 * in a psect named "lib$initialize" with particular attributes (one
804 * being "concatenate" so that multiple instances of lib$initialize
805 * are appended rather than overwriting each other).
807 * VAX C made global variables become named program sections, to be
808 * compatable with Fortran COMMON blocks, simplifying mixed-language
809 * programs. GNU C for VAX/VMS did the same, to be compatable with
810 * VAX C. By default, DEC C makes global variables be global symbols
811 * instead, with its /Extern_Model=Relaxed_Ref_Def mode, but can be
812 * told to be VAX C compatable by using /Extern_Model=Common_Block.
814 * We don't want to force that for the whole program; occasional use
815 * of /Extern_Model=Strict_Ref_Def to find mistakes is too useful.
816 * Also, using symbols instead of psects is more robust when linking
817 * with an object library if the module defining the symbol contains
818 * only data. With a psect, any declaration is enough to become a
819 * definition and the linker won't bother hunting through a library
820 * to find another one unless explicitly told to do so. Bad news
821 * if that other one happens to include the intended initial value
822 * and someone bypasses `make' to link interactively but neglects
823 * to give the linker enough explicit directions. Linking like that
824 * would work, but the program wouldn't.
826 * So, we switch modes for this hack only. Besides, psect attributes
827 * for lib$initialize are different from the ones used for ordinary
828 * variables, so we'd need to resort to some linker magic anyway.
829 * (With assembly language, in addtion to having full control of the
830 * psect attributes in the source code, Macro32 would include enough
831 * information in its object file such that linker wouldn't need any
832 * extra instructions from us to make this work.) [If anyone links
833 * manually now and neglects the esoteric details, vmsexeini() won't
834 * get called and `debuggable' will stay 0, so lib$signal(SS$_DEBUG)
835 * will be avoided even when its use is viable. But the program will
836 * still work correctly.]
838 #define C_LIB$INITIALIZE /* comment out if this won't compile... */
839 /* (then `debuggable' will always stay 0) */
840 #ifdef C_LIB$INITIALIZE
842 #pragma extern_model save /* push current mode */
843 #pragma extern_model common_block /* set new mode */
845 /* values are 32-bit function addresses; pointers might be 64 so avoid them */
846 extern const unsigned long lib$initialize[1]; /* size is actually variable */
847 const unsigned long lib$initialize[] = { (unsigned long) (void *) vmsexeini };
849 #pragma extern_model restore /* pop previous mode */
851 /* We also need to link against a linker options file containing:
852 sys$library:starlet.olb/Include=(lib$initialize)
853 psect_attr=lib$initialize, Con,Usr,noPic,Rel,Gbl,noShr,noExe,Rd,noWrt,Long
855 #endif /* C_LIB$INITIALIZE */
856 /* End of debugger hackery. */