1 /*	SCCS Id: @(#)vmsfiles.c 3.3	1999/08/29	*/
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 /*
6  *  VMS-specific file manipulation routines to implement some missing
7  *  routines or substitute for ones where we want behavior modification.
8  */
9 #include "config.h"
10 #include <ctype.h>
11 
12 /* lint supression due to lack of extern.h */
13 int FDECL(vms_link, (const char *,const char *));
14 int FDECL(vms_unlink, (const char *));
15 int FDECL(vms_creat, (const char *,unsigned int));
16 int FDECL(vms_open, (const char *,int,unsigned int));
17 boolean FDECL(same_dir, (const char *,const char *));
18 int FDECL(c__translate, (int));
19 char *FDECL(vms_basename, (const char *));
20 
21 #include <rms.h>
22 #if 0
23 #include <psldef.h>
24 #else
25 #define PSL$C_EXEC 1	/* executive mode, for priv'd logical name handling */
26 #endif
27 #include <errno.h>
28 #ifndef C$$TRANSLATE	/* don't rely on VAXCRTL's internal routine */
29 #define C$$TRANSLATE(status) (errno = EVMSERR,  vaxc$errno = (status))
30 #endif
31 extern unsigned long sys$parse(), sys$search(), sys$enter(), sys$remove();
32 extern int VDECL(lib$match_cond, (int,int,...));
33 
34 #define vms_success(sts) ((sts)&1)		/* odd, */
35 #define vms_failure(sts) (!vms_success(sts))	/* even */
36 
37 /* vms_link() -- create an additional directory for an existing file */
vms_link(file,new)38 int vms_link(file, new)
39 const char *file, *new;
40 {
41     struct FAB fab;
42     struct NAM nam;
43     unsigned short fid[3];
44     char esa[NAM$C_MAXRSS];
45 
46     fab = cc$rms_fab;	/* set block ID and length, zero the rest */
47     fab.fab$l_fop = FAB$M_OFP;
48     fab.fab$l_fna = (char *) file;
49     fab.fab$b_fns = strlen(file);
50     fab.fab$l_nam = &nam;
51     nam = cc$rms_nam;
52     nam.nam$l_esa = esa;
53     nam.nam$b_ess = sizeof esa;
54 
55     if (vms_success(sys$parse(&fab)) && vms_success(sys$search(&fab))) {
56 	fid[0] = nam.nam$w_fid[0];
57 	fid[1] = nam.nam$w_fid[1];
58 	fid[2] = nam.nam$w_fid[2];
59 	fab.fab$l_fna = (char *) new;
60 	fab.fab$b_fns = strlen(new);
61 
62 	if (vms_success(sys$parse(&fab))) {
63 	    nam.nam$w_fid[0] = fid[0];
64 	    nam.nam$w_fid[1] = fid[1];
65 	    nam.nam$w_fid[2] = fid[2];
66 	    nam.nam$l_esa = nam.nam$l_name;
67 	    nam.nam$b_esl = nam.nam$b_name + nam.nam$b_type + nam.nam$b_ver;
68 
69 	    (void) sys$enter(&fab);
70 	}
71     }
72 
73     if (vms_failure(fab.fab$l_sts)) {
74 	C$$TRANSLATE(fab.fab$l_sts);
75 	return -1;
76     }
77     return 0;	/* success */
78 }
79 
80 /*
81    vms_unlink() -- remove a directory entry for a file; should only be used
82    for files which have had extra directory entries added, not for deletion
83    (because the file won't be deleted, just made inaccessible!).
84  */
vms_unlink(file)85 int vms_unlink(file)
86 const char *file;
87 {
88     struct FAB fab;
89     struct NAM nam;
90     char esa[NAM$C_MAXRSS];
91 
92     fab = cc$rms_fab;	/* set block ID and length, zero the rest */
93     fab.fab$l_fop = FAB$M_DLT;
94     fab.fab$l_fna = (char *) file;
95     fab.fab$b_fns = strlen(file);
96     fab.fab$l_nam = &nam;
97     nam = cc$rms_nam;
98     nam.nam$l_esa = esa;
99     nam.nam$b_ess = sizeof esa;
100 
101     if (vms_failure(sys$parse(&fab)) || vms_failure(sys$remove(&fab))) {
102 	C$$TRANSLATE(fab.fab$l_sts);
103 	return -1;
104     }
105     return 0;
106 }
107 
108 /*
109    Substitute creat() routine -- if trying to create a specific version,
110    explicitly remove an existing file of the same name.  Since it's only
111    used when we expect exclusive access, add a couple RMS options for
112    optimization.  (Don't allow sharing--eliminates coordination overhead,
113    and use 32 block buffer for faster throughput; ~30% speedup measured.)
114  */
115 #undef creat
vms_creat(file,mode)116 int vms_creat(file, mode)
117 const char *file;
118 unsigned int mode;
119 {
120     if (index(file, ';')) {
121 	/* assumes remove or delete, not vms_unlink */
122 	if (!unlink(file)) {
123 	    (void)sleep(1);
124 	    (void)unlink(file);
125 	}
126     }
127     return creat(file, mode, "shr=nil", "mbc=32", "mbf=2", "rop=wbh");
128 }
129 
130 /*
131    Similar substitute for open() -- if an open attempt fails due to being
132    locked by another user, retry it once (work-around for a limitation of
133    at least one NFS implementation).
134  */
135 #undef open
vms_open(file,flags,mode)136 int vms_open(file, flags, mode)
137 const char *file;
138 int flags;
139 unsigned int mode;
140 {
141     int fd = open(file, flags, mode, "mbc=32", "mbf=2", "rop=rah");
142 
143     if (fd < 0 && errno == EVMSERR && lib$match_cond(vaxc$errno, RMS$_FLK)) {
144 	(void)sleep(1);
145 	fd = open(file, flags, mode, "mbc=32", "mbf=2", "rop=rah");
146     }
147     return fd;
148 }
149 
150 /*
151    Determine whether two strings contain the same directory name.
152    Used for deciding whether installed privileges should be disabled
153    when HACKDIR is defined in the environment (or specified via -d on
154    the command line).  This version doesn't handle Unix-style file specs.
155  */
156 boolean
same_dir(d1,d2)157 same_dir(d1, d2)
158 const char *d1, *d2;
159 {
160     if (!d1 || !*d1 || !d2 || !*d2)
161 	return FALSE;
162     else if (!strcmp(d1, d2))	/* strcmpi() would be better, but that leads */
163 	return TRUE;		/* to linking problems for the utilities */
164     else {
165 	struct FAB f1, f2;
166 	struct NAM n1, n2;
167 
168 	f1 = f2 = cc$rms_fab;	/* initialize file access block */
169 	n1 = n2 = cc$rms_nam;	/* initialize name block */
170 	f1.fab$b_acmodes = PSL$C_EXEC << FAB$V_LNM_MODE;
171 	f1.fab$b_fns = strlen( f1.fab$l_fna = (char *)d1 );
172 	f2.fab$b_fns = strlen( f2.fab$l_fna = (char *)d2 );
173 	f1.fab$l_nam = (genericptr_t)&n1;	/* link nam to fab */
174 	f2.fab$l_nam = (genericptr_t)&n2;
175 	n1.nam$b_nop = n2.nam$b_nop = NAM$M_NOCONCEAL; /* want true device name */
176 
177 	return (vms_success(sys$parse(&f1)) && vms_success(sys$parse(&f2))
178 	     && n1.nam$t_dvi[0] == n2.nam$t_dvi[0]
179 	     && !strncmp(&n1.nam$t_dvi[1], &n2.nam$t_dvi[1], n1.nam$t_dvi[0])
180 	     && !memcmp((genericptr_t)n1.nam$w_did,
181 			(genericptr_t)n2.nam$w_did,
182 			sizeof n1.nam$w_did));	/*{ short nam$w_did[3]; }*/
183     }
184 }
185 
186 
187 /*
188  * c__translate -- substitute for VAXCRTL routine C$$TRANSLATE.
189  *
190  *	Try to convert a VMS status code into its Unix equivalent,
191  *	then set `errno' to that value; use EVMSERR if there's no
192  *	appropriate translation; set `vaxc$errno' to the original
193  *	status code regardless.
194  *
195  *	These translations match only a subset of VAXCRTL's lookup
196  *	table, but work even if the severity has been adjusted or
197  *	the inhibit-message bit has been set.
198  */
199 #include <errno.h>
200 #include <ssdef.h>
201 #include <rmsdef.h>
202 /* #include <libdef.h> */
203 /* #include <mthdef.h> */
204 
205 #define VALUE(U)	trans = U; break
206 #define CASE1(V)	case (V >> 3)
207 #define CASE2(V,W)	CASE1(V): CASE1(W)
208 
c__translate(code)209 int c__translate(code)
210     int code;
211 {
212     register int trans;
213 
214     switch ((code & 0x0FFFFFF8) >> 3) {	/* strip upper 4 and bottom 3 bits */
215 	CASE2(RMS$_PRV,SS$_NOPRIV):
216 				VALUE(EPERM);	/* not owner */
217 	CASE2(RMS$_DNF,RMS$_DIR):
218 	CASE2(RMS$_FNF,RMS$_FND):
219 	CASE1(SS$_NOSUCHFILE):
220 				VALUE(ENOENT);	/* no such file or directory */
221 	CASE2(RMS$_IFI,RMS$_ISI):
222 				VALUE(EIO);	/* i/o error */
223 	CASE1(RMS$_DEV):
224 	CASE2(SS$_NOSUCHDEV,SS$_DEVNOTMOUNT):
225 				VALUE(ENXIO);	/* no such device or address codes */
226 	CASE1(RMS$_DME):
227      /* CASE1(LIB$INSVIRMEM): */
228 	CASE2(SS$_VASFULL,SS$_INSFWSL):
229 				VALUE(ENOMEM);	/* not enough core */
230 	CASE1(SS$_ACCVIO):
231 				VALUE(EFAULT);	/* bad address */
232 	CASE2(RMS$_DNR,SS$_DEVASSIGN):
233 	CASE2(SS$_DEVALLOC,SS$_DEVALRALLOC):
234 	CASE2(SS$_DEVMOUNT,SS$_DEVACTIVE):
235 				VALUE(EBUSY);	/* mount device busy codes to name a few */
236 	CASE2(RMS$_FEX,SS$_FILALRACC):
237 				VALUE(EEXIST);	/* file exists */
238 	CASE2(RMS$_IDR,SS$_BADIRECTORY):
239 				VALUE(ENOTDIR);	/* not a directory */
240 	CASE1(SS$_NOIOCHAN):
241 				VALUE(EMFILE);	/* too many open files */
242 	CASE1(RMS$_FUL):
243 	CASE2(SS$_DEVICEFULL,SS$_EXDISKQUOTA):
244 				VALUE(ENOSPC);	/* no space left on disk codes */
245 	CASE2(RMS$_WLK,SS$_WRITLCK):
246 				VALUE(EROFS);	/* read-only file system */
247 	default:
248 				VALUE(EVMSERR);
249     };
250 
251     errno = trans;
252     vaxc$errno = code;
253     return code;	/* (not very useful) */
254 }
255 
256 #undef VALUE
257 #undef CASE1
258 #undef CASE2
259 
260 static char base_name[NAM$C_MAXRSS+1];
261 
262 /* return a copy of the 'base' portion of a filename */
263 char *
vms_basename(name)264 vms_basename(name)
265 const char *name;
266 {
267     unsigned len;
268     char *base, *base_p;
269     register const char *name_p;
270 
271     /* skip directory/path */
272     if ((name_p = strrchr(name, ']')) != 0) name = name_p + 1;
273     if ((name_p = strrchr(name, '>')) != 0) name = name_p + 1;
274     if ((name_p = strrchr(name, ':')) != 0) name = name_p + 1;
275     if ((name_p = strrchr(name, '/')) != 0) name = name_p + 1;
276     if (!*name) name = ".";		/* this should never happen */
277 
278     /* find extension/version and derive length of basename */
279     if ((name_p = strchr(name, '.')) == 0 || name_p == name)
280 	name_p = strchr(name, ';');
281     len = (name_p && name_p > name) ? name_p - name : strlen(name);
282 
283     /* return a lowercase copy of the name in a private static buffer */
284     base = strncpy(base_name, name, len);
285     base[len] = '\0';
286     /* we don't use lcase() so that utilities won't need hacklib.c */
287     for (base_p = base; base_p < &base[len]; base_p++)
288 	if (isupper(*base_p)) *base_p = tolower(*base_p);
289 
290     return base;
291 }
292 
293 /*vmsfiles.c*/
294