3 * VMS-specific routines for perl5
5 * Last revised: 14-Oct-1996 by Charles Bailey bailey@genetics.upenn.edu
14 #include <climsgdef.h>
23 #include <lib$routines.h>
36 /* Older versions of ssdef.h don't have these */
37 #ifndef SS$_INVFILFOROP
38 # define SS$_INVFILFOROP 3930
40 #ifndef SS$_NOSUCHOBJECT
41 # define SS$_NOSUCHOBJECT 2696
44 /* Don't intercept calls to vfork, since my_vfork below needs to
45 * get to the underlying CRTL routine. */
46 #define __DONT_MASK_VFORK
51 /* gcc's header files don't #define direct access macros
52 * corresponding to VAXC's variant structs */
54 # define uic$v_format uic$r_uic_form.uic$v_format
55 # define uic$v_group uic$r_uic_form.uic$v_group
56 # define uic$v_member uic$r_uic_form.uic$v_member
57 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
58 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
59 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
60 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
65 unsigned short int buflen;
66 unsigned short int itmcode;
68 unsigned short int *retlen;
71 static char *__mystrtolower(char *str)
73 if (str) for (; *str; ++str) *str= tolower(*str);
78 my_trnlnm(char *lnm, char *eqv, unsigned long int idx)
80 static char __my_trnlnm_eqv[LNM$C_NAMLENGTH+1];
81 unsigned short int eqvlen;
82 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
83 $DESCRIPTOR(tabdsc,"LNM$FILE_DEV");
84 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
85 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
86 {LNM$C_NAMLENGTH, LNM$_STRING, 0, &eqvlen},
89 if (!lnm || idx > LNM$_MAX_INDEX) {
90 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
92 if (!eqv) eqv = __my_trnlnm_eqv;
93 lnmlst[1].bufadr = (void *)eqv;
94 lnmdsc.dsc$a_pointer = lnm;
95 lnmdsc.dsc$w_length = strlen(lnm);
96 retsts = sys$trnlnm(&attr,&tabdsc,&lnmdsc,0,lnmlst);
97 if (retsts == SS$_NOLOGNAM || retsts == SS$_IVLOGNAM) {
98 set_vaxc_errno(retsts); set_errno(EINVAL); return 0;
100 else if (retsts & 1) {
104 _ckvmssts(retsts); /* Must be an error */
105 return 0; /* Not reached, assuming _ckvmssts() bails out */
107 } /* end of my_trnlnm */
110 * Translate a logical name. Substitute for CRTL getenv() to avoid
111 * memory leak, and to keep my_getenv() and my_setenv() in the same
112 * domain (mostly - my_getenv() need not return a translation from
113 * the process logical name table)
115 * Note: Uses static buffer -- not thread-safe!
117 /*{{{ char *my_getenv(char *lnm)*/
121 static char __my_getenv_eqv[LNM$C_NAMLENGTH+1];
122 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2;
123 unsigned long int idx = 0;
126 for (cp1 = lnm, cp2= uplnm; *cp1; cp1++, cp2++) *cp2 = _toupper(*cp1);
128 if (cp1 - lnm == 7 && !strncmp(uplnm,"DEFAULT",7)) {
129 getcwd(__my_getenv_eqv,sizeof __my_getenv_eqv);
130 return __my_getenv_eqv;
133 if ((cp2 = strchr(uplnm,';')) != NULL) {
135 idx = strtoul(cp2+1,NULL,0);
137 trnsuccess = my_trnlnm(uplnm,__my_getenv_eqv,idx);
138 /* If we had a translation index, we're only interested in lnms */
139 if (!trnsuccess && cp2 != NULL) return Nullch;
140 if (trnsuccess) return __my_getenv_eqv;
142 unsigned long int retsts;
143 struct dsc$descriptor_s symdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
144 valdsc = {sizeof __my_getenv_eqv,DSC$K_DTYPE_T,
145 DSC$K_CLASS_S, __my_getenv_eqv};
146 symdsc.dsc$w_length = cp1 - lnm;
147 symdsc.dsc$a_pointer = uplnm;
148 retsts = lib$get_symbol(&symdsc,&valdsc,&(valdsc.dsc$w_length),0);
149 if (retsts == LIB$_INVSYMNAM) return Nullch;
150 if (retsts != LIB$_NOSUCHSYM) {
151 /* We want to return only logical names or CRTL Unix emulations */
152 if (retsts & 1) return Nullch;
155 /* Try for CRTL emulation of a Unix/POSIX name */
156 else return getenv(uplnm);
161 } /* end of my_getenv() */
164 /*{{{ void prime_env_iter() */
167 /* Fill the %ENV associative array with all logical names we can
168 * find, in preparation for iterating over it.
171 static int primed = 0; /* XXX Not thread-safe!!! */
172 HV *envhv = GvHVn(envgv);
174 char eqv[LNM$C_NAMLENGTH+1],*start,*end;
176 SV *oldrs, *linesv, *eqvsv;
179 /* Perform a dummy fetch as an lval to insure that the hash table is
180 * set up. Otherwise, the hv_store() will turn into a nullop */
181 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
182 /* Also, set up the four "special" keys that the CRTL defines,
183 * whether or not underlying logical names exist. */
184 (void) hv_fetch(envhv,"HOME",4,TRUE);
185 (void) hv_fetch(envhv,"TERM",4,TRUE);
186 (void) hv_fetch(envhv,"PATH",4,TRUE);
187 (void) hv_fetch(envhv,"USER",4,TRUE);
189 /* Now, go get the logical names */
190 if ((sholog = my_popen("$ Show Logical *","r")) == Nullfp)
191 _ckvmssts(vaxc$errno);
192 /* We use Perl's sv_gets to read from the pipe, since my_popen is
193 * tied to Perl's I/O layer, so it may not return a simple FILE * */
195 rs = newSVpv("\n",1);
196 linesv = newSVpv("",0);
198 if ((start = sv_gets(linesv,sholog,0)) == Nullch) {
200 SvREFCNT_dec(linesv); SvREFCNT_dec(rs); rs = oldrs;
204 while (*start != '"' && *start != '=' && *start) start++;
205 if (*start != '"') continue;
206 for (end = ++start; *end && *end != '"'; end++) ;
207 if (*end) *end = '\0';
209 if ((eqvlen = my_trnlnm(start,eqv,0)) == 0) {
210 if (vaxc$errno == SS$_NOLOGNAM || vaxc$errno == SS$_IVLOGNAM) {
212 warn("Ill-formed logical name |%s| in prime_env_iter",start);
215 else _ckvmssts(vaxc$errno);
218 eqvsv = newSVpv(eqv,eqvlen);
219 hv_store(envhv,start,(end ? end - start : strlen(start)),eqvsv,0);
222 } /* end of prime_env_iter */
226 /*{{{ void my_setenv(char *lnm, char *eqv)*/
228 my_setenv(char *lnm,char *eqv)
229 /* Define a supervisor-mode logical name in the process table.
230 * In the future we'll add tables, attribs, and acmodes,
231 * probably through a different call.
234 char uplnm[LNM$C_NAMLENGTH], *cp1, *cp2;
235 unsigned long int retsts, usermode = PSL$C_USER;
236 $DESCRIPTOR(tabdsc,"LNM$PROCESS");
237 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
238 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
240 for(cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) *cp2 = _toupper(*cp1);
241 lnmdsc.dsc$w_length = cp1 - lnm;
243 if (!eqv || !*eqv) { /* we're deleting a logical name */
244 retsts = sys$dellnm(&tabdsc,&lnmdsc,&usermode); /* try user mode first */
245 if (retsts == SS$_IVLOGNAM) return;
246 if (retsts != SS$_NOLOGNAM) _ckvmssts(retsts);
248 retsts = lib$delete_logical(&lnmdsc,&tabdsc); /* then supervisor mode */
249 if (retsts != SS$_NOLOGNAM) _ckvmssts(retsts);
253 eqvdsc.dsc$w_length = strlen(eqv);
254 eqvdsc.dsc$a_pointer = eqv;
256 _ckvmssts(lib$set_logical(&lnmdsc,&eqvdsc,&tabdsc,0,0));
259 } /* end of my_setenv() */
263 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
264 /* my_crypt - VMS password hashing
265 * my_crypt() provides an interface compatible with the Unix crypt()
266 * C library function, and uses sys$hash_password() to perform VMS
267 * password hashing. The quadword hashed password value is returned
268 * as a NUL-terminated 8 character string. my_crypt() does not change
269 * the case of its string arguments; in order to match the behavior
270 * of LOGINOUT et al., alphabetic characters in both arguments must
271 * be upcased by the caller.
274 my_crypt(const char *textpasswd, const char *usrname)
276 # ifndef UAI$C_PREFERRED_ALGORITHM
277 # define UAI$C_PREFERRED_ALGORITHM 127
279 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
280 unsigned short int salt = 0;
281 unsigned long int sts;
283 unsigned short int dsc$w_length;
284 unsigned char dsc$b_type;
285 unsigned char dsc$b_class;
286 const char * dsc$a_pointer;
287 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
288 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
289 struct itmlst_3 uailst[3] = {
290 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
291 { sizeof salt, UAI$_SALT, &salt, 0},
292 { 0, 0, NULL, NULL}};
295 usrdsc.dsc$w_length = strlen(usrname);
296 usrdsc.dsc$a_pointer = usrname;
297 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
304 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
310 if (sts != RMS$_RNF) return NULL;
313 txtdsc.dsc$w_length = strlen(textpasswd);
314 txtdsc.dsc$a_pointer = textpasswd;
315 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
316 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
319 return (char *) hash;
321 } /* end of my_crypt() */
325 static char *do_rmsexpand(char *, char *, int, char *, unsigned);
326 static char *do_fileify_dirspec(char *, char *, int);
327 static char *do_tovmsspec(char *, char *, int);
329 /*{{{int do_rmdir(char *name)*/
333 char dirfile[NAM$C_MAXRSS+1];
337 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
338 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
339 else retval = kill_file(dirfile);
342 } /* end of do_rmdir */
346 * Delete any file to which user has control access, regardless of whether
347 * delete access is explicitly allowed.
348 * Limitations: User must have write access to parent directory.
349 * Does not block signals or ASTs; if interrupted in midstream
350 * may leave file with an altered ACL.
353 /*{{{int kill_file(char *name)*/
355 kill_file(char *name)
357 char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
358 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
359 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
360 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
362 unsigned char myace$b_length;
363 unsigned char myace$b_type;
364 unsigned short int myace$w_flags;
365 unsigned long int myace$l_access;
366 unsigned long int myace$l_ident;
367 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
368 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
369 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
371 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
372 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
373 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
374 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
375 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
376 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
378 /* Expand the input spec using RMS, since the CRTL remove() and
379 * system services won't do this by themselves, so we may miss
380 * a file "hiding" behind a logical name or search list. */
381 if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
382 if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
383 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
384 /* If not, can changing protections help? */
385 if (vaxc$errno != RMS$_PRV) return -1;
387 /* No, so we get our own UIC to use as a rights identifier,
388 * and the insert an ACE at the head of the ACL which allows us
389 * to delete the file.
391 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
392 fildsc.dsc$w_length = strlen(rspec);
393 fildsc.dsc$a_pointer = rspec;
395 newace.myace$l_ident = oldace.myace$l_ident;
396 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
401 case SS$_NOSUCHOBJECT:
402 set_errno(ENOENT); break;
404 set_errno(ENODEV); break;
406 case SS$_INVFILFOROP:
407 set_errno(EINVAL); break;
409 set_errno(EACCES); break;
413 set_vaxc_errno(aclsts);
416 /* Grab any existing ACEs with this identifier in case we fail */
417 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
418 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
419 || fndsts == SS$_NOMOREACE ) {
420 /* Add the new ACE . . . */
421 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
423 if ((rmsts = remove(name))) {
424 /* We blew it - dir with files in it, no write priv for
425 * parent directory, etc. Put things back the way they were. */
426 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
429 addlst[0].bufadr = &oldace;
430 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
437 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
438 /* We just deleted it, so of course it's not there. Some versions of
439 * VMS seem to return success on the unlock operation anyhow (after all
440 * the unlock is successful), but others don't.
442 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
443 if (aclsts & 1) aclsts = fndsts;
446 set_vaxc_errno(aclsts);
452 } /* end of kill_file() */
455 /* my_utime - update modification time of a file
456 * calling sequence is identical to POSIX utime(), but under
457 * VMS only the modification time is changed; ODS-2 does not
458 * maintain access times. Restrictions differ from the POSIX
459 * definition in that the time can be changed as long as the
460 * caller has permission to execute the necessary IO$_MODIFY $QIO;
461 * no separate checks are made to insure that the caller is the
462 * owner of the file or has special privs enabled.
463 * Code here is based on Joe Meadows' FILE utility.
466 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
467 * to VMS epoch (01-JAN-1858 00:00:00.00)
468 * in 100 ns intervals.
470 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
472 /*{{{int my_utime(char *path, struct utimbuf *utimes)*/
473 int my_utime(char *file, struct utimbuf *utimes)
476 long int bintime[2], len = 2, lowbit, unixtime,
477 secscale = 10000000; /* seconds --> 100 ns intervals */
478 unsigned long int chan, iosb[2], retsts;
479 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
480 struct FAB myfab = cc$rms_fab;
481 struct NAM mynam = cc$rms_nam;
482 #if defined (__DECC) && defined (__VAX)
483 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
484 * at least through VMS V6.1, which causes a type-conversion warning.
486 # pragma message save
487 # pragma message disable cvtdiftypes
489 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
491 #if defined (__DECC) && defined (__VAX)
492 /* This should be right after the declaration of myatr, but due
493 * to a bug in VAX DEC C, this takes effect a statement early.
495 # pragma message restore
497 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
498 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
499 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
501 if (file == NULL || *file == '\0') {
503 set_vaxc_errno(LIB$_INVARG);
506 if (do_tovmsspec(file,vmsspec,0) == NULL) return -1;
508 if (utimes != NULL) {
509 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
510 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
511 * Since time_t is unsigned long int, and lib$emul takes a signed long int
512 * as input, we force the sign bit to be clear by shifting unixtime right
513 * one bit, then multiplying by an extra factor of 2 in lib$emul().
515 lowbit = (utimes->modtime & 1) ? secscale : 0;
516 unixtime = (long int) utimes->modtime;
517 unixtime >> 1; secscale << 1;
518 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
521 set_vaxc_errno(retsts);
524 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
527 set_vaxc_errno(retsts);
532 /* Just get the current time in VMS format directly */
533 retsts = sys$gettim(bintime);
536 set_vaxc_errno(retsts);
541 myfab.fab$l_fna = vmsspec;
542 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
543 myfab.fab$l_nam = &mynam;
544 mynam.nam$l_esa = esa;
545 mynam.nam$b_ess = (unsigned char) sizeof esa;
546 mynam.nam$l_rsa = rsa;
547 mynam.nam$b_rss = (unsigned char) sizeof rsa;
549 /* Look for the file to be affected, letting RMS parse the file
550 * specification for us as well. I have set errno using only
551 * values documented in the utime() man page for VMS POSIX.
553 retsts = sys$parse(&myfab,0,0);
555 set_vaxc_errno(retsts);
556 if (retsts == RMS$_PRV) set_errno(EACCES);
557 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
558 else set_errno(EVMSERR);
561 retsts = sys$search(&myfab,0,0);
563 set_vaxc_errno(retsts);
564 if (retsts == RMS$_PRV) set_errno(EACCES);
565 else if (retsts == RMS$_FNF) set_errno(ENOENT);
566 else set_errno(EVMSERR);
570 devdsc.dsc$w_length = mynam.nam$b_dev;
571 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
573 retsts = sys$assign(&devdsc,&chan,0,0);
575 set_vaxc_errno(retsts);
576 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
577 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
578 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
579 else set_errno(EVMSERR);
583 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
584 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
586 memset((void *) &myfib, 0, sizeof myfib);
588 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
589 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
590 /* This prevents the revision time of the file being reset to the current
591 * time as a result of our IO$_MODIFY $QIO. */
592 myfib.fib$l_acctl = FIB$M_NORECORD;
594 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
595 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
596 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
598 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
599 _ckvmssts(sys$dassgn(chan));
600 if (retsts & 1) retsts = iosb[0];
602 set_vaxc_errno(retsts);
603 if (retsts == SS$_NOPRIV) set_errno(EACCES);
604 else set_errno(EVMSERR);
609 } /* end of my_utime() */
613 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
615 static unsigned long int mbxbufsiz;
616 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
620 * Get the SYSGEN parameter MAXBUF, and the smaller of it and the
621 * preprocessor consant BUFSIZ from stdio.h as the size of the
624 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
625 if (mbxbufsiz > BUFSIZ) mbxbufsiz = BUFSIZ;
627 _ckvmssts(sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
629 _ckvmssts(lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
630 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
632 } /* end of create_mbx() */
634 /*{{{ my_popen and my_pclose*/
637 struct pipe_details *next;
638 PerlIO *fp; /* stdio file pointer to pipe mailbox */
639 int pid; /* PID of subprocess */
640 int mode; /* == 'r' if pipe open for reading */
641 int done; /* subprocess has completed */
642 unsigned long int completion; /* termination status of subprocess */
645 struct exit_control_block
647 struct exit_control_block *flink;
648 unsigned long int (*exit_routine)();
649 unsigned long int arg_count;
650 unsigned long int *status_address;
651 unsigned long int exit_status;
654 static struct pipe_details *open_pipes = NULL;
655 static $DESCRIPTOR(nl_desc, "NL:");
656 static int waitpid_asleep = 0;
658 static unsigned long int
661 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT, sts;
663 while (open_pipes != NULL) {
664 if (!open_pipes->done) { /* Tap them gently on the shoulder . . .*/
665 _ckvmssts(sys$forcex(&open_pipes->pid,0,&abort));
668 if (!open_pipes->done) /* We tried to be nice . . . */
669 _ckvmssts(sys$delprc(&open_pipes->pid,0));
670 if (!((sts = my_pclose(open_pipes->fp))&1)) retsts = sts;
675 static struct exit_control_block pipe_exitblock =
676 {(struct exit_control_block *) 0,
677 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
681 popen_completion_ast(struct pipe_details *thispipe)
683 thispipe->done = TRUE;
684 if (waitpid_asleep) {
690 /*{{{ FILE *my_popen(char *cmd, char *mode)*/
692 my_popen(char *cmd, char *mode)
694 static int handler_set_up = FALSE;
696 unsigned short int chan;
697 unsigned long int flags=1; /* nowait - gnu c doesn't allow &1 */
698 struct pipe_details *info;
699 struct dsc$descriptor_s namdsc = {sizeof mbxname, DSC$K_DTYPE_T,
700 DSC$K_CLASS_S, mbxname},
701 cmddsc = {0, DSC$K_DTYPE_T,
705 cmddsc.dsc$w_length=strlen(cmd);
706 cmddsc.dsc$a_pointer=cmd;
707 if (cmddsc.dsc$w_length > 255) {
708 set_errno(E2BIG); set_vaxc_errno(CLI$_BUFOVF);
712 New(7001,info,1,struct pipe_details);
715 create_mbx(&chan,&namdsc);
717 /* open a FILE* onto it */
718 info->fp = PerlIO_open(mbxname, mode);
720 /* give up other channel onto it */
721 _ckvmssts(sys$dassgn(chan));
731 _ckvmssts(lib$spawn(&cmddsc, &nl_desc, &namdsc, &flags,
732 0 /* name */, &info->pid, &info->completion,
733 0, popen_completion_ast,info,0,0,0));
736 _ckvmssts(lib$spawn(&cmddsc, &namdsc, 0 /* sys$output */, &flags,
737 0 /* name */, &info->pid, &info->completion,
738 0, popen_completion_ast,info,0,0,0));
741 if (!handler_set_up) {
742 _ckvmssts(sys$dclexh(&pipe_exitblock));
743 handler_set_up = TRUE;
745 info->next=open_pipes; /* prepend to list */
748 forkprocess = info->pid;
753 /*{{{ I32 my_pclose(FILE *fp)*/
754 I32 my_pclose(FILE *fp)
756 struct pipe_details *info, *last = NULL;
757 unsigned long int retsts;
759 for (info = open_pipes; info != NULL; last = info, info = info->next)
760 if (info->fp == fp) break;
763 /* get here => no such pipe open */
764 croak("No such pipe open");
766 /* If we were writing to a subprocess, insure that someone reading from
767 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
768 * produce an EOF record in the mailbox. */
769 if (info->mode != 'r') {
770 char devnam[NAM$C_MAXRSS+1], *cp;
771 unsigned long int chan, iosb[2], retsts, retsts2;
772 struct dsc$descriptor devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, devnam};
774 if (fgetname(info->fp,devnam)) {
775 /* It oughta be a mailbox, so fgetname should give just the device
776 * name, but just in case . . . */
777 if ((cp = strrchr(devnam,':')) != NULL) *(cp+1) = '\0';
778 devdsc.dsc$w_length = strlen(devnam);
779 _ckvmssts(sys$assign(&devdsc,&chan,0,0));
780 retsts = sys$qiow(0,chan,IO$_WRITEOF,iosb,0,0,0,0,0,0,0,0);
781 if (retsts & 1) retsts = iosb[0];
782 retsts2 = sys$dassgn(chan); /* Be sure to deassign the channel */
783 if (retsts & 1) retsts = retsts2;
786 else _ckvmssts(vaxc$errno); /* Should never happen */
788 PerlIO_close(info->fp);
790 if (info->done) retsts = info->completion;
791 else waitpid(info->pid,(int *) &retsts,0);
793 /* remove from list of open pipes */
794 if (last) last->next = info->next;
795 else open_pipes = info->next;
800 } /* end of my_pclose() */
802 /* sort-of waitpid; use only with popen() */
803 /*{{{unsigned long int waitpid(unsigned long int pid, int *statusp, int flags)*/
805 waitpid(unsigned long int pid, int *statusp, int flags)
807 struct pipe_details *info;
809 for (info = open_pipes; info != NULL; info = info->next)
810 if (info->pid == pid) break;
812 if (info != NULL) { /* we know about this child */
813 while (!info->done) {
818 *statusp = info->completion;
821 else { /* we haven't heard of this child */
822 $DESCRIPTOR(intdsc,"0 00:00:01");
823 unsigned long int ownercode = JPI$_OWNER, ownerpid, mypid;
824 unsigned long int interval[2],sts;
827 _ckvmssts(lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0));
828 _ckvmssts(lib$getjpi(&ownercode,0,0,&mypid,0,0));
829 if (ownerpid != mypid)
830 warn("pid %d not a child",pid);
833 _ckvmssts(sys$bintim(&intdsc,interval));
834 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
835 _ckvmssts(sys$schdwk(0,0,interval,0));
836 _ckvmssts(sys$hiber());
840 /* There's no easy way to find the termination status a child we're
841 * not aware of beforehand. If we're really interested in the future,
842 * we can go looking for a termination mailbox, or chase after the
843 * accounting record for the process.
849 } /* end of waitpid() */
854 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
856 my_gconvert(double val, int ndig, int trail, char *buf)
858 static char __gcvtbuf[DBL_DIG+1];
861 loc = buf ? buf : __gcvtbuf;
863 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
865 sprintf(loc,"%.*g",ndig,val);
871 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
872 return gcvt(val,ndig,loc);
875 loc[0] = '0'; loc[1] = '\0';
883 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
884 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
885 * to expand file specification. Allows for a single default file
886 * specification and a simple mask of options. If outbuf is non-NULL,
887 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
888 * the resultant file specification is placed. If outbuf is NULL, the
889 * resultant file specification is placed into a static buffer.
890 * The third argument, if non-NULL, is taken to be a default file
891 * specification string. The fourth argument is unused at present.
892 * rmesexpand() returns the address of the resultant string if
893 * successful, and NULL on error.
896 do_rmsexpand(char *filespec, char *outbuf, int ts, char *defspec, unsigned opts)
898 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
899 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
900 struct FAB myfab = cc$rms_fab;
901 struct NAM mynam = cc$rms_nam;
903 unsigned long int retsts, haslower = 0;
905 if (!filespec || !*filespec) {
906 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
910 if (ts) out = New(7019,outbuf,NAM$C_MAXRSS+1,char);
911 else outbuf = __rmsexpand_retbuf;
914 myfab.fab$l_fna = filespec;
915 myfab.fab$b_fns = strlen(filespec);
916 myfab.fab$l_nam = &mynam;
918 if (defspec && *defspec) {
919 myfab.fab$l_dna = defspec;
920 myfab.fab$b_dns = strlen(defspec);
923 mynam.nam$l_esa = esa;
924 mynam.nam$b_ess = sizeof esa;
925 mynam.nam$l_rsa = outbuf;
926 mynam.nam$b_rss = NAM$C_MAXRSS;
928 retsts = sys$parse(&myfab,0,0);
930 if (retsts == RMS$_DNF || retsts == RMS$_DIR ||
931 retsts == RMS$_DEV || retsts == RMS$_DEV) {
932 mynam.nam$b_nop |= NAM$M_SYNCHK;
933 retsts = sys$parse(&myfab,0,0);
934 if (retsts & 1) goto expanded;
936 if (out) Safefree(out);
937 set_vaxc_errno(retsts);
938 if (retsts == RMS$_PRV) set_errno(EACCES);
939 else if (retsts == RMS$_DEV) set_errno(ENODEV);
940 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
941 else set_errno(EVMSERR);
944 retsts = sys$search(&myfab,0,0);
945 if (!(retsts & 1) && retsts != RMS$_FNF) {
946 if (out) Safefree(out);
947 set_vaxc_errno(retsts);
948 if (retsts == RMS$_PRV) set_errno(EACCES);
949 else set_errno(EVMSERR);
953 /* If the input filespec contained any lowercase characters,
954 * downcase the result for compatibility with Unix-minded code. */
956 for (out = myfab.fab$l_fna; *out; out++)
957 if (islower(*out)) { haslower = 1; break; }
958 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
959 else { out = esa; speclen = mynam.nam$b_esl; }
960 if (!(mynam.nam$l_fnb & NAM$M_EXP_VER) &&
961 (!defspec || !*defspec || !strchr(myfab.fab$l_dna,';')))
962 speclen = mynam.nam$l_ver - out;
963 /* If we just had a directory spec on input, $PARSE "helpfully"
964 * adds an empty name and type for us */
965 if (mynam.nam$l_name == mynam.nam$l_type &&
966 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
967 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
968 speclen = mynam.nam$l_name - out;
970 if (haslower) __mystrtolower(out);
972 /* Have we been working with an expanded, but not resultant, spec? */
973 if (!mynam.nam$b_rsl) strcpy(outbuf,esa);
977 /* External entry points */
978 char *rmsexpand(char *spec, char *buf, char *def, unsigned opt)
979 { return do_rmsexpand(spec,buf,0,def,opt); }
980 char *rmsexpand_ts(char *spec, char *buf, char *def, unsigned opt)
981 { return do_rmsexpand(spec,buf,1,def,opt); }
985 ** The following routines are provided to make life easier when
986 ** converting among VMS-style and Unix-style directory specifications.
987 ** All will take input specifications in either VMS or Unix syntax. On
988 ** failure, all return NULL. If successful, the routines listed below
989 ** return a pointer to a buffer containing the appropriately
990 ** reformatted spec (and, therefore, subsequent calls to that routine
991 ** will clobber the result), while the routines of the same names with
992 ** a _ts suffix appended will return a pointer to a mallocd string
993 ** containing the appropriately reformatted spec.
994 ** In all cases, only explicit syntax is altered; no check is made that
995 ** the resulting string is valid or that the directory in question
998 ** fileify_dirspec() - convert a directory spec into the name of the
999 ** directory file (i.e. what you can stat() to see if it's a dir).
1000 ** The style (VMS or Unix) of the result is the same as the style
1001 ** of the parameter passed in.
1002 ** pathify_dirspec() - convert a directory spec into a path (i.e.
1003 ** what you prepend to a filename to indicate what directory it's in).
1004 ** The style (VMS or Unix) of the result is the same as the style
1005 ** of the parameter passed in.
1006 ** tounixpath() - convert a directory spec into a Unix-style path.
1007 ** tovmspath() - convert a directory spec into a VMS-style path.
1008 ** tounixspec() - convert any file spec into a Unix-style file spec.
1009 ** tovmsspec() - convert any file spec into a VMS-style spec.
1011 ** Copyright 1996 by Charles Bailey <bailey@genetics.upenn.edu>
1012 ** Permission is given to distribute this code as part of the Perl
1013 ** standard distribution under the terms of the GNU General Public
1014 ** License or the Perl Artistic License. Copies of each may be
1015 ** found in the Perl standard distribution.
1018 static char *do_tounixspec(char *, char *, int);
1020 /*{{{ char *fileify_dirspec[_ts](char *path, char *buf)*/
1021 static char *do_fileify_dirspec(char *dir,char *buf,int ts)
1023 static char __fileify_retbuf[NAM$C_MAXRSS+1];
1024 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
1025 char *retspec, *cp1, *cp2, *lastdir;
1026 char trndir[NAM$C_MAXRSS+1], vmsdir[NAM$C_MAXRSS+1];
1028 if (!dir || !*dir) {
1029 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
1031 dirlen = strlen(dir);
1032 if (dir[dirlen-1] == '/') --dirlen;
1035 set_vaxc_errno(RMS$_DIR);
1038 if (!strpbrk(dir+1,"/]>:")) {
1039 strcpy(trndir,*dir == '/' ? dir + 1: dir);
1040 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) ;
1042 dirlen = strlen(dir);
1045 strncpy(trndir,dir,dirlen);
1046 trndir[dirlen] = '\0';
1049 /* If we were handed a rooted logical name or spec, treat it like a
1050 * simple directory, so that
1051 * $ Define myroot dev:[dir.]
1052 * ... do_fileify_dirspec("myroot",buf,1) ...
1053 * does something useful.
1055 if (!strcmp(dir+dirlen-2,".]")) {
1056 dir[--dirlen] = '\0';
1057 dir[dirlen-1] = ']';
1060 if ((cp1 = strrchr(dir,']')) != NULL || (cp1 = strrchr(dir,'>')) != NULL) {
1061 /* If we've got an explicit filename, we can just shuffle the string. */
1062 if (*(cp1+1)) hasfilename = 1;
1063 /* Similarly, we can just back up a level if we've got multiple levels
1064 of explicit directories in a VMS spec which ends with directories. */
1066 for (cp2 = cp1; cp2 > dir; cp2--) {
1068 *cp2 = *cp1; *cp1 = '\0';
1072 if (*cp2 == '[' || *cp2 == '<') break;
1077 if (hasfilename || !strpbrk(dir,"]:>")) { /* Unix-style path or filename */
1078 if (dir[0] == '.') {
1079 if (dir[1] == '\0' || (dir[1] == '/' && dir[2] == '\0'))
1080 return do_fileify_dirspec("[]",buf,ts);
1081 else if (dir[1] == '.' &&
1082 (dir[2] == '\0' || (dir[2] == '/' && dir[3] == '\0')))
1083 return do_fileify_dirspec("[-]",buf,ts);
1085 if (dir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
1086 dirlen -= 1; /* to last element */
1087 lastdir = strrchr(dir,'/');
1089 else if ((cp1 = strstr(dir,"/.")) != NULL) {
1090 /* If we have "/." or "/..", VMSify it and let the VMS code
1091 * below expand it, rather than repeating the code to handle
1092 * relative components of a filespec here */
1094 if (*(cp1+2) == '.') cp1++;
1095 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
1096 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
1097 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
1098 return do_tounixspec(trndir,buf,ts);
1101 } while ((cp1 = strstr(cp1,"/.")) != NULL);
1104 if ( !(lastdir = cp1 = strrchr(dir,'/')) &&
1105 !(lastdir = cp1 = strrchr(dir,']')) &&
1106 !(lastdir = cp1 = strrchr(dir,'>'))) cp1 = dir;
1107 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
1109 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
1110 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
1111 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
1112 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
1113 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
1114 (ver || *cp3)))))) {
1116 set_vaxc_errno(RMS$_DIR);
1122 /* If we lead off with a device or rooted logical, add the MFD
1123 if we're specifying a top-level directory. */
1124 if (lastdir && *dir == '/') {
1126 for (cp1 = lastdir - 1; cp1 > dir; cp1--) {
1133 retlen = dirlen + (addmfd ? 13 : 6);
1134 if (buf) retspec = buf;
1135 else if (ts) New(7009,retspec,retlen+1,char);
1136 else retspec = __fileify_retbuf;
1138 dirlen = lastdir - dir;
1139 memcpy(retspec,dir,dirlen);
1140 strcpy(&retspec[dirlen],"/000000");
1141 strcpy(&retspec[dirlen+7],lastdir);
1144 memcpy(retspec,dir,dirlen);
1145 retspec[dirlen] = '\0';
1147 /* We've picked up everything up to the directory file name.
1148 Now just add the type and version, and we're set. */
1149 strcat(retspec,".dir;1");
1152 else { /* VMS-style directory spec */
1153 char esa[NAM$C_MAXRSS+1], term, *cp;
1154 unsigned long int sts, cmplen, haslower = 0;
1155 struct FAB dirfab = cc$rms_fab;
1156 struct NAM savnam, dirnam = cc$rms_nam;
1158 dirfab.fab$b_fns = strlen(dir);
1159 dirfab.fab$l_fna = dir;
1160 dirfab.fab$l_nam = &dirnam;
1161 dirfab.fab$l_dna = ".DIR;1";
1162 dirfab.fab$b_dns = 6;
1163 dirnam.nam$b_ess = NAM$C_MAXRSS;
1164 dirnam.nam$l_esa = esa;
1166 for (cp = dir; *cp; cp++)
1167 if (islower(*cp)) { haslower = 1; break; }
1168 if (!((sts = sys$parse(&dirfab))&1)) {
1169 if (dirfab.fab$l_sts == RMS$_DIR) {
1170 dirnam.nam$b_nop |= NAM$M_SYNCHK;
1171 sts = sys$parse(&dirfab) & 1;
1175 set_vaxc_errno(dirfab.fab$l_sts);
1181 if (sys$search(&dirfab)&1) { /* Does the file really exist? */
1182 /* Yes; fake the fnb bits so we'll check type below */
1183 dirnam.nam$l_fnb |= NAM$M_EXP_TYPE | NAM$M_EXP_VER;
1186 if (dirfab.fab$l_sts != RMS$_FNF) {
1188 set_vaxc_errno(dirfab.fab$l_sts);
1191 dirnam = savnam; /* No; just work with potential name */
1194 if (!(dirnam.nam$l_fnb & (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
1195 cp1 = strchr(esa,']');
1196 if (!cp1) cp1 = strchr(esa,'>');
1197 if (cp1) { /* Should always be true */
1198 dirnam.nam$b_esl -= cp1 - esa - 1;
1199 memcpy(esa,cp1 + 1,dirnam.nam$b_esl);
1202 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
1203 /* Yep; check version while we're at it, if it's there. */
1204 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
1205 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
1206 /* Something other than .DIR[;1]. Bzzt. */
1208 set_vaxc_errno(RMS$_DIR);
1212 esa[dirnam.nam$b_esl] = '\0';
1213 if (dirnam.nam$l_fnb & NAM$M_EXP_NAME) {
1214 /* They provided at least the name; we added the type, if necessary, */
1215 if (buf) retspec = buf; /* in sys$parse() */
1216 else if (ts) New(7011,retspec,dirnam.nam$b_esl+1,char);
1217 else retspec = __fileify_retbuf;
1218 strcpy(retspec,esa);
1221 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
1222 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
1224 dirnam.nam$b_esl -= 9;
1226 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
1227 if (cp1 == NULL) return NULL; /* should never happen */
1230 retlen = strlen(esa);
1231 if ((cp1 = strrchr(esa,'.')) != NULL) {
1232 /* There's more than one directory in the path. Just roll back. */
1234 if (buf) retspec = buf;
1235 else if (ts) New(7011,retspec,retlen+7,char);
1236 else retspec = __fileify_retbuf;
1237 strcpy(retspec,esa);
1240 if (dirnam.nam$l_fnb & NAM$M_ROOT_DIR) {
1241 /* Go back and expand rooted logical name */
1242 dirnam.nam$b_nop = NAM$M_SYNCHK | NAM$M_NOCONCEAL;
1243 if (!(sys$parse(&dirfab) & 1)) {
1245 set_vaxc_errno(dirfab.fab$l_sts);
1248 retlen = dirnam.nam$b_esl - 9; /* esa - '][' - '].DIR;1' */
1249 if (buf) retspec = buf;
1250 else if (ts) New(7012,retspec,retlen+16,char);
1251 else retspec = __fileify_retbuf;
1252 cp1 = strstr(esa,"][");
1254 memcpy(retspec,esa,dirlen);
1255 if (!strncmp(cp1+2,"000000]",7)) {
1256 retspec[dirlen-1] = '\0';
1257 for (cp1 = retspec+dirlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
1258 if (*cp1 == '.') *cp1 = ']';
1260 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
1261 memcpy(cp1+1,"000000]",7);
1265 memcpy(retspec+dirlen,cp1+2,retlen-dirlen);
1266 retspec[retlen] = '\0';
1267 /* Convert last '.' to ']' */
1268 for (cp1 = retspec+retlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
1269 if (*cp1 == '.') *cp1 = ']';
1271 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
1272 memcpy(cp1+1,"000000]",7);
1276 else { /* This is a top-level dir. Add the MFD to the path. */
1277 if (buf) retspec = buf;
1278 else if (ts) New(7012,retspec,retlen+16,char);
1279 else retspec = __fileify_retbuf;
1282 while (*cp1 != ':') *(cp2++) = *(cp1++);
1283 strcpy(cp2,":[000000]");
1288 /* We've set up the string up through the filename. Add the
1289 type and version, and we're done. */
1290 strcat(retspec,".DIR;1");
1292 /* $PARSE may have upcased filespec, so convert output to lower
1293 * case if input contained any lowercase characters. */
1294 if (haslower) __mystrtolower(retspec);
1297 } /* end of do_fileify_dirspec() */
1299 /* External entry points */
1300 char *fileify_dirspec(char *dir, char *buf)
1301 { return do_fileify_dirspec(dir,buf,0); }
1302 char *fileify_dirspec_ts(char *dir, char *buf)
1303 { return do_fileify_dirspec(dir,buf,1); }
1305 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
1306 static char *do_pathify_dirspec(char *dir,char *buf, int ts)
1308 static char __pathify_retbuf[NAM$C_MAXRSS+1];
1309 unsigned long int retlen;
1310 char *retpath, *cp1, *cp2, trndir[NAM$C_MAXRSS+1];
1312 if (!dir || !*dir) {
1313 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
1316 if (*dir) strcpy(trndir,dir);
1317 else getcwd(trndir,sizeof trndir - 1);
1319 while (!strpbrk(trndir,"/]:>") && my_trnlnm(trndir,trndir,0)) {
1320 STRLEN trnlen = strlen(trndir);
1322 /* Trap simple rooted lnms, and return lnm:[000000] */
1323 if (!strcmp(trndir+trnlen-2,".]")) {
1324 if (buf) retpath = buf;
1325 else if (ts) New(7018,retpath,strlen(dir)+10,char);
1326 else retpath = __pathify_retbuf;
1327 strcpy(retpath,dir);
1328 strcat(retpath,":[000000]");
1334 if (!strpbrk(dir,"]:>")) { /* Unix-style path or plain name */
1335 if (*dir == '.' && (*(dir+1) == '\0' ||
1336 (*(dir+1) == '.' && *(dir+2) == '\0')))
1337 retlen = 2 + (*(dir+1) != '\0');
1339 if ( !(cp1 = strrchr(dir,'/')) &&
1340 !(cp1 = strrchr(dir,']')) &&
1341 !(cp1 = strrchr(dir,'>')) ) cp1 = dir;
1342 if ((cp2 = strchr(cp1,'.')) != NULL) {
1344 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
1345 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
1346 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
1347 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
1348 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
1349 (ver || *cp3)))))) {
1351 set_vaxc_errno(RMS$_DIR);
1354 retlen = cp2 - dir + 1;
1356 else { /* No file type present. Treat the filename as a directory. */
1357 retlen = strlen(dir) + 1;
1360 if (buf) retpath = buf;
1361 else if (ts) New(7013,retpath,retlen+1,char);
1362 else retpath = __pathify_retbuf;
1363 strncpy(retpath,dir,retlen-1);
1364 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
1365 retpath[retlen-1] = '/'; /* with '/', add it. */
1366 retpath[retlen] = '\0';
1368 else retpath[retlen-1] = '\0';
1370 else { /* VMS-style directory spec */
1371 char esa[NAM$C_MAXRSS+1], *cp;
1372 unsigned long int sts, cmplen, haslower;
1373 struct FAB dirfab = cc$rms_fab;
1374 struct NAM savnam, dirnam = cc$rms_nam;
1376 /* If we've got an explicit filename, we can just shuffle the string. */
1377 if ( ( (cp1 = strrchr(dir,']')) != NULL ||
1378 (cp1 = strrchr(dir,'>')) != NULL ) && *(cp1+1)) {
1379 if ((cp2 = strchr(cp1,'.')) != NULL) {
1381 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
1382 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
1383 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
1384 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
1385 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
1386 (ver || *cp3)))))) {
1388 set_vaxc_errno(RMS$_DIR);
1392 else { /* No file type, so just draw name into directory part */
1393 for (cp2 = cp1; *cp2; cp2++) ;
1396 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
1398 /* We've now got a VMS 'path'; fall through */
1400 dirfab.fab$b_fns = strlen(dir);
1401 dirfab.fab$l_fna = dir;
1402 if (dir[dirfab.fab$b_fns-1] == ']' ||
1403 dir[dirfab.fab$b_fns-1] == '>' ||
1404 dir[dirfab.fab$b_fns-1] == ':') { /* It's already a VMS 'path' */
1405 if (buf) retpath = buf;
1406 else if (ts) New(7014,retpath,strlen(dir)+1,char);
1407 else retpath = __pathify_retbuf;
1408 strcpy(retpath,dir);
1411 dirfab.fab$l_dna = ".DIR;1";
1412 dirfab.fab$b_dns = 6;
1413 dirfab.fab$l_nam = &dirnam;
1414 dirnam.nam$b_ess = (unsigned char) sizeof esa - 1;
1415 dirnam.nam$l_esa = esa;
1417 for (cp = dir; *cp; cp++)
1418 if (islower(*cp)) { haslower = 1; break; }
1420 if (!(sts = (sys$parse(&dirfab)&1))) {
1421 if (dirfab.fab$l_sts == RMS$_DIR) {
1422 dirnam.nam$b_nop |= NAM$M_SYNCHK;
1423 sts = sys$parse(&dirfab) & 1;
1427 set_vaxc_errno(dirfab.fab$l_sts);
1433 if (!(sys$search(&dirfab)&1)) { /* Does the file really exist? */
1434 if (dirfab.fab$l_sts != RMS$_FNF) {
1436 set_vaxc_errno(dirfab.fab$l_sts);
1439 dirnam = savnam; /* No; just work with potential name */
1442 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
1443 /* Yep; check version while we're at it, if it's there. */
1444 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
1445 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
1446 /* Something other than .DIR[;1]. Bzzt. */
1448 set_vaxc_errno(RMS$_DIR);
1452 /* OK, the type was fine. Now pull any file name into the
1454 if ((cp1 = strrchr(esa,']'))) *dirnam.nam$l_type = ']';
1456 cp1 = strrchr(esa,'>');
1457 *dirnam.nam$l_type = '>';
1460 *(dirnam.nam$l_type + 1) = '\0';
1461 retlen = dirnam.nam$l_type - esa + 2;
1462 if (buf) retpath = buf;
1463 else if (ts) New(7014,retpath,retlen,char);
1464 else retpath = __pathify_retbuf;
1465 strcpy(retpath,esa);
1466 /* $PARSE may have upcased filespec, so convert output to lower
1467 * case if input contained any lowercase characters. */
1468 if (haslower) __mystrtolower(retpath);
1472 } /* end of do_pathify_dirspec() */
1474 /* External entry points */
1475 char *pathify_dirspec(char *dir, char *buf)
1476 { return do_pathify_dirspec(dir,buf,0); }
1477 char *pathify_dirspec_ts(char *dir, char *buf)
1478 { return do_pathify_dirspec(dir,buf,1); }
1480 /*{{{ char *tounixspec[_ts](char *path, char *buf)*/
1481 static char *do_tounixspec(char *spec, char *buf, int ts)
1483 static char __tounixspec_retbuf[NAM$C_MAXRSS+1];
1484 char *dirend, *rslt, *cp1, *cp2, *cp3, tmp[NAM$C_MAXRSS+1];
1485 int devlen, dirlen, retlen = NAM$C_MAXRSS+1, dashes = 0;
1487 if (spec == NULL) return NULL;
1488 if (strlen(spec) > NAM$C_MAXRSS) return NULL;
1489 if (buf) rslt = buf;
1491 retlen = strlen(spec);
1492 cp1 = strchr(spec,'[');
1493 if (!cp1) cp1 = strchr(spec,'<');
1495 for (cp1++; *cp1 == '-'; cp1++) dashes++; /* VMS '-' ==> Unix '../' */
1497 New(7015,rslt,retlen+2+2*dashes,char);
1499 else rslt = __tounixspec_retbuf;
1500 if (strchr(spec,'/') != NULL) {
1507 dirend = strrchr(spec,']');
1508 if (dirend == NULL) dirend = strrchr(spec,'>');
1509 if (dirend == NULL) dirend = strchr(spec,':');
1510 if (dirend == NULL) {
1514 if (*cp2 != '[' && *cp2 != '<') {
1517 else { /* the VMS spec begins with directories */
1519 if (*cp2 == ']' || *cp2 == '>') {
1523 else if ( *cp2 != '.' && *cp2 != '-') {
1524 *(cp1++) = '/'; /* add the implied device into the Unix spec */
1525 if (getcwd(tmp,sizeof tmp,1) == NULL) {
1526 if (ts) Safefree(rslt);
1531 while (*cp3 != ':' && *cp3) cp3++;
1533 if (strchr(cp3,']') != NULL) break;
1534 } while (((cp3 = my_getenv(tmp)) != NULL) && strcpy(tmp,cp3));
1536 while (*cp3) *(cp1++) = *(cp3++);
1539 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
1540 int offset = cp1 - rslt;
1542 retlen = devlen + dirlen;
1543 Renew(rslt,retlen+1+2*dashes,char);
1544 cp1 = rslt + offset;
1547 else if (*cp2 == '.') cp2++;
1549 for (; cp2 <= dirend; cp2++) {
1552 if (*(cp2+1) == '[') cp2++;
1554 else if (*cp2 == ']' || *cp2 == '>') *(cp1++) = '/';
1555 else if (*cp2 == '.') {
1557 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
1558 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
1559 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
1560 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
1561 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
1564 else if (*cp2 == '-') {
1565 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
1566 while (*cp2 == '-') {
1568 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
1570 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
1571 if (ts) Safefree(rslt); /* filespecs like */
1572 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
1576 else *(cp1++) = *cp2;
1578 else *(cp1++) = *cp2;
1580 while (*cp2) *(cp1++) = *(cp2++);
1585 } /* end of do_tounixspec() */
1587 /* External entry points */
1588 char *tounixspec(char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
1589 char *tounixspec_ts(char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
1591 /*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
1592 static char *do_tovmsspec(char *path, char *buf, int ts) {
1593 static char __tovmsspec_retbuf[NAM$C_MAXRSS+1];
1594 char *rslt, *dirend;
1595 register char *cp1, *cp2;
1596 unsigned long int infront = 0, hasdir = 1;
1598 if (path == NULL) return NULL;
1599 if (buf) rslt = buf;
1600 else if (ts) New(7016,rslt,strlen(path)+9,char);
1601 else rslt = __tovmsspec_retbuf;
1602 if (strpbrk(path,"]:>") ||
1603 (dirend = strrchr(path,'/')) == NULL) {
1604 if (path[0] == '.') {
1605 if (path[1] == '\0') strcpy(rslt,"[]");
1606 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
1607 else strcpy(rslt,path); /* probably garbage */
1609 else strcpy(rslt,path);
1612 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.."? */
1613 if (!*(dirend+2)) dirend +=2;
1614 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
1619 char trndev[NAM$C_MAXRSS+1];
1623 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
1624 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
1626 islnm = my_trnlnm(rslt,trndev,0);
1627 trnend = islnm ? strlen(trndev) - 1 : 0;
1628 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
1629 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
1630 /* If the first element of the path is a logical name, determine
1631 * whether it has to be translated so we can add more directories. */
1632 if (!islnm || rooted) {
1635 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
1639 if (cp2 != dirend) {
1640 if (!buf && ts) Renew(rslt,strlen(path)-strlen(rslt)+trnend+4,char);
1641 strcpy(rslt,trndev);
1642 cp1 = rslt + trnend;
1655 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
1656 cp2 += 2; /* skip over "./" - it's redundant */
1657 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
1659 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
1660 *(cp1++) = '-'; /* "../" --> "-" */
1663 if (cp2 > dirend) cp2 = dirend;
1665 else *(cp1++) = '.';
1667 for (; cp2 < dirend; cp2++) {
1669 if (*(cp2-1) == '/') continue;
1670 if (*(cp1-1) != '.') *(cp1++) = '.';
1673 else if (!infront && *cp2 == '.') {
1674 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
1675 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
1676 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
1677 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
1678 else if (*(cp1-2) == '[') *(cp1-1) = '-';
1679 else { /* back up over previous directory name */
1681 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
1682 if (*(cp1-1) == '[') {
1683 memcpy(cp1,"000000.",7);
1688 if (cp2 == dirend) break;
1690 else *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
1693 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
1694 if (*cp2 == '.') *(cp1++) = '_';
1695 else *(cp1++) = *cp2;
1699 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
1700 if (hasdir) *(cp1++) = ']';
1701 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
1702 while (*cp2) *(cp1++) = *(cp2++);
1707 } /* end of do_tovmsspec() */
1709 /* External entry points */
1710 char *tovmsspec(char *path, char *buf) { return do_tovmsspec(path,buf,0); }
1711 char *tovmsspec_ts(char *path, char *buf) { return do_tovmsspec(path,buf,1); }
1713 /*{{{ char *tovmspath[_ts](char *path, char *buf)*/
1714 static char *do_tovmspath(char *path, char *buf, int ts) {
1715 static char __tovmspath_retbuf[NAM$C_MAXRSS+1];
1717 char pathified[NAM$C_MAXRSS+1], vmsified[NAM$C_MAXRSS+1], *cp;
1719 if (path == NULL) return NULL;
1720 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
1721 if (do_tovmsspec(pathified,buf ? buf : vmsified,0) == NULL) return NULL;
1722 if (buf) return buf;
1724 vmslen = strlen(vmsified);
1725 New(7017,cp,vmslen+1,char);
1726 memcpy(cp,vmsified,vmslen);
1731 strcpy(__tovmspath_retbuf,vmsified);
1732 return __tovmspath_retbuf;
1735 } /* end of do_tovmspath() */
1737 /* External entry points */
1738 char *tovmspath(char *path, char *buf) { return do_tovmspath(path,buf,0); }
1739 char *tovmspath_ts(char *path, char *buf) { return do_tovmspath(path,buf,1); }
1742 /*{{{ char *tounixpath[_ts](char *path, char *buf)*/
1743 static char *do_tounixpath(char *path, char *buf, int ts) {
1744 static char __tounixpath_retbuf[NAM$C_MAXRSS+1];
1746 char pathified[NAM$C_MAXRSS+1], unixified[NAM$C_MAXRSS+1], *cp;
1748 if (path == NULL) return NULL;
1749 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
1750 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) return NULL;
1751 if (buf) return buf;
1753 unixlen = strlen(unixified);
1754 New(7017,cp,unixlen+1,char);
1755 memcpy(cp,unixified,unixlen);
1760 strcpy(__tounixpath_retbuf,unixified);
1761 return __tounixpath_retbuf;
1764 } /* end of do_tounixpath() */
1766 /* External entry points */
1767 char *tounixpath(char *path, char *buf) { return do_tounixpath(path,buf,0); }
1768 char *tounixpath_ts(char *path, char *buf) { return do_tounixpath(path,buf,1); }
1771 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
1773 *****************************************************************************
1775 * Copyright (C) 1989-1994 by *
1776 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
1778 * Permission is hereby granted for the reproduction of this software, *
1779 * on condition that this copyright notice is included in the reproduction, *
1780 * and that such reproduction is not for purposes of profit or material *
1783 * 27-Aug-1994 Modified for inclusion in perl5 *
1784 * by Charles Bailey bailey@genetics.upenn.edu *
1785 *****************************************************************************
1789 * getredirection() is intended to aid in porting C programs
1790 * to VMS (Vax-11 C). The native VMS environment does not support
1791 * '>' and '<' I/O redirection, or command line wild card expansion,
1792 * or a command line pipe mechanism using the '|' AND background
1793 * command execution '&'. All of these capabilities are provided to any
1794 * C program which calls this procedure as the first thing in the
1796 * The piping mechanism will probably work with almost any 'filter' type
1797 * of program. With suitable modification, it may useful for other
1798 * portability problems as well.
1800 * Author: Mark Pizzolato mark@infocomm.com
1804 struct list_item *next;
1808 static void add_item(struct list_item **head,
1809 struct list_item **tail,
1813 static void expand_wild_cards(char *item,
1814 struct list_item **head,
1815 struct list_item **tail,
1818 static int background_process(int argc, char **argv);
1820 static void pipe_and_fork(char **cmargv);
1822 /*{{{ void getredirection(int *ac, char ***av)*/
1824 getredirection(int *ac, char ***av)
1826 * Process vms redirection arg's. Exit if any error is seen.
1827 * If getredirection() processes an argument, it is erased
1828 * from the vector. getredirection() returns a new argc and argv value.
1829 * In the event that a background command is requested (by a trailing "&"),
1830 * this routine creates a background subprocess, and simply exits the program.
1832 * Warning: do not try to simplify the code for vms. The code
1833 * presupposes that getredirection() is called before any data is
1834 * read from stdin or written to stdout.
1836 * Normal usage is as follows:
1842 * getredirection(&argc, &argv);
1846 int argc = *ac; /* Argument Count */
1847 char **argv = *av; /* Argument Vector */
1848 char *ap; /* Argument pointer */
1849 int j; /* argv[] index */
1850 int item_count = 0; /* Count of Items in List */
1851 struct list_item *list_head = 0; /* First Item in List */
1852 struct list_item *list_tail; /* Last Item in List */
1853 char *in = NULL; /* Input File Name */
1854 char *out = NULL; /* Output File Name */
1855 char *outmode = "w"; /* Mode to Open Output File */
1856 char *err = NULL; /* Error File Name */
1857 char *errmode = "w"; /* Mode to Open Error File */
1858 int cmargc = 0; /* Piped Command Arg Count */
1859 char **cmargv = NULL;/* Piped Command Arg Vector */
1862 * First handle the case where the last thing on the line ends with
1863 * a '&'. This indicates the desire for the command to be run in a
1864 * subprocess, so we satisfy that desire.
1867 if (0 == strcmp("&", ap))
1868 exit(background_process(--argc, argv));
1869 if (*ap && '&' == ap[strlen(ap)-1])
1871 ap[strlen(ap)-1] = '\0';
1872 exit(background_process(argc, argv));
1875 * Now we handle the general redirection cases that involve '>', '>>',
1876 * '<', and pipes '|'.
1878 for (j = 0; j < argc; ++j)
1880 if (0 == strcmp("<", argv[j]))
1884 PerlIO_printf(Perl_debug_log,"No input file after < on command line");
1885 exit(LIB$_WRONUMARG);
1890 if ('<' == *(ap = argv[j]))
1895 if (0 == strcmp(">", ap))
1899 PerlIO_printf(Perl_debug_log,"No output file after > on command line");
1900 exit(LIB$_WRONUMARG);
1919 PerlIO_printf(Perl_debug_log,"No output file after > or >> on command line");
1920 exit(LIB$_WRONUMARG);
1924 if (('2' == *ap) && ('>' == ap[1]))
1941 PerlIO_printf(Perl_debug_log,"No output file after 2> or 2>> on command line");
1942 exit(LIB$_WRONUMARG);
1946 if (0 == strcmp("|", argv[j]))
1950 PerlIO_printf(Perl_debug_log,"No command into which to pipe on command line");
1951 exit(LIB$_WRONUMARG);
1953 cmargc = argc-(j+1);
1954 cmargv = &argv[j+1];
1958 if ('|' == *(ap = argv[j]))
1966 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
1969 * Allocate and fill in the new argument vector, Some Unix's terminate
1970 * the list with an extra null pointer.
1972 New(7002, argv, item_count+1, char *);
1974 for (j = 0; j < item_count; ++j, list_head = list_head->next)
1975 argv[j] = list_head->value;
1981 PerlIO_printf(Perl_debug_log,"'|' and '>' may not both be specified on command line");
1982 exit(LIB$_INVARGORD);
1984 pipe_and_fork(cmargv);
1987 /* Check for input from a pipe (mailbox) */
1989 if (in == NULL && 1 == isapipe(0))
1991 char mbxname[L_tmpnam];
1993 long int dvi_item = DVI$_DEVBUFSIZ;
1994 $DESCRIPTOR(mbxnam, "");
1995 $DESCRIPTOR(mbxdevnam, "");
1997 /* Input from a pipe, reopen it in binary mode to disable */
1998 /* carriage control processing. */
2000 PerlIO_getname(stdin, mbxname);
2001 mbxnam.dsc$a_pointer = mbxname;
2002 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
2003 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
2004 mbxdevnam.dsc$a_pointer = mbxname;
2005 mbxdevnam.dsc$w_length = sizeof(mbxname);
2006 dvi_item = DVI$_DEVNAM;
2007 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
2008 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
2011 freopen(mbxname, "rb", stdin);
2014 PerlIO_printf(Perl_debug_log,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
2018 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
2020 PerlIO_printf(Perl_debug_log,"Can't open input file %s as stdin",in);
2023 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
2025 PerlIO_printf(Perl_debug_log,"Can't open output file %s as stdout",out);
2030 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
2032 PerlIO_printf(Perl_debug_log,"Can't open error file %s as stderr",err);
2036 if (NULL == freopen(err, "a", Perl_debug_log, "mbc=32", "mbf=2"))
2041 #ifdef ARGPROC_DEBUG
2042 PerlIO_printf(Perl_debug_log, "Arglist:\n");
2043 for (j = 0; j < *ac; ++j)
2044 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
2046 /* Clear errors we may have hit expanding wildcards, so they don't
2047 show up in Perl's $! later */
2048 set_errno(0); set_vaxc_errno(1);
2049 } /* end of getredirection() */
2052 static void add_item(struct list_item **head,
2053 struct list_item **tail,
2059 New(7003,*head,1,struct list_item);
2063 New(7004,(*tail)->next,1,struct list_item);
2064 *tail = (*tail)->next;
2066 (*tail)->value = value;
2070 static void expand_wild_cards(char *item,
2071 struct list_item **head,
2072 struct list_item **tail,
2076 unsigned long int context = 0;
2082 char vmsspec[NAM$C_MAXRSS+1];
2083 $DESCRIPTOR(filespec, "");
2084 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
2085 $DESCRIPTOR(resultspec, "");
2086 unsigned long int zero = 0, sts;
2088 if (strcspn(item, "*%") == strlen(item) || strchr(item,' ') != NULL)
2090 add_item(head, tail, item, count);
2093 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
2094 resultspec.dsc$b_class = DSC$K_CLASS_D;
2095 resultspec.dsc$a_pointer = NULL;
2096 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
2097 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
2098 if (!isunix || !filespec.dsc$a_pointer)
2099 filespec.dsc$a_pointer = item;
2100 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
2102 * Only return version specs, if the caller specified a version
2104 had_version = strchr(item, ';');
2106 * Only return device and directory specs, if the caller specifed either.
2108 had_device = strchr(item, ':');
2109 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
2111 while (1 == (1 & (sts = lib$find_file(&filespec, &resultspec, &context,
2112 &defaultspec, 0, 0, &zero))))
2117 New(7005,string,resultspec.dsc$w_length+1,char);
2118 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
2119 string[resultspec.dsc$w_length] = '\0';
2120 if (NULL == had_version)
2121 *((char *)strrchr(string, ';')) = '\0';
2122 if ((!had_directory) && (had_device == NULL))
2124 if (NULL == (devdir = strrchr(string, ']')))
2125 devdir = strrchr(string, '>');
2126 strcpy(string, devdir + 1);
2129 * Be consistent with what the C RTL has already done to the rest of
2130 * the argv items and lowercase all of these names.
2132 for (c = string; *c; ++c)
2135 if (isunix) trim_unixpath(string,item);
2136 add_item(head, tail, string, count);
2139 if (sts != RMS$_NMF)
2141 set_vaxc_errno(sts);
2147 set_errno(ENOENT); break;
2149 set_errno(ENODEV); break;
2152 set_errno(EINVAL); break;
2154 set_errno(EACCES); break;
2156 _ckvmssts_noperl(sts);
2160 add_item(head, tail, item, count);
2161 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
2162 _ckvmssts_noperl(lib$find_file_end(&context));
2165 static int child_st[2];/* Event Flag set when child process completes */
2167 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
2169 static unsigned long int exit_handler(int *status)
2173 if (0 == child_st[0])
2175 #ifdef ARGPROC_DEBUG
2176 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
2178 fflush(stdout); /* Have to flush pipe for binary data to */
2179 /* terminate properly -- <tp@mccall.com> */
2180 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
2181 sys$dassgn(child_chan);
2183 sys$synch(0, child_st);
2188 static void sig_child(int chan)
2190 #ifdef ARGPROC_DEBUG
2191 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
2193 if (child_st[0] == 0)
2197 static struct exit_control_block exit_block =
2202 &exit_block.exit_status,
2206 static void pipe_and_fork(char **cmargv)
2209 $DESCRIPTOR(cmddsc, "");
2210 static char mbxname[64];
2211 $DESCRIPTOR(mbxdsc, mbxname);
2213 unsigned long int zero = 0, one = 1;
2215 strcpy(subcmd, cmargv[0]);
2216 for (j = 1; NULL != cmargv[j]; ++j)
2218 strcat(subcmd, " \"");
2219 strcat(subcmd, cmargv[j]);
2220 strcat(subcmd, "\"");
2222 cmddsc.dsc$a_pointer = subcmd;
2223 cmddsc.dsc$w_length = strlen(cmddsc.dsc$a_pointer);
2225 create_mbx(&child_chan,&mbxdsc);
2226 #ifdef ARGPROC_DEBUG
2227 PerlIO_printf(Perl_debug_log, "Pipe Mailbox Name = '%s'\n", mbxdsc.dsc$a_pointer);
2228 PerlIO_printf(Perl_debug_log, "Sub Process Command = '%s'\n", cmddsc.dsc$a_pointer);
2230 _ckvmssts_noperl(lib$spawn(&cmddsc, &mbxdsc, 0, &one,
2231 0, &pid, child_st, &zero, sig_child,
2233 #ifdef ARGPROC_DEBUG
2234 PerlIO_printf(Perl_debug_log, "Subprocess's Pid = %08X\n", pid);
2236 sys$dclexh(&exit_block);
2237 if (NULL == freopen(mbxname, "wb", stdout))
2239 PerlIO_printf(Perl_debug_log,"Can't open output pipe (name %s)",mbxname);
2243 static int background_process(int argc, char **argv)
2245 char command[2048] = "$";
2246 $DESCRIPTOR(value, "");
2247 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
2248 static $DESCRIPTOR(null, "NLA0:");
2249 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
2251 $DESCRIPTOR(pidstr, "");
2253 unsigned long int flags = 17, one = 1, retsts;
2255 strcat(command, argv[0]);
2258 strcat(command, " \"");
2259 strcat(command, *(++argv));
2260 strcat(command, "\"");
2262 value.dsc$a_pointer = command;
2263 value.dsc$w_length = strlen(value.dsc$a_pointer);
2264 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
2265 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
2266 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
2267 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
2270 _ckvmssts_noperl(retsts);
2272 #ifdef ARGPROC_DEBUG
2273 PerlIO_printf(Perl_debug_log, "%s\n", command);
2275 sprintf(pidstring, "%08X", pid);
2276 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
2277 pidstr.dsc$a_pointer = pidstring;
2278 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
2279 lib$set_symbol(&pidsymbol, &pidstr);
2283 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
2286 * Trim Unix-style prefix off filespec, so it looks like what a shell
2287 * glob expansion would return (i.e. from specified prefix on, not
2288 * full path). Note that returned filespec is Unix-style, regardless
2289 * of whether input filespec was VMS-style or Unix-style.
2291 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
2292 * determine prefix (both may be in VMS or Unix syntax).
2294 * Returns !=0 on success, with trimmed filespec replacing contents of
2295 * fspec, and 0 on failure, with contents of fpsec unchanged.
2297 /*{{{int trim_unixpath(char *fspec, char *wildspec)*/
2299 trim_unixpath(char *fspec, char *wildspec)
2301 char unixified[NAM$C_MAXRSS+1], unixwild[NAM$C_MAXRSS+1],
2302 *template, *base, *cp1, *cp2;
2303 register int tmplen, reslen = 0;
2305 if (!wildspec || !fspec) return 0;
2306 if (strpbrk(wildspec,"]>:") != NULL) {
2307 if (do_tounixspec(wildspec,unixwild,0) == NULL) return 0;
2308 else template = unixified;
2310 else template = wildspec;
2311 if (strpbrk(fspec,"]>:") != NULL) {
2312 if (do_tounixspec(fspec,unixified,0) == NULL) return 0;
2313 else base = unixified;
2314 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
2315 * check to see that final result fits into (isn't longer than) fspec */
2316 reslen = strlen(fspec);
2320 /* No prefix or absolute path on wildcard, so nothing to remove */
2321 if (!*template || *template == '/') {
2322 if (base == fspec) return 1;
2323 tmplen = strlen(unixified);
2324 if (tmplen > reslen) return 0; /* not enough space */
2325 /* Copy unixified resultant, including trailing NUL */
2326 memmove(fspec,unixified,tmplen+1);
2330 /* Find prefix to template consisting of path elements without wildcards */
2331 if ((cp1 = strpbrk(template,"*%?")) == NULL)
2332 for (cp1 = template; *cp1; cp1++) ;
2333 else while (cp1 > template && *cp1 != '/') cp1--;
2334 for (cp2 = base; *cp2; cp2++) ; /* Find end of resultant filespec */
2336 /* Wildcard was in first element, so we don't have a reliable string to
2337 * match against. Guess where to trim resultant filespec by counting
2338 * directory levels in the Unix template. (We could do this instead of
2339 * string matching in all cases, since Unix doesn't have a ... wildcard
2340 * that can expand into multiple levels of subdirectory, but we try for
2341 * the string match so our caller can interpret foo/.../bar.* as
2342 * [.foo...]bar.* if it wants, and only get burned if there was a
2343 * wildcard in the first word (in which case, caveat caller). */
2344 if (cp1 == template) {
2346 for ( ; *cp1; cp1++) if (*cp1 == '/') subdirs++;
2347 /* need to back one more '/' than in template, to pick up leading dirname */
2349 while (cp2 > base) {
2350 if (*cp2 == '/') subdirs--;
2351 if (!subdirs) break; /* quit without decrement when we hit last '/' */
2354 /* ran out of directories on resultant; allow for already trimmed
2355 * resultant, which hits start of string looking for leading '/' */
2356 if (subdirs && (cp2 != base || subdirs != 1)) return 0;
2357 /* Move past leading '/', if there is one */
2358 base = cp2 + (*cp2 == '/' ? 1 : 0);
2359 tmplen = strlen(base);
2360 if (reslen && tmplen > reslen) return 0; /* not enough space */
2361 memmove(fspec,base,tmplen+1); /* copy result to fspec, with trailing NUL */
2364 /* We have a prefix string of complete directory names, so we
2365 * try to find it on the resultant filespec */
2367 tmplen = cp1 - template;
2368 if (!memcmp(base,template,tmplen)) { /* Nothing before prefix; we're done */
2369 if (reslen) { /* we converted to Unix syntax; copy result over */
2370 tmplen = cp2 - base;
2371 if (tmplen > reslen) return 0; /* not enough space */
2372 memmove(fspec,base,tmplen+1); /* Copy trimmed spec + trailing NUL */
2376 for ( ; cp2 - base > tmplen; base++) {
2377 if (*base != '/') continue;
2378 if (!memcmp(base + 1,template,tmplen)) break;
2381 if (cp2 - base == tmplen) return 0; /* Not there - not good */
2382 base++; /* Move past leading '/' */
2383 if (reslen && cp2 - base > reslen) return 0; /* not enough space */
2384 /* Copy down remaining portion of filespec, including trailing NUL */
2385 memmove(fspec,base,cp2 - base + 1);
2389 } /* end of trim_unixpath() */
2394 * VMS readdir() routines.
2395 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
2396 * This code has no copyright.
2398 * 21-Jul-1994 Charles Bailey bailey@genetics.upenn.edu
2399 * Minor modifications to original routines.
2402 /* Number of elements in vms_versions array */
2403 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
2406 * Open a directory, return a handle for later use.
2408 /*{{{ DIR *opendir(char*name) */
2413 char dir[NAM$C_MAXRSS+1];
2415 /* Get memory for the handle, and the pattern. */
2417 if (do_tovmspath(name,dir,0) == NULL) {
2418 Safefree((char *)dd);
2421 New(7007,dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
2423 /* Fill in the fields; mainly playing with the descriptor. */
2424 (void)sprintf(dd->pattern, "%s*.*",dir);
2427 dd->vms_wantversions = 0;
2428 dd->pat.dsc$a_pointer = dd->pattern;
2429 dd->pat.dsc$w_length = strlen(dd->pattern);
2430 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
2431 dd->pat.dsc$b_class = DSC$K_CLASS_S;
2434 } /* end of opendir() */
2438 * Set the flag to indicate we want versions or not.
2440 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
2442 vmsreaddirversions(DIR *dd, int flag)
2444 dd->vms_wantversions = flag;
2449 * Free up an opened directory.
2451 /*{{{ void closedir(DIR *dd)*/
2455 (void)lib$find_file_end(&dd->context);
2456 Safefree(dd->pattern);
2457 Safefree((char *)dd);
2462 * Collect all the version numbers for the current file.
2468 struct dsc$descriptor_s pat;
2469 struct dsc$descriptor_s res;
2471 char *p, *text, buff[sizeof dd->entry.d_name];
2473 unsigned long context, tmpsts;
2475 /* Convenient shorthand. */
2478 /* Add the version wildcard, ignoring the "*.*" put on before */
2479 i = strlen(dd->pattern);
2480 New(7008,text,i + e->d_namlen + 3,char);
2481 (void)strcpy(text, dd->pattern);
2482 (void)sprintf(&text[i - 3], "%s;*", e->d_name);
2484 /* Set up the pattern descriptor. */
2485 pat.dsc$a_pointer = text;
2486 pat.dsc$w_length = i + e->d_namlen - 1;
2487 pat.dsc$b_dtype = DSC$K_DTYPE_T;
2488 pat.dsc$b_class = DSC$K_CLASS_S;
2490 /* Set up result descriptor. */
2491 res.dsc$a_pointer = buff;
2492 res.dsc$w_length = sizeof buff - 2;
2493 res.dsc$b_dtype = DSC$K_DTYPE_T;
2494 res.dsc$b_class = DSC$K_CLASS_S;
2496 /* Read files, collecting versions. */
2497 for (context = 0, e->vms_verscount = 0;
2498 e->vms_verscount < VERSIZE(e);
2499 e->vms_verscount++) {
2500 tmpsts = lib$find_file(&pat, &res, &context);
2501 if (tmpsts == RMS$_NMF || context == 0) break;
2503 buff[sizeof buff - 1] = '\0';
2504 if ((p = strchr(buff, ';')))
2505 e->vms_versions[e->vms_verscount] = atoi(p + 1);
2507 e->vms_versions[e->vms_verscount] = -1;
2510 _ckvmssts(lib$find_file_end(&context));
2513 } /* end of collectversions() */
2516 * Read the next entry from the directory.
2518 /*{{{ struct dirent *readdir(DIR *dd)*/
2522 struct dsc$descriptor_s res;
2523 char *p, buff[sizeof dd->entry.d_name];
2524 unsigned long int tmpsts;
2526 /* Set up result descriptor, and get next file. */
2527 res.dsc$a_pointer = buff;
2528 res.dsc$w_length = sizeof buff - 2;
2529 res.dsc$b_dtype = DSC$K_DTYPE_T;
2530 res.dsc$b_class = DSC$K_CLASS_S;
2531 tmpsts = lib$find_file(&dd->pat, &res, &dd->context);
2532 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
2533 if (!(tmpsts & 1)) {
2534 set_vaxc_errno(tmpsts);
2537 set_errno(EACCES); break;
2539 set_errno(ENODEV); break;
2542 set_errno(ENOENT); break;
2549 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
2550 buff[sizeof buff - 1] = '\0';
2551 for (p = buff; !isspace(*p); p++) *p = _tolower(*p);
2554 /* Skip any directory component and just copy the name. */
2555 if ((p = strchr(buff, ']'))) (void)strcpy(dd->entry.d_name, p + 1);
2556 else (void)strcpy(dd->entry.d_name, buff);
2558 /* Clobber the version. */
2559 if ((p = strchr(dd->entry.d_name, ';'))) *p = '\0';
2561 dd->entry.d_namlen = strlen(dd->entry.d_name);
2562 dd->entry.vms_verscount = 0;
2563 if (dd->vms_wantversions) collectversions(dd);
2566 } /* end of readdir() */
2570 * Return something that can be used in a seekdir later.
2572 /*{{{ long telldir(DIR *dd)*/
2581 * Return to a spot where we used to be. Brute force.
2583 /*{{{ void seekdir(DIR *dd,long count)*/
2585 seekdir(DIR *dd, long count)
2587 int vms_wantversions;
2589 /* If we haven't done anything yet... */
2593 /* Remember some state, and clear it. */
2594 vms_wantversions = dd->vms_wantversions;
2595 dd->vms_wantversions = 0;
2596 _ckvmssts(lib$find_file_end(&dd->context));
2599 /* The increment is in readdir(). */
2600 for (dd->count = 0; dd->count < count; )
2603 dd->vms_wantversions = vms_wantversions;
2605 } /* end of seekdir() */
2608 /* VMS subprocess management
2610 * my_vfork() - just a vfork(), after setting a flag to record that
2611 * the current script is trying a Unix-style fork/exec.
2613 * vms_do_aexec() and vms_do_exec() are called in response to the
2614 * perl 'exec' function. If this follows a vfork call, then they
2615 * call out the the regular perl routines in doio.c which do an
2616 * execvp (for those who really want to try this under VMS).
2617 * Otherwise, they do exactly what the perl docs say exec should
2618 * do - terminate the current script and invoke a new command
2619 * (See below for notes on command syntax.)
2621 * do_aspawn() and do_spawn() implement the VMS side of the perl
2622 * 'system' function.
2624 * Note on command arguments to perl 'exec' and 'system': When handled
2625 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
2626 * are concatenated to form a DCL command string. If the first arg
2627 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
2628 * the the command string is hrnded off to DCL directly. Otherwise,
2629 * the first token of the command is taken as the filespec of an image
2630 * to run. The filespec is expanded using a default type of '.EXE' and
2631 * the process defaults for device, directory, etc., and the resultant
2632 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
2633 * the command string as parameters. This is perhaps a bit compicated,
2634 * but I hope it will form a happy medium between what VMS folks expect
2635 * from lib$spawn and what Unix folks expect from exec.
2638 static int vfork_called;
2640 /*{{{int my_vfork()*/
2650 static struct dsc$descriptor_s VMScmd = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,Nullch};
2658 if (VMScmd.dsc$a_pointer) {
2659 Safefree(VMScmd.dsc$a_pointer);
2660 VMScmd.dsc$w_length = 0;
2661 VMScmd.dsc$a_pointer = Nullch;
2666 setup_argstr(SV *really, SV **mark, SV **sp)
2668 char *junk, *tmps = Nullch;
2669 register size_t cmdlen = 0;
2675 tmps = SvPV(really,rlen);
2682 for (idx++; idx <= sp; idx++) {
2684 junk = SvPVx(*idx,rlen);
2685 cmdlen += rlen ? rlen + 1 : 0;
2688 New(401,Cmd,cmdlen+1,char);
2690 if (tmps && *tmps) {
2695 while (++mark <= sp) {
2698 strcat(Cmd,SvPVx(*mark,na));
2703 } /* end of setup_argstr() */
2706 static unsigned long int
2707 setup_cmddsc(char *cmd, int check_img)
2709 char resspec[NAM$C_MAXRSS+1];
2710 $DESCRIPTOR(defdsc,".EXE");
2711 $DESCRIPTOR(resdsc,resspec);
2712 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
2713 unsigned long int cxt = 0, flags = 1, retsts;
2714 register char *s, *rest, *cp;
2715 register int isdcl = 0;
2718 while (*s && isspace(*s)) s++;
2720 if (*s == '$') { /* Check whether this is a DCL command: leading $ and */
2721 isdcl = 1; /* no dev/dir separators (i.e. not a foreign command) */
2722 for (cp = s; *cp && *cp != '/' && !isspace(*cp); cp++) {
2723 if (*cp == ':' || *cp == '[' || *cp == '<') {
2731 if (isdcl) { /* It's a DCL command, just do it. */
2732 VMScmd.dsc$w_length = strlen(cmd);
2734 VMScmd.dsc$a_pointer = Cmd;
2735 Cmd = Nullch; /* Don't try to free twice in vms_execfree() */
2737 else VMScmd.dsc$a_pointer = savepvn(cmd,VMScmd.dsc$w_length);
2739 else { /* assume first token is an image spec */
2741 while (*s && !isspace(*s)) s++;
2743 imgdsc.dsc$a_pointer = cmd;
2744 imgdsc.dsc$w_length = s - cmd;
2745 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
2746 if (!(retsts & 1)) {
2747 /* just hand off status values likely to be due to user error */
2748 if (retsts == RMS$_FNF || retsts == RMS$_DNF ||
2749 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
2750 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
2751 else { _ckvmssts(retsts); }
2754 _ckvmssts(lib$find_file_end(&cxt));
2756 while (*s && !isspace(*s)) s++;
2758 New(402,VMScmd.dsc$a_pointer,7 + s - resspec + (rest ? strlen(rest) : 0),char);
2759 strcpy(VMScmd.dsc$a_pointer,"$ MCR ");
2760 strcat(VMScmd.dsc$a_pointer,resspec);
2761 if (rest) strcat(VMScmd.dsc$a_pointer,rest);
2762 VMScmd.dsc$w_length = strlen(VMScmd.dsc$a_pointer);
2766 return (VMScmd.dsc$w_length > 255 ? CLI$_BUFOVF : SS$_NORMAL);
2768 } /* end of setup_cmddsc() */
2771 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
2773 vms_do_aexec(SV *really,SV **mark,SV **sp)
2776 if (vfork_called) { /* this follows a vfork - act Unixish */
2778 if (vfork_called < 0) {
2779 warn("Internal inconsistency in tracking vforks");
2782 else return do_aexec(really,mark,sp);
2784 /* no vfork - act VMSish */
2785 return vms_do_exec(setup_argstr(really,mark,sp));
2790 } /* end of vms_do_aexec() */
2793 /* {{{bool vms_do_exec(char *cmd) */
2795 vms_do_exec(char *cmd)
2798 if (vfork_called) { /* this follows a vfork - act Unixish */
2800 if (vfork_called < 0) {
2801 warn("Internal inconsistency in tracking vforks");
2804 else return do_exec(cmd);
2807 { /* no vfork - act VMSish */
2808 unsigned long int retsts;
2810 if ((retsts = setup_cmddsc(cmd,1)) & 1)
2811 retsts = lib$do_command(&VMScmd);
2814 set_vaxc_errno(retsts);
2816 warn("Can't exec \"%s\": %s", VMScmd.dsc$a_pointer, Strerror(errno));
2822 } /* end of vms_do_exec() */
2825 unsigned long int do_spawn(char *);
2827 /* {{{ unsigned long int do_aspawn(SV *really,SV **mark,SV **sp) */
2829 do_aspawn(SV *really,SV **mark,SV **sp)
2831 if (sp > mark) return do_spawn(setup_argstr(really,mark,sp));
2834 } /* end of do_aspawn() */
2837 /* {{{unsigned long int do_spawn(char *cmd) */
2841 unsigned long int substs, hadcmd = 1;
2843 if (!cmd || !*cmd) {
2845 _ckvmssts(lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0));
2847 else if ((substs = setup_cmddsc(cmd,0)) & 1) {
2848 _ckvmssts(lib$spawn(&VMScmd,0,0,0,0,0,&substs,0,0,0,0,0,0));
2853 set_vaxc_errno(substs);
2855 warn("Can't spawn \"%s\": %s",
2856 hadcmd ? VMScmd.dsc$a_pointer : "", Strerror(errno));
2861 } /* end of do_spawn() */
2865 * A simple fwrite replacement which outputs itmsz*nitm chars without
2866 * introducing record boundaries every itmsz chars.
2868 /*{{{ int my_fwrite(void *src, size_t itmsz, size_t nitm, FILE *dest)*/
2870 my_fwrite(void *src, size_t itmsz, size_t nitm, FILE *dest)
2872 register char *cp, *end;
2874 end = (char *)src + itmsz * nitm;
2876 while ((char *)src <= end) {
2877 for (cp = src; cp <= end; cp++) if (!*cp) break;
2878 if (fputs(src,dest) == EOF) return EOF;
2880 if (fputc('\0',dest) == EOF) return EOF;
2886 } /* end of my_fwrite() */
2890 * Here are replacements for the following Unix routines in the VMS environment:
2891 * getpwuid Get information for a particular UIC or UID
2892 * getpwnam Get information for a named user
2893 * getpwent Get information for each user in the rights database
2894 * setpwent Reset search to the start of the rights database
2895 * endpwent Finish searching for users in the rights database
2897 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
2898 * (defined in pwd.h), which contains the following fields:-
2900 * char *pw_name; Username (in lower case)
2901 * char *pw_passwd; Hashed password
2902 * unsigned int pw_uid; UIC
2903 * unsigned int pw_gid; UIC group number
2904 * char *pw_unixdir; Default device/directory (VMS-style)
2905 * char *pw_gecos; Owner name
2906 * char *pw_dir; Default device/directory (Unix-style)
2907 * char *pw_shell; Default CLI name (eg. DCL)
2909 * If the specified user does not exist, getpwuid and getpwnam return NULL.
2911 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
2912 * not the UIC member number (eg. what's returned by getuid()),
2913 * getpwuid() can accept either as input (if uid is specified, the caller's
2914 * UIC group is used), though it won't recognise gid=0.
2916 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
2917 * information about other users in your group or in other groups, respectively.
2918 * If the required privilege is not available, then these routines fill only
2919 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
2922 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
2925 /* sizes of various UAF record fields */
2926 #define UAI$S_USERNAME 12
2927 #define UAI$S_IDENT 31
2928 #define UAI$S_OWNER 31
2929 #define UAI$S_DEFDEV 31
2930 #define UAI$S_DEFDIR 63
2931 #define UAI$S_DEFCLI 31
2934 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
2935 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
2936 (uic).uic$v_group != UIC$K_WILD_GROUP)
2938 static char __empty[]= "";
2939 static struct passwd __passwd_empty=
2940 {(char *) __empty, (char *) __empty, 0, 0,
2941 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
2942 static int contxt= 0;
2943 static struct passwd __pwdcache;
2944 static char __pw_namecache[UAI$S_IDENT+1];
2947 * This routine does most of the work extracting the user information.
2949 static int fillpasswd (const char *name, struct passwd *pwd)
2952 unsigned char length;
2953 char pw_gecos[UAI$S_OWNER+1];
2955 static union uicdef uic;
2957 unsigned char length;
2958 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
2961 unsigned char length;
2962 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
2965 unsigned char length;
2966 char pw_shell[UAI$S_DEFCLI+1];
2968 static char pw_passwd[UAI$S_PWD+1];
2970 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
2971 struct dsc$descriptor_s name_desc;
2972 unsigned long int sts;
2974 static struct itmlst_3 itmlst[]= {
2975 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
2976 {sizeof(uic), UAI$_UIC, &uic, &luic},
2977 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
2978 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
2979 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
2980 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
2981 {0, 0, NULL, NULL}};
2983 name_desc.dsc$w_length= strlen(name);
2984 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
2985 name_desc.dsc$b_class= DSC$K_CLASS_S;
2986 name_desc.dsc$a_pointer= (char *) name;
2988 /* Note that sys$getuai returns many fields as counted strings. */
2989 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
2990 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
2991 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
2993 else { _ckvmssts(sts); }
2994 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
2996 if ((int) owner.length < lowner) lowner= (int) owner.length;
2997 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
2998 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
2999 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
3000 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
3001 owner.pw_gecos[lowner]= '\0';
3002 defdev.pw_dir[ldefdev+ldefdir]= '\0';
3003 defcli.pw_shell[ldefcli]= '\0';
3004 if (valid_uic(uic)) {
3005 pwd->pw_uid= uic.uic$l_uic;
3006 pwd->pw_gid= uic.uic$v_group;
3009 warn("getpwnam returned invalid UIC %#o for user \"%s\"");
3010 pwd->pw_passwd= pw_passwd;
3011 pwd->pw_gecos= owner.pw_gecos;
3012 pwd->pw_dir= defdev.pw_dir;
3013 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
3014 pwd->pw_shell= defcli.pw_shell;
3015 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
3017 ldir= strlen(pwd->pw_unixdir) - 1;
3018 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
3021 strcpy(pwd->pw_unixdir, pwd->pw_dir);
3022 __mystrtolower(pwd->pw_unixdir);
3027 * Get information for a named user.
3029 /*{{{struct passwd *getpwnam(char *name)*/
3030 struct passwd *my_getpwnam(char *name)
3032 struct dsc$descriptor_s name_desc;
3034 unsigned long int status, stat;
3036 __pwdcache = __passwd_empty;
3037 if (!fillpasswd(name, &__pwdcache)) {
3038 /* We still may be able to determine pw_uid and pw_gid */
3039 name_desc.dsc$w_length= strlen(name);
3040 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
3041 name_desc.dsc$b_class= DSC$K_CLASS_S;
3042 name_desc.dsc$a_pointer= (char *) name;
3043 if ((stat = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
3044 __pwdcache.pw_uid= uic.uic$l_uic;
3045 __pwdcache.pw_gid= uic.uic$v_group;
3048 if (stat == SS$_NOSUCHID || stat == SS$_IVIDENT || stat == RMS$_PRV) {
3049 set_vaxc_errno(stat);
3050 set_errno(stat == RMS$_PRV ? EACCES : EINVAL);
3053 else { _ckvmssts(stat); }
3056 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
3057 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
3058 __pwdcache.pw_name= __pw_namecache;
3060 } /* end of my_getpwnam() */
3064 * Get information for a particular UIC or UID.
3065 * Called by my_getpwent with uid=-1 to list all users.
3067 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
3068 struct passwd *my_getpwuid(Uid_t uid)
3070 const $DESCRIPTOR(name_desc,__pw_namecache);
3071 unsigned short lname;
3073 unsigned long int status;
3075 if (uid == (unsigned int) -1) {
3077 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
3078 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
3079 set_vaxc_errno(status);
3080 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
3084 else { _ckvmssts(status); }
3085 } while (!valid_uic (uic));
3089 if (!uic.uic$v_group)
3090 uic.uic$v_group= getgid();
3092 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
3093 else status = SS$_IVIDENT;
3094 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
3095 status == RMS$_PRV) {
3096 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
3099 else { _ckvmssts(status); }
3101 __pw_namecache[lname]= '\0';
3102 __mystrtolower(__pw_namecache);
3104 __pwdcache = __passwd_empty;
3105 __pwdcache.pw_name = __pw_namecache;
3107 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
3108 The identifier's value is usually the UIC, but it doesn't have to be,
3109 so if we can, we let fillpasswd update this. */
3110 __pwdcache.pw_uid = uic.uic$l_uic;
3111 __pwdcache.pw_gid = uic.uic$v_group;
3113 fillpasswd(__pw_namecache, &__pwdcache);
3116 } /* end of my_getpwuid() */
3120 * Get information for next user.
3122 /*{{{struct passwd *my_getpwent()*/
3123 struct passwd *my_getpwent()
3125 return (my_getpwuid((unsigned int) -1));
3130 * Finish searching rights database for users.
3132 /*{{{void my_endpwent()*/
3136 _ckvmssts(sys$finish_rdb(&contxt));
3144 * If the CRTL has a real gmtime(), use it, else look for the logical
3145 * name SYS$TIMEZONE_DIFFERENTIAL used by the native UTC routines on
3146 * VMS >= 6.0. Can be manually defined under earlier versions of VMS
3147 * to translate to the number of seconds which must be added to UTC
3148 * to get to the local time of the system.
3149 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
3152 /*{{{struct tm *my_gmtime(const time_t *time)*/
3153 /* We #defined 'gmtime' as 'my_gmtime' in vmsish.h. #undef it here
3154 * so we can call the CRTL's routine to see if it works.
3158 my_gmtime(const time_t *time)
3160 static int gmtime_emulation_type;
3161 static long int utc_offset_secs;
3165 if (gmtime_emulation_type == 0) {
3166 gmtime_emulation_type++;
3168 if (gmtime(&when) == NULL) { /* CRTL gmtime() is just a stub */
3169 gmtime_emulation_type++;
3170 if ((p = my_getenv("SYS$TIMEZONE_DIFFERENTIAL")) == NULL)
3171 gmtime_emulation_type++;
3173 utc_offset_secs = atol(p);
3177 switch (gmtime_emulation_type) {
3179 return gmtime(time);
3181 when = *time - utc_offset_secs;
3182 return localtime(&when);
3184 warn("gmtime not supported on this system");
3187 } /* end of my_gmtime() */
3188 /* Reset definition for later calls */
3189 #define gmtime(t) my_gmtime(t)
3194 * flex_stat, flex_fstat
3195 * basic stat, but gets it right when asked to stat
3196 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
3199 /* encode_dev packs a VMS device name string into an integer to allow
3200 * simple comparisons. This can be used, for example, to check whether two
3201 * files are located on the same device, by comparing their encoded device
3202 * names. Even a string comparison would not do, because stat() reuses the
3203 * device name buffer for each call; so without encode_dev, it would be
3204 * necessary to save the buffer and use strcmp (this would mean a number of
3205 * changes to the standard Perl code, to say nothing of what a Perl script
3208 * The device lock id, if it exists, should be unique (unless perhaps compared
3209 * with lock ids transferred from other nodes). We have a lock id if the disk is
3210 * mounted cluster-wide, which is when we tend to get long (host-qualified)
3211 * device names. Thus we use the lock id in preference, and only if that isn't
3212 * available, do we try to pack the device name into an integer (flagged by
3213 * the sign bit (LOCKID_MASK) being set).
3215 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
3216 * name and its encoded form, but it seems very unlikely that we will find
3217 * two files on different disks that share the same encoded device names,
3218 * and even more remote that they will share the same file id (if the test
3219 * is to check for the same file).
3221 * A better method might be to use sys$device_scan on the first call, and to
3222 * search for the device, returning an index into the cached array.
3223 * The number returned would be more intelligable.
3224 * This is probably not worth it, and anyway would take quite a bit longer
3225 * on the first call.
3227 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
3228 static dev_t encode_dev (const char *dev)
3231 unsigned long int f;
3236 if (!dev || !dev[0]) return 0;
3240 struct dsc$descriptor_s dev_desc;
3241 unsigned long int status, lockid, item = DVI$_LOCKID;
3243 /* For cluster-mounted disks, the disk lock identifier is unique, so we
3244 can try that first. */
3245 dev_desc.dsc$w_length = strlen (dev);
3246 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
3247 dev_desc.dsc$b_class = DSC$K_CLASS_S;
3248 dev_desc.dsc$a_pointer = (char *) dev;
3249 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
3250 if (lockid) return (lockid & ~LOCKID_MASK);
3254 /* Otherwise we try to encode the device name */
3258 for (q = dev + strlen(dev); q--; q >= dev) {
3261 else if (isalpha (toupper (*q)))
3262 c= toupper (*q) - 'A' + (char)10;
3264 continue; /* Skip '$'s */
3266 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
3268 enc += f * (unsigned long int) c;
3270 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
3272 } /* end of encode_dev() */
3274 static char namecache[NAM$C_MAXRSS+1];
3277 is_null_device(name)
3280 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
3281 The underscore prefix, controller letter, and unit number are
3282 independently optional; for our purposes, the colon punctuation
3283 is not. The colon can be trailed by optional directory and/or
3284 filename, but two consecutive colons indicates a nodename rather
3285 than a device. [pr] */
3286 if (*name == '_') ++name;
3287 if (tolower(*name++) != 'n') return 0;
3288 if (tolower(*name++) != 'l') return 0;
3289 if (tolower(*name) == 'a') ++name;
3290 if (*name == '0') ++name;
3291 return (*name++ == ':') && (*name != ':');
3294 /* Do the permissions allow some operation? Assumes statcache already set. */
3295 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
3296 * subset of the applicable information.
3298 /*{{{I32 cando(I32 bit, I32 effective, struct stat *statbufp)*/
3300 cando(I32 bit, I32 effective, struct stat *statbufp)
3302 if (statbufp == &statcache)
3303 return cando_by_name(bit,effective,namecache);
3305 char fname[NAM$C_MAXRSS+1];
3306 unsigned long int retsts;
3307 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
3308 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
3310 /* If the struct mystat is stale, we're OOL; stat() overwrites the
3311 device name on successive calls */
3312 devdsc.dsc$a_pointer = statbufp->st_devnam;
3313 devdsc.dsc$w_length = strlen(statbufp->st_devnam);
3314 namdsc.dsc$a_pointer = fname;
3315 namdsc.dsc$w_length = sizeof fname - 1;
3317 retsts = lib$fid_to_name(&devdsc,&(statbufp->st_ino),&namdsc,
3318 &namdsc.dsc$w_length,0,0);
3320 fname[namdsc.dsc$w_length] = '\0';
3321 return cando_by_name(bit,effective,fname);
3323 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
3324 warn("Can't get filespec - stale stat buffer?\n");
3328 return FALSE; /* Should never get to here */
3330 } /* end of cando() */
3334 /*{{{I32 cando_by_name(I32 bit, I32 effective, char *fname)*/
3336 cando_by_name(I32 bit, I32 effective, char *fname)
3338 static char usrname[L_cuserid];
3339 static struct dsc$descriptor_s usrdsc =
3340 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
3341 char vmsname[NAM$C_MAXRSS+1], fileified[NAM$C_MAXRSS+1];
3342 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
3343 unsigned short int retlen;
3344 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
3345 union prvdef curprv;
3346 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
3347 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
3348 struct itmlst_3 jpilst[2] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
3351 if (!fname || !*fname) return FALSE;
3352 /* Make sure we expand logical names, since sys$check_access doesn't */
3353 if (!strpbrk(fname,"/]>:")) {
3354 strcpy(fileified,fname);
3355 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) ;
3358 if (!do_tovmsspec(fname,vmsname,1)) return FALSE;
3359 retlen = namdsc.dsc$w_length = strlen(vmsname);
3360 namdsc.dsc$a_pointer = vmsname;
3361 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
3362 vmsname[retlen-1] == ':') {
3363 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
3364 namdsc.dsc$w_length = strlen(fileified);
3365 namdsc.dsc$a_pointer = fileified;
3368 if (!usrdsc.dsc$w_length) {
3370 usrdsc.dsc$w_length = strlen(usrname);
3377 access = ARM$M_EXECUTE;
3382 access = ARM$M_READ;
3387 access = ARM$M_WRITE;
3392 access = ARM$M_DELETE;
3398 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
3399 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
3400 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF ||
3401 retsts == RMS$_DIR || retsts == RMS$_DEV) {
3402 set_vaxc_errno(retsts);
3403 if (retsts == SS$_NOPRIV) set_errno(EACCES);
3404 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
3405 else set_errno(ENOENT);
3408 if (retsts == SS$_NORMAL) {
3409 if (!privused) return TRUE;
3410 /* We can get access, but only by using privs. Do we have the
3411 necessary privs currently enabled? */
3412 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
3413 if ((privused & CHP$M_BYPASS) && !curprv.prv$v_bypass) return FALSE;
3414 if ((privused & CHP$M_SYSPRV) && !curprv.prv$v_sysprv &&
3415 !curprv.prv$v_bypass) return FALSE;
3416 if ((privused & CHP$M_GRPPRV) && !curprv.prv$v_grpprv &&
3417 !curprv.prv$v_sysprv && !curprv.prv$v_bypass) return FALSE;
3418 if ((privused & CHP$M_READALL) && !curprv.prv$v_readall) return FALSE;
3423 return FALSE; /* Should never get here */
3425 } /* end of cando_by_name() */
3429 /*{{{ int flex_fstat(int fd, struct stat *statbuf)*/
3432 flex_fstat(int fd, struct mystat *statbufp)
3434 if (!fstat(fd,(stat_t *) statbufp)) {
3435 if (statbufp == &statcache) *namecache == '\0';
3436 statbufp->st_dev = encode_dev(statbufp->st_devnam);
3441 } /* end of flex_fstat() */
3444 /*{{{ int flex_stat(char *fspec, struct stat *statbufp)*/
3445 /* We defined 'stat' as 'mystat' in vmsish.h so that declarations of
3446 * 'struct stat' elsewhere in Perl would use our struct. We go back
3447 * to the system version here, since we're actually calling their
3451 flex_stat(char *fspec, struct mystat *statbufp)
3453 char fileified[NAM$C_MAXRSS+1];
3456 if (statbufp == &statcache) do_tovmsspec(fspec,namecache,0);
3457 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
3458 memset(statbufp,0,sizeof *statbufp);
3459 statbufp->st_dev = encode_dev("_NLA0:");
3460 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
3461 statbufp->st_uid = 0x00010001;
3462 statbufp->st_gid = 0x0001;
3463 time((time_t *)&statbufp->st_mtime);
3464 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
3468 /* Try for a directory name first. If fspec contains a filename without
3469 * a type (e.g. sea:[dark.dark]water), and both sea:[wine.dark]water.dir
3470 * and sea:[wine.dark]water. exist, we prefer the directory here.
3471 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
3472 * not sea:[wine.dark]., if the latter exists. If the intended target is
3473 * the file with null type, specify this by calling flex_stat() with
3474 * a '.' at the end of fspec.
3476 if (do_fileify_dirspec(fspec,fileified,0) != NULL) {
3477 retval = stat(fileified,(stat_t *) statbufp);
3478 if (!retval && statbufp == &statcache) strcpy(namecache,fileified);
3480 if (retval) retval = stat(fspec,(stat_t *) statbufp);
3481 if (!retval) statbufp->st_dev = encode_dev(statbufp->st_devnam);
3484 } /* end of flex_stat() */
3485 /* Reset definition for later calls */
3489 /* Insures that no carriage-control translation will be done on a file. */
3490 /*{{{FILE *my_binmode(FILE *fp, char iotype)*/
3492 my_binmode(FILE *fp, char iotype)
3494 char filespec[NAM$C_MAXRSS], *acmode;
3497 if (!fgetname(fp,filespec)) return NULL;
3498 if (iotype != '-' && fgetpos(fp,&pos) == -1) return NULL;
3500 case '<': case 'r': acmode = "rb"; break;
3502 /* use 'a' instead of 'w' to avoid creating new file;
3503 fsetpos below will take care of restoring file position */
3504 case 'a': acmode = "ab"; break;
3505 case '+': case '|': case 's': acmode = "rb+"; break;
3506 case '-': acmode = fileno(fp) ? "ab" : "rb"; break;
3508 warn("Unrecognized iotype %c in my_binmode",iotype);
3511 if (freopen(filespec,acmode,fp) == NULL) return NULL;
3512 if (iotype != '-' && fsetpos(fp,&pos) == -1) return NULL;
3514 } /* end of my_binmode() */
3518 /*{{{char *my_getlogin()*/
3519 /* VMS cuserid == Unix getlogin, except calling sequence */
3523 static char user[L_cuserid];
3524 return cuserid(user);
3529 /* rmscopy - copy a file using VMS RMS routines
3531 * Copies contents and attributes of spec_in to spec_out, except owner
3532 * and protection information. Name and type of spec_in are used as
3533 * defaults for spec_out. The third parameter specifies whether rmscopy()
3534 * should try to propagate timestamps from the input file to the output file.
3535 * If it is less than 0, no timestamps are preserved. If it is 0, then
3536 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
3537 * propagated to the output file at creation iff the output file specification
3538 * did not contain an explicit name or type, and the revision date is always
3539 * updated at the end of the copy operation. If it is greater than 0, then
3540 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
3541 * other than the revision date should be propagated, and bit 1 indicates
3542 * that the revision date should be propagated.
3544 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
3546 * Copyright 1996 by Charles Bailey <bailey@genetics.upenn.edu>.
3547 * Incorporates, with permission, some code from EZCOPY by Tim Adye
3548 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
3549 * as part of the Perl standard distribution under the terms of the
3550 * GNU General Public License or the Perl Artistic License. Copies
3551 * of each may be found in the Perl standard distribution.
3553 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
3555 rmscopy(char *spec_in, char *spec_out, int preserve_dates)
3557 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
3558 rsa[NAM$C_MAXRSS], ubf[32256];
3559 unsigned long int i, sts, sts2;
3560 struct FAB fab_in, fab_out;
3561 struct RAB rab_in, rab_out;
3563 struct XABDAT xabdat;
3564 struct XABFHC xabfhc;
3565 struct XABRDT xabrdt;
3566 struct XABSUM xabsum;
3568 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
3569 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
3570 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
3574 fab_in = cc$rms_fab;
3575 fab_in.fab$l_fna = vmsin;
3576 fab_in.fab$b_fns = strlen(vmsin);
3577 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
3578 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
3579 fab_in.fab$l_fop = FAB$M_SQO;
3580 fab_in.fab$l_nam = &nam;
3581 fab_in.fab$l_xab = (void *) &xabdat;
3584 nam.nam$l_rsa = rsa;
3585 nam.nam$b_rss = sizeof(rsa);
3586 nam.nam$l_esa = esa;
3587 nam.nam$b_ess = sizeof (esa);
3588 nam.nam$b_esl = nam.nam$b_rsl = 0;
3590 xabdat = cc$rms_xabdat; /* To get creation date */
3591 xabdat.xab$l_nxt = (void *) &xabfhc;
3593 xabfhc = cc$rms_xabfhc; /* To get record length */
3594 xabfhc.xab$l_nxt = (void *) &xabsum;
3596 xabsum = cc$rms_xabsum; /* To get key and area information */
3598 if (!((sts = sys$open(&fab_in)) & 1)) {
3599 set_vaxc_errno(sts);
3603 set_errno(ENOENT); break;
3605 set_errno(ENODEV); break;
3607 set_errno(EINVAL); break;
3609 set_errno(EACCES); break;
3617 fab_out.fab$w_ifi = 0;
3618 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
3619 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
3620 fab_out.fab$l_fop = FAB$M_SQO;
3621 fab_out.fab$l_fna = vmsout;
3622 fab_out.fab$b_fns = strlen(vmsout);
3623 fab_out.fab$l_dna = nam.nam$l_name;
3624 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
3626 if (preserve_dates == 0) { /* Act like DCL COPY */
3627 nam.nam$b_nop = NAM$M_SYNCHK;
3628 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
3629 if (!((sts = sys$parse(&fab_out)) & 1)) {
3630 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
3631 set_vaxc_errno(sts);
3634 fab_out.fab$l_xab = (void *) &xabdat;
3635 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
3637 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
3638 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
3639 preserve_dates =0; /* bitmask from this point forward */
3641 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
3642 if (!((sts = sys$create(&fab_out)) & 1)) {
3643 set_vaxc_errno(sts);
3646 set_errno(ENOENT); break;
3648 set_errno(ENODEV); break;
3650 set_errno(EINVAL); break;
3652 set_errno(EACCES); break;
3658 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
3659 if (preserve_dates & 2) {
3660 /* sys$close() will process xabrdt, not xabdat */
3661 xabrdt = cc$rms_xabrdt;
3663 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
3665 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
3666 * is unsigned long[2], while DECC & VAXC use a struct */
3667 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
3669 fab_out.fab$l_xab = (void *) &xabrdt;
3672 rab_in = cc$rms_rab;
3673 rab_in.rab$l_fab = &fab_in;
3674 rab_in.rab$l_rop = RAB$M_BIO;
3675 rab_in.rab$l_ubf = ubf;
3676 rab_in.rab$w_usz = sizeof ubf;
3677 if (!((sts = sys$connect(&rab_in)) & 1)) {
3678 sys$close(&fab_in); sys$close(&fab_out);
3679 set_errno(EVMSERR); set_vaxc_errno(sts);
3683 rab_out = cc$rms_rab;
3684 rab_out.rab$l_fab = &fab_out;
3685 rab_out.rab$l_rbf = ubf;
3686 if (!((sts = sys$connect(&rab_out)) & 1)) {
3687 sys$close(&fab_in); sys$close(&fab_out);
3688 set_errno(EVMSERR); set_vaxc_errno(sts);
3692 while ((sts = sys$read(&rab_in))) { /* always true */
3693 if (sts == RMS$_EOF) break;
3694 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
3695 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
3696 sys$close(&fab_in); sys$close(&fab_out);
3697 set_errno(EVMSERR); set_vaxc_errno(sts);
3702 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
3703 sys$close(&fab_in); sys$close(&fab_out);
3704 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
3706 set_errno(EVMSERR); set_vaxc_errno(sts);
3712 } /* end of rmscopy() */
3716 /*** The following glue provides 'hooks' to make some of the routines
3717 * from this file available from Perl. These routines are sufficiently
3718 * basic, and are required sufficiently early in the build process,
3719 * that's it's nice to have them available to miniperl as well as the
3720 * full Perl, so they're set up here instead of in an extension. The
3721 * Perl code which handles importation of these names into a given
3722 * package lives in [.VMS]Filespec.pm in @INC.
3726 rmsexpand_fromperl(CV *cv)
3729 char *fspec, *defspec = NULL, *rslt;
3731 if (!items || items > 2)
3732 croak("Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
3733 fspec = SvPV(ST(0),na);
3734 if (!fspec || !*fspec) XSRETURN_UNDEF;
3735 if (items == 2) defspec = SvPV(ST(1),na);
3737 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
3738 ST(0) = sv_newmortal();
3739 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
3744 vmsify_fromperl(CV *cv)
3749 if (items != 1) croak("Usage: VMS::Filespec::vmsify(spec)");
3750 vmsified = do_tovmsspec(SvPV(ST(0),na),NULL,1);
3751 ST(0) = sv_newmortal();
3752 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
3757 unixify_fromperl(CV *cv)
3762 if (items != 1) croak("Usage: VMS::Filespec::unixify(spec)");
3763 unixified = do_tounixspec(SvPV(ST(0),na),NULL,1);
3764 ST(0) = sv_newmortal();
3765 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
3770 fileify_fromperl(CV *cv)
3775 if (items != 1) croak("Usage: VMS::Filespec::fileify(spec)");
3776 fileified = do_fileify_dirspec(SvPV(ST(0),na),NULL,1);
3777 ST(0) = sv_newmortal();
3778 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
3783 pathify_fromperl(CV *cv)
3788 if (items != 1) croak("Usage: VMS::Filespec::pathify(spec)");
3789 pathified = do_pathify_dirspec(SvPV(ST(0),na),NULL,1);
3790 ST(0) = sv_newmortal();
3791 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
3796 vmspath_fromperl(CV *cv)
3801 if (items != 1) croak("Usage: VMS::Filespec::vmspath(spec)");
3802 vmspath = do_tovmspath(SvPV(ST(0),na),NULL,1);
3803 ST(0) = sv_newmortal();
3804 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
3809 unixpath_fromperl(CV *cv)
3814 if (items != 1) croak("Usage: VMS::Filespec::unixpath(spec)");
3815 unixpath = do_tounixpath(SvPV(ST(0),na),NULL,1);
3816 ST(0) = sv_newmortal();
3817 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
3822 candelete_fromperl(CV *cv)
3825 char fspec[NAM$C_MAXRSS+1], *fsp;
3829 if (items != 1) croak("Usage: VMS::Filespec::candelete(spec)");
3831 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
3832 if (SvTYPE(mysv) == SVt_PVGV) {
3833 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),fspec)) {
3834 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
3841 if (mysv != ST(0) || !(fsp = SvPV(mysv,na)) || !*fsp) {
3842 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
3848 ST(0) = cando_by_name(S_IDUSR,0,fsp) ? &sv_yes : &sv_no;
3853 rmscopy_fromperl(CV *cv)
3856 char inspec[NAM$C_MAXRSS+1], outspec[NAM$C_MAXRSS+1], *inp, *outp;
3858 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
3859 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
3860 unsigned long int sts;
3864 if (items < 2 || items > 3)
3865 croak("Usage: File::Copy::rmscopy(from,to[,date_flag])");
3867 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
3868 if (SvTYPE(mysv) == SVt_PVGV) {
3869 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),inspec)) {
3870 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
3877 if (mysv != ST(0) || !(inp = SvPV(mysv,na)) || !*inp) {
3878 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
3883 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
3884 if (SvTYPE(mysv) == SVt_PVGV) {
3885 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),outspec)) {
3886 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
3893 if (mysv != ST(1) || !(outp = SvPV(mysv,na)) || !*outp) {
3894 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
3899 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
3901 ST(0) = rmscopy(inp,outp,date_flag) ? &sv_yes : &sv_no;
3908 char* file = __FILE__;
3910 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
3911 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
3912 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
3913 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
3914 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
3915 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
3916 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
3917 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
3918 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);