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