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