This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
newer Getopt/Long.pm from public distribution cited in:
[perl5.git] / vms / vms.c
CommitLineData
748a9306 1/* vms.c
a0d0e21e 2 *
748a9306
LW
3 * VMS-specific routines for perl5
4 *
09b7f37c
CB
5 * Last revised: 27-Feb-1998 by Charles Bailey bailey@newman.upenn.edu
6 * Version: 5.4.61
a0d0e21e
LW
7 */
8
9#include <acedef.h>
10#include <acldef.h>
11#include <armdef.h>
748a9306 12#include <atrdef.h>
a0d0e21e 13#include <chpdef.h>
8fde5078 14#include <clidef.h>
a3e9d8c9 15#include <climsgdef.h>
a0d0e21e
LW
16#include <descrip.h>
17#include <dvidef.h>
748a9306 18#include <fibdef.h>
a0d0e21e
LW
19#include <float.h>
20#include <fscndef.h>
21#include <iodef.h>
22#include <jpidef.h>
61bb5906 23#include <kgbdef.h>
a0d0e21e
LW
24#include <libdef.h>
25#include <lib$routines.h>
26#include <lnmdef.h>
748a9306 27#include <prvdef.h>
a0d0e21e
LW
28#include <psldef.h>
29#include <rms.h>
30#include <shrdef.h>
31#include <ssdef.h>
32#include <starlet.h>
f86702cc 33#include <strdef.h>
34#include <str$routines.h>
a0d0e21e 35#include <syidef.h>
748a9306
LW
36#include <uaidef.h>
37#include <uicdef.h>
a0d0e21e 38
740ce14c 39/* Older versions of ssdef.h don't have these */
40#ifndef SS$_INVFILFOROP
41# define SS$_INVFILFOROP 3930
42#endif
43#ifndef SS$_NOSUCHOBJECT
b7ae7a0d 44# define SS$_NOSUCHOBJECT 2696
45#endif
46
aa689395 47/* Don't replace system definitions of vfork, getenv, and stat,
48 * code below needs to get to the underlying CRTL routines. */
49#define DONT_MASK_RTL_CALLS
a0d0e21e
LW
50#include "EXTERN.h"
51#include "perl.h"
748a9306 52#include "XSUB.h"
a0d0e21e 53
c07a80fd 54/* gcc's header files don't #define direct access macros
55 * corresponding to VAXC's variant structs */
56#ifdef __GNUC__
482b294c 57# define uic$v_format uic$r_uic_form.uic$v_format
58# define uic$v_group uic$r_uic_form.uic$v_group
59# define uic$v_member uic$r_uic_form.uic$v_member
c07a80fd 60# define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
61# define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
62# define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
63# define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
64#endif
65
66
a0d0e21e
LW
67struct itmlst_3 {
68 unsigned short int buflen;
69 unsigned short int itmcode;
70 void *bufadr;
748a9306 71 unsigned short int *retlen;
a0d0e21e
LW
72};
73
01b8edb6 74static char *__mystrtolower(char *str)
75{
76 if (str) for (; *str; ++str) *str= tolower(*str);
77 return str;
78}
79
c07a80fd 80int
81my_trnlnm(char *lnm, char *eqv, unsigned long int idx)
748a9306
LW
82{
83 static char __my_trnlnm_eqv[LNM$C_NAMLENGTH+1];
84 unsigned short int eqvlen;
85 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
86 $DESCRIPTOR(tabdsc,"LNM$FILE_DEV");
87 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
c07a80fd 88 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
89 {LNM$C_NAMLENGTH, LNM$_STRING, 0, &eqvlen},
748a9306
LW
90 {0, 0, 0, 0}};
91
b7ae7a0d 92 if (!lnm || idx > LNM$_MAX_INDEX) {
93 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
94 }
748a9306 95 if (!eqv) eqv = __my_trnlnm_eqv;
c07a80fd 96 lnmlst[1].bufadr = (void *)eqv;
748a9306
LW
97 lnmdsc.dsc$a_pointer = lnm;
98 lnmdsc.dsc$w_length = strlen(lnm);
99 retsts = sys$trnlnm(&attr,&tabdsc,&lnmdsc,0,lnmlst);
c07a80fd 100 if (retsts == SS$_NOLOGNAM || retsts == SS$_IVLOGNAM) {
101 set_vaxc_errno(retsts); set_errno(EINVAL); return 0;
102 }
748a9306
LW
103 else if (retsts & 1) {
104 eqv[eqvlen] = '\0';
740ce14c 105 return eqvlen;
748a9306
LW
106 }
107 _ckvmssts(retsts); /* Must be an error */
c07a80fd 108 return 0; /* Not reached, assuming _ckvmssts() bails out */
109
110} /* end of my_trnlnm */
a0d0e21e
LW
111
112/* my_getenv
113 * Translate a logical name. Substitute for CRTL getenv() to avoid
114 * memory leak, and to keep my_getenv() and my_setenv() in the same
115 * domain (mostly - my_getenv() need not return a translation from
116 * the process logical name table)
117 *
61bb5906
CB
118 * Note: Uses Perl temp to store result so char * can be returned to
119 * caller; this pointer will be invalidated at next Perl statement
120 * transition.
a0d0e21e
LW
121 */
122/*{{{ char *my_getenv(char *lnm)*/
123char *
124my_getenv(char *lnm)
125{
126 static char __my_getenv_eqv[LNM$C_NAMLENGTH+1];
61bb5906 127 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2, *eqv;
c07a80fd 128 unsigned long int idx = 0;
edc7bc49 129 int trnsuccess;
61bb5906 130 SV *tmpsv;
a0d0e21e 131
61bb5906
CB
132 if (curinterp) { /* Perl interpreter running -- may be threaded */
133 /* Set up a temporary buffer for the return value; Perl will
134 * clean it up at the next statement transition */
135 tmpsv = sv_2mortal(newSVpv("",LNM$C_NAMLENGTH+1));
136 if (!tmpsv) return NULL;
137 eqv = SvPVX(tmpsv);
138 }
139 else eqv = __my_getenv_eqv; /* Assume no interpreter ==> single thread */
a0d0e21e
LW
140 for (cp1 = lnm, cp2= uplnm; *cp1; cp1++, cp2++) *cp2 = _toupper(*cp1);
141 *cp2 = '\0';
748a9306 142 if (cp1 - lnm == 7 && !strncmp(uplnm,"DEFAULT",7)) {
61bb5906
CB
143 getcwd(eqv,LNM$C_NAMLENGTH);
144 return eqv;
748a9306 145 }
a0d0e21e 146 else {
c07a80fd 147 if ((cp2 = strchr(uplnm,';')) != NULL) {
148 *cp2 = '\0';
149 idx = strtoul(cp2+1,NULL,0);
150 }
61bb5906 151 trnsuccess = my_trnlnm(uplnm,eqv,idx);
edc7bc49
CB
152 /* If we had a translation index, we're only interested in lnms */
153 if (!trnsuccess && cp2 != NULL) return Nullch;
61bb5906 154 if (trnsuccess) return eqv;
c07a80fd 155 else {
156 unsigned long int retsts;
157 struct dsc$descriptor_s symdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
61bb5906
CB
158 valdsc = {LNM$C_NAMLENGTH,DSC$K_DTYPE_T,
159 DSC$K_CLASS_S, eqv};
c07a80fd 160 symdsc.dsc$w_length = cp1 - lnm;
161 symdsc.dsc$a_pointer = uplnm;
162 retsts = lib$get_symbol(&symdsc,&valdsc,&(valdsc.dsc$w_length),0);
163 if (retsts == LIB$_INVSYMNAM) return Nullch;
164 if (retsts != LIB$_NOSUCHSYM) {
165 /* We want to return only logical names or CRTL Unix emulations */
166 if (retsts & 1) return Nullch;
167 _ckvmssts(retsts);
168 }
169 /* Try for CRTL emulation of a Unix/POSIX name */
740ce14c 170 else return getenv(uplnm);
a0d0e21e
LW
171 }
172 }
748a9306 173 return Nullch;
a0d0e21e
LW
174
175} /* end of my_getenv() */
176/*}}}*/
177
8fde5078
CB
178static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
179
180static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1e422769 181
740ce14c 182/*{{{ void prime_env_iter() */
183void
184prime_env_iter(void)
185/* Fill the %ENV associative array with all logical names we can
186 * find, in preparation for iterating over it.
187 */
188{
61bb5906 189 dTHR;
17f28c40 190 static int primed = 0;
740ce14c 191 HV *envhv = GvHVn(envgv);
8fde5078
CB
192 PerlIO *sholog;
193 char eqv[LNM$C_NAMLENGTH+1],mbxnam[LNM$C_NAMLENGTH+1],*start,*end;
194 unsigned short int chan;
195#ifndef CLI$M_TRUSTED
196# define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
197#endif
198 unsigned long int flags = CLI$M_NOWAIT | CLI$M_NOCLISYM | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
7b8d334a 199 unsigned long int i, retsts, substs = 0, wakect = 0;
740ce14c 200 STRLEN eqvlen;
201 SV *oldrs, *linesv, *eqvsv;
8fde5078
CB
202 $DESCRIPTOR(cmddsc,"Show Logical *"); $DESCRIPTOR(nldsc,"_NLA0:");
203 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(tabdsc,"DCLTABLES");
204 $DESCRIPTOR(mbxdsc,mbxnam);
61bb5906
CB
205#ifdef USE_THREADS
206 static perl_mutex primenv_mutex = PTHREAD_MUTEX_INITIALIZER;
207#endif
740ce14c 208
209 if (primed) return;
61bb5906
CB
210 MUTEX_LOCK(&primenv_mutex);
211 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
740ce14c 212 /* Perform a dummy fetch as an lval to insure that the hash table is
8fde5078 213 * set up. Otherwise, the hv_store() will turn into a nullop. */
740ce14c 214 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
7b8d334a
GS
215 /* Also, set up any "special" keys that the CRTL defines,
216 * either by itself or becasue we were called from a C program
217 * using exec[lv]e() */
218 for (i = 0; environ[i]; i++) {
219 if (!(start = strchr(environ[i],'='))) {
220 warn("Ill-formed CRTL environ value \"%s\"\n",environ[i]);
221 }
222 else {
223 start++;
224 (void) hv_store(envhv,environ[i],start - environ[i] - 1,newSVpv(start,0),0);
225 }
226 }
740ce14c 227
228 /* Now, go get the logical names */
8fde5078
CB
229 create_mbx(&chan,&mbxdsc);
230 if ((sholog = PerlIO_open(mbxnam,"r")) != Nullfp) {
231 if ((retsts = sys$dassgn(chan)) & 1) {
232 /* Be certain that subprocess is using the CLI and command tables we
233 * expect, and don't pass symbols through so that we insure that
234 * "Show Logical" can't be subverted.
235 */
236 do {
237 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,0,&substs,
238 0,&riseandshine,0,0,&clidsc,&tabdsc);
239 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
240 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
241 }
242 }
243 if (sholog == Nullfp || !(retsts & 1)) {
244 if (sholog != Nullfp) PerlIO_close(sholog);
61bb5906 245 MUTEX_UNLOCK(&primenv_mutex);
8fde5078 246 _ckvmssts(sholog == Nullfp ? vaxc$errno : retsts);
61bb5906 247 }
8fde5078 248 /* We use Perl's sv_gets to read from the pipe, since PerlIO_open is
740ce14c 249 * tied to Perl's I/O layer, so it may not return a simple FILE * */
250 oldrs = rs;
251 rs = newSVpv("\n",1);
252 linesv = newSVpv("",0);
253 while (1) {
254 if ((start = sv_gets(linesv,sholog,0)) == Nullch) {
8fde5078 255 PerlIO_close(sholog);
740ce14c 256 SvREFCNT_dec(linesv); SvREFCNT_dec(rs); rs = oldrs;
257 primed = 1;
8fde5078
CB
258 /* Wait for subprocess to clean up (we know subproc won't return 0) */
259 while (substs == 0) { sys$hiber(); wakect++;}
260 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
261 _ckvmssts(substs);
61bb5906 262 MUTEX_UNLOCK(&primenv_mutex);
740ce14c 263 return;
264 }
265 while (*start != '"' && *start != '=' && *start) start++;
266 if (*start != '"') continue;
267 for (end = ++start; *end && *end != '"'; end++) ;
268 if (*end) *end = '\0';
269 else end = Nullch;
edc7bc49
CB
270 if ((eqvlen = my_trnlnm(start,eqv,0)) == 0) {
271 if (vaxc$errno == SS$_NOLOGNAM || vaxc$errno == SS$_IVLOGNAM) {
272 if (dowarn)
273 warn("Ill-formed logical name |%s| in prime_env_iter",start);
274 continue;
275 }
61bb5906 276 else { MUTEX_UNLOCK(&primenv_mutex); _ckvmssts(vaxc$errno); }
edc7bc49 277 }
740ce14c 278 else {
279 eqvsv = newSVpv(eqv,eqvlen);
280 hv_store(envhv,start,(end ? end - start : strlen(start)),eqvsv,0);
281 }
282 }
283} /* end of prime_env_iter */
284/*}}}*/
285
286
a0d0e21e
LW
287/*{{{ void my_setenv(char *lnm, char *eqv)*/
288void
289my_setenv(char *lnm,char *eqv)
290/* Define a supervisor-mode logical name in the process table.
291 * In the future we'll add tables, attribs, and acmodes,
292 * probably through a different call.
293 */
294{
295 char uplnm[LNM$C_NAMLENGTH], *cp1, *cp2;
296 unsigned long int retsts, usermode = PSL$C_USER;
297 $DESCRIPTOR(tabdsc,"LNM$PROCESS");
298 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
299 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
300
301 for(cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) *cp2 = _toupper(*cp1);
302 lnmdsc.dsc$w_length = cp1 - lnm;
303
304 if (!eqv || !*eqv) { /* we're deleting a logical name */
305 retsts = sys$dellnm(&tabdsc,&lnmdsc,&usermode); /* try user mode first */
748a9306
LW
306 if (retsts == SS$_IVLOGNAM) return;
307 if (retsts != SS$_NOLOGNAM) _ckvmssts(retsts);
a0d0e21e
LW
308 if (!(retsts & 1)) {
309 retsts = lib$delete_logical(&lnmdsc,&tabdsc); /* then supervisor mode */
748a9306 310 if (retsts != SS$_NOLOGNAM) _ckvmssts(retsts);
a0d0e21e
LW
311 }
312 }
313 else {
314 eqvdsc.dsc$w_length = strlen(eqv);
315 eqvdsc.dsc$a_pointer = eqv;
316
748a9306 317 _ckvmssts(lib$set_logical(&lnmdsc,&eqvdsc,&tabdsc,0,0));
a0d0e21e
LW
318 }
319
320} /* end of my_setenv() */
321/*}}}*/
322
c07a80fd 323
324/*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
325/* my_crypt - VMS password hashing
326 * my_crypt() provides an interface compatible with the Unix crypt()
327 * C library function, and uses sys$hash_password() to perform VMS
328 * password hashing. The quadword hashed password value is returned
329 * as a NUL-terminated 8 character string. my_crypt() does not change
330 * the case of its string arguments; in order to match the behavior
331 * of LOGINOUT et al., alphabetic characters in both arguments must
332 * be upcased by the caller.
333 */
334char *
335my_crypt(const char *textpasswd, const char *usrname)
336{
337# ifndef UAI$C_PREFERRED_ALGORITHM
338# define UAI$C_PREFERRED_ALGORITHM 127
339# endif
340 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
341 unsigned short int salt = 0;
342 unsigned long int sts;
343 struct const_dsc {
344 unsigned short int dsc$w_length;
345 unsigned char dsc$b_type;
346 unsigned char dsc$b_class;
347 const char * dsc$a_pointer;
348 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
349 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
350 struct itmlst_3 uailst[3] = {
351 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
352 { sizeof salt, UAI$_SALT, &salt, 0},
353 { 0, 0, NULL, NULL}};
354 static char hash[9];
355
356 usrdsc.dsc$w_length = strlen(usrname);
357 usrdsc.dsc$a_pointer = usrname;
358 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
359 switch (sts) {
360 case SS$_NOGRPPRV:
361 case SS$_NOSYSPRV:
362 set_errno(EACCES);
363 break;
364 case RMS$_RNF:
365 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
366 break;
367 default:
368 set_errno(EVMSERR);
369 }
370 set_vaxc_errno(sts);
371 if (sts != RMS$_RNF) return NULL;
372 }
373
374 txtdsc.dsc$w_length = strlen(textpasswd);
375 txtdsc.dsc$a_pointer = textpasswd;
376 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
377 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
378 }
379
380 return (char *) hash;
381
382} /* end of my_crypt() */
383/*}}}*/
384
385
bbce6d69 386static char *do_rmsexpand(char *, char *, int, char *, unsigned);
a0d0e21e
LW
387static char *do_fileify_dirspec(char *, char *, int);
388static char *do_tovmsspec(char *, char *, int);
389
390/*{{{int do_rmdir(char *name)*/
391int
392do_rmdir(char *name)
393{
394 char dirfile[NAM$C_MAXRSS+1];
395 int retval;
61bb5906 396 Stat_t st;
a0d0e21e
LW
397
398 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
399 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
400 else retval = kill_file(dirfile);
401 return retval;
402
403} /* end of do_rmdir */
404/*}}}*/
405
406/* kill_file
407 * Delete any file to which user has control access, regardless of whether
408 * delete access is explicitly allowed.
409 * Limitations: User must have write access to parent directory.
410 * Does not block signals or ASTs; if interrupted in midstream
411 * may leave file with an altered ACL.
412 * HANDLE WITH CARE!
413 */
414/*{{{int kill_file(char *name)*/
415int
416kill_file(char *name)
417{
bbce6d69 418 char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
a0d0e21e 419 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
748a9306 420 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
a0d0e21e
LW
421 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
422 struct myacedef {
748a9306
LW
423 unsigned char myace$b_length;
424 unsigned char myace$b_type;
425 unsigned short int myace$w_flags;
426 unsigned long int myace$l_access;
427 unsigned long int myace$l_ident;
a0d0e21e
LW
428 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
429 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
430 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
431 struct itmlst_3
748a9306
LW
432 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
433 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
434 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
435 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
436 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
437 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
a0d0e21e 438
bbce6d69 439 /* Expand the input spec using RMS, since the CRTL remove() and
440 * system services won't do this by themselves, so we may miss
441 * a file "hiding" behind a logical name or search list. */
442 if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
443 if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
444 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
740ce14c 445 /* If not, can changing protections help? */
446 if (vaxc$errno != RMS$_PRV) return -1;
a0d0e21e
LW
447
448 /* No, so we get our own UIC to use as a rights identifier,
449 * and the insert an ACE at the head of the ACL which allows us
450 * to delete the file.
451 */
748a9306 452 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
bbce6d69 453 fildsc.dsc$w_length = strlen(rspec);
454 fildsc.dsc$a_pointer = rspec;
a0d0e21e 455 cxt = 0;
748a9306 456 newace.myace$l_ident = oldace.myace$l_ident;
a0d0e21e 457 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
740ce14c 458 switch (aclsts) {
459 case RMS$_FNF:
460 case RMS$_DNF:
461 case RMS$_DIR:
462 case SS$_NOSUCHOBJECT:
463 set_errno(ENOENT); break;
464 case RMS$_DEV:
465 set_errno(ENODEV); break;
466 case RMS$_SYN:
467 case SS$_INVFILFOROP:
468 set_errno(EINVAL); break;
469 case RMS$_PRV:
470 set_errno(EACCES); break;
471 default:
472 _ckvmssts(aclsts);
473 }
748a9306 474 set_vaxc_errno(aclsts);
a0d0e21e
LW
475 return -1;
476 }
477 /* Grab any existing ACEs with this identifier in case we fail */
478 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
e518068a 479 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
480 || fndsts == SS$_NOMOREACE ) {
a0d0e21e
LW
481 /* Add the new ACE . . . */
482 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
483 goto yourroom;
748a9306 484 if ((rmsts = remove(name))) {
a0d0e21e
LW
485 /* We blew it - dir with files in it, no write priv for
486 * parent directory, etc. Put things back the way they were. */
487 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
488 goto yourroom;
489 if (fndsts & 1) {
490 addlst[0].bufadr = &oldace;
491 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
492 goto yourroom;
493 }
494 }
495 }
496
497 yourroom:
b7ae7a0d 498 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
499 /* We just deleted it, so of course it's not there. Some versions of
500 * VMS seem to return success on the unlock operation anyhow (after all
501 * the unlock is successful), but others don't.
502 */
760ac839 503 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
b7ae7a0d 504 if (aclsts & 1) aclsts = fndsts;
a0d0e21e 505 if (!(aclsts & 1)) {
748a9306
LW
506 set_errno(EVMSERR);
507 set_vaxc_errno(aclsts);
a0d0e21e
LW
508 return -1;
509 }
510
511 return rmsts;
512
513} /* end of kill_file() */
514/*}}}*/
515
8cc95fdb 516
84902520 517/*{{{int my_mkdir(char *,Mode_t)*/
8cc95fdb 518int
84902520 519my_mkdir(char *dir, Mode_t mode)
8cc95fdb 520{
521 STRLEN dirlen = strlen(dir);
522
523 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
524 * null file name/type. However, it's commonplace under Unix,
525 * so we'll allow it for a gain in portability.
526 */
527 if (dir[dirlen-1] == '/') {
528 char *newdir = savepvn(dir,dirlen-1);
529 int ret = mkdir(newdir,mode);
530 Safefree(newdir);
531 return ret;
532 }
533 else return mkdir(dir,mode);
534} /* end of my_mkdir */
535/*}}}*/
536
537
a0d0e21e
LW
538static void
539create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
540{
541 static unsigned long int mbxbufsiz;
542 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
543
544 if (!mbxbufsiz) {
545 /*
546 * Get the SYSGEN parameter MAXBUF, and the smaller of it and the
547 * preprocessor consant BUFSIZ from stdio.h as the size of the
548 * 'pipe' mailbox.
549 */
748a9306 550 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
a0d0e21e
LW
551 if (mbxbufsiz > BUFSIZ) mbxbufsiz = BUFSIZ;
552 }
748a9306 553 _ckvmssts(sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
a0d0e21e 554
748a9306 555 _ckvmssts(lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
a0d0e21e
LW
556 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
557
558} /* end of create_mbx() */
559
560/*{{{ my_popen and my_pclose*/
561struct pipe_details
562{
563 struct pipe_details *next;
740ce14c 564 PerlIO *fp; /* stdio file pointer to pipe mailbox */
748a9306
LW
565 int pid; /* PID of subprocess */
566 int mode; /* == 'r' if pipe open for reading */
567 int done; /* subprocess has completed */
568 unsigned long int completion; /* termination status of subprocess */
a0d0e21e
LW
569};
570
748a9306
LW
571struct exit_control_block
572{
573 struct exit_control_block *flink;
574 unsigned long int (*exit_routine)();
575 unsigned long int arg_count;
576 unsigned long int *status_address;
577 unsigned long int exit_status;
578};
579
a0d0e21e
LW
580static struct pipe_details *open_pipes = NULL;
581static $DESCRIPTOR(nl_desc, "NL:");
582static int waitpid_asleep = 0;
583
748a9306
LW
584static unsigned long int
585pipe_exit_routine()
586{
1e422769 587 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
588 int sts;
748a9306
LW
589
590 while (open_pipes != NULL) {
591 if (!open_pipes->done) { /* Tap them gently on the shoulder . . .*/
592 _ckvmssts(sys$forcex(&open_pipes->pid,0,&abort));
593 sleep(1);
594 }
595 if (!open_pipes->done) /* We tried to be nice . . . */
596 _ckvmssts(sys$delprc(&open_pipes->pid,0));
1e422769 597 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
598 else if (!(sts & 1)) retsts = sts;
748a9306
LW
599 }
600 return retsts;
601}
602
603static struct exit_control_block pipe_exitblock =
604 {(struct exit_control_block *) 0,
605 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
606
607
a0d0e21e 608static void
748a9306 609popen_completion_ast(struct pipe_details *thispipe)
a0d0e21e 610{
748a9306 611 thispipe->done = TRUE;
a0d0e21e
LW
612 if (waitpid_asleep) {
613 waitpid_asleep = 0;
614 sys$wake(0,0);
615 }
616}
617
8fde5078 618static PerlIO *
1e422769 619safe_popen(char *cmd, char *mode)
a0d0e21e 620{
748a9306 621 static int handler_set_up = FALSE;
a0d0e21e
LW
622 char mbxname[64];
623 unsigned short int chan;
624 unsigned long int flags=1; /* nowait - gnu c doesn't allow &1 */
625 struct pipe_details *info;
626 struct dsc$descriptor_s namdsc = {sizeof mbxname, DSC$K_DTYPE_T,
627 DSC$K_CLASS_S, mbxname},
628 cmddsc = {0, DSC$K_DTYPE_T,
629 DSC$K_CLASS_S, 0};
630
631
a3e9d8c9 632 cmddsc.dsc$w_length=strlen(cmd);
633 cmddsc.dsc$a_pointer=cmd;
634 if (cmddsc.dsc$w_length > 255) {
635 set_errno(E2BIG); set_vaxc_errno(CLI$_BUFOVF);
636 return Nullfp;
637 }
638
fc36a67e 639 New(1301,info,1,struct pipe_details);
a0d0e21e 640
a0d0e21e
LW
641 /* create mailbox */
642 create_mbx(&chan,&namdsc);
643
644 /* open a FILE* onto it */
740ce14c 645 info->fp = PerlIO_open(mbxname, mode);
a0d0e21e
LW
646
647 /* give up other channel onto it */
748a9306 648 _ckvmssts(sys$dassgn(chan));
a0d0e21e
LW
649
650 if (!info->fp)
651 return Nullfp;
652
748a9306
LW
653 info->mode = *mode;
654 info->done = FALSE;
655 info->completion=0;
656
657 if (*mode == 'r') {
658 _ckvmssts(lib$spawn(&cmddsc, &nl_desc, &namdsc, &flags,
a0d0e21e 659 0 /* name */, &info->pid, &info->completion,
748a9306 660 0, popen_completion_ast,info,0,0,0));
a0d0e21e
LW
661 }
662 else {
748a9306
LW
663 _ckvmssts(lib$spawn(&cmddsc, &namdsc, 0 /* sys$output */, &flags,
664 0 /* name */, &info->pid, &info->completion,
665 0, popen_completion_ast,info,0,0,0));
a0d0e21e
LW
666 }
667
748a9306
LW
668 if (!handler_set_up) {
669 _ckvmssts(sys$dclexh(&pipe_exitblock));
670 handler_set_up = TRUE;
671 }
a0d0e21e
LW
672 info->next=open_pipes; /* prepend to list */
673 open_pipes=info;
674
e518068a 675 forkprocess = info->pid;
a0d0e21e 676 return info->fp;
1e422769 677} /* end of safe_popen */
678
679
680/*{{{ FILE *my_popen(char *cmd, char *mode)*/
681FILE *
682my_popen(char *cmd, char *mode)
683{
684 TAINT_ENV();
685 TAINT_PROPER("popen");
686 return safe_popen(cmd,mode);
a0d0e21e 687}
1e422769 688
a0d0e21e
LW
689/*}}}*/
690
691/*{{{ I32 my_pclose(FILE *fp)*/
692I32 my_pclose(FILE *fp)
693{
694 struct pipe_details *info, *last = NULL;
748a9306 695 unsigned long int retsts;
a0d0e21e
LW
696
697 for (info = open_pipes; info != NULL; last = info, info = info->next)
698 if (info->fp == fp) break;
699
1e422769 700 if (info == NULL) { /* no such pipe open */
701 set_errno(ECHILD); /* quoth POSIX */
702 set_vaxc_errno(SS$_NONEXPR);
703 return -1;
704 }
748a9306 705
bbce6d69 706 /* If we were writing to a subprocess, insure that someone reading from
707 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
708 * produce an EOF record in the mailbox. */
709 if (info->mode != 'r') {
710 char devnam[NAM$C_MAXRSS+1], *cp;
711 unsigned long int chan, iosb[2], retsts, retsts2;
712 struct dsc$descriptor devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, devnam};
713
714 if (fgetname(info->fp,devnam)) {
715 /* It oughta be a mailbox, so fgetname should give just the device
716 * name, but just in case . . . */
717 if ((cp = strrchr(devnam,':')) != NULL) *(cp+1) = '\0';
718 devdsc.dsc$w_length = strlen(devnam);
719 _ckvmssts(sys$assign(&devdsc,&chan,0,0));
720 retsts = sys$qiow(0,chan,IO$_WRITEOF,iosb,0,0,0,0,0,0,0,0);
721 if (retsts & 1) retsts = iosb[0];
722 retsts2 = sys$dassgn(chan); /* Be sure to deassign the channel */
723 if (retsts & 1) retsts = retsts2;
724 _ckvmssts(retsts);
725 }
726 else _ckvmssts(vaxc$errno); /* Should never happen */
727 }
740ce14c 728 PerlIO_close(info->fp);
c07a80fd 729
748a9306
LW
730 if (info->done) retsts = info->completion;
731 else waitpid(info->pid,(int *) &retsts,0);
a0d0e21e 732
a0d0e21e
LW
733 /* remove from list of open pipes */
734 if (last) last->next = info->next;
735 else open_pipes = info->next;
a0d0e21e
LW
736 Safefree(info);
737
738 return retsts;
748a9306 739
a0d0e21e
LW
740} /* end of my_pclose() */
741
a0d0e21e 742/* sort-of waitpid; use only with popen() */
4fdae800 743/*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
744Pid_t
745my_waitpid(Pid_t pid, int *statusp, int flags)
a0d0e21e
LW
746{
747 struct pipe_details *info;
a0d0e21e
LW
748
749 for (info = open_pipes; info != NULL; info = info->next)
750 if (info->pid == pid) break;
751
752 if (info != NULL) { /* we know about this child */
748a9306 753 while (!info->done) {
a0d0e21e
LW
754 waitpid_asleep = 1;
755 sys$hiber();
756 }
757
758 *statusp = info->completion;
759 return pid;
760 }
761 else { /* we haven't heard of this child */
762 $DESCRIPTOR(intdsc,"0 00:00:01");
763 unsigned long int ownercode = JPI$_OWNER, ownerpid, mypid;
748a9306 764 unsigned long int interval[2],sts;
a0d0e21e 765
748a9306
LW
766 if (dowarn) {
767 _ckvmssts(lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0));
768 _ckvmssts(lib$getjpi(&ownercode,0,0,&mypid,0,0));
769 if (ownerpid != mypid)
770 warn("pid %d not a child",pid);
771 }
a0d0e21e 772
748a9306 773 _ckvmssts(sys$bintim(&intdsc,interval));
a0d0e21e 774 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
748a9306
LW
775 _ckvmssts(sys$schdwk(0,0,interval,0));
776 _ckvmssts(sys$hiber());
a0d0e21e 777 }
748a9306 778 _ckvmssts(sts);
a0d0e21e
LW
779
780 /* There's no easy way to find the termination status a child we're
781 * not aware of beforehand. If we're really interested in the future,
782 * we can go looking for a termination mailbox, or chase after the
783 * accounting record for the process.
784 */
785 *statusp = 0;
786 return pid;
787 }
788
789} /* end of waitpid() */
a0d0e21e
LW
790/*}}}*/
791/*}}}*/
792/*}}}*/
793
794/*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
795char *
796my_gconvert(double val, int ndig, int trail, char *buf)
797{
798 static char __gcvtbuf[DBL_DIG+1];
799 char *loc;
800
801 loc = buf ? buf : __gcvtbuf;
71be2cbc 802
803#ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
804 if (val < 1) {
805 sprintf(loc,"%.*g",ndig,val);
806 return loc;
807 }
808#endif
809
a0d0e21e
LW
810 if (val) {
811 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
812 return gcvt(val,ndig,loc);
813 }
814 else {
815 loc[0] = '0'; loc[1] = '\0';
816 return loc;
817 }
818
819}
820/*}}}*/
821
bbce6d69 822
823/*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
824/* Shortcut for common case of simple calls to $PARSE and $SEARCH
825 * to expand file specification. Allows for a single default file
826 * specification and a simple mask of options. If outbuf is non-NULL,
827 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
828 * the resultant file specification is placed. If outbuf is NULL, the
829 * resultant file specification is placed into a static buffer.
830 * The third argument, if non-NULL, is taken to be a default file
831 * specification string. The fourth argument is unused at present.
832 * rmesexpand() returns the address of the resultant string if
833 * successful, and NULL on error.
834 */
96e4d5b1 835static char *do_tounixspec(char *, char *, int);
836
bbce6d69 837static char *
838do_rmsexpand(char *filespec, char *outbuf, int ts, char *defspec, unsigned opts)
839{
840 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
96e4d5b1 841 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
bbce6d69 842 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
843 struct FAB myfab = cc$rms_fab;
844 struct NAM mynam = cc$rms_nam;
845 STRLEN speclen;
96e4d5b1 846 unsigned long int retsts, haslower = 0, isunix = 0;
bbce6d69 847
848 if (!filespec || !*filespec) {
849 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
850 return NULL;
851 }
852 if (!outbuf) {
fc36a67e 853 if (ts) out = New(1319,outbuf,NAM$C_MAXRSS+1,char);
bbce6d69 854 else outbuf = __rmsexpand_retbuf;
855 }
96e4d5b1 856 if ((isunix = (strchr(filespec,'/') != NULL))) {
857 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) return NULL;
858 filespec = vmsfspec;
859 }
bbce6d69 860
861 myfab.fab$l_fna = filespec;
862 myfab.fab$b_fns = strlen(filespec);
863 myfab.fab$l_nam = &mynam;
864
865 if (defspec && *defspec) {
96e4d5b1 866 if (strchr(defspec,'/') != NULL) {
867 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) return NULL;
868 defspec = tmpfspec;
869 }
bbce6d69 870 myfab.fab$l_dna = defspec;
871 myfab.fab$b_dns = strlen(defspec);
872 }
873
874 mynam.nam$l_esa = esa;
875 mynam.nam$b_ess = sizeof esa;
876 mynam.nam$l_rsa = outbuf;
877 mynam.nam$b_rss = NAM$C_MAXRSS;
878
879 retsts = sys$parse(&myfab,0,0);
880 if (!(retsts & 1)) {
17f28c40 881 mynam.nam$b_nop |= NAM$M_SYNCHK;
bbce6d69 882 if (retsts == RMS$_DNF || retsts == RMS$_DIR ||
883 retsts == RMS$_DEV || retsts == RMS$_DEV) {
bbce6d69 884 retsts = sys$parse(&myfab,0,0);
885 if (retsts & 1) goto expanded;
886 }
17f28c40
CB
887 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
888 (void) sys$parse(&myfab,0,0); /* Free search context */
bbce6d69 889 if (out) Safefree(out);
890 set_vaxc_errno(retsts);
891 if (retsts == RMS$_PRV) set_errno(EACCES);
892 else if (retsts == RMS$_DEV) set_errno(ENODEV);
893 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
894 else set_errno(EVMSERR);
895 return NULL;
896 }
897 retsts = sys$search(&myfab,0,0);
898 if (!(retsts & 1) && retsts != RMS$_FNF) {
17f28c40
CB
899 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
900 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
bbce6d69 901 if (out) Safefree(out);
902 set_vaxc_errno(retsts);
903 if (retsts == RMS$_PRV) set_errno(EACCES);
904 else set_errno(EVMSERR);
905 return NULL;
906 }
907
908 /* If the input filespec contained any lowercase characters,
909 * downcase the result for compatibility with Unix-minded code. */
910 expanded:
911 for (out = myfab.fab$l_fna; *out; out++)
912 if (islower(*out)) { haslower = 1; break; }
913 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
914 else { out = esa; speclen = mynam.nam$b_esl; }
915 if (!(mynam.nam$l_fnb & NAM$M_EXP_VER) &&
916 (!defspec || !*defspec || !strchr(myfab.fab$l_dna,';')))
917 speclen = mynam.nam$l_ver - out;
17f28c40
CB
918 if (!(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
919 (!defspec || !*defspec || defspec[myfab.fab$b_dns-1] != '.' ||
920 defspec[myfab.fab$b_dns-2] == '.'))
921 speclen = mynam.nam$l_type - out;
bbce6d69 922 /* If we just had a directory spec on input, $PARSE "helpfully"
923 * adds an empty name and type for us */
924 if (mynam.nam$l_name == mynam.nam$l_type &&
925 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
926 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
927 speclen = mynam.nam$l_name - out;
928 out[speclen] = '\0';
929 if (haslower) __mystrtolower(out);
930
931 /* Have we been working with an expanded, but not resultant, spec? */
96e4d5b1 932 /* Also, convert back to Unix syntax if necessary. */
933 if (!mynam.nam$b_rsl) {
934 if (isunix) {
935 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
936 }
937 else strcpy(outbuf,esa);
938 }
939 else if (isunix) {
940 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
941 strcpy(outbuf,tmpfspec);
942 }
17f28c40
CB
943 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
944 mynam.nam$l_rsa = NULL; mynam.nam$b_rss = 0;
945 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
bbce6d69 946 return outbuf;
947}
948/*}}}*/
949/* External entry points */
950char *rmsexpand(char *spec, char *buf, char *def, unsigned opt)
951{ return do_rmsexpand(spec,buf,0,def,opt); }
952char *rmsexpand_ts(char *spec, char *buf, char *def, unsigned opt)
953{ return do_rmsexpand(spec,buf,1,def,opt); }
954
955
a0d0e21e
LW
956/*
957** The following routines are provided to make life easier when
958** converting among VMS-style and Unix-style directory specifications.
959** All will take input specifications in either VMS or Unix syntax. On
960** failure, all return NULL. If successful, the routines listed below
748a9306 961** return a pointer to a buffer containing the appropriately
a0d0e21e
LW
962** reformatted spec (and, therefore, subsequent calls to that routine
963** will clobber the result), while the routines of the same names with
964** a _ts suffix appended will return a pointer to a mallocd string
965** containing the appropriately reformatted spec.
966** In all cases, only explicit syntax is altered; no check is made that
967** the resulting string is valid or that the directory in question
968** actually exists.
969**
970** fileify_dirspec() - convert a directory spec into the name of the
971** directory file (i.e. what you can stat() to see if it's a dir).
972** The style (VMS or Unix) of the result is the same as the style
973** of the parameter passed in.
974** pathify_dirspec() - convert a directory spec into a path (i.e.
975** what you prepend to a filename to indicate what directory it's in).
976** The style (VMS or Unix) of the result is the same as the style
977** of the parameter passed in.
978** tounixpath() - convert a directory spec into a Unix-style path.
979** tovmspath() - convert a directory spec into a VMS-style path.
980** tounixspec() - convert any file spec into a Unix-style file spec.
981** tovmsspec() - convert any file spec into a VMS-style spec.
e518068a 982**
a3e9d8c9 983** Copyright 1996 by Charles Bailey <bailey@genetics.upenn.edu>
01b8edb6 984** Permission is given to distribute this code as part of the Perl
985** standard distribution under the terms of the GNU General Public
986** License or the Perl Artistic License. Copies of each may be
987** found in the Perl standard distribution.
a0d0e21e
LW
988 */
989
990/*{{{ char *fileify_dirspec[_ts](char *path, char *buf)*/
991static char *do_fileify_dirspec(char *dir,char *buf,int ts)
992{
993 static char __fileify_retbuf[NAM$C_MAXRSS+1];
b7ae7a0d 994 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
a0d0e21e 995 char *retspec, *cp1, *cp2, *lastdir;
61bb5906 996 char trndir[NAM$C_MAXRSS+2], vmsdir[NAM$C_MAXRSS+1];
a0d0e21e 997
c07a80fd 998 if (!dir || !*dir) {
999 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
1000 }
a0d0e21e 1001 dirlen = strlen(dir);
61bb5906
CB
1002 while (dir[dirlen-1] == '/') --dirlen;
1003 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
1004 strcpy(trndir,"/sys$disk/000000");
1005 dir = trndir;
1006 dirlen = 16;
1007 }
1008 if (dirlen > NAM$C_MAXRSS) {
1009 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); return NULL;
c07a80fd 1010 }
e518068a 1011 if (!strpbrk(dir+1,"/]>:")) {
1012 strcpy(trndir,*dir == '/' ? dir + 1: dir);
c07a80fd 1013 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) ;
e518068a 1014 dir = trndir;
1015 dirlen = strlen(dir);
1016 }
01b8edb6 1017 else {
1018 strncpy(trndir,dir,dirlen);
1019 trndir[dirlen] = '\0';
1020 dir = trndir;
1021 }
c07a80fd 1022 /* If we were handed a rooted logical name or spec, treat it like a
1023 * simple directory, so that
1024 * $ Define myroot dev:[dir.]
1025 * ... do_fileify_dirspec("myroot",buf,1) ...
1026 * does something useful.
1027 */
1028 if (!strcmp(dir+dirlen-2,".]")) {
1029 dir[--dirlen] = '\0';
1030 dir[dirlen-1] = ']';
1031 }
e518068a 1032
b7ae7a0d 1033 if ((cp1 = strrchr(dir,']')) != NULL || (cp1 = strrchr(dir,'>')) != NULL) {
1034 /* If we've got an explicit filename, we can just shuffle the string. */
1035 if (*(cp1+1)) hasfilename = 1;
1036 /* Similarly, we can just back up a level if we've got multiple levels
1037 of explicit directories in a VMS spec which ends with directories. */
1038 else {
1039 for (cp2 = cp1; cp2 > dir; cp2--) {
1040 if (*cp2 == '.') {
1041 *cp2 = *cp1; *cp1 = '\0';
1042 hasfilename = 1;
1043 break;
1044 }
1045 if (*cp2 == '[' || *cp2 == '<') break;
1046 }
1047 }
1048 }
1049
1050 if (hasfilename || !strpbrk(dir,"]:>")) { /* Unix-style path or filename */
748a9306
LW
1051 if (dir[0] == '.') {
1052 if (dir[1] == '\0' || (dir[1] == '/' && dir[2] == '\0'))
1053 return do_fileify_dirspec("[]",buf,ts);
1054 else if (dir[1] == '.' &&
1055 (dir[2] == '\0' || (dir[2] == '/' && dir[3] == '\0')))
1056 return do_fileify_dirspec("[-]",buf,ts);
1057 }
a0d0e21e
LW
1058 if (dir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
1059 dirlen -= 1; /* to last element */
1060 lastdir = strrchr(dir,'/');
1061 }
01b8edb6 1062 else if ((cp1 = strstr(dir,"/.")) != NULL) {
1063 /* If we have "/." or "/..", VMSify it and let the VMS code
1064 * below expand it, rather than repeating the code to handle
1065 * relative components of a filespec here */
4633a7c4
LW
1066 do {
1067 if (*(cp1+2) == '.') cp1++;
1068 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
01b8edb6 1069 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
fc1ce8cc
CB
1070 if (strchr(vmsdir,'/') != NULL) {
1071 /* If do_tovmsspec() returned it, it must have VMS syntax
1072 * delimiters in it, so it's a mixed VMS/Unix spec. We take
1073 * the time to check this here only so we avoid a recursion
1074 * loop; otherwise, gigo.
1075 */
1076 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); return NULL;
1077 }
01b8edb6 1078 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
1079 return do_tounixspec(trndir,buf,ts);
4633a7c4
LW
1080 }
1081 cp1++;
1082 } while ((cp1 = strstr(cp1,"/.")) != NULL);
17f28c40 1083 lastdir = strrchr(dir,'/');
748a9306 1084 }
61bb5906
CB
1085 else if (!strcmp(&dir[dirlen-7],"/000000")) {
1086 /* Ditto for specs that end in an MFD -- let the VMS code
1087 * figure out whether it's a real device or a rooted logical. */
1088 dir[dirlen] = '/'; dir[dirlen+1] = '\0';
1089 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
1090 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
1091 return do_tounixspec(trndir,buf,ts);
1092 }
a0d0e21e 1093 else {
b7ae7a0d 1094 if ( !(lastdir = cp1 = strrchr(dir,'/')) &&
1095 !(lastdir = cp1 = strrchr(dir,']')) &&
1096 !(lastdir = cp1 = strrchr(dir,'>'))) cp1 = dir;
a0d0e21e 1097 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
b7ae7a0d 1098 int ver; char *cp3;
1099 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
1100 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
1101 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
1102 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
1103 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
1104 (ver || *cp3)))))) {
1105 set_errno(ENOTDIR);
748a9306 1106 set_vaxc_errno(RMS$_DIR);
a0d0e21e
LW
1107 return NULL;
1108 }
b7ae7a0d 1109 dirlen = cp2 - dir;
a0d0e21e 1110 }
748a9306
LW
1111 }
1112 /* If we lead off with a device or rooted logical, add the MFD
1113 if we're specifying a top-level directory. */
1114 if (lastdir && *dir == '/') {
1115 addmfd = 1;
1116 for (cp1 = lastdir - 1; cp1 > dir; cp1--) {
1117 if (*cp1 == '/') {
1118 addmfd = 0;
1119 break;
a0d0e21e
LW
1120 }
1121 }
748a9306 1122 }
4633a7c4 1123 retlen = dirlen + (addmfd ? 13 : 6);
748a9306 1124 if (buf) retspec = buf;
fc36a67e 1125 else if (ts) New(1309,retspec,retlen+1,char);
748a9306
LW
1126 else retspec = __fileify_retbuf;
1127 if (addmfd) {
1128 dirlen = lastdir - dir;
1129 memcpy(retspec,dir,dirlen);
1130 strcpy(&retspec[dirlen],"/000000");
1131 strcpy(&retspec[dirlen+7],lastdir);
1132 }
1133 else {
1134 memcpy(retspec,dir,dirlen);
1135 retspec[dirlen] = '\0';
a0d0e21e
LW
1136 }
1137 /* We've picked up everything up to the directory file name.
1138 Now just add the type and version, and we're set. */
1139 strcat(retspec,".dir;1");
1140 return retspec;
1141 }
1142 else { /* VMS-style directory spec */
01b8edb6 1143 char esa[NAM$C_MAXRSS+1], term, *cp;
1144 unsigned long int sts, cmplen, haslower = 0;
a0d0e21e
LW
1145 struct FAB dirfab = cc$rms_fab;
1146 struct NAM savnam, dirnam = cc$rms_nam;
1147
1148 dirfab.fab$b_fns = strlen(dir);
1149 dirfab.fab$l_fna = dir;
1150 dirfab.fab$l_nam = &dirnam;
748a9306
LW
1151 dirfab.fab$l_dna = ".DIR;1";
1152 dirfab.fab$b_dns = 6;
a0d0e21e
LW
1153 dirnam.nam$b_ess = NAM$C_MAXRSS;
1154 dirnam.nam$l_esa = esa;
01b8edb6 1155
1156 for (cp = dir; *cp; cp++)
1157 if (islower(*cp)) { haslower = 1; break; }
e518068a 1158 if (!((sts = sys$parse(&dirfab))&1)) {
1159 if (dirfab.fab$l_sts == RMS$_DIR) {
1160 dirnam.nam$b_nop |= NAM$M_SYNCHK;
1161 sts = sys$parse(&dirfab) & 1;
1162 }
1163 if (!sts) {
748a9306
LW
1164 set_errno(EVMSERR);
1165 set_vaxc_errno(dirfab.fab$l_sts);
a0d0e21e
LW
1166 return NULL;
1167 }
e518068a 1168 }
1169 else {
1170 savnam = dirnam;
1171 if (sys$search(&dirfab)&1) { /* Does the file really exist? */
1172 /* Yes; fake the fnb bits so we'll check type below */
1173 dirnam.nam$l_fnb |= NAM$M_EXP_TYPE | NAM$M_EXP_VER;
1174 }
1175 else {
1176 if (dirfab.fab$l_sts != RMS$_FNF) {
1177 set_errno(EVMSERR);
1178 set_vaxc_errno(dirfab.fab$l_sts);
1179 return NULL;
1180 }
1181 dirnam = savnam; /* No; just work with potential name */
1182 }
a0d0e21e 1183 }
748a9306
LW
1184 if (!(dirnam.nam$l_fnb & (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
1185 cp1 = strchr(esa,']');
1186 if (!cp1) cp1 = strchr(esa,'>');
1187 if (cp1) { /* Should always be true */
1188 dirnam.nam$b_esl -= cp1 - esa - 1;
1189 memcpy(esa,cp1 + 1,dirnam.nam$b_esl);
1190 }
1191 }
a0d0e21e
LW
1192 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
1193 /* Yep; check version while we're at it, if it's there. */
1194 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
1195 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
1196 /* Something other than .DIR[;1]. Bzzt. */
748a9306
LW
1197 set_errno(ENOTDIR);
1198 set_vaxc_errno(RMS$_DIR);
a0d0e21e
LW
1199 return NULL;
1200 }
748a9306
LW
1201 }
1202 esa[dirnam.nam$b_esl] = '\0';
1203 if (dirnam.nam$l_fnb & NAM$M_EXP_NAME) {
1204 /* They provided at least the name; we added the type, if necessary, */
1205 if (buf) retspec = buf; /* in sys$parse() */
fc36a67e 1206 else if (ts) New(1311,retspec,dirnam.nam$b_esl+1,char);
748a9306
LW
1207 else retspec = __fileify_retbuf;
1208 strcpy(retspec,esa);
1209 return retspec;
1210 }
c07a80fd 1211 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
1212 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
1213 *cp1 = '\0';
1214 dirnam.nam$b_esl -= 9;
1215 }
748a9306
LW
1216 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
1217 if (cp1 == NULL) return NULL; /* should never happen */
1218 term = *cp1;
1219 *cp1 = '\0';
1220 retlen = strlen(esa);
1221 if ((cp1 = strrchr(esa,'.')) != NULL) {
1222 /* There's more than one directory in the path. Just roll back. */
1223 *cp1 = term;
1224 if (buf) retspec = buf;
fc36a67e 1225 else if (ts) New(1311,retspec,retlen+7,char);
748a9306
LW
1226 else retspec = __fileify_retbuf;
1227 strcpy(retspec,esa);
a0d0e21e
LW
1228 }
1229 else {
748a9306
LW
1230 if (dirnam.nam$l_fnb & NAM$M_ROOT_DIR) {
1231 /* Go back and expand rooted logical name */
1232 dirnam.nam$b_nop = NAM$M_SYNCHK | NAM$M_NOCONCEAL;
1233 if (!(sys$parse(&dirfab) & 1)) {
1234 set_errno(EVMSERR);
1235 set_vaxc_errno(dirfab.fab$l_sts);
1236 return NULL;
1237 }
1238 retlen = dirnam.nam$b_esl - 9; /* esa - '][' - '].DIR;1' */
a0d0e21e 1239 if (buf) retspec = buf;
fc36a67e 1240 else if (ts) New(1312,retspec,retlen+16,char);
a0d0e21e 1241 else retspec = __fileify_retbuf;
748a9306
LW
1242 cp1 = strstr(esa,"][");
1243 dirlen = cp1 - esa;
1244 memcpy(retspec,esa,dirlen);
1245 if (!strncmp(cp1+2,"000000]",7)) {
1246 retspec[dirlen-1] = '\0';
4633a7c4
LW
1247 for (cp1 = retspec+dirlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
1248 if (*cp1 == '.') *cp1 = ']';
1249 else {
1250 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
1251 memcpy(cp1+1,"000000]",7);
1252 }
748a9306
LW
1253 }
1254 else {
1255 memcpy(retspec+dirlen,cp1+2,retlen-dirlen);
1256 retspec[retlen] = '\0';
1257 /* Convert last '.' to ']' */
4633a7c4
LW
1258 for (cp1 = retspec+retlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
1259 if (*cp1 == '.') *cp1 = ']';
1260 else {
1261 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
1262 memcpy(cp1+1,"000000]",7);
1263 }
748a9306 1264 }
a0d0e21e 1265 }
748a9306 1266 else { /* This is a top-level dir. Add the MFD to the path. */
a0d0e21e 1267 if (buf) retspec = buf;
fc36a67e 1268 else if (ts) New(1312,retspec,retlen+16,char);
a0d0e21e
LW
1269 else retspec = __fileify_retbuf;
1270 cp1 = esa;
1271 cp2 = retspec;
1272 while (*cp1 != ':') *(cp2++) = *(cp1++);
1273 strcpy(cp2,":[000000]");
1274 cp1 += 2;
1275 strcpy(cp2+9,cp1);
1276 }
748a9306
LW
1277 }
1278 /* We've set up the string up through the filename. Add the
a0d0e21e
LW
1279 type and version, and we're done. */
1280 strcat(retspec,".DIR;1");
01b8edb6 1281
1282 /* $PARSE may have upcased filespec, so convert output to lower
1283 * case if input contained any lowercase characters. */
1284 if (haslower) __mystrtolower(retspec);
a0d0e21e
LW
1285 return retspec;
1286 }
1287} /* end of do_fileify_dirspec() */
1288/*}}}*/
1289/* External entry points */
1290char *fileify_dirspec(char *dir, char *buf)
1291{ return do_fileify_dirspec(dir,buf,0); }
1292char *fileify_dirspec_ts(char *dir, char *buf)
1293{ return do_fileify_dirspec(dir,buf,1); }
1294
1295/*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
1296static char *do_pathify_dirspec(char *dir,char *buf, int ts)
1297{
1298 static char __pathify_retbuf[NAM$C_MAXRSS+1];
1299 unsigned long int retlen;
748a9306 1300 char *retpath, *cp1, *cp2, trndir[NAM$C_MAXRSS+1];
a0d0e21e 1301
c07a80fd 1302 if (!dir || !*dir) {
1303 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
1304 }
1305
1306 if (*dir) strcpy(trndir,dir);
1307 else getcwd(trndir,sizeof trndir - 1);
1308
1309 while (!strpbrk(trndir,"/]:>") && my_trnlnm(trndir,trndir,0)) {
1310 STRLEN trnlen = strlen(trndir);
a0d0e21e 1311
c07a80fd 1312 /* Trap simple rooted lnms, and return lnm:[000000] */
1313 if (!strcmp(trndir+trnlen-2,".]")) {
1314 if (buf) retpath = buf;
fc36a67e 1315 else if (ts) New(1318,retpath,strlen(dir)+10,char);
c07a80fd 1316 else retpath = __pathify_retbuf;
1317 strcpy(retpath,dir);
1318 strcat(retpath,":[000000]");
1319 return retpath;
1320 }
1321 }
748a9306
LW
1322 dir = trndir;
1323
b7ae7a0d 1324 if (!strpbrk(dir,"]:>")) { /* Unix-style path or plain name */
748a9306
LW
1325 if (*dir == '.' && (*(dir+1) == '\0' ||
1326 (*(dir+1) == '.' && *(dir+2) == '\0')))
1327 retlen = 2 + (*(dir+1) != '\0');
1328 else {
b7ae7a0d 1329 if ( !(cp1 = strrchr(dir,'/')) &&
1330 !(cp1 = strrchr(dir,']')) &&
1331 !(cp1 = strrchr(dir,'>')) ) cp1 = dir;
f86702cc 1332 if ((cp2 = strchr(cp1,'.')) != NULL &&
1333 (*(cp2-1) != '/' || /* Trailing '.', '..', */
1334 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
1335 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
1336 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
b7ae7a0d 1337 int ver; char *cp3;
1338 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
1339 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
1340 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
1341 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
1342 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
1343 (ver || *cp3)))))) {
748a9306
LW
1344 set_errno(ENOTDIR);
1345 set_vaxc_errno(RMS$_DIR);
1346 return NULL;
1347 }
b7ae7a0d 1348 retlen = cp2 - dir + 1;
a0d0e21e 1349 }
748a9306
LW
1350 else { /* No file type present. Treat the filename as a directory. */
1351 retlen = strlen(dir) + 1;
a0d0e21e
LW
1352 }
1353 }
a0d0e21e 1354 if (buf) retpath = buf;
fc36a67e 1355 else if (ts) New(1313,retpath,retlen+1,char);
a0d0e21e
LW
1356 else retpath = __pathify_retbuf;
1357 strncpy(retpath,dir,retlen-1);
1358 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
1359 retpath[retlen-1] = '/'; /* with '/', add it. */
1360 retpath[retlen] = '\0';
1361 }
1362 else retpath[retlen-1] = '\0';
1363 }
1364 else { /* VMS-style directory spec */
01b8edb6 1365 char esa[NAM$C_MAXRSS+1], *cp;
1366 unsigned long int sts, cmplen, haslower;
a0d0e21e
LW
1367 struct FAB dirfab = cc$rms_fab;
1368 struct NAM savnam, dirnam = cc$rms_nam;
1369
b7ae7a0d 1370 /* If we've got an explicit filename, we can just shuffle the string. */
1371 if ( ( (cp1 = strrchr(dir,']')) != NULL ||
1372 (cp1 = strrchr(dir,'>')) != NULL ) && *(cp1+1)) {
1373 if ((cp2 = strchr(cp1,'.')) != NULL) {
1374 int ver; char *cp3;
1375 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
1376 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
1377 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
1378 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
1379 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
1380 (ver || *cp3)))))) {
1381 set_errno(ENOTDIR);
1382 set_vaxc_errno(RMS$_DIR);
1383 return NULL;
1384 }
1385 }
1386 else { /* No file type, so just draw name into directory part */
1387 for (cp2 = cp1; *cp2; cp2++) ;
1388 }
1389 *cp2 = *cp1;
1390 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
1391 *cp1 = '.';
1392 /* We've now got a VMS 'path'; fall through */
1393 }
a0d0e21e
LW
1394 dirfab.fab$b_fns = strlen(dir);
1395 dirfab.fab$l_fna = dir;
748a9306
LW
1396 if (dir[dirfab.fab$b_fns-1] == ']' ||
1397 dir[dirfab.fab$b_fns-1] == '>' ||
1398 dir[dirfab.fab$b_fns-1] == ':') { /* It's already a VMS 'path' */
1399 if (buf) retpath = buf;
fc36a67e 1400 else if (ts) New(1314,retpath,strlen(dir)+1,char);
748a9306
LW
1401 else retpath = __pathify_retbuf;
1402 strcpy(retpath,dir);
1403 return retpath;
1404 }
1405 dirfab.fab$l_dna = ".DIR;1";
1406 dirfab.fab$b_dns = 6;
a0d0e21e 1407 dirfab.fab$l_nam = &dirnam;
e518068a 1408 dirnam.nam$b_ess = (unsigned char) sizeof esa - 1;
a0d0e21e 1409 dirnam.nam$l_esa = esa;
01b8edb6 1410
1411 for (cp = dir; *cp; cp++)
1412 if (islower(*cp)) { haslower = 1; break; }
1413
1414 if (!(sts = (sys$parse(&dirfab)&1))) {
e518068a 1415 if (dirfab.fab$l_sts == RMS$_DIR) {
1416 dirnam.nam$b_nop |= NAM$M_SYNCHK;
1417 sts = sys$parse(&dirfab) & 1;
1418 }
1419 if (!sts) {
748a9306
LW
1420 set_errno(EVMSERR);
1421 set_vaxc_errno(dirfab.fab$l_sts);
a0d0e21e
LW
1422 return NULL;
1423 }
a0d0e21e 1424 }
e518068a 1425 else {
1426 savnam = dirnam;
1427 if (!(sys$search(&dirfab)&1)) { /* Does the file really exist? */
1428 if (dirfab.fab$l_sts != RMS$_FNF) {
1429 set_errno(EVMSERR);
1430 set_vaxc_errno(dirfab.fab$l_sts);
1431 return NULL;
1432 }
1433 dirnam = savnam; /* No; just work with potential name */
1434 }
1435 }
a0d0e21e
LW
1436 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
1437 /* Yep; check version while we're at it, if it's there. */
1438 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
1439 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
1440 /* Something other than .DIR[;1]. Bzzt. */
748a9306
LW
1441 set_errno(ENOTDIR);
1442 set_vaxc_errno(RMS$_DIR);
a0d0e21e
LW
1443 return NULL;
1444 }
a0d0e21e 1445 }
748a9306
LW
1446 /* OK, the type was fine. Now pull any file name into the
1447 directory path. */
1448 if ((cp1 = strrchr(esa,']'))) *dirnam.nam$l_type = ']';
a0d0e21e 1449 else {
748a9306
LW
1450 cp1 = strrchr(esa,'>');
1451 *dirnam.nam$l_type = '>';
a0d0e21e 1452 }
748a9306
LW
1453 *cp1 = '.';
1454 *(dirnam.nam$l_type + 1) = '\0';
1455 retlen = dirnam.nam$l_type - esa + 2;
a0d0e21e 1456 if (buf) retpath = buf;
fc36a67e 1457 else if (ts) New(1314,retpath,retlen,char);
a0d0e21e
LW
1458 else retpath = __pathify_retbuf;
1459 strcpy(retpath,esa);
01b8edb6 1460 /* $PARSE may have upcased filespec, so convert output to lower
1461 * case if input contained any lowercase characters. */
1462 if (haslower) __mystrtolower(retpath);
a0d0e21e
LW
1463 }
1464
1465 return retpath;
1466} /* end of do_pathify_dirspec() */
1467/*}}}*/
1468/* External entry points */
1469char *pathify_dirspec(char *dir, char *buf)
1470{ return do_pathify_dirspec(dir,buf,0); }
1471char *pathify_dirspec_ts(char *dir, char *buf)
1472{ return do_pathify_dirspec(dir,buf,1); }
1473
1474/*{{{ char *tounixspec[_ts](char *path, char *buf)*/
1475static char *do_tounixspec(char *spec, char *buf, int ts)
1476{
1477 static char __tounixspec_retbuf[NAM$C_MAXRSS+1];
1478 char *dirend, *rslt, *cp1, *cp2, *cp3, tmp[NAM$C_MAXRSS+1];
f86702cc 1479 int devlen, dirlen, retlen = NAM$C_MAXRSS+1, expand = 0;
a0d0e21e 1480
748a9306 1481 if (spec == NULL) return NULL;
e518068a 1482 if (strlen(spec) > NAM$C_MAXRSS) return NULL;
a0d0e21e 1483 if (buf) rslt = buf;
e518068a 1484 else if (ts) {
1485 retlen = strlen(spec);
1486 cp1 = strchr(spec,'[');
1487 if (!cp1) cp1 = strchr(spec,'<');
1488 if (cp1) {
f86702cc 1489 for (cp1++; *cp1; cp1++) {
1490 if (*cp1 == '-') expand++; /* VMS '-' ==> Unix '../' */
1491 if (*cp1 == '.' && *(cp1+1) == '.' && *(cp1+2) == '.')
1492 { expand++; cp1 +=2; } /* VMS '...' ==> Unix '/.../' */
1493 }
e518068a 1494 }
fc36a67e 1495 New(1315,rslt,retlen+2+2*expand,char);
e518068a 1496 }
a0d0e21e
LW
1497 else rslt = __tounixspec_retbuf;
1498 if (strchr(spec,'/') != NULL) {
1499 strcpy(rslt,spec);
1500 return rslt;
1501 }
1502
1503 cp1 = rslt;
1504 cp2 = spec;
1505 dirend = strrchr(spec,']');
1506 if (dirend == NULL) dirend = strrchr(spec,'>');
1507 if (dirend == NULL) dirend = strchr(spec,':');
1508 if (dirend == NULL) {
1509 strcpy(rslt,spec);
1510 return rslt;
1511 }
a5f75d66 1512 if (*cp2 != '[' && *cp2 != '<') {
a0d0e21e
LW
1513 *(cp1++) = '/';
1514 }
1515 else { /* the VMS spec begins with directories */
1516 cp2++;
a5f75d66 1517 if (*cp2 == ']' || *cp2 == '>') {
f86702cc 1518 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
a5f75d66
AD
1519 return rslt;
1520 }
f86702cc 1521 else if ( *cp2 != '.' && *cp2 != '-') { /* add the implied device */
a0d0e21e
LW
1522 if (getcwd(tmp,sizeof tmp,1) == NULL) {
1523 if (ts) Safefree(rslt);
1524 return NULL;
1525 }
1526 do {
1527 cp3 = tmp;
1528 while (*cp3 != ':' && *cp3) cp3++;
1529 *(cp3++) = '\0';
1530 if (strchr(cp3,']') != NULL) break;
e518068a 1531 } while (((cp3 = my_getenv(tmp)) != NULL) && strcpy(tmp,cp3));
f86702cc 1532 if (ts && !buf &&
e518068a 1533 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
e518068a 1534 retlen = devlen + dirlen;
f86702cc 1535 Renew(rslt,retlen+1+2*expand,char);
1536 cp1 = rslt;
1537 }
1538 cp3 = tmp;
1539 *(cp1++) = '/';
1540 while (*cp3) {
1541 *(cp1++) = *(cp3++);
1542 if (cp1 - rslt > NAM$C_MAXRSS && !ts && !buf) return NULL; /* No room */
a0d0e21e 1543 }
f86702cc 1544 *(cp1++) = '/';
1545 }
1546 else if ( *cp2 == '.') {
1547 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
1548 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
1549 cp2 += 3;
1550 }
1551 else cp2++;
a0d0e21e 1552 }
a0d0e21e
LW
1553 }
1554 for (; cp2 <= dirend; cp2++) {
1555 if (*cp2 == ':') {
1556 *(cp1++) = '/';
1557 if (*(cp2+1) == '[') cp2++;
1558 }
f86702cc 1559 else if (*cp2 == ']' || *cp2 == '>') {
1560 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
1561 }
a0d0e21e
LW
1562 else if (*cp2 == '.') {
1563 *(cp1++) = '/';
e518068a 1564 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
1565 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
1566 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
1567 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
1568 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
1569 }
f86702cc 1570 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
1571 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
1572 cp2 += 2;
1573 }
a0d0e21e
LW
1574 }
1575 else if (*cp2 == '-') {
1576 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
1577 while (*cp2 == '-') {
1578 cp2++;
1579 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
1580 }
1581 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
1582 if (ts) Safefree(rslt); /* filespecs like */
01b8edb6 1583 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
a0d0e21e
LW
1584 return NULL;
1585 }
a0d0e21e
LW
1586 }
1587 else *(cp1++) = *cp2;
1588 }
1589 else *(cp1++) = *cp2;
1590 }
1591 while (*cp2) *(cp1++) = *(cp2++);
1592 *cp1 = '\0';
1593
1594 return rslt;
1595
1596} /* end of do_tounixspec() */
1597/*}}}*/
1598/* External entry points */
1599char *tounixspec(char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
1600char *tounixspec_ts(char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
1601
1602/*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
1603static char *do_tovmsspec(char *path, char *buf, int ts) {
1604 static char __tovmsspec_retbuf[NAM$C_MAXRSS+1];
e518068a 1605 char *rslt, *dirend;
1606 register char *cp1, *cp2;
1607 unsigned long int infront = 0, hasdir = 1;
a0d0e21e 1608
748a9306 1609 if (path == NULL) return NULL;
a0d0e21e 1610 if (buf) rslt = buf;
fc36a67e 1611 else if (ts) New(1316,rslt,strlen(path)+9,char);
a0d0e21e 1612 else rslt = __tovmsspec_retbuf;
748a9306 1613 if (strpbrk(path,"]:>") ||
a0d0e21e 1614 (dirend = strrchr(path,'/')) == NULL) {
748a9306
LW
1615 if (path[0] == '.') {
1616 if (path[1] == '\0') strcpy(rslt,"[]");
1617 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
1618 else strcpy(rslt,path); /* probably garbage */
1619 }
1620 else strcpy(rslt,path);
a0d0e21e
LW
1621 return rslt;
1622 }
f86702cc 1623 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
748a9306
LW
1624 if (!*(dirend+2)) dirend +=2;
1625 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
f86702cc 1626 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
748a9306 1627 }
a0d0e21e
LW
1628 cp1 = rslt;
1629 cp2 = path;
1630 if (*cp2 == '/') {
e518068a 1631 char trndev[NAM$C_MAXRSS+1];
1632 int islnm, rooted;
1633 STRLEN trnend;
1634
b7ae7a0d 1635 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
61bb5906
CB
1636 if (!*(cp2+1)) {
1637 if (!buf & ts) Renew(rslt,18,char);
1638 strcpy(rslt,"sys$disk:[000000]");
1639 return rslt;
1640 }
a0d0e21e 1641 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
e518068a 1642 *cp1 = '\0';
c07a80fd 1643 islnm = my_trnlnm(rslt,trndev,0);
e518068a 1644 trnend = islnm ? strlen(trndev) - 1 : 0;
1645 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
1646 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
1647 /* If the first element of the path is a logical name, determine
1648 * whether it has to be translated so we can add more directories. */
1649 if (!islnm || rooted) {
1650 *(cp1++) = ':';
1651 *(cp1++) = '[';
1652 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
1653 else cp2++;
1654 }
1655 else {
1656 if (cp2 != dirend) {
1657 if (!buf && ts) Renew(rslt,strlen(path)-strlen(rslt)+trnend+4,char);
1658 strcpy(rslt,trndev);
1659 cp1 = rslt + trnend;
1660 *(cp1++) = '.';
1661 cp2++;
1662 }
1663 else {
1664 *(cp1++) = ':';
1665 hasdir = 0;
1666 }
1667 }
748a9306 1668 }
a0d0e21e
LW
1669 else {
1670 *(cp1++) = '[';
748a9306
LW
1671 if (*cp2 == '.') {
1672 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
1673 cp2 += 2; /* skip over "./" - it's redundant */
1674 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
1675 }
1676 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
1677 *(cp1++) = '-'; /* "../" --> "-" */
1678 cp2 += 3;
1679 }
f86702cc 1680 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
1681 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
1682 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
1683 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
1684 cp2 += 4;
1685 }
748a9306
LW
1686 if (cp2 > dirend) cp2 = dirend;
1687 }
1688 else *(cp1++) = '.';
1689 }
1690 for (; cp2 < dirend; cp2++) {
1691 if (*cp2 == '/') {
01b8edb6 1692 if (*(cp2-1) == '/') continue;
748a9306
LW
1693 if (*(cp1-1) != '.') *(cp1++) = '.';
1694 infront = 0;
1695 }
1696 else if (!infront && *cp2 == '.') {
01b8edb6 1697 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
1698 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
748a9306
LW
1699 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
1700 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
1701 else if (*(cp1-2) == '[') *(cp1-1) = '-';
1702 else { /* back up over previous directory name */
1703 cp1--;
1704 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
4633a7c4
LW
1705 if (*(cp1-1) == '[') {
1706 memcpy(cp1,"000000.",7);
1707 cp1 += 7;
1708 }
748a9306
LW
1709 }
1710 cp2 += 2;
01b8edb6 1711 if (cp2 == dirend) break;
748a9306 1712 }
f86702cc 1713 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
1714 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
1715 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
1716 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
1717 if (!*(cp2+3)) {
1718 *(cp1++) = '.'; /* Simulate trailing '/' */
1719 cp2 += 2; /* for loop will incr this to == dirend */
1720 }
1721 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
1722 }
748a9306
LW
1723 else *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
1724 }
1725 else {
e518068a 1726 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
01b8edb6 1727 if (*cp2 == '.') *(cp1++) = '_';
748a9306
LW
1728 else *(cp1++) = *cp2;
1729 infront = 1;
1730 }
a0d0e21e 1731 }
748a9306 1732 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
e518068a 1733 if (hasdir) *(cp1++) = ']';
748a9306 1734 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
a0d0e21e
LW
1735 while (*cp2) *(cp1++) = *(cp2++);
1736 *cp1 = '\0';
1737
1738 return rslt;
1739
1740} /* end of do_tovmsspec() */
1741/*}}}*/
1742/* External entry points */
1743char *tovmsspec(char *path, char *buf) { return do_tovmsspec(path,buf,0); }
1744char *tovmsspec_ts(char *path, char *buf) { return do_tovmsspec(path,buf,1); }
1745
1746/*{{{ char *tovmspath[_ts](char *path, char *buf)*/
1747static char *do_tovmspath(char *path, char *buf, int ts) {
1748 static char __tovmspath_retbuf[NAM$C_MAXRSS+1];
1749 int vmslen;
1750 char pathified[NAM$C_MAXRSS+1], vmsified[NAM$C_MAXRSS+1], *cp;
1751
748a9306 1752 if (path == NULL) return NULL;
a0d0e21e
LW
1753 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
1754 if (do_tovmsspec(pathified,buf ? buf : vmsified,0) == NULL) return NULL;
1755 if (buf) return buf;
1756 else if (ts) {
1757 vmslen = strlen(vmsified);
fc36a67e 1758 New(1317,cp,vmslen+1,char);
a0d0e21e
LW
1759 memcpy(cp,vmsified,vmslen);
1760 cp[vmslen] = '\0';
1761 return cp;
1762 }
1763 else {
1764 strcpy(__tovmspath_retbuf,vmsified);
1765 return __tovmspath_retbuf;
1766 }
1767
1768} /* end of do_tovmspath() */
1769/*}}}*/
1770/* External entry points */
1771char *tovmspath(char *path, char *buf) { return do_tovmspath(path,buf,0); }
1772char *tovmspath_ts(char *path, char *buf) { return do_tovmspath(path,buf,1); }
1773
1774
1775/*{{{ char *tounixpath[_ts](char *path, char *buf)*/
1776static char *do_tounixpath(char *path, char *buf, int ts) {
1777 static char __tounixpath_retbuf[NAM$C_MAXRSS+1];
1778 int unixlen;
1779 char pathified[NAM$C_MAXRSS+1], unixified[NAM$C_MAXRSS+1], *cp;
1780
748a9306 1781 if (path == NULL) return NULL;
a0d0e21e
LW
1782 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
1783 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) return NULL;
1784 if (buf) return buf;
1785 else if (ts) {
1786 unixlen = strlen(unixified);
fc36a67e 1787 New(1317,cp,unixlen+1,char);
a0d0e21e
LW
1788 memcpy(cp,unixified,unixlen);
1789 cp[unixlen] = '\0';
1790 return cp;
1791 }
1792 else {
1793 strcpy(__tounixpath_retbuf,unixified);
1794 return __tounixpath_retbuf;
1795 }
1796
1797} /* end of do_tounixpath() */
1798/*}}}*/
1799/* External entry points */
1800char *tounixpath(char *path, char *buf) { return do_tounixpath(path,buf,0); }
1801char *tounixpath_ts(char *path, char *buf) { return do_tounixpath(path,buf,1); }
1802
1803/*
1804 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
1805 *
1806 *****************************************************************************
1807 * *
1808 * Copyright (C) 1989-1994 by *
1809 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
1810 * *
1811 * Permission is hereby granted for the reproduction of this software, *
1812 * on condition that this copyright notice is included in the reproduction, *
1813 * and that such reproduction is not for purposes of profit or material *
1814 * gain. *
1815 * *
1816 * 27-Aug-1994 Modified for inclusion in perl5 *
1817 * by Charles Bailey bailey@genetics.upenn.edu *
1818 *****************************************************************************
1819 */
1820
1821/*
1822 * getredirection() is intended to aid in porting C programs
1823 * to VMS (Vax-11 C). The native VMS environment does not support
1824 * '>' and '<' I/O redirection, or command line wild card expansion,
1825 * or a command line pipe mechanism using the '|' AND background
1826 * command execution '&'. All of these capabilities are provided to any
1827 * C program which calls this procedure as the first thing in the
1828 * main program.
1829 * The piping mechanism will probably work with almost any 'filter' type
1830 * of program. With suitable modification, it may useful for other
1831 * portability problems as well.
1832 *
1833 * Author: Mark Pizzolato mark@infocomm.com
1834 */
1835struct list_item
1836 {
1837 struct list_item *next;
1838 char *value;
1839 };
1840
1841static void add_item(struct list_item **head,
1842 struct list_item **tail,
1843 char *value,
1844 int *count);
1845
1846static void expand_wild_cards(char *item,
1847 struct list_item **head,
1848 struct list_item **tail,
1849 int *count);
1850
1851static int background_process(int argc, char **argv);
1852
1853static void pipe_and_fork(char **cmargv);
1854
1855/*{{{ void getredirection(int *ac, char ***av)*/
84902520 1856static void
a0d0e21e
LW
1857getredirection(int *ac, char ***av)
1858/*
1859 * Process vms redirection arg's. Exit if any error is seen.
1860 * If getredirection() processes an argument, it is erased
1861 * from the vector. getredirection() returns a new argc and argv value.
1862 * In the event that a background command is requested (by a trailing "&"),
1863 * this routine creates a background subprocess, and simply exits the program.
1864 *
1865 * Warning: do not try to simplify the code for vms. The code
1866 * presupposes that getredirection() is called before any data is
1867 * read from stdin or written to stdout.
1868 *
1869 * Normal usage is as follows:
1870 *
1871 * main(argc, argv)
1872 * int argc;
1873 * char *argv[];
1874 * {
1875 * getredirection(&argc, &argv);
1876 * }
1877 */
1878{
1879 int argc = *ac; /* Argument Count */
1880 char **argv = *av; /* Argument Vector */
1881 char *ap; /* Argument pointer */
1882 int j; /* argv[] index */
1883 int item_count = 0; /* Count of Items in List */
1884 struct list_item *list_head = 0; /* First Item in List */
1885 struct list_item *list_tail; /* Last Item in List */
1886 char *in = NULL; /* Input File Name */
1887 char *out = NULL; /* Output File Name */
1888 char *outmode = "w"; /* Mode to Open Output File */
1889 char *err = NULL; /* Error File Name */
1890 char *errmode = "w"; /* Mode to Open Error File */
1891 int cmargc = 0; /* Piped Command Arg Count */
1892 char **cmargv = NULL;/* Piped Command Arg Vector */
a0d0e21e
LW
1893
1894 /*
1895 * First handle the case where the last thing on the line ends with
1896 * a '&'. This indicates the desire for the command to be run in a
1897 * subprocess, so we satisfy that desire.
1898 */
1899 ap = argv[argc-1];
1900 if (0 == strcmp("&", ap))
1901 exit(background_process(--argc, argv));
e518068a 1902 if (*ap && '&' == ap[strlen(ap)-1])
a0d0e21e
LW
1903 {
1904 ap[strlen(ap)-1] = '\0';
1905 exit(background_process(argc, argv));
1906 }
1907 /*
1908 * Now we handle the general redirection cases that involve '>', '>>',
1909 * '<', and pipes '|'.
1910 */
1911 for (j = 0; j < argc; ++j)
1912 {
1913 if (0 == strcmp("<", argv[j]))
1914 {
1915 if (j+1 >= argc)
1916 {
740ce14c 1917 PerlIO_printf(Perl_debug_log,"No input file after < on command line");
748a9306 1918 exit(LIB$_WRONUMARG);
a0d0e21e
LW
1919 }
1920 in = argv[++j];
1921 continue;
1922 }
1923 if ('<' == *(ap = argv[j]))
1924 {
1925 in = 1 + ap;
1926 continue;
1927 }
1928 if (0 == strcmp(">", ap))
1929 {
1930 if (j+1 >= argc)
1931 {
740ce14c 1932 PerlIO_printf(Perl_debug_log,"No output file after > on command line");
748a9306 1933 exit(LIB$_WRONUMARG);
a0d0e21e
LW
1934 }
1935 out = argv[++j];
1936 continue;
1937 }
1938 if ('>' == *ap)
1939 {
1940 if ('>' == ap[1])
1941 {
1942 outmode = "a";
1943 if ('\0' == ap[2])
1944 out = argv[++j];
1945 else
1946 out = 2 + ap;
1947 }
1948 else
1949 out = 1 + ap;
1950 if (j >= argc)
1951 {
740ce14c 1952 PerlIO_printf(Perl_debug_log,"No output file after > or >> on command line");
748a9306 1953 exit(LIB$_WRONUMARG);
a0d0e21e
LW
1954 }
1955 continue;
1956 }
1957 if (('2' == *ap) && ('>' == ap[1]))
1958 {
1959 if ('>' == ap[2])
1960 {
1961 errmode = "a";
1962 if ('\0' == ap[3])
1963 err = argv[++j];
1964 else
1965 err = 3 + ap;
1966 }
1967 else
1968 if ('\0' == ap[2])
1969 err = argv[++j];
1970 else
748a9306 1971 err = 2 + ap;
a0d0e21e
LW
1972 if (j >= argc)
1973 {
740ce14c 1974 PerlIO_printf(Perl_debug_log,"No output file after 2> or 2>> on command line");
748a9306 1975 exit(LIB$_WRONUMARG);
a0d0e21e
LW
1976 }
1977 continue;
1978 }
1979 if (0 == strcmp("|", argv[j]))
1980 {
1981 if (j+1 >= argc)
1982 {
740ce14c 1983 PerlIO_printf(Perl_debug_log,"No command into which to pipe on command line");
748a9306 1984 exit(LIB$_WRONUMARG);
a0d0e21e
LW
1985 }
1986 cmargc = argc-(j+1);
1987 cmargv = &argv[j+1];
1988 argc = j;
1989 continue;
1990 }
1991 if ('|' == *(ap = argv[j]))
1992 {
1993 ++argv[j];
1994 cmargc = argc-j;
1995 cmargv = &argv[j];
1996 argc = j;
1997 continue;
1998 }
1999 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
2000 }
2001 /*
2002 * Allocate and fill in the new argument vector, Some Unix's terminate
2003 * the list with an extra null pointer.
2004 */
fc36a67e 2005 New(1302, argv, item_count+1, char *);
a0d0e21e
LW
2006 *av = argv;
2007 for (j = 0; j < item_count; ++j, list_head = list_head->next)
2008 argv[j] = list_head->value;
2009 *ac = item_count;
2010 if (cmargv != NULL)
2011 {
2012 if (out != NULL)
2013 {
740ce14c 2014 PerlIO_printf(Perl_debug_log,"'|' and '>' may not both be specified on command line");
748a9306 2015 exit(LIB$_INVARGORD);
a0d0e21e
LW
2016 }
2017 pipe_and_fork(cmargv);
2018 }
2019
2020 /* Check for input from a pipe (mailbox) */
2021
a5f75d66 2022 if (in == NULL && 1 == isapipe(0))
a0d0e21e
LW
2023 {
2024 char mbxname[L_tmpnam];
2025 long int bufsize;
2026 long int dvi_item = DVI$_DEVBUFSIZ;
2027 $DESCRIPTOR(mbxnam, "");
2028 $DESCRIPTOR(mbxdevnam, "");
2029
2030 /* Input from a pipe, reopen it in binary mode to disable */
2031 /* carriage control processing. */
2032
740ce14c 2033 PerlIO_getname(stdin, mbxname);
a0d0e21e
LW
2034 mbxnam.dsc$a_pointer = mbxname;
2035 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
2036 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
2037 mbxdevnam.dsc$a_pointer = mbxname;
2038 mbxdevnam.dsc$w_length = sizeof(mbxname);
2039 dvi_item = DVI$_DEVNAM;
2040 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
2041 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
748a9306
LW
2042 set_errno(0);
2043 set_vaxc_errno(1);
a0d0e21e
LW
2044 freopen(mbxname, "rb", stdin);
2045 if (errno != 0)
2046 {
740ce14c 2047 PerlIO_printf(Perl_debug_log,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
748a9306 2048 exit(vaxc$errno);
a0d0e21e
LW
2049 }
2050 }
2051 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
2052 {
740ce14c 2053 PerlIO_printf(Perl_debug_log,"Can't open input file %s as stdin",in);
748a9306 2054 exit(vaxc$errno);
a0d0e21e
LW
2055 }
2056 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
2057 {
740ce14c 2058 PerlIO_printf(Perl_debug_log,"Can't open output file %s as stdout",out);
748a9306 2059 exit(vaxc$errno);
a0d0e21e 2060 }
748a9306
LW
2061 if (err != NULL) {
2062 FILE *tmperr;
2063 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
2064 {
740ce14c 2065 PerlIO_printf(Perl_debug_log,"Can't open error file %s as stderr",err);
748a9306
LW
2066 exit(vaxc$errno);
2067 }
2068 fclose(tmperr);
b7ae7a0d 2069 if (NULL == freopen(err, "a", Perl_debug_log, "mbc=32", "mbf=2"))
748a9306
LW
2070 {
2071 exit(vaxc$errno);
2072 }
a0d0e21e
LW
2073 }
2074#ifdef ARGPROC_DEBUG
740ce14c 2075 PerlIO_printf(Perl_debug_log, "Arglist:\n");
a0d0e21e 2076 for (j = 0; j < *ac; ++j)
740ce14c 2077 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
a0d0e21e 2078#endif
b7ae7a0d 2079 /* Clear errors we may have hit expanding wildcards, so they don't
2080 show up in Perl's $! later */
2081 set_errno(0); set_vaxc_errno(1);
a0d0e21e
LW
2082} /* end of getredirection() */
2083/*}}}*/
2084
2085static void add_item(struct list_item **head,
2086 struct list_item **tail,
2087 char *value,
2088 int *count)
2089{
2090 if (*head == 0)
2091 {
fc36a67e 2092 New(1303,*head,1,struct list_item);
a0d0e21e
LW
2093 *tail = *head;
2094 }
2095 else {
fc36a67e 2096 New(1304,(*tail)->next,1,struct list_item);
a0d0e21e
LW
2097 *tail = (*tail)->next;
2098 }
2099 (*tail)->value = value;
2100 ++(*count);
2101}
2102
2103static void expand_wild_cards(char *item,
2104 struct list_item **head,
2105 struct list_item **tail,
2106 int *count)
2107{
2108int expcount = 0;
748a9306 2109unsigned long int context = 0;
a0d0e21e 2110int isunix = 0;
a0d0e21e
LW
2111char *had_version;
2112char *had_device;
2113int had_directory;
2114char *devdir;
2115char vmsspec[NAM$C_MAXRSS+1];
2116$DESCRIPTOR(filespec, "");
748a9306 2117$DESCRIPTOR(defaultspec, "SYS$DISK:[]");
a0d0e21e 2118$DESCRIPTOR(resultspec, "");
c07a80fd 2119unsigned long int zero = 0, sts;
a0d0e21e 2120
b7ae7a0d 2121 if (strcspn(item, "*%") == strlen(item) || strchr(item,' ') != NULL)
a0d0e21e
LW
2122 {
2123 add_item(head, tail, item, count);
2124 return;
2125 }
2126 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
2127 resultspec.dsc$b_class = DSC$K_CLASS_D;
2128 resultspec.dsc$a_pointer = NULL;
748a9306 2129 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
a0d0e21e
LW
2130 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
2131 if (!isunix || !filespec.dsc$a_pointer)
2132 filespec.dsc$a_pointer = item;
2133 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
2134 /*
2135 * Only return version specs, if the caller specified a version
2136 */
2137 had_version = strchr(item, ';');
2138 /*
2139 * Only return device and directory specs, if the caller specifed either.
2140 */
2141 had_device = strchr(item, ':');
2142 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
2143
c07a80fd 2144 while (1 == (1 & (sts = lib$find_file(&filespec, &resultspec, &context,
2145 &defaultspec, 0, 0, &zero))))
a0d0e21e
LW
2146 {
2147 char *string;
2148 char *c;
2149
fc36a67e 2150 New(1305,string,resultspec.dsc$w_length+1,char);
a0d0e21e
LW
2151 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
2152 string[resultspec.dsc$w_length] = '\0';
2153 if (NULL == had_version)
2154 *((char *)strrchr(string, ';')) = '\0';
2155 if ((!had_directory) && (had_device == NULL))
2156 {
2157 if (NULL == (devdir = strrchr(string, ']')))
2158 devdir = strrchr(string, '>');
2159 strcpy(string, devdir + 1);
2160 }
2161 /*
2162 * Be consistent with what the C RTL has already done to the rest of
2163 * the argv items and lowercase all of these names.
2164 */
2165 for (c = string; *c; ++c)
2166 if (isupper(*c))
2167 *c = tolower(*c);
f86702cc 2168 if (isunix) trim_unixpath(string,item,1);
a0d0e21e
LW
2169 add_item(head, tail, string, count);
2170 ++expcount;
2171 }
c07a80fd 2172 if (sts != RMS$_NMF)
2173 {
2174 set_vaxc_errno(sts);
2175 switch (sts)
2176 {
2177 case RMS$_FNF:
b7ae7a0d 2178 case RMS$_DNF:
c07a80fd 2179 case RMS$_DIR:
2180 set_errno(ENOENT); break;
2181 case RMS$_DEV:
2182 set_errno(ENODEV); break;
71be2cbc 2183 case RMS$_FNM:
c07a80fd 2184 case RMS$_SYN:
2185 set_errno(EINVAL); break;
2186 case RMS$_PRV:
2187 set_errno(EACCES); break;
2188 default:
b7ae7a0d 2189 _ckvmssts_noperl(sts);
c07a80fd 2190 }
2191 }
a0d0e21e
LW
2192 if (expcount == 0)
2193 add_item(head, tail, item, count);
b7ae7a0d 2194 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
2195 _ckvmssts_noperl(lib$find_file_end(&context));
a0d0e21e
LW
2196}
2197
2198static int child_st[2];/* Event Flag set when child process completes */
2199
748a9306 2200static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
a0d0e21e 2201
748a9306 2202static unsigned long int exit_handler(int *status)
a0d0e21e
LW
2203{
2204short iosb[4];
2205
2206 if (0 == child_st[0])
2207 {
2208#ifdef ARGPROC_DEBUG
740ce14c 2209 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
a0d0e21e
LW
2210#endif
2211 fflush(stdout); /* Have to flush pipe for binary data to */
2212 /* terminate properly -- <tp@mccall.com> */
2213 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
2214 sys$dassgn(child_chan);
2215 fclose(stdout);
2216 sys$synch(0, child_st);
2217 }
2218 return(1);
2219}
2220
2221static void sig_child(int chan)
2222{
2223#ifdef ARGPROC_DEBUG
740ce14c 2224 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
a0d0e21e
LW
2225#endif
2226 if (child_st[0] == 0)
2227 child_st[0] = 1;
2228}
2229
748a9306 2230static struct exit_control_block exit_block =
a0d0e21e
LW
2231 {
2232 0,
2233 exit_handler,
2234 1,
2235 &exit_block.exit_status,
2236 0
2237 };
2238
2239static void pipe_and_fork(char **cmargv)
2240{
2241 char subcmd[2048];
2242 $DESCRIPTOR(cmddsc, "");
2243 static char mbxname[64];
2244 $DESCRIPTOR(mbxdsc, mbxname);
a0d0e21e 2245 int pid, j;
a0d0e21e
LW
2246 unsigned long int zero = 0, one = 1;
2247
2248 strcpy(subcmd, cmargv[0]);
2249 for (j = 1; NULL != cmargv[j]; ++j)
2250 {
2251 strcat(subcmd, " \"");
2252 strcat(subcmd, cmargv[j]);
2253 strcat(subcmd, "\"");
2254 }
2255 cmddsc.dsc$a_pointer = subcmd;
2256 cmddsc.dsc$w_length = strlen(cmddsc.dsc$a_pointer);
2257
2258 create_mbx(&child_chan,&mbxdsc);
2259#ifdef ARGPROC_DEBUG
740ce14c 2260 PerlIO_printf(Perl_debug_log, "Pipe Mailbox Name = '%s'\n", mbxdsc.dsc$a_pointer);
2261 PerlIO_printf(Perl_debug_log, "Sub Process Command = '%s'\n", cmddsc.dsc$a_pointer);
a0d0e21e 2262#endif
b7ae7a0d 2263 _ckvmssts_noperl(lib$spawn(&cmddsc, &mbxdsc, 0, &one,
2264 0, &pid, child_st, &zero, sig_child,
2265 &child_chan));
a0d0e21e 2266#ifdef ARGPROC_DEBUG
740ce14c 2267 PerlIO_printf(Perl_debug_log, "Subprocess's Pid = %08X\n", pid);
a0d0e21e
LW
2268#endif
2269 sys$dclexh(&exit_block);
2270 if (NULL == freopen(mbxname, "wb", stdout))
2271 {
740ce14c 2272 PerlIO_printf(Perl_debug_log,"Can't open output pipe (name %s)",mbxname);
a0d0e21e
LW
2273 }
2274}
2275
2276static int background_process(int argc, char **argv)
2277{
2278char command[2048] = "$";
2279$DESCRIPTOR(value, "");
2280static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
2281static $DESCRIPTOR(null, "NLA0:");
2282static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
2283char pidstring[80];
2284$DESCRIPTOR(pidstr, "");
2285int pid;
748a9306 2286unsigned long int flags = 17, one = 1, retsts;
a0d0e21e
LW
2287
2288 strcat(command, argv[0]);
2289 while (--argc)
2290 {
2291 strcat(command, " \"");
2292 strcat(command, *(++argv));
2293 strcat(command, "\"");
2294 }
2295 value.dsc$a_pointer = command;
2296 value.dsc$w_length = strlen(value.dsc$a_pointer);
b7ae7a0d 2297 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
748a9306
LW
2298 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
2299 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
b7ae7a0d 2300 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
748a9306
LW
2301 }
2302 else {
b7ae7a0d 2303 _ckvmssts_noperl(retsts);
748a9306 2304 }
a0d0e21e 2305#ifdef ARGPROC_DEBUG
740ce14c 2306 PerlIO_printf(Perl_debug_log, "%s\n", command);
a0d0e21e
LW
2307#endif
2308 sprintf(pidstring, "%08X", pid);
740ce14c 2309 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
a0d0e21e
LW
2310 pidstr.dsc$a_pointer = pidstring;
2311 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
2312 lib$set_symbol(&pidsymbol, &pidstr);
2313 return(SS$_NORMAL);
2314}
2315/*}}}*/
2316/***** End of code taken from Mark Pizzolato's argproc.c package *****/
2317
84902520
TB
2318
2319/* OS-specific initialization at image activation (not thread startup) */
61bb5906
CB
2320/* Older VAXC header files lack these constants */
2321#ifndef JPI$_RIGHTS_SIZE
2322# define JPI$_RIGHTS_SIZE 817
2323#endif
2324#ifndef KGB$M_SUBSYSTEM
2325# define KGB$M_SUBSYSTEM 0x8
2326#endif
2327
84902520
TB
2328/*{{{void vms_image_init(int *, char ***)*/
2329void
2330vms_image_init(int *argcp, char ***argvp)
2331{
61bb5906
CB
2332 unsigned long int *mask, iosb[2], i, rlst[128], rsz, add_taint = FALSE;
2333 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
2334 unsigned short int dummy, rlen;
2335 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
2336 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
2337 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
2338 { 0, 0, 0, 0} };
84902520
TB
2339
2340 _ckvmssts(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
2341 _ckvmssts(iosb[0]);
61bb5906
CB
2342 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
2343 if (iprv[i]) { /* Running image installed with privs? */
2344 _ckvmssts(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
2345 add_taint = TRUE;
84902520
TB
2346 break;
2347 }
2348 }
61bb5906
CB
2349 /* Rights identifiers might trigger tainting as well. */
2350 if (!add_taint && (rlen || rsz)) {
2351 while (rlen < rsz) {
2352 /* We didn't get all the identifiers on the first pass. Allocate a
2353 * buffer much larger than $GETJPI wants (rsz is size in bytes that
2354 * were needed to hold all identifiers at time of last call; we'll
2355 * allocate that many unsigned long ints), and go back and get 'em.
2356 */
2357 if (jpilist[1].bufadr != rlst) Safefree(jpilist[1].bufadr);
2358 jpilist[1].bufadr = New(1320,mask,rsz,unsigned long int);
2359 jpilist[1].buflen = rsz * sizeof(unsigned long int);
2360 _ckvmssts(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
2361 _ckvmssts(iosb[0]);
2362 }
2363 mask = jpilist[1].bufadr;
2364 /* Check attribute flags for each identifier (2nd longword); protected
2365 * subsystem identifiers trigger tainting.
2366 */
2367 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
2368 if (mask[i] & KGB$M_SUBSYSTEM) {
2369 add_taint = TRUE;
2370 break;
2371 }
2372 }
2373 if (mask != rlst) Safefree(mask);
2374 }
2375 /* We need to use this hack to tell Perl it should run with tainting,
2376 * since its tainting flag may be part of the curinterp struct, which
2377 * hasn't been allocated when vms_image_init() is called.
2378 */
2379 if (add_taint) {
2380 char ***newap;
2381 New(1320,newap,*argcp+2,char **);
2382 newap[0] = argvp[0];
2383 *newap[1] = "-T";
2384 Copy(argvp[1],newap[2],*argcp-1,char **);
2385 /* We orphan the old argv, since we don't know where it's come from,
2386 * so we don't know how to free it.
2387 */
2388 *argcp++; argvp = newap;
2389 }
84902520 2390 getredirection(argcp,argvp);
09b7f37c
CB
2391#if defined(USE_THREADS) && defined(__DECC)
2392 {
2393# include <reentrancy.h>
2394 (void) decc$set_reentrancy(C$C_MULTITHREAD);
2395 }
2396#endif
84902520
TB
2397 return;
2398}
2399/*}}}*/
2400
2401
a0d0e21e
LW
2402/* trim_unixpath()
2403 * Trim Unix-style prefix off filespec, so it looks like what a shell
2404 * glob expansion would return (i.e. from specified prefix on, not
2405 * full path). Note that returned filespec is Unix-style, regardless
2406 * of whether input filespec was VMS-style or Unix-style.
2407 *
a3e9d8c9 2408 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
f86702cc 2409 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
2410 * vector of options; at present, only bit 0 is used, and if set tells
2411 * trim unixpath to try the current default directory as a prefix when
2412 * presented with a possibly ambiguous ... wildcard.
a3e9d8c9 2413 *
2414 * Returns !=0 on success, with trimmed filespec replacing contents of
2415 * fspec, and 0 on failure, with contents of fpsec unchanged.
a0d0e21e 2416 */
f86702cc 2417/*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
a0d0e21e 2418int
f86702cc 2419trim_unixpath(char *fspec, char *wildspec, int opts)
a0d0e21e 2420{
a3e9d8c9 2421 char unixified[NAM$C_MAXRSS+1], unixwild[NAM$C_MAXRSS+1],
f86702cc 2422 *template, *base, *end, *cp1, *cp2;
2423 register int tmplen, reslen = 0, dirs = 0;
a0d0e21e 2424
a3e9d8c9 2425 if (!wildspec || !fspec) return 0;
2426 if (strpbrk(wildspec,"]>:") != NULL) {
2427 if (do_tounixspec(wildspec,unixwild,0) == NULL) return 0;
f86702cc 2428 else template = unixwild;
a3e9d8c9 2429 }
2430 else template = wildspec;
a0d0e21e
LW
2431 if (strpbrk(fspec,"]>:") != NULL) {
2432 if (do_tounixspec(fspec,unixified,0) == NULL) return 0;
2433 else base = unixified;
a3e9d8c9 2434 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
2435 * check to see that final result fits into (isn't longer than) fspec */
2436 reslen = strlen(fspec);
a0d0e21e
LW
2437 }
2438 else base = fspec;
a3e9d8c9 2439
2440 /* No prefix or absolute path on wildcard, so nothing to remove */
2441 if (!*template || *template == '/') {
2442 if (base == fspec) return 1;
2443 tmplen = strlen(unixified);
2444 if (tmplen > reslen) return 0; /* not enough space */
2445 /* Copy unixified resultant, including trailing NUL */
2446 memmove(fspec,unixified,tmplen+1);
2447 return 1;
2448 }
a0d0e21e 2449
f86702cc 2450 for (end = base; *end; end++) ; /* Find end of resultant filespec */
2451 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
2452 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
2453 for (cp1 = end ;cp1 >= base; cp1--)
2454 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
2455 { cp1++; break; }
2456 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
a3e9d8c9 2457 return 1;
2458 }
f86702cc 2459 else {
2460 char tpl[NAM$C_MAXRSS+1], lcres[NAM$C_MAXRSS+1];
2461 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
2462 int ells = 1, totells, segdirs, match;
2463 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, tpl},
2464 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
2465
2466 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
2467 totells = ells;
2468 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
2469 if (ellipsis == template && opts & 1) {
2470 /* Template begins with an ellipsis. Since we can't tell how many
2471 * directory names at the front of the resultant to keep for an
2472 * arbitrary starting point, we arbitrarily choose the current
2473 * default directory as a starting point. If it's there as a prefix,
2474 * clip it off. If not, fall through and act as if the leading
2475 * ellipsis weren't there (i.e. return shortest possible path that
2476 * could match template).
2477 */
2478 if (getcwd(tpl, sizeof tpl,0) == NULL) return 0;
2479 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
2480 if (_tolower(*cp1) != _tolower(*cp2)) break;
2481 segdirs = dirs - totells; /* Min # of dirs we must have left */
2482 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
2483 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
2484 memcpy(fspec,cp2+1,end - cp2);
2485 return 1;
a3e9d8c9 2486 }
a3e9d8c9 2487 }
f86702cc 2488 /* First off, back up over constant elements at end of path */
2489 if (dirs) {
2490 for (front = end ; front >= base; front--)
2491 if (*front == '/' && !dirs--) { front++; break; }
a3e9d8c9 2492 }
17f28c40 2493 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + sizeof lcres;
f86702cc 2494 cp1++,cp2++) *cp2 = _tolower(*cp1); /* Make lc copy for match */
2495 if (cp1 != '\0') return 0; /* Path too long. */
2496 lcend = cp2;
2497 *cp2 = '\0'; /* Pick up with memcpy later */
2498 lcfront = lcres + (front - base);
2499 /* Now skip over each ellipsis and try to match the path in front of it. */
2500 while (ells--) {
2501 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
2502 if (*(cp1) == '.' && *(cp1+1) == '.' &&
2503 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
2504 if (cp1 < template) break; /* template started with an ellipsis */
2505 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
2506 ellipsis = cp1; continue;
2507 }
2508 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
2509 nextell = cp1;
2510 for (segdirs = 0, cp2 = tpl;
2511 cp1 <= ellipsis - 1 && cp2 <= tpl + sizeof tpl;
2512 cp1++, cp2++) {
2513 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
2514 else *cp2 = _tolower(*cp1); /* else lowercase for match */
2515 if (*cp2 == '/') segdirs++;
2516 }
2517 if (cp1 != ellipsis - 1) return 0; /* Path too long */
2518 /* Back up at least as many dirs as in template before matching */
2519 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
2520 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
2521 for (match = 0; cp1 > lcres;) {
2522 resdsc.dsc$a_pointer = cp1;
2523 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
2524 match++;
2525 if (match == 1) lcfront = cp1;
2526 }
2527 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
2528 }
2529 if (!match) return 0; /* Can't find prefix ??? */
2530 if (match > 1 && opts & 1) {
2531 /* This ... wildcard could cover more than one set of dirs (i.e.
2532 * a set of similar dir names is repeated). If the template
2533 * contains more than 1 ..., upstream elements could resolve the
2534 * ambiguity, but it's not worth a full backtracking setup here.
2535 * As a quick heuristic, clip off the current default directory
2536 * if it's present to find the trimmed spec, else use the
2537 * shortest string that this ... could cover.
2538 */
2539 char def[NAM$C_MAXRSS+1], *st;
2540
2541 if (getcwd(def, sizeof def,0) == NULL) return 0;
2542 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
2543 if (_tolower(*cp1) != _tolower(*cp2)) break;
2544 segdirs = dirs - totells; /* Min # of dirs we must have left */
2545 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
2546 if (*cp1 == '\0' && *cp2 == '/') {
2547 memcpy(fspec,cp2+1,end - cp2);
2548 return 1;
2549 }
2550 /* Nope -- stick with lcfront from above and keep going. */
2551 }
2552 }
2553 memcpy(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
a3e9d8c9 2554 return 1;
f86702cc 2555 ellipsis = nextell;
a0d0e21e 2556 }
a0d0e21e
LW
2557
2558} /* end of trim_unixpath() */
2559/*}}}*/
2560
a0d0e21e
LW
2561
2562/*
2563 * VMS readdir() routines.
2564 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
a0d0e21e
LW
2565 *
2566 * 21-Jul-1994 Charles Bailey bailey@genetics.upenn.edu
2567 * Minor modifications to original routines.
2568 */
2569
2570 /* Number of elements in vms_versions array */
2571#define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
2572
2573/*
2574 * Open a directory, return a handle for later use.
2575 */
2576/*{{{ DIR *opendir(char*name) */
2577DIR *
2578opendir(char *name)
2579{
2580 DIR *dd;
2581 char dir[NAM$C_MAXRSS+1];
61bb5906
CB
2582 Stat_t sb;
2583
a0d0e21e 2584 if (do_tovmspath(name,dir,0) == NULL) {
61bb5906 2585 return NULL;
a0d0e21e 2586 }
61bb5906
CB
2587 if (flex_stat(dir,&sb) == -1) return NULL;
2588 if (!S_ISDIR(sb.st_mode)) {
2589 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
2590 return NULL;
2591 }
2592 if (!cando_by_name(S_IRUSR,0,dir)) {
2593 set_errno(EACCES); set_vaxc_errno(RMS$_PRV);
2594 return NULL;
2595 }
2596 /* Get memory for the handle, and the pattern. */
2597 New(1306,dd,1,DIR);
fc36a67e 2598 New(1307,dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
a0d0e21e
LW
2599
2600 /* Fill in the fields; mainly playing with the descriptor. */
2601 (void)sprintf(dd->pattern, "%s*.*",dir);
2602 dd->context = 0;
2603 dd->count = 0;
2604 dd->vms_wantversions = 0;
2605 dd->pat.dsc$a_pointer = dd->pattern;
2606 dd->pat.dsc$w_length = strlen(dd->pattern);
2607 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
2608 dd->pat.dsc$b_class = DSC$K_CLASS_S;
2609
2610 return dd;
2611} /* end of opendir() */
2612/*}}}*/
2613
2614/*
2615 * Set the flag to indicate we want versions or not.
2616 */
2617/*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
2618void
2619vmsreaddirversions(DIR *dd, int flag)
2620{
2621 dd->vms_wantversions = flag;
2622}
2623/*}}}*/
2624
2625/*
2626 * Free up an opened directory.
2627 */
2628/*{{{ void closedir(DIR *dd)*/
2629void
2630closedir(DIR *dd)
2631{
2632 (void)lib$find_file_end(&dd->context);
2633 Safefree(dd->pattern);
2634 Safefree((char *)dd);
2635}
2636/*}}}*/
2637
2638/*
2639 * Collect all the version numbers for the current file.
2640 */
2641static void
2642collectversions(dd)
2643 DIR *dd;
2644{
2645 struct dsc$descriptor_s pat;
2646 struct dsc$descriptor_s res;
2647 struct dirent *e;
2648 char *p, *text, buff[sizeof dd->entry.d_name];
2649 int i;
2650 unsigned long context, tmpsts;
2651
2652 /* Convenient shorthand. */
2653 e = &dd->entry;
2654
2655 /* Add the version wildcard, ignoring the "*.*" put on before */
2656 i = strlen(dd->pattern);
fc36a67e 2657 New(1308,text,i + e->d_namlen + 3,char);
a0d0e21e
LW
2658 (void)strcpy(text, dd->pattern);
2659 (void)sprintf(&text[i - 3], "%s;*", e->d_name);
2660
2661 /* Set up the pattern descriptor. */
2662 pat.dsc$a_pointer = text;
2663 pat.dsc$w_length = i + e->d_namlen - 1;
2664 pat.dsc$b_dtype = DSC$K_DTYPE_T;
2665 pat.dsc$b_class = DSC$K_CLASS_S;
2666
2667 /* Set up result descriptor. */
2668 res.dsc$a_pointer = buff;
2669 res.dsc$w_length = sizeof buff - 2;
2670 res.dsc$b_dtype = DSC$K_DTYPE_T;
2671 res.dsc$b_class = DSC$K_CLASS_S;
2672
2673 /* Read files, collecting versions. */
2674 for (context = 0, e->vms_verscount = 0;
2675 e->vms_verscount < VERSIZE(e);
2676 e->vms_verscount++) {
2677 tmpsts = lib$find_file(&pat, &res, &context);
2678 if (tmpsts == RMS$_NMF || context == 0) break;
748a9306 2679 _ckvmssts(tmpsts);
a0d0e21e 2680 buff[sizeof buff - 1] = '\0';
748a9306 2681 if ((p = strchr(buff, ';')))
a0d0e21e
LW
2682 e->vms_versions[e->vms_verscount] = atoi(p + 1);
2683 else
2684 e->vms_versions[e->vms_verscount] = -1;
2685 }
2686
748a9306 2687 _ckvmssts(lib$find_file_end(&context));
a0d0e21e
LW
2688 Safefree(text);
2689
2690} /* end of collectversions() */
2691
2692/*
2693 * Read the next entry from the directory.
2694 */
2695/*{{{ struct dirent *readdir(DIR *dd)*/
2696struct dirent *
2697readdir(DIR *dd)
2698{
2699 struct dsc$descriptor_s res;
2700 char *p, buff[sizeof dd->entry.d_name];
a0d0e21e
LW
2701 unsigned long int tmpsts;
2702
2703 /* Set up result descriptor, and get next file. */
2704 res.dsc$a_pointer = buff;
2705 res.dsc$w_length = sizeof buff - 2;
2706 res.dsc$b_dtype = DSC$K_DTYPE_T;
2707 res.dsc$b_class = DSC$K_CLASS_S;
a0d0e21e 2708 tmpsts = lib$find_file(&dd->pat, &res, &dd->context);
4633a7c4
LW
2709 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
2710 if (!(tmpsts & 1)) {
2711 set_vaxc_errno(tmpsts);
2712 switch (tmpsts) {
2713 case RMS$_PRV:
c07a80fd 2714 set_errno(EACCES); break;
4633a7c4 2715 case RMS$_DEV:
c07a80fd 2716 set_errno(ENODEV); break;
4633a7c4 2717 case RMS$_DIR:
4633a7c4 2718 case RMS$_FNF:
c07a80fd 2719 set_errno(ENOENT); break;
4633a7c4
LW
2720 default:
2721 set_errno(EVMSERR);
2722 }
2723 return NULL;
2724 }
2725 dd->count++;
a0d0e21e
LW
2726 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
2727 buff[sizeof buff - 1] = '\0';
2728 for (p = buff; !isspace(*p); p++) *p = _tolower(*p);
2729 *p = '\0';
2730
2731 /* Skip any directory component and just copy the name. */
748a9306 2732 if ((p = strchr(buff, ']'))) (void)strcpy(dd->entry.d_name, p + 1);
a0d0e21e
LW
2733 else (void)strcpy(dd->entry.d_name, buff);
2734
2735 /* Clobber the version. */
748a9306 2736 if ((p = strchr(dd->entry.d_name, ';'))) *p = '\0';
a0d0e21e
LW
2737
2738 dd->entry.d_namlen = strlen(dd->entry.d_name);
2739 dd->entry.vms_verscount = 0;
2740 if (dd->vms_wantversions) collectversions(dd);
2741 return &dd->entry;
2742
2743} /* end of readdir() */
2744/*}}}*/
2745
2746/*
2747 * Return something that can be used in a seekdir later.
2748 */
2749/*{{{ long telldir(DIR *dd)*/
2750long
2751telldir(DIR *dd)
2752{
2753 return dd->count;
2754}
2755/*}}}*/
2756
2757/*
2758 * Return to a spot where we used to be. Brute force.
2759 */
2760/*{{{ void seekdir(DIR *dd,long count)*/
2761void
2762seekdir(DIR *dd, long count)
2763{
2764 int vms_wantversions;
a0d0e21e
LW
2765
2766 /* If we haven't done anything yet... */
2767 if (dd->count == 0)
2768 return;
2769
2770 /* Remember some state, and clear it. */
2771 vms_wantversions = dd->vms_wantversions;
2772 dd->vms_wantversions = 0;
748a9306 2773 _ckvmssts(lib$find_file_end(&dd->context));
a0d0e21e
LW
2774 dd->context = 0;
2775
2776 /* The increment is in readdir(). */
2777 for (dd->count = 0; dd->count < count; )
2778 (void)readdir(dd);
2779
2780 dd->vms_wantversions = vms_wantversions;
2781
2782} /* end of seekdir() */
2783/*}}}*/
2784
2785/* VMS subprocess management
2786 *
2787 * my_vfork() - just a vfork(), after setting a flag to record that
2788 * the current script is trying a Unix-style fork/exec.
2789 *
2790 * vms_do_aexec() and vms_do_exec() are called in response to the
2791 * perl 'exec' function. If this follows a vfork call, then they
2792 * call out the the regular perl routines in doio.c which do an
2793 * execvp (for those who really want to try this under VMS).
2794 * Otherwise, they do exactly what the perl docs say exec should
2795 * do - terminate the current script and invoke a new command
2796 * (See below for notes on command syntax.)
2797 *
2798 * do_aspawn() and do_spawn() implement the VMS side of the perl
2799 * 'system' function.
2800 *
2801 * Note on command arguments to perl 'exec' and 'system': When handled
2802 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
2803 * are concatenated to form a DCL command string. If the first arg
2804 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
2805 * the the command string is hrnded off to DCL directly. Otherwise,
2806 * the first token of the command is taken as the filespec of an image
2807 * to run. The filespec is expanded using a default type of '.EXE' and
2808 * the process defaults for device, directory, etc., and the resultant
2809 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
2810 * the command string as parameters. This is perhaps a bit compicated,
2811 * but I hope it will form a happy medium between what VMS folks expect
2812 * from lib$spawn and what Unix folks expect from exec.
2813 */
2814
2815static int vfork_called;
2816
2817/*{{{int my_vfork()*/
2818int
2819my_vfork()
2820{
748a9306 2821 vfork_called++;
a0d0e21e
LW
2822 return vfork();
2823}
2824/*}}}*/
2825
4633a7c4
LW
2826
2827static struct dsc$descriptor_s VMScmd = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,Nullch};
2828
a0d0e21e 2829static void
4633a7c4
LW
2830vms_execfree() {
2831 if (Cmd) {
e518068a 2832 Safefree(Cmd);
4633a7c4
LW
2833 Cmd = Nullch;
2834 }
2835 if (VMScmd.dsc$a_pointer) {
2836 Safefree(VMScmd.dsc$a_pointer);
2837 VMScmd.dsc$w_length = 0;
2838 VMScmd.dsc$a_pointer = Nullch;
2839 }
2840}
2841
2842static char *
2843setup_argstr(SV *really, SV **mark, SV **sp)
a0d0e21e 2844{
809a5acc 2845 dTHR;
4633a7c4 2846 char *junk, *tmps = Nullch;
a0d0e21e
LW
2847 register size_t cmdlen = 0;
2848 size_t rlen;
2849 register SV **idx;
2850
2851 idx = mark;
4633a7c4
LW
2852 if (really) {
2853 tmps = SvPV(really,rlen);
2854 if (*tmps) {
2855 cmdlen += rlen + 1;
2856 idx++;
2857 }
a0d0e21e
LW
2858 }
2859
2860 for (idx++; idx <= sp; idx++) {
2861 if (*idx) {
2862 junk = SvPVx(*idx,rlen);
2863 cmdlen += rlen ? rlen + 1 : 0;
2864 }
2865 }
e518068a 2866 New(401,Cmd,cmdlen+1,char);
a0d0e21e 2867
4633a7c4
LW
2868 if (tmps && *tmps) {
2869 strcpy(Cmd,tmps);
a0d0e21e
LW
2870 mark++;
2871 }
4633a7c4 2872 else *Cmd = '\0';
a0d0e21e
LW
2873 while (++mark <= sp) {
2874 if (*mark) {
4633a7c4
LW
2875 strcat(Cmd," ");
2876 strcat(Cmd,SvPVx(*mark,na));
a0d0e21e
LW
2877 }
2878 }
4633a7c4 2879 return Cmd;
a0d0e21e
LW
2880
2881} /* end of setup_argstr() */
2882
4633a7c4 2883
a0d0e21e 2884static unsigned long int
4633a7c4 2885setup_cmddsc(char *cmd, int check_img)
a0d0e21e
LW
2886{
2887 char resspec[NAM$C_MAXRSS+1];
2888 $DESCRIPTOR(defdsc,".EXE");
2889 $DESCRIPTOR(resdsc,resspec);
2890 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
2891 unsigned long int cxt = 0, flags = 1, retsts;
2892 register char *s, *rest, *cp;
2893 register int isdcl = 0;
2894
2895 s = cmd;
2896 while (*s && isspace(*s)) s++;
2897 if (check_img) {
2898 if (*s == '$') { /* Check whether this is a DCL command: leading $ and */
2899 isdcl = 1; /* no dev/dir separators (i.e. not a foreign command) */
2900 for (cp = s; *cp && *cp != '/' && !isspace(*cp); cp++) {
2901 if (*cp == ':' || *cp == '[' || *cp == '<') {
2902 isdcl = 0;
2903 break;
2904 }
2905 }
2906 }
2907 }
2908 else isdcl = 1;
2909 if (isdcl) { /* It's a DCL command, just do it. */
4633a7c4 2910 VMScmd.dsc$w_length = strlen(cmd);
e518068a 2911 if (cmd == Cmd) {
2912 VMScmd.dsc$a_pointer = Cmd;
2913 Cmd = Nullch; /* Don't try to free twice in vms_execfree() */
2914 }
2915 else VMScmd.dsc$a_pointer = savepvn(cmd,VMScmd.dsc$w_length);
a0d0e21e
LW
2916 }
2917 else { /* assume first token is an image spec */
2918 cmd = s;
2919 while (*s && !isspace(*s)) s++;
2920 rest = *s ? s : 0;
2921 imgdsc.dsc$a_pointer = cmd;
2922 imgdsc.dsc$w_length = s - cmd;
2923 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
4633a7c4
LW
2924 if (!(retsts & 1)) {
2925 /* just hand off status values likely to be due to user error */
2926 if (retsts == RMS$_FNF || retsts == RMS$_DNF ||
2927 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
2928 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
2929 else { _ckvmssts(retsts); }
2930 }
a0d0e21e 2931 else {
748a9306 2932 _ckvmssts(lib$find_file_end(&cxt));
a0d0e21e
LW
2933 s = resspec;
2934 while (*s && !isspace(*s)) s++;
2935 *s = '\0';
09b7f37c 2936 if (!cando_by_name(S_IXUSR,0,resspec)) return RMS$_PRV;
e518068a 2937 New(402,VMScmd.dsc$a_pointer,7 + s - resspec + (rest ? strlen(rest) : 0),char);
4633a7c4
LW
2938 strcpy(VMScmd.dsc$a_pointer,"$ MCR ");
2939 strcat(VMScmd.dsc$a_pointer,resspec);
2940 if (rest) strcat(VMScmd.dsc$a_pointer,rest);
2941 VMScmd.dsc$w_length = strlen(VMScmd.dsc$a_pointer);
a0d0e21e
LW
2942 }
2943 }
2944
a3e9d8c9 2945 return (VMScmd.dsc$w_length > 255 ? CLI$_BUFOVF : SS$_NORMAL);
2946
a0d0e21e
LW
2947} /* end of setup_cmddsc() */
2948
a3e9d8c9 2949
a0d0e21e
LW
2950/* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
2951bool
2952vms_do_aexec(SV *really,SV **mark,SV **sp)
2953{
61bb5906 2954 dTHR;
a0d0e21e
LW
2955 if (sp > mark) {
2956 if (vfork_called) { /* this follows a vfork - act Unixish */
748a9306
LW
2957 vfork_called--;
2958 if (vfork_called < 0) {
2959 warn("Internal inconsistency in tracking vforks");
2960 vfork_called = 0;
2961 }
2962 else return do_aexec(really,mark,sp);
a0d0e21e 2963 }
4633a7c4
LW
2964 /* no vfork - act VMSish */
2965 return vms_do_exec(setup_argstr(really,mark,sp));
748a9306 2966
a0d0e21e
LW
2967 }
2968
2969 return FALSE;
2970} /* end of vms_do_aexec() */
2971/*}}}*/
2972
2973/* {{{bool vms_do_exec(char *cmd) */
2974bool
2975vms_do_exec(char *cmd)
2976{
2977
2978 if (vfork_called) { /* this follows a vfork - act Unixish */
748a9306
LW
2979 vfork_called--;
2980 if (vfork_called < 0) {
2981 warn("Internal inconsistency in tracking vforks");
2982 vfork_called = 0;
2983 }
2984 else return do_exec(cmd);
a0d0e21e 2985 }
748a9306
LW
2986
2987 { /* no vfork - act VMSish */
748a9306 2988 unsigned long int retsts;
a0d0e21e 2989
1e422769 2990 TAINT_ENV();
2991 TAINT_PROPER("exec");
4633a7c4
LW
2992 if ((retsts = setup_cmddsc(cmd,1)) & 1)
2993 retsts = lib$do_command(&VMScmd);
a0d0e21e 2994
09b7f37c
CB
2995 switch (retsts) {
2996 case RMS$_FNF:
2997 set_errno(ENOENT); break;
2998 case RMS$_DNF: case RMS$_DIR: case RMS$_DEV:
2999 set_errno(ENOTDIR); break;
3000 case RMS$_PRV:
3001 set_errno(EACCES); break;
3002 case RMS$_SYN:
3003 set_errno(EINVAL); break;
3004 case CLI$_BUFOVF:
3005 set_errno(E2BIG); break;
3006 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
3007 _ckvmssts(retsts); /* fall through */
3008 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
3009 set_errno(EVMSERR);
3010 }
748a9306 3011 set_vaxc_errno(retsts);
a0d0e21e 3012 if (dowarn)
4633a7c4
LW
3013 warn("Can't exec \"%s\": %s", VMScmd.dsc$a_pointer, Strerror(errno));
3014 vms_execfree();
a0d0e21e
LW
3015 }
3016
3017 return FALSE;
3018
3019} /* end of vms_do_exec() */
3020/*}}}*/
3021
3022unsigned long int do_spawn(char *);
3023
61bb5906 3024/* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
a0d0e21e 3025unsigned long int
61bb5906 3026do_aspawn(void *really,void **mark,void **sp)
a0d0e21e 3027{
61bb5906
CB
3028 dTHR;
3029 if (sp > mark) return do_spawn(setup_argstr((SV *)really,(SV **)mark,(SV **)sp));
a0d0e21e
LW
3030
3031 return SS$_ABORT;
3032} /* end of do_aspawn() */
3033/*}}}*/
3034
3035/* {{{unsigned long int do_spawn(char *cmd) */
3036unsigned long int
3037do_spawn(char *cmd)
3038{
09b7f37c 3039 unsigned long int sts, substs, hadcmd = 1;
a0d0e21e 3040
1e422769 3041 TAINT_ENV();
3042 TAINT_PROPER("spawn");
748a9306 3043 if (!cmd || !*cmd) {
4633a7c4 3044 hadcmd = 0;
09b7f37c 3045 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
748a9306 3046 }
09b7f37c
CB
3047 else if ((sts = setup_cmddsc(cmd,0)) & 1) {
3048 sts = lib$spawn(&VMScmd,0,0,0,0,0,&substs,0,0,0,0,0,0);
748a9306 3049 }
a0d0e21e 3050
09b7f37c
CB
3051 if (!(sts & 1)) {
3052 switch (sts) {
3053 case RMS$_FNF:
3054 set_errno(ENOENT); break;
3055 case RMS$_DNF: case RMS$_DIR: case RMS$_DEV:
3056 set_errno(ENOTDIR); break;
3057 case RMS$_PRV:
3058 set_errno(EACCES); break;
3059 case RMS$_SYN:
3060 set_errno(EINVAL); break;
3061 case CLI$_BUFOVF:
3062 set_errno(E2BIG); break;
3063 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
3064 _ckvmssts(sts); /* fall through */
3065 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
3066 set_errno(EVMSERR);
3067 }
3068 set_vaxc_errno(sts);
a0d0e21e 3069 if (dowarn)
a3e9d8c9 3070 warn("Can't spawn \"%s\": %s",
4633a7c4 3071 hadcmd ? VMScmd.dsc$a_pointer : "", Strerror(errno));
a0d0e21e 3072 }
4633a7c4 3073 vms_execfree();
a0d0e21e
LW
3074 return substs;
3075
3076} /* end of do_spawn() */
3077/*}}}*/
3078
3079/*
3080 * A simple fwrite replacement which outputs itmsz*nitm chars without
3081 * introducing record boundaries every itmsz chars.
3082 */
3083/*{{{ int my_fwrite(void *src, size_t itmsz, size_t nitm, FILE *dest)*/
3084int
3085my_fwrite(void *src, size_t itmsz, size_t nitm, FILE *dest)
3086{
3087 register char *cp, *end;
3088
3089 end = (char *)src + itmsz * nitm;
3090
3091 while ((char *)src <= end) {
3092 for (cp = src; cp <= end; cp++) if (!*cp) break;
3093 if (fputs(src,dest) == EOF) return EOF;
3094 if (cp < end)
3095 if (fputc('\0',dest) == EOF) return EOF;
3096 src = cp + 1;
3097 }
3098
3099 return 1;
3100
3101} /* end of my_fwrite() */
3102/*}}}*/
3103
d27fe803
JH
3104/*{{{ int my_flush(FILE *fp)*/
3105int
3106my_flush(FILE *fp)
3107{
3108 int res;
3109 if ((res = fflush(fp)) == 0) {
3110#ifdef VMS_DO_SOCKETS
61bb5906 3111 Stat_t s;
d27fe803
JH
3112 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
3113#endif
3114 res = fsync(fileno(fp));
3115 }
3116 return res;
3117}
3118/*}}}*/
3119
748a9306
LW
3120/*
3121 * Here are replacements for the following Unix routines in the VMS environment:
3122 * getpwuid Get information for a particular UIC or UID
3123 * getpwnam Get information for a named user
3124 * getpwent Get information for each user in the rights database
3125 * setpwent Reset search to the start of the rights database
3126 * endpwent Finish searching for users in the rights database
3127 *
3128 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
3129 * (defined in pwd.h), which contains the following fields:-
3130 * struct passwd {
3131 * char *pw_name; Username (in lower case)
3132 * char *pw_passwd; Hashed password
3133 * unsigned int pw_uid; UIC
3134 * unsigned int pw_gid; UIC group number
3135 * char *pw_unixdir; Default device/directory (VMS-style)
3136 * char *pw_gecos; Owner name
3137 * char *pw_dir; Default device/directory (Unix-style)
3138 * char *pw_shell; Default CLI name (eg. DCL)
3139 * };
3140 * If the specified user does not exist, getpwuid and getpwnam return NULL.
3141 *
3142 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
3143 * not the UIC member number (eg. what's returned by getuid()),
3144 * getpwuid() can accept either as input (if uid is specified, the caller's
3145 * UIC group is used), though it won't recognise gid=0.
3146 *
3147 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
3148 * information about other users in your group or in other groups, respectively.
3149 * If the required privilege is not available, then these routines fill only
3150 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
3151 * string).
3152 *
3153 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
3154 */
3155
3156/* sizes of various UAF record fields */
3157#define UAI$S_USERNAME 12
3158#define UAI$S_IDENT 31
3159#define UAI$S_OWNER 31
3160#define UAI$S_DEFDEV 31
3161#define UAI$S_DEFDIR 63
3162#define UAI$S_DEFCLI 31
3163#define UAI$S_PWD 8
3164
3165#define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
3166 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
3167 (uic).uic$v_group != UIC$K_WILD_GROUP)
3168
4633a7c4
LW
3169static char __empty[]= "";
3170static struct passwd __passwd_empty=
748a9306
LW
3171 {(char *) __empty, (char *) __empty, 0, 0,
3172 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
3173static int contxt= 0;
3174static struct passwd __pwdcache;
3175static char __pw_namecache[UAI$S_IDENT+1];
3176
748a9306
LW
3177/*
3178 * This routine does most of the work extracting the user information.
3179 */
3180static int fillpasswd (const char *name, struct passwd *pwd)
a0d0e21e 3181{
748a9306
LW
3182 static struct {
3183 unsigned char length;
3184 char pw_gecos[UAI$S_OWNER+1];
3185 } owner;
3186 static union uicdef uic;
3187 static struct {
3188 unsigned char length;
3189 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
3190 } defdev;
3191 static struct {
3192 unsigned char length;
3193 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
3194 } defdir;
3195 static struct {
3196 unsigned char length;
3197 char pw_shell[UAI$S_DEFCLI+1];
3198 } defcli;
3199 static char pw_passwd[UAI$S_PWD+1];
3200
3201 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
3202 struct dsc$descriptor_s name_desc;
c07a80fd 3203 unsigned long int sts;
748a9306 3204
4633a7c4 3205 static struct itmlst_3 itmlst[]= {
748a9306
LW
3206 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
3207 {sizeof(uic), UAI$_UIC, &uic, &luic},
3208 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
3209 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
3210 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
3211 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
3212 {0, 0, NULL, NULL}};
3213
3214 name_desc.dsc$w_length= strlen(name);
3215 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
3216 name_desc.dsc$b_class= DSC$K_CLASS_S;
3217 name_desc.dsc$a_pointer= (char *) name;
3218
3219/* Note that sys$getuai returns many fields as counted strings. */
c07a80fd 3220 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
3221 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
3222 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
3223 }
3224 else { _ckvmssts(sts); }
3225 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
748a9306
LW
3226
3227 if ((int) owner.length < lowner) lowner= (int) owner.length;
3228 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
3229 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
3230 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
3231 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
3232 owner.pw_gecos[lowner]= '\0';
3233 defdev.pw_dir[ldefdev+ldefdir]= '\0';
3234 defcli.pw_shell[ldefcli]= '\0';
3235 if (valid_uic(uic)) {
3236 pwd->pw_uid= uic.uic$l_uic;
3237 pwd->pw_gid= uic.uic$v_group;
3238 }
3239 else
3240 warn("getpwnam returned invalid UIC %#o for user \"%s\"");
3241 pwd->pw_passwd= pw_passwd;
3242 pwd->pw_gecos= owner.pw_gecos;
3243 pwd->pw_dir= defdev.pw_dir;
3244 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
3245 pwd->pw_shell= defcli.pw_shell;
3246 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
3247 int ldir;
3248 ldir= strlen(pwd->pw_unixdir) - 1;
3249 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
3250 }
3251 else
3252 strcpy(pwd->pw_unixdir, pwd->pw_dir);
01b8edb6 3253 __mystrtolower(pwd->pw_unixdir);
c07a80fd 3254 return 1;
a0d0e21e 3255}
748a9306
LW
3256
3257/*
3258 * Get information for a named user.
3259*/
3260/*{{{struct passwd *getpwnam(char *name)*/
3261struct passwd *my_getpwnam(char *name)
3262{
3263 struct dsc$descriptor_s name_desc;
3264 union uicdef uic;
aa689395 3265 unsigned long int status, sts;
748a9306
LW
3266
3267 __pwdcache = __passwd_empty;
c07a80fd 3268 if (!fillpasswd(name, &__pwdcache)) {
748a9306
LW
3269 /* We still may be able to determine pw_uid and pw_gid */
3270 name_desc.dsc$w_length= strlen(name);
3271 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
3272 name_desc.dsc$b_class= DSC$K_CLASS_S;
3273 name_desc.dsc$a_pointer= (char *) name;
aa689395 3274 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
748a9306
LW
3275 __pwdcache.pw_uid= uic.uic$l_uic;
3276 __pwdcache.pw_gid= uic.uic$v_group;
3277 }
c07a80fd 3278 else {
aa689395 3279 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
3280 set_vaxc_errno(sts);
3281 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
c07a80fd 3282 return NULL;
3283 }
aa689395 3284 else { _ckvmssts(sts); }
c07a80fd 3285 }
748a9306 3286 }
748a9306
LW
3287 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
3288 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
3289 __pwdcache.pw_name= __pw_namecache;
3290 return &__pwdcache;
3291} /* end of my_getpwnam() */
a0d0e21e
LW
3292/*}}}*/
3293
748a9306
LW
3294/*
3295 * Get information for a particular UIC or UID.
3296 * Called by my_getpwent with uid=-1 to list all users.
3297*/
3298/*{{{struct passwd *my_getpwuid(Uid_t uid)*/
3299struct passwd *my_getpwuid(Uid_t uid)
a0d0e21e 3300{
748a9306
LW
3301 const $DESCRIPTOR(name_desc,__pw_namecache);
3302 unsigned short lname;
3303 union uicdef uic;
3304 unsigned long int status;
3305
3306 if (uid == (unsigned int) -1) {
3307 do {
3308 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
3309 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
c07a80fd 3310 set_vaxc_errno(status);
3311 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
748a9306
LW
3312 my_endpwent();
3313 return NULL;
3314 }
3315 else { _ckvmssts(status); }
3316 } while (!valid_uic (uic));
3317 }
3318 else {
3319 uic.uic$l_uic= uid;
c07a80fd 3320 if (!uic.uic$v_group)
76e3520e 3321 uic.uic$v_group= PerlProc_getgid();
748a9306
LW
3322 if (valid_uic(uic))
3323 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
3324 else status = SS$_IVIDENT;
c07a80fd 3325 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
3326 status == RMS$_PRV) {
3327 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
3328 return NULL;
3329 }
3330 else { _ckvmssts(status); }
748a9306
LW
3331 }
3332 __pw_namecache[lname]= '\0';
01b8edb6 3333 __mystrtolower(__pw_namecache);
748a9306
LW
3334
3335 __pwdcache = __passwd_empty;
3336 __pwdcache.pw_name = __pw_namecache;
3337
3338/* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
3339 The identifier's value is usually the UIC, but it doesn't have to be,
3340 so if we can, we let fillpasswd update this. */
3341 __pwdcache.pw_uid = uic.uic$l_uic;
3342 __pwdcache.pw_gid = uic.uic$v_group;
3343
c07a80fd 3344 fillpasswd(__pw_namecache, &__pwdcache);
748a9306 3345 return &__pwdcache;
a0d0e21e 3346
748a9306
LW
3347} /* end of my_getpwuid() */
3348/*}}}*/
3349
3350/*
3351 * Get information for next user.
3352*/
3353/*{{{struct passwd *my_getpwent()*/
3354struct passwd *my_getpwent()
3355{
3356 return (my_getpwuid((unsigned int) -1));
3357}
3358/*}}}*/
a0d0e21e 3359
748a9306
LW
3360/*
3361 * Finish searching rights database for users.
3362*/
3363/*{{{void my_endpwent()*/
3364void my_endpwent()
3365{
3366 if (contxt) {
3367 _ckvmssts(sys$finish_rdb(&contxt));
3368 contxt= 0;
3369 }
a0d0e21e
LW
3370}
3371/*}}}*/
748a9306 3372
61bb5906
CB
3373#ifdef HOMEGROWN_POSIX_SIGNALS
3374 /* Signal handling routines, pulled into the core from POSIX.xs.
3375 *
3376 * We need these for threads, so they've been rolled into the core,
3377 * rather than left in POSIX.xs.
3378 *
3379 * (DRS, Oct 23, 1997)
3380 */
5b411029 3381
61bb5906
CB
3382 /* sigset_t is atomic under VMS, so these routines are easy */
3383/*{{{int my_sigemptyset(sigset_t *) */
5b411029 3384int my_sigemptyset(sigset_t *set) {
61bb5906
CB
3385 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
3386 *set = 0; return 0;
5b411029 3387}
61bb5906
CB
3388/*}}}*/
3389
3390
3391/*{{{int my_sigfillset(sigset_t *)*/
5b411029 3392int my_sigfillset(sigset_t *set) {
61bb5906
CB
3393 int i;
3394 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
3395 for (i = 0; i < NSIG; i++) *set |= (1 << i);
3396 return 0;
5b411029 3397}
61bb5906
CB
3398/*}}}*/
3399
3400
3401/*{{{int my_sigaddset(sigset_t *set, int sig)*/
5b411029 3402int my_sigaddset(sigset_t *set, int sig) {
61bb5906
CB
3403 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
3404 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
3405 *set |= (1 << (sig - 1));
3406 return 0;
5b411029 3407}
61bb5906
CB
3408/*}}}*/
3409
3410
3411/*{{{int my_sigdelset(sigset_t *set, int sig)*/
5b411029 3412int my_sigdelset(sigset_t *set, int sig) {
61bb5906
CB
3413 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
3414 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
3415 *set &= ~(1 << (sig - 1));
3416 return 0;
5b411029 3417}
61bb5906
CB
3418/*}}}*/
3419
3420
3421/*{{{int my_sigismember(sigset_t *set, int sig)*/
5b411029 3422int my_sigismember(sigset_t *set, int sig) {
61bb5906
CB
3423 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
3424 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
3425 *set & (1 << (sig - 1));
5b411029 3426}
61bb5906 3427/*}}}*/
5b411029 3428
5b411029 3429
61bb5906
CB
3430/*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
3431int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
3432 sigset_t tempmask;
3433
3434 /* If set and oset are both null, then things are badly wrong. Bail out. */
3435 if ((oset == NULL) && (set == NULL)) {
3436 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
5b411029
MB
3437 return -1;
3438 }
5b411029 3439
61bb5906
CB
3440 /* If set's null, then we're just handling a fetch. */
3441 if (set == NULL) {
3442 tempmask = sigblock(0);
3443 }
3444 else {
3445 switch (how) {
3446 case SIG_SETMASK:
3447 tempmask = sigsetmask(*set);
3448 break;
3449 case SIG_BLOCK:
3450 tempmask = sigblock(*set);
3451 break;
3452 case SIG_UNBLOCK:
3453 tempmask = sigblock(0);
3454 sigsetmask(*oset & ~tempmask);
3455 break;
3456 default:
3457 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
3458 return -1;
3459 }
3460 }
3461
3462 /* Did they pass us an oset? If so, stick our holding mask into it */
3463 if (oset)
3464 *oset = tempmask;
5b411029 3465
61bb5906 3466 return 0;
5b411029 3467}
61bb5906
CB
3468/*}}}*/
3469#endif /* HOMEGROWN_POSIX_SIGNALS */
3470
5b411029 3471
ff0cee69 3472/* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
3473 * my_utime(), and flex_stat(), all of which operate on UTC unless
3474 * VMSISH_TIMES is true.
3475 */
3476/* method used to handle UTC conversions:
3477 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
e518068a 3478 */
ff0cee69 3479static int gmtime_emulation_type;
3480/* number of secs to add to UTC POSIX-style time to get local time */
3481static long int utc_offset_secs;
e518068a 3482
ff0cee69 3483/* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
3484 * in vmsish.h. #undef them here so we can call the CRTL routines
3485 * directly.
e518068a 3486 */
3487#undef gmtime
ff0cee69 3488#undef localtime
3489#undef time
3490
61bb5906
CB
3491#if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
3492# define RTL_USES_UTC 1
3493#endif
3494
3495static time_t toutc_dst(time_t loc) {
3496 struct tm *rsltmp;
3497
3498 if ((rsltmp = localtime(&loc)) == NULL) return -1;
3499 loc -= utc_offset_secs;
3500 if (rsltmp->tm_isdst) loc -= 3600;
3501 return loc;
3502}
3503#define _toutc(secs) ((secs) == -1 ? -1 : \
3504 ((gmtime_emulation_type || my_time(NULL)), \
3505 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
3506 ((secs) - utc_offset_secs))))
3507
3508static time_t toloc_dst(time_t utc) {
3509 struct tm *rsltmp;
3510
3511 utc += utc_offset_secs;
3512 if ((rsltmp = localtime(&utc)) == NULL) return -1;
3513 if (rsltmp->tm_isdst) utc += 3600;
3514 return utc;
3515}
3516#define _toloc(secs) ((secs) == -1 ? -1 : \
3517 ((gmtime_emulation_type || my_time(NULL)), \
3518 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
3519 ((secs) + utc_offset_secs))))
3520
3521
ff0cee69 3522/* my_time(), my_localtime(), my_gmtime()
61bb5906 3523 * By default traffic in UTC time values, using CRTL gmtime() or
ff0cee69 3524 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
61bb5906
CB
3525 * Note: We need to use these functions even when the CRTL has working
3526 * UTC support, since they also handle C<use vmsish qw(times);>
3527 *
ff0cee69 3528 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
3529 * Modified by Charles Bailey <bailey@genetics.upenn.edu>
3530 */
3531
3532/*{{{time_t my_time(time_t *timep)*/
3533time_t my_time(time_t *timep)
e518068a 3534{
809a5acc 3535 dTHR;
e518068a 3536 time_t when;
61bb5906 3537 struct tm *tm_p;
e518068a 3538
3539 if (gmtime_emulation_type == 0) {
61bb5906
CB
3540 int dstnow;
3541 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
3542 /* results of calls to gmtime() and localtime() */
3543 /* for same &base */
ff0cee69 3544
e518068a 3545 gmtime_emulation_type++;
ff0cee69 3546 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
3547 char *off;
3548
e518068a 3549 gmtime_emulation_type++;
ff0cee69 3550 if ((off = my_getenv("SYS$TIMEZONE_DIFFERENTIAL")) == NULL) {
e518068a 3551 gmtime_emulation_type++;
ff0cee69 3552 warn("no UTC offset information; assuming local time is UTC");
3553 }
3554 else { utc_offset_secs = atol(off); }
e518068a 3555 }
ff0cee69 3556 else { /* We've got a working gmtime() */
3557 struct tm gmt, local;
e518068a 3558
ff0cee69 3559 gmt = *tm_p;
3560 tm_p = localtime(&base);
3561 local = *tm_p;
3562 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
3563 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
3564 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
3565 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
3566 }
e518068a 3567 }
ff0cee69 3568
3569 when = time(NULL);
61bb5906
CB
3570# ifdef VMSISH_TIME
3571# ifdef RTL_USES_UTC
3572 if (VMSISH_TIME) when = _toloc(when);
3573# else
3574 if (!VMSISH_TIME) when = _toutc(when);
3575# endif
3576# endif
ff0cee69 3577 if (timep != NULL) *timep = when;
3578 return when;
3579
3580} /* end of my_time() */
3581/*}}}*/
3582
3583
3584/*{{{struct tm *my_gmtime(const time_t *timep)*/
3585struct tm *
3586my_gmtime(const time_t *timep)
3587{
809a5acc 3588 dTHR;
ff0cee69 3589 char *p;
3590 time_t when;
61bb5906 3591 struct tm *rsltmp;
ff0cee69 3592
68dc0745 3593 if (timep == NULL) {
3594 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
3595 return NULL;
3596 }
3597 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
ff0cee69 3598
3599 when = *timep;
3600# ifdef VMSISH_TIME
61bb5906
CB
3601 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
3602# endif
3603# ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
3604 return gmtime(&when);
3605# else
ff0cee69 3606 /* CRTL localtime() wants local time as input, so does no tz correction */
61bb5906
CB
3607 rsltmp = localtime(&when);
3608 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
3609 return rsltmp;
3610#endif
e518068a 3611} /* end of my_gmtime() */
e518068a 3612/*}}}*/
3613
3614
ff0cee69 3615/*{{{struct tm *my_localtime(const time_t *timep)*/
3616struct tm *
3617my_localtime(const time_t *timep)
3618{
809a5acc 3619 dTHR;
ff0cee69 3620 time_t when;
61bb5906 3621 struct tm *rsltmp;
ff0cee69 3622
68dc0745 3623 if (timep == NULL) {
3624 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
3625 return NULL;
3626 }
3627 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
ff0cee69 3628 if (gmtime_emulation_type == 0) (void) my_time(NULL); /* Init UTC */
3629
3630 when = *timep;
61bb5906 3631# ifdef RTL_USES_UTC
ff0cee69 3632# ifdef VMSISH_TIME
61bb5906 3633 if (VMSISH_TIME) when = _toutc(when);
ff0cee69 3634# endif
61bb5906 3635 /* CRTL localtime() wants UTC as input, does tz correction itself */
ff0cee69 3636 return localtime(&when);
61bb5906
CB
3637# else
3638# ifdef VMSISH_TIME
3639 if (!VMSISH_TIME) when = _toloc(when); /* Input was UTC */
3640# endif
3641# endif
3642 /* CRTL localtime() wants local time as input, so does no tz correction */
3643 rsltmp = localtime(&when);
3644 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = -1;
3645 return rsltmp;
ff0cee69 3646
3647} /* end of my_localtime() */
3648/*}}}*/
3649
3650/* Reset definitions for later calls */
3651#define gmtime(t) my_gmtime(t)
3652#define localtime(t) my_localtime(t)
3653#define time(t) my_time(t)
3654
3655
3656/* my_utime - update modification time of a file
3657 * calling sequence is identical to POSIX utime(), but under
3658 * VMS only the modification time is changed; ODS-2 does not
3659 * maintain access times. Restrictions differ from the POSIX
3660 * definition in that the time can be changed as long as the
3661 * caller has permission to execute the necessary IO$_MODIFY $QIO;
3662 * no separate checks are made to insure that the caller is the
3663 * owner of the file or has special privs enabled.
3664 * Code here is based on Joe Meadows' FILE utility.
3665 */
3666
3667/* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
3668 * to VMS epoch (01-JAN-1858 00:00:00.00)
3669 * in 100 ns intervals.
3670 */
3671static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
3672
3673/*{{{int my_utime(char *path, struct utimbuf *utimes)*/
3674int my_utime(char *file, struct utimbuf *utimes)
3675{
809a5acc 3676 dTHR;
ff0cee69 3677 register int i;
3678 long int bintime[2], len = 2, lowbit, unixtime,
3679 secscale = 10000000; /* seconds --> 100 ns intervals */
3680 unsigned long int chan, iosb[2], retsts;
3681 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
3682 struct FAB myfab = cc$rms_fab;
3683 struct NAM mynam = cc$rms_nam;
3684#if defined (__DECC) && defined (__VAX)
3685 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
3686 * at least through VMS V6.1, which causes a type-conversion warning.
3687 */
3688# pragma message save
3689# pragma message disable cvtdiftypes
3690#endif
3691 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
3692 struct fibdef myfib;
3693#if defined (__DECC) && defined (__VAX)
3694 /* This should be right after the declaration of myatr, but due
3695 * to a bug in VAX DEC C, this takes effect a statement early.
3696 */
3697# pragma message restore
3698#endif
3699 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
3700 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
3701 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
3702
3703 if (file == NULL || *file == '\0') {
3704 set_errno(ENOENT);
3705 set_vaxc_errno(LIB$_INVARG);
3706 return -1;
3707 }
3708 if (do_tovmsspec(file,vmsspec,0) == NULL) return -1;
3709
3710 if (utimes != NULL) {
3711 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
3712 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
3713 * Since time_t is unsigned long int, and lib$emul takes a signed long int
3714 * as input, we force the sign bit to be clear by shifting unixtime right
3715 * one bit, then multiplying by an extra factor of 2 in lib$emul().
3716 */
3717 lowbit = (utimes->modtime & 1) ? secscale : 0;
3718 unixtime = (long int) utimes->modtime;
61bb5906
CB
3719# ifdef VMSISH_TIME
3720 /* If input was UTC; convert to local for sys svc */
3721 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
ff0cee69 3722# endif
3723 unixtime >> 1; secscale << 1;
3724 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
3725 if (!(retsts & 1)) {
3726 set_errno(EVMSERR);
3727 set_vaxc_errno(retsts);
3728 return -1;
3729 }
3730 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
3731 if (!(retsts & 1)) {
3732 set_errno(EVMSERR);
3733 set_vaxc_errno(retsts);
3734 return -1;
3735 }
3736 }
3737 else {
3738 /* Just get the current time in VMS format directly */
3739 retsts = sys$gettim(bintime);
3740 if (!(retsts & 1)) {
3741 set_errno(EVMSERR);
3742 set_vaxc_errno(retsts);
3743 return -1;
3744 }
3745 }
3746
3747 myfab.fab$l_fna = vmsspec;
3748 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
3749 myfab.fab$l_nam = &mynam;
3750 mynam.nam$l_esa = esa;
3751 mynam.nam$b_ess = (unsigned char) sizeof esa;
3752 mynam.nam$l_rsa = rsa;
3753 mynam.nam$b_rss = (unsigned char) sizeof rsa;
3754
3755 /* Look for the file to be affected, letting RMS parse the file
3756 * specification for us as well. I have set errno using only
3757 * values documented in the utime() man page for VMS POSIX.
3758 */
3759 retsts = sys$parse(&myfab,0,0);
3760 if (!(retsts & 1)) {
3761 set_vaxc_errno(retsts);
3762 if (retsts == RMS$_PRV) set_errno(EACCES);
3763 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
3764 else set_errno(EVMSERR);
3765 return -1;
3766 }
3767 retsts = sys$search(&myfab,0,0);
3768 if (!(retsts & 1)) {
3769 set_vaxc_errno(retsts);
3770 if (retsts == RMS$_PRV) set_errno(EACCES);
3771 else if (retsts == RMS$_FNF) set_errno(ENOENT);
3772 else set_errno(EVMSERR);
3773 return -1;
3774 }
3775
3776 devdsc.dsc$w_length = mynam.nam$b_dev;
3777 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
3778
3779 retsts = sys$assign(&devdsc,&chan,0,0);
3780 if (!(retsts & 1)) {
3781 set_vaxc_errno(retsts);
3782 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
3783 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
3784 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
3785 else set_errno(EVMSERR);
3786 return -1;
3787 }
3788
3789 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
3790 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
3791
3792 memset((void *) &myfib, 0, sizeof myfib);
3793#ifdef __DECC
3794 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
3795 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
3796 /* This prevents the revision time of the file being reset to the current
3797 * time as a result of our IO$_MODIFY $QIO. */
3798 myfib.fib$l_acctl = FIB$M_NORECORD;
3799#else
3800 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
3801 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
3802 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
3803#endif
3804 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
3805 _ckvmssts(sys$dassgn(chan));
3806 if (retsts & 1) retsts = iosb[0];
3807 if (!(retsts & 1)) {
3808 set_vaxc_errno(retsts);
3809 if (retsts == SS$_NOPRIV) set_errno(EACCES);
3810 else set_errno(EVMSERR);
3811 return -1;
3812 }
3813
3814 return 0;
3815} /* end of my_utime() */
3816/*}}}*/
3817
748a9306
LW
3818/*
3819 * flex_stat, flex_fstat
3820 * basic stat, but gets it right when asked to stat
3821 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
3822 */
3823
3824/* encode_dev packs a VMS device name string into an integer to allow
3825 * simple comparisons. This can be used, for example, to check whether two
3826 * files are located on the same device, by comparing their encoded device
3827 * names. Even a string comparison would not do, because stat() reuses the
3828 * device name buffer for each call; so without encode_dev, it would be
3829 * necessary to save the buffer and use strcmp (this would mean a number of
3830 * changes to the standard Perl code, to say nothing of what a Perl script
3831 * would have to do.
3832 *
3833 * The device lock id, if it exists, should be unique (unless perhaps compared
3834 * with lock ids transferred from other nodes). We have a lock id if the disk is
3835 * mounted cluster-wide, which is when we tend to get long (host-qualified)
3836 * device names. Thus we use the lock id in preference, and only if that isn't
3837 * available, do we try to pack the device name into an integer (flagged by
3838 * the sign bit (LOCKID_MASK) being set).
3839 *
e518068a 3840 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
748a9306
LW
3841 * name and its encoded form, but it seems very unlikely that we will find
3842 * two files on different disks that share the same encoded device names,
3843 * and even more remote that they will share the same file id (if the test
3844 * is to check for the same file).
3845 *
3846 * A better method might be to use sys$device_scan on the first call, and to
3847 * search for the device, returning an index into the cached array.
3848 * The number returned would be more intelligable.
3849 * This is probably not worth it, and anyway would take quite a bit longer
3850 * on the first call.
3851 */
3852#define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
aa689395 3853static mydev_t encode_dev (const char *dev)
748a9306
LW
3854{
3855 int i;
3856 unsigned long int f;
aa689395 3857 mydev_t enc;
748a9306
LW
3858 char c;
3859 const char *q;
3860
3861 if (!dev || !dev[0]) return 0;
3862
3863#if LOCKID_MASK
3864 {
3865 struct dsc$descriptor_s dev_desc;
3866 unsigned long int status, lockid, item = DVI$_LOCKID;
3867
3868 /* For cluster-mounted disks, the disk lock identifier is unique, so we
3869 can try that first. */
3870 dev_desc.dsc$w_length = strlen (dev);
3871 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
3872 dev_desc.dsc$b_class = DSC$K_CLASS_S;
3873 dev_desc.dsc$a_pointer = (char *) dev;
3874 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
3875 if (lockid) return (lockid & ~LOCKID_MASK);
3876 }
a0d0e21e 3877#endif
748a9306
LW
3878
3879 /* Otherwise we try to encode the device name */
3880 enc = 0;
3881 f = 1;
3882 i = 0;
3883 for (q = dev + strlen(dev); q--; q >= dev) {
3884 if (isdigit (*q))
3885 c= (*q) - '0';
3886 else if (isalpha (toupper (*q)))
3887 c= toupper (*q) - 'A' + (char)10;
3888 else
3889 continue; /* Skip '$'s */
3890 i++;
3891 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
3892 if (i>1) f *= 36;
3893 enc += f * (unsigned long int) c;
3894 }
3895 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
3896
3897} /* end of encode_dev() */
3898
3899static char namecache[NAM$C_MAXRSS+1];
3900
3901static int
3902is_null_device(name)
3903 const char *name;
3904{
3905 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
3906 The underscore prefix, controller letter, and unit number are
3907 independently optional; for our purposes, the colon punctuation
3908 is not. The colon can be trailed by optional directory and/or
3909 filename, but two consecutive colons indicates a nodename rather
3910 than a device. [pr] */
3911 if (*name == '_') ++name;
3912 if (tolower(*name++) != 'n') return 0;
3913 if (tolower(*name++) != 'l') return 0;
3914 if (tolower(*name) == 'a') ++name;
3915 if (*name == '0') ++name;
3916 return (*name++ == ':') && (*name != ':');
3917}
3918
3919/* Do the permissions allow some operation? Assumes statcache already set. */
3920/* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
61bb5906 3921 * subset of the applicable information.
748a9306
LW
3922 */
3923/*{{{I32 cando(I32 bit, I32 effective, struct stat *statbufp)*/
3924I32
61bb5906 3925cando(I32 bit, I32 effective, Stat_t *statbufp)
748a9306 3926{
aa689395 3927 if (statbufp == &statcache) return cando_by_name(bit,effective,namecache);
748a9306
LW
3928 else {
3929 char fname[NAM$C_MAXRSS+1];
3930 unsigned long int retsts;
3931 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
3932 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
3933
3934 /* If the struct mystat is stale, we're OOL; stat() overwrites the
3935 device name on successive calls */
61bb5906
CB
3936 devdsc.dsc$a_pointer = ((Stat_t *)statbufp)->st_devnam;
3937 devdsc.dsc$w_length = strlen(((Stat_t *)statbufp)->st_devnam);
748a9306
LW
3938 namdsc.dsc$a_pointer = fname;
3939 namdsc.dsc$w_length = sizeof fname - 1;
3940
61bb5906 3941 retsts = lib$fid_to_name(&devdsc,&(((Stat_t *)statbufp)->st_ino),
aa689395 3942 &namdsc,&namdsc.dsc$w_length,0,0);
748a9306
LW
3943 if (retsts & 1) {
3944 fname[namdsc.dsc$w_length] = '\0';
3945 return cando_by_name(bit,effective,fname);
3946 }
3947 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
3948 warn("Can't get filespec - stale stat buffer?\n");
3949 return FALSE;
3950 }
3951 _ckvmssts(retsts);
3952 return FALSE; /* Should never get to here */
3953 }
e518068a 3954} /* end of cando() */
748a9306
LW
3955/*}}}*/
3956
c07a80fd 3957
748a9306
LW
3958/*{{{I32 cando_by_name(I32 bit, I32 effective, char *fname)*/
3959I32
3960cando_by_name(I32 bit, I32 effective, char *fname)
3961{
3962 static char usrname[L_cuserid];
3963 static struct dsc$descriptor_s usrdsc =
3964 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
a5f75d66 3965 char vmsname[NAM$C_MAXRSS+1], fileified[NAM$C_MAXRSS+1];
748a9306
LW
3966 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
3967 unsigned short int retlen;
3968 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
3969 union prvdef curprv;
3970 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
3971 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
3972 struct itmlst_3 jpilst[2] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
3973 {0,0,0,0}};
3974
3975 if (!fname || !*fname) return FALSE;
01b8edb6 3976 /* Make sure we expand logical names, since sys$check_access doesn't */
3977 if (!strpbrk(fname,"/]>:")) {
3978 strcpy(fileified,fname);
3979 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) ;
3980 fname = fileified;
3981 }
a5f75d66
AD
3982 if (!do_tovmsspec(fname,vmsname,1)) return FALSE;
3983 retlen = namdsc.dsc$w_length = strlen(vmsname);
3984 namdsc.dsc$a_pointer = vmsname;
3985 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
3986 vmsname[retlen-1] == ':') {
3987 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
3988 namdsc.dsc$w_length = strlen(fileified);
3989 namdsc.dsc$a_pointer = fileified;
3990 }
3991
748a9306
LW
3992 if (!usrdsc.dsc$w_length) {
3993 cuserid(usrname);
3994 usrdsc.dsc$w_length = strlen(usrname);
3995 }
a5f75d66 3996
748a9306
LW
3997 switch (bit) {
3998 case S_IXUSR:
3999 case S_IXGRP:
4000 case S_IXOTH:
4001 access = ARM$M_EXECUTE;
4002 break;
4003 case S_IRUSR:
4004 case S_IRGRP:
4005 case S_IROTH:
4006 access = ARM$M_READ;
4007 break;
4008 case S_IWUSR:
4009 case S_IWGRP:
4010 case S_IWOTH:
4011 access = ARM$M_WRITE;
4012 break;
4013 case S_IDUSR:
4014 case S_IDGRP:
4015 case S_IDOTH:
4016 access = ARM$M_DELETE;
4017 break;
4018 default:
4019 return FALSE;
4020 }
4021
4022 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
bbce6d69 4023 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
61bb5906 4024 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
bbce6d69 4025 retsts == RMS$_DIR || retsts == RMS$_DEV) {
4026 set_vaxc_errno(retsts);
4027 if (retsts == SS$_NOPRIV) set_errno(EACCES);
4028 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
4029 else set_errno(ENOENT);
a3e9d8c9 4030 return FALSE;
4031 }
748a9306
LW
4032 if (retsts == SS$_NORMAL) {
4033 if (!privused) return TRUE;
4034 /* We can get access, but only by using privs. Do we have the
4035 necessary privs currently enabled? */
4036 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
4037 if ((privused & CHP$M_BYPASS) && !curprv.prv$v_bypass) return FALSE;
c07a80fd 4038 if ((privused & CHP$M_SYSPRV) && !curprv.prv$v_sysprv &&
4039 !curprv.prv$v_bypass) return FALSE;
4040 if ((privused & CHP$M_GRPPRV) && !curprv.prv$v_grpprv &&
4041 !curprv.prv$v_sysprv && !curprv.prv$v_bypass) return FALSE;
748a9306
LW
4042 if ((privused & CHP$M_READALL) && !curprv.prv$v_readall) return FALSE;
4043 return TRUE;
4044 }
4045 _ckvmssts(retsts);
4046
4047 return FALSE; /* Should never get here */
4048
4049} /* end of cando_by_name() */
4050/*}}}*/
4051
4052
61bb5906 4053/*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
748a9306 4054int
61bb5906 4055flex_fstat(int fd, Stat_t *statbufp)
748a9306 4056{
809a5acc 4057 dTHR;
b7ae7a0d 4058 if (!fstat(fd,(stat_t *) statbufp)) {
61bb5906 4059 if (statbufp == (Stat_t *) &statcache) *namecache == '\0';
b7ae7a0d 4060 statbufp->st_dev = encode_dev(statbufp->st_devnam);
61bb5906
CB
4061# ifdef RTL_USES_UTC
4062# ifdef VMSISH_TIME
4063 if (VMSISH_TIME) {
4064 statbufp->st_mtime = _toloc(statbufp->st_mtime);
4065 statbufp->st_atime = _toloc(statbufp->st_atime);
4066 statbufp->st_ctime = _toloc(statbufp->st_ctime);
4067 }
4068# endif
4069# else
ff0cee69 4070# ifdef VMSISH_TIME
4071 if (!VMSISH_TIME) { /* Return UTC instead of local time */
4072# else
4073 if (1) {
4074# endif
61bb5906
CB
4075 statbufp->st_mtime = _toutc(statbufp->st_mtime);
4076 statbufp->st_atime = _toutc(statbufp->st_atime);
4077 statbufp->st_ctime = _toutc(statbufp->st_ctime);
ff0cee69 4078 }
61bb5906 4079#endif
b7ae7a0d 4080 return 0;
4081 }
4082 return -1;
748a9306
LW
4083
4084} /* end of flex_fstat() */
4085/*}}}*/
4086
61bb5906 4087/*{{{ int flex_stat(char *fspec, Stat_t *statbufp)*/
748a9306 4088int
61bb5906 4089flex_stat(char *fspec, Stat_t *statbufp)
748a9306 4090{
809a5acc 4091 dTHR;
748a9306 4092 char fileified[NAM$C_MAXRSS+1];
bbce6d69 4093 int retval = -1;
748a9306 4094
61bb5906 4095 if (statbufp == (Stat_t *) &statcache)
aa689395 4096 do_tovmsspec(fspec,namecache,0);
748a9306
LW
4097 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
4098 memset(statbufp,0,sizeof *statbufp);
4099 statbufp->st_dev = encode_dev("_NLA0:");
4100 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
4101 statbufp->st_uid = 0x00010001;
4102 statbufp->st_gid = 0x0001;
4103 time((time_t *)&statbufp->st_mtime);
4104 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
4105 return 0;
4106 }
4107
bbce6d69 4108 /* Try for a directory name first. If fspec contains a filename without
61bb5906 4109 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
bbce6d69 4110 * and sea:[wine.dark]water. exist, we prefer the directory here.
4111 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
4112 * not sea:[wine.dark]., if the latter exists. If the intended target is
4113 * the file with null type, specify this by calling flex_stat() with
4114 * a '.' at the end of fspec.
4115 */
4116 if (do_fileify_dirspec(fspec,fileified,0) != NULL) {
4117 retval = stat(fileified,(stat_t *) statbufp);
61bb5906 4118 if (!retval && statbufp == (Stat_t *) &statcache)
aa689395 4119 strcpy(namecache,fileified);
748a9306 4120 }
bbce6d69 4121 if (retval) retval = stat(fspec,(stat_t *) statbufp);
ff0cee69 4122 if (!retval) {
4123 statbufp->st_dev = encode_dev(statbufp->st_devnam);
61bb5906
CB
4124# ifdef RTL_USES_UTC
4125# ifdef VMSISH_TIME
4126 if (VMSISH_TIME) {
4127 statbufp->st_mtime = _toloc(statbufp->st_mtime);
4128 statbufp->st_atime = _toloc(statbufp->st_atime);
4129 statbufp->st_ctime = _toloc(statbufp->st_ctime);
4130 }
4131# endif
4132# else
ff0cee69 4133# ifdef VMSISH_TIME
4134 if (!VMSISH_TIME) { /* Return UTC instead of local time */
4135# else
4136 if (1) {
4137# endif
61bb5906
CB
4138 statbufp->st_mtime = _toutc(statbufp->st_mtime);
4139 statbufp->st_atime = _toutc(statbufp->st_atime);
4140 statbufp->st_ctime = _toutc(statbufp->st_ctime);
ff0cee69 4141 }
61bb5906 4142# endif
ff0cee69 4143 }
748a9306
LW
4144 return retval;
4145
4146} /* end of flex_stat() */
4147/*}}}*/
4148
b7ae7a0d 4149/* Insures that no carriage-control translation will be done on a file. */
4150/*{{{FILE *my_binmode(FILE *fp, char iotype)*/
4151FILE *
4152my_binmode(FILE *fp, char iotype)
4153{
09b7f37c
CB
4154 char filespec[NAM$C_MAXRSS], *acmode, *s, *colon, *dirend = Nullch;
4155 int ret = 0, saverrno = errno, savevmserrno = vaxc$errno;
b7ae7a0d 4156 fpos_t pos;
4157
4158 if (!fgetname(fp,filespec)) return NULL;
09b7f37c
CB
4159 for (s = filespec; *s; s++) {
4160 if (*s == ':') colon = s;
4161 else if (*s == ']' || *s == '>') dirend = s;
4162 }
4163 /* Looks like a tmpfile, which will go away if reopened */
4164 if (s == dirend + 3) return fp;
4165 /* If we've got a non-file-structured device, clip off the trailing
4166 * junk, and don't lose sleep if we can't get a stream position. */
4167 if (dirend == Nullch) *(colon+1) = '\0';
4168 if (iotype != '-'&& (ret = fgetpos(fp, &pos)) == -1 && dirend) return NULL;
b7ae7a0d 4169 switch (iotype) {
4170 case '<': case 'r': acmode = "rb"; break;
17f28c40 4171 case '>': case 'w': case '|':
71be2cbc 4172 /* use 'a' instead of 'w' to avoid creating new file;
4173 fsetpos below will take care of restoring file position */
b7ae7a0d 4174 case 'a': acmode = "ab"; break;
17f28c40 4175 case '+': case 's': acmode = "rb+"; break;
71be2cbc 4176 case '-': acmode = fileno(fp) ? "ab" : "rb"; break;
4177 default:
4178 warn("Unrecognized iotype %c in my_binmode",iotype);
4179 acmode = "rb+";
b7ae7a0d 4180 }
4181 if (freopen(filespec,acmode,fp) == NULL) return NULL;
09b7f37c
CB
4182 if (iotype != '-' && ret != -1 && fsetpos(fp,&pos) == -1) return NULL;
4183 if (ret == -1) { set_errno(saverrno); set_vaxc_errno(savevmserrno); }
71be2cbc 4184 return fp;
b7ae7a0d 4185} /* end of my_binmode() */
4186/*}}}*/
4187
4188
c07a80fd 4189/*{{{char *my_getlogin()*/
4190/* VMS cuserid == Unix getlogin, except calling sequence */
4191char *
4192my_getlogin()
4193{
4194 static char user[L_cuserid];
4195 return cuserid(user);
4196}
4197/*}}}*/
4198
4199
a5f75d66
AD
4200/* rmscopy - copy a file using VMS RMS routines
4201 *
4202 * Copies contents and attributes of spec_in to spec_out, except owner
4203 * and protection information. Name and type of spec_in are used as
a3e9d8c9 4204 * defaults for spec_out. The third parameter specifies whether rmscopy()
4205 * should try to propagate timestamps from the input file to the output file.
4206 * If it is less than 0, no timestamps are preserved. If it is 0, then
4207 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
4208 * propagated to the output file at creation iff the output file specification
4209 * did not contain an explicit name or type, and the revision date is always
4210 * updated at the end of the copy operation. If it is greater than 0, then
4211 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
4212 * other than the revision date should be propagated, and bit 1 indicates
4213 * that the revision date should be propagated.
4214 *
4215 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
a5f75d66
AD
4216 *
4217 * Copyright 1996 by Charles Bailey <bailey@genetics.upenn.edu>.
4218 * Incorporates, with permission, some code from EZCOPY by Tim Adye
01b8edb6 4219 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
4220 * as part of the Perl standard distribution under the terms of the
4221 * GNU General Public License or the Perl Artistic License. Copies
4222 * of each may be found in the Perl standard distribution.
a5f75d66 4223 */
a3e9d8c9 4224/*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
a5f75d66 4225int
a3e9d8c9 4226rmscopy(char *spec_in, char *spec_out, int preserve_dates)
a5f75d66
AD
4227{
4228 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
4229 rsa[NAM$C_MAXRSS], ubf[32256];
4230 unsigned long int i, sts, sts2;
4231 struct FAB fab_in, fab_out;
4232 struct RAB rab_in, rab_out;
4233 struct NAM nam;
4234 struct XABDAT xabdat;
4235 struct XABFHC xabfhc;
4236 struct XABRDT xabrdt;
4237 struct XABSUM xabsum;
4238
4239 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
4240 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
4241 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4242 return 0;
4243 }
4244
4245 fab_in = cc$rms_fab;
4246 fab_in.fab$l_fna = vmsin;
4247 fab_in.fab$b_fns = strlen(vmsin);
4248 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
4249 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
4250 fab_in.fab$l_fop = FAB$M_SQO;
4251 fab_in.fab$l_nam = &nam;
a3e9d8c9 4252 fab_in.fab$l_xab = (void *) &xabdat;
a5f75d66
AD
4253
4254 nam = cc$rms_nam;
4255 nam.nam$l_rsa = rsa;
4256 nam.nam$b_rss = sizeof(rsa);
4257 nam.nam$l_esa = esa;
4258 nam.nam$b_ess = sizeof (esa);
4259 nam.nam$b_esl = nam.nam$b_rsl = 0;
4260
4261 xabdat = cc$rms_xabdat; /* To get creation date */
a3e9d8c9 4262 xabdat.xab$l_nxt = (void *) &xabfhc;
a5f75d66
AD
4263
4264 xabfhc = cc$rms_xabfhc; /* To get record length */
a3e9d8c9 4265 xabfhc.xab$l_nxt = (void *) &xabsum;
a5f75d66
AD
4266
4267 xabsum = cc$rms_xabsum; /* To get key and area information */
4268
4269 if (!((sts = sys$open(&fab_in)) & 1)) {
4270 set_vaxc_errno(sts);
4271 switch (sts) {
4272 case RMS$_FNF:
4273 case RMS$_DIR:
4274 set_errno(ENOENT); break;
4275 case RMS$_DEV:
4276 set_errno(ENODEV); break;
4277 case RMS$_SYN:
4278 set_errno(EINVAL); break;
4279 case RMS$_PRV:
4280 set_errno(EACCES); break;
4281 default:
4282 set_errno(EVMSERR);
4283 }
4284 return 0;
4285 }
4286
4287 fab_out = fab_in;
4288 fab_out.fab$w_ifi = 0;
4289 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
4290 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
4291 fab_out.fab$l_fop = FAB$M_SQO;
4292 fab_out.fab$l_fna = vmsout;
4293 fab_out.fab$b_fns = strlen(vmsout);
4294 fab_out.fab$l_dna = nam.nam$l_name;
4295 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
a3e9d8c9 4296
4297 if (preserve_dates == 0) { /* Act like DCL COPY */
4298 nam.nam$b_nop = NAM$M_SYNCHK;
4299 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
4300 if (!((sts = sys$parse(&fab_out)) & 1)) {
4301 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
4302 set_vaxc_errno(sts);
4303 return 0;
4304 }
4305 fab_out.fab$l_xab = (void *) &xabdat;
4306 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
4307 }
4308 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
4309 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
4310 preserve_dates =0; /* bitmask from this point forward */
4311
4312 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
a5f75d66
AD
4313 if (!((sts = sys$create(&fab_out)) & 1)) {
4314 set_vaxc_errno(sts);
4315 switch (sts) {
4316 case RMS$_DIR:
4317 set_errno(ENOENT); break;
4318 case RMS$_DEV:
4319 set_errno(ENODEV); break;
4320 case RMS$_SYN:
4321 set_errno(EINVAL); break;
4322 case RMS$_PRV:
4323 set_errno(EACCES); break;
4324 default:
4325 set_errno(EVMSERR);
4326 }
4327 return 0;
4328 }
4329 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
a3e9d8c9 4330 if (preserve_dates & 2) {
4331 /* sys$close() will process xabrdt, not xabdat */
4332 xabrdt = cc$rms_xabrdt;
b7ae7a0d 4333#ifndef __GNUC__
a3e9d8c9 4334 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
b7ae7a0d 4335#else
4336 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
4337 * is unsigned long[2], while DECC & VAXC use a struct */
4338 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
4339#endif
a3e9d8c9 4340 fab_out.fab$l_xab = (void *) &xabrdt;
4341 }
a5f75d66
AD
4342
4343 rab_in = cc$rms_rab;
4344 rab_in.rab$l_fab = &fab_in;
4345 rab_in.rab$l_rop = RAB$M_BIO;
4346 rab_in.rab$l_ubf = ubf;
4347 rab_in.rab$w_usz = sizeof ubf;
4348 if (!((sts = sys$connect(&rab_in)) & 1)) {
4349 sys$close(&fab_in); sys$close(&fab_out);
4350 set_errno(EVMSERR); set_vaxc_errno(sts);
4351 return 0;
4352 }
4353
4354 rab_out = cc$rms_rab;
4355 rab_out.rab$l_fab = &fab_out;
4356 rab_out.rab$l_rbf = ubf;
4357 if (!((sts = sys$connect(&rab_out)) & 1)) {
4358 sys$close(&fab_in); sys$close(&fab_out);
4359 set_errno(EVMSERR); set_vaxc_errno(sts);
4360 return 0;
4361 }
4362
4363 while ((sts = sys$read(&rab_in))) { /* always true */
4364 if (sts == RMS$_EOF) break;
4365 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
4366 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
4367 sys$close(&fab_in); sys$close(&fab_out);
4368 set_errno(EVMSERR); set_vaxc_errno(sts);
4369 return 0;
4370 }
4371 }
4372
4373 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
4374 sys$close(&fab_in); sys$close(&fab_out);
4375 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
4376 if (!(sts & 1)) {
4377 set_errno(EVMSERR); set_vaxc_errno(sts);
4378 return 0;
4379 }
4380
4381 return 1;
4382
4383} /* end of rmscopy() */
4384/*}}}*/
4385
4386
748a9306
LW
4387/*** The following glue provides 'hooks' to make some of the routines
4388 * from this file available from Perl. These routines are sufficiently
4389 * basic, and are required sufficiently early in the build process,
4390 * that's it's nice to have them available to miniperl as well as the
4391 * full Perl, so they're set up here instead of in an extension. The
4392 * Perl code which handles importation of these names into a given
4393 * package lives in [.VMS]Filespec.pm in @INC.
4394 */
4395
4396void
01b8edb6 4397rmsexpand_fromperl(CV *cv)
4398{
4399 dXSARGS;
bbce6d69 4400 char *fspec, *defspec = NULL, *rslt;
01b8edb6 4401
bbce6d69 4402 if (!items || items > 2)
4403 croak("Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
4404 fspec = SvPV(ST(0),na);
4405 if (!fspec || !*fspec) XSRETURN_UNDEF;
4406 if (items == 2) defspec = SvPV(ST(1),na);
b7ae7a0d 4407
bbce6d69 4408 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
4409 ST(0) = sv_newmortal();
4410 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
740ce14c 4411 XSRETURN(1);
01b8edb6 4412}
4413
4414void
748a9306
LW
4415vmsify_fromperl(CV *cv)
4416{
4417 dXSARGS;
4418 char *vmsified;
4419
4420 if (items != 1) croak("Usage: VMS::Filespec::vmsify(spec)");
4421 vmsified = do_tovmsspec(SvPV(ST(0),na),NULL,1);
4422 ST(0) = sv_newmortal();
4423 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
4424 XSRETURN(1);
4425}
4426
4427void
4428unixify_fromperl(CV *cv)
4429{
4430 dXSARGS;
4431 char *unixified;
4432
4433 if (items != 1) croak("Usage: VMS::Filespec::unixify(spec)");
4434 unixified = do_tounixspec(SvPV(ST(0),na),NULL,1);
4435 ST(0) = sv_newmortal();
4436 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
4437 XSRETURN(1);
4438}
4439
4440void
4441fileify_fromperl(CV *cv)
4442{
4443 dXSARGS;
4444 char *fileified;
4445
4446 if (items != 1) croak("Usage: VMS::Filespec::fileify(spec)");
4447 fileified = do_fileify_dirspec(SvPV(ST(0),na),NULL,1);
4448 ST(0) = sv_newmortal();
4449 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
4450 XSRETURN(1);
4451}
4452
4453void
4454pathify_fromperl(CV *cv)
4455{
4456 dXSARGS;
4457 char *pathified;
4458
4459 if (items != 1) croak("Usage: VMS::Filespec::pathify(spec)");
4460 pathified = do_pathify_dirspec(SvPV(ST(0),na),NULL,1);
4461 ST(0) = sv_newmortal();
4462 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
4463 XSRETURN(1);
4464}
4465
4466void
4467vmspath_fromperl(CV *cv)
4468{
4469 dXSARGS;
4470 char *vmspath;
4471
4472 if (items != 1) croak("Usage: VMS::Filespec::vmspath(spec)");
4473 vmspath = do_tovmspath(SvPV(ST(0),na),NULL,1);
4474 ST(0) = sv_newmortal();
4475 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
4476 XSRETURN(1);
4477}
4478
4479void
4480unixpath_fromperl(CV *cv)
4481{
4482 dXSARGS;
4483 char *unixpath;
4484
4485 if (items != 1) croak("Usage: VMS::Filespec::unixpath(spec)");
4486 unixpath = do_tounixpath(SvPV(ST(0),na),NULL,1);
4487 ST(0) = sv_newmortal();
4488 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
4489 XSRETURN(1);
4490}
4491
4492void
4493candelete_fromperl(CV *cv)
4494{
4495 dXSARGS;
a5f75d66
AD
4496 char fspec[NAM$C_MAXRSS+1], *fsp;
4497 SV *mysv;
4498 IO *io;
748a9306
LW
4499
4500 if (items != 1) croak("Usage: VMS::Filespec::candelete(spec)");
a5f75d66
AD
4501
4502 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
4503 if (SvTYPE(mysv) == SVt_PVGV) {
4504 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),fspec)) {
4505 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4506 ST(0) = &sv_no;
4507 XSRETURN(1);
4508 }
4509 fsp = fspec;
4510 }
4511 else {
4512 if (mysv != ST(0) || !(fsp = SvPV(mysv,na)) || !*fsp) {
4513 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4514 ST(0) = &sv_no;
4515 XSRETURN(1);
4516 }
4517 }
4518
54310121 4519 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
a5f75d66
AD
4520 XSRETURN(1);
4521}
4522
4523void
4524rmscopy_fromperl(CV *cv)
4525{
4526 dXSARGS;
4527 char inspec[NAM$C_MAXRSS+1], outspec[NAM$C_MAXRSS+1], *inp, *outp;
a3e9d8c9 4528 int date_flag;
a5f75d66
AD
4529 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
4530 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4531 unsigned long int sts;
4532 SV *mysv;
4533 IO *io;
4534
a3e9d8c9 4535 if (items < 2 || items > 3)
4536 croak("Usage: File::Copy::rmscopy(from,to[,date_flag])");
a5f75d66
AD
4537
4538 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
4539 if (SvTYPE(mysv) == SVt_PVGV) {
4540 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),inspec)) {
4541 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4542 ST(0) = &sv_no;
4543 XSRETURN(1);
4544 }
4545 inp = inspec;
4546 }
4547 else {
4548 if (mysv != ST(0) || !(inp = SvPV(mysv,na)) || !*inp) {
4549 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4550 ST(0) = &sv_no;
4551 XSRETURN(1);
4552 }
4553 }
4554 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
4555 if (SvTYPE(mysv) == SVt_PVGV) {
4556 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),outspec)) {
4557 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4558 ST(0) = &sv_no;
4559 XSRETURN(1);
4560 }
4561 outp = outspec;
4562 }
4563 else {
4564 if (mysv != ST(1) || !(outp = SvPV(mysv,na)) || !*outp) {
4565 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4566 ST(0) = &sv_no;
4567 XSRETURN(1);
4568 }
4569 }
a3e9d8c9 4570 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
a5f75d66 4571
54310121 4572 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
748a9306
LW
4573 XSRETURN(1);
4574}
4575
4576void
4577init_os_extras()
4578{
4579 char* file = __FILE__;
4580
740ce14c 4581 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
a5f75d66
AD
4582 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
4583 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
4584 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
4585 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
4586 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
4587 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
4588 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
4589 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
17f28c40
CB
4590
4591#ifdef PRIME_ENV_AT_STARTUP
4592 prime_env_iter();
4593#endif
4594
748a9306
LW
4595 return;
4596}
4597
4598/* End of vms.c */