3 * VMS-specific routines for perl5
5 * Last revised: 27-Feb-1998 by Charles Bailey bailey@newman.upenn.edu
14 #include <climsgdef.h>
24 #include <lib$routines.h>
33 #include <str$routines.h>
38 /* Older versions of ssdef.h don't have these */
39 #ifndef SS$_INVFILFOROP
40 # define SS$_INVFILFOROP 3930
42 #ifndef SS$_NOSUCHOBJECT
43 # define SS$_NOSUCHOBJECT 2696
46 /* Don't replace system definitions of vfork, getenv, and stat,
47 * code below needs to get to the underlying CRTL routines. */
48 #define DONT_MASK_RTL_CALLS
53 /* gcc's header files don't #define direct access macros
54 * corresponding to VAXC's variant structs */
56 # define uic$v_format uic$r_uic_form.uic$v_format
57 # define uic$v_group uic$r_uic_form.uic$v_group
58 # define uic$v_member uic$r_uic_form.uic$v_member
59 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
60 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
61 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
62 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
67 unsigned short int buflen;
68 unsigned short int itmcode;
70 unsigned short int *retlen;
73 static char *__mystrtolower(char *str)
75 if (str) for (; *str; ++str) *str= tolower(*str);
80 my_trnlnm(char *lnm, char *eqv, unsigned long int idx)
82 static char __my_trnlnm_eqv[LNM$C_NAMLENGTH+1];
83 unsigned short int eqvlen;
84 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
85 $DESCRIPTOR(tabdsc,"LNM$FILE_DEV");
86 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
87 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
88 {LNM$C_NAMLENGTH, LNM$_STRING, 0, &eqvlen},
91 if (!lnm || idx > LNM$_MAX_INDEX) {
92 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
94 if (!eqv) eqv = __my_trnlnm_eqv;
95 lnmlst[1].bufadr = (void *)eqv;
96 lnmdsc.dsc$a_pointer = lnm;
97 lnmdsc.dsc$w_length = strlen(lnm);
98 retsts = sys$trnlnm(&attr,&tabdsc,&lnmdsc,0,lnmlst);
99 if (retsts == SS$_NOLOGNAM || retsts == SS$_IVLOGNAM) {
100 set_vaxc_errno(retsts); set_errno(EINVAL); return 0;
102 else if (retsts & 1) {
106 _ckvmssts(retsts); /* Must be an error */
107 return 0; /* Not reached, assuming _ckvmssts() bails out */
109 } /* end of my_trnlnm */
112 * Translate a logical name. Substitute for CRTL getenv() to avoid
113 * memory leak, and to keep my_getenv() and my_setenv() in the same
114 * domain (mostly - my_getenv() need not return a translation from
115 * the process logical name table)
117 * Note: Uses Perl temp to store result so char * can be returned to
118 * caller; this pointer will be invalidated at next Perl statement
121 /*{{{ char *my_getenv(char *lnm)*/
125 static char __my_getenv_eqv[LNM$C_NAMLENGTH+1];
126 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2, *eqv;
127 unsigned long int idx = 0;
131 if (curinterp) { /* Perl interpreter running -- may be threaded */
132 /* Set up a temporary buffer for the return value; Perl will
133 * clean it up at the next statement transition */
134 tmpsv = sv_2mortal(newSVpv("",LNM$C_NAMLENGTH+1));
135 if (!tmpsv) return NULL;
138 else eqv = __my_getenv_eqv; /* Assume no interpreter ==> single thread */
139 for (cp1 = lnm, cp2= uplnm; *cp1; cp1++, cp2++) *cp2 = _toupper(*cp1);
141 if (cp1 - lnm == 7 && !strncmp(uplnm,"DEFAULT",7)) {
142 getcwd(eqv,LNM$C_NAMLENGTH);
146 if ((cp2 = strchr(uplnm,';')) != NULL) {
148 idx = strtoul(cp2+1,NULL,0);
150 trnsuccess = my_trnlnm(uplnm,eqv,idx);
151 /* If we had a translation index, we're only interested in lnms */
152 if (!trnsuccess && cp2 != NULL) return Nullch;
153 if (trnsuccess) return eqv;
155 unsigned long int retsts;
156 struct dsc$descriptor_s symdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
157 valdsc = {LNM$C_NAMLENGTH,DSC$K_DTYPE_T,
159 symdsc.dsc$w_length = cp1 - lnm;
160 symdsc.dsc$a_pointer = uplnm;
161 retsts = lib$get_symbol(&symdsc,&valdsc,&(valdsc.dsc$w_length),0);
162 if (retsts == LIB$_INVSYMNAM) return Nullch;
163 if (retsts != LIB$_NOSUCHSYM) {
164 /* We want to return only logical names or CRTL Unix emulations */
165 if (retsts & 1) return Nullch;
168 /* Try for CRTL emulation of a Unix/POSIX name */
169 else return getenv(uplnm);
174 } /* end of my_getenv() */
177 static FILE *safe_popen(char *, char *);
179 /*{{{ void prime_env_iter() */
182 /* Fill the %ENV associative array with all logical names we can
183 * find, in preparation for iterating over it.
187 static int primed = 0;
188 HV *envhv = GvHVn(envgv);
190 char eqv[LNM$C_NAMLENGTH+1],*start,*end;
192 SV *oldrs, *linesv, *eqvsv;
194 static perl_mutex primenv_mutex = PTHREAD_MUTEX_INITIALIZER;
198 MUTEX_LOCK(&primenv_mutex);
199 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
200 /* Perform a dummy fetch as an lval to insure that the hash table is
201 * set up. Otherwise, the hv_store() will turn into a nullop */
202 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
203 /* Also, set up the four "special" keys that the CRTL defines,
204 * whether or not underlying logical names exist. */
205 (void) hv_fetch(envhv,"HOME",4,TRUE);
206 (void) hv_fetch(envhv,"TERM",4,TRUE);
207 (void) hv_fetch(envhv,"PATH",4,TRUE);
208 (void) hv_fetch(envhv,"USER",4,TRUE);
210 /* Now, go get the logical names */
211 if ((sholog = safe_popen("$ Show Logical *","r")) == Nullfp) {
212 MUTEX_UNLOCK(&primenv_mutex);
213 _ckvmssts(vaxc$errno);
215 /* We use Perl's sv_gets to read from the pipe, since safe_popen is
216 * tied to Perl's I/O layer, so it may not return a simple FILE * */
218 rs = newSVpv("\n",1);
219 linesv = newSVpv("",0);
221 if ((start = sv_gets(linesv,sholog,0)) == Nullch) {
223 SvREFCNT_dec(linesv); SvREFCNT_dec(rs); rs = oldrs;
225 MUTEX_UNLOCK(&primenv_mutex);
228 while (*start != '"' && *start != '=' && *start) start++;
229 if (*start != '"') continue;
230 for (end = ++start; *end && *end != '"'; end++) ;
231 if (*end) *end = '\0';
233 if ((eqvlen = my_trnlnm(start,eqv,0)) == 0) {
234 if (vaxc$errno == SS$_NOLOGNAM || vaxc$errno == SS$_IVLOGNAM) {
236 warn("Ill-formed logical name |%s| in prime_env_iter",start);
239 else { MUTEX_UNLOCK(&primenv_mutex); _ckvmssts(vaxc$errno); }
242 eqvsv = newSVpv(eqv,eqvlen);
243 hv_store(envhv,start,(end ? end - start : strlen(start)),eqvsv,0);
246 } /* end of prime_env_iter */
250 /*{{{ void my_setenv(char *lnm, char *eqv)*/
252 my_setenv(char *lnm,char *eqv)
253 /* Define a supervisor-mode logical name in the process table.
254 * In the future we'll add tables, attribs, and acmodes,
255 * probably through a different call.
258 char uplnm[LNM$C_NAMLENGTH], *cp1, *cp2;
259 unsigned long int retsts, usermode = PSL$C_USER;
260 $DESCRIPTOR(tabdsc,"LNM$PROCESS");
261 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
262 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
264 for(cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) *cp2 = _toupper(*cp1);
265 lnmdsc.dsc$w_length = cp1 - lnm;
267 if (!eqv || !*eqv) { /* we're deleting a logical name */
268 retsts = sys$dellnm(&tabdsc,&lnmdsc,&usermode); /* try user mode first */
269 if (retsts == SS$_IVLOGNAM) return;
270 if (retsts != SS$_NOLOGNAM) _ckvmssts(retsts);
272 retsts = lib$delete_logical(&lnmdsc,&tabdsc); /* then supervisor mode */
273 if (retsts != SS$_NOLOGNAM) _ckvmssts(retsts);
277 eqvdsc.dsc$w_length = strlen(eqv);
278 eqvdsc.dsc$a_pointer = eqv;
280 _ckvmssts(lib$set_logical(&lnmdsc,&eqvdsc,&tabdsc,0,0));
283 } /* end of my_setenv() */
287 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
288 /* my_crypt - VMS password hashing
289 * my_crypt() provides an interface compatible with the Unix crypt()
290 * C library function, and uses sys$hash_password() to perform VMS
291 * password hashing. The quadword hashed password value is returned
292 * as a NUL-terminated 8 character string. my_crypt() does not change
293 * the case of its string arguments; in order to match the behavior
294 * of LOGINOUT et al., alphabetic characters in both arguments must
295 * be upcased by the caller.
298 my_crypt(const char *textpasswd, const char *usrname)
300 # ifndef UAI$C_PREFERRED_ALGORITHM
301 # define UAI$C_PREFERRED_ALGORITHM 127
303 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
304 unsigned short int salt = 0;
305 unsigned long int sts;
307 unsigned short int dsc$w_length;
308 unsigned char dsc$b_type;
309 unsigned char dsc$b_class;
310 const char * dsc$a_pointer;
311 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
312 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
313 struct itmlst_3 uailst[3] = {
314 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
315 { sizeof salt, UAI$_SALT, &salt, 0},
316 { 0, 0, NULL, NULL}};
319 usrdsc.dsc$w_length = strlen(usrname);
320 usrdsc.dsc$a_pointer = usrname;
321 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
328 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
334 if (sts != RMS$_RNF) return NULL;
337 txtdsc.dsc$w_length = strlen(textpasswd);
338 txtdsc.dsc$a_pointer = textpasswd;
339 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
340 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
343 return (char *) hash;
345 } /* end of my_crypt() */
349 static char *do_rmsexpand(char *, char *, int, char *, unsigned);
350 static char *do_fileify_dirspec(char *, char *, int);
351 static char *do_tovmsspec(char *, char *, int);
353 /*{{{int do_rmdir(char *name)*/
357 char dirfile[NAM$C_MAXRSS+1];
361 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
362 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
363 else retval = kill_file(dirfile);
366 } /* end of do_rmdir */
370 * Delete any file to which user has control access, regardless of whether
371 * delete access is explicitly allowed.
372 * Limitations: User must have write access to parent directory.
373 * Does not block signals or ASTs; if interrupted in midstream
374 * may leave file with an altered ACL.
377 /*{{{int kill_file(char *name)*/
379 kill_file(char *name)
381 char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
382 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
383 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
384 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
386 unsigned char myace$b_length;
387 unsigned char myace$b_type;
388 unsigned short int myace$w_flags;
389 unsigned long int myace$l_access;
390 unsigned long int myace$l_ident;
391 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
392 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
393 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
395 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
396 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
397 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
398 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
399 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
400 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
402 /* Expand the input spec using RMS, since the CRTL remove() and
403 * system services won't do this by themselves, so we may miss
404 * a file "hiding" behind a logical name or search list. */
405 if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
406 if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
407 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
408 /* If not, can changing protections help? */
409 if (vaxc$errno != RMS$_PRV) return -1;
411 /* No, so we get our own UIC to use as a rights identifier,
412 * and the insert an ACE at the head of the ACL which allows us
413 * to delete the file.
415 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
416 fildsc.dsc$w_length = strlen(rspec);
417 fildsc.dsc$a_pointer = rspec;
419 newace.myace$l_ident = oldace.myace$l_ident;
420 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
425 case SS$_NOSUCHOBJECT:
426 set_errno(ENOENT); break;
428 set_errno(ENODEV); break;
430 case SS$_INVFILFOROP:
431 set_errno(EINVAL); break;
433 set_errno(EACCES); break;
437 set_vaxc_errno(aclsts);
440 /* Grab any existing ACEs with this identifier in case we fail */
441 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
442 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
443 || fndsts == SS$_NOMOREACE ) {
444 /* Add the new ACE . . . */
445 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
447 if ((rmsts = remove(name))) {
448 /* We blew it - dir with files in it, no write priv for
449 * parent directory, etc. Put things back the way they were. */
450 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
453 addlst[0].bufadr = &oldace;
454 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
461 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
462 /* We just deleted it, so of course it's not there. Some versions of
463 * VMS seem to return success on the unlock operation anyhow (after all
464 * the unlock is successful), but others don't.
466 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
467 if (aclsts & 1) aclsts = fndsts;
470 set_vaxc_errno(aclsts);
476 } /* end of kill_file() */
480 /*{{{int my_mkdir(char *,Mode_t)*/
482 my_mkdir(char *dir, Mode_t mode)
484 STRLEN dirlen = strlen(dir);
486 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
487 * null file name/type. However, it's commonplace under Unix,
488 * so we'll allow it for a gain in portability.
490 if (dir[dirlen-1] == '/') {
491 char *newdir = savepvn(dir,dirlen-1);
492 int ret = mkdir(newdir,mode);
496 else return mkdir(dir,mode);
497 } /* end of my_mkdir */
502 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
504 static unsigned long int mbxbufsiz;
505 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
509 * Get the SYSGEN parameter MAXBUF, and the smaller of it and the
510 * preprocessor consant BUFSIZ from stdio.h as the size of the
513 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
514 if (mbxbufsiz > BUFSIZ) mbxbufsiz = BUFSIZ;
516 _ckvmssts(sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
518 _ckvmssts(lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
519 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
521 } /* end of create_mbx() */
523 /*{{{ my_popen and my_pclose*/
526 struct pipe_details *next;
527 PerlIO *fp; /* stdio file pointer to pipe mailbox */
528 int pid; /* PID of subprocess */
529 int mode; /* == 'r' if pipe open for reading */
530 int done; /* subprocess has completed */
531 unsigned long int completion; /* termination status of subprocess */
534 struct exit_control_block
536 struct exit_control_block *flink;
537 unsigned long int (*exit_routine)();
538 unsigned long int arg_count;
539 unsigned long int *status_address;
540 unsigned long int exit_status;
543 static struct pipe_details *open_pipes = NULL;
544 static $DESCRIPTOR(nl_desc, "NL:");
545 static int waitpid_asleep = 0;
547 static unsigned long int
550 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
553 while (open_pipes != NULL) {
554 if (!open_pipes->done) { /* Tap them gently on the shoulder . . .*/
555 _ckvmssts(sys$forcex(&open_pipes->pid,0,&abort));
558 if (!open_pipes->done) /* We tried to be nice . . . */
559 _ckvmssts(sys$delprc(&open_pipes->pid,0));
560 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
561 else if (!(sts & 1)) retsts = sts;
566 static struct exit_control_block pipe_exitblock =
567 {(struct exit_control_block *) 0,
568 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
572 popen_completion_ast(struct pipe_details *thispipe)
574 thispipe->done = TRUE;
575 if (waitpid_asleep) {
582 safe_popen(char *cmd, char *mode)
584 static int handler_set_up = FALSE;
586 unsigned short int chan;
587 unsigned long int flags=1; /* nowait - gnu c doesn't allow &1 */
588 struct pipe_details *info;
589 struct dsc$descriptor_s namdsc = {sizeof mbxname, DSC$K_DTYPE_T,
590 DSC$K_CLASS_S, mbxname},
591 cmddsc = {0, DSC$K_DTYPE_T,
595 cmddsc.dsc$w_length=strlen(cmd);
596 cmddsc.dsc$a_pointer=cmd;
597 if (cmddsc.dsc$w_length > 255) {
598 set_errno(E2BIG); set_vaxc_errno(CLI$_BUFOVF);
602 New(1301,info,1,struct pipe_details);
605 create_mbx(&chan,&namdsc);
607 /* open a FILE* onto it */
608 info->fp = PerlIO_open(mbxname, mode);
610 /* give up other channel onto it */
611 _ckvmssts(sys$dassgn(chan));
621 _ckvmssts(lib$spawn(&cmddsc, &nl_desc, &namdsc, &flags,
622 0 /* name */, &info->pid, &info->completion,
623 0, popen_completion_ast,info,0,0,0));
626 _ckvmssts(lib$spawn(&cmddsc, &namdsc, 0 /* sys$output */, &flags,
627 0 /* name */, &info->pid, &info->completion,
628 0, popen_completion_ast,info,0,0,0));
631 if (!handler_set_up) {
632 _ckvmssts(sys$dclexh(&pipe_exitblock));
633 handler_set_up = TRUE;
635 info->next=open_pipes; /* prepend to list */
638 forkprocess = info->pid;
640 } /* end of safe_popen */
643 /*{{{ FILE *my_popen(char *cmd, char *mode)*/
645 my_popen(char *cmd, char *mode)
648 TAINT_PROPER("popen");
649 return safe_popen(cmd,mode);
654 /*{{{ I32 my_pclose(FILE *fp)*/
655 I32 my_pclose(FILE *fp)
657 struct pipe_details *info, *last = NULL;
658 unsigned long int retsts;
660 for (info = open_pipes; info != NULL; last = info, info = info->next)
661 if (info->fp == fp) break;
663 if (info == NULL) { /* no such pipe open */
664 set_errno(ECHILD); /* quoth POSIX */
665 set_vaxc_errno(SS$_NONEXPR);
669 /* If we were writing to a subprocess, insure that someone reading from
670 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
671 * produce an EOF record in the mailbox. */
672 if (info->mode != 'r') {
673 char devnam[NAM$C_MAXRSS+1], *cp;
674 unsigned long int chan, iosb[2], retsts, retsts2;
675 struct dsc$descriptor devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, devnam};
677 if (fgetname(info->fp,devnam)) {
678 /* It oughta be a mailbox, so fgetname should give just the device
679 * name, but just in case . . . */
680 if ((cp = strrchr(devnam,':')) != NULL) *(cp+1) = '\0';
681 devdsc.dsc$w_length = strlen(devnam);
682 _ckvmssts(sys$assign(&devdsc,&chan,0,0));
683 retsts = sys$qiow(0,chan,IO$_WRITEOF,iosb,0,0,0,0,0,0,0,0);
684 if (retsts & 1) retsts = iosb[0];
685 retsts2 = sys$dassgn(chan); /* Be sure to deassign the channel */
686 if (retsts & 1) retsts = retsts2;
689 else _ckvmssts(vaxc$errno); /* Should never happen */
691 PerlIO_close(info->fp);
693 if (info->done) retsts = info->completion;
694 else waitpid(info->pid,(int *) &retsts,0);
696 /* remove from list of open pipes */
697 if (last) last->next = info->next;
698 else open_pipes = info->next;
703 } /* end of my_pclose() */
705 /* sort-of waitpid; use only with popen() */
706 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
708 my_waitpid(Pid_t pid, int *statusp, int flags)
710 struct pipe_details *info;
712 for (info = open_pipes; info != NULL; info = info->next)
713 if (info->pid == pid) break;
715 if (info != NULL) { /* we know about this child */
716 while (!info->done) {
721 *statusp = info->completion;
724 else { /* we haven't heard of this child */
725 $DESCRIPTOR(intdsc,"0 00:00:01");
726 unsigned long int ownercode = JPI$_OWNER, ownerpid, mypid;
727 unsigned long int interval[2],sts;
730 _ckvmssts(lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0));
731 _ckvmssts(lib$getjpi(&ownercode,0,0,&mypid,0,0));
732 if (ownerpid != mypid)
733 warn("pid %d not a child",pid);
736 _ckvmssts(sys$bintim(&intdsc,interval));
737 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
738 _ckvmssts(sys$schdwk(0,0,interval,0));
739 _ckvmssts(sys$hiber());
743 /* There's no easy way to find the termination status a child we're
744 * not aware of beforehand. If we're really interested in the future,
745 * we can go looking for a termination mailbox, or chase after the
746 * accounting record for the process.
752 } /* end of waitpid() */
757 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
759 my_gconvert(double val, int ndig, int trail, char *buf)
761 static char __gcvtbuf[DBL_DIG+1];
764 loc = buf ? buf : __gcvtbuf;
766 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
768 sprintf(loc,"%.*g",ndig,val);
774 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
775 return gcvt(val,ndig,loc);
778 loc[0] = '0'; loc[1] = '\0';
786 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
787 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
788 * to expand file specification. Allows for a single default file
789 * specification and a simple mask of options. If outbuf is non-NULL,
790 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
791 * the resultant file specification is placed. If outbuf is NULL, the
792 * resultant file specification is placed into a static buffer.
793 * The third argument, if non-NULL, is taken to be a default file
794 * specification string. The fourth argument is unused at present.
795 * rmesexpand() returns the address of the resultant string if
796 * successful, and NULL on error.
798 static char *do_tounixspec(char *, char *, int);
801 do_rmsexpand(char *filespec, char *outbuf, int ts, char *defspec, unsigned opts)
803 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
804 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
805 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
806 struct FAB myfab = cc$rms_fab;
807 struct NAM mynam = cc$rms_nam;
809 unsigned long int retsts, haslower = 0, isunix = 0;
811 if (!filespec || !*filespec) {
812 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
816 if (ts) out = New(1319,outbuf,NAM$C_MAXRSS+1,char);
817 else outbuf = __rmsexpand_retbuf;
819 if ((isunix = (strchr(filespec,'/') != NULL))) {
820 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) return NULL;
824 myfab.fab$l_fna = filespec;
825 myfab.fab$b_fns = strlen(filespec);
826 myfab.fab$l_nam = &mynam;
828 if (defspec && *defspec) {
829 if (strchr(defspec,'/') != NULL) {
830 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) return NULL;
833 myfab.fab$l_dna = defspec;
834 myfab.fab$b_dns = strlen(defspec);
837 mynam.nam$l_esa = esa;
838 mynam.nam$b_ess = sizeof esa;
839 mynam.nam$l_rsa = outbuf;
840 mynam.nam$b_rss = NAM$C_MAXRSS;
842 retsts = sys$parse(&myfab,0,0);
844 mynam.nam$b_nop |= NAM$M_SYNCHK;
845 if (retsts == RMS$_DNF || retsts == RMS$_DIR ||
846 retsts == RMS$_DEV || retsts == RMS$_DEV) {
847 retsts = sys$parse(&myfab,0,0);
848 if (retsts & 1) goto expanded;
850 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
851 (void) sys$parse(&myfab,0,0); /* Free search context */
852 if (out) Safefree(out);
853 set_vaxc_errno(retsts);
854 if (retsts == RMS$_PRV) set_errno(EACCES);
855 else if (retsts == RMS$_DEV) set_errno(ENODEV);
856 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
857 else set_errno(EVMSERR);
860 retsts = sys$search(&myfab,0,0);
861 if (!(retsts & 1) && retsts != RMS$_FNF) {
862 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
863 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
864 if (out) Safefree(out);
865 set_vaxc_errno(retsts);
866 if (retsts == RMS$_PRV) set_errno(EACCES);
867 else set_errno(EVMSERR);
871 /* If the input filespec contained any lowercase characters,
872 * downcase the result for compatibility with Unix-minded code. */
874 for (out = myfab.fab$l_fna; *out; out++)
875 if (islower(*out)) { haslower = 1; break; }
876 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
877 else { out = esa; speclen = mynam.nam$b_esl; }
878 if (!(mynam.nam$l_fnb & NAM$M_EXP_VER) &&
879 (!defspec || !*defspec || !strchr(myfab.fab$l_dna,';')))
880 speclen = mynam.nam$l_ver - out;
881 if (!(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
882 (!defspec || !*defspec || defspec[myfab.fab$b_dns-1] != '.' ||
883 defspec[myfab.fab$b_dns-2] == '.'))
884 speclen = mynam.nam$l_type - out;
885 /* If we just had a directory spec on input, $PARSE "helpfully"
886 * adds an empty name and type for us */
887 if (mynam.nam$l_name == mynam.nam$l_type &&
888 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
889 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
890 speclen = mynam.nam$l_name - out;
892 if (haslower) __mystrtolower(out);
894 /* Have we been working with an expanded, but not resultant, spec? */
895 /* Also, convert back to Unix syntax if necessary. */
896 if (!mynam.nam$b_rsl) {
898 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
900 else strcpy(outbuf,esa);
903 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
904 strcpy(outbuf,tmpfspec);
906 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
907 mynam.nam$l_rsa = NULL; mynam.nam$b_rss = 0;
908 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
912 /* External entry points */
913 char *rmsexpand(char *spec, char *buf, char *def, unsigned opt)
914 { return do_rmsexpand(spec,buf,0,def,opt); }
915 char *rmsexpand_ts(char *spec, char *buf, char *def, unsigned opt)
916 { return do_rmsexpand(spec,buf,1,def,opt); }
920 ** The following routines are provided to make life easier when
921 ** converting among VMS-style and Unix-style directory specifications.
922 ** All will take input specifications in either VMS or Unix syntax. On
923 ** failure, all return NULL. If successful, the routines listed below
924 ** return a pointer to a buffer containing the appropriately
925 ** reformatted spec (and, therefore, subsequent calls to that routine
926 ** will clobber the result), while the routines of the same names with
927 ** a _ts suffix appended will return a pointer to a mallocd string
928 ** containing the appropriately reformatted spec.
929 ** In all cases, only explicit syntax is altered; no check is made that
930 ** the resulting string is valid or that the directory in question
933 ** fileify_dirspec() - convert a directory spec into the name of the
934 ** directory file (i.e. what you can stat() to see if it's a dir).
935 ** The style (VMS or Unix) of the result is the same as the style
936 ** of the parameter passed in.
937 ** pathify_dirspec() - convert a directory spec into a path (i.e.
938 ** what you prepend to a filename to indicate what directory it's in).
939 ** The style (VMS or Unix) of the result is the same as the style
940 ** of the parameter passed in.
941 ** tounixpath() - convert a directory spec into a Unix-style path.
942 ** tovmspath() - convert a directory spec into a VMS-style path.
943 ** tounixspec() - convert any file spec into a Unix-style file spec.
944 ** tovmsspec() - convert any file spec into a VMS-style spec.
946 ** Copyright 1996 by Charles Bailey <bailey@genetics.upenn.edu>
947 ** Permission is given to distribute this code as part of the Perl
948 ** standard distribution under the terms of the GNU General Public
949 ** License or the Perl Artistic License. Copies of each may be
950 ** found in the Perl standard distribution.
953 /*{{{ char *fileify_dirspec[_ts](char *path, char *buf)*/
954 static char *do_fileify_dirspec(char *dir,char *buf,int ts)
956 static char __fileify_retbuf[NAM$C_MAXRSS+1];
957 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
958 char *retspec, *cp1, *cp2, *lastdir;
959 char trndir[NAM$C_MAXRSS+2], vmsdir[NAM$C_MAXRSS+1];
962 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
964 dirlen = strlen(dir);
965 while (dir[dirlen-1] == '/') --dirlen;
966 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
967 strcpy(trndir,"/sys$disk/000000");
971 if (dirlen > NAM$C_MAXRSS) {
972 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); return NULL;
974 if (!strpbrk(dir+1,"/]>:")) {
975 strcpy(trndir,*dir == '/' ? dir + 1: dir);
976 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) ;
978 dirlen = strlen(dir);
981 strncpy(trndir,dir,dirlen);
982 trndir[dirlen] = '\0';
985 /* If we were handed a rooted logical name or spec, treat it like a
986 * simple directory, so that
987 * $ Define myroot dev:[dir.]
988 * ... do_fileify_dirspec("myroot",buf,1) ...
989 * does something useful.
991 if (!strcmp(dir+dirlen-2,".]")) {
992 dir[--dirlen] = '\0';
996 if ((cp1 = strrchr(dir,']')) != NULL || (cp1 = strrchr(dir,'>')) != NULL) {
997 /* If we've got an explicit filename, we can just shuffle the string. */
998 if (*(cp1+1)) hasfilename = 1;
999 /* Similarly, we can just back up a level if we've got multiple levels
1000 of explicit directories in a VMS spec which ends with directories. */
1002 for (cp2 = cp1; cp2 > dir; cp2--) {
1004 *cp2 = *cp1; *cp1 = '\0';
1008 if (*cp2 == '[' || *cp2 == '<') break;
1013 if (hasfilename || !strpbrk(dir,"]:>")) { /* Unix-style path or filename */
1014 if (dir[0] == '.') {
1015 if (dir[1] == '\0' || (dir[1] == '/' && dir[2] == '\0'))
1016 return do_fileify_dirspec("[]",buf,ts);
1017 else if (dir[1] == '.' &&
1018 (dir[2] == '\0' || (dir[2] == '/' && dir[3] == '\0')))
1019 return do_fileify_dirspec("[-]",buf,ts);
1021 if (dir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
1022 dirlen -= 1; /* to last element */
1023 lastdir = strrchr(dir,'/');
1025 else if ((cp1 = strstr(dir,"/.")) != NULL) {
1026 /* If we have "/." or "/..", VMSify it and let the VMS code
1027 * below expand it, rather than repeating the code to handle
1028 * relative components of a filespec here */
1030 if (*(cp1+2) == '.') cp1++;
1031 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
1032 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
1033 if (strchr(vmsdir,'/') != NULL) {
1034 /* If do_tovmsspec() returned it, it must have VMS syntax
1035 * delimiters in it, so it's a mixed VMS/Unix spec. We take
1036 * the time to check this here only so we avoid a recursion
1037 * loop; otherwise, gigo.
1039 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); return NULL;
1041 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
1042 return do_tounixspec(trndir,buf,ts);
1045 } while ((cp1 = strstr(cp1,"/.")) != NULL);
1046 lastdir = strrchr(dir,'/');
1048 else if (!strcmp(&dir[dirlen-7],"/000000")) {
1049 /* Ditto for specs that end in an MFD -- let the VMS code
1050 * figure out whether it's a real device or a rooted logical. */
1051 dir[dirlen] = '/'; dir[dirlen+1] = '\0';
1052 if (do_tovmsspec(dir,vmsdir,0) == NULL) return NULL;
1053 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
1054 return do_tounixspec(trndir,buf,ts);
1057 if ( !(lastdir = cp1 = strrchr(dir,'/')) &&
1058 !(lastdir = cp1 = strrchr(dir,']')) &&
1059 !(lastdir = cp1 = strrchr(dir,'>'))) cp1 = dir;
1060 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
1062 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
1063 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
1064 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
1065 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
1066 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
1067 (ver || *cp3)))))) {
1069 set_vaxc_errno(RMS$_DIR);
1075 /* If we lead off with a device or rooted logical, add the MFD
1076 if we're specifying a top-level directory. */
1077 if (lastdir && *dir == '/') {
1079 for (cp1 = lastdir - 1; cp1 > dir; cp1--) {
1086 retlen = dirlen + (addmfd ? 13 : 6);
1087 if (buf) retspec = buf;
1088 else if (ts) New(1309,retspec,retlen+1,char);
1089 else retspec = __fileify_retbuf;
1091 dirlen = lastdir - dir;
1092 memcpy(retspec,dir,dirlen);
1093 strcpy(&retspec[dirlen],"/000000");
1094 strcpy(&retspec[dirlen+7],lastdir);
1097 memcpy(retspec,dir,dirlen);
1098 retspec[dirlen] = '\0';
1100 /* We've picked up everything up to the directory file name.
1101 Now just add the type and version, and we're set. */
1102 strcat(retspec,".dir;1");
1105 else { /* VMS-style directory spec */
1106 char esa[NAM$C_MAXRSS+1], term, *cp;
1107 unsigned long int sts, cmplen, haslower = 0;
1108 struct FAB dirfab = cc$rms_fab;
1109 struct NAM savnam, dirnam = cc$rms_nam;
1111 dirfab.fab$b_fns = strlen(dir);
1112 dirfab.fab$l_fna = dir;
1113 dirfab.fab$l_nam = &dirnam;
1114 dirfab.fab$l_dna = ".DIR;1";
1115 dirfab.fab$b_dns = 6;
1116 dirnam.nam$b_ess = NAM$C_MAXRSS;
1117 dirnam.nam$l_esa = esa;
1119 for (cp = dir; *cp; cp++)
1120 if (islower(*cp)) { haslower = 1; break; }
1121 if (!((sts = sys$parse(&dirfab))&1)) {
1122 if (dirfab.fab$l_sts == RMS$_DIR) {
1123 dirnam.nam$b_nop |= NAM$M_SYNCHK;
1124 sts = sys$parse(&dirfab) & 1;
1128 set_vaxc_errno(dirfab.fab$l_sts);
1134 if (sys$search(&dirfab)&1) { /* Does the file really exist? */
1135 /* Yes; fake the fnb bits so we'll check type below */
1136 dirnam.nam$l_fnb |= NAM$M_EXP_TYPE | NAM$M_EXP_VER;
1139 if (dirfab.fab$l_sts != RMS$_FNF) {
1141 set_vaxc_errno(dirfab.fab$l_sts);
1144 dirnam = savnam; /* No; just work with potential name */
1147 if (!(dirnam.nam$l_fnb & (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
1148 cp1 = strchr(esa,']');
1149 if (!cp1) cp1 = strchr(esa,'>');
1150 if (cp1) { /* Should always be true */
1151 dirnam.nam$b_esl -= cp1 - esa - 1;
1152 memcpy(esa,cp1 + 1,dirnam.nam$b_esl);
1155 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
1156 /* Yep; check version while we're at it, if it's there. */
1157 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
1158 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
1159 /* Something other than .DIR[;1]. Bzzt. */
1161 set_vaxc_errno(RMS$_DIR);
1165 esa[dirnam.nam$b_esl] = '\0';
1166 if (dirnam.nam$l_fnb & NAM$M_EXP_NAME) {
1167 /* They provided at least the name; we added the type, if necessary, */
1168 if (buf) retspec = buf; /* in sys$parse() */
1169 else if (ts) New(1311,retspec,dirnam.nam$b_esl+1,char);
1170 else retspec = __fileify_retbuf;
1171 strcpy(retspec,esa);
1174 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
1175 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
1177 dirnam.nam$b_esl -= 9;
1179 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
1180 if (cp1 == NULL) return NULL; /* should never happen */
1183 retlen = strlen(esa);
1184 if ((cp1 = strrchr(esa,'.')) != NULL) {
1185 /* There's more than one directory in the path. Just roll back. */
1187 if (buf) retspec = buf;
1188 else if (ts) New(1311,retspec,retlen+7,char);
1189 else retspec = __fileify_retbuf;
1190 strcpy(retspec,esa);
1193 if (dirnam.nam$l_fnb & NAM$M_ROOT_DIR) {
1194 /* Go back and expand rooted logical name */
1195 dirnam.nam$b_nop = NAM$M_SYNCHK | NAM$M_NOCONCEAL;
1196 if (!(sys$parse(&dirfab) & 1)) {
1198 set_vaxc_errno(dirfab.fab$l_sts);
1201 retlen = dirnam.nam$b_esl - 9; /* esa - '][' - '].DIR;1' */
1202 if (buf) retspec = buf;
1203 else if (ts) New(1312,retspec,retlen+16,char);
1204 else retspec = __fileify_retbuf;
1205 cp1 = strstr(esa,"][");
1207 memcpy(retspec,esa,dirlen);
1208 if (!strncmp(cp1+2,"000000]",7)) {
1209 retspec[dirlen-1] = '\0';
1210 for (cp1 = retspec+dirlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
1211 if (*cp1 == '.') *cp1 = ']';
1213 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
1214 memcpy(cp1+1,"000000]",7);
1218 memcpy(retspec+dirlen,cp1+2,retlen-dirlen);
1219 retspec[retlen] = '\0';
1220 /* Convert last '.' to ']' */
1221 for (cp1 = retspec+retlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
1222 if (*cp1 == '.') *cp1 = ']';
1224 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
1225 memcpy(cp1+1,"000000]",7);
1229 else { /* This is a top-level dir. Add the MFD to the path. */
1230 if (buf) retspec = buf;
1231 else if (ts) New(1312,retspec,retlen+16,char);
1232 else retspec = __fileify_retbuf;
1235 while (*cp1 != ':') *(cp2++) = *(cp1++);
1236 strcpy(cp2,":[000000]");
1241 /* We've set up the string up through the filename. Add the
1242 type and version, and we're done. */
1243 strcat(retspec,".DIR;1");
1245 /* $PARSE may have upcased filespec, so convert output to lower
1246 * case if input contained any lowercase characters. */
1247 if (haslower) __mystrtolower(retspec);
1250 } /* end of do_fileify_dirspec() */
1252 /* External entry points */
1253 char *fileify_dirspec(char *dir, char *buf)
1254 { return do_fileify_dirspec(dir,buf,0); }
1255 char *fileify_dirspec_ts(char *dir, char *buf)
1256 { return do_fileify_dirspec(dir,buf,1); }
1258 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
1259 static char *do_pathify_dirspec(char *dir,char *buf, int ts)
1261 static char __pathify_retbuf[NAM$C_MAXRSS+1];
1262 unsigned long int retlen;
1263 char *retpath, *cp1, *cp2, trndir[NAM$C_MAXRSS+1];
1265 if (!dir || !*dir) {
1266 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
1269 if (*dir) strcpy(trndir,dir);
1270 else getcwd(trndir,sizeof trndir - 1);
1272 while (!strpbrk(trndir,"/]:>") && my_trnlnm(trndir,trndir,0)) {
1273 STRLEN trnlen = strlen(trndir);
1275 /* Trap simple rooted lnms, and return lnm:[000000] */
1276 if (!strcmp(trndir+trnlen-2,".]")) {
1277 if (buf) retpath = buf;
1278 else if (ts) New(1318,retpath,strlen(dir)+10,char);
1279 else retpath = __pathify_retbuf;
1280 strcpy(retpath,dir);
1281 strcat(retpath,":[000000]");
1287 if (!strpbrk(dir,"]:>")) { /* Unix-style path or plain name */
1288 if (*dir == '.' && (*(dir+1) == '\0' ||
1289 (*(dir+1) == '.' && *(dir+2) == '\0')))
1290 retlen = 2 + (*(dir+1) != '\0');
1292 if ( !(cp1 = strrchr(dir,'/')) &&
1293 !(cp1 = strrchr(dir,']')) &&
1294 !(cp1 = strrchr(dir,'>')) ) cp1 = dir;
1295 if ((cp2 = strchr(cp1,'.')) != NULL &&
1296 (*(cp2-1) != '/' || /* Trailing '.', '..', */
1297 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
1298 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
1299 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
1301 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
1302 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
1303 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
1304 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
1305 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
1306 (ver || *cp3)))))) {
1308 set_vaxc_errno(RMS$_DIR);
1311 retlen = cp2 - dir + 1;
1313 else { /* No file type present. Treat the filename as a directory. */
1314 retlen = strlen(dir) + 1;
1317 if (buf) retpath = buf;
1318 else if (ts) New(1313,retpath,retlen+1,char);
1319 else retpath = __pathify_retbuf;
1320 strncpy(retpath,dir,retlen-1);
1321 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
1322 retpath[retlen-1] = '/'; /* with '/', add it. */
1323 retpath[retlen] = '\0';
1325 else retpath[retlen-1] = '\0';
1327 else { /* VMS-style directory spec */
1328 char esa[NAM$C_MAXRSS+1], *cp;
1329 unsigned long int sts, cmplen, haslower;
1330 struct FAB dirfab = cc$rms_fab;
1331 struct NAM savnam, dirnam = cc$rms_nam;
1333 /* If we've got an explicit filename, we can just shuffle the string. */
1334 if ( ( (cp1 = strrchr(dir,']')) != NULL ||
1335 (cp1 = strrchr(dir,'>')) != NULL ) && *(cp1+1)) {
1336 if ((cp2 = strchr(cp1,'.')) != NULL) {
1338 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
1339 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
1340 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
1341 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
1342 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
1343 (ver || *cp3)))))) {
1345 set_vaxc_errno(RMS$_DIR);
1349 else { /* No file type, so just draw name into directory part */
1350 for (cp2 = cp1; *cp2; cp2++) ;
1353 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
1355 /* We've now got a VMS 'path'; fall through */
1357 dirfab.fab$b_fns = strlen(dir);
1358 dirfab.fab$l_fna = dir;
1359 if (dir[dirfab.fab$b_fns-1] == ']' ||
1360 dir[dirfab.fab$b_fns-1] == '>' ||
1361 dir[dirfab.fab$b_fns-1] == ':') { /* It's already a VMS 'path' */
1362 if (buf) retpath = buf;
1363 else if (ts) New(1314,retpath,strlen(dir)+1,char);
1364 else retpath = __pathify_retbuf;
1365 strcpy(retpath,dir);
1368 dirfab.fab$l_dna = ".DIR;1";
1369 dirfab.fab$b_dns = 6;
1370 dirfab.fab$l_nam = &dirnam;
1371 dirnam.nam$b_ess = (unsigned char) sizeof esa - 1;
1372 dirnam.nam$l_esa = esa;
1374 for (cp = dir; *cp; cp++)
1375 if (islower(*cp)) { haslower = 1; break; }
1377 if (!(sts = (sys$parse(&dirfab)&1))) {
1378 if (dirfab.fab$l_sts == RMS$_DIR) {
1379 dirnam.nam$b_nop |= NAM$M_SYNCHK;
1380 sts = sys$parse(&dirfab) & 1;
1384 set_vaxc_errno(dirfab.fab$l_sts);
1390 if (!(sys$search(&dirfab)&1)) { /* Does the file really exist? */
1391 if (dirfab.fab$l_sts != RMS$_FNF) {
1393 set_vaxc_errno(dirfab.fab$l_sts);
1396 dirnam = savnam; /* No; just work with potential name */
1399 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
1400 /* Yep; check version while we're at it, if it's there. */
1401 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
1402 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
1403 /* Something other than .DIR[;1]. Bzzt. */
1405 set_vaxc_errno(RMS$_DIR);
1409 /* OK, the type was fine. Now pull any file name into the
1411 if ((cp1 = strrchr(esa,']'))) *dirnam.nam$l_type = ']';
1413 cp1 = strrchr(esa,'>');
1414 *dirnam.nam$l_type = '>';
1417 *(dirnam.nam$l_type + 1) = '\0';
1418 retlen = dirnam.nam$l_type - esa + 2;
1419 if (buf) retpath = buf;
1420 else if (ts) New(1314,retpath,retlen,char);
1421 else retpath = __pathify_retbuf;
1422 strcpy(retpath,esa);
1423 /* $PARSE may have upcased filespec, so convert output to lower
1424 * case if input contained any lowercase characters. */
1425 if (haslower) __mystrtolower(retpath);
1429 } /* end of do_pathify_dirspec() */
1431 /* External entry points */
1432 char *pathify_dirspec(char *dir, char *buf)
1433 { return do_pathify_dirspec(dir,buf,0); }
1434 char *pathify_dirspec_ts(char *dir, char *buf)
1435 { return do_pathify_dirspec(dir,buf,1); }
1437 /*{{{ char *tounixspec[_ts](char *path, char *buf)*/
1438 static char *do_tounixspec(char *spec, char *buf, int ts)
1440 static char __tounixspec_retbuf[NAM$C_MAXRSS+1];
1441 char *dirend, *rslt, *cp1, *cp2, *cp3, tmp[NAM$C_MAXRSS+1];
1442 int devlen, dirlen, retlen = NAM$C_MAXRSS+1, expand = 0;
1444 if (spec == NULL) return NULL;
1445 if (strlen(spec) > NAM$C_MAXRSS) return NULL;
1446 if (buf) rslt = buf;
1448 retlen = strlen(spec);
1449 cp1 = strchr(spec,'[');
1450 if (!cp1) cp1 = strchr(spec,'<');
1452 for (cp1++; *cp1; cp1++) {
1453 if (*cp1 == '-') expand++; /* VMS '-' ==> Unix '../' */
1454 if (*cp1 == '.' && *(cp1+1) == '.' && *(cp1+2) == '.')
1455 { expand++; cp1 +=2; } /* VMS '...' ==> Unix '/.../' */
1458 New(1315,rslt,retlen+2+2*expand,char);
1460 else rslt = __tounixspec_retbuf;
1461 if (strchr(spec,'/') != NULL) {
1468 dirend = strrchr(spec,']');
1469 if (dirend == NULL) dirend = strrchr(spec,'>');
1470 if (dirend == NULL) dirend = strchr(spec,':');
1471 if (dirend == NULL) {
1475 if (*cp2 != '[' && *cp2 != '<') {
1478 else { /* the VMS spec begins with directories */
1480 if (*cp2 == ']' || *cp2 == '>') {
1481 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
1484 else if ( *cp2 != '.' && *cp2 != '-') { /* add the implied device */
1485 if (getcwd(tmp,sizeof tmp,1) == NULL) {
1486 if (ts) Safefree(rslt);
1491 while (*cp3 != ':' && *cp3) cp3++;
1493 if (strchr(cp3,']') != NULL) break;
1494 } while (((cp3 = my_getenv(tmp)) != NULL) && strcpy(tmp,cp3));
1496 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
1497 retlen = devlen + dirlen;
1498 Renew(rslt,retlen+1+2*expand,char);
1504 *(cp1++) = *(cp3++);
1505 if (cp1 - rslt > NAM$C_MAXRSS && !ts && !buf) return NULL; /* No room */
1509 else if ( *cp2 == '.') {
1510 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
1511 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
1517 for (; cp2 <= dirend; cp2++) {
1520 if (*(cp2+1) == '[') cp2++;
1522 else if (*cp2 == ']' || *cp2 == '>') {
1523 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
1525 else if (*cp2 == '.') {
1527 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
1528 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
1529 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
1530 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
1531 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
1533 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
1534 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
1538 else if (*cp2 == '-') {
1539 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
1540 while (*cp2 == '-') {
1542 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
1544 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
1545 if (ts) Safefree(rslt); /* filespecs like */
1546 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
1550 else *(cp1++) = *cp2;
1552 else *(cp1++) = *cp2;
1554 while (*cp2) *(cp1++) = *(cp2++);
1559 } /* end of do_tounixspec() */
1561 /* External entry points */
1562 char *tounixspec(char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
1563 char *tounixspec_ts(char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
1565 /*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
1566 static char *do_tovmsspec(char *path, char *buf, int ts) {
1567 static char __tovmsspec_retbuf[NAM$C_MAXRSS+1];
1568 char *rslt, *dirend;
1569 register char *cp1, *cp2;
1570 unsigned long int infront = 0, hasdir = 1;
1572 if (path == NULL) return NULL;
1573 if (buf) rslt = buf;
1574 else if (ts) New(1316,rslt,strlen(path)+9,char);
1575 else rslt = __tovmsspec_retbuf;
1576 if (strpbrk(path,"]:>") ||
1577 (dirend = strrchr(path,'/')) == NULL) {
1578 if (path[0] == '.') {
1579 if (path[1] == '\0') strcpy(rslt,"[]");
1580 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
1581 else strcpy(rslt,path); /* probably garbage */
1583 else strcpy(rslt,path);
1586 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
1587 if (!*(dirend+2)) dirend +=2;
1588 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
1589 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
1594 char trndev[NAM$C_MAXRSS+1];
1598 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
1600 if (!buf & ts) Renew(rslt,18,char);
1601 strcpy(rslt,"sys$disk:[000000]");
1604 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
1606 islnm = my_trnlnm(rslt,trndev,0);
1607 trnend = islnm ? strlen(trndev) - 1 : 0;
1608 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
1609 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
1610 /* If the first element of the path is a logical name, determine
1611 * whether it has to be translated so we can add more directories. */
1612 if (!islnm || rooted) {
1615 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
1619 if (cp2 != dirend) {
1620 if (!buf && ts) Renew(rslt,strlen(path)-strlen(rslt)+trnend+4,char);
1621 strcpy(rslt,trndev);
1622 cp1 = rslt + trnend;
1635 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
1636 cp2 += 2; /* skip over "./" - it's redundant */
1637 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
1639 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
1640 *(cp1++) = '-'; /* "../" --> "-" */
1643 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
1644 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
1645 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
1646 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
1649 if (cp2 > dirend) cp2 = dirend;
1651 else *(cp1++) = '.';
1653 for (; cp2 < dirend; cp2++) {
1655 if (*(cp2-1) == '/') continue;
1656 if (*(cp1-1) != '.') *(cp1++) = '.';
1659 else if (!infront && *cp2 == '.') {
1660 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
1661 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
1662 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
1663 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
1664 else if (*(cp1-2) == '[') *(cp1-1) = '-';
1665 else { /* back up over previous directory name */
1667 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
1668 if (*(cp1-1) == '[') {
1669 memcpy(cp1,"000000.",7);
1674 if (cp2 == dirend) break;
1676 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
1677 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
1678 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
1679 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
1681 *(cp1++) = '.'; /* Simulate trailing '/' */
1682 cp2 += 2; /* for loop will incr this to == dirend */
1684 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
1686 else *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
1689 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
1690 if (*cp2 == '.') *(cp1++) = '_';
1691 else *(cp1++) = *cp2;
1695 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
1696 if (hasdir) *(cp1++) = ']';
1697 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
1698 while (*cp2) *(cp1++) = *(cp2++);
1703 } /* end of do_tovmsspec() */
1705 /* External entry points */
1706 char *tovmsspec(char *path, char *buf) { return do_tovmsspec(path,buf,0); }
1707 char *tovmsspec_ts(char *path, char *buf) { return do_tovmsspec(path,buf,1); }
1709 /*{{{ char *tovmspath[_ts](char *path, char *buf)*/
1710 static char *do_tovmspath(char *path, char *buf, int ts) {
1711 static char __tovmspath_retbuf[NAM$C_MAXRSS+1];
1713 char pathified[NAM$C_MAXRSS+1], vmsified[NAM$C_MAXRSS+1], *cp;
1715 if (path == NULL) return NULL;
1716 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
1717 if (do_tovmsspec(pathified,buf ? buf : vmsified,0) == NULL) return NULL;
1718 if (buf) return buf;
1720 vmslen = strlen(vmsified);
1721 New(1317,cp,vmslen+1,char);
1722 memcpy(cp,vmsified,vmslen);
1727 strcpy(__tovmspath_retbuf,vmsified);
1728 return __tovmspath_retbuf;
1731 } /* end of do_tovmspath() */
1733 /* External entry points */
1734 char *tovmspath(char *path, char *buf) { return do_tovmspath(path,buf,0); }
1735 char *tovmspath_ts(char *path, char *buf) { return do_tovmspath(path,buf,1); }
1738 /*{{{ char *tounixpath[_ts](char *path, char *buf)*/
1739 static char *do_tounixpath(char *path, char *buf, int ts) {
1740 static char __tounixpath_retbuf[NAM$C_MAXRSS+1];
1742 char pathified[NAM$C_MAXRSS+1], unixified[NAM$C_MAXRSS+1], *cp;
1744 if (path == NULL) return NULL;
1745 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
1746 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) return NULL;
1747 if (buf) return buf;
1749 unixlen = strlen(unixified);
1750 New(1317,cp,unixlen+1,char);
1751 memcpy(cp,unixified,unixlen);
1756 strcpy(__tounixpath_retbuf,unixified);
1757 return __tounixpath_retbuf;
1760 } /* end of do_tounixpath() */
1762 /* External entry points */
1763 char *tounixpath(char *path, char *buf) { return do_tounixpath(path,buf,0); }
1764 char *tounixpath_ts(char *path, char *buf) { return do_tounixpath(path,buf,1); }
1767 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
1769 *****************************************************************************
1771 * Copyright (C) 1989-1994 by *
1772 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
1774 * Permission is hereby granted for the reproduction of this software, *
1775 * on condition that this copyright notice is included in the reproduction, *
1776 * and that such reproduction is not for purposes of profit or material *
1779 * 27-Aug-1994 Modified for inclusion in perl5 *
1780 * by Charles Bailey bailey@genetics.upenn.edu *
1781 *****************************************************************************
1785 * getredirection() is intended to aid in porting C programs
1786 * to VMS (Vax-11 C). The native VMS environment does not support
1787 * '>' and '<' I/O redirection, or command line wild card expansion,
1788 * or a command line pipe mechanism using the '|' AND background
1789 * command execution '&'. All of these capabilities are provided to any
1790 * C program which calls this procedure as the first thing in the
1792 * The piping mechanism will probably work with almost any 'filter' type
1793 * of program. With suitable modification, it may useful for other
1794 * portability problems as well.
1796 * Author: Mark Pizzolato mark@infocomm.com
1800 struct list_item *next;
1804 static void add_item(struct list_item **head,
1805 struct list_item **tail,
1809 static void expand_wild_cards(char *item,
1810 struct list_item **head,
1811 struct list_item **tail,
1814 static int background_process(int argc, char **argv);
1816 static void pipe_and_fork(char **cmargv);
1818 /*{{{ void getredirection(int *ac, char ***av)*/
1820 getredirection(int *ac, char ***av)
1822 * Process vms redirection arg's. Exit if any error is seen.
1823 * If getredirection() processes an argument, it is erased
1824 * from the vector. getredirection() returns a new argc and argv value.
1825 * In the event that a background command is requested (by a trailing "&"),
1826 * this routine creates a background subprocess, and simply exits the program.
1828 * Warning: do not try to simplify the code for vms. The code
1829 * presupposes that getredirection() is called before any data is
1830 * read from stdin or written to stdout.
1832 * Normal usage is as follows:
1838 * getredirection(&argc, &argv);
1842 int argc = *ac; /* Argument Count */
1843 char **argv = *av; /* Argument Vector */
1844 char *ap; /* Argument pointer */
1845 int j; /* argv[] index */
1846 int item_count = 0; /* Count of Items in List */
1847 struct list_item *list_head = 0; /* First Item in List */
1848 struct list_item *list_tail; /* Last Item in List */
1849 char *in = NULL; /* Input File Name */
1850 char *out = NULL; /* Output File Name */
1851 char *outmode = "w"; /* Mode to Open Output File */
1852 char *err = NULL; /* Error File Name */
1853 char *errmode = "w"; /* Mode to Open Error File */
1854 int cmargc = 0; /* Piped Command Arg Count */
1855 char **cmargv = NULL;/* Piped Command Arg Vector */
1858 * First handle the case where the last thing on the line ends with
1859 * a '&'. This indicates the desire for the command to be run in a
1860 * subprocess, so we satisfy that desire.
1863 if (0 == strcmp("&", ap))
1864 exit(background_process(--argc, argv));
1865 if (*ap && '&' == ap[strlen(ap)-1])
1867 ap[strlen(ap)-1] = '\0';
1868 exit(background_process(argc, argv));
1871 * Now we handle the general redirection cases that involve '>', '>>',
1872 * '<', and pipes '|'.
1874 for (j = 0; j < argc; ++j)
1876 if (0 == strcmp("<", argv[j]))
1880 PerlIO_printf(Perl_debug_log,"No input file after < on command line");
1881 exit(LIB$_WRONUMARG);
1886 if ('<' == *(ap = argv[j]))
1891 if (0 == strcmp(">", ap))
1895 PerlIO_printf(Perl_debug_log,"No output file after > on command line");
1896 exit(LIB$_WRONUMARG);
1915 PerlIO_printf(Perl_debug_log,"No output file after > or >> on command line");
1916 exit(LIB$_WRONUMARG);
1920 if (('2' == *ap) && ('>' == ap[1]))
1937 PerlIO_printf(Perl_debug_log,"No output file after 2> or 2>> on command line");
1938 exit(LIB$_WRONUMARG);
1942 if (0 == strcmp("|", argv[j]))
1946 PerlIO_printf(Perl_debug_log,"No command into which to pipe on command line");
1947 exit(LIB$_WRONUMARG);
1949 cmargc = argc-(j+1);
1950 cmargv = &argv[j+1];
1954 if ('|' == *(ap = argv[j]))
1962 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
1965 * Allocate and fill in the new argument vector, Some Unix's terminate
1966 * the list with an extra null pointer.
1968 New(1302, argv, item_count+1, char *);
1970 for (j = 0; j < item_count; ++j, list_head = list_head->next)
1971 argv[j] = list_head->value;
1977 PerlIO_printf(Perl_debug_log,"'|' and '>' may not both be specified on command line");
1978 exit(LIB$_INVARGORD);
1980 pipe_and_fork(cmargv);
1983 /* Check for input from a pipe (mailbox) */
1985 if (in == NULL && 1 == isapipe(0))
1987 char mbxname[L_tmpnam];
1989 long int dvi_item = DVI$_DEVBUFSIZ;
1990 $DESCRIPTOR(mbxnam, "");
1991 $DESCRIPTOR(mbxdevnam, "");
1993 /* Input from a pipe, reopen it in binary mode to disable */
1994 /* carriage control processing. */
1996 PerlIO_getname(stdin, mbxname);
1997 mbxnam.dsc$a_pointer = mbxname;
1998 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
1999 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
2000 mbxdevnam.dsc$a_pointer = mbxname;
2001 mbxdevnam.dsc$w_length = sizeof(mbxname);
2002 dvi_item = DVI$_DEVNAM;
2003 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
2004 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
2007 freopen(mbxname, "rb", stdin);
2010 PerlIO_printf(Perl_debug_log,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
2014 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
2016 PerlIO_printf(Perl_debug_log,"Can't open input file %s as stdin",in);
2019 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
2021 PerlIO_printf(Perl_debug_log,"Can't open output file %s as stdout",out);
2026 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
2028 PerlIO_printf(Perl_debug_log,"Can't open error file %s as stderr",err);
2032 if (NULL == freopen(err, "a", Perl_debug_log, "mbc=32", "mbf=2"))
2037 #ifdef ARGPROC_DEBUG
2038 PerlIO_printf(Perl_debug_log, "Arglist:\n");
2039 for (j = 0; j < *ac; ++j)
2040 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
2042 /* Clear errors we may have hit expanding wildcards, so they don't
2043 show up in Perl's $! later */
2044 set_errno(0); set_vaxc_errno(1);
2045 } /* end of getredirection() */
2048 static void add_item(struct list_item **head,
2049 struct list_item **tail,
2055 New(1303,*head,1,struct list_item);
2059 New(1304,(*tail)->next,1,struct list_item);
2060 *tail = (*tail)->next;
2062 (*tail)->value = value;
2066 static void expand_wild_cards(char *item,
2067 struct list_item **head,
2068 struct list_item **tail,
2072 unsigned long int context = 0;
2078 char vmsspec[NAM$C_MAXRSS+1];
2079 $DESCRIPTOR(filespec, "");
2080 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
2081 $DESCRIPTOR(resultspec, "");
2082 unsigned long int zero = 0, sts;
2084 if (strcspn(item, "*%") == strlen(item) || strchr(item,' ') != NULL)
2086 add_item(head, tail, item, count);
2089 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
2090 resultspec.dsc$b_class = DSC$K_CLASS_D;
2091 resultspec.dsc$a_pointer = NULL;
2092 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
2093 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
2094 if (!isunix || !filespec.dsc$a_pointer)
2095 filespec.dsc$a_pointer = item;
2096 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
2098 * Only return version specs, if the caller specified a version
2100 had_version = strchr(item, ';');
2102 * Only return device and directory specs, if the caller specifed either.
2104 had_device = strchr(item, ':');
2105 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
2107 while (1 == (1 & (sts = lib$find_file(&filespec, &resultspec, &context,
2108 &defaultspec, 0, 0, &zero))))
2113 New(1305,string,resultspec.dsc$w_length+1,char);
2114 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
2115 string[resultspec.dsc$w_length] = '\0';
2116 if (NULL == had_version)
2117 *((char *)strrchr(string, ';')) = '\0';
2118 if ((!had_directory) && (had_device == NULL))
2120 if (NULL == (devdir = strrchr(string, ']')))
2121 devdir = strrchr(string, '>');
2122 strcpy(string, devdir + 1);
2125 * Be consistent with what the C RTL has already done to the rest of
2126 * the argv items and lowercase all of these names.
2128 for (c = string; *c; ++c)
2131 if (isunix) trim_unixpath(string,item,1);
2132 add_item(head, tail, string, count);
2135 if (sts != RMS$_NMF)
2137 set_vaxc_errno(sts);
2143 set_errno(ENOENT); break;
2145 set_errno(ENODEV); break;
2148 set_errno(EINVAL); break;
2150 set_errno(EACCES); break;
2152 _ckvmssts_noperl(sts);
2156 add_item(head, tail, item, count);
2157 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
2158 _ckvmssts_noperl(lib$find_file_end(&context));
2161 static int child_st[2];/* Event Flag set when child process completes */
2163 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
2165 static unsigned long int exit_handler(int *status)
2169 if (0 == child_st[0])
2171 #ifdef ARGPROC_DEBUG
2172 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
2174 fflush(stdout); /* Have to flush pipe for binary data to */
2175 /* terminate properly -- <tp@mccall.com> */
2176 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
2177 sys$dassgn(child_chan);
2179 sys$synch(0, child_st);
2184 static void sig_child(int chan)
2186 #ifdef ARGPROC_DEBUG
2187 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
2189 if (child_st[0] == 0)
2193 static struct exit_control_block exit_block =
2198 &exit_block.exit_status,
2202 static void pipe_and_fork(char **cmargv)
2205 $DESCRIPTOR(cmddsc, "");
2206 static char mbxname[64];
2207 $DESCRIPTOR(mbxdsc, mbxname);
2209 unsigned long int zero = 0, one = 1;
2211 strcpy(subcmd, cmargv[0]);
2212 for (j = 1; NULL != cmargv[j]; ++j)
2214 strcat(subcmd, " \"");
2215 strcat(subcmd, cmargv[j]);
2216 strcat(subcmd, "\"");
2218 cmddsc.dsc$a_pointer = subcmd;
2219 cmddsc.dsc$w_length = strlen(cmddsc.dsc$a_pointer);
2221 create_mbx(&child_chan,&mbxdsc);
2222 #ifdef ARGPROC_DEBUG
2223 PerlIO_printf(Perl_debug_log, "Pipe Mailbox Name = '%s'\n", mbxdsc.dsc$a_pointer);
2224 PerlIO_printf(Perl_debug_log, "Sub Process Command = '%s'\n", cmddsc.dsc$a_pointer);
2226 _ckvmssts_noperl(lib$spawn(&cmddsc, &mbxdsc, 0, &one,
2227 0, &pid, child_st, &zero, sig_child,
2229 #ifdef ARGPROC_DEBUG
2230 PerlIO_printf(Perl_debug_log, "Subprocess's Pid = %08X\n", pid);
2232 sys$dclexh(&exit_block);
2233 if (NULL == freopen(mbxname, "wb", stdout))
2235 PerlIO_printf(Perl_debug_log,"Can't open output pipe (name %s)",mbxname);
2239 static int background_process(int argc, char **argv)
2241 char command[2048] = "$";
2242 $DESCRIPTOR(value, "");
2243 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
2244 static $DESCRIPTOR(null, "NLA0:");
2245 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
2247 $DESCRIPTOR(pidstr, "");
2249 unsigned long int flags = 17, one = 1, retsts;
2251 strcat(command, argv[0]);
2254 strcat(command, " \"");
2255 strcat(command, *(++argv));
2256 strcat(command, "\"");
2258 value.dsc$a_pointer = command;
2259 value.dsc$w_length = strlen(value.dsc$a_pointer);
2260 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
2261 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
2262 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
2263 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
2266 _ckvmssts_noperl(retsts);
2268 #ifdef ARGPROC_DEBUG
2269 PerlIO_printf(Perl_debug_log, "%s\n", command);
2271 sprintf(pidstring, "%08X", pid);
2272 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
2273 pidstr.dsc$a_pointer = pidstring;
2274 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
2275 lib$set_symbol(&pidsymbol, &pidstr);
2279 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
2282 /* OS-specific initialization at image activation (not thread startup) */
2283 /* Older VAXC header files lack these constants */
2284 #ifndef JPI$_RIGHTS_SIZE
2285 # define JPI$_RIGHTS_SIZE 817
2287 #ifndef KGB$M_SUBSYSTEM
2288 # define KGB$M_SUBSYSTEM 0x8
2291 /*{{{void vms_image_init(int *, char ***)*/
2293 vms_image_init(int *argcp, char ***argvp)
2295 unsigned long int *mask, iosb[2], i, rlst[128], rsz, add_taint = FALSE;
2296 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
2297 unsigned short int dummy, rlen;
2298 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
2299 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
2300 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
2303 _ckvmssts(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
2305 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
2306 if (iprv[i]) { /* Running image installed with privs? */
2307 _ckvmssts(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
2312 /* Rights identifiers might trigger tainting as well. */
2313 if (!add_taint && (rlen || rsz)) {
2314 while (rlen < rsz) {
2315 /* We didn't get all the identifiers on the first pass. Allocate a
2316 * buffer much larger than $GETJPI wants (rsz is size in bytes that
2317 * were needed to hold all identifiers at time of last call; we'll
2318 * allocate that many unsigned long ints), and go back and get 'em.
2320 if (jpilist[1].bufadr != rlst) Safefree(jpilist[1].bufadr);
2321 jpilist[1].bufadr = New(1320,mask,rsz,unsigned long int);
2322 jpilist[1].buflen = rsz * sizeof(unsigned long int);
2323 _ckvmssts(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
2326 mask = jpilist[1].bufadr;
2327 /* Check attribute flags for each identifier (2nd longword); protected
2328 * subsystem identifiers trigger tainting.
2330 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
2331 if (mask[i] & KGB$M_SUBSYSTEM) {
2336 if (mask != rlst) Safefree(mask);
2338 /* We need to use this hack to tell Perl it should run with tainting,
2339 * since its tainting flag may be part of the curinterp struct, which
2340 * hasn't been allocated when vms_image_init() is called.
2344 New(1320,newap,*argcp+2,char **);
2345 newap[0] = argvp[0];
2347 Copy(argvp[1],newap[2],*argcp-1,char **);
2348 /* We orphan the old argv, since we don't know where it's come from,
2349 * so we don't know how to free it.
2351 *argcp++; argvp = newap;
2353 getredirection(argcp,argvp);
2354 #if defined(USE_THREADS) && defined(__DECC)
2356 # include <reentrancy.h>
2357 (void) decc$set_reentrancy(C$C_MULTITHREAD);
2366 * Trim Unix-style prefix off filespec, so it looks like what a shell
2367 * glob expansion would return (i.e. from specified prefix on, not
2368 * full path). Note that returned filespec is Unix-style, regardless
2369 * of whether input filespec was VMS-style or Unix-style.
2371 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
2372 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
2373 * vector of options; at present, only bit 0 is used, and if set tells
2374 * trim unixpath to try the current default directory as a prefix when
2375 * presented with a possibly ambiguous ... wildcard.
2377 * Returns !=0 on success, with trimmed filespec replacing contents of
2378 * fspec, and 0 on failure, with contents of fpsec unchanged.
2380 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
2382 trim_unixpath(char *fspec, char *wildspec, int opts)
2384 char unixified[NAM$C_MAXRSS+1], unixwild[NAM$C_MAXRSS+1],
2385 *template, *base, *end, *cp1, *cp2;
2386 register int tmplen, reslen = 0, dirs = 0;
2388 if (!wildspec || !fspec) return 0;
2389 if (strpbrk(wildspec,"]>:") != NULL) {
2390 if (do_tounixspec(wildspec,unixwild,0) == NULL) return 0;
2391 else template = unixwild;
2393 else template = wildspec;
2394 if (strpbrk(fspec,"]>:") != NULL) {
2395 if (do_tounixspec(fspec,unixified,0) == NULL) return 0;
2396 else base = unixified;
2397 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
2398 * check to see that final result fits into (isn't longer than) fspec */
2399 reslen = strlen(fspec);
2403 /* No prefix or absolute path on wildcard, so nothing to remove */
2404 if (!*template || *template == '/') {
2405 if (base == fspec) return 1;
2406 tmplen = strlen(unixified);
2407 if (tmplen > reslen) return 0; /* not enough space */
2408 /* Copy unixified resultant, including trailing NUL */
2409 memmove(fspec,unixified,tmplen+1);
2413 for (end = base; *end; end++) ; /* Find end of resultant filespec */
2414 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
2415 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
2416 for (cp1 = end ;cp1 >= base; cp1--)
2417 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
2419 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
2423 char tpl[NAM$C_MAXRSS+1], lcres[NAM$C_MAXRSS+1];
2424 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
2425 int ells = 1, totells, segdirs, match;
2426 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, tpl},
2427 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
2429 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
2431 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
2432 if (ellipsis == template && opts & 1) {
2433 /* Template begins with an ellipsis. Since we can't tell how many
2434 * directory names at the front of the resultant to keep for an
2435 * arbitrary starting point, we arbitrarily choose the current
2436 * default directory as a starting point. If it's there as a prefix,
2437 * clip it off. If not, fall through and act as if the leading
2438 * ellipsis weren't there (i.e. return shortest possible path that
2439 * could match template).
2441 if (getcwd(tpl, sizeof tpl,0) == NULL) return 0;
2442 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
2443 if (_tolower(*cp1) != _tolower(*cp2)) break;
2444 segdirs = dirs - totells; /* Min # of dirs we must have left */
2445 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
2446 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
2447 memcpy(fspec,cp2+1,end - cp2);
2451 /* First off, back up over constant elements at end of path */
2453 for (front = end ; front >= base; front--)
2454 if (*front == '/' && !dirs--) { front++; break; }
2456 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + sizeof lcres;
2457 cp1++,cp2++) *cp2 = _tolower(*cp1); /* Make lc copy for match */
2458 if (cp1 != '\0') return 0; /* Path too long. */
2460 *cp2 = '\0'; /* Pick up with memcpy later */
2461 lcfront = lcres + (front - base);
2462 /* Now skip over each ellipsis and try to match the path in front of it. */
2464 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
2465 if (*(cp1) == '.' && *(cp1+1) == '.' &&
2466 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
2467 if (cp1 < template) break; /* template started with an ellipsis */
2468 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
2469 ellipsis = cp1; continue;
2471 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
2473 for (segdirs = 0, cp2 = tpl;
2474 cp1 <= ellipsis - 1 && cp2 <= tpl + sizeof tpl;
2476 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
2477 else *cp2 = _tolower(*cp1); /* else lowercase for match */
2478 if (*cp2 == '/') segdirs++;
2480 if (cp1 != ellipsis - 1) return 0; /* Path too long */
2481 /* Back up at least as many dirs as in template before matching */
2482 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
2483 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
2484 for (match = 0; cp1 > lcres;) {
2485 resdsc.dsc$a_pointer = cp1;
2486 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
2488 if (match == 1) lcfront = cp1;
2490 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
2492 if (!match) return 0; /* Can't find prefix ??? */
2493 if (match > 1 && opts & 1) {
2494 /* This ... wildcard could cover more than one set of dirs (i.e.
2495 * a set of similar dir names is repeated). If the template
2496 * contains more than 1 ..., upstream elements could resolve the
2497 * ambiguity, but it's not worth a full backtracking setup here.
2498 * As a quick heuristic, clip off the current default directory
2499 * if it's present to find the trimmed spec, else use the
2500 * shortest string that this ... could cover.
2502 char def[NAM$C_MAXRSS+1], *st;
2504 if (getcwd(def, sizeof def,0) == NULL) return 0;
2505 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
2506 if (_tolower(*cp1) != _tolower(*cp2)) break;
2507 segdirs = dirs - totells; /* Min # of dirs we must have left */
2508 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
2509 if (*cp1 == '\0' && *cp2 == '/') {
2510 memcpy(fspec,cp2+1,end - cp2);
2513 /* Nope -- stick with lcfront from above and keep going. */
2516 memcpy(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
2521 } /* end of trim_unixpath() */
2526 * VMS readdir() routines.
2527 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
2529 * 21-Jul-1994 Charles Bailey bailey@genetics.upenn.edu
2530 * Minor modifications to original routines.
2533 /* Number of elements in vms_versions array */
2534 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
2537 * Open a directory, return a handle for later use.
2539 /*{{{ DIR *opendir(char*name) */
2544 char dir[NAM$C_MAXRSS+1];
2547 if (do_tovmspath(name,dir,0) == NULL) {
2550 if (flex_stat(dir,&sb) == -1) return NULL;
2551 if (!S_ISDIR(sb.st_mode)) {
2552 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
2555 if (!cando_by_name(S_IRUSR,0,dir)) {
2556 set_errno(EACCES); set_vaxc_errno(RMS$_PRV);
2559 /* Get memory for the handle, and the pattern. */
2561 New(1307,dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
2563 /* Fill in the fields; mainly playing with the descriptor. */
2564 (void)sprintf(dd->pattern, "%s*.*",dir);
2567 dd->vms_wantversions = 0;
2568 dd->pat.dsc$a_pointer = dd->pattern;
2569 dd->pat.dsc$w_length = strlen(dd->pattern);
2570 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
2571 dd->pat.dsc$b_class = DSC$K_CLASS_S;
2574 } /* end of opendir() */
2578 * Set the flag to indicate we want versions or not.
2580 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
2582 vmsreaddirversions(DIR *dd, int flag)
2584 dd->vms_wantversions = flag;
2589 * Free up an opened directory.
2591 /*{{{ void closedir(DIR *dd)*/
2595 (void)lib$find_file_end(&dd->context);
2596 Safefree(dd->pattern);
2597 Safefree((char *)dd);
2602 * Collect all the version numbers for the current file.
2608 struct dsc$descriptor_s pat;
2609 struct dsc$descriptor_s res;
2611 char *p, *text, buff[sizeof dd->entry.d_name];
2613 unsigned long context, tmpsts;
2615 /* Convenient shorthand. */
2618 /* Add the version wildcard, ignoring the "*.*" put on before */
2619 i = strlen(dd->pattern);
2620 New(1308,text,i + e->d_namlen + 3,char);
2621 (void)strcpy(text, dd->pattern);
2622 (void)sprintf(&text[i - 3], "%s;*", e->d_name);
2624 /* Set up the pattern descriptor. */
2625 pat.dsc$a_pointer = text;
2626 pat.dsc$w_length = i + e->d_namlen - 1;
2627 pat.dsc$b_dtype = DSC$K_DTYPE_T;
2628 pat.dsc$b_class = DSC$K_CLASS_S;
2630 /* Set up result descriptor. */
2631 res.dsc$a_pointer = buff;
2632 res.dsc$w_length = sizeof buff - 2;
2633 res.dsc$b_dtype = DSC$K_DTYPE_T;
2634 res.dsc$b_class = DSC$K_CLASS_S;
2636 /* Read files, collecting versions. */
2637 for (context = 0, e->vms_verscount = 0;
2638 e->vms_verscount < VERSIZE(e);
2639 e->vms_verscount++) {
2640 tmpsts = lib$find_file(&pat, &res, &context);
2641 if (tmpsts == RMS$_NMF || context == 0) break;
2643 buff[sizeof buff - 1] = '\0';
2644 if ((p = strchr(buff, ';')))
2645 e->vms_versions[e->vms_verscount] = atoi(p + 1);
2647 e->vms_versions[e->vms_verscount] = -1;
2650 _ckvmssts(lib$find_file_end(&context));
2653 } /* end of collectversions() */
2656 * Read the next entry from the directory.
2658 /*{{{ struct dirent *readdir(DIR *dd)*/
2662 struct dsc$descriptor_s res;
2663 char *p, buff[sizeof dd->entry.d_name];
2664 unsigned long int tmpsts;
2666 /* Set up result descriptor, and get next file. */
2667 res.dsc$a_pointer = buff;
2668 res.dsc$w_length = sizeof buff - 2;
2669 res.dsc$b_dtype = DSC$K_DTYPE_T;
2670 res.dsc$b_class = DSC$K_CLASS_S;
2671 tmpsts = lib$find_file(&dd->pat, &res, &dd->context);
2672 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
2673 if (!(tmpsts & 1)) {
2674 set_vaxc_errno(tmpsts);
2677 set_errno(EACCES); break;
2679 set_errno(ENODEV); break;
2682 set_errno(ENOENT); break;
2689 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
2690 buff[sizeof buff - 1] = '\0';
2691 for (p = buff; !isspace(*p); p++) *p = _tolower(*p);
2694 /* Skip any directory component and just copy the name. */
2695 if ((p = strchr(buff, ']'))) (void)strcpy(dd->entry.d_name, p + 1);
2696 else (void)strcpy(dd->entry.d_name, buff);
2698 /* Clobber the version. */
2699 if ((p = strchr(dd->entry.d_name, ';'))) *p = '\0';
2701 dd->entry.d_namlen = strlen(dd->entry.d_name);
2702 dd->entry.vms_verscount = 0;
2703 if (dd->vms_wantversions) collectversions(dd);
2706 } /* end of readdir() */
2710 * Return something that can be used in a seekdir later.
2712 /*{{{ long telldir(DIR *dd)*/
2721 * Return to a spot where we used to be. Brute force.
2723 /*{{{ void seekdir(DIR *dd,long count)*/
2725 seekdir(DIR *dd, long count)
2727 int vms_wantversions;
2729 /* If we haven't done anything yet... */
2733 /* Remember some state, and clear it. */
2734 vms_wantversions = dd->vms_wantversions;
2735 dd->vms_wantversions = 0;
2736 _ckvmssts(lib$find_file_end(&dd->context));
2739 /* The increment is in readdir(). */
2740 for (dd->count = 0; dd->count < count; )
2743 dd->vms_wantversions = vms_wantversions;
2745 } /* end of seekdir() */
2748 /* VMS subprocess management
2750 * my_vfork() - just a vfork(), after setting a flag to record that
2751 * the current script is trying a Unix-style fork/exec.
2753 * vms_do_aexec() and vms_do_exec() are called in response to the
2754 * perl 'exec' function. If this follows a vfork call, then they
2755 * call out the the regular perl routines in doio.c which do an
2756 * execvp (for those who really want to try this under VMS).
2757 * Otherwise, they do exactly what the perl docs say exec should
2758 * do - terminate the current script and invoke a new command
2759 * (See below for notes on command syntax.)
2761 * do_aspawn() and do_spawn() implement the VMS side of the perl
2762 * 'system' function.
2764 * Note on command arguments to perl 'exec' and 'system': When handled
2765 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
2766 * are concatenated to form a DCL command string. If the first arg
2767 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
2768 * the the command string is hrnded off to DCL directly. Otherwise,
2769 * the first token of the command is taken as the filespec of an image
2770 * to run. The filespec is expanded using a default type of '.EXE' and
2771 * the process defaults for device, directory, etc., and the resultant
2772 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
2773 * the command string as parameters. This is perhaps a bit compicated,
2774 * but I hope it will form a happy medium between what VMS folks expect
2775 * from lib$spawn and what Unix folks expect from exec.
2778 static int vfork_called;
2780 /*{{{int my_vfork()*/
2790 static struct dsc$descriptor_s VMScmd = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,Nullch};
2798 if (VMScmd.dsc$a_pointer) {
2799 Safefree(VMScmd.dsc$a_pointer);
2800 VMScmd.dsc$w_length = 0;
2801 VMScmd.dsc$a_pointer = Nullch;
2806 setup_argstr(SV *really, SV **mark, SV **sp)
2809 char *junk, *tmps = Nullch;
2810 register size_t cmdlen = 0;
2816 tmps = SvPV(really,rlen);
2823 for (idx++; idx <= sp; idx++) {
2825 junk = SvPVx(*idx,rlen);
2826 cmdlen += rlen ? rlen + 1 : 0;
2829 New(401,Cmd,cmdlen+1,char);
2831 if (tmps && *tmps) {
2836 while (++mark <= sp) {
2839 strcat(Cmd,SvPVx(*mark,na));
2844 } /* end of setup_argstr() */
2847 static unsigned long int
2848 setup_cmddsc(char *cmd, int check_img)
2850 char resspec[NAM$C_MAXRSS+1];
2851 $DESCRIPTOR(defdsc,".EXE");
2852 $DESCRIPTOR(resdsc,resspec);
2853 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
2854 unsigned long int cxt = 0, flags = 1, retsts;
2855 register char *s, *rest, *cp;
2856 register int isdcl = 0;
2859 while (*s && isspace(*s)) s++;
2861 if (*s == '$') { /* Check whether this is a DCL command: leading $ and */
2862 isdcl = 1; /* no dev/dir separators (i.e. not a foreign command) */
2863 for (cp = s; *cp && *cp != '/' && !isspace(*cp); cp++) {
2864 if (*cp == ':' || *cp == '[' || *cp == '<') {
2872 if (isdcl) { /* It's a DCL command, just do it. */
2873 VMScmd.dsc$w_length = strlen(cmd);
2875 VMScmd.dsc$a_pointer = Cmd;
2876 Cmd = Nullch; /* Don't try to free twice in vms_execfree() */
2878 else VMScmd.dsc$a_pointer = savepvn(cmd,VMScmd.dsc$w_length);
2880 else { /* assume first token is an image spec */
2882 while (*s && !isspace(*s)) s++;
2884 imgdsc.dsc$a_pointer = cmd;
2885 imgdsc.dsc$w_length = s - cmd;
2886 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
2887 if (!(retsts & 1)) {
2888 /* just hand off status values likely to be due to user error */
2889 if (retsts == RMS$_FNF || retsts == RMS$_DNF ||
2890 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
2891 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
2892 else { _ckvmssts(retsts); }
2895 _ckvmssts(lib$find_file_end(&cxt));
2897 while (*s && !isspace(*s)) s++;
2899 if (!cando_by_name(S_IXUSR,0,resspec)) return RMS$_PRV;
2900 New(402,VMScmd.dsc$a_pointer,7 + s - resspec + (rest ? strlen(rest) : 0),char);
2901 strcpy(VMScmd.dsc$a_pointer,"$ MCR ");
2902 strcat(VMScmd.dsc$a_pointer,resspec);
2903 if (rest) strcat(VMScmd.dsc$a_pointer,rest);
2904 VMScmd.dsc$w_length = strlen(VMScmd.dsc$a_pointer);
2908 return (VMScmd.dsc$w_length > 255 ? CLI$_BUFOVF : SS$_NORMAL);
2910 } /* end of setup_cmddsc() */
2913 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
2915 vms_do_aexec(SV *really,SV **mark,SV **sp)
2919 if (vfork_called) { /* this follows a vfork - act Unixish */
2921 if (vfork_called < 0) {
2922 warn("Internal inconsistency in tracking vforks");
2925 else return do_aexec(really,mark,sp);
2927 /* no vfork - act VMSish */
2928 return vms_do_exec(setup_argstr(really,mark,sp));
2933 } /* end of vms_do_aexec() */
2936 /* {{{bool vms_do_exec(char *cmd) */
2938 vms_do_exec(char *cmd)
2941 if (vfork_called) { /* this follows a vfork - act Unixish */
2943 if (vfork_called < 0) {
2944 warn("Internal inconsistency in tracking vforks");
2947 else return do_exec(cmd);
2950 { /* no vfork - act VMSish */
2951 unsigned long int retsts;
2954 TAINT_PROPER("exec");
2955 if ((retsts = setup_cmddsc(cmd,1)) & 1)
2956 retsts = lib$do_command(&VMScmd);
2960 set_errno(ENOENT); break;
2961 case RMS$_DNF: case RMS$_DIR: case RMS$_DEV:
2962 set_errno(ENOTDIR); break;
2964 set_errno(EACCES); break;
2966 set_errno(EINVAL); break;
2968 set_errno(E2BIG); break;
2969 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
2970 _ckvmssts(retsts); /* fall through */
2971 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
2974 set_vaxc_errno(retsts);
2976 warn("Can't exec \"%s\": %s", VMScmd.dsc$a_pointer, Strerror(errno));
2982 } /* end of vms_do_exec() */
2985 unsigned long int do_spawn(char *);
2987 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
2989 do_aspawn(void *really,void **mark,void **sp)
2992 if (sp > mark) return do_spawn(setup_argstr((SV *)really,(SV **)mark,(SV **)sp));
2995 } /* end of do_aspawn() */
2998 /* {{{unsigned long int do_spawn(char *cmd) */
3002 unsigned long int sts, substs, hadcmd = 1;
3005 TAINT_PROPER("spawn");
3006 if (!cmd || !*cmd) {
3008 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
3010 else if ((sts = setup_cmddsc(cmd,0)) & 1) {
3011 sts = lib$spawn(&VMScmd,0,0,0,0,0,&substs,0,0,0,0,0,0);
3017 set_errno(ENOENT); break;
3018 case RMS$_DNF: case RMS$_DIR: case RMS$_DEV:
3019 set_errno(ENOTDIR); break;
3021 set_errno(EACCES); break;
3023 set_errno(EINVAL); break;
3025 set_errno(E2BIG); break;
3026 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
3027 _ckvmssts(sts); /* fall through */
3028 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
3031 set_vaxc_errno(sts);
3033 warn("Can't spawn \"%s\": %s",
3034 hadcmd ? VMScmd.dsc$a_pointer : "", Strerror(errno));
3039 } /* end of do_spawn() */
3043 * A simple fwrite replacement which outputs itmsz*nitm chars without
3044 * introducing record boundaries every itmsz chars.
3046 /*{{{ int my_fwrite(void *src, size_t itmsz, size_t nitm, FILE *dest)*/
3048 my_fwrite(void *src, size_t itmsz, size_t nitm, FILE *dest)
3050 register char *cp, *end;
3052 end = (char *)src + itmsz * nitm;
3054 while ((char *)src <= end) {
3055 for (cp = src; cp <= end; cp++) if (!*cp) break;
3056 if (fputs(src,dest) == EOF) return EOF;
3058 if (fputc('\0',dest) == EOF) return EOF;
3064 } /* end of my_fwrite() */
3067 /*{{{ int my_flush(FILE *fp)*/
3072 if ((res = fflush(fp)) == 0) {
3073 #ifdef VMS_DO_SOCKETS
3075 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
3077 res = fsync(fileno(fp));
3084 * Here are replacements for the following Unix routines in the VMS environment:
3085 * getpwuid Get information for a particular UIC or UID
3086 * getpwnam Get information for a named user
3087 * getpwent Get information for each user in the rights database
3088 * setpwent Reset search to the start of the rights database
3089 * endpwent Finish searching for users in the rights database
3091 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
3092 * (defined in pwd.h), which contains the following fields:-
3094 * char *pw_name; Username (in lower case)
3095 * char *pw_passwd; Hashed password
3096 * unsigned int pw_uid; UIC
3097 * unsigned int pw_gid; UIC group number
3098 * char *pw_unixdir; Default device/directory (VMS-style)
3099 * char *pw_gecos; Owner name
3100 * char *pw_dir; Default device/directory (Unix-style)
3101 * char *pw_shell; Default CLI name (eg. DCL)
3103 * If the specified user does not exist, getpwuid and getpwnam return NULL.
3105 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
3106 * not the UIC member number (eg. what's returned by getuid()),
3107 * getpwuid() can accept either as input (if uid is specified, the caller's
3108 * UIC group is used), though it won't recognise gid=0.
3110 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
3111 * information about other users in your group or in other groups, respectively.
3112 * If the required privilege is not available, then these routines fill only
3113 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
3116 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
3119 /* sizes of various UAF record fields */
3120 #define UAI$S_USERNAME 12
3121 #define UAI$S_IDENT 31
3122 #define UAI$S_OWNER 31
3123 #define UAI$S_DEFDEV 31
3124 #define UAI$S_DEFDIR 63
3125 #define UAI$S_DEFCLI 31
3128 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
3129 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
3130 (uic).uic$v_group != UIC$K_WILD_GROUP)
3132 static char __empty[]= "";
3133 static struct passwd __passwd_empty=
3134 {(char *) __empty, (char *) __empty, 0, 0,
3135 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
3136 static int contxt= 0;
3137 static struct passwd __pwdcache;
3138 static char __pw_namecache[UAI$S_IDENT+1];
3141 * This routine does most of the work extracting the user information.
3143 static int fillpasswd (const char *name, struct passwd *pwd)
3146 unsigned char length;
3147 char pw_gecos[UAI$S_OWNER+1];
3149 static union uicdef uic;
3151 unsigned char length;
3152 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
3155 unsigned char length;
3156 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
3159 unsigned char length;
3160 char pw_shell[UAI$S_DEFCLI+1];
3162 static char pw_passwd[UAI$S_PWD+1];
3164 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
3165 struct dsc$descriptor_s name_desc;
3166 unsigned long int sts;
3168 static struct itmlst_3 itmlst[]= {
3169 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
3170 {sizeof(uic), UAI$_UIC, &uic, &luic},
3171 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
3172 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
3173 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
3174 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
3175 {0, 0, NULL, NULL}};
3177 name_desc.dsc$w_length= strlen(name);
3178 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
3179 name_desc.dsc$b_class= DSC$K_CLASS_S;
3180 name_desc.dsc$a_pointer= (char *) name;
3182 /* Note that sys$getuai returns many fields as counted strings. */
3183 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
3184 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
3185 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
3187 else { _ckvmssts(sts); }
3188 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
3190 if ((int) owner.length < lowner) lowner= (int) owner.length;
3191 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
3192 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
3193 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
3194 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
3195 owner.pw_gecos[lowner]= '\0';
3196 defdev.pw_dir[ldefdev+ldefdir]= '\0';
3197 defcli.pw_shell[ldefcli]= '\0';
3198 if (valid_uic(uic)) {
3199 pwd->pw_uid= uic.uic$l_uic;
3200 pwd->pw_gid= uic.uic$v_group;
3203 warn("getpwnam returned invalid UIC %#o for user \"%s\"");
3204 pwd->pw_passwd= pw_passwd;
3205 pwd->pw_gecos= owner.pw_gecos;
3206 pwd->pw_dir= defdev.pw_dir;
3207 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
3208 pwd->pw_shell= defcli.pw_shell;
3209 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
3211 ldir= strlen(pwd->pw_unixdir) - 1;
3212 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
3215 strcpy(pwd->pw_unixdir, pwd->pw_dir);
3216 __mystrtolower(pwd->pw_unixdir);
3221 * Get information for a named user.
3223 /*{{{struct passwd *getpwnam(char *name)*/
3224 struct passwd *my_getpwnam(char *name)
3226 struct dsc$descriptor_s name_desc;
3228 unsigned long int status, sts;
3230 __pwdcache = __passwd_empty;
3231 if (!fillpasswd(name, &__pwdcache)) {
3232 /* We still may be able to determine pw_uid and pw_gid */
3233 name_desc.dsc$w_length= strlen(name);
3234 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
3235 name_desc.dsc$b_class= DSC$K_CLASS_S;
3236 name_desc.dsc$a_pointer= (char *) name;
3237 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
3238 __pwdcache.pw_uid= uic.uic$l_uic;
3239 __pwdcache.pw_gid= uic.uic$v_group;
3242 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
3243 set_vaxc_errno(sts);
3244 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
3247 else { _ckvmssts(sts); }
3250 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
3251 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
3252 __pwdcache.pw_name= __pw_namecache;
3254 } /* end of my_getpwnam() */
3258 * Get information for a particular UIC or UID.
3259 * Called by my_getpwent with uid=-1 to list all users.
3261 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
3262 struct passwd *my_getpwuid(Uid_t uid)
3264 const $DESCRIPTOR(name_desc,__pw_namecache);
3265 unsigned short lname;
3267 unsigned long int status;
3269 if (uid == (unsigned int) -1) {
3271 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
3272 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
3273 set_vaxc_errno(status);
3274 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
3278 else { _ckvmssts(status); }
3279 } while (!valid_uic (uic));
3283 if (!uic.uic$v_group)
3284 uic.uic$v_group= getgid();
3286 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
3287 else status = SS$_IVIDENT;
3288 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
3289 status == RMS$_PRV) {
3290 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
3293 else { _ckvmssts(status); }
3295 __pw_namecache[lname]= '\0';
3296 __mystrtolower(__pw_namecache);
3298 __pwdcache = __passwd_empty;
3299 __pwdcache.pw_name = __pw_namecache;
3301 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
3302 The identifier's value is usually the UIC, but it doesn't have to be,
3303 so if we can, we let fillpasswd update this. */
3304 __pwdcache.pw_uid = uic.uic$l_uic;
3305 __pwdcache.pw_gid = uic.uic$v_group;
3307 fillpasswd(__pw_namecache, &__pwdcache);
3310 } /* end of my_getpwuid() */
3314 * Get information for next user.
3316 /*{{{struct passwd *my_getpwent()*/
3317 struct passwd *my_getpwent()
3319 return (my_getpwuid((unsigned int) -1));
3324 * Finish searching rights database for users.
3326 /*{{{void my_endpwent()*/
3330 _ckvmssts(sys$finish_rdb(&contxt));
3336 #ifdef HOMEGROWN_POSIX_SIGNALS
3337 /* Signal handling routines, pulled into the core from POSIX.xs.
3339 * We need these for threads, so they've been rolled into the core,
3340 * rather than left in POSIX.xs.
3342 * (DRS, Oct 23, 1997)
3345 /* sigset_t is atomic under VMS, so these routines are easy */
3346 /*{{{int my_sigemptyset(sigset_t *) */
3347 int my_sigemptyset(sigset_t *set) {
3348 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
3354 /*{{{int my_sigfillset(sigset_t *)*/
3355 int my_sigfillset(sigset_t *set) {
3357 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
3358 for (i = 0; i < NSIG; i++) *set |= (1 << i);
3364 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
3365 int my_sigaddset(sigset_t *set, int sig) {
3366 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
3367 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
3368 *set |= (1 << (sig - 1));
3374 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
3375 int my_sigdelset(sigset_t *set, int sig) {
3376 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
3377 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
3378 *set &= ~(1 << (sig - 1));
3384 /*{{{int my_sigismember(sigset_t *set, int sig)*/
3385 int my_sigismember(sigset_t *set, int sig) {
3386 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
3387 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
3388 *set & (1 << (sig - 1));
3393 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
3394 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
3397 /* If set and oset are both null, then things are badly wrong. Bail out. */
3398 if ((oset == NULL) && (set == NULL)) {
3399 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
3403 /* If set's null, then we're just handling a fetch. */
3405 tempmask = sigblock(0);
3410 tempmask = sigsetmask(*set);
3413 tempmask = sigblock(*set);
3416 tempmask = sigblock(0);
3417 sigsetmask(*oset & ~tempmask);
3420 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
3425 /* Did they pass us an oset? If so, stick our holding mask into it */
3432 #endif /* HOMEGROWN_POSIX_SIGNALS */
3435 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
3436 * my_utime(), and flex_stat(), all of which operate on UTC unless
3437 * VMSISH_TIMES is true.
3439 /* method used to handle UTC conversions:
3440 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
3442 static int gmtime_emulation_type;
3443 /* number of secs to add to UTC POSIX-style time to get local time */
3444 static long int utc_offset_secs;
3446 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
3447 * in vmsish.h. #undef them here so we can call the CRTL routines
3454 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
3455 # define RTL_USES_UTC 1
3458 static time_t toutc_dst(time_t loc) {
3461 if ((rsltmp = localtime(&loc)) == NULL) return -1;
3462 loc -= utc_offset_secs;
3463 if (rsltmp->tm_isdst) loc -= 3600;
3466 #define _toutc(secs) ((secs) == -1 ? -1 : \
3467 ((gmtime_emulation_type || my_time(NULL)), \
3468 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
3469 ((secs) - utc_offset_secs))))
3471 static time_t toloc_dst(time_t utc) {
3474 utc += utc_offset_secs;
3475 if ((rsltmp = localtime(&utc)) == NULL) return -1;
3476 if (rsltmp->tm_isdst) utc += 3600;
3479 #define _toloc(secs) ((secs) == -1 ? -1 : \
3480 ((gmtime_emulation_type || my_time(NULL)), \
3481 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
3482 ((secs) + utc_offset_secs))))
3485 /* my_time(), my_localtime(), my_gmtime()
3486 * By default traffic in UTC time values, using CRTL gmtime() or
3487 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
3488 * Note: We need to use these functions even when the CRTL has working
3489 * UTC support, since they also handle C<use vmsish qw(times);>
3491 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
3492 * Modified by Charles Bailey <bailey@genetics.upenn.edu>
3495 /*{{{time_t my_time(time_t *timep)*/
3496 time_t my_time(time_t *timep)
3502 if (gmtime_emulation_type == 0) {
3504 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
3505 /* results of calls to gmtime() and localtime() */
3506 /* for same &base */
3508 gmtime_emulation_type++;
3509 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
3512 gmtime_emulation_type++;
3513 if ((off = my_getenv("SYS$TIMEZONE_DIFFERENTIAL")) == NULL) {
3514 gmtime_emulation_type++;
3515 warn("no UTC offset information; assuming local time is UTC");
3517 else { utc_offset_secs = atol(off); }
3519 else { /* We've got a working gmtime() */
3520 struct tm gmt, local;
3523 tm_p = localtime(&base);
3525 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
3526 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
3527 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
3528 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
3534 # ifdef RTL_USES_UTC
3535 if (VMSISH_TIME) when = _toloc(when);
3537 if (!VMSISH_TIME) when = _toutc(when);
3540 if (timep != NULL) *timep = when;
3543 } /* end of my_time() */
3547 /*{{{struct tm *my_gmtime(const time_t *timep)*/
3549 my_gmtime(const time_t *timep)
3556 if (timep == NULL) {
3557 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
3560 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
3564 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
3566 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
3567 return gmtime(&when);
3569 /* CRTL localtime() wants local time as input, so does no tz correction */
3570 rsltmp = localtime(&when);
3571 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
3574 } /* end of my_gmtime() */
3578 /*{{{struct tm *my_localtime(const time_t *timep)*/
3580 my_localtime(const time_t *timep)
3586 if (timep == NULL) {
3587 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
3590 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
3591 if (gmtime_emulation_type == 0) (void) my_time(NULL); /* Init UTC */
3594 # ifdef RTL_USES_UTC
3596 if (VMSISH_TIME) when = _toutc(when);
3598 /* CRTL localtime() wants UTC as input, does tz correction itself */
3599 return localtime(&when);
3602 if (!VMSISH_TIME) when = _toloc(when); /* Input was UTC */
3605 /* CRTL localtime() wants local time as input, so does no tz correction */
3606 rsltmp = localtime(&when);
3607 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = -1;
3610 } /* end of my_localtime() */
3613 /* Reset definitions for later calls */
3614 #define gmtime(t) my_gmtime(t)
3615 #define localtime(t) my_localtime(t)
3616 #define time(t) my_time(t)
3619 /* my_utime - update modification time of a file
3620 * calling sequence is identical to POSIX utime(), but under
3621 * VMS only the modification time is changed; ODS-2 does not
3622 * maintain access times. Restrictions differ from the POSIX
3623 * definition in that the time can be changed as long as the
3624 * caller has permission to execute the necessary IO$_MODIFY $QIO;
3625 * no separate checks are made to insure that the caller is the
3626 * owner of the file or has special privs enabled.
3627 * Code here is based on Joe Meadows' FILE utility.
3630 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
3631 * to VMS epoch (01-JAN-1858 00:00:00.00)
3632 * in 100 ns intervals.
3634 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
3636 /*{{{int my_utime(char *path, struct utimbuf *utimes)*/
3637 int my_utime(char *file, struct utimbuf *utimes)
3641 long int bintime[2], len = 2, lowbit, unixtime,
3642 secscale = 10000000; /* seconds --> 100 ns intervals */
3643 unsigned long int chan, iosb[2], retsts;
3644 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
3645 struct FAB myfab = cc$rms_fab;
3646 struct NAM mynam = cc$rms_nam;
3647 #if defined (__DECC) && defined (__VAX)
3648 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
3649 * at least through VMS V6.1, which causes a type-conversion warning.
3651 # pragma message save
3652 # pragma message disable cvtdiftypes
3654 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
3655 struct fibdef myfib;
3656 #if defined (__DECC) && defined (__VAX)
3657 /* This should be right after the declaration of myatr, but due
3658 * to a bug in VAX DEC C, this takes effect a statement early.
3660 # pragma message restore
3662 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
3663 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
3664 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
3666 if (file == NULL || *file == '\0') {
3668 set_vaxc_errno(LIB$_INVARG);
3671 if (do_tovmsspec(file,vmsspec,0) == NULL) return -1;
3673 if (utimes != NULL) {
3674 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
3675 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
3676 * Since time_t is unsigned long int, and lib$emul takes a signed long int
3677 * as input, we force the sign bit to be clear by shifting unixtime right
3678 * one bit, then multiplying by an extra factor of 2 in lib$emul().
3680 lowbit = (utimes->modtime & 1) ? secscale : 0;
3681 unixtime = (long int) utimes->modtime;
3683 /* If input was UTC; convert to local for sys svc */
3684 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
3686 unixtime >> 1; secscale << 1;
3687 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
3688 if (!(retsts & 1)) {
3690 set_vaxc_errno(retsts);
3693 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
3694 if (!(retsts & 1)) {
3696 set_vaxc_errno(retsts);
3701 /* Just get the current time in VMS format directly */
3702 retsts = sys$gettim(bintime);
3703 if (!(retsts & 1)) {
3705 set_vaxc_errno(retsts);
3710 myfab.fab$l_fna = vmsspec;
3711 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
3712 myfab.fab$l_nam = &mynam;
3713 mynam.nam$l_esa = esa;
3714 mynam.nam$b_ess = (unsigned char) sizeof esa;
3715 mynam.nam$l_rsa = rsa;
3716 mynam.nam$b_rss = (unsigned char) sizeof rsa;
3718 /* Look for the file to be affected, letting RMS parse the file
3719 * specification for us as well. I have set errno using only
3720 * values documented in the utime() man page for VMS POSIX.
3722 retsts = sys$parse(&myfab,0,0);
3723 if (!(retsts & 1)) {
3724 set_vaxc_errno(retsts);
3725 if (retsts == RMS$_PRV) set_errno(EACCES);
3726 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
3727 else set_errno(EVMSERR);
3730 retsts = sys$search(&myfab,0,0);
3731 if (!(retsts & 1)) {
3732 set_vaxc_errno(retsts);
3733 if (retsts == RMS$_PRV) set_errno(EACCES);
3734 else if (retsts == RMS$_FNF) set_errno(ENOENT);
3735 else set_errno(EVMSERR);
3739 devdsc.dsc$w_length = mynam.nam$b_dev;
3740 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
3742 retsts = sys$assign(&devdsc,&chan,0,0);
3743 if (!(retsts & 1)) {
3744 set_vaxc_errno(retsts);
3745 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
3746 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
3747 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
3748 else set_errno(EVMSERR);
3752 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
3753 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
3755 memset((void *) &myfib, 0, sizeof myfib);
3757 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
3758 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
3759 /* This prevents the revision time of the file being reset to the current
3760 * time as a result of our IO$_MODIFY $QIO. */
3761 myfib.fib$l_acctl = FIB$M_NORECORD;
3763 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
3764 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
3765 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
3767 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
3768 _ckvmssts(sys$dassgn(chan));
3769 if (retsts & 1) retsts = iosb[0];
3770 if (!(retsts & 1)) {
3771 set_vaxc_errno(retsts);
3772 if (retsts == SS$_NOPRIV) set_errno(EACCES);
3773 else set_errno(EVMSERR);
3778 } /* end of my_utime() */
3782 * flex_stat, flex_fstat
3783 * basic stat, but gets it right when asked to stat
3784 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
3787 /* encode_dev packs a VMS device name string into an integer to allow
3788 * simple comparisons. This can be used, for example, to check whether two
3789 * files are located on the same device, by comparing their encoded device
3790 * names. Even a string comparison would not do, because stat() reuses the
3791 * device name buffer for each call; so without encode_dev, it would be
3792 * necessary to save the buffer and use strcmp (this would mean a number of
3793 * changes to the standard Perl code, to say nothing of what a Perl script
3796 * The device lock id, if it exists, should be unique (unless perhaps compared
3797 * with lock ids transferred from other nodes). We have a lock id if the disk is
3798 * mounted cluster-wide, which is when we tend to get long (host-qualified)
3799 * device names. Thus we use the lock id in preference, and only if that isn't
3800 * available, do we try to pack the device name into an integer (flagged by
3801 * the sign bit (LOCKID_MASK) being set).
3803 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
3804 * name and its encoded form, but it seems very unlikely that we will find
3805 * two files on different disks that share the same encoded device names,
3806 * and even more remote that they will share the same file id (if the test
3807 * is to check for the same file).
3809 * A better method might be to use sys$device_scan on the first call, and to
3810 * search for the device, returning an index into the cached array.
3811 * The number returned would be more intelligable.
3812 * This is probably not worth it, and anyway would take quite a bit longer
3813 * on the first call.
3815 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
3816 static mydev_t encode_dev (const char *dev)
3819 unsigned long int f;
3824 if (!dev || !dev[0]) return 0;
3828 struct dsc$descriptor_s dev_desc;
3829 unsigned long int status, lockid, item = DVI$_LOCKID;
3831 /* For cluster-mounted disks, the disk lock identifier is unique, so we
3832 can try that first. */
3833 dev_desc.dsc$w_length = strlen (dev);
3834 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
3835 dev_desc.dsc$b_class = DSC$K_CLASS_S;
3836 dev_desc.dsc$a_pointer = (char *) dev;
3837 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
3838 if (lockid) return (lockid & ~LOCKID_MASK);
3842 /* Otherwise we try to encode the device name */
3846 for (q = dev + strlen(dev); q--; q >= dev) {
3849 else if (isalpha (toupper (*q)))
3850 c= toupper (*q) - 'A' + (char)10;
3852 continue; /* Skip '$'s */
3854 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
3856 enc += f * (unsigned long int) c;
3858 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
3860 } /* end of encode_dev() */
3862 static char namecache[NAM$C_MAXRSS+1];
3865 is_null_device(name)
3868 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
3869 The underscore prefix, controller letter, and unit number are
3870 independently optional; for our purposes, the colon punctuation
3871 is not. The colon can be trailed by optional directory and/or
3872 filename, but two consecutive colons indicates a nodename rather
3873 than a device. [pr] */
3874 if (*name == '_') ++name;
3875 if (tolower(*name++) != 'n') return 0;
3876 if (tolower(*name++) != 'l') return 0;
3877 if (tolower(*name) == 'a') ++name;
3878 if (*name == '0') ++name;
3879 return (*name++ == ':') && (*name != ':');
3882 /* Do the permissions allow some operation? Assumes statcache already set. */
3883 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
3884 * subset of the applicable information.
3886 /*{{{I32 cando(I32 bit, I32 effective, struct stat *statbufp)*/
3888 cando(I32 bit, I32 effective, Stat_t *statbufp)
3890 if (statbufp == &statcache) return cando_by_name(bit,effective,namecache);
3892 char fname[NAM$C_MAXRSS+1];
3893 unsigned long int retsts;
3894 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
3895 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
3897 /* If the struct mystat is stale, we're OOL; stat() overwrites the
3898 device name on successive calls */
3899 devdsc.dsc$a_pointer = ((Stat_t *)statbufp)->st_devnam;
3900 devdsc.dsc$w_length = strlen(((Stat_t *)statbufp)->st_devnam);
3901 namdsc.dsc$a_pointer = fname;
3902 namdsc.dsc$w_length = sizeof fname - 1;
3904 retsts = lib$fid_to_name(&devdsc,&(((Stat_t *)statbufp)->st_ino),
3905 &namdsc,&namdsc.dsc$w_length,0,0);
3907 fname[namdsc.dsc$w_length] = '\0';
3908 return cando_by_name(bit,effective,fname);
3910 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
3911 warn("Can't get filespec - stale stat buffer?\n");
3915 return FALSE; /* Should never get to here */
3917 } /* end of cando() */
3921 /*{{{I32 cando_by_name(I32 bit, I32 effective, char *fname)*/
3923 cando_by_name(I32 bit, I32 effective, char *fname)
3925 static char usrname[L_cuserid];
3926 static struct dsc$descriptor_s usrdsc =
3927 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
3928 char vmsname[NAM$C_MAXRSS+1], fileified[NAM$C_MAXRSS+1];
3929 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
3930 unsigned short int retlen;
3931 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
3932 union prvdef curprv;
3933 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
3934 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
3935 struct itmlst_3 jpilst[2] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
3938 if (!fname || !*fname) return FALSE;
3939 /* Make sure we expand logical names, since sys$check_access doesn't */
3940 if (!strpbrk(fname,"/]>:")) {
3941 strcpy(fileified,fname);
3942 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) ;
3945 if (!do_tovmsspec(fname,vmsname,1)) return FALSE;
3946 retlen = namdsc.dsc$w_length = strlen(vmsname);
3947 namdsc.dsc$a_pointer = vmsname;
3948 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
3949 vmsname[retlen-1] == ':') {
3950 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
3951 namdsc.dsc$w_length = strlen(fileified);
3952 namdsc.dsc$a_pointer = fileified;
3955 if (!usrdsc.dsc$w_length) {
3957 usrdsc.dsc$w_length = strlen(usrname);
3964 access = ARM$M_EXECUTE;
3969 access = ARM$M_READ;
3974 access = ARM$M_WRITE;
3979 access = ARM$M_DELETE;
3985 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
3986 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
3987 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
3988 retsts == RMS$_DIR || retsts == RMS$_DEV) {
3989 set_vaxc_errno(retsts);
3990 if (retsts == SS$_NOPRIV) set_errno(EACCES);
3991 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
3992 else set_errno(ENOENT);
3995 if (retsts == SS$_NORMAL) {
3996 if (!privused) return TRUE;
3997 /* We can get access, but only by using privs. Do we have the
3998 necessary privs currently enabled? */
3999 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
4000 if ((privused & CHP$M_BYPASS) && !curprv.prv$v_bypass) return FALSE;
4001 if ((privused & CHP$M_SYSPRV) && !curprv.prv$v_sysprv &&
4002 !curprv.prv$v_bypass) return FALSE;
4003 if ((privused & CHP$M_GRPPRV) && !curprv.prv$v_grpprv &&
4004 !curprv.prv$v_sysprv && !curprv.prv$v_bypass) return FALSE;
4005 if ((privused & CHP$M_READALL) && !curprv.prv$v_readall) return FALSE;
4010 return FALSE; /* Should never get here */
4012 } /* end of cando_by_name() */
4016 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
4018 flex_fstat(int fd, Stat_t *statbufp)
4021 if (!fstat(fd,(stat_t *) statbufp)) {
4022 if (statbufp == (Stat_t *) &statcache) *namecache == '\0';
4023 statbufp->st_dev = encode_dev(statbufp->st_devnam);
4024 # ifdef RTL_USES_UTC
4027 statbufp->st_mtime = _toloc(statbufp->st_mtime);
4028 statbufp->st_atime = _toloc(statbufp->st_atime);
4029 statbufp->st_ctime = _toloc(statbufp->st_ctime);
4034 if (!VMSISH_TIME) { /* Return UTC instead of local time */
4038 statbufp->st_mtime = _toutc(statbufp->st_mtime);
4039 statbufp->st_atime = _toutc(statbufp->st_atime);
4040 statbufp->st_ctime = _toutc(statbufp->st_ctime);
4047 } /* end of flex_fstat() */
4050 /*{{{ int flex_stat(char *fspec, Stat_t *statbufp)*/
4052 flex_stat(char *fspec, Stat_t *statbufp)
4055 char fileified[NAM$C_MAXRSS+1];
4058 if (statbufp == (Stat_t *) &statcache)
4059 do_tovmsspec(fspec,namecache,0);
4060 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
4061 memset(statbufp,0,sizeof *statbufp);
4062 statbufp->st_dev = encode_dev("_NLA0:");
4063 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
4064 statbufp->st_uid = 0x00010001;
4065 statbufp->st_gid = 0x0001;
4066 time((time_t *)&statbufp->st_mtime);
4067 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
4071 /* Try for a directory name first. If fspec contains a filename without
4072 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
4073 * and sea:[wine.dark]water. exist, we prefer the directory here.
4074 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
4075 * not sea:[wine.dark]., if the latter exists. If the intended target is
4076 * the file with null type, specify this by calling flex_stat() with
4077 * a '.' at the end of fspec.
4079 if (do_fileify_dirspec(fspec,fileified,0) != NULL) {
4080 retval = stat(fileified,(stat_t *) statbufp);
4081 if (!retval && statbufp == (Stat_t *) &statcache)
4082 strcpy(namecache,fileified);
4084 if (retval) retval = stat(fspec,(stat_t *) statbufp);
4086 statbufp->st_dev = encode_dev(statbufp->st_devnam);
4087 # ifdef RTL_USES_UTC
4090 statbufp->st_mtime = _toloc(statbufp->st_mtime);
4091 statbufp->st_atime = _toloc(statbufp->st_atime);
4092 statbufp->st_ctime = _toloc(statbufp->st_ctime);
4097 if (!VMSISH_TIME) { /* Return UTC instead of local time */
4101 statbufp->st_mtime = _toutc(statbufp->st_mtime);
4102 statbufp->st_atime = _toutc(statbufp->st_atime);
4103 statbufp->st_ctime = _toutc(statbufp->st_ctime);
4109 } /* end of flex_stat() */
4112 /* Insures that no carriage-control translation will be done on a file. */
4113 /*{{{FILE *my_binmode(FILE *fp, char iotype)*/
4115 my_binmode(FILE *fp, char iotype)
4117 char filespec[NAM$C_MAXRSS], *acmode, *s, *colon, *dirend = Nullch;
4118 int ret = 0, saverrno = errno, savevmserrno = vaxc$errno;
4121 if (!fgetname(fp,filespec)) return NULL;
4122 for (s = filespec; *s; s++) {
4123 if (*s == ':') colon = s;
4124 else if (*s == ']' || *s == '>') dirend = s;
4126 /* Looks like a tmpfile, which will go away if reopened */
4127 if (s == dirend + 3) return fp;
4128 /* If we've got a non-file-structured device, clip off the trailing
4129 * junk, and don't lose sleep if we can't get a stream position. */
4130 if (dirend == Nullch) *(colon+1) = '\0';
4131 if (iotype != '-'&& (ret = fgetpos(fp, &pos)) == -1 && dirend) return NULL;
4133 case '<': case 'r': acmode = "rb"; break;
4134 case '>': case 'w': case '|':
4135 /* use 'a' instead of 'w' to avoid creating new file;
4136 fsetpos below will take care of restoring file position */
4137 case 'a': acmode = "ab"; break;
4138 case '+': case 's': acmode = "rb+"; break;
4139 case '-': acmode = fileno(fp) ? "ab" : "rb"; break;
4141 warn("Unrecognized iotype %c in my_binmode",iotype);
4144 if (freopen(filespec,acmode,fp) == NULL) return NULL;
4145 if (iotype != '-' && ret != -1 && fsetpos(fp,&pos) == -1) return NULL;
4146 if (ret == -1) { set_errno(saverrno); set_vaxc_errno(savevmserrno); }
4148 } /* end of my_binmode() */
4152 /*{{{char *my_getlogin()*/
4153 /* VMS cuserid == Unix getlogin, except calling sequence */
4157 static char user[L_cuserid];
4158 return cuserid(user);
4163 /* rmscopy - copy a file using VMS RMS routines
4165 * Copies contents and attributes of spec_in to spec_out, except owner
4166 * and protection information. Name and type of spec_in are used as
4167 * defaults for spec_out. The third parameter specifies whether rmscopy()
4168 * should try to propagate timestamps from the input file to the output file.
4169 * If it is less than 0, no timestamps are preserved. If it is 0, then
4170 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
4171 * propagated to the output file at creation iff the output file specification
4172 * did not contain an explicit name or type, and the revision date is always
4173 * updated at the end of the copy operation. If it is greater than 0, then
4174 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
4175 * other than the revision date should be propagated, and bit 1 indicates
4176 * that the revision date should be propagated.
4178 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
4180 * Copyright 1996 by Charles Bailey <bailey@genetics.upenn.edu>.
4181 * Incorporates, with permission, some code from EZCOPY by Tim Adye
4182 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
4183 * as part of the Perl standard distribution under the terms of the
4184 * GNU General Public License or the Perl Artistic License. Copies
4185 * of each may be found in the Perl standard distribution.
4187 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
4189 rmscopy(char *spec_in, char *spec_out, int preserve_dates)
4191 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
4192 rsa[NAM$C_MAXRSS], ubf[32256];
4193 unsigned long int i, sts, sts2;
4194 struct FAB fab_in, fab_out;
4195 struct RAB rab_in, rab_out;
4197 struct XABDAT xabdat;
4198 struct XABFHC xabfhc;
4199 struct XABRDT xabrdt;
4200 struct XABSUM xabsum;
4202 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
4203 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
4204 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4208 fab_in = cc$rms_fab;
4209 fab_in.fab$l_fna = vmsin;
4210 fab_in.fab$b_fns = strlen(vmsin);
4211 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
4212 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
4213 fab_in.fab$l_fop = FAB$M_SQO;
4214 fab_in.fab$l_nam = &nam;
4215 fab_in.fab$l_xab = (void *) &xabdat;
4218 nam.nam$l_rsa = rsa;
4219 nam.nam$b_rss = sizeof(rsa);
4220 nam.nam$l_esa = esa;
4221 nam.nam$b_ess = sizeof (esa);
4222 nam.nam$b_esl = nam.nam$b_rsl = 0;
4224 xabdat = cc$rms_xabdat; /* To get creation date */
4225 xabdat.xab$l_nxt = (void *) &xabfhc;
4227 xabfhc = cc$rms_xabfhc; /* To get record length */
4228 xabfhc.xab$l_nxt = (void *) &xabsum;
4230 xabsum = cc$rms_xabsum; /* To get key and area information */
4232 if (!((sts = sys$open(&fab_in)) & 1)) {
4233 set_vaxc_errno(sts);
4237 set_errno(ENOENT); break;
4239 set_errno(ENODEV); break;
4241 set_errno(EINVAL); break;
4243 set_errno(EACCES); break;
4251 fab_out.fab$w_ifi = 0;
4252 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
4253 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
4254 fab_out.fab$l_fop = FAB$M_SQO;
4255 fab_out.fab$l_fna = vmsout;
4256 fab_out.fab$b_fns = strlen(vmsout);
4257 fab_out.fab$l_dna = nam.nam$l_name;
4258 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
4260 if (preserve_dates == 0) { /* Act like DCL COPY */
4261 nam.nam$b_nop = NAM$M_SYNCHK;
4262 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
4263 if (!((sts = sys$parse(&fab_out)) & 1)) {
4264 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
4265 set_vaxc_errno(sts);
4268 fab_out.fab$l_xab = (void *) &xabdat;
4269 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
4271 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
4272 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
4273 preserve_dates =0; /* bitmask from this point forward */
4275 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
4276 if (!((sts = sys$create(&fab_out)) & 1)) {
4277 set_vaxc_errno(sts);
4280 set_errno(ENOENT); break;
4282 set_errno(ENODEV); break;
4284 set_errno(EINVAL); break;
4286 set_errno(EACCES); break;
4292 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
4293 if (preserve_dates & 2) {
4294 /* sys$close() will process xabrdt, not xabdat */
4295 xabrdt = cc$rms_xabrdt;
4297 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
4299 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
4300 * is unsigned long[2], while DECC & VAXC use a struct */
4301 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
4303 fab_out.fab$l_xab = (void *) &xabrdt;
4306 rab_in = cc$rms_rab;
4307 rab_in.rab$l_fab = &fab_in;
4308 rab_in.rab$l_rop = RAB$M_BIO;
4309 rab_in.rab$l_ubf = ubf;
4310 rab_in.rab$w_usz = sizeof ubf;
4311 if (!((sts = sys$connect(&rab_in)) & 1)) {
4312 sys$close(&fab_in); sys$close(&fab_out);
4313 set_errno(EVMSERR); set_vaxc_errno(sts);
4317 rab_out = cc$rms_rab;
4318 rab_out.rab$l_fab = &fab_out;
4319 rab_out.rab$l_rbf = ubf;
4320 if (!((sts = sys$connect(&rab_out)) & 1)) {
4321 sys$close(&fab_in); sys$close(&fab_out);
4322 set_errno(EVMSERR); set_vaxc_errno(sts);
4326 while ((sts = sys$read(&rab_in))) { /* always true */
4327 if (sts == RMS$_EOF) break;
4328 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
4329 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
4330 sys$close(&fab_in); sys$close(&fab_out);
4331 set_errno(EVMSERR); set_vaxc_errno(sts);
4336 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
4337 sys$close(&fab_in); sys$close(&fab_out);
4338 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
4340 set_errno(EVMSERR); set_vaxc_errno(sts);
4346 } /* end of rmscopy() */
4350 /*** The following glue provides 'hooks' to make some of the routines
4351 * from this file available from Perl. These routines are sufficiently
4352 * basic, and are required sufficiently early in the build process,
4353 * that's it's nice to have them available to miniperl as well as the
4354 * full Perl, so they're set up here instead of in an extension. The
4355 * Perl code which handles importation of these names into a given
4356 * package lives in [.VMS]Filespec.pm in @INC.
4360 rmsexpand_fromperl(CV *cv)
4363 char *fspec, *defspec = NULL, *rslt;
4365 if (!items || items > 2)
4366 croak("Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
4367 fspec = SvPV(ST(0),na);
4368 if (!fspec || !*fspec) XSRETURN_UNDEF;
4369 if (items == 2) defspec = SvPV(ST(1),na);
4371 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
4372 ST(0) = sv_newmortal();
4373 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
4378 vmsify_fromperl(CV *cv)
4383 if (items != 1) croak("Usage: VMS::Filespec::vmsify(spec)");
4384 vmsified = do_tovmsspec(SvPV(ST(0),na),NULL,1);
4385 ST(0) = sv_newmortal();
4386 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
4391 unixify_fromperl(CV *cv)
4396 if (items != 1) croak("Usage: VMS::Filespec::unixify(spec)");
4397 unixified = do_tounixspec(SvPV(ST(0),na),NULL,1);
4398 ST(0) = sv_newmortal();
4399 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
4404 fileify_fromperl(CV *cv)
4409 if (items != 1) croak("Usage: VMS::Filespec::fileify(spec)");
4410 fileified = do_fileify_dirspec(SvPV(ST(0),na),NULL,1);
4411 ST(0) = sv_newmortal();
4412 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
4417 pathify_fromperl(CV *cv)
4422 if (items != 1) croak("Usage: VMS::Filespec::pathify(spec)");
4423 pathified = do_pathify_dirspec(SvPV(ST(0),na),NULL,1);
4424 ST(0) = sv_newmortal();
4425 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
4430 vmspath_fromperl(CV *cv)
4435 if (items != 1) croak("Usage: VMS::Filespec::vmspath(spec)");
4436 vmspath = do_tovmspath(SvPV(ST(0),na),NULL,1);
4437 ST(0) = sv_newmortal();
4438 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
4443 unixpath_fromperl(CV *cv)
4448 if (items != 1) croak("Usage: VMS::Filespec::unixpath(spec)");
4449 unixpath = do_tounixpath(SvPV(ST(0),na),NULL,1);
4450 ST(0) = sv_newmortal();
4451 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
4456 candelete_fromperl(CV *cv)
4459 char fspec[NAM$C_MAXRSS+1], *fsp;
4463 if (items != 1) croak("Usage: VMS::Filespec::candelete(spec)");
4465 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
4466 if (SvTYPE(mysv) == SVt_PVGV) {
4467 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),fspec)) {
4468 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4475 if (mysv != ST(0) || !(fsp = SvPV(mysv,na)) || !*fsp) {
4476 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4482 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
4487 rmscopy_fromperl(CV *cv)
4490 char inspec[NAM$C_MAXRSS+1], outspec[NAM$C_MAXRSS+1], *inp, *outp;
4492 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
4493 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4494 unsigned long int sts;
4498 if (items < 2 || items > 3)
4499 croak("Usage: File::Copy::rmscopy(from,to[,date_flag])");
4501 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
4502 if (SvTYPE(mysv) == SVt_PVGV) {
4503 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),inspec)) {
4504 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4511 if (mysv != ST(0) || !(inp = SvPV(mysv,na)) || !*inp) {
4512 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4517 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
4518 if (SvTYPE(mysv) == SVt_PVGV) {
4519 if (!(io = GvIOp(mysv)) || !fgetname(IoIFP(io),outspec)) {
4520 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4527 if (mysv != ST(1) || !(outp = SvPV(mysv,na)) || !*outp) {
4528 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
4533 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
4535 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
4542 char* file = __FILE__;
4544 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
4545 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
4546 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
4547 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
4548 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
4549 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
4550 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
4551 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
4552 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
4554 #ifdef PRIME_ENV_AT_STARTUP