294 lines
8.7 KiB
C
294 lines
8.7 KiB
C
/* SCCS Id: @(#)vmsfiles.c 3.5 1999/08/29 */
|
|
/* 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));
|
|
int FDECL(vms_open, (const char *,int,unsigned int));
|
|
boolean FDECL(same_dir, (const char *,const char *));
|
|
int FDECL(c__translate, (int));
|
|
char *FDECL(vms_basename, (const char *));
|
|
|
|
#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;
|
|
{
|
|
if (index(file, ';')) {
|
|
/* assumes remove or delete, not vms_unlink */
|
|
if (!unlink(file)) {
|
|
(void)sleep(1);
|
|
(void)unlink(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;
|
|
{
|
|
int 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;
|
|
}
|
|
|
|
/*
|
|
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*/
|