1 /* SCCS Id: @(#)vmsfiles.c 3.4 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