Fix the problem From a bug report. His system has a logical name "DATA" pointing at some disk, and
when the dlb utility tried to open "data" for inclusion in the library
being built at install time, it attempted to access the wrong thing and
failed. He then attempted to fix it in a manner which let dlb finish, by
modifying dlb_main.c to append "." to file names that lack a dot, but
then nethack couldn't access "dungeon" in the library because string
comparison didn't match the altered dlb directory entry of "dungeon.".
NetHack was working around this unintended interaction with the
environment issue in fopen_datafile(), and dlb was doing so for fopen()
but not open(). This moves nethack's fixup out of src/files.c and into
sys/vms/vmsfiles.c, adds another routine there so that both open() and
fopen() are covered, and updates the vms Makefiles so that the various
utility programs all link with vmsfiles. (The build script vmsbuild.com
puts object files into a library and gets that last bit for free.)
328 lines
9.6 KiB
C
328 lines
9.6 KiB
C
/* SCCS Id: @(#)vmsfiles.c 3.5 2007/10/27 */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
/*
|
|
* VMS-specific file manipulation routines to implement some missing
|
|
* routines or substitute for ones where we want behavior modification.
|
|
*/
|
|
#include "config.h"
|
|
#include <ctype.h>
|
|
|
|
/* lint supression due to lack of extern.h */
|
|
int FDECL(vms_link, (const char *,const char *));
|
|
int FDECL(vms_unlink, (const char *));
|
|
int FDECL(vms_creat, (const char *,unsigned int));
|
|
boolean FDECL(same_dir, (const char *,const char *));
|
|
int FDECL(c__translate, (int));
|
|
|
|
#include <rms.h>
|
|
#if 0
|
|
#include <psldef.h>
|
|
#else
|
|
#define PSL$C_EXEC 1 /* executive mode, for priv'd logical name handling */
|
|
#endif
|
|
#include <errno.h>
|
|
#ifndef C$$TRANSLATE /* don't rely on VAXCRTL's internal routine */
|
|
#define C$$TRANSLATE(status) (errno = EVMSERR, vaxc$errno = (status))
|
|
#endif
|
|
extern unsigned long sys$parse(), sys$search(), sys$enter(), sys$remove();
|
|
extern int VDECL(lib$match_cond, (int,int,...));
|
|
|
|
#define vms_success(sts) ((sts)&1) /* odd, */
|
|
#define vms_failure(sts) (!vms_success(sts)) /* even */
|
|
|
|
/* vms_link() -- create an additional directory for an existing file */
|
|
int vms_link(file, new)
|
|
const char *file, *new;
|
|
{
|
|
struct FAB fab;
|
|
struct NAM nam;
|
|
unsigned short fid[3];
|
|
char esa[NAM$C_MAXRSS];
|
|
|
|
fab = cc$rms_fab; /* set block ID and length, zero the rest */
|
|
fab.fab$l_fop = FAB$M_OFP;
|
|
fab.fab$l_fna = (char *) file;
|
|
fab.fab$b_fns = strlen(file);
|
|
fab.fab$l_nam = &nam;
|
|
nam = cc$rms_nam;
|
|
nam.nam$l_esa = esa;
|
|
nam.nam$b_ess = sizeof esa;
|
|
|
|
if (vms_success(sys$parse(&fab)) && vms_success(sys$search(&fab))) {
|
|
fid[0] = nam.nam$w_fid[0];
|
|
fid[1] = nam.nam$w_fid[1];
|
|
fid[2] = nam.nam$w_fid[2];
|
|
fab.fab$l_fna = (char *) new;
|
|
fab.fab$b_fns = strlen(new);
|
|
|
|
if (vms_success(sys$parse(&fab))) {
|
|
nam.nam$w_fid[0] = fid[0];
|
|
nam.nam$w_fid[1] = fid[1];
|
|
nam.nam$w_fid[2] = fid[2];
|
|
nam.nam$l_esa = nam.nam$l_name;
|
|
nam.nam$b_esl = nam.nam$b_name + nam.nam$b_type + nam.nam$b_ver;
|
|
|
|
(void) sys$enter(&fab);
|
|
}
|
|
}
|
|
|
|
if (vms_failure(fab.fab$l_sts)) {
|
|
C$$TRANSLATE(fab.fab$l_sts);
|
|
return -1;
|
|
}
|
|
return 0; /* success */
|
|
}
|
|
|
|
/*
|
|
vms_unlink() -- remove a directory entry for a file; should only be used
|
|
for files which have had extra directory entries added, not for deletion
|
|
(because the file won't be deleted, just made inaccessible!).
|
|
*/
|
|
int vms_unlink(file)
|
|
const char *file;
|
|
{
|
|
struct FAB fab;
|
|
struct NAM nam;
|
|
char esa[NAM$C_MAXRSS];
|
|
|
|
fab = cc$rms_fab; /* set block ID and length, zero the rest */
|
|
fab.fab$l_fop = FAB$M_DLT;
|
|
fab.fab$l_fna = (char *) file;
|
|
fab.fab$b_fns = strlen(file);
|
|
fab.fab$l_nam = &nam;
|
|
nam = cc$rms_nam;
|
|
nam.nam$l_esa = esa;
|
|
nam.nam$b_ess = sizeof esa;
|
|
|
|
if (vms_failure(sys$parse(&fab)) || vms_failure(sys$remove(&fab))) {
|
|
C$$TRANSLATE(fab.fab$l_sts);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Substitute creat() routine -- if trying to create a specific version,
|
|
explicitly remove an existing file of the same name. Since it's only
|
|
used when we expect exclusive access, add a couple RMS options for
|
|
optimization. (Don't allow sharing--eliminates coordination overhead,
|
|
and use 32 block buffer for faster throughput; ~30% speedup measured.)
|
|
*/
|
|
#undef creat
|
|
int
|
|
vms_creat(file, mode)
|
|
const char *file;
|
|
unsigned int mode;
|
|
{
|
|
char filnambuf[BUFSIZ]; /*(not BUFSZ)*/
|
|
|
|
if (index(file, ';')) {
|
|
/* assumes remove or delete, not vms_unlink */
|
|
if (!unlink(file)) {
|
|
(void)sleep(1);
|
|
(void)unlink(file);
|
|
}
|
|
} else if (!index(file, '.')) {
|
|
/* force some punctuation to be present */
|
|
file = strcat(strcpy(filnambuf, file), ".");
|
|
}
|
|
return creat(file, mode, "shr=nil", "mbc=32", "mbf=2", "rop=wbh");
|
|
}
|
|
|
|
/*
|
|
Similar substitute for open() -- if an open attempt fails due to being
|
|
locked by another user, retry it once (work-around for a limitation of
|
|
at least one NFS implementation).
|
|
*/
|
|
#undef open
|
|
int
|
|
vms_open(file, flags, mode)
|
|
const char *file;
|
|
int flags;
|
|
unsigned int mode;
|
|
{
|
|
char filnambuf[BUFSIZ]; /*(not BUFSZ)*/
|
|
int fd;
|
|
|
|
if (!index(file, '.') && !index(file, ';')) {
|
|
/* force some punctuation to be present to make sure that
|
|
the file name can't accidentally match a logical name */
|
|
file = strcat(strcpy(filnambuf, file), ";0");
|
|
}
|
|
fd = open(file, flags, mode, "mbc=32", "mbf=2", "rop=rah");
|
|
if (fd < 0 && errno == EVMSERR && lib$match_cond(vaxc$errno, RMS$_FLK)) {
|
|
(void)sleep(1);
|
|
fd = open(file, flags, mode, "mbc=32", "mbf=2", "rop=rah");
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
/* do likewise for fopen() */
|
|
#undef fopen
|
|
FILE *
|
|
vms_fopen(file, mode)
|
|
const char *file, *mode;
|
|
{
|
|
char filnambuf[BUFSIZ]; /*(not BUFSZ)*/
|
|
FILE *fp;
|
|
|
|
if (!index(file, '.') && !index(file, ';')) {
|
|
/* force some punctuation to be present to make sure that
|
|
the file name can't accidentally match a logical name */
|
|
file = strcat(strcpy(filnambuf, file), ";0");
|
|
}
|
|
fp = fopen(file, mode, "mbc=32", "mbf=2", "rop=rah");
|
|
if (!fp && errno == EVMSERR && lib$match_cond(vaxc$errno, RMS$_FLK)) {
|
|
(void)sleep(1);
|
|
fp = fopen(file, mode, "mbc=32", "mbf=2", "rop=rah");
|
|
}
|
|
return fp;
|
|
}
|
|
|
|
/*
|
|
Determine whether two strings contain the same directory name.
|
|
Used for deciding whether installed privileges should be disabled
|
|
when HACKDIR is defined in the environment (or specified via -d on
|
|
the command line). This version doesn't handle Unix-style file specs.
|
|
*/
|
|
boolean
|
|
same_dir(d1, d2)
|
|
const char *d1, *d2;
|
|
{
|
|
if (!d1 || !*d1 || !d2 || !*d2)
|
|
return FALSE;
|
|
else if (!strcmp(d1, d2)) /* strcmpi() would be better, but that leads */
|
|
return TRUE; /* to linking problems for the utilities */
|
|
else {
|
|
struct FAB f1, f2;
|
|
struct NAM n1, n2;
|
|
|
|
f1 = f2 = cc$rms_fab; /* initialize file access block */
|
|
n1 = n2 = cc$rms_nam; /* initialize name block */
|
|
f1.fab$b_acmodes = PSL$C_EXEC << FAB$V_LNM_MODE;
|
|
f1.fab$b_fns = strlen( f1.fab$l_fna = (char *)d1 );
|
|
f2.fab$b_fns = strlen( f2.fab$l_fna = (char *)d2 );
|
|
f1.fab$l_nam = (genericptr_t)&n1; /* link nam to fab */
|
|
f2.fab$l_nam = (genericptr_t)&n2;
|
|
n1.nam$b_nop = n2.nam$b_nop = NAM$M_NOCONCEAL; /* want true device name */
|
|
|
|
return (vms_success(sys$parse(&f1)) && vms_success(sys$parse(&f2))
|
|
&& n1.nam$t_dvi[0] == n2.nam$t_dvi[0]
|
|
&& !strncmp(&n1.nam$t_dvi[1], &n2.nam$t_dvi[1], n1.nam$t_dvi[0])
|
|
&& !memcmp((genericptr_t)n1.nam$w_did,
|
|
(genericptr_t)n2.nam$w_did,
|
|
sizeof n1.nam$w_did)); /*{ short nam$w_did[3]; }*/
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* c__translate -- substitute for VAXCRTL routine C$$TRANSLATE.
|
|
*
|
|
* Try to convert a VMS status code into its Unix equivalent,
|
|
* then set `errno' to that value; use EVMSERR if there's no
|
|
* appropriate translation; set `vaxc$errno' to the original
|
|
* status code regardless.
|
|
*
|
|
* These translations match only a subset of VAXCRTL's lookup
|
|
* table, but work even if the severity has been adjusted or
|
|
* the inhibit-message bit has been set.
|
|
*/
|
|
#include <errno.h>
|
|
#include <ssdef.h>
|
|
#include <rmsdef.h>
|
|
/* #include <libdef.h> */
|
|
/* #include <mthdef.h> */
|
|
|
|
#define VALUE(U) trans = U; break
|
|
#define CASE1(V) case (V >> 3)
|
|
#define CASE2(V,W) CASE1(V): CASE1(W)
|
|
|
|
int c__translate(code)
|
|
int code;
|
|
{
|
|
register int trans;
|
|
|
|
switch ((code & 0x0FFFFFF8) >> 3) { /* strip upper 4 and bottom 3 bits */
|
|
CASE2(RMS$_PRV,SS$_NOPRIV):
|
|
VALUE(EPERM); /* not owner */
|
|
CASE2(RMS$_DNF,RMS$_DIR):
|
|
CASE2(RMS$_FNF,RMS$_FND):
|
|
CASE1(SS$_NOSUCHFILE):
|
|
VALUE(ENOENT); /* no such file or directory */
|
|
CASE2(RMS$_IFI,RMS$_ISI):
|
|
VALUE(EIO); /* i/o error */
|
|
CASE1(RMS$_DEV):
|
|
CASE2(SS$_NOSUCHDEV,SS$_DEVNOTMOUNT):
|
|
VALUE(ENXIO); /* no such device or address codes */
|
|
CASE1(RMS$_DME):
|
|
/* CASE1(LIB$INSVIRMEM): */
|
|
CASE2(SS$_VASFULL,SS$_INSFWSL):
|
|
VALUE(ENOMEM); /* not enough core */
|
|
CASE1(SS$_ACCVIO):
|
|
VALUE(EFAULT); /* bad address */
|
|
CASE2(RMS$_DNR,SS$_DEVASSIGN):
|
|
CASE2(SS$_DEVALLOC,SS$_DEVALRALLOC):
|
|
CASE2(SS$_DEVMOUNT,SS$_DEVACTIVE):
|
|
VALUE(EBUSY); /* mount device busy codes to name a few */
|
|
CASE2(RMS$_FEX,SS$_FILALRACC):
|
|
VALUE(EEXIST); /* file exists */
|
|
CASE2(RMS$_IDR,SS$_BADIRECTORY):
|
|
VALUE(ENOTDIR); /* not a directory */
|
|
CASE1(SS$_NOIOCHAN):
|
|
VALUE(EMFILE); /* too many open files */
|
|
CASE1(RMS$_FUL):
|
|
CASE2(SS$_DEVICEFULL,SS$_EXDISKQUOTA):
|
|
VALUE(ENOSPC); /* no space left on disk codes */
|
|
CASE2(RMS$_WLK,SS$_WRITLCK):
|
|
VALUE(EROFS); /* read-only file system */
|
|
default:
|
|
VALUE(EVMSERR);
|
|
};
|
|
|
|
errno = trans;
|
|
vaxc$errno = code;
|
|
return code; /* (not very useful) */
|
|
}
|
|
|
|
#undef VALUE
|
|
#undef CASE1
|
|
#undef CASE2
|
|
|
|
static char base_name[NAM$C_MAXRSS+1];
|
|
|
|
/* return a copy of the 'base' portion of a filename */
|
|
char *
|
|
vms_basename(name)
|
|
const char *name;
|
|
{
|
|
unsigned len;
|
|
char *base, *base_p;
|
|
register const char *name_p;
|
|
|
|
/* skip directory/path */
|
|
if ((name_p = strrchr(name, ']')) != 0) name = name_p + 1;
|
|
if ((name_p = strrchr(name, '>')) != 0) name = name_p + 1;
|
|
if ((name_p = strrchr(name, ':')) != 0) name = name_p + 1;
|
|
if ((name_p = strrchr(name, '/')) != 0) name = name_p + 1;
|
|
if (!*name) name = "."; /* this should never happen */
|
|
|
|
/* find extension/version and derive length of basename */
|
|
if ((name_p = strchr(name, '.')) == 0 || name_p == name)
|
|
name_p = strchr(name, ';');
|
|
len = (name_p && name_p > name) ? name_p - name : strlen(name);
|
|
|
|
/* return a lowercase copy of the name in a private static buffer */
|
|
base = strncpy(base_name, name, len);
|
|
base[len] = '\0';
|
|
/* we don't use lcase() so that utilities won't need hacklib.c */
|
|
for (base_p = base; base_p < &base[len]; base_p++)
|
|
if (isupper(*base_p)) *base_p = tolower(*base_p);
|
|
|
|
return base;
|
|
}
|
|
|
|
/*vmsfiles.c*/
|