3 * VMS-specific routines for perl5
6 * August 2005 Convert VMS status code to UNIX status codes
7 * August 2000 tweaks to vms_image_init, my_flush, my_fwrite, cando_by_name,
8 * and Perl_cando by Craig Berry
9 * 29-Aug-2000 Charles Lane's piping improvements rolled in
10 * 20-Aug-1999 revisions by Charles Bailey bailey@newman.upenn.edu
19 #include <climsgdef.h>
29 #include <libclidef.h>
31 #include <lib$routines.h>
41 #include <str$routines.h>
48 /* Older versions of ssdef.h don't have these */
49 #ifndef SS$_INVFILFOROP
50 # define SS$_INVFILFOROP 3930
52 #ifndef SS$_NOSUCHOBJECT
53 # define SS$_NOSUCHOBJECT 2696
56 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
57 #define PERLIO_NOT_STDIO 0
59 /* Don't replace system definitions of vfork, getenv, and stat,
60 * code below needs to get to the underlying CRTL routines. */
61 #define DONT_MASK_RTL_CALLS
65 /* Anticipating future expansion in lexical warnings . . . */
67 # define WARN_INTERNAL WARN_MISC
70 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
71 # define RTL_USES_UTC 1
75 /* gcc's header files don't #define direct access macros
76 * corresponding to VAXC's variant structs */
78 # define uic$v_format uic$r_uic_form.uic$v_format
79 # define uic$v_group uic$r_uic_form.uic$v_group
80 # define uic$v_member uic$r_uic_form.uic$v_member
81 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
82 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
83 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
84 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
87 #if defined(NEED_AN_H_ERRNO)
92 unsigned short int buflen;
93 unsigned short int itmcode;
95 unsigned short int *retlen;
98 #define do_fileify_dirspec(a,b,c) mp_do_fileify_dirspec(aTHX_ a,b,c)
99 #define do_pathify_dirspec(a,b,c) mp_do_pathify_dirspec(aTHX_ a,b,c)
100 #define do_tovmsspec(a,b,c) mp_do_tovmsspec(aTHX_ a,b,c)
101 #define do_tovmspath(a,b,c) mp_do_tovmspath(aTHX_ a,b,c)
102 #define do_rmsexpand(a,b,c,d,e) mp_do_rmsexpand(aTHX_ a,b,c,d,e)
103 #define do_tounixspec(a,b,c) mp_do_tounixspec(aTHX_ a,b,c)
104 #define do_tounixpath(a,b,c) mp_do_tounixpath(aTHX_ a,b,c)
105 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
106 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
108 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
109 #define PERL_LNM_MAX_ALLOWED_INDEX 127
111 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
112 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
115 #define PERL_LNM_MAX_ITER 10
117 #define MAX_DCL_SYMBOL 255 /* well, what *we* can set, at least*/
118 #define MAX_DCL_LINE_LENGTH (4*MAX_DCL_SYMBOL-4)
120 static char *__mystrtolower(char *str)
122 if (str) for (; *str; ++str) *str= tolower(*str);
126 static struct dsc$descriptor_s fildevdsc =
127 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
128 static struct dsc$descriptor_s crtlenvdsc =
129 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
130 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
131 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
132 static struct dsc$descriptor_s **env_tables = defenv;
133 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
135 /* True if we shouldn't treat barewords as logicals during directory */
137 static int no_translate_barewords;
140 static int tz_updated = 1;
144 * Routine to retrieve the maximum equivalence index for an input
145 * logical name. Some calls to this routine have no knowledge if
146 * the variable is a logical or not. So on error we return a max
149 /*{{{int my_maxidx(char *lnm) */
155 int attr = LNM$M_CASE_BLIND;
156 struct dsc$descriptor lnmdsc;
157 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
160 lnmdsc.dsc$w_length = strlen(lnm);
161 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
162 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
163 lnmdsc.dsc$a_pointer = lnm;
165 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
166 if ((status & 1) == 0)
173 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
175 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
176 struct dsc$descriptor_s **tabvec, unsigned long int flags)
178 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2;
179 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
180 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
182 unsigned char acmode;
183 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
184 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
185 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
186 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
188 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
189 #if defined(PERL_IMPLICIT_CONTEXT)
192 aTHX = PERL_GET_INTERP;
198 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
199 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
201 for (cp1 = (char *)lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
202 *cp2 = _toupper(*cp1);
203 if (cp1 - lnm > LNM$C_NAMLENGTH) {
204 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
208 lnmdsc.dsc$w_length = cp1 - lnm;
209 lnmdsc.dsc$a_pointer = uplnm;
210 uplnm[lnmdsc.dsc$w_length] = '\0';
211 secure = flags & PERL__TRNENV_SECURE;
212 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
213 if (!tabvec || !*tabvec) tabvec = env_tables;
215 for (curtab = 0; tabvec[curtab]; curtab++) {
216 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
217 if (!ivenv && !secure) {
222 Perl_warn(aTHX_ "Can't read CRTL environ\n");
225 retsts = SS$_NOLOGNAM;
226 for (i = 0; environ[i]; i++) {
227 if ((eq = strchr(environ[i],'=')) &&
228 lnmdsc.dsc$w_length == (eq - environ[i]) &&
229 !strncmp(environ[i],uplnm,eq - environ[i])) {
231 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
232 if (!eqvlen) continue;
237 if (retsts != SS$_NOLOGNAM) break;
240 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
241 !str$case_blind_compare(&tmpdsc,&clisym)) {
242 if (!ivsym && !secure) {
243 unsigned short int deflen = LNM$C_NAMLENGTH;
244 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
245 /* dynamic dsc to accomodate possible long value */
246 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
247 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
250 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
252 /* Special hack--we might be called before the interpreter's */
253 /* fully initialized, in which case either thr or PL_curcop */
254 /* might be bogus. We have to check, since ckWARN needs them */
255 /* both to be valid if running threaded */
256 if (ckWARN(WARN_MISC)) {
257 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
260 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
262 _ckvmssts(lib$sfree1_dd(&eqvdsc));
263 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
264 if (retsts == LIB$_NOSUCHSYM) continue;
269 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
270 midx = my_maxidx((char *) lnm);
271 for (idx = 0, cp1 = eqv; idx <= midx; idx++) {
272 lnmlst[1].bufadr = cp1;
274 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
275 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
276 if (retsts == SS$_NOLOGNAM) break;
277 /* PPFs have a prefix */
280 *((int *)uplnm) == *((int *)"SYS$") &&
282 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
283 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
284 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
285 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
286 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
287 memcpy(eqv,eqv+4,eqvlen-4);
293 if ((retsts == SS$_IVLOGNAM) ||
294 (retsts == SS$_NOLOGNAM)) { continue; }
297 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
298 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
299 if (retsts == SS$_NOLOGNAM) continue;
302 eqvlen = strlen(eqv);
306 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
307 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
308 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
309 retsts == SS$_NOLOGNAM) {
310 set_errno(EINVAL); set_vaxc_errno(retsts);
312 else _ckvmssts(retsts);
314 } /* end of vmstrnenv */
317 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
318 /* Define as a function so we can access statics. */
319 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
321 return vmstrnenv(lnm,eqv,idx,fildev,
322 #ifdef SECURE_INTERNAL_GETENV
323 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
332 * Note: Uses Perl temp to store result so char * can be returned to
333 * caller; this pointer will be invalidated at next Perl statement
335 * We define this as a function rather than a macro in terms of my_getenv_len()
336 * so that it'll work when PL_curinterp is undefined (and we therefore can't
339 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
341 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
343 static char *__my_getenv_eqv = NULL;
344 char uplnm[LNM$C_NAMLENGTH+1], *cp1, *cp2, *eqv;
345 unsigned long int idx = 0;
346 int trnsuccess, success, secure, saverr, savvmserr;
350 midx = my_maxidx((char *) lnm) + 1;
352 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
353 /* Set up a temporary buffer for the return value; Perl will
354 * clean it up at the next statement transition */
355 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
356 if (!tmpsv) return NULL;
360 /* Assume no interpreter ==> single thread */
361 if (__my_getenv_eqv != NULL) {
362 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
365 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
367 eqv = __my_getenv_eqv;
370 for (cp1 = (char *) lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
371 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
372 getcwd(eqv,LNM$C_NAMLENGTH);
376 /* Impose security constraints only if tainting */
378 /* Impose security constraints only if tainting */
379 secure = PL_curinterp ? PL_tainting : will_taint;
380 saverr = errno; savvmserr = vaxc$errno;
387 #ifdef SECURE_INTERNAL_GETENV
388 secure ? PERL__TRNENV_SECURE : 0
394 /* For the getenv interface we combine all the equivalence names
395 * of a search list logical into one value to acquire a maximum
396 * value length of 255*128 (assuming %ENV is using logicals).
398 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
400 /* If the name contains a semicolon-delimited index, parse it
401 * off and make sure we only retrieve the equivalence name for
403 if ((cp2 = strchr(lnm,';')) != NULL) {
405 uplnm[cp2-lnm] = '\0';
406 idx = strtoul(cp2+1,NULL,0);
408 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
411 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
413 /* Discard NOLOGNAM on internal calls since we're often looking
414 * for an optional name, and this "error" often shows up as the
415 * (bogus) exit status for a die() call later on. */
416 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
417 return success ? eqv : Nullch;
420 } /* end of my_getenv() */
424 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
426 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
428 char *buf, *cp1, *cp2;
429 unsigned long idx = 0;
431 static char *__my_getenv_len_eqv = NULL;
432 int secure, saverr, savvmserr;
435 midx = my_maxidx((char *) lnm) + 1;
437 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
438 /* Set up a temporary buffer for the return value; Perl will
439 * clean it up at the next statement transition */
440 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
441 if (!tmpsv) return NULL;
445 /* Assume no interpreter ==> single thread */
446 if (__my_getenv_len_eqv != NULL) {
447 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
450 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
452 buf = __my_getenv_len_eqv;
455 for (cp1 = (char *)lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
456 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
457 getcwd(buf,LNM$C_NAMLENGTH);
463 /* Impose security constraints only if tainting */
464 secure = PL_curinterp ? PL_tainting : will_taint;
465 saverr = errno; savvmserr = vaxc$errno;
472 #ifdef SECURE_INTERNAL_GETENV
473 secure ? PERL__TRNENV_SECURE : 0
479 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
481 if ((cp2 = strchr(lnm,';')) != NULL) {
484 idx = strtoul(cp2+1,NULL,0);
486 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
489 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
491 /* Discard NOLOGNAM on internal calls since we're often looking
492 * for an optional name, and this "error" often shows up as the
493 * (bogus) exit status for a die() call later on. */
494 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
495 return *len ? buf : Nullch;
498 } /* end of my_getenv_len() */
501 static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *);
503 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
505 /*{{{ void prime_env_iter() */
508 /* Fill the %ENV associative array with all logical names we can
509 * find, in preparation for iterating over it.
512 static int primed = 0;
513 HV *seenhv = NULL, *envhv;
515 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
516 unsigned short int chan;
517 #ifndef CLI$M_TRUSTED
518 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
520 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
521 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
523 bool have_sym = FALSE, have_lnm = FALSE;
524 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
525 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
526 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
527 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
528 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
529 #if defined(PERL_IMPLICIT_CONTEXT)
532 #if defined(USE_ITHREADS)
533 static perl_mutex primenv_mutex;
534 MUTEX_INIT(&primenv_mutex);
537 #if defined(PERL_IMPLICIT_CONTEXT)
538 /* We jump through these hoops because we can be called at */
539 /* platform-specific initialization time, which is before anything is */
540 /* set up--we can't even do a plain dTHX since that relies on the */
541 /* interpreter structure to be initialized */
543 aTHX = PERL_GET_INTERP;
549 if (primed || !PL_envgv) return;
550 MUTEX_LOCK(&primenv_mutex);
551 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
552 envhv = GvHVn(PL_envgv);
553 /* Perform a dummy fetch as an lval to insure that the hash table is
554 * set up. Otherwise, the hv_store() will turn into a nullop. */
555 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
557 for (i = 0; env_tables[i]; i++) {
558 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
559 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
560 if (!have_lnm && !str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
562 if (have_sym || have_lnm) {
563 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
564 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
565 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
566 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
569 for (i--; i >= 0; i--) {
570 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
573 for (j = 0; environ[j]; j++) {
574 if (!(start = strchr(environ[j],'='))) {
575 if (ckWARN(WARN_INTERNAL))
576 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
580 sv = newSVpv(start,0);
582 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
587 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
588 !str$case_blind_compare(&tmpdsc,&clisym)) {
589 strcpy(cmd,"Show Symbol/Global *");
590 cmddsc.dsc$w_length = 20;
591 if (env_tables[i]->dsc$w_length == 12 &&
592 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
593 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
594 flags = defflags | CLI$M_NOLOGNAM;
597 strcpy(cmd,"Show Logical *");
598 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
599 strcat(cmd," /Table=");
600 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
601 cmddsc.dsc$w_length = strlen(cmd);
603 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
604 flags = defflags | CLI$M_NOCLISYM;
607 /* Create a new subprocess to execute each command, to exclude the
608 * remote possibility that someone could subvert a mbx or file used
609 * to write multiple commands to a single subprocess.
612 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
613 0,&riseandshine,0,0,&clidsc,&clitabdsc);
614 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
615 defflags &= ~CLI$M_TRUSTED;
616 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
618 if (!buf) Newx(buf,mbxbufsiz + 1,char);
619 if (seenhv) SvREFCNT_dec(seenhv);
622 char *cp1, *cp2, *key;
623 unsigned long int sts, iosb[2], retlen, keylen;
626 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
627 if (sts & 1) sts = iosb[0] & 0xffff;
628 if (sts == SS$_ENDOFFILE) {
630 while (substs == 0) { sys$hiber(); wakect++;}
631 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
636 retlen = iosb[0] >> 16;
637 if (!retlen) continue; /* blank line */
639 if (iosb[1] != subpid) {
641 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
645 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
646 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
648 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
649 if (*cp1 == '(' || /* Logical name table name */
650 *cp1 == '=' /* Next eqv of searchlist */) continue;
651 if (*cp1 == '"') cp1++;
652 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
653 key = cp1; keylen = cp2 - cp1;
654 if (keylen && hv_exists(seenhv,key,keylen)) continue;
655 while (*cp2 && *cp2 != '=') cp2++;
656 while (*cp2 && *cp2 == '=') cp2++;
657 while (*cp2 && *cp2 == ' ') cp2++;
658 if (*cp2 == '"') { /* String translation; may embed "" */
659 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
660 cp2++; cp1--; /* Skip "" surrounding translation */
662 else { /* Numeric translation */
663 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
664 cp1--; /* stop on last non-space char */
666 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
667 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
670 PERL_HASH(hash,key,keylen);
672 if (cp1 == cp2 && *cp2 == '.') {
673 /* A single dot usually means an unprintable character, such as a null
674 * to indicate a zero-length value. Get the actual value to make sure.
676 char lnm[LNM$C_NAMLENGTH+1];
677 char eqv[LNM$C_NAMLENGTH+1];
678 strncpy(lnm, key, keylen);
679 int trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
680 sv = newSVpvn(eqv, strlen(eqv));
683 sv = newSVpvn(cp2,cp1 - cp2 + 1);
687 hv_store(envhv,key,keylen,sv,hash);
688 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
690 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
691 /* get the PPFs for this process, not the subprocess */
692 char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
693 char eqv[LNM$C_NAMLENGTH+1];
695 for (i = 0; ppfs[i]; i++) {
696 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
697 sv = newSVpv(eqv,trnlen);
699 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
704 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
705 if (buf) Safefree(buf);
706 if (seenhv) SvREFCNT_dec(seenhv);
707 MUTEX_UNLOCK(&primenv_mutex);
710 } /* end of prime_env_iter */
714 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
715 /* Define or delete an element in the same "environment" as
716 * vmstrnenv(). If an element is to be deleted, it's removed from
717 * the first place it's found. If it's to be set, it's set in the
718 * place designated by the first element of the table vector.
719 * Like setenv() returns 0 for success, non-zero on error.
722 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
724 char uplnm[LNM$C_NAMLENGTH], *cp1, *cp2, *c;
725 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
727 unsigned long int retsts, usermode = PSL$C_USER;
728 struct itmlst_3 *ile, *ilist;
729 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
730 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
731 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
732 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
733 $DESCRIPTOR(local,"_LOCAL");
736 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
740 for (cp1 = (char *)lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
741 *cp2 = _toupper(*cp1);
742 if (cp1 - lnm > LNM$C_NAMLENGTH) {
743 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
747 lnmdsc.dsc$w_length = cp1 - lnm;
748 if (!tabvec || !*tabvec) tabvec = env_tables;
750 if (!eqv) { /* we're deleting n element */
751 for (curtab = 0; tabvec[curtab]; curtab++) {
752 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
754 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
755 if ((cp1 = strchr(environ[i],'=')) &&
756 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
757 !strncmp(environ[i],lnm,cp1 - environ[i])) {
759 return setenv(lnm,"",1) ? vaxc$errno : 0;
762 ivenv = 1; retsts = SS$_NOLOGNAM;
764 if (ckWARN(WARN_INTERNAL))
765 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
766 ivenv = 1; retsts = SS$_NOSUCHPGM;
772 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
773 !str$case_blind_compare(&tmpdsc,&clisym)) {
774 unsigned int symtype;
775 if (tabvec[curtab]->dsc$w_length == 12 &&
776 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
777 !str$case_blind_compare(&tmpdsc,&local))
778 symtype = LIB$K_CLI_LOCAL_SYM;
779 else symtype = LIB$K_CLI_GLOBAL_SYM;
780 retsts = lib$delete_symbol(&lnmdsc,&symtype);
781 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
782 if (retsts == LIB$_NOSUCHSYM) continue;
786 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
787 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
788 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
789 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
790 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
794 else { /* we're defining a value */
795 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
797 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
799 if (ckWARN(WARN_INTERNAL))
800 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
801 retsts = SS$_NOSUCHPGM;
805 eqvdsc.dsc$a_pointer = (char *)eqv;
806 eqvdsc.dsc$w_length = strlen(eqv);
807 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
808 !str$case_blind_compare(&tmpdsc,&clisym)) {
809 unsigned int symtype;
810 if (tabvec[0]->dsc$w_length == 12 &&
811 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
812 !str$case_blind_compare(&tmpdsc,&local))
813 symtype = LIB$K_CLI_LOCAL_SYM;
814 else symtype = LIB$K_CLI_GLOBAL_SYM;
815 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
818 if (!*eqv) eqvdsc.dsc$w_length = 1;
819 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
821 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
822 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
823 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
824 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
825 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
826 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
829 Newx(ilist,nseg+1,struct itmlst_3);
832 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
835 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
837 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
838 ile->itmcode = LNM$_STRING;
841 ile->buflen = strlen(c);
842 /* in case we are truncating one that's too long */
843 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
846 ile->buflen = LNM$C_NAMLENGTH;
850 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
854 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
861 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
862 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
863 set_errno(EVMSERR); break;
864 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
865 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
866 set_errno(EINVAL); break;
873 set_vaxc_errno(retsts);
874 return (int) retsts || 44; /* retsts should never be 0, but just in case */
877 /* We reset error values on success because Perl does an hv_fetch()
878 * before each hv_store(), and if the thing we're setting didn't
879 * previously exist, we've got a leftover error message. (Of course,
880 * this fails in the face of
881 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
882 * in that the error reported in $! isn't spurious,
883 * but it's right more often than not.)
885 set_errno(0); set_vaxc_errno(retsts);
889 } /* end of vmssetenv() */
892 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
893 /* This has to be a function since there's a prototype for it in proto.h */
895 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
898 int len = strlen(lnm);
902 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
903 if (!strcmp(uplnm,"DEFAULT")) {
904 if (eqv && *eqv) chdir(eqv);
909 if (len == 6 || len == 2) {
912 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
914 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
915 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
919 (void) vmssetenv(lnm,eqv,NULL);
923 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
925 * sets a user-mode logical in the process logical name table
926 * used for redirection of sys$error
929 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
931 $DESCRIPTOR(d_tab, "LNM$PROCESS");
932 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
933 unsigned long int iss, attr = LNM$M_CONFINE;
934 unsigned char acmode = PSL$C_USER;
935 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
937 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
938 d_name.dsc$w_length = strlen(name);
940 lnmlst[0].buflen = strlen(eqv);
941 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
943 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
944 if (!(iss&1)) lib$signal(iss);
949 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
950 /* my_crypt - VMS password hashing
951 * my_crypt() provides an interface compatible with the Unix crypt()
952 * C library function, and uses sys$hash_password() to perform VMS
953 * password hashing. The quadword hashed password value is returned
954 * as a NUL-terminated 8 character string. my_crypt() does not change
955 * the case of its string arguments; in order to match the behavior
956 * of LOGINOUT et al., alphabetic characters in both arguments must
957 * be upcased by the caller.
960 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
962 # ifndef UAI$C_PREFERRED_ALGORITHM
963 # define UAI$C_PREFERRED_ALGORITHM 127
965 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
966 unsigned short int salt = 0;
967 unsigned long int sts;
969 unsigned short int dsc$w_length;
970 unsigned char dsc$b_type;
971 unsigned char dsc$b_class;
972 const char * dsc$a_pointer;
973 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
974 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
975 struct itmlst_3 uailst[3] = {
976 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
977 { sizeof salt, UAI$_SALT, &salt, 0},
978 { 0, 0, NULL, NULL}};
981 usrdsc.dsc$w_length = strlen(usrname);
982 usrdsc.dsc$a_pointer = usrname;
983 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
985 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
989 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
995 if (sts != RMS$_RNF) return NULL;
998 txtdsc.dsc$w_length = strlen(textpasswd);
999 txtdsc.dsc$a_pointer = textpasswd;
1000 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1001 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1004 return (char *) hash;
1006 } /* end of my_crypt() */
1010 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned);
1011 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int);
1012 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int);
1014 /*{{{int do_rmdir(char *name)*/
1016 Perl_do_rmdir(pTHX_ const char *name)
1018 char dirfile[NAM$C_MAXRSS+1];
1022 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
1023 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
1024 else retval = kill_file(dirfile);
1027 } /* end of do_rmdir */
1031 * Delete any file to which user has control access, regardless of whether
1032 * delete access is explicitly allowed.
1033 * Limitations: User must have write access to parent directory.
1034 * Does not block signals or ASTs; if interrupted in midstream
1035 * may leave file with an altered ACL.
1038 /*{{{int kill_file(char *name)*/
1040 Perl_kill_file(pTHX_ const char *name)
1042 char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
1043 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1044 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1045 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1047 unsigned char myace$b_length;
1048 unsigned char myace$b_type;
1049 unsigned short int myace$w_flags;
1050 unsigned long int myace$l_access;
1051 unsigned long int myace$l_ident;
1052 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1053 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1054 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1056 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1057 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1058 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1059 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1060 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1061 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1063 /* Expand the input spec using RMS, since the CRTL remove() and
1064 * system services won't do this by themselves, so we may miss
1065 * a file "hiding" behind a logical name or search list. */
1066 if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
1067 if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
1068 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
1069 /* If not, can changing protections help? */
1070 if (vaxc$errno != RMS$_PRV) return -1;
1072 /* No, so we get our own UIC to use as a rights identifier,
1073 * and the insert an ACE at the head of the ACL which allows us
1074 * to delete the file.
1076 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1077 fildsc.dsc$w_length = strlen(rspec);
1078 fildsc.dsc$a_pointer = rspec;
1080 newace.myace$l_ident = oldace.myace$l_ident;
1081 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1083 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1084 set_errno(ENOENT); break;
1086 set_errno(ENOTDIR); break;
1088 set_errno(ENODEV); break;
1089 case RMS$_SYN: case SS$_INVFILFOROP:
1090 set_errno(EINVAL); break;
1092 set_errno(EACCES); break;
1096 set_vaxc_errno(aclsts);
1099 /* Grab any existing ACEs with this identifier in case we fail */
1100 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1101 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1102 || fndsts == SS$_NOMOREACE ) {
1103 /* Add the new ACE . . . */
1104 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1106 if ((rmsts = remove(name))) {
1107 /* We blew it - dir with files in it, no write priv for
1108 * parent directory, etc. Put things back the way they were. */
1109 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1112 addlst[0].bufadr = &oldace;
1113 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1120 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1121 /* We just deleted it, so of course it's not there. Some versions of
1122 * VMS seem to return success on the unlock operation anyhow (after all
1123 * the unlock is successful), but others don't.
1125 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1126 if (aclsts & 1) aclsts = fndsts;
1127 if (!(aclsts & 1)) {
1129 set_vaxc_errno(aclsts);
1135 } /* end of kill_file() */
1139 /*{{{int my_mkdir(char *,Mode_t)*/
1141 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
1143 STRLEN dirlen = strlen(dir);
1145 /* zero length string sometimes gives ACCVIO */
1146 if (dirlen == 0) return -1;
1148 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
1149 * null file name/type. However, it's commonplace under Unix,
1150 * so we'll allow it for a gain in portability.
1152 if (dir[dirlen-1] == '/') {
1153 char *newdir = savepvn(dir,dirlen-1);
1154 int ret = mkdir(newdir,mode);
1158 else return mkdir(dir,mode);
1159 } /* end of my_mkdir */
1162 /*{{{int my_chdir(char *)*/
1164 Perl_my_chdir(pTHX_ const char *dir)
1166 STRLEN dirlen = strlen(dir);
1168 /* zero length string sometimes gives ACCVIO */
1169 if (dirlen == 0) return -1;
1171 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
1173 * null file name/type. However, it's commonplace under Unix,
1174 * so we'll allow it for a gain in portability.
1176 if (dir[dirlen-1] == '/') {
1177 char *newdir = savepvn(dir,dirlen-1);
1178 int ret = chdir(newdir);
1182 else return chdir(dir);
1183 } /* end of my_chdir */
1187 /*{{{FILE *my_tmpfile()*/
1194 if ((fp = tmpfile())) return fp;
1196 Newx(cp,L_tmpnam+24,char);
1197 strcpy(cp,"Sys$Scratch:");
1198 tmpnam(cp+strlen(cp));
1199 strcat(cp,".Perltmp");
1200 fp = fopen(cp,"w+","fop=dlt");
1207 #ifndef HOMEGROWN_POSIX_SIGNALS
1209 * The C RTL's sigaction fails to check for invalid signal numbers so we
1210 * help it out a bit. The docs are correct, but the actual routine doesn't
1211 * do what the docs say it will.
1213 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
1215 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
1216 struct sigaction* oact)
1218 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
1219 SETERRNO(EINVAL, SS$_INVARG);
1222 return sigaction(sig, act, oact);
1227 #ifdef KILL_BY_SIGPRC
1228 #include <errnodef.h>
1230 /* We implement our own kill() using the undocumented system service
1231 sys$sigprc for one of two reasons:
1233 1.) If the kill() in an older CRTL uses sys$forcex, causing the
1234 target process to do a sys$exit, which usually can't be handled
1235 gracefully...certainly not by Perl and the %SIG{} mechanism.
1237 2.) If the kill() in the CRTL can't be called from a signal
1238 handler without disappearing into the ether, i.e., the signal
1239 it purportedly sends is never trapped. Still true as of VMS 7.3.
1241 sys$sigprc has the same parameters as sys$forcex, but throws an exception
1242 in the target process rather than calling sys$exit.
1244 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
1245 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
1246 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
1247 with condition codes C$_SIG0+nsig*8, catching the exception on the
1248 target process and resignaling with appropriate arguments.
1250 But we don't have that VMS 7.0+ exception handler, so if you
1251 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
1253 Also note that SIGTERM is listed in the docs as being "unimplemented",
1254 yet always seems to be signaled with a VMS condition code of 4 (and
1255 correctly handled for that code). So we hardwire it in.
1257 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
1258 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
1259 than signalling with an unrecognized (and unhandled by CRTL) code.
1262 #define _MY_SIG_MAX 17
1265 Perl_sig_to_vmscondition(int sig)
1267 static unsigned int sig_code[_MY_SIG_MAX+1] =
1270 SS$_HANGUP, /* 1 SIGHUP */
1271 SS$_CONTROLC, /* 2 SIGINT */
1272 SS$_CONTROLY, /* 3 SIGQUIT */
1273 SS$_RADRMOD, /* 4 SIGILL */
1274 SS$_BREAK, /* 5 SIGTRAP */
1275 SS$_OPCCUS, /* 6 SIGABRT */
1276 SS$_COMPAT, /* 7 SIGEMT */
1278 SS$_FLTOVF, /* 8 SIGFPE VAX */
1280 SS$_HPARITH, /* 8 SIGFPE AXP */
1282 SS$_ABORT, /* 9 SIGKILL */
1283 SS$_ACCVIO, /* 10 SIGBUS */
1284 SS$_ACCVIO, /* 11 SIGSEGV */
1285 SS$_BADPARAM, /* 12 SIGSYS */
1286 SS$_NOMBX, /* 13 SIGPIPE */
1287 SS$_ASTFLT, /* 14 SIGALRM */
1293 #if __VMS_VER >= 60200000
1294 static int initted = 0;
1297 sig_code[16] = C$_SIGUSR1;
1298 sig_code[17] = C$_SIGUSR2;
1302 if (sig < _SIG_MIN) return 0;
1303 if (sig > _MY_SIG_MAX) return 0;
1304 return sig_code[sig];
1308 Perl_my_kill(int pid, int sig)
1313 int sys$sigprc(unsigned int *pidadr,
1314 struct dsc$descriptor_s *prcname,
1317 code = Perl_sig_to_vmscondition(sig);
1319 if (!pid || !code) {
1323 iss = sys$sigprc((unsigned int *)&pid,0,code);
1324 if (iss&1) return 0;
1328 set_errno(EPERM); break;
1330 case SS$_NOSUCHNODE:
1331 case SS$_UNREACHABLE:
1332 set_errno(ESRCH); break;
1334 set_errno(ENOMEM); break;
1339 set_vaxc_errno(iss);
1345 /* Routine to convert a VMS status code to a UNIX status code.
1346 ** More tricky than it appears because of conflicting conventions with
1349 ** VMS status codes are a bit mask, with the least significant bit set for
1352 ** Special UNIX status of EVMSERR indicates that no translation is currently
1353 ** available, and programs should check the VMS status code.
1355 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
1359 #ifndef C_FACILITY_NO
1360 #define C_FACILITY_NO 0x350000
1363 #define DCL_IVVERB 0x38090
1366 int vms_status_to_unix(int vms_status)
1374 /* Assume the best or the worst */
1375 if (vms_status & STS$M_SUCCESS)
1378 unix_status = EVMSERR;
1380 msg_status = vms_status & ~STS$M_CONTROL;
1382 facility = vms_status & STS$M_FAC_NO;
1383 fac_sp = vms_status & STS$M_FAC_SP;
1384 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
1386 if ((facility == 0) || (fac_sp == 0)) {
1392 unix_status = EFAULT;
1399 case SS$_INVFILFOROP:
1403 unix_status = EINVAL;
1408 unix_status = EACCES;
1410 case SS$_DEVICEFULL:
1411 unix_status = ENOSPC;
1414 unix_status = ENODEV;
1416 case SS$_NOSUCHFILE:
1417 case SS$_NOSUCHOBJECT:
1418 unix_status = ENOENT;
1421 unix_status = EINTR;
1424 unix_status = E2BIG;
1427 unix_status = ENOMEM;
1430 unix_status = EPERM;
1432 case SS$_NOSUCHNODE:
1433 case SS$_UNREACHABLE:
1434 unix_status = ESRCH;
1437 unix_status = ECHILD;
1440 if ((facility == 0) && (msg_no < 8)) {
1441 /* These are not real VMS status codes so assume that they are
1442 ** already UNIX status codes
1444 unix_status = msg_no;
1450 /* Translate a POSIX exit code to a UNIX exit code */
1451 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
1452 unix_status = (msg_no & 0x0FF0) >> 3;
1455 switch(msg_status) {
1456 /* case RMS$_EOF: */ /* End of File */
1457 case RMS$_FNF: /* File Not Found */
1458 case RMS$_DNF: /* Dir Not Found */
1459 unix_status = ENOENT;
1461 case RMS$_RNF: /* Record Not Found */
1462 unix_status = ESRCH;
1465 unix_status = ENOTDIR;
1468 unix_status = ENODEV;
1472 case LIB$_INVSTRDES:
1474 case LIB$_NOSUCHSYM:
1475 case LIB$_INVSYMNAM:
1477 unix_status = EINVAL;
1483 unix_status = E2BIG;
1485 case RMS$_PRV: /* No privilege */
1486 case RMS$_ACC: /* ACP file access failed */
1487 case RMS$_WLK: /* Device write locked */
1488 unix_status = EACCES;
1490 /* case RMS$_NMF: */ /* No more files */
1500 /* default piping mailbox size */
1501 #define PERL_BUFSIZ 512
1505 create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc)
1507 unsigned long int mbxbufsiz;
1508 static unsigned long int syssize = 0;
1509 unsigned long int dviitm = DVI$_DEVNAM;
1510 char csize[LNM$C_NAMLENGTH+1];
1513 unsigned long syiitm = SYI$_MAXBUF;
1515 * Get the SYSGEN parameter MAXBUF
1517 * If the logical 'PERL_MBX_SIZE' is defined
1518 * use the value of the logical instead of PERL_BUFSIZ, but
1519 * keep the size between 128 and MAXBUF.
1522 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
1525 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
1526 mbxbufsiz = atoi(csize);
1528 mbxbufsiz = PERL_BUFSIZ;
1530 if (mbxbufsiz < 128) mbxbufsiz = 128;
1531 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
1533 _ckvmssts(sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
1535 _ckvmssts(lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
1536 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
1538 } /* end of create_mbx() */
1541 /*{{{ my_popen and my_pclose*/
1543 typedef struct _iosb IOSB;
1544 typedef struct _iosb* pIOSB;
1545 typedef struct _pipe Pipe;
1546 typedef struct _pipe* pPipe;
1547 typedef struct pipe_details Info;
1548 typedef struct pipe_details* pInfo;
1549 typedef struct _srqp RQE;
1550 typedef struct _srqp* pRQE;
1551 typedef struct _tochildbuf CBuf;
1552 typedef struct _tochildbuf* pCBuf;
1555 unsigned short status;
1556 unsigned short count;
1557 unsigned long dvispec;
1560 #pragma member_alignment save
1561 #pragma nomember_alignment quadword
1562 struct _srqp { /* VMS self-relative queue entry */
1563 unsigned long qptr[2];
1565 #pragma member_alignment restore
1566 static RQE RQE_ZERO = {0,0};
1568 struct _tochildbuf {
1571 unsigned short size;
1579 unsigned short chan_in;
1580 unsigned short chan_out;
1582 unsigned int bufsize;
1594 #if defined(PERL_IMPLICIT_CONTEXT)
1595 void *thx; /* Either a thread or an interpreter */
1596 /* pointer, depending on how we're built */
1604 PerlIO *fp; /* file pointer to pipe mailbox */
1605 int useFILE; /* using stdio, not perlio */
1606 int pid; /* PID of subprocess */
1607 int mode; /* == 'r' if pipe open for reading */
1608 int done; /* subprocess has completed */
1609 int waiting; /* waiting for completion/closure */
1610 int closing; /* my_pclose is closing this pipe */
1611 unsigned long completion; /* termination status of subprocess */
1612 pPipe in; /* pipe in to sub */
1613 pPipe out; /* pipe out of sub */
1614 pPipe err; /* pipe of sub's sys$error */
1615 int in_done; /* true when in pipe finished */
1620 struct exit_control_block
1622 struct exit_control_block *flink;
1623 unsigned long int (*exit_routine)();
1624 unsigned long int arg_count;
1625 unsigned long int *status_address;
1626 unsigned long int exit_status;
1629 typedef struct _closed_pipes Xpipe;
1630 typedef struct _closed_pipes* pXpipe;
1632 struct _closed_pipes {
1633 int pid; /* PID of subprocess */
1634 unsigned long completion; /* termination status of subprocess */
1636 #define NKEEPCLOSED 50
1637 static Xpipe closed_list[NKEEPCLOSED];
1638 static int closed_index = 0;
1639 static int closed_num = 0;
1641 #define RETRY_DELAY "0 ::0.20"
1642 #define MAX_RETRY 50
1644 static int pipe_ef = 0; /* first call to safe_popen inits these*/
1645 static unsigned long mypid;
1646 static unsigned long delaytime[2];
1648 static pInfo open_pipes = NULL;
1649 static $DESCRIPTOR(nl_desc, "NL:");
1651 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
1655 static unsigned long int
1656 pipe_exit_routine(pTHX)
1659 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
1660 int sts, did_stuff, need_eof, j;
1663 flush any pending i/o
1669 PerlIO_flush(info->fp); /* first, flush data */
1671 fflush((FILE *)info->fp);
1677 next we try sending an EOF...ignore if doesn't work, make sure we
1685 _ckvmssts(sys$setast(0));
1686 if (info->in && !info->in->shut_on_empty) {
1687 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
1692 _ckvmssts(sys$setast(1));
1696 /* wait for EOF to have effect, up to ~ 30 sec [default] */
1698 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
1703 _ckvmssts(sys$setast(0));
1704 if (info->waiting && info->done)
1706 nwait += info->waiting;
1707 _ckvmssts(sys$setast(1));
1717 _ckvmssts(sys$setast(0));
1718 if (!info->done) { /* Tap them gently on the shoulder . . .*/
1719 sts = sys$forcex(&info->pid,0,&abort);
1720 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
1723 _ckvmssts(sys$setast(1));
1727 /* again, wait for effect */
1729 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
1734 _ckvmssts(sys$setast(0));
1735 if (info->waiting && info->done)
1737 nwait += info->waiting;
1738 _ckvmssts(sys$setast(1));
1747 _ckvmssts(sys$setast(0));
1748 if (!info->done) { /* We tried to be nice . . . */
1749 sts = sys$delprc(&info->pid,0);
1750 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
1752 _ckvmssts(sys$setast(1));
1757 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
1758 else if (!(sts & 1)) retsts = sts;
1763 static struct exit_control_block pipe_exitblock =
1764 {(struct exit_control_block *) 0,
1765 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
1767 static void pipe_mbxtofd_ast(pPipe p);
1768 static void pipe_tochild1_ast(pPipe p);
1769 static void pipe_tochild2_ast(pPipe p);
1772 popen_completion_ast(pInfo info)
1774 pInfo i = open_pipes;
1778 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
1779 closed_list[closed_index].pid = info->pid;
1780 closed_list[closed_index].completion = info->completion;
1782 if (closed_index == NKEEPCLOSED)
1787 if (i == info) break;
1790 if (!i) return; /* unlinked, probably freed too */
1795 Writing to subprocess ...
1796 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
1798 chan_out may be waiting for "done" flag, or hung waiting
1799 for i/o completion to child...cancel the i/o. This will
1800 put it into "snarf mode" (done but no EOF yet) that discards
1803 Output from subprocess (stdout, stderr) needs to be flushed and
1804 shut down. We try sending an EOF, but if the mbx is full the pipe
1805 routine should still catch the "shut_on_empty" flag, telling it to
1806 use immediate-style reads so that "mbx empty" -> EOF.
1810 if (info->in && !info->in_done) { /* only for mode=w */
1811 if (info->in->shut_on_empty && info->in->need_wake) {
1812 info->in->need_wake = FALSE;
1813 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
1815 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
1819 if (info->out && !info->out_done) { /* were we also piping output? */
1820 info->out->shut_on_empty = TRUE;
1821 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1822 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
1823 _ckvmssts_noperl(iss);
1826 if (info->err && !info->err_done) { /* we were piping stderr */
1827 info->err->shut_on_empty = TRUE;
1828 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1829 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
1830 _ckvmssts_noperl(iss);
1832 _ckvmssts_noperl(sys$setef(pipe_ef));
1836 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
1837 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
1840 we actually differ from vmstrnenv since we use this to
1841 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
1842 are pointing to the same thing
1845 static unsigned short
1846 popen_translate(pTHX_ char *logical, char *result)
1849 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
1850 $DESCRIPTOR(d_log,"");
1852 unsigned short length;
1853 unsigned short code;
1855 unsigned short *retlenaddr;
1857 unsigned short l, ifi;
1859 d_log.dsc$a_pointer = logical;
1860 d_log.dsc$w_length = strlen(logical);
1862 itmlst[0].code = LNM$_STRING;
1863 itmlst[0].length = 255;
1864 itmlst[0].buffer_addr = result;
1865 itmlst[0].retlenaddr = &l;
1868 itmlst[1].length = 0;
1869 itmlst[1].buffer_addr = 0;
1870 itmlst[1].retlenaddr = 0;
1872 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
1873 if (iss == SS$_NOLOGNAM) {
1877 if (!(iss&1)) lib$signal(iss);
1880 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
1881 strip it off and return the ifi, if any
1884 if (result[0] == 0x1b && result[1] == 0x00) {
1885 memcpy(&ifi,result+2,2);
1886 strcpy(result,result+4);
1888 return ifi; /* this is the RMS internal file id */
1891 static void pipe_infromchild_ast(pPipe p);
1894 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
1895 inside an AST routine without worrying about reentrancy and which Perl
1896 memory allocator is being used.
1898 We read data and queue up the buffers, then spit them out one at a
1899 time to the output mailbox when the output mailbox is ready for one.
1902 #define INITIAL_TOCHILDQUEUE 2
1905 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
1909 char mbx1[64], mbx2[64];
1910 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
1911 DSC$K_CLASS_S, mbx1},
1912 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
1913 DSC$K_CLASS_S, mbx2};
1914 unsigned int dviitm = DVI$_DEVBUFSIZ;
1919 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
1920 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
1921 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
1924 p->shut_on_empty = FALSE;
1925 p->need_wake = FALSE;
1928 p->iosb.status = SS$_NORMAL;
1929 p->iosb2.status = SS$_NORMAL;
1935 #ifdef PERL_IMPLICIT_CONTEXT
1939 n = sizeof(CBuf) + p->bufsize;
1941 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
1942 _ckvmssts(lib$get_vm(&n, &b));
1943 b->buf = (char *) b + sizeof(CBuf);
1944 _ckvmssts(lib$insqhi(b, &p->free));
1947 pipe_tochild2_ast(p);
1948 pipe_tochild1_ast(p);
1954 /* reads the MBX Perl is writing, and queues */
1957 pipe_tochild1_ast(pPipe p)
1960 int iss = p->iosb.status;
1961 int eof = (iss == SS$_ENDOFFILE);
1962 #ifdef PERL_IMPLICIT_CONTEXT
1968 p->shut_on_empty = TRUE;
1970 _ckvmssts(sys$dassgn(p->chan_in));
1976 b->size = p->iosb.count;
1977 _ckvmssts(lib$insqhi(b, &p->wait));
1979 p->need_wake = FALSE;
1980 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
1983 p->retry = 1; /* initial call */
1986 if (eof) { /* flush the free queue, return when done */
1987 int n = sizeof(CBuf) + p->bufsize;
1989 iss = lib$remqti(&p->free, &b);
1990 if (iss == LIB$_QUEWASEMP) return;
1992 _ckvmssts(lib$free_vm(&n, &b));
1996 iss = lib$remqti(&p->free, &b);
1997 if (iss == LIB$_QUEWASEMP) {
1998 int n = sizeof(CBuf) + p->bufsize;
1999 _ckvmssts(lib$get_vm(&n, &b));
2000 b->buf = (char *) b + sizeof(CBuf);
2006 iss = sys$qio(0,p->chan_in,
2007 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
2009 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
2010 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
2015 /* writes queued buffers to output, waits for each to complete before
2019 pipe_tochild2_ast(pPipe p)
2022 int iss = p->iosb2.status;
2023 int n = sizeof(CBuf) + p->bufsize;
2024 int done = (p->info && p->info->done) ||
2025 iss == SS$_CANCEL || iss == SS$_ABORT;
2026 #if defined(PERL_IMPLICIT_CONTEXT)
2031 if (p->type) { /* type=1 has old buffer, dispose */
2032 if (p->shut_on_empty) {
2033 _ckvmssts(lib$free_vm(&n, &b));
2035 _ckvmssts(lib$insqhi(b, &p->free));
2040 iss = lib$remqti(&p->wait, &b);
2041 if (iss == LIB$_QUEWASEMP) {
2042 if (p->shut_on_empty) {
2044 _ckvmssts(sys$dassgn(p->chan_out));
2045 *p->pipe_done = TRUE;
2046 _ckvmssts(sys$setef(pipe_ef));
2048 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
2049 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
2053 p->need_wake = TRUE;
2063 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
2064 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
2066 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
2067 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
2076 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
2079 char mbx1[64], mbx2[64];
2080 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
2081 DSC$K_CLASS_S, mbx1},
2082 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
2083 DSC$K_CLASS_S, mbx2};
2084 unsigned int dviitm = DVI$_DEVBUFSIZ;
2087 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
2088 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
2090 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2091 Newx(p->buf, p->bufsize, char);
2092 p->shut_on_empty = FALSE;
2095 p->iosb.status = SS$_NORMAL;
2096 #if defined(PERL_IMPLICIT_CONTEXT)
2099 pipe_infromchild_ast(p);
2107 pipe_infromchild_ast(pPipe p)
2109 int iss = p->iosb.status;
2110 int eof = (iss == SS$_ENDOFFILE);
2111 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
2112 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
2113 #if defined(PERL_IMPLICIT_CONTEXT)
2117 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
2118 _ckvmssts(sys$dassgn(p->chan_out));
2123 input shutdown if EOF from self (done or shut_on_empty)
2124 output shutdown if closing flag set (my_pclose)
2125 send data/eof from child or eof from self
2126 otherwise, re-read (snarf of data from child)
2131 if (myeof && p->chan_in) { /* input shutdown */
2132 _ckvmssts(sys$dassgn(p->chan_in));
2137 if (myeof || kideof) { /* pass EOF to parent */
2138 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
2139 pipe_infromchild_ast, p,
2142 } else if (eof) { /* eat EOF --- fall through to read*/
2144 } else { /* transmit data */
2145 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
2146 pipe_infromchild_ast,p,
2147 p->buf, p->iosb.count, 0, 0, 0, 0));
2153 /* everything shut? flag as done */
2155 if (!p->chan_in && !p->chan_out) {
2156 *p->pipe_done = TRUE;
2157 _ckvmssts(sys$setef(pipe_ef));
2161 /* write completed (or read, if snarfing from child)
2162 if still have input active,
2163 queue read...immediate mode if shut_on_empty so we get EOF if empty
2165 check if Perl reading, generate EOFs as needed
2171 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
2172 pipe_infromchild_ast,p,
2173 p->buf, p->bufsize, 0, 0, 0, 0);
2174 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
2176 } else { /* send EOFs for extra reads */
2177 p->iosb.status = SS$_ENDOFFILE;
2178 p->iosb.dvispec = 0;
2179 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
2181 pipe_infromchild_ast, p, 0, 0, 0, 0));
2187 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
2191 unsigned long dviitm = DVI$_DEVBUFSIZ;
2193 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
2194 DSC$K_CLASS_S, mbx};
2196 /* things like terminals and mbx's don't need this filter */
2197 if (fd && fstat(fd,&s) == 0) {
2198 unsigned long dviitm = DVI$_DEVCHAR, devchar;
2199 struct dsc$descriptor_s d_dev = {strlen(s.st_dev), DSC$K_DTYPE_T,
2200 DSC$K_CLASS_S, s.st_dev};
2202 _ckvmssts(lib$getdvi(&dviitm,0,&d_dev,&devchar,0,0));
2203 if (!(devchar & DEV$M_DIR)) { /* non directory structured...*/
2204 strcpy(out, s.st_dev);
2210 p->fd_out = dup(fd);
2211 create_mbx(aTHX_ &p->chan_in, &d_mbx);
2212 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2213 Newx(p->buf, p->bufsize+1, char);
2214 p->shut_on_empty = FALSE;
2219 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
2220 pipe_mbxtofd_ast, p,
2221 p->buf, p->bufsize, 0, 0, 0, 0));
2227 pipe_mbxtofd_ast(pPipe p)
2229 int iss = p->iosb.status;
2230 int done = p->info->done;
2232 int eof = (iss == SS$_ENDOFFILE);
2233 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
2234 int err = !(iss&1) && !eof;
2235 #if defined(PERL_IMPLICIT_CONTEXT)
2239 if (done && myeof) { /* end piping */
2241 sys$dassgn(p->chan_in);
2242 *p->pipe_done = TRUE;
2243 _ckvmssts(sys$setef(pipe_ef));
2247 if (!err && !eof) { /* good data to send to file */
2248 p->buf[p->iosb.count] = '\n';
2249 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
2252 if (p->retry < MAX_RETRY) {
2253 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
2263 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
2264 pipe_mbxtofd_ast, p,
2265 p->buf, p->bufsize, 0, 0, 0, 0);
2266 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
2271 typedef struct _pipeloc PLOC;
2272 typedef struct _pipeloc* pPLOC;
2276 char dir[NAM$C_MAXRSS+1];
2278 static pPLOC head_PLOC = 0;
2281 free_pipelocs(pTHX_ void *head)
2284 pPLOC *pHead = (pPLOC *)head;
2296 store_pipelocs(pTHX)
2305 char temp[NAM$C_MAXRSS+1];
2309 free_pipelocs(aTHX_ &head_PLOC);
2311 /* the . directory from @INC comes last */
2314 p->next = head_PLOC;
2316 strcpy(p->dir,"./");
2318 /* get the directory from $^X */
2320 #ifdef PERL_IMPLICIT_CONTEXT
2321 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
2323 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
2325 strcpy(temp, PL_origargv[0]);
2326 x = strrchr(temp,']');
2329 if ((unixdir = tounixpath(temp, Nullch)) != Nullch) {
2331 p->next = head_PLOC;
2333 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2334 p->dir[NAM$C_MAXRSS] = '\0';
2338 /* reverse order of @INC entries, skip "." since entered above */
2340 #ifdef PERL_IMPLICIT_CONTEXT
2343 if (PL_incgv) av = GvAVn(PL_incgv);
2345 for (i = 0; av && i <= AvFILL(av); i++) {
2346 dirsv = *av_fetch(av,i,TRUE);
2348 if (SvROK(dirsv)) continue;
2349 dir = SvPVx(dirsv,n_a);
2350 if (strcmp(dir,".") == 0) continue;
2351 if ((unixdir = tounixpath(dir, Nullch)) == Nullch)
2355 p->next = head_PLOC;
2357 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2358 p->dir[NAM$C_MAXRSS] = '\0';
2361 /* most likely spot (ARCHLIB) put first in the list */
2364 if ((unixdir = tounixpath(ARCHLIB_EXP, Nullch)) != Nullch) {
2366 p->next = head_PLOC;
2368 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2369 p->dir[NAM$C_MAXRSS] = '\0';
2378 static int vmspipe_file_status = 0;
2379 static char vmspipe_file[NAM$C_MAXRSS+1];
2381 /* already found? Check and use ... need read+execute permission */
2383 if (vmspipe_file_status == 1) {
2384 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
2385 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
2386 return vmspipe_file;
2388 vmspipe_file_status = 0;
2391 /* scan through stored @INC, $^X */
2393 if (vmspipe_file_status == 0) {
2394 char file[NAM$C_MAXRSS+1];
2395 pPLOC p = head_PLOC;
2398 strcpy(file, p->dir);
2399 strncat(file, "vmspipe.com",NAM$C_MAXRSS);
2400 file[NAM$C_MAXRSS] = '\0';
2403 if (!do_tovmsspec(file,vmspipe_file,0)) continue;
2405 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
2406 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
2407 vmspipe_file_status = 1;
2408 return vmspipe_file;
2411 vmspipe_file_status = -1; /* failed, use tempfiles */
2418 vmspipe_tempfile(pTHX)
2420 char file[NAM$C_MAXRSS+1];
2422 static int index = 0;
2425 /* create a tempfile */
2427 /* we can't go from W, shr=get to R, shr=get without
2428 an intermediate vulnerable state, so don't bother trying...
2430 and lib$spawn doesn't shr=put, so have to close the write
2432 So... match up the creation date/time and the FID to
2433 make sure we're dealing with the same file
2438 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
2439 fp = fopen(file,"w");
2441 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
2442 fp = fopen(file,"w");
2444 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
2445 fp = fopen(file,"w");
2448 if (!fp) return 0; /* we're hosed */
2450 fprintf(fp,"$! 'f$verify(0)'\n");
2451 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
2452 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
2453 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
2454 fprintf(fp,"$ perl_on = \"set noon\"\n");
2455 fprintf(fp,"$ perl_exit = \"exit\"\n");
2456 fprintf(fp,"$ perl_del = \"delete\"\n");
2457 fprintf(fp,"$ pif = \"if\"\n");
2458 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
2459 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
2460 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
2461 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
2462 fprintf(fp,"$! --- build command line to get max possible length\n");
2463 fprintf(fp,"$c=perl_popen_cmd0\n");
2464 fprintf(fp,"$c=c+perl_popen_cmd1\n");
2465 fprintf(fp,"$c=c+perl_popen_cmd2\n");
2466 fprintf(fp,"$x=perl_popen_cmd3\n");
2467 fprintf(fp,"$c=c+x\n");
2468 fprintf(fp,"$ perl_on\n");
2469 fprintf(fp,"$ 'c'\n");
2470 fprintf(fp,"$ perl_status = $STATUS\n");
2471 fprintf(fp,"$ perl_del 'perl_cfile'\n");
2472 fprintf(fp,"$ perl_exit 'perl_status'\n");
2475 fgetname(fp, file, 1);
2476 fstat(fileno(fp), &s0);
2479 fp = fopen(file,"r","shr=get");
2481 fstat(fileno(fp), &s1);
2483 if (s0.st_ino[0] != s1.st_ino[0] ||
2484 s0.st_ino[1] != s1.st_ino[1] ||
2485 s0.st_ino[2] != s1.st_ino[2] ||
2486 s0.st_ctime != s1.st_ctime ) {
2497 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
2499 static int handler_set_up = FALSE;
2500 unsigned long int sts, flags = CLI$M_NOWAIT;
2501 /* The use of a GLOBAL table (as was done previously) rendered
2502 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
2503 * environment. Hence we've switched to LOCAL symbol table.
2505 unsigned int table = LIB$K_CLI_LOCAL_SYM;
2507 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
2508 char in[512], out[512], err[512], mbx[512];
2510 char tfilebuf[NAM$C_MAXRSS+1];
2512 char cmd_sym_name[20];
2513 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
2514 DSC$K_CLASS_S, symbol};
2515 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
2517 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
2518 DSC$K_CLASS_S, cmd_sym_name};
2519 struct dsc$descriptor_s *vmscmd;
2520 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
2521 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
2522 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
2524 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
2526 /* once-per-program initialization...
2527 note that the SETAST calls and the dual test of pipe_ef
2528 makes sure that only the FIRST thread through here does
2529 the initialization...all other threads wait until it's
2532 Yeah, uglier than a pthread call, it's got all the stuff inline
2533 rather than in a separate routine.
2537 _ckvmssts(sys$setast(0));
2539 unsigned long int pidcode = JPI$_PID;
2540 $DESCRIPTOR(d_delay, RETRY_DELAY);
2541 _ckvmssts(lib$get_ef(&pipe_ef));
2542 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
2543 _ckvmssts(sys$bintim(&d_delay, delaytime));
2545 if (!handler_set_up) {
2546 _ckvmssts(sys$dclexh(&pipe_exitblock));
2547 handler_set_up = TRUE;
2549 _ckvmssts(sys$setast(1));
2552 /* see if we can find a VMSPIPE.COM */
2555 vmspipe = find_vmspipe(aTHX);
2557 strcpy(tfilebuf+1,vmspipe);
2558 } else { /* uh, oh...we're in tempfile hell */
2559 tpipe = vmspipe_tempfile(aTHX);
2560 if (!tpipe) { /* a fish popular in Boston */
2561 if (ckWARN(WARN_PIPE)) {
2562 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
2566 fgetname(tpipe,tfilebuf+1,1);
2568 vmspipedsc.dsc$a_pointer = tfilebuf;
2569 vmspipedsc.dsc$w_length = strlen(tfilebuf);
2571 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
2574 case RMS$_FNF: case RMS$_DNF:
2575 set_errno(ENOENT); break;
2577 set_errno(ENOTDIR); break;
2579 set_errno(ENODEV); break;
2581 set_errno(EACCES); break;
2583 set_errno(EINVAL); break;
2584 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
2585 set_errno(E2BIG); break;
2586 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
2587 _ckvmssts(sts); /* fall through */
2588 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
2591 set_vaxc_errno(sts);
2592 if (*mode != 'n' && ckWARN(WARN_PIPE)) {
2593 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
2600 strcpy(mode,in_mode);
2603 info->completion = 0;
2604 info->closing = FALSE;
2611 info->in_done = TRUE;
2612 info->out_done = TRUE;
2613 info->err_done = TRUE;
2614 in[0] = out[0] = err[0] = '\0';
2616 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
2620 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
2625 if (*mode == 'r') { /* piping from subroutine */
2627 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
2629 info->out->pipe_done = &info->out_done;
2630 info->out_done = FALSE;
2631 info->out->info = info;
2633 if (!info->useFILE) {
2634 info->fp = PerlIO_open(mbx, mode);
2636 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
2637 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
2640 if (!info->fp && info->out) {
2641 sys$cancel(info->out->chan_out);
2643 while (!info->out_done) {
2645 _ckvmssts(sys$setast(0));
2646 done = info->out_done;
2647 if (!done) _ckvmssts(sys$clref(pipe_ef));
2648 _ckvmssts(sys$setast(1));
2649 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2652 if (info->out->buf) Safefree(info->out->buf);
2653 Safefree(info->out);
2659 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
2661 info->err->pipe_done = &info->err_done;
2662 info->err_done = FALSE;
2663 info->err->info = info;
2666 } else if (*mode == 'w') { /* piping to subroutine */
2668 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
2670 info->out->pipe_done = &info->out_done;
2671 info->out_done = FALSE;
2672 info->out->info = info;
2675 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
2677 info->err->pipe_done = &info->err_done;
2678 info->err_done = FALSE;
2679 info->err->info = info;
2682 info->in = pipe_tochild_setup(aTHX_ in,mbx);
2683 if (!info->useFILE) {
2684 info->fp = PerlIO_open(mbx, mode);
2686 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
2687 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
2691 info->in->pipe_done = &info->in_done;
2692 info->in_done = FALSE;
2693 info->in->info = info;
2697 if (!info->fp && info->in) {
2699 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
2700 0, 0, 0, 0, 0, 0, 0, 0));
2702 while (!info->in_done) {
2704 _ckvmssts(sys$setast(0));
2705 done = info->in_done;
2706 if (!done) _ckvmssts(sys$clref(pipe_ef));
2707 _ckvmssts(sys$setast(1));
2708 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2711 if (info->in->buf) Safefree(info->in->buf);
2719 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
2720 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
2722 info->out->pipe_done = &info->out_done;
2723 info->out_done = FALSE;
2724 info->out->info = info;
2727 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
2729 info->err->pipe_done = &info->err_done;
2730 info->err_done = FALSE;
2731 info->err->info = info;
2735 symbol[MAX_DCL_SYMBOL] = '\0';
2737 strncpy(symbol, in, MAX_DCL_SYMBOL);
2738 d_symbol.dsc$w_length = strlen(symbol);
2739 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
2741 strncpy(symbol, err, MAX_DCL_SYMBOL);
2742 d_symbol.dsc$w_length = strlen(symbol);
2743 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
2745 strncpy(symbol, out, MAX_DCL_SYMBOL);
2746 d_symbol.dsc$w_length = strlen(symbol);
2747 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table));
2749 p = vmscmd->dsc$a_pointer;
2750 while (*p && *p != '\n') p++;
2751 *p = '\0'; /* truncate on \n */
2752 p = vmscmd->dsc$a_pointer;
2753 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
2754 if (*p == '$') p++; /* remove leading $ */
2755 while (*p == ' ' || *p == '\t') p++;
2757 for (j = 0; j < 4; j++) {
2758 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
2759 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
2761 strncpy(symbol, p, MAX_DCL_SYMBOL);
2762 d_symbol.dsc$w_length = strlen(symbol);
2763 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
2765 if (strlen(p) > MAX_DCL_SYMBOL) {
2766 p += MAX_DCL_SYMBOL;
2771 _ckvmssts(sys$setast(0));
2772 info->next=open_pipes; /* prepend to list */
2774 _ckvmssts(sys$setast(1));
2775 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
2776 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
2777 * have SYS$COMMAND if we need it.
2779 _ckvmssts(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
2780 0, &info->pid, &info->completion,
2781 0, popen_completion_ast,info,0,0,0));
2783 /* if we were using a tempfile, close it now */
2785 if (tpipe) fclose(tpipe);
2787 /* once the subprocess is spawned, it has copied the symbols and
2788 we can get rid of ours */
2790 for (j = 0; j < 4; j++) {
2791 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
2792 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
2793 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
2795 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
2796 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
2797 _ckvmssts(lib$delete_symbol(&d_sym_out, &table));
2798 vms_execfree(vmscmd);
2800 #ifdef PERL_IMPLICIT_CONTEXT
2803 PL_forkprocess = info->pid;
2808 _ckvmssts(sys$setast(0));
2810 if (!done) _ckvmssts(sys$clref(pipe_ef));
2811 _ckvmssts(sys$setast(1));
2812 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2814 *psts = info->completion;
2815 /* Caller thinks it is open and tries to close it. */
2816 /* This causes some problems, as it changes the error status */
2817 /* my_pclose(info->fp); */
2822 } /* end of safe_popen */
2825 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
2827 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
2831 TAINT_PROPER("popen");
2832 PERL_FLUSHALL_FOR_CHILD;
2833 return safe_popen(aTHX_ cmd,mode,&sts);
2838 /*{{{ I32 my_pclose(PerlIO *fp)*/
2839 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
2841 pInfo info, last = NULL;
2842 unsigned long int retsts;
2845 for (info = open_pipes; info != NULL; last = info, info = info->next)
2846 if (info->fp == fp) break;
2848 if (info == NULL) { /* no such pipe open */
2849 set_errno(ECHILD); /* quoth POSIX */
2850 set_vaxc_errno(SS$_NONEXPR);
2854 /* If we were writing to a subprocess, insure that someone reading from
2855 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
2856 * produce an EOF record in the mailbox.
2858 * well, at least sometimes it *does*, so we have to watch out for
2859 * the first EOF closing the pipe (and DASSGN'ing the channel)...
2863 PerlIO_flush(info->fp); /* first, flush data */
2865 fflush((FILE *)info->fp);
2868 _ckvmssts(sys$setast(0));
2869 info->closing = TRUE;
2870 done = info->done && info->in_done && info->out_done && info->err_done;
2871 /* hanging on write to Perl's input? cancel it */
2872 if (info->mode == 'r' && info->out && !info->out_done) {
2873 if (info->out->chan_out) {
2874 _ckvmssts(sys$cancel(info->out->chan_out));
2875 if (!info->out->chan_in) { /* EOF generation, need AST */
2876 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
2880 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
2881 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2883 _ckvmssts(sys$setast(1));
2886 PerlIO_close(info->fp);
2888 fclose((FILE *)info->fp);
2891 we have to wait until subprocess completes, but ALSO wait until all
2892 the i/o completes...otherwise we'll be freeing the "info" structure
2893 that the i/o ASTs could still be using...
2897 _ckvmssts(sys$setast(0));
2898 done = info->done && info->in_done && info->out_done && info->err_done;
2899 if (!done) _ckvmssts(sys$clref(pipe_ef));
2900 _ckvmssts(sys$setast(1));
2901 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2903 retsts = info->completion;
2905 /* remove from list of open pipes */
2906 _ckvmssts(sys$setast(0));
2907 if (last) last->next = info->next;
2908 else open_pipes = info->next;
2909 _ckvmssts(sys$setast(1));
2911 /* free buffers and structures */
2914 if (info->in->buf) Safefree(info->in->buf);
2918 if (info->out->buf) Safefree(info->out->buf);
2919 Safefree(info->out);
2922 if (info->err->buf) Safefree(info->err->buf);
2923 Safefree(info->err);
2929 } /* end of my_pclose() */
2931 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
2932 /* Roll our own prototype because we want this regardless of whether
2933 * _VMS_WAIT is defined.
2935 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
2937 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
2938 created with popen(); otherwise partially emulate waitpid() unless
2939 we have a suitable one from the CRTL that came with VMS 7.2 and later.
2940 Also check processes not considered by the CRTL waitpid().
2942 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
2944 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
2951 if (statusp) *statusp = 0;
2953 for (info = open_pipes; info != NULL; info = info->next)
2954 if (info->pid == pid) break;
2956 if (info != NULL) { /* we know about this child */
2957 while (!info->done) {
2958 _ckvmssts(sys$setast(0));
2960 if (!done) _ckvmssts(sys$clref(pipe_ef));
2961 _ckvmssts(sys$setast(1));
2962 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
2965 if (statusp) *statusp = info->completion;
2969 /* child that already terminated? */
2971 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
2972 if (closed_list[j].pid == pid) {
2973 if (statusp) *statusp = closed_list[j].completion;
2978 /* fall through if this child is not one of our own pipe children */
2980 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
2982 /* waitpid() became available in the CRTL as of VMS 7.0, but only
2983 * in 7.2 did we get a version that fills in the VMS completion
2984 * status as Perl has always tried to do.
2987 sts = __vms_waitpid( pid, statusp, flags );
2989 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
2992 /* If the real waitpid tells us the child does not exist, we
2993 * fall through here to implement waiting for a child that
2994 * was created by some means other than exec() (say, spawned
2995 * from DCL) or to wait for a process that is not a subprocess
2996 * of the current process.
2999 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
3002 $DESCRIPTOR(intdsc,"0 00:00:01");
3003 unsigned long int ownercode = JPI$_OWNER, ownerpid;
3004 unsigned long int pidcode = JPI$_PID, mypid;
3005 unsigned long int interval[2];
3006 unsigned int jpi_iosb[2];
3007 struct itmlst_3 jpilist[2] = {
3008 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
3013 /* Sorry folks, we don't presently implement rooting around for
3014 the first child we can find, and we definitely don't want to
3015 pass a pid of -1 to $getjpi, where it is a wildcard operation.
3021 /* Get the owner of the child so I can warn if it's not mine. If the
3022 * process doesn't exist or I don't have the privs to look at it,
3023 * I can go home early.
3025 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
3026 if (sts & 1) sts = jpi_iosb[0];
3038 set_vaxc_errno(sts);
3042 if (ckWARN(WARN_EXEC)) {
3043 /* remind folks they are asking for non-standard waitpid behavior */
3044 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
3045 if (ownerpid != mypid)
3046 Perl_warner(aTHX_ packWARN(WARN_EXEC),
3047 "waitpid: process %x is not a child of process %x",
3051 /* simply check on it once a second until it's not there anymore. */
3053 _ckvmssts(sys$bintim(&intdsc,interval));
3054 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
3055 _ckvmssts(sys$schdwk(0,0,interval,0));
3056 _ckvmssts(sys$hiber());
3058 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
3063 } /* end of waitpid() */
3068 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
3070 my_gconvert(double val, int ndig, int trail, char *buf)
3072 static char __gcvtbuf[DBL_DIG+1];
3075 loc = buf ? buf : __gcvtbuf;
3077 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
3079 sprintf(loc,"%.*g",ndig,val);
3085 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
3086 return gcvt(val,ndig,loc);
3089 loc[0] = '0'; loc[1] = '\0';
3097 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
3098 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
3099 * to expand file specification. Allows for a single default file
3100 * specification and a simple mask of options. If outbuf is non-NULL,
3101 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
3102 * the resultant file specification is placed. If outbuf is NULL, the
3103 * resultant file specification is placed into a static buffer.
3104 * The third argument, if non-NULL, is taken to be a default file
3105 * specification string. The fourth argument is unused at present.
3106 * rmesexpand() returns the address of the resultant string if
3107 * successful, and NULL on error.
3109 static char *mp_do_tounixspec(pTHX_ const char *, char *, int);
3112 mp_do_rmsexpand(pTHX_ const char *filespec, char *outbuf, int ts, const char *defspec, unsigned opts)
3114 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
3115 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
3116 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
3117 struct FAB myfab = cc$rms_fab;
3118 struct NAM mynam = cc$rms_nam;
3120 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
3122 if (!filespec || !*filespec) {
3123 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
3127 if (ts) out = Newx(outbuf,NAM$C_MAXRSS+1,char);
3128 else outbuf = __rmsexpand_retbuf;
3130 if ((isunix = (strchr(filespec,'/') != NULL))) {
3131 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) return NULL;
3132 filespec = vmsfspec;
3135 myfab.fab$l_fna = (char *)filespec; /* cast ok for read only pointer */
3136 myfab.fab$b_fns = strlen(filespec);
3137 myfab.fab$l_nam = &mynam;
3139 if (defspec && *defspec) {
3140 if (strchr(defspec,'/') != NULL) {
3141 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) return NULL;
3144 myfab.fab$l_dna = (char *)defspec; /* cast ok for read only pointer */
3145 myfab.fab$b_dns = strlen(defspec);
3148 mynam.nam$l_esa = esa;
3149 mynam.nam$b_ess = sizeof esa;
3150 mynam.nam$l_rsa = outbuf;
3151 mynam.nam$b_rss = NAM$C_MAXRSS;
3153 retsts = sys$parse(&myfab,0,0);
3154 if (!(retsts & 1)) {
3155 mynam.nam$b_nop |= NAM$M_SYNCHK;
3156 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
3157 retsts = sys$parse(&myfab,0,0);
3158 if (retsts & 1) goto expanded;
3160 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
3161 (void) sys$parse(&myfab,0,0); /* Free search context */
3162 if (out) Safefree(out);
3163 set_vaxc_errno(retsts);
3164 if (retsts == RMS$_PRV) set_errno(EACCES);
3165 else if (retsts == RMS$_DEV) set_errno(ENODEV);
3166 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
3167 else set_errno(EVMSERR);
3170 retsts = sys$search(&myfab,0,0);
3171 if (!(retsts & 1) && retsts != RMS$_FNF) {
3172 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
3173 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
3174 if (out) Safefree(out);
3175 set_vaxc_errno(retsts);
3176 if (retsts == RMS$_PRV) set_errno(EACCES);
3177 else set_errno(EVMSERR);
3181 /* If the input filespec contained any lowercase characters,
3182 * downcase the result for compatibility with Unix-minded code. */
3184 for (out = myfab.fab$l_fna; *out; out++)
3185 if (islower(*out)) { haslower = 1; break; }
3186 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
3187 else { out = esa; speclen = mynam.nam$b_esl; }
3188 /* Trim off null fields added by $PARSE
3189 * If type > 1 char, must have been specified in original or default spec
3190 * (not true for version; $SEARCH may have added version of existing file).
3192 trimver = !(mynam.nam$l_fnb & NAM$M_EXP_VER);
3193 trimtype = !(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
3194 (mynam.nam$l_ver - mynam.nam$l_type == 1);
3195 if (trimver || trimtype) {
3196 if (defspec && *defspec) {
3197 char defesa[NAM$C_MAXRSS];
3198 struct FAB deffab = cc$rms_fab;
3199 struct NAM defnam = cc$rms_nam;
3201 deffab.fab$l_nam = &defnam;
3202 deffab.fab$l_fna = (char *)defspec; deffab.fab$b_fns = myfab.fab$b_dns;
3203 defnam.nam$l_esa = defesa; defnam.nam$b_ess = sizeof defesa;
3204 defnam.nam$b_nop = NAM$M_SYNCHK;
3205 if (sys$parse(&deffab,0,0) & 1) {
3206 if (trimver) trimver = !(defnam.nam$l_fnb & NAM$M_EXP_VER);
3207 if (trimtype) trimtype = !(defnam.nam$l_fnb & NAM$M_EXP_TYPE);
3210 if (trimver) speclen = mynam.nam$l_ver - out;
3212 /* If we didn't already trim version, copy down */
3213 if (speclen > mynam.nam$l_ver - out)
3214 memcpy(mynam.nam$l_type, mynam.nam$l_ver,
3215 speclen - (mynam.nam$l_ver - out));
3216 speclen -= mynam.nam$l_ver - mynam.nam$l_type;
3219 /* If we just had a directory spec on input, $PARSE "helpfully"
3220 * adds an empty name and type for us */
3221 if (mynam.nam$l_name == mynam.nam$l_type &&
3222 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
3223 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
3224 speclen = mynam.nam$l_name - out;
3225 out[speclen] = '\0';
3226 if (haslower) __mystrtolower(out);
3228 /* Have we been working with an expanded, but not resultant, spec? */
3229 /* Also, convert back to Unix syntax if necessary. */
3230 if (!mynam.nam$b_rsl) {
3232 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
3234 else strcpy(outbuf,esa);
3237 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
3238 strcpy(outbuf,tmpfspec);
3240 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
3241 mynam.nam$l_rsa = NULL; mynam.nam$b_rss = 0;
3242 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0); /* Free search context */
3246 /* External entry points */
3247 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
3248 { return do_rmsexpand(spec,buf,0,def,opt); }
3249 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
3250 { return do_rmsexpand(spec,buf,1,def,opt); }
3254 ** The following routines are provided to make life easier when
3255 ** converting among VMS-style and Unix-style directory specifications.
3256 ** All will take input specifications in either VMS or Unix syntax. On
3257 ** failure, all return NULL. If successful, the routines listed below
3258 ** return a pointer to a buffer containing the appropriately
3259 ** reformatted spec (and, therefore, subsequent calls to that routine
3260 ** will clobber the result), while the routines of the same names with
3261 ** a _ts suffix appended will return a pointer to a mallocd string
3262 ** containing the appropriately reformatted spec.
3263 ** In all cases, only explicit syntax is altered; no check is made that
3264 ** the resulting string is valid or that the directory in question
3267 ** fileify_dirspec() - convert a directory spec into the name of the
3268 ** directory file (i.e. what you can stat() to see if it's a dir).
3269 ** The style (VMS or Unix) of the result is the same as the style
3270 ** of the parameter passed in.
3271 ** pathify_dirspec() - convert a directory spec into a path (i.e.
3272 ** what you prepend to a filename to indicate what directory it's in).
3273 ** The style (VMS or Unix) of the result is the same as the style
3274 ** of the parameter passed in.
3275 ** tounixpath() - convert a directory spec into a Unix-style path.
3276 ** tovmspath() - convert a directory spec into a VMS-style path.
3277 ** tounixspec() - convert any file spec into a Unix-style file spec.
3278 ** tovmsspec() - convert any file spec into a VMS-style spec.
3280 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
3281 ** Permission is given to distribute this code as part of the Perl
3282 ** standard distribution under the terms of the GNU General Public
3283 ** License or the Perl Artistic License. Copies of each may be
3284 ** found in the Perl standard distribution.
3287 /*{{{ char *fileify_dirspec[_ts](char *path, char *buf)*/
3288 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts)
3290 static char __fileify_retbuf[NAM$C_MAXRSS+1];
3291 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
3292 char *retspec, *cp1, *cp2, *lastdir;
3293 char trndir[NAM$C_MAXRSS+2], vmsdir[NAM$C_MAXRSS+1];
3294 unsigned short int trnlnm_iter_count;
3296 if (!dir || !*dir) {
3297 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
3299 dirlen = strlen(dir);
3300 while (dirlen && dir[dirlen-1] == '/') --dirlen;
3301 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
3305 if (dirlen > NAM$C_MAXRSS) {
3306 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); return NULL;
3308 if (!strpbrk(dir+1,"/]>:")) {
3309 strcpy(trndir,*dir == '/' ? dir + 1: dir);
3310 trnlnm_iter_count = 0;
3311 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) {
3312 trnlnm_iter_count++;
3313 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
3315 dirlen = strlen(trndir);
3318 strncpy(trndir,dir,dirlen);
3319 trndir[dirlen] = '\0';
3322 /* At this point we are done with *dir and use *trndir which is a
3323 * copy that can be modified. *dir must not be modified.
3326 /* If we were handed a rooted logical name or spec, treat it like a
3327 * simple directory, so that
3328 * $ Define myroot dev:[dir.]
3329 * ... do_fileify_dirspec("myroot",buf,1) ...
3330 * does something useful.
3332 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
3333 trndir[--dirlen] = '\0';
3334 trndir[dirlen-1] = ']';
3336 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
3337 trndir[--dirlen] = '\0';
3338 trndir[dirlen-1] = '>';
3341 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
3342 /* If we've got an explicit filename, we can just shuffle the string. */
3343 if (*(cp1+1)) hasfilename = 1;
3344 /* Similarly, we can just back up a level if we've got multiple levels
3345 of explicit directories in a VMS spec which ends with directories. */
3347 for (cp2 = cp1; cp2 > trndir; cp2--) {
3349 *cp2 = *cp1; *cp1 = '\0';
3353 if (*cp2 == '[' || *cp2 == '<') break;
3358 if (hasfilename || !strpbrk(trndir,"]:>")) { /* Unix-style path or filename */
3359 if (trndir[0] == '.') {
3360 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0'))
3361 return do_fileify_dirspec("[]",buf,ts);
3362 else if (trndir[1] == '.' &&
3363 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0')))
3364 return do_fileify_dirspec("[-]",buf,ts);
3366 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
3367 dirlen -= 1; /* to last element */
3368 lastdir = strrchr(trndir,'/');
3370 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
3371 /* If we have "/." or "/..", VMSify it and let the VMS code
3372 * below expand it, rather than repeating the code to handle
3373 * relative components of a filespec here */
3375 if (*(cp1+2) == '.') cp1++;
3376 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
3377 if (do_tovmsspec(trndir,vmsdir,0) == NULL) return NULL;
3378 if (strchr(vmsdir,'/') != NULL) {
3379 /* If do_tovmsspec() returned it, it must have VMS syntax
3380 * delimiters in it, so it's a mixed VMS/Unix spec. We take
3381 * the time to check this here only so we avoid a recursion
3382 * loop; otherwise, gigo.
3384 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); return NULL;
3386 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
3387 return do_tounixspec(trndir,buf,ts);
3390 } while ((cp1 = strstr(cp1,"/.")) != NULL);
3391 lastdir = strrchr(trndir,'/');
3393 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
3394 /* Ditto for specs that end in an MFD -- let the VMS code
3395 * figure out whether it's a real device or a rooted logical. */
3396 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
3397 if (do_tovmsspec(trndir,vmsdir,0) == NULL) return NULL;
3398 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
3399 return do_tounixspec(trndir,buf,ts);
3402 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
3403 !(lastdir = cp1 = strrchr(trndir,']')) &&
3404 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
3405 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
3407 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
3408 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
3409 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
3410 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
3411 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
3412 (ver || *cp3)))))) {
3414 set_vaxc_errno(RMS$_DIR);
3417 dirlen = cp2 - trndir;
3420 /* If we lead off with a device or rooted logical, add the MFD
3421 if we're specifying a top-level directory. */
3422 if (lastdir && *trndir == '/') {
3424 for (cp1 = lastdir - 1; cp1 > trndir; cp1--) {
3431 retlen = dirlen + (addmfd ? 13 : 6);
3432 if (buf) retspec = buf;
3433 else if (ts) Newx(retspec,retlen+1,char);
3434 else retspec = __fileify_retbuf;
3436 dirlen = lastdir - trndir;
3437 memcpy(retspec,trndir,dirlen);
3438 strcpy(&retspec[dirlen],"/000000");
3439 strcpy(&retspec[dirlen+7],lastdir);
3442 memcpy(retspec,trndir,dirlen);
3443 retspec[dirlen] = '\0';
3445 /* We've picked up everything up to the directory file name.
3446 Now just add the type and version, and we're set. */
3447 strcat(retspec,".dir;1");
3450 else { /* VMS-style directory spec */
3451 char esa[NAM$C_MAXRSS+1], term, *cp;
3452 unsigned long int sts, cmplen, haslower = 0;
3453 struct FAB dirfab = cc$rms_fab;
3454 struct NAM savnam, dirnam = cc$rms_nam;
3456 dirfab.fab$b_fns = strlen(dir);
3457 dirfab.fab$l_fna = trndir;
3458 dirfab.fab$l_nam = &dirnam;
3459 dirfab.fab$l_dna = ".DIR;1";
3460 dirfab.fab$b_dns = 6;
3461 dirnam.nam$b_ess = NAM$C_MAXRSS;
3462 dirnam.nam$l_esa = esa;
3464 for (cp = trndir; *cp; cp++)
3465 if (islower(*cp)) { haslower = 1; break; }
3466 if (!((sts = sys$parse(&dirfab))&1)) {
3467 if (dirfab.fab$l_sts == RMS$_DIR) {
3468 dirnam.nam$b_nop |= NAM$M_SYNCHK;
3469 sts = sys$parse(&dirfab) & 1;
3473 set_vaxc_errno(dirfab.fab$l_sts);
3479 if (sys$search(&dirfab)&1) { /* Does the file really exist? */
3480 /* Yes; fake the fnb bits so we'll check type below */
3481 dirnam.nam$l_fnb |= NAM$M_EXP_TYPE | NAM$M_EXP_VER;
3483 else { /* No; just work with potential name */
3484 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
3486 set_errno(EVMSERR); set_vaxc_errno(dirfab.fab$l_sts);
3487 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3488 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3493 if (!(dirnam.nam$l_fnb & (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
3494 cp1 = strchr(esa,']');
3495 if (!cp1) cp1 = strchr(esa,'>');
3496 if (cp1) { /* Should always be true */
3497 dirnam.nam$b_esl -= cp1 - esa - 1;
3498 memcpy(esa,cp1 + 1,dirnam.nam$b_esl);
3501 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
3502 /* Yep; check version while we're at it, if it's there. */
3503 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
3504 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
3505 /* Something other than .DIR[;1]. Bzzt. */
3506 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3507 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3509 set_vaxc_errno(RMS$_DIR);
3513 esa[dirnam.nam$b_esl] = '\0';
3514 if (dirnam.nam$l_fnb & NAM$M_EXP_NAME) {
3515 /* They provided at least the name; we added the type, if necessary, */
3516 if (buf) retspec = buf; /* in sys$parse() */
3517 else if (ts) Newx(retspec,dirnam.nam$b_esl+1,char);
3518 else retspec = __fileify_retbuf;
3519 strcpy(retspec,esa);
3520 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3521 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3524 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
3525 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
3527 dirnam.nam$b_esl -= 9;
3529 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
3530 if (cp1 == NULL) { /* should never happen */
3531 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3532 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3537 retlen = strlen(esa);
3538 if ((cp1 = strrchr(esa,'.')) != NULL) {
3539 /* There's more than one directory in the path. Just roll back. */
3541 if (buf) retspec = buf;
3542 else if (ts) Newx(retspec,retlen+7,char);
3543 else retspec = __fileify_retbuf;
3544 strcpy(retspec,esa);
3547 if (dirnam.nam$l_fnb & NAM$M_ROOT_DIR) {
3548 /* Go back and expand rooted logical name */
3549 dirnam.nam$b_nop = NAM$M_SYNCHK | NAM$M_NOCONCEAL;
3550 if (!(sys$parse(&dirfab) & 1)) {
3551 dirnam.nam$l_rlf = NULL;
3552 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3554 set_vaxc_errno(dirfab.fab$l_sts);
3557 retlen = dirnam.nam$b_esl - 9; /* esa - '][' - '].DIR;1' */
3558 if (buf) retspec = buf;
3559 else if (ts) Newx(retspec,retlen+16,char);
3560 else retspec = __fileify_retbuf;
3561 cp1 = strstr(esa,"][");
3562 if (!cp1) cp1 = strstr(esa,"]<");
3564 memcpy(retspec,esa,dirlen);
3565 if (!strncmp(cp1+2,"000000]",7)) {
3566 retspec[dirlen-1] = '\0';
3567 for (cp1 = retspec+dirlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
3568 if (*cp1 == '.') *cp1 = ']';
3570 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
3571 memcpy(cp1+1,"000000]",7);
3575 memcpy(retspec+dirlen,cp1+2,retlen-dirlen);
3576 retspec[retlen] = '\0';
3577 /* Convert last '.' to ']' */
3578 for (cp1 = retspec+retlen-1; *cp1 != '.' && *cp1 != '['; cp1--) ;
3579 if (*cp1 == '.') *cp1 = ']';
3581 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
3582 memcpy(cp1+1,"000000]",7);
3586 else { /* This is a top-level dir. Add the MFD to the path. */
3587 if (buf) retspec = buf;
3588 else if (ts) Newx(retspec,retlen+16,char);
3589 else retspec = __fileify_retbuf;
3592 while (*cp1 != ':') *(cp2++) = *(cp1++);
3593 strcpy(cp2,":[000000]");
3598 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3599 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3600 /* We've set up the string up through the filename. Add the
3601 type and version, and we're done. */
3602 strcat(retspec,".DIR;1");
3604 /* $PARSE may have upcased filespec, so convert output to lower
3605 * case if input contained any lowercase characters. */
3606 if (haslower) __mystrtolower(retspec);
3609 } /* end of do_fileify_dirspec() */
3611 /* External entry points */
3612 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
3613 { return do_fileify_dirspec(dir,buf,0); }
3614 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
3615 { return do_fileify_dirspec(dir,buf,1); }
3617 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
3618 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts)
3620 static char __pathify_retbuf[NAM$C_MAXRSS+1];
3621 unsigned long int retlen;
3622 char *retpath, *cp1, *cp2, trndir[NAM$C_MAXRSS+1];
3623 unsigned short int trnlnm_iter_count;
3626 if (!dir || !*dir) {
3627 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
3630 if (*dir) strcpy(trndir,dir);
3631 else getcwd(trndir,sizeof trndir - 1);
3633 trnlnm_iter_count = 0;
3634 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
3635 && my_trnlnm(trndir,trndir,0)) {
3636 trnlnm_iter_count++;
3637 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
3638 trnlen = strlen(trndir);
3640 /* Trap simple rooted lnms, and return lnm:[000000] */
3641 if (!strcmp(trndir+trnlen-2,".]")) {
3642 if (buf) retpath = buf;
3643 else if (ts) Newx(retpath,strlen(dir)+10,char);
3644 else retpath = __pathify_retbuf;
3645 strcpy(retpath,dir);
3646 strcat(retpath,":[000000]");
3651 /* At this point we do not work with *dir, but the copy in
3652 * *trndir that is modifiable.
3655 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
3656 if (*trndir == '.' && (*(trndir+1) == '\0' ||
3657 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
3658 retlen = 2 + (*(trndir+1) != '\0');
3660 if ( !(cp1 = strrchr(trndir,'/')) &&
3661 !(cp1 = strrchr(trndir,']')) &&
3662 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
3663 if ((cp2 = strchr(cp1,'.')) != NULL &&
3664 (*(cp2-1) != '/' || /* Trailing '.', '..', */
3665 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
3666 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
3667 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
3669 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
3670 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
3671 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
3672 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
3673 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
3674 (ver || *cp3)))))) {
3676 set_vaxc_errno(RMS$_DIR);
3679 retlen = cp2 - trndir + 1;
3681 else { /* No file type present. Treat the filename as a directory. */
3682 retlen = strlen(trndir) + 1;
3685 if (buf) retpath = buf;
3686 else if (ts) Newx(retpath,retlen+1,char);
3687 else retpath = __pathify_retbuf;
3688 strncpy(retpath, trndir, retlen-1);
3689 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
3690 retpath[retlen-1] = '/'; /* with '/', add it. */
3691 retpath[retlen] = '\0';
3693 else retpath[retlen-1] = '\0';
3695 else { /* VMS-style directory spec */
3696 char esa[NAM$C_MAXRSS+1], *cp;
3697 unsigned long int sts, cmplen, haslower;
3698 struct FAB dirfab = cc$rms_fab;
3699 struct NAM savnam, dirnam = cc$rms_nam;
3701 /* If we've got an explicit filename, we can just shuffle the string. */
3702 if ( ( (cp1 = strrchr(trndir,']')) != NULL ||
3703 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) {
3704 if ((cp2 = strchr(cp1,'.')) != NULL) {
3706 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
3707 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
3708 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
3709 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
3710 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
3711 (ver || *cp3)))))) {
3713 set_vaxc_errno(RMS$_DIR);
3717 else { /* No file type, so just draw name into directory part */
3718 for (cp2 = cp1; *cp2; cp2++) ;
3721 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
3723 /* We've now got a VMS 'path'; fall through */
3725 dirfab.fab$b_fns = strlen(trndir);
3726 dirfab.fab$l_fna = trndir;
3727 if (dir[dirfab.fab$b_fns-1] == ']' ||
3728 dir[dirfab.fab$b_fns-1] == '>' ||
3729 dir[dirfab.fab$b_fns-1] == ':') { /* It's already a VMS 'path' */
3730 if (buf) retpath = buf;
3731 else if (ts) Newx(retpath,strlen(dir)+1,char);
3732 else retpath = __pathify_retbuf;
3733 strcpy(retpath,trndir);
3736 dirfab.fab$l_dna = ".DIR;1";
3737 dirfab.fab$b_dns = 6;
3738 dirfab.fab$l_nam = &dirnam;
3739 dirnam.nam$b_ess = (unsigned char) sizeof esa - 1;
3740 dirnam.nam$l_esa = esa;
3742 for (cp = trndir; *cp; cp++)
3743 if (islower(*cp)) { haslower = 1; break; }
3745 if (!(sts = (sys$parse(&dirfab)&1))) {
3746 if (dirfab.fab$l_sts == RMS$_DIR) {
3747 dirnam.nam$b_nop |= NAM$M_SYNCHK;
3748 sts = sys$parse(&dirfab) & 1;
3752 set_vaxc_errno(dirfab.fab$l_sts);
3758 if (!(sys$search(&dirfab)&1)) { /* Does the file really exist? */
3759 if (dirfab.fab$l_sts != RMS$_FNF) {
3760 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3761 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3763 set_vaxc_errno(dirfab.fab$l_sts);
3766 dirnam = savnam; /* No; just work with potential name */
3769 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
3770 /* Yep; check version while we're at it, if it's there. */
3771 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
3772 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
3773 /* Something other than .DIR[;1]. Bzzt. */
3774 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3775 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3777 set_vaxc_errno(RMS$_DIR);
3781 /* OK, the type was fine. Now pull any file name into the
3783 if ((cp1 = strrchr(esa,']'))) *dirnam.nam$l_type = ']';
3785 cp1 = strrchr(esa,'>');
3786 *dirnam.nam$l_type = '>';
3789 *(dirnam.nam$l_type + 1) = '\0';
3790 retlen = dirnam.nam$l_type - esa + 2;
3791 if (buf) retpath = buf;
3792 else if (ts) Newx(retpath,retlen,char);
3793 else retpath = __pathify_retbuf;
3794 strcpy(retpath,esa);
3795 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
3796 dirfab.fab$b_dns = 0; (void) sys$parse(&dirfab,0,0);
3797 /* $PARSE may have upcased filespec, so convert output to lower
3798 * case if input contained any lowercase characters. */
3799 if (haslower) __mystrtolower(retpath);
3803 } /* end of do_pathify_dirspec() */
3805 /* External entry points */
3806 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
3807 { return do_pathify_dirspec(dir,buf,0); }
3808 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
3809 { return do_pathify_dirspec(dir,buf,1); }
3811 /*{{{ char *tounixspec[_ts](char *path, char *buf)*/
3812 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts)
3814 static char __tounixspec_retbuf[NAM$C_MAXRSS+1];
3815 char *dirend, *rslt, *cp1, *cp3, tmp[NAM$C_MAXRSS+1];
3817 int devlen, dirlen, retlen = NAM$C_MAXRSS+1;
3818 int expand = 1; /* guarantee room for leading and trailing slashes */
3819 unsigned short int trnlnm_iter_count;
3821 if (spec == NULL) return NULL;
3822 if (strlen(spec) > NAM$C_MAXRSS) return NULL;
3823 if (buf) rslt = buf;
3825 retlen = strlen(spec);
3826 cp1 = strchr(spec,'[');
3827 if (!cp1) cp1 = strchr(spec,'<');
3829 for (cp1++; *cp1; cp1++) {
3830 if (*cp1 == '-') expand++; /* VMS '-' ==> Unix '../' */
3831 if (*cp1 == '.' && *(cp1+1) == '.' && *(cp1+2) == '.')
3832 { expand++; cp1 +=2; } /* VMS '...' ==> Unix '/.../' */
3835 Newx(rslt,retlen+2+2*expand,char);
3837 else rslt = __tounixspec_retbuf;
3838 if (strchr(spec,'/') != NULL) {
3845 dirend = strrchr(spec,']');
3846 if (dirend == NULL) dirend = strrchr(spec,'>');
3847 if (dirend == NULL) dirend = strchr(spec,':');
3848 if (dirend == NULL) {
3852 if (*cp2 != '[' && *cp2 != '<') {
3855 else { /* the VMS spec begins with directories */
3857 if (*cp2 == ']' || *cp2 == '>') {
3858 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
3861 else if ( *cp2 != '.' && *cp2 != '-') { /* add the implied device */
3862 if (getcwd(tmp,sizeof tmp,1) == NULL) {
3863 if (ts) Safefree(rslt);
3866 trnlnm_iter_count = 0;
3869 while (*cp3 != ':' && *cp3) cp3++;
3871 if (strchr(cp3,']') != NULL) break;
3872 trnlnm_iter_count++;
3873 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
3874 } while (vmstrnenv(tmp,tmp,0,fildev,0));
3876 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
3877 retlen = devlen + dirlen;
3878 Renew(rslt,retlen+1+2*expand,char);
3884 *(cp1++) = *(cp3++);
3885 if (cp1 - rslt > NAM$C_MAXRSS && !ts && !buf) return NULL; /* No room */
3889 else if ( *cp2 == '.') {
3890 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
3891 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
3897 for (; cp2 <= dirend; cp2++) {
3900 if (*(cp2+1) == '[') cp2++;
3902 else if (*cp2 == ']' || *cp2 == '>') {
3903 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
3905 else if (*cp2 == '.') {
3907 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
3908 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
3909 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
3910 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
3911 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
3913 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
3914 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
3918 else if (*cp2 == '-') {
3919 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
3920 while (*cp2 == '-') {
3922 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
3924 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
3925 if (ts) Safefree(rslt); /* filespecs like */
3926 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
3930 else *(cp1++) = *cp2;
3932 else *(cp1++) = *cp2;
3934 while (*cp2) *(cp1++) = *(cp2++);
3939 } /* end of do_tounixspec() */
3941 /* External entry points */
3942 char *Perl_tounixspec(pTHX_ const char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
3943 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
3945 /*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
3946 static char *mp_do_tovmsspec(pTHX_ const char *path, char *buf, int ts) {
3947 static char __tovmsspec_retbuf[NAM$C_MAXRSS+1];
3948 char *rslt, *dirend;
3951 unsigned long int infront = 0, hasdir = 1;
3953 if (path == NULL) return NULL;
3954 if (buf) rslt = buf;
3955 else if (ts) Newx(rslt,strlen(path)+9,char);
3956 else rslt = __tovmsspec_retbuf;
3957 if (strpbrk(path,"]:>") ||
3958 (dirend = strrchr(path,'/')) == NULL) {
3959 if (path[0] == '.') {
3960 if (path[1] == '\0') strcpy(rslt,"[]");
3961 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
3962 else strcpy(rslt,path); /* probably garbage */
3964 else strcpy(rslt,path);
3967 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
3968 if (!*(dirend+2)) dirend +=2;
3969 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
3970 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
3975 char trndev[NAM$C_MAXRSS+1];
3979 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
3981 if (!buf & ts) Renew(rslt,18,char);
3982 strcpy(rslt,"sys$disk:[000000]");
3985 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
3987 islnm = my_trnlnm(rslt,trndev,0);
3988 trnend = islnm ? strlen(trndev) - 1 : 0;
3989 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
3990 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
3991 /* If the first element of the path is a logical name, determine
3992 * whether it has to be translated so we can add more directories. */
3993 if (!islnm || rooted) {
3996 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
4000 if (cp2 != dirend) {
4001 if (!buf && ts) Renew(rslt,strlen(path)-strlen(rslt)+trnend+4,char);
4002 strcpy(rslt,trndev);
4003 cp1 = rslt + trnend;
4018 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
4019 cp2 += 2; /* skip over "./" - it's redundant */
4020 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
4022 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
4023 *(cp1++) = '-'; /* "../" --> "-" */
4026 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
4027 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
4028 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
4029 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
4032 if (cp2 > dirend) cp2 = dirend;
4034 else *(cp1++) = '.';
4036 for (; cp2 < dirend; cp2++) {
4038 if (*(cp2-1) == '/') continue;
4039 if (*(cp1-1) != '.') *(cp1++) = '.';
4042 else if (!infront && *cp2 == '.') {
4043 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
4044 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
4045 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
4046 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
4047 else if (*(cp1-2) == '[') *(cp1-1) = '-';
4048 else { /* back up over previous directory name */
4050 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
4051 if (*(cp1-1) == '[') {
4052 memcpy(cp1,"000000.",7);
4057 if (cp2 == dirend) break;
4059 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
4060 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
4061 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
4062 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
4064 *(cp1++) = '.'; /* Simulate trailing '/' */
4065 cp2 += 2; /* for loop will incr this to == dirend */
4067 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
4069 else *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
4072 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
4073 if (*cp2 == '.') *(cp1++) = '_';
4074 else *(cp1++) = *cp2;
4078 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
4079 if (hasdir) *(cp1++) = ']';
4080 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
4081 while (*cp2) *(cp1++) = *(cp2++);
4086 } /* end of do_tovmsspec() */
4088 /* External entry points */
4089 char *Perl_tovmsspec(pTHX_ const char *path, char *buf) { return do_tovmsspec(path,buf,0); }
4090 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf) { return do_tovmsspec(path,buf,1); }
4092 /*{{{ char *tovmspath[_ts](char *path, char *buf)*/
4093 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts) {
4094 static char __tovmspath_retbuf[NAM$C_MAXRSS+1];
4096 char pathified[NAM$C_MAXRSS+1], vmsified[NAM$C_MAXRSS+1], *cp;
4098 if (path == NULL) return NULL;
4099 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
4100 if (do_tovmsspec(pathified,buf ? buf : vmsified,0) == NULL) return NULL;
4101 if (buf) return buf;
4103 vmslen = strlen(vmsified);
4104 Newx(cp,vmslen+1,char);
4105 memcpy(cp,vmsified,vmslen);
4110 strcpy(__tovmspath_retbuf,vmsified);
4111 return __tovmspath_retbuf;
4114 } /* end of do_tovmspath() */
4116 /* External entry points */
4117 char *Perl_tovmspath(pTHX_ const char *path, char *buf) { return do_tovmspath(path,buf,0); }
4118 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf) { return do_tovmspath(path,buf,1); }
4121 /*{{{ char *tounixpath[_ts](char *path, char *buf)*/
4122 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts) {
4123 static char __tounixpath_retbuf[NAM$C_MAXRSS+1];
4125 char pathified[NAM$C_MAXRSS+1], unixified[NAM$C_MAXRSS+1], *cp;
4127 if (path == NULL) return NULL;
4128 if (do_pathify_dirspec(path,pathified,0) == NULL) return NULL;
4129 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) return NULL;
4130 if (buf) return buf;
4132 unixlen = strlen(unixified);
4133 Newx(cp,unixlen+1,char);
4134 memcpy(cp,unixified,unixlen);
4139 strcpy(__tounixpath_retbuf,unixified);
4140 return __tounixpath_retbuf;
4143 } /* end of do_tounixpath() */
4145 /* External entry points */
4146 char *Perl_tounixpath(pTHX_ const char *path, char *buf) { return do_tounixpath(path,buf,0); }
4147 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf) { return do_tounixpath(path,buf,1); }
4150 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
4152 *****************************************************************************
4154 * Copyright (C) 1989-1994 by *
4155 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
4157 * Permission is hereby granted for the reproduction of this software, *
4158 * on condition that this copyright notice is included in the reproduction, *
4159 * and that such reproduction is not for purposes of profit or material *
4162 * 27-Aug-1994 Modified for inclusion in perl5 *
4163 * by Charles Bailey bailey@newman.upenn.edu *
4164 *****************************************************************************
4168 * getredirection() is intended to aid in porting C programs
4169 * to VMS (Vax-11 C). The native VMS environment does not support
4170 * '>' and '<' I/O redirection, or command line wild card expansion,
4171 * or a command line pipe mechanism using the '|' AND background
4172 * command execution '&'. All of these capabilities are provided to any
4173 * C program which calls this procedure as the first thing in the
4175 * The piping mechanism will probably work with almost any 'filter' type
4176 * of program. With suitable modification, it may useful for other
4177 * portability problems as well.
4179 * Author: Mark Pizzolato mark@infocomm.com
4183 struct list_item *next;
4187 static void add_item(struct list_item **head,
4188 struct list_item **tail,
4192 static void mp_expand_wild_cards(pTHX_ char *item,
4193 struct list_item **head,
4194 struct list_item **tail,
4197 static int background_process(pTHX_ int argc, char **argv);
4199 static void pipe_and_fork(pTHX_ char **cmargv);
4201 /*{{{ void getredirection(int *ac, char ***av)*/
4203 mp_getredirection(pTHX_ int *ac, char ***av)
4205 * Process vms redirection arg's. Exit if any error is seen.
4206 * If getredirection() processes an argument, it is erased
4207 * from the vector. getredirection() returns a new argc and argv value.
4208 * In the event that a background command is requested (by a trailing "&"),
4209 * this routine creates a background subprocess, and simply exits the program.
4211 * Warning: do not try to simplify the code for vms. The code
4212 * presupposes that getredirection() is called before any data is
4213 * read from stdin or written to stdout.
4215 * Normal usage is as follows:
4221 * getredirection(&argc, &argv);
4225 int argc = *ac; /* Argument Count */
4226 char **argv = *av; /* Argument Vector */
4227 char *ap; /* Argument pointer */
4228 int j; /* argv[] index */
4229 int item_count = 0; /* Count of Items in List */
4230 struct list_item *list_head = 0; /* First Item in List */
4231 struct list_item *list_tail; /* Last Item in List */
4232 char *in = NULL; /* Input File Name */
4233 char *out = NULL; /* Output File Name */
4234 char *outmode = "w"; /* Mode to Open Output File */
4235 char *err = NULL; /* Error File Name */
4236 char *errmode = "w"; /* Mode to Open Error File */
4237 int cmargc = 0; /* Piped Command Arg Count */
4238 char **cmargv = NULL;/* Piped Command Arg Vector */
4241 * First handle the case where the last thing on the line ends with
4242 * a '&'. This indicates the desire for the command to be run in a
4243 * subprocess, so we satisfy that desire.
4246 if (0 == strcmp("&", ap))
4247 exit(background_process(aTHX_ --argc, argv));
4248 if (*ap && '&' == ap[strlen(ap)-1])
4250 ap[strlen(ap)-1] = '\0';
4251 exit(background_process(aTHX_ argc, argv));
4254 * Now we handle the general redirection cases that involve '>', '>>',
4255 * '<', and pipes '|'.
4257 for (j = 0; j < argc; ++j)
4259 if (0 == strcmp("<", argv[j]))
4263 fprintf(stderr,"No input file after < on command line");
4264 exit(LIB$_WRONUMARG);
4269 if ('<' == *(ap = argv[j]))
4274 if (0 == strcmp(">", ap))
4278 fprintf(stderr,"No output file after > on command line");
4279 exit(LIB$_WRONUMARG);
4298 fprintf(stderr,"No output file after > or >> on command line");
4299 exit(LIB$_WRONUMARG);
4303 if (('2' == *ap) && ('>' == ap[1]))
4320 fprintf(stderr,"No output file after 2> or 2>> on command line");
4321 exit(LIB$_WRONUMARG);
4325 if (0 == strcmp("|", argv[j]))
4329 fprintf(stderr,"No command into which to pipe on command line");
4330 exit(LIB$_WRONUMARG);
4332 cmargc = argc-(j+1);
4333 cmargv = &argv[j+1];
4337 if ('|' == *(ap = argv[j]))
4345 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
4348 * Allocate and fill in the new argument vector, Some Unix's terminate
4349 * the list with an extra null pointer.
4351 Newx(argv, item_count+1, char *);
4353 for (j = 0; j < item_count; ++j, list_head = list_head->next)
4354 argv[j] = list_head->value;
4360 fprintf(stderr,"'|' and '>' may not both be specified on command line");
4361 exit(LIB$_INVARGORD);
4363 pipe_and_fork(aTHX_ cmargv);
4366 /* Check for input from a pipe (mailbox) */
4368 if (in == NULL && 1 == isapipe(0))
4370 char mbxname[L_tmpnam];
4372 long int dvi_item = DVI$_DEVBUFSIZ;
4373 $DESCRIPTOR(mbxnam, "");
4374 $DESCRIPTOR(mbxdevnam, "");
4376 /* Input from a pipe, reopen it in binary mode to disable */
4377 /* carriage control processing. */
4379 fgetname(stdin, mbxname);
4380 mbxnam.dsc$a_pointer = mbxname;
4381 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
4382 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
4383 mbxdevnam.dsc$a_pointer = mbxname;
4384 mbxdevnam.dsc$w_length = sizeof(mbxname);
4385 dvi_item = DVI$_DEVNAM;
4386 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
4387 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
4390 freopen(mbxname, "rb", stdin);
4393 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
4397 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
4399 fprintf(stderr,"Can't open input file %s as stdin",in);
4402 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
4404 fprintf(stderr,"Can't open output file %s as stdout",out);
4407 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
4410 if (strcmp(err,"&1") == 0) {
4411 dup2(fileno(stdout), fileno(stderr));
4412 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
4415 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
4417 fprintf(stderr,"Can't open error file %s as stderr",err);
4421 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
4425 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
4428 #ifdef ARGPROC_DEBUG
4429 PerlIO_printf(Perl_debug_log, "Arglist:\n");
4430 for (j = 0; j < *ac; ++j)
4431 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
4433 /* Clear errors we may have hit expanding wildcards, so they don't
4434 show up in Perl's $! later */
4435 set_errno(0); set_vaxc_errno(1);
4436 } /* end of getredirection() */
4439 static void add_item(struct list_item **head,
4440 struct list_item **tail,
4446 Newx(*head,1,struct list_item);
4450 Newx((*tail)->next,1,struct list_item);
4451 *tail = (*tail)->next;
4453 (*tail)->value = value;
4457 static void mp_expand_wild_cards(pTHX_ char *item,
4458 struct list_item **head,
4459 struct list_item **tail,
4463 unsigned long int context = 0;
4470 char vmsspec[NAM$C_MAXRSS+1];
4471 $DESCRIPTOR(filespec, "");
4472 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
4473 $DESCRIPTOR(resultspec, "");
4474 unsigned long int zero = 0, sts;
4476 for (cp = item; *cp; cp++) {
4477 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
4478 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
4480 if (!*cp || isspace(*cp))
4482 add_item(head, tail, item, count);
4487 /* "double quoted" wild card expressions pass as is */
4488 /* From DCL that means using e.g.: */
4489 /* perl program """perl.*""" */
4490 item_len = strlen(item);
4491 if ( '"' == *item && '"' == item[item_len-1] )
4494 item[item_len-2] = '\0';
4495 add_item(head, tail, item, count);
4499 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
4500 resultspec.dsc$b_class = DSC$K_CLASS_D;
4501 resultspec.dsc$a_pointer = NULL;
4502 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
4503 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
4504 if (!isunix || !filespec.dsc$a_pointer)
4505 filespec.dsc$a_pointer = item;
4506 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
4508 * Only return version specs, if the caller specified a version
4510 had_version = strchr(item, ';');
4512 * Only return device and directory specs, if the caller specifed either.
4514 had_device = strchr(item, ':');
4515 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
4517 while (1 == (1 & (sts = lib$find_file(&filespec, &resultspec, &context,
4518 &defaultspec, 0, 0, &zero))))
4523 Newx(string,resultspec.dsc$w_length+1,char);
4524 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
4525 string[resultspec.dsc$w_length] = '\0';
4526 if (NULL == had_version)
4527 *((char *)strrchr(string, ';')) = '\0';
4528 if ((!had_directory) && (had_device == NULL))
4530 if (NULL == (devdir = strrchr(string, ']')))
4531 devdir = strrchr(string, '>');
4532 strcpy(string, devdir + 1);
4535 * Be consistent with what the C RTL has already done to the rest of
4536 * the argv items and lowercase all of these names.
4538 for (c = string; *c; ++c)
4541 if (isunix) trim_unixpath(string,item,1);
4542 add_item(head, tail, string, count);
4545 if (sts != RMS$_NMF)
4547 set_vaxc_errno(sts);
4550 case RMS$_FNF: case RMS$_DNF:
4551 set_errno(ENOENT); break;
4553 set_errno(ENOTDIR); break;
4555 set_errno(ENODEV); break;
4556 case RMS$_FNM: case RMS$_SYN:
4557 set_errno(EINVAL); break;
4559 set_errno(EACCES); break;
4561 _ckvmssts_noperl(sts);
4565 add_item(head, tail, item, count);
4566 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
4567 _ckvmssts_noperl(lib$find_file_end(&context));
4570 static int child_st[2];/* Event Flag set when child process completes */
4572 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
4574 static unsigned long int exit_handler(int *status)
4578 if (0 == child_st[0])
4580 #ifdef ARGPROC_DEBUG
4581 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
4583 fflush(stdout); /* Have to flush pipe for binary data to */
4584 /* terminate properly -- <tp@mccall.com> */
4585 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
4586 sys$dassgn(child_chan);
4588 sys$synch(0, child_st);
4593 static void sig_child(int chan)
4595 #ifdef ARGPROC_DEBUG
4596 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
4598 if (child_st[0] == 0)
4602 static struct exit_control_block exit_block =
4607 &exit_block.exit_status,
4612 pipe_and_fork(pTHX_ char **cmargv)
4615 struct dsc$descriptor_s *vmscmd;
4616 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
4617 int sts, j, l, ismcr, quote, tquote = 0;
4619 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
4620 vms_execfree(vmscmd);
4625 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
4626 && toupper(*(q+2)) == 'R' && !*(q+3);
4628 while (q && l < MAX_DCL_LINE_LENGTH) {
4630 if (j > 0 && quote) {
4636 if (ismcr && j > 1) quote = 1;
4637 tquote = (strchr(q,' ')) != NULL || *q == '\0';
4640 if (quote || tquote) {
4646 if ((quote||tquote) && *q == '"') {
4656 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
4658 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
4662 static int background_process(pTHX_ int argc, char **argv)
4664 char command[2048] = "$";
4665 $DESCRIPTOR(value, "");
4666 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
4667 static $DESCRIPTOR(null, "NLA0:");
4668 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
4670 $DESCRIPTOR(pidstr, "");
4672 unsigned long int flags = 17, one = 1, retsts;
4674 strcat(command, argv[0]);
4677 strcat(command, " \"");
4678 strcat(command, *(++argv));
4679 strcat(command, "\"");
4681 value.dsc$a_pointer = command;
4682 value.dsc$w_length = strlen(value.dsc$a_pointer);
4683 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
4684 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
4685 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
4686 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
4689 _ckvmssts_noperl(retsts);
4691 #ifdef ARGPROC_DEBUG
4692 PerlIO_printf(Perl_debug_log, "%s\n", command);
4694 sprintf(pidstring, "%08X", pid);
4695 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
4696 pidstr.dsc$a_pointer = pidstring;
4697 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
4698 lib$set_symbol(&pidsymbol, &pidstr);
4702 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
4705 /* OS-specific initialization at image activation (not thread startup) */
4706 /* Older VAXC header files lack these constants */
4707 #ifndef JPI$_RIGHTS_SIZE
4708 # define JPI$_RIGHTS_SIZE 817
4710 #ifndef KGB$M_SUBSYSTEM
4711 # define KGB$M_SUBSYSTEM 0x8
4714 /*{{{void vms_image_init(int *, char ***)*/
4716 vms_image_init(int *argcp, char ***argvp)
4718 char eqv[LNM$C_NAMLENGTH+1] = "";
4719 unsigned int len, tabct = 8, tabidx = 0;
4720 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
4721 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
4722 unsigned short int dummy, rlen;
4723 struct dsc$descriptor_s **tabvec;
4724 #if defined(PERL_IMPLICIT_CONTEXT)
4727 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
4728 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
4729 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
4732 #ifdef KILL_BY_SIGPRC
4733 (void) Perl_csighandler_init();
4736 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
4737 _ckvmssts_noperl(iosb[0]);
4738 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
4739 if (iprv[i]) { /* Running image installed with privs? */
4740 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
4745 /* Rights identifiers might trigger tainting as well. */
4746 if (!will_taint && (rlen || rsz)) {
4747 while (rlen < rsz) {
4748 /* We didn't get all the identifiers on the first pass. Allocate a
4749 * buffer much larger than $GETJPI wants (rsz is size in bytes that
4750 * were needed to hold all identifiers at time of last call; we'll
4751 * allocate that many unsigned long ints), and go back and get 'em.
4752 * If it gave us less than it wanted to despite ample buffer space,
4753 * something's broken. Is your system missing a system identifier?
4755 if (rsz <= jpilist[1].buflen) {
4756 /* Perl_croak accvios when used this early in startup. */
4757 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
4758 rsz, (unsigned long) jpilist[1].buflen,
4759 "Check your rights database for corruption.\n");
4762 if (jpilist[1].bufadr != rlst) Safefree(jpilist[1].bufadr);
4763 jpilist[1].bufadr = Newx(mask,rsz,unsigned long int);
4764 jpilist[1].buflen = rsz * sizeof(unsigned long int);
4765 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
4766 _ckvmssts_noperl(iosb[0]);
4768 mask = jpilist[1].bufadr;
4769 /* Check attribute flags for each identifier (2nd longword); protected
4770 * subsystem identifiers trigger tainting.
4772 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
4773 if (mask[i] & KGB$M_SUBSYSTEM) {
4778 if (mask != rlst) Safefree(mask);
4780 /* We need to use this hack to tell Perl it should run with tainting,
4781 * since its tainting flag may be part of the PL_curinterp struct, which
4782 * hasn't been allocated when vms_image_init() is called.
4785 char **newargv, **oldargv;
4787 Newx(newargv,(*argcp)+2,char *);
4788 newargv[0] = oldargv[0];
4789 Newx(newargv[1],3,char);
4790 strcpy(newargv[1], "-T");
4791 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
4793 newargv[*argcp] = NULL;
4794 /* We orphan the old argv, since we don't know where it's come from,
4795 * so we don't know how to free it.
4799 else { /* Did user explicitly request tainting? */
4801 char *cp, **av = *argvp;
4802 for (i = 1; i < *argcp; i++) {
4803 if (*av[i] != '-') break;
4804 for (cp = av[i]+1; *cp; cp++) {
4805 if (*cp == 'T') { will_taint = 1; break; }
4806 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
4807 strchr("DFIiMmx",*cp)) break;
4809 if (will_taint) break;
4814 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
4816 if (!tabidx) Newx(tabvec,tabct,struct dsc$descriptor_s *);
4817 else if (tabidx >= tabct) {
4819 Renew(tabvec,tabct,struct dsc$descriptor_s *);
4821 Newx(tabvec[tabidx],1,struct dsc$descriptor_s);
4822 tabvec[tabidx]->dsc$w_length = 0;
4823 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
4824 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
4825 tabvec[tabidx]->dsc$a_pointer = NULL;
4826 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
4828 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
4830 getredirection(argcp,argvp);
4831 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
4833 # include <reentrancy.h>
4834 (void) decc$set_reentrancy(C$C_MULTITHREAD);
4843 * Trim Unix-style prefix off filespec, so it looks like what a shell
4844 * glob expansion would return (i.e. from specified prefix on, not
4845 * full path). Note that returned filespec is Unix-style, regardless
4846 * of whether input filespec was VMS-style or Unix-style.
4848 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
4849 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
4850 * vector of options; at present, only bit 0 is used, and if set tells
4851 * trim unixpath to try the current default directory as a prefix when
4852 * presented with a possibly ambiguous ... wildcard.
4854 * Returns !=0 on success, with trimmed filespec replacing contents of
4855 * fspec, and 0 on failure, with contents of fpsec unchanged.
4857 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
4859 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
4861 char unixified[NAM$C_MAXRSS+1], unixwild[NAM$C_MAXRSS+1],
4862 *template, *base, *end, *cp1, *cp2;
4863 register int tmplen, reslen = 0, dirs = 0;
4865 if (!wildspec || !fspec) return 0;
4866 template = unixwild;
4867 if (strpbrk(wildspec,"]>:") != NULL) {
4868 if (do_tounixspec(wildspec,unixwild,0) == NULL) return 0;
4871 strncpy(unixwild, wildspec, NAM$C_MAXRSS);
4872 unixwild[NAM$C_MAXRSS] = 0;
4874 if (strpbrk(fspec,"]>:") != NULL) {
4875 if (do_tounixspec(fspec,unixified,0) == NULL) return 0;
4876 else base = unixified;
4877 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
4878 * check to see that final result fits into (isn't longer than) fspec */
4879 reslen = strlen(fspec);
4883 /* No prefix or absolute path on wildcard, so nothing to remove */
4884 if (!*template || *template == '/') {
4885 if (base == fspec) return 1;
4886 tmplen = strlen(unixified);
4887 if (tmplen > reslen) return 0; /* not enough space */
4888 /* Copy unixified resultant, including trailing NUL */
4889 memmove(fspec,unixified,tmplen+1);
4893 for (end = base; *end; end++) ; /* Find end of resultant filespec */
4894 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
4895 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
4896 for (cp1 = end ;cp1 >= base; cp1--)
4897 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
4899 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
4903 char tpl[NAM$C_MAXRSS+1], lcres[NAM$C_MAXRSS+1];
4904 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
4905 int ells = 1, totells, segdirs, match;
4906 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, tpl},
4907 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4909 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
4911 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
4912 if (ellipsis == template && opts & 1) {
4913 /* Template begins with an ellipsis. Since we can't tell how many
4914 * directory names at the front of the resultant to keep for an
4915 * arbitrary starting point, we arbitrarily choose the current
4916 * default directory as a starting point. If it's there as a prefix,
4917 * clip it off. If not, fall through and act as if the leading
4918 * ellipsis weren't there (i.e. return shortest possible path that
4919 * could match template).
4921 if (getcwd(tpl, sizeof tpl,0) == NULL) return 0;
4922 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
4923 if (_tolower(*cp1) != _tolower(*cp2)) break;
4924 segdirs = dirs - totells; /* Min # of dirs we must have left */
4925 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
4926 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
4927 memcpy(fspec,cp2+1,end - cp2);
4931 /* First off, back up over constant elements at end of path */
4933 for (front = end ; front >= base; front--)
4934 if (*front == '/' && !dirs--) { front++; break; }
4936 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + sizeof lcres;
4937 cp1++,cp2++) *cp2 = _tolower(*cp1); /* Make lc copy for match */
4938 if (cp1 != '\0') return 0; /* Path too long. */
4940 *cp2 = '\0'; /* Pick up with memcpy later */
4941 lcfront = lcres + (front - base);
4942 /* Now skip over each ellipsis and try to match the path in front of it. */
4944 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
4945 if (*(cp1) == '.' && *(cp1+1) == '.' &&
4946 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
4947 if (cp1 < template) break; /* template started with an ellipsis */
4948 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
4949 ellipsis = cp1; continue;
4951 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
4953 for (segdirs = 0, cp2 = tpl;
4954 cp1 <= ellipsis - 1 && cp2 <= tpl + sizeof tpl;
4956 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
4957 else *cp2 = _tolower(*cp1); /* else lowercase for match */
4958 if (*cp2 == '/') segdirs++;
4960 if (cp1 != ellipsis - 1) return 0; /* Path too long */
4961 /* Back up at least as many dirs as in template before matching */
4962 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
4963 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
4964 for (match = 0; cp1 > lcres;) {
4965 resdsc.dsc$a_pointer = cp1;
4966 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
4968 if (match == 1) lcfront = cp1;
4970 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
4972 if (!match) return 0; /* Can't find prefix ??? */
4973 if (match > 1 && opts & 1) {
4974 /* This ... wildcard could cover more than one set of dirs (i.e.
4975 * a set of similar dir names is repeated). If the template
4976 * contains more than 1 ..., upstream elements could resolve the
4977 * ambiguity, but it's not worth a full backtracking setup here.
4978 * As a quick heuristic, clip off the current default directory
4979 * if it's present to find the trimmed spec, else use the
4980 * shortest string that this ... could cover.
4982 char def[NAM$C_MAXRSS+1], *st;
4984 if (getcwd(def, sizeof def,0) == NULL) return 0;
4985 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
4986 if (_tolower(*cp1) != _tolower(*cp2)) break;
4987 segdirs = dirs - totells; /* Min # of dirs we must have left */
4988 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
4989 if (*cp1 == '\0' && *cp2 == '/') {
4990 memcpy(fspec,cp2+1,end - cp2);
4993 /* Nope -- stick with lcfront from above and keep going. */
4996 memcpy(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
5001 } /* end of trim_unixpath() */
5006 * VMS readdir() routines.
5007 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
5009 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
5010 * Minor modifications to original routines.
5013 /* readdir may have been redefined by reentr.h, so make sure we get
5014 * the local version for what we do here.
5019 #if !defined(PERL_IMPLICIT_CONTEXT)
5020 # define readdir Perl_readdir
5022 # define readdir(a) Perl_readdir(aTHX_ a)
5025 /* Number of elements in vms_versions array */
5026 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
5029 * Open a directory, return a handle for later use.
5031 /*{{{ DIR *opendir(char*name) */
5033 Perl_opendir(pTHX_ const char *name)
5036 char dir[NAM$C_MAXRSS+1];
5039 if (do_tovmspath(name,dir,0) == NULL) {
5042 /* Check access before stat; otherwise stat does not
5043 * accurately report whether it's a directory.
5045 if (!cando_by_name(S_IRUSR,0,dir)) {
5046 /* cando_by_name has already set errno */
5049 if (flex_stat(dir,&sb) == -1) return NULL;
5050 if (!S_ISDIR(sb.st_mode)) {
5051 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
5054 /* Get memory for the handle, and the pattern. */
5056 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
5058 /* Fill in the fields; mainly playing with the descriptor. */
5059 (void)sprintf(dd->pattern, "%s*.*",dir);
5062 dd->vms_wantversions = 0;
5063 dd->pat.dsc$a_pointer = dd->pattern;
5064 dd->pat.dsc$w_length = strlen(dd->pattern);
5065 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
5066 dd->pat.dsc$b_class = DSC$K_CLASS_S;
5067 #if defined(USE_ITHREADS)
5068 Newx(dd->mutex,1,perl_mutex);
5069 MUTEX_INIT( (perl_mutex *) dd->mutex );
5075 } /* end of opendir() */
5079 * Set the flag to indicate we want versions or not.
5081 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
5083 vmsreaddirversions(DIR *dd, int flag)
5085 dd->vms_wantversions = flag;
5090 * Free up an opened directory.
5092 /*{{{ void closedir(DIR *dd)*/
5096 (void)lib$find_file_end(&dd->context);
5097 Safefree(dd->pattern);
5098 #if defined(USE_ITHREADS)
5099 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
5100 Safefree(dd->mutex);
5102 Safefree((char *)dd);
5107 * Collect all the version numbers for the current file.
5110 collectversions(pTHX_ DIR *dd)
5112 struct dsc$descriptor_s pat;
5113 struct dsc$descriptor_s res;
5115 char *p, *text, buff[sizeof dd->entry.d_name];
5117 unsigned long context, tmpsts;
5119 /* Convenient shorthand. */
5122 /* Add the version wildcard, ignoring the "*.*" put on before */
5123 i = strlen(dd->pattern);
5124 Newx(text,i + e->d_namlen + 3,char);
5125 (void)strcpy(text, dd->pattern);
5126 (void)sprintf(&text[i - 3], "%s;*", e->d_name);
5128 /* Set up the pattern descriptor. */
5129 pat.dsc$a_pointer = text;
5130 pat.dsc$w_length = i + e->d_namlen - 1;
5131 pat.dsc$b_dtype = DSC$K_DTYPE_T;
5132 pat.dsc$b_class = DSC$K_CLASS_S;
5134 /* Set up result descriptor. */
5135 res.dsc$a_pointer = buff;
5136 res.dsc$w_length = sizeof buff - 2;
5137 res.dsc$b_dtype = DSC$K_DTYPE_T;
5138 res.dsc$b_class = DSC$K_CLASS_S;
5140 /* Read files, collecting versions. */
5141 for (context = 0, e->vms_verscount = 0;
5142 e->vms_verscount < VERSIZE(e);
5143 e->vms_verscount++) {
5144 tmpsts = lib$find_file(&pat, &res, &context);
5145 if (tmpsts == RMS$_NMF || context == 0) break;
5147 buff[sizeof buff - 1] = '\0';
5148 if ((p = strchr(buff, ';')))
5149 e->vms_versions[e->vms_verscount] = atoi(p + 1);
5151 e->vms_versions[e->vms_verscount] = -1;
5154 _ckvmssts(lib$find_file_end(&context));
5157 } /* end of collectversions() */
5160 * Read the next entry from the directory.
5162 /*{{{ struct dirent *readdir(DIR *dd)*/
5164 Perl_readdir(pTHX_ DIR *dd)
5166 struct dsc$descriptor_s res;
5167 char *p, buff[sizeof dd->entry.d_name];
5168 unsigned long int tmpsts;
5170 /* Set up result descriptor, and get next file. */
5171 res.dsc$a_pointer = buff;
5172 res.dsc$w_length = sizeof buff - 2;
5173 res.dsc$b_dtype = DSC$K_DTYPE_T;
5174 res.dsc$b_class = DSC$K_CLASS_S;
5175 tmpsts = lib$find_file(&dd->pat, &res, &dd->context);
5176 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
5177 if (!(tmpsts & 1)) {
5178 set_vaxc_errno(tmpsts);
5181 set_errno(EACCES); break;
5183 set_errno(ENODEV); break;
5185 set_errno(ENOTDIR); break;
5186 case RMS$_FNF: case RMS$_DNF:
5187 set_errno(ENOENT); break;
5194 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
5195 buff[sizeof buff - 1] = '\0';
5196 for (p = buff; *p; p++) *p = _tolower(*p);
5197 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
5200 /* Skip any directory component and just copy the name. */
5201 if ((p = strchr(buff, ']'))) (void)strcpy(dd->entry.d_name, p + 1);
5202 else (void)strcpy(dd->entry.d_name, buff);
5204 /* Clobber the version. */
5205 if ((p = strchr(dd->entry.d_name, ';'))) *p = '\0';
5207 dd->entry.d_namlen = strlen(dd->entry.d_name);
5208 dd->entry.vms_verscount = 0;
5209 if (dd->vms_wantversions) collectversions(aTHX_ dd);
5212 } /* end of readdir() */
5216 * Read the next entry from the directory -- thread-safe version.
5218 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
5220 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
5224 MUTEX_LOCK( (perl_mutex *) dd->mutex );
5226 entry = readdir(dd);
5228 retval = ( *result == NULL ? errno : 0 );
5230 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
5234 } /* end of readdir_r() */
5238 * Return something that can be used in a seekdir later.
5240 /*{{{ long telldir(DIR *dd)*/
5249 * Return to a spot where we used to be. Brute force.
5251 /*{{{ void seekdir(DIR *dd,long count)*/
5253 Perl_seekdir(pTHX_ DIR *dd, long count)
5255 int vms_wantversions;
5257 /* If we haven't done anything yet... */
5261 /* Remember some state, and clear it. */
5262 vms_wantversions = dd->vms_wantversions;
5263 dd->vms_wantversions = 0;
5264 _ckvmssts(lib$find_file_end(&dd->context));
5267 /* The increment is in readdir(). */
5268 for (dd->count = 0; dd->count < count; )
5271 dd->vms_wantversions = vms_wantversions;
5273 } /* end of seekdir() */
5276 /* VMS subprocess management
5278 * my_vfork() - just a vfork(), after setting a flag to record that
5279 * the current script is trying a Unix-style fork/exec.
5281 * vms_do_aexec() and vms_do_exec() are called in response to the
5282 * perl 'exec' function. If this follows a vfork call, then they
5283 * call out the regular perl routines in doio.c which do an
5284 * execvp (for those who really want to try this under VMS).
5285 * Otherwise, they do exactly what the perl docs say exec should
5286 * do - terminate the current script and invoke a new command
5287 * (See below for notes on command syntax.)
5289 * do_aspawn() and do_spawn() implement the VMS side of the perl
5290 * 'system' function.
5292 * Note on command arguments to perl 'exec' and 'system': When handled
5293 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
5294 * are concatenated to form a DCL command string. If the first arg
5295 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
5296 * the command string is handed off to DCL directly. Otherwise,
5297 * the first token of the command is taken as the filespec of an image
5298 * to run. The filespec is expanded using a default type of '.EXE' and
5299 * the process defaults for device, directory, etc., and if found, the resultant
5300 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
5301 * the command string as parameters. This is perhaps a bit complicated,
5302 * but I hope it will form a happy medium between what VMS folks expect
5303 * from lib$spawn and what Unix folks expect from exec.
5306 static int vfork_called;
5308 /*{{{int my_vfork()*/
5319 vms_execfree(struct dsc$descriptor_s *vmscmd)
5322 if (vmscmd->dsc$a_pointer) {
5323 Safefree(vmscmd->dsc$a_pointer);
5330 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
5332 char *junk, *tmps = Nullch;
5333 register size_t cmdlen = 0;
5340 tmps = SvPV(really,rlen);
5347 for (idx++; idx <= sp; idx++) {
5349 junk = SvPVx(*idx,rlen);
5350 cmdlen += rlen ? rlen + 1 : 0;
5353 Newx(PL_Cmd,cmdlen+1,char);
5355 if (tmps && *tmps) {
5356 strcpy(PL_Cmd,tmps);
5359 else *PL_Cmd = '\0';
5360 while (++mark <= sp) {
5362 char *s = SvPVx(*mark,n_a);
5364 if (*PL_Cmd) strcat(PL_Cmd," ");
5370 } /* end of setup_argstr() */
5373 static unsigned long int
5374 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
5375 struct dsc$descriptor_s **pvmscmd)
5377 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
5378 $DESCRIPTOR(defdsc,".EXE");
5379 $DESCRIPTOR(defdsc2,".");
5380 $DESCRIPTOR(resdsc,resspec);
5381 struct dsc$descriptor_s *vmscmd;
5382 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5383 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
5384 register char *s, *rest, *cp, *wordbreak;
5389 Newx(vmscmd,sizeof(struct dsc$descriptor_s),struct dsc$descriptor_s);
5391 /* Make a copy for modification */
5392 cmdlen = strlen(incmd);
5393 Newx(cmd, cmdlen+1, char);
5394 strncpy(cmd, incmd, cmdlen);
5397 vmscmd->dsc$a_pointer = NULL;
5398 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
5399 vmscmd->dsc$b_class = DSC$K_CLASS_S;
5400 vmscmd->dsc$w_length = 0;
5401 if (pvmscmd) *pvmscmd = vmscmd;
5403 if (suggest_quote) *suggest_quote = 0;
5405 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
5406 return CLI$_BUFOVF; /* continuation lines currently unsupported */
5412 while (*s && isspace(*s)) s++;
5414 if (*s == '@' || *s == '$') {
5415 vmsspec[0] = *s; rest = s + 1;
5416 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
5418 else { cp = vmsspec; rest = s; }
5419 if (*rest == '.' || *rest == '/') {
5422 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
5423 rest++, cp2++) *cp2 = *rest;
5425 if (do_tovmsspec(resspec,cp,0)) {
5428 for (cp2 = vmsspec + strlen(vmsspec);
5429 *rest && cp2 - vmsspec < sizeof vmsspec;
5430 rest++, cp2++) *cp2 = *rest;
5435 /* Intuit whether verb (first word of cmd) is a DCL command:
5436 * - if first nonspace char is '@', it's a DCL indirection
5438 * - if verb contains a filespec separator, it's not a DCL command
5439 * - if it doesn't, caller tells us whether to default to a DCL
5440 * command, or to a local image unless told it's DCL (by leading '$')
5444 if (suggest_quote) *suggest_quote = 1;
5446 register char *filespec = strpbrk(s,":<[.;");
5447 rest = wordbreak = strpbrk(s," \"\t/");
5448 if (!wordbreak) wordbreak = s + strlen(s);
5449 if (*s == '$') check_img = 0;
5450 if (filespec && (filespec < wordbreak)) isdcl = 0;
5451 else isdcl = !check_img;
5455 imgdsc.dsc$a_pointer = s;
5456 imgdsc.dsc$w_length = wordbreak - s;
5457 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
5459 _ckvmssts(lib$find_file_end(&cxt));
5460 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
5461 if (!(retsts & 1) && *s == '$') {
5462 _ckvmssts(lib$find_file_end(&cxt));
5463 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
5464 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,0,&flags);
5466 _ckvmssts(lib$find_file_end(&cxt));
5467 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,0,&flags);
5471 _ckvmssts(lib$find_file_end(&cxt));
5476 while (*s && !isspace(*s)) s++;
5479 /* check that it's really not DCL with no file extension */
5480 fp = fopen(resspec,"r","ctx=bin","shr=get");
5482 char b[4] = {0,0,0,0};
5483 read(fileno(fp),b,4);
5484 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
5487 if (check_img && isdcl) return RMS$_FNF;
5489 if (cando_by_name(S_IXUSR,0,resspec)) {
5490 Newx(vmscmd->dsc$a_pointer,7 + s - resspec + (rest ? strlen(rest) : 0),char);
5492 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
5493 if (suggest_quote) *suggest_quote = 1;
5495 strcpy(vmscmd->dsc$a_pointer,"@");
5496 if (suggest_quote) *suggest_quote = 1;
5498 strcat(vmscmd->dsc$a_pointer,resspec);
5499 if (rest) strcat(vmscmd->dsc$a_pointer,rest);
5500 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
5502 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
5504 else retsts = RMS$_PRV;
5507 /* It's either a DCL command or we couldn't find a suitable image */
5508 vmscmd->dsc$w_length = strlen(cmd);
5509 /* if (cmd == PL_Cmd) {
5510 vmscmd->dsc$a_pointer = PL_Cmd;
5511 if (suggest_quote) *suggest_quote = 1;
5514 vmscmd->dsc$a_pointer = savepvn(cmd,vmscmd->dsc$w_length);
5518 /* check if it's a symbol (for quoting purposes) */
5519 if (suggest_quote && !*suggest_quote) {
5521 char equiv[LNM$C_NAMLENGTH];
5522 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5523 eqvdsc.dsc$a_pointer = equiv;
5525 iss = lib$get_symbol(vmscmd,&eqvdsc);
5526 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
5528 if (!(retsts & 1)) {
5529 /* just hand off status values likely to be due to user error */
5530 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
5531 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
5532 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
5533 else { _ckvmssts(retsts); }
5536 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
5538 } /* end of setup_cmddsc() */
5541 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
5543 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
5546 if (vfork_called) { /* this follows a vfork - act Unixish */
5548 if (vfork_called < 0) {
5549 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
5552 else return do_aexec(really,mark,sp);
5554 /* no vfork - act VMSish */
5555 return vms_do_exec(setup_argstr(aTHX_ really,mark,sp));
5560 } /* end of vms_do_aexec() */
5563 /* {{{bool vms_do_exec(char *cmd) */
5565 Perl_vms_do_exec(pTHX_ const char *cmd)
5567 struct dsc$descriptor_s *vmscmd;
5569 if (vfork_called) { /* this follows a vfork - act Unixish */
5571 if (vfork_called < 0) {
5572 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
5575 else return do_exec(cmd);
5578 { /* no vfork - act VMSish */
5579 unsigned long int retsts;
5582 TAINT_PROPER("exec");
5583 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
5584 retsts = lib$do_command(vmscmd);
5587 case RMS$_FNF: case RMS$_DNF:
5588 set_errno(ENOENT); break;
5590 set_errno(ENOTDIR); break;
5592 set_errno(ENODEV); break;
5594 set_errno(EACCES); break;
5596 set_errno(EINVAL); break;
5597 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
5598 set_errno(E2BIG); break;
5599 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
5600 _ckvmssts(retsts); /* fall through */
5601 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
5604 set_vaxc_errno(retsts);
5605 if (ckWARN(WARN_EXEC)) {
5606 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
5607 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
5609 vms_execfree(vmscmd);
5614 } /* end of vms_do_exec() */
5617 unsigned long int Perl_do_spawn(pTHX_ const char *);
5619 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
5621 Perl_do_aspawn(pTHX_ void *really,void **mark,void **sp)
5623 if (sp > mark) return do_spawn(setup_argstr(aTHX_ (SV *)really,(SV **)mark,(SV **)sp));
5626 } /* end of do_aspawn() */
5629 /* {{{unsigned long int do_spawn(char *cmd) */
5631 Perl_do_spawn(pTHX_ const char *cmd)
5633 unsigned long int sts, substs;
5636 TAINT_PROPER("spawn");
5637 if (!cmd || !*cmd) {
5638 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
5641 case RMS$_FNF: case RMS$_DNF:
5642 set_errno(ENOENT); break;
5644 set_errno(ENOTDIR); break;
5646 set_errno(ENODEV); break;
5648 set_errno(EACCES); break;
5650 set_errno(EINVAL); break;
5651 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
5652 set_errno(E2BIG); break;
5653 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
5654 _ckvmssts(sts); /* fall through */
5655 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
5658 set_vaxc_errno(sts);
5659 if (ckWARN(WARN_EXEC)) {
5660 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
5668 fp = safe_popen(aTHX_ cmd, "nW", (int *)&sts);
5673 } /* end of do_spawn() */
5677 static unsigned int *sockflags, sockflagsize;
5680 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
5681 * routines found in some versions of the CRTL can't deal with sockets.
5682 * We don't shim the other file open routines since a socket isn't
5683 * likely to be opened by a name.
5685 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
5686 FILE *my_fdopen(int fd, const char *mode)
5688 FILE *fp = fdopen(fd, (char *) mode);
5691 unsigned int fdoff = fd / sizeof(unsigned int);
5692 struct stat sbuf; /* native stat; we don't need flex_stat */
5693 if (!sockflagsize || fdoff > sockflagsize) {
5694 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
5695 else Newx (sockflags,fdoff+2,unsigned int);
5696 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
5697 sockflagsize = fdoff + 2;
5699 if (fstat(fd,&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
5700 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
5709 * Clear the corresponding bit when the (possibly) socket stream is closed.
5710 * There still a small hole: we miss an implicit close which might occur
5711 * via freopen(). >> Todo
5713 /*{{{ int my_fclose(FILE *fp)*/
5714 int my_fclose(FILE *fp) {
5716 unsigned int fd = fileno(fp);
5717 unsigned int fdoff = fd / sizeof(unsigned int);
5719 if (sockflagsize && fdoff <= sockflagsize)
5720 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
5728 * A simple fwrite replacement which outputs itmsz*nitm chars without
5729 * introducing record boundaries every itmsz chars.
5730 * We are using fputs, which depends on a terminating null. We may
5731 * well be writing binary data, so we need to accommodate not only
5732 * data with nulls sprinkled in the middle but also data with no null
5735 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
5737 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
5739 register char *cp, *end, *cpd, *data;
5740 register unsigned int fd = fileno(dest);
5741 register unsigned int fdoff = fd / sizeof(unsigned int);
5743 int bufsize = itmsz * nitm + 1;
5745 if (fdoff < sockflagsize &&
5746 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
5747 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
5751 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
5752 memcpy( data, src, itmsz*nitm );
5753 data[itmsz*nitm] = '\0';
5755 end = data + itmsz * nitm;
5756 retval = (int) nitm; /* on success return # items written */
5759 while (cpd <= end) {
5760 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
5761 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
5763 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
5767 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
5770 } /* end of my_fwrite() */
5773 /*{{{ int my_flush(FILE *fp)*/
5775 Perl_my_flush(pTHX_ FILE *fp)
5778 if ((res = fflush(fp)) == 0 && fp) {
5779 #ifdef VMS_DO_SOCKETS
5781 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
5783 res = fsync(fileno(fp));
5786 * If the flush succeeded but set end-of-file, we need to clear
5787 * the error because our caller may check ferror(). BTW, this
5788 * probably means we just flushed an empty file.
5790 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
5797 * Here are replacements for the following Unix routines in the VMS environment:
5798 * getpwuid Get information for a particular UIC or UID
5799 * getpwnam Get information for a named user
5800 * getpwent Get information for each user in the rights database
5801 * setpwent Reset search to the start of the rights database
5802 * endpwent Finish searching for users in the rights database
5804 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
5805 * (defined in pwd.h), which contains the following fields:-
5807 * char *pw_name; Username (in lower case)
5808 * char *pw_passwd; Hashed password
5809 * unsigned int pw_uid; UIC
5810 * unsigned int pw_gid; UIC group number
5811 * char *pw_unixdir; Default device/directory (VMS-style)
5812 * char *pw_gecos; Owner name
5813 * char *pw_dir; Default device/directory (Unix-style)
5814 * char *pw_shell; Default CLI name (eg. DCL)
5816 * If the specified user does not exist, getpwuid and getpwnam return NULL.
5818 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
5819 * not the UIC member number (eg. what's returned by getuid()),
5820 * getpwuid() can accept either as input (if uid is specified, the caller's
5821 * UIC group is used), though it won't recognise gid=0.
5823 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
5824 * information about other users in your group or in other groups, respectively.
5825 * If the required privilege is not available, then these routines fill only
5826 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
5829 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
5832 /* sizes of various UAF record fields */
5833 #define UAI$S_USERNAME 12
5834 #define UAI$S_IDENT 31
5835 #define UAI$S_OWNER 31
5836 #define UAI$S_DEFDEV 31
5837 #define UAI$S_DEFDIR 63
5838 #define UAI$S_DEFCLI 31
5841 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
5842 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
5843 (uic).uic$v_group != UIC$K_WILD_GROUP)
5845 static char __empty[]= "";
5846 static struct passwd __passwd_empty=
5847 {(char *) __empty, (char *) __empty, 0, 0,
5848 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
5849 static int contxt= 0;
5850 static struct passwd __pwdcache;
5851 static char __pw_namecache[UAI$S_IDENT+1];
5854 * This routine does most of the work extracting the user information.
5856 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
5859 unsigned char length;
5860 char pw_gecos[UAI$S_OWNER+1];
5862 static union uicdef uic;
5864 unsigned char length;
5865 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
5868 unsigned char length;
5869 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
5872 unsigned char length;
5873 char pw_shell[UAI$S_DEFCLI+1];
5875 static char pw_passwd[UAI$S_PWD+1];
5877 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
5878 struct dsc$descriptor_s name_desc;
5879 unsigned long int sts;
5881 static struct itmlst_3 itmlst[]= {
5882 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
5883 {sizeof(uic), UAI$_UIC, &uic, &luic},
5884 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
5885 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
5886 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
5887 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
5888 {0, 0, NULL, NULL}};
5890 name_desc.dsc$w_length= strlen(name);
5891 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
5892 name_desc.dsc$b_class= DSC$K_CLASS_S;
5893 name_desc.dsc$a_pointer= (char *) name;
5895 /* Note that sys$getuai returns many fields as counted strings. */
5896 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
5897 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
5898 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
5900 else { _ckvmssts(sts); }
5901 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
5903 if ((int) owner.length < lowner) lowner= (int) owner.length;
5904 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
5905 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
5906 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
5907 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
5908 owner.pw_gecos[lowner]= '\0';
5909 defdev.pw_dir[ldefdev+ldefdir]= '\0';
5910 defcli.pw_shell[ldefcli]= '\0';
5911 if (valid_uic(uic)) {
5912 pwd->pw_uid= uic.uic$l_uic;
5913 pwd->pw_gid= uic.uic$v_group;
5916 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
5917 pwd->pw_passwd= pw_passwd;
5918 pwd->pw_gecos= owner.pw_gecos;
5919 pwd->pw_dir= defdev.pw_dir;
5920 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
5921 pwd->pw_shell= defcli.pw_shell;
5922 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
5924 ldir= strlen(pwd->pw_unixdir) - 1;
5925 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
5928 strcpy(pwd->pw_unixdir, pwd->pw_dir);
5929 __mystrtolower(pwd->pw_unixdir);
5934 * Get information for a named user.
5936 /*{{{struct passwd *getpwnam(char *name)*/
5937 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
5939 struct dsc$descriptor_s name_desc;
5941 unsigned long int status, sts;
5943 __pwdcache = __passwd_empty;
5944 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
5945 /* We still may be able to determine pw_uid and pw_gid */
5946 name_desc.dsc$w_length= strlen(name);
5947 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
5948 name_desc.dsc$b_class= DSC$K_CLASS_S;
5949 name_desc.dsc$a_pointer= (char *) name;
5950 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
5951 __pwdcache.pw_uid= uic.uic$l_uic;
5952 __pwdcache.pw_gid= uic.uic$v_group;
5955 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
5956 set_vaxc_errno(sts);
5957 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
5960 else { _ckvmssts(sts); }
5963 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
5964 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
5965 __pwdcache.pw_name= __pw_namecache;
5967 } /* end of my_getpwnam() */
5971 * Get information for a particular UIC or UID.
5972 * Called by my_getpwent with uid=-1 to list all users.
5974 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
5975 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
5977 const $DESCRIPTOR(name_desc,__pw_namecache);
5978 unsigned short lname;
5980 unsigned long int status;
5982 if (uid == (unsigned int) -1) {
5984 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
5985 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
5986 set_vaxc_errno(status);
5987 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
5991 else { _ckvmssts(status); }
5992 } while (!valid_uic (uic));
5996 if (!uic.uic$v_group)
5997 uic.uic$v_group= PerlProc_getgid();
5999 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
6000 else status = SS$_IVIDENT;
6001 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
6002 status == RMS$_PRV) {
6003 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
6006 else { _ckvmssts(status); }
6008 __pw_namecache[lname]= '\0';
6009 __mystrtolower(__pw_namecache);
6011 __pwdcache = __passwd_empty;
6012 __pwdcache.pw_name = __pw_namecache;
6014 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
6015 The identifier's value is usually the UIC, but it doesn't have to be,
6016 so if we can, we let fillpasswd update this. */
6017 __pwdcache.pw_uid = uic.uic$l_uic;
6018 __pwdcache.pw_gid = uic.uic$v_group;
6020 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
6023 } /* end of my_getpwuid() */
6027 * Get information for next user.
6029 /*{{{struct passwd *my_getpwent()*/
6030 struct passwd *Perl_my_getpwent(pTHX)
6032 return (my_getpwuid((unsigned int) -1));
6037 * Finish searching rights database for users.
6039 /*{{{void my_endpwent()*/
6040 void Perl_my_endpwent(pTHX)
6043 _ckvmssts(sys$finish_rdb(&contxt));
6049 #ifdef HOMEGROWN_POSIX_SIGNALS
6050 /* Signal handling routines, pulled into the core from POSIX.xs.
6052 * We need these for threads, so they've been rolled into the core,
6053 * rather than left in POSIX.xs.
6055 * (DRS, Oct 23, 1997)
6058 /* sigset_t is atomic under VMS, so these routines are easy */
6059 /*{{{int my_sigemptyset(sigset_t *) */
6060 int my_sigemptyset(sigset_t *set) {
6061 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
6067 /*{{{int my_sigfillset(sigset_t *)*/
6068 int my_sigfillset(sigset_t *set) {
6070 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
6071 for (i = 0; i < NSIG; i++) *set |= (1 << i);
6077 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
6078 int my_sigaddset(sigset_t *set, int sig) {
6079 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
6080 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
6081 *set |= (1 << (sig - 1));
6087 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
6088 int my_sigdelset(sigset_t *set, int sig) {
6089 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
6090 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
6091 *set &= ~(1 << (sig - 1));
6097 /*{{{int my_sigismember(sigset_t *set, int sig)*/
6098 int my_sigismember(sigset_t *set, int sig) {
6099 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
6100 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
6101 return *set & (1 << (sig - 1));
6106 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
6107 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
6110 /* If set and oset are both null, then things are badly wrong. Bail out. */
6111 if ((oset == NULL) && (set == NULL)) {
6112 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
6116 /* If set's null, then we're just handling a fetch. */
6118 tempmask = sigblock(0);
6123 tempmask = sigsetmask(*set);
6126 tempmask = sigblock(*set);
6129 tempmask = sigblock(0);
6130 sigsetmask(*oset & ~tempmask);
6133 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6138 /* Did they pass us an oset? If so, stick our holding mask into it */
6145 #endif /* HOMEGROWN_POSIX_SIGNALS */
6148 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
6149 * my_utime(), and flex_stat(), all of which operate on UTC unless
6150 * VMSISH_TIMES is true.
6152 /* method used to handle UTC conversions:
6153 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
6155 static int gmtime_emulation_type;
6156 /* number of secs to add to UTC POSIX-style time to get local time */
6157 static long int utc_offset_secs;
6159 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
6160 * in vmsish.h. #undef them here so we can call the CRTL routines
6169 * DEC C previous to 6.0 corrupts the behavior of the /prefix
6170 * qualifier with the extern prefix pragma. This provisional
6171 * hack circumvents this prefix pragma problem in previous
6174 #if defined(__VMS_VER) && __VMS_VER >= 70000000
6175 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
6176 # pragma __extern_prefix save
6177 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
6178 # define gmtime decc$__utctz_gmtime
6179 # define localtime decc$__utctz_localtime
6180 # define time decc$__utc_time
6181 # pragma __extern_prefix restore
6183 struct tm *gmtime(), *localtime();
6189 static time_t toutc_dst(time_t loc) {
6192 if ((rsltmp = localtime(&loc)) == NULL) return -1;
6193 loc -= utc_offset_secs;
6194 if (rsltmp->tm_isdst) loc -= 3600;
6197 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
6198 ((gmtime_emulation_type || my_time(NULL)), \
6199 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
6200 ((secs) - utc_offset_secs))))
6202 static time_t toloc_dst(time_t utc) {
6205 utc += utc_offset_secs;
6206 if ((rsltmp = localtime(&utc)) == NULL) return -1;
6207 if (rsltmp->tm_isdst) utc += 3600;
6210 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
6211 ((gmtime_emulation_type || my_time(NULL)), \
6212 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
6213 ((secs) + utc_offset_secs))))
6215 #ifndef RTL_USES_UTC
6218 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
6219 DST starts on 1st sun of april at 02:00 std time
6220 ends on last sun of october at 02:00 dst time
6221 see the UCX management command reference, SET CONFIG TIMEZONE
6222 for formatting info.
6224 No, it's not as general as it should be, but then again, NOTHING
6225 will handle UK times in a sensible way.
6230 parse the DST start/end info:
6231 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
6235 tz_parse_startend(char *s, struct tm *w, int *past)
6237 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
6238 int ly, dozjd, d, m, n, hour, min, sec, j, k;
6243 if (!past) return 0;
6246 if (w->tm_year % 4 == 0) ly = 1;
6247 if (w->tm_year % 100 == 0) ly = 0;
6248 if (w->tm_year+1900 % 400 == 0) ly = 1;
6251 dozjd = isdigit(*s);
6252 if (*s == 'J' || *s == 'j' || dozjd) {
6253 if (!dozjd && !isdigit(*++s)) return 0;
6256 d = d*10 + *s++ - '0';
6258 d = d*10 + *s++ - '0';
6261 if (d == 0) return 0;
6262 if (d > 366) return 0;
6264 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
6267 } else if (*s == 'M' || *s == 'm') {
6268 if (!isdigit(*++s)) return 0;
6270 if (isdigit(*s)) m = 10*m + *s++ - '0';
6271 if (*s != '.') return 0;
6272 if (!isdigit(*++s)) return 0;
6274 if (n < 1 || n > 5) return 0;
6275 if (*s != '.') return 0;
6276 if (!isdigit(*++s)) return 0;
6278 if (d > 6) return 0;
6282 if (!isdigit(*++s)) return 0;
6284 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
6286 if (!isdigit(*++s)) return 0;
6288 if (isdigit(*s)) min = 10*min + *s++ - '0';
6290 if (!isdigit(*++s)) return 0;
6292 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
6302 if (w->tm_yday < d) goto before;
6303 if (w->tm_yday > d) goto after;
6305 if (w->tm_mon+1 < m) goto before;
6306 if (w->tm_mon+1 > m) goto after;
6308 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
6309 k = d - j; /* mday of first d */
6311 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
6312 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
6313 if (w->tm_mday < k) goto before;
6314 if (w->tm_mday > k) goto after;
6317 if (w->tm_hour < hour) goto before;
6318 if (w->tm_hour > hour) goto after;
6319 if (w->tm_min < min) goto before;
6320 if (w->tm_min > min) goto after;
6321 if (w->tm_sec < sec) goto before;
6335 /* parse the offset: (+|-)hh[:mm[:ss]] */
6338 tz_parse_offset(char *s, int *offset)
6340 int hour = 0, min = 0, sec = 0;
6343 if (!offset) return 0;
6345 if (*s == '-') {neg++; s++;}
6347 if (!isdigit(*s)) return 0;
6349 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
6350 if (hour > 24) return 0;
6352 if (!isdigit(*++s)) return 0;
6354 if (isdigit(*s)) min = min*10 + (*s++ - '0');
6355 if (min > 59) return 0;
6357 if (!isdigit(*++s)) return 0;
6359 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
6360 if (sec > 59) return 0;
6364 *offset = (hour*60+min)*60 + sec;
6365 if (neg) *offset = -*offset;
6370 input time is w, whatever type of time the CRTL localtime() uses.
6371 sets dst, the zone, and the gmtoff (seconds)
6373 caches the value of TZ and UCX$TZ env variables; note that
6374 my_setenv looks for these and sets a flag if they're changed
6377 We have to watch out for the "australian" case (dst starts in
6378 october, ends in april)...flagged by "reverse" and checked by
6379 scanning through the months of the previous year.
6384 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
6389 char *dstzone, *tz, *s_start, *s_end;
6390 int std_off, dst_off, isdst;
6391 int y, dststart, dstend;
6392 static char envtz[1025]; /* longer than any logical, symbol, ... */
6393 static char ucxtz[1025];
6394 static char reversed = 0;
6400 reversed = -1; /* flag need to check */
6401 envtz[0] = ucxtz[0] = '\0';
6402 tz = my_getenv("TZ",0);
6403 if (tz) strcpy(envtz, tz);
6404 tz = my_getenv("UCX$TZ",0);
6405 if (tz) strcpy(ucxtz, tz);
6406 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
6409 if (!*tz) tz = ucxtz;
6412 while (isalpha(*s)) s++;
6413 s = tz_parse_offset(s, &std_off);
6415 if (!*s) { /* no DST, hurray we're done! */
6421 while (isalpha(*s)) s++;
6422 s2 = tz_parse_offset(s, &dst_off);
6426 dst_off = std_off - 3600;
6429 if (!*s) { /* default dst start/end?? */
6430 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
6431 s = strchr(ucxtz,',');
6433 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
6435 if (*s != ',') return 0;
6438 when = _toutc(when); /* convert to utc */
6439 when = when - std_off; /* convert to pseudolocal time*/
6441 w2 = localtime(&when);
6444 s = tz_parse_startend(s_start,w2,&dststart);
6446 if (*s != ',') return 0;
6449 when = _toutc(when); /* convert to utc */
6450 when = when - dst_off; /* convert to pseudolocal time*/
6451 w2 = localtime(&when);
6452 if (w2->tm_year != y) { /* spans a year, just check one time */
6453 when += dst_off - std_off;
6454 w2 = localtime(&when);
6457 s = tz_parse_startend(s_end,w2,&dstend);
6460 if (reversed == -1) { /* need to check if start later than end */
6464 if (when < 2*365*86400) {
6465 when += 2*365*86400;
6469 w2 =localtime(&when);
6470 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
6472 for (j = 0; j < 12; j++) {
6473 w2 =localtime(&when);
6474 (void) tz_parse_startend(s_start,w2,&ds);
6475 (void) tz_parse_startend(s_end,w2,&de);
6476 if (ds != de) break;
6480 if (de && !ds) reversed = 1;
6483 isdst = dststart && !dstend;
6484 if (reversed) isdst = dststart || !dstend;
6487 if (dst) *dst = isdst;
6488 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
6489 if (isdst) tz = dstzone;
6491 while(isalpha(*tz)) *zone++ = *tz++;
6497 #endif /* !RTL_USES_UTC */
6499 /* my_time(), my_localtime(), my_gmtime()
6500 * By default traffic in UTC time values, using CRTL gmtime() or
6501 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
6502 * Note: We need to use these functions even when the CRTL has working
6503 * UTC support, since they also handle C<use vmsish qw(times);>
6505 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
6506 * Modified by Charles Bailey <bailey@newman.upenn.edu>
6509 /*{{{time_t my_time(time_t *timep)*/
6510 time_t Perl_my_time(pTHX_ time_t *timep)
6515 if (gmtime_emulation_type == 0) {
6517 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
6518 /* results of calls to gmtime() and localtime() */
6519 /* for same &base */
6521 gmtime_emulation_type++;
6522 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
6523 char off[LNM$C_NAMLENGTH+1];;
6525 gmtime_emulation_type++;
6526 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
6527 gmtime_emulation_type++;
6528 utc_offset_secs = 0;
6529 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
6531 else { utc_offset_secs = atol(off); }
6533 else { /* We've got a working gmtime() */
6534 struct tm gmt, local;
6537 tm_p = localtime(&base);
6539 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
6540 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
6541 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
6542 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
6548 # ifdef RTL_USES_UTC
6549 if (VMSISH_TIME) when = _toloc(when);
6551 if (!VMSISH_TIME) when = _toutc(when);
6554 if (timep != NULL) *timep = when;
6557 } /* end of my_time() */
6561 /*{{{struct tm *my_gmtime(const time_t *timep)*/
6563 Perl_my_gmtime(pTHX_ const time_t *timep)
6569 if (timep == NULL) {
6570 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6573 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
6577 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
6579 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
6580 return gmtime(&when);
6582 /* CRTL localtime() wants local time as input, so does no tz correction */
6583 rsltmp = localtime(&when);
6584 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
6587 } /* end of my_gmtime() */
6591 /*{{{struct tm *my_localtime(const time_t *timep)*/
6593 Perl_my_localtime(pTHX_ const time_t *timep)
6595 time_t when, whenutc;
6599 if (timep == NULL) {
6600 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
6603 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
6604 if (gmtime_emulation_type == 0) (void) my_time(NULL); /* Init UTC */
6607 # ifdef RTL_USES_UTC
6609 if (VMSISH_TIME) when = _toutc(when);
6611 /* CRTL localtime() wants UTC as input, does tz correction itself */
6612 return localtime(&when);
6614 # else /* !RTL_USES_UTC */
6617 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
6618 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
6621 #ifndef RTL_USES_UTC
6622 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
6623 when = whenutc - offset; /* pseudolocal time*/
6626 /* CRTL localtime() wants local time as input, so does no tz correction */
6627 rsltmp = localtime(&when);
6628 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
6632 } /* end of my_localtime() */
6635 /* Reset definitions for later calls */
6636 #define gmtime(t) my_gmtime(t)
6637 #define localtime(t) my_localtime(t)
6638 #define time(t) my_time(t)
6641 /* my_utime - update modification time of a file
6642 * calling sequence is identical to POSIX utime(), but under
6643 * VMS only the modification time is changed; ODS-2 does not
6644 * maintain access times. Restrictions differ from the POSIX
6645 * definition in that the time can be changed as long as the
6646 * caller has permission to execute the necessary IO$_MODIFY $QIO;
6647 * no separate checks are made to insure that the caller is the
6648 * owner of the file or has special privs enabled.
6649 * Code here is based on Joe Meadows' FILE utility.
6652 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
6653 * to VMS epoch (01-JAN-1858 00:00:00.00)
6654 * in 100 ns intervals.
6656 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
6658 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
6659 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
6662 long int bintime[2], len = 2, lowbit, unixtime,
6663 secscale = 10000000; /* seconds --> 100 ns intervals */
6664 unsigned long int chan, iosb[2], retsts;
6665 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
6666 struct FAB myfab = cc$rms_fab;
6667 struct NAM mynam = cc$rms_nam;
6668 #if defined (__DECC) && defined (__VAX)
6669 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
6670 * at least through VMS V6.1, which causes a type-conversion warning.
6672 # pragma message save
6673 # pragma message disable cvtdiftypes
6675 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
6676 struct fibdef myfib;
6677 #if defined (__DECC) && defined (__VAX)
6678 /* This should be right after the declaration of myatr, but due
6679 * to a bug in VAX DEC C, this takes effect a statement early.
6681 # pragma message restore
6683 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
6684 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
6685 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
6687 if (file == NULL || *file == '\0') {
6689 set_vaxc_errno(LIB$_INVARG);
6692 if (do_tovmsspec((char *)file,vmsspec,0) == NULL) return -1;
6694 if (utimes != NULL) {
6695 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
6696 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
6697 * Since time_t is unsigned long int, and lib$emul takes a signed long int
6698 * as input, we force the sign bit to be clear by shifting unixtime right
6699 * one bit, then multiplying by an extra factor of 2 in lib$emul().
6701 lowbit = (utimes->modtime & 1) ? secscale : 0;
6702 unixtime = (long int) utimes->modtime;
6704 /* If input was UTC; convert to local for sys svc */
6705 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
6707 unixtime >>= 1; secscale <<= 1;
6708 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
6709 if (!(retsts & 1)) {
6711 set_vaxc_errno(retsts);
6714 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
6715 if (!(retsts & 1)) {
6717 set_vaxc_errno(retsts);
6722 /* Just get the current time in VMS format directly */
6723 retsts = sys$gettim(bintime);
6724 if (!(retsts & 1)) {
6726 set_vaxc_errno(retsts);
6731 myfab.fab$l_fna = vmsspec;
6732 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
6733 myfab.fab$l_nam = &mynam;
6734 mynam.nam$l_esa = esa;
6735 mynam.nam$b_ess = (unsigned char) sizeof esa;
6736 mynam.nam$l_rsa = rsa;
6737 mynam.nam$b_rss = (unsigned char) sizeof rsa;
6739 /* Look for the file to be affected, letting RMS parse the file
6740 * specification for us as well. I have set errno using only
6741 * values documented in the utime() man page for VMS POSIX.
6743 retsts = sys$parse(&myfab,0,0);
6744 if (!(retsts & 1)) {
6745 set_vaxc_errno(retsts);
6746 if (retsts == RMS$_PRV) set_errno(EACCES);
6747 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
6748 else set_errno(EVMSERR);
6751 retsts = sys$search(&myfab,0,0);
6752 if (!(retsts & 1)) {
6753 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
6754 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
6755 set_vaxc_errno(retsts);
6756 if (retsts == RMS$_PRV) set_errno(EACCES);
6757 else if (retsts == RMS$_FNF) set_errno(ENOENT);
6758 else set_errno(EVMSERR);
6762 devdsc.dsc$w_length = mynam.nam$b_dev;
6763 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
6765 retsts = sys$assign(&devdsc,&chan,0,0);
6766 if (!(retsts & 1)) {
6767 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
6768 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
6769 set_vaxc_errno(retsts);
6770 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
6771 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
6772 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
6773 else set_errno(EVMSERR);
6777 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
6778 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
6780 memset((void *) &myfib, 0, sizeof myfib);
6781 #if defined(__DECC) || defined(__DECCXX)
6782 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
6783 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
6784 /* This prevents the revision time of the file being reset to the current
6785 * time as a result of our IO$_MODIFY $QIO. */
6786 myfib.fib$l_acctl = FIB$M_NORECORD;
6788 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
6789 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
6790 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
6792 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
6793 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
6794 myfab.fab$b_dns = 0; (void) sys$parse(&myfab,0,0);
6795 _ckvmssts(sys$dassgn(chan));
6796 if (retsts & 1) retsts = iosb[0];
6797 if (!(retsts & 1)) {
6798 set_vaxc_errno(retsts);
6799 if (retsts == SS$_NOPRIV) set_errno(EACCES);
6800 else set_errno(EVMSERR);
6805 } /* end of my_utime() */
6809 * flex_stat, flex_fstat
6810 * basic stat, but gets it right when asked to stat
6811 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
6814 /* encode_dev packs a VMS device name string into an integer to allow
6815 * simple comparisons. This can be used, for example, to check whether two
6816 * files are located on the same device, by comparing their encoded device
6817 * names. Even a string comparison would not do, because stat() reuses the
6818 * device name buffer for each call; so without encode_dev, it would be
6819 * necessary to save the buffer and use strcmp (this would mean a number of
6820 * changes to the standard Perl code, to say nothing of what a Perl script
6823 * The device lock id, if it exists, should be unique (unless perhaps compared
6824 * with lock ids transferred from other nodes). We have a lock id if the disk is
6825 * mounted cluster-wide, which is when we tend to get long (host-qualified)
6826 * device names. Thus we use the lock id in preference, and only if that isn't
6827 * available, do we try to pack the device name into an integer (flagged by
6828 * the sign bit (LOCKID_MASK) being set).
6830 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
6831 * name and its encoded form, but it seems very unlikely that we will find
6832 * two files on different disks that share the same encoded device names,
6833 * and even more remote that they will share the same file id (if the test
6834 * is to check for the same file).
6836 * A better method might be to use sys$device_scan on the first call, and to
6837 * search for the device, returning an index into the cached array.
6838 * The number returned would be more intelligable.
6839 * This is probably not worth it, and anyway would take quite a bit longer
6840 * on the first call.
6842 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
6843 static mydev_t encode_dev (pTHX_ const char *dev)
6846 unsigned long int f;
6851 if (!dev || !dev[0]) return 0;
6855 struct dsc$descriptor_s dev_desc;
6856 unsigned long int status, lockid, item = DVI$_LOCKID;
6858 /* For cluster-mounted disks, the disk lock identifier is unique, so we
6859 can try that first. */
6860 dev_desc.dsc$w_length = strlen (dev);
6861 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
6862 dev_desc.dsc$b_class = DSC$K_CLASS_S;
6863 dev_desc.dsc$a_pointer = (char *) dev;
6864 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
6865 if (lockid) return (lockid & ~LOCKID_MASK);
6869 /* Otherwise we try to encode the device name */
6873 for (q = dev + strlen(dev); q--; q >= dev) {
6876 else if (isalpha (toupper (*q)))
6877 c= toupper (*q) - 'A' + (char)10;
6879 continue; /* Skip '$'s */
6881 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
6883 enc += f * (unsigned long int) c;
6885 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
6887 } /* end of encode_dev() */
6889 static char namecache[NAM$C_MAXRSS+1];
6892 is_null_device(name)
6895 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
6896 The underscore prefix, controller letter, and unit number are
6897 independently optional; for our purposes, the colon punctuation
6898 is not. The colon can be trailed by optional directory and/or
6899 filename, but two consecutive colons indicates a nodename rather
6900 than a device. [pr] */
6901 if (*name == '_') ++name;
6902 if (tolower(*name++) != 'n') return 0;
6903 if (tolower(*name++) != 'l') return 0;
6904 if (tolower(*name) == 'a') ++name;
6905 if (*name == '0') ++name;
6906 return (*name++ == ':') && (*name != ':');
6909 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
6910 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
6911 * subset of the applicable information.
6914 Perl_cando(pTHX_ Mode_t bit, Uid_t effective, const Stat_t *statbufp)
6916 char fname_phdev[NAM$C_MAXRSS+1];
6917 if (statbufp == &PL_statcache) return cando_by_name(bit,effective,namecache);
6919 char fname[NAM$C_MAXRSS+1];
6920 unsigned long int retsts;
6921 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
6922 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6924 /* If the struct mystat is stale, we're OOL; stat() overwrites the
6925 device name on successive calls */
6926 devdsc.dsc$a_pointer = ((Stat_t *)statbufp)->st_devnam;
6927 devdsc.dsc$w_length = strlen(((Stat_t *)statbufp)->st_devnam);
6928 namdsc.dsc$a_pointer = fname;
6929 namdsc.dsc$w_length = sizeof fname - 1;
6931 retsts = lib$fid_to_name(&devdsc,&(((Stat_t *)statbufp)->st_ino),
6932 &namdsc,&namdsc.dsc$w_length,0,0);
6934 fname[namdsc.dsc$w_length] = '\0';
6936 * lib$fid_to_name returns DVI$_LOGVOLNAM as the device part of the name,
6937 * but if someone has redefined that logical, Perl gets very lost. Since
6938 * we have the physical device name from the stat buffer, just paste it on.
6940 strcpy( fname_phdev, statbufp->st_devnam );
6941 strcat( fname_phdev, strrchr(fname, ':') );
6943 return cando_by_name(bit,effective,fname_phdev);
6945 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
6946 Perl_warn(aTHX_ "Can't get filespec - stale stat buffer?\n");
6950 return FALSE; /* Should never get to here */
6952 } /* end of cando() */
6956 /*{{{I32 cando_by_name(I32 bit, Uid_t effective, char *fname)*/
6958 Perl_cando_by_name(pTHX_ I32 bit, Uid_t effective, const char *fname)
6960 static char usrname[L_cuserid];
6961 static struct dsc$descriptor_s usrdsc =
6962 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
6963 char vmsname[NAM$C_MAXRSS+1], fileified[NAM$C_MAXRSS+1];
6964 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
6965 unsigned short int retlen, trnlnm_iter_count;
6966 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6967 union prvdef curprv;
6968 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
6969 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
6970 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
6971 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
6973 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
6975 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6977 if (!fname || !*fname) return FALSE;
6978 /* Make sure we expand logical names, since sys$check_access doesn't */
6979 if (!strpbrk(fname,"/]>:")) {
6980 strcpy(fileified,fname);
6981 trnlnm_iter_count = 0;
6982 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) {
6983 trnlnm_iter_count++;
6984 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6988 if (!do_tovmsspec(fname,vmsname,1)) return FALSE;
6989 retlen = namdsc.dsc$w_length = strlen(vmsname);
6990 namdsc.dsc$a_pointer = vmsname;
6991 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
6992 vmsname[retlen-1] == ':') {
6993 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
6994 namdsc.dsc$w_length = strlen(fileified);
6995 namdsc.dsc$a_pointer = fileified;
6999 case S_IXUSR: case S_IXGRP: case S_IXOTH:
7000 access = ARM$M_EXECUTE; break;
7001 case S_IRUSR: case S_IRGRP: case S_IROTH:
7002 access = ARM$M_READ; break;
7003 case S_IWUSR: case S_IWGRP: case S_IWOTH:
7004 access = ARM$M_WRITE; break;
7005 case S_IDUSR: case S_IDGRP: case S_IDOTH:
7006 access = ARM$M_DELETE; break;
7011 /* Before we call $check_access, create a user profile with the current
7012 * process privs since otherwise it just uses the default privs from the
7013 * UAF and might give false positives or negatives. This only works on
7014 * VMS versions v6.0 and later since that's when sys$create_user_profile
7018 /* get current process privs and username */
7019 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
7022 #if defined(__VMS_VER) && __VMS_VER >= 60000000
7024 /* find out the space required for the profile */
7025 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
7026 &usrprodsc.dsc$w_length,0));
7028 /* allocate space for the profile and get it filled in */
7029 Newx(usrprodsc.dsc$a_pointer,usrprodsc.dsc$w_length,char);
7030 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
7031 &usrprodsc.dsc$w_length,0));
7033 /* use the profile to check access to the file; free profile & analyze results */
7034 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,0,0,0,&usrprodsc);
7035 Safefree(usrprodsc.dsc$a_pointer);
7036 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
7040 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
7044 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
7045 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
7046 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
7047 set_vaxc_errno(retsts);
7048 if (retsts == SS$_NOPRIV) set_errno(EACCES);
7049 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
7050 else set_errno(ENOENT);
7053 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
7058 return FALSE; /* Should never get here */
7060 } /* end of cando_by_name() */
7064 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
7066 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
7068 if (!fstat(fd,(stat_t *) statbufp)) {
7069 if (statbufp == (Stat_t *) &PL_statcache) *namecache == '\0';
7070 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
7071 # ifdef RTL_USES_UTC
7074 statbufp->st_mtime = _toloc(statbufp->st_mtime);
7075 statbufp->st_atime = _toloc(statbufp->st_atime);
7076 statbufp->st_ctime = _toloc(statbufp->st_ctime);
7081 if (!VMSISH_TIME) { /* Return UTC instead of local time */
7085 statbufp->st_mtime = _toutc(statbufp->st_mtime);
7086 statbufp->st_atime = _toutc(statbufp->st_atime);
7087 statbufp->st_ctime = _toutc(statbufp->st_ctime);
7094 } /* end of flex_fstat() */
7097 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
7099 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
7101 char fileified[NAM$C_MAXRSS+1];
7102 char temp_fspec[NAM$C_MAXRSS+300];
7104 int saved_errno, saved_vaxc_errno;
7106 if (!fspec) return retval;
7107 saved_errno = errno; saved_vaxc_errno = vaxc$errno;
7108 strcpy(temp_fspec, fspec);
7109 if (statbufp == (Stat_t *) &PL_statcache)
7110 do_tovmsspec(temp_fspec,namecache,0);
7111 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
7112 memset(statbufp,0,sizeof *statbufp);
7113 statbufp->st_dev = encode_dev(aTHX_ "_NLA0:");
7114 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
7115 statbufp->st_uid = 0x00010001;
7116 statbufp->st_gid = 0x0001;
7117 time((time_t *)&statbufp->st_mtime);
7118 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
7122 /* Try for a directory name first. If fspec contains a filename without
7123 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
7124 * and sea:[wine.dark]water. exist, we prefer the directory here.
7125 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
7126 * not sea:[wine.dark]., if the latter exists. If the intended target is
7127 * the file with null type, specify this by calling flex_stat() with
7128 * a '.' at the end of fspec.
7130 if (do_fileify_dirspec(temp_fspec,fileified,0) != NULL) {
7131 retval = stat(fileified,(stat_t *) statbufp);
7132 if (!retval && statbufp == (Stat_t *) &PL_statcache)
7133 strcpy(namecache,fileified);
7135 if (retval) retval = stat(temp_fspec,(stat_t *) statbufp);
7137 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
7138 # ifdef RTL_USES_UTC
7141 statbufp->st_mtime = _toloc(statbufp->st_mtime);
7142 statbufp->st_atime = _toloc(statbufp->st_atime);
7143 statbufp->st_ctime = _toloc(statbufp->st_ctime);
7148 if (!VMSISH_TIME) { /* Return UTC instead of local time */
7152 statbufp->st_mtime = _toutc(statbufp->st_mtime);
7153 statbufp->st_atime = _toutc(statbufp->st_atime);
7154 statbufp->st_ctime = _toutc(statbufp->st_ctime);
7158 /* If we were successful, leave errno where we found it */
7159 if (retval == 0) { errno = saved_errno; vaxc$errno = saved_vaxc_errno; }
7162 } /* end of flex_stat() */
7166 /*{{{char *my_getlogin()*/
7167 /* VMS cuserid == Unix getlogin, except calling sequence */
7171 static char user[L_cuserid];
7172 return cuserid(user);
7177 /* rmscopy - copy a file using VMS RMS routines
7179 * Copies contents and attributes of spec_in to spec_out, except owner
7180 * and protection information. Name and type of spec_in are used as
7181 * defaults for spec_out. The third parameter specifies whether rmscopy()
7182 * should try to propagate timestamps from the input file to the output file.
7183 * If it is less than 0, no timestamps are preserved. If it is 0, then
7184 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
7185 * propagated to the output file at creation iff the output file specification
7186 * did not contain an explicit name or type, and the revision date is always
7187 * updated at the end of the copy operation. If it is greater than 0, then
7188 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
7189 * other than the revision date should be propagated, and bit 1 indicates
7190 * that the revision date should be propagated.
7192 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
7194 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
7195 * Incorporates, with permission, some code from EZCOPY by Tim Adye
7196 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
7197 * as part of the Perl standard distribution under the terms of the
7198 * GNU General Public License or the Perl Artistic License. Copies
7199 * of each may be found in the Perl standard distribution.
7201 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
7203 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
7205 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
7206 rsa[NAM$C_MAXRSS], ubf[32256];
7207 unsigned long int i, sts, sts2;
7208 struct FAB fab_in, fab_out;
7209 struct RAB rab_in, rab_out;
7211 struct XABDAT xabdat;
7212 struct XABFHC xabfhc;
7213 struct XABRDT xabrdt;
7214 struct XABSUM xabsum;
7216 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
7217 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
7218 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7222 fab_in = cc$rms_fab;
7223 fab_in.fab$l_fna = vmsin;
7224 fab_in.fab$b_fns = strlen(vmsin);
7225 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
7226 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
7227 fab_in.fab$l_fop = FAB$M_SQO;
7228 fab_in.fab$l_nam = &nam;
7229 fab_in.fab$l_xab = (void *) &xabdat;
7232 nam.nam$l_rsa = rsa;
7233 nam.nam$b_rss = sizeof(rsa);
7234 nam.nam$l_esa = esa;
7235 nam.nam$b_ess = sizeof (esa);
7236 nam.nam$b_esl = nam.nam$b_rsl = 0;
7238 xabdat = cc$rms_xabdat; /* To get creation date */
7239 xabdat.xab$l_nxt = (void *) &xabfhc;
7241 xabfhc = cc$rms_xabfhc; /* To get record length */
7242 xabfhc.xab$l_nxt = (void *) &xabsum;
7244 xabsum = cc$rms_xabsum; /* To get key and area information */
7246 if (!((sts = sys$open(&fab_in)) & 1)) {
7247 set_vaxc_errno(sts);
7249 case RMS$_FNF: case RMS$_DNF:
7250 set_errno(ENOENT); break;
7252 set_errno(ENOTDIR); break;
7254 set_errno(ENODEV); break;
7256 set_errno(EINVAL); break;
7258 set_errno(EACCES); break;
7266 fab_out.fab$w_ifi = 0;
7267 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
7268 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
7269 fab_out.fab$l_fop = FAB$M_SQO;
7270 fab_out.fab$l_fna = vmsout;
7271 fab_out.fab$b_fns = strlen(vmsout);
7272 fab_out.fab$l_dna = nam.nam$l_name;
7273 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
7275 if (preserve_dates == 0) { /* Act like DCL COPY */
7276 nam.nam$b_nop = NAM$M_SYNCHK;
7277 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
7278 if (!((sts = sys$parse(&fab_out)) & 1)) {
7279 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
7280 set_vaxc_errno(sts);
7283 fab_out.fab$l_xab = (void *) &xabdat;
7284 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
7286 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
7287 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
7288 preserve_dates =0; /* bitmask from this point forward */
7290 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
7291 if (!((sts = sys$create(&fab_out)) & 1)) {
7292 set_vaxc_errno(sts);
7295 set_errno(ENOENT); break;
7297 set_errno(ENOTDIR); break;
7299 set_errno(ENODEV); break;
7301 set_errno(EINVAL); break;
7303 set_errno(EACCES); break;
7309 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
7310 if (preserve_dates & 2) {
7311 /* sys$close() will process xabrdt, not xabdat */
7312 xabrdt = cc$rms_xabrdt;
7314 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
7316 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
7317 * is unsigned long[2], while DECC & VAXC use a struct */
7318 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
7320 fab_out.fab$l_xab = (void *) &xabrdt;
7323 rab_in = cc$rms_rab;
7324 rab_in.rab$l_fab = &fab_in;
7325 rab_in.rab$l_rop = RAB$M_BIO;
7326 rab_in.rab$l_ubf = ubf;
7327 rab_in.rab$w_usz = sizeof ubf;
7328 if (!((sts = sys$connect(&rab_in)) & 1)) {
7329 sys$close(&fab_in); sys$close(&fab_out);
7330 set_errno(EVMSERR); set_vaxc_errno(sts);
7334 rab_out = cc$rms_rab;
7335 rab_out.rab$l_fab = &fab_out;
7336 rab_out.rab$l_rbf = ubf;
7337 if (!((sts = sys$connect(&rab_out)) & 1)) {
7338 sys$close(&fab_in); sys$close(&fab_out);
7339 set_errno(EVMSERR); set_vaxc_errno(sts);
7343 while ((sts = sys$read(&rab_in))) { /* always true */
7344 if (sts == RMS$_EOF) break;
7345 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
7346 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
7347 sys$close(&fab_in); sys$close(&fab_out);
7348 set_errno(EVMSERR); set_vaxc_errno(sts);
7353 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
7354 sys$close(&fab_in); sys$close(&fab_out);
7355 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
7357 set_errno(EVMSERR); set_vaxc_errno(sts);
7363 } /* end of rmscopy() */
7367 /*** The following glue provides 'hooks' to make some of the routines
7368 * from this file available from Perl. These routines are sufficiently
7369 * basic, and are required sufficiently early in the build process,
7370 * that's it's nice to have them available to miniperl as well as the
7371 * full Perl, so they're set up here instead of in an extension. The
7372 * Perl code which handles importation of these names into a given
7373 * package lives in [.VMS]Filespec.pm in @INC.
7377 rmsexpand_fromperl(pTHX_ CV *cv)
7380 char *fspec, *defspec = NULL, *rslt;
7383 if (!items || items > 2)
7384 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
7385 fspec = SvPV(ST(0),n_a);
7386 if (!fspec || !*fspec) XSRETURN_UNDEF;
7387 if (items == 2) defspec = SvPV(ST(1),n_a);
7389 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
7390 ST(0) = sv_newmortal();
7391 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
7396 vmsify_fromperl(pTHX_ CV *cv)
7402 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
7403 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1);
7404 ST(0) = sv_newmortal();
7405 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
7410 unixify_fromperl(pTHX_ CV *cv)
7416 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
7417 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1);
7418 ST(0) = sv_newmortal();
7419 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
7424 fileify_fromperl(pTHX_ CV *cv)
7430 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
7431 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1);
7432 ST(0) = sv_newmortal();
7433 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
7438 pathify_fromperl(pTHX_ CV *cv)
7444 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
7445 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1);
7446 ST(0) = sv_newmortal();
7447 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
7452 vmspath_fromperl(pTHX_ CV *cv)
7458 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
7459 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1);
7460 ST(0) = sv_newmortal();
7461 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
7466 unixpath_fromperl(pTHX_ CV *cv)
7472 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
7473 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1);
7474 ST(0) = sv_newmortal();
7475 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
7480 candelete_fromperl(pTHX_ CV *cv)
7483 char fspec[NAM$C_MAXRSS+1], *fsp;
7488 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
7490 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
7491 if (SvTYPE(mysv) == SVt_PVGV) {
7492 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
7493 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7500 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
7501 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7507 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
7512 rmscopy_fromperl(pTHX_ CV *cv)
7515 char inspec[NAM$C_MAXRSS+1], outspec[NAM$C_MAXRSS+1], *inp, *outp;
7517 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
7518 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7519 unsigned long int sts;
7524 if (items < 2 || items > 3)
7525 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
7527 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
7528 if (SvTYPE(mysv) == SVt_PVGV) {
7529 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
7530 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7537 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
7538 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7543 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
7544 if (SvTYPE(mysv) == SVt_PVGV) {
7545 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
7546 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7553 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
7554 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
7559 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
7561 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
7567 mod2fname(pTHX_ CV *cv)
7570 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
7571 workbuff[NAM$C_MAXRSS*1 + 1];
7572 int total_namelen = 3, counter, num_entries;
7573 /* ODS-5 ups this, but we want to be consistent, so... */
7574 int max_name_len = 39;
7575 AV *in_array = (AV *)SvRV(ST(0));
7577 num_entries = av_len(in_array);
7579 /* All the names start with PL_. */
7580 strcpy(ultimate_name, "PL_");
7582 /* Clean up our working buffer */
7583 Zero(work_name, sizeof(work_name), char);
7585 /* Run through the entries and build up a working name */
7586 for(counter = 0; counter <= num_entries; counter++) {
7587 /* If it's not the first name then tack on a __ */
7589 strcat(work_name, "__");
7591 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
7595 /* Check to see if we actually have to bother...*/
7596 if (strlen(work_name) + 3 <= max_name_len) {
7597 strcat(ultimate_name, work_name);
7599 /* It's too darned big, so we need to go strip. We use the same */
7600 /* algorithm as xsubpp does. First, strip out doubled __ */
7601 char *source, *dest, last;
7604 for (source = work_name; *source; source++) {
7605 if (last == *source && last == '_') {
7611 /* Go put it back */
7612 strcpy(work_name, workbuff);
7613 /* Is it still too big? */
7614 if (strlen(work_name) + 3 > max_name_len) {
7615 /* Strip duplicate letters */
7618 for (source = work_name; *source; source++) {
7619 if (last == toupper(*source)) {
7623 last = toupper(*source);
7625 strcpy(work_name, workbuff);
7628 /* Is it *still* too big? */
7629 if (strlen(work_name) + 3 > max_name_len) {
7630 /* Too bad, we truncate */
7631 work_name[max_name_len - 2] = 0;
7633 strcat(ultimate_name, work_name);
7636 /* Okay, return it */
7637 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
7642 hushexit_fromperl(pTHX_ CV *cv)
7647 VMSISH_HUSHED = SvTRUE(ST(0));
7649 ST(0) = boolSV(VMSISH_HUSHED);
7654 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
7655 struct interp_intern *dst)
7657 memcpy(dst,src,sizeof(struct interp_intern));
7661 Perl_sys_intern_clear(pTHX)
7666 Perl_sys_intern_init(pTHX)
7668 unsigned int ix = RAND_MAX;
7674 MY_INV_RAND_MAX = 1./x;
7681 char* file = __FILE__;
7682 char temp_buff[512];
7683 if (my_trnlnm("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", temp_buff, 0)) {
7684 no_translate_barewords = TRUE;
7686 no_translate_barewords = FALSE;
7689 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
7690 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
7691 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
7692 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
7693 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
7694 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
7695 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
7696 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
7697 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
7698 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
7699 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
7701 store_pipelocs(aTHX); /* will redo any earlier attempts */