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>
34 #if __CRTL_VER >= 70301000 && !defined(__VAX)
44 #include <str$routines.h>
51 /* Set the maximum filespec size here as it is larger for EFS file
53 * Not fully implemented at this time because the larger size
54 * will likely impact the stack local storage requirements of
55 * threaded code, and probably cause hard to diagnose failures.
56 * To implement the larger sizes, all places where filename
57 * storage is put on the stack need to be changed to use
58 * New()/SafeFree() instead.
63 #define VMS_MAXRSS (NAML$C_MAXRSS+1)
64 #ifndef VMS_LONGNAME_SUPPORT
65 #define VMS_LONGNAME_SUPPORT 1
66 #endif /* VMS_LONGNAME_SUPPORT */
67 #endif /* NAML$C_MAXRSS */
68 #endif /* VMS_MAXRSS */
71 /* temporary hack until support is complete */
72 #ifdef VMS_LONGNAME_SUPPORT
73 #undef VMS_LONGNAME_SUPPORT
76 /* end of temporary hack until support is complete */
79 #define VMS_MAXRSS (NAM$C_MAXRSS + 1)
82 #if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
83 int decc$feature_get_index(const char *name);
84 char* decc$feature_get_name(int index);
85 int decc$feature_get_value(int index, int mode);
86 int decc$feature_set_value(int index, int mode, int value);
91 #if __CRTL_VER >= 70300000 && !defined(__VAX)
93 static int set_feature_default(const char *name, int value)
98 index = decc$feature_get_index(name);
100 status = decc$feature_set_value(index, 1, value);
101 if (index == -1 || (status == -1)) {
105 status = decc$feature_get_value(index, 1);
106 if (status != value) {
114 /* Older versions of ssdef.h don't have these */
115 #ifndef SS$_INVFILFOROP
116 # define SS$_INVFILFOROP 3930
118 #ifndef SS$_NOSUCHOBJECT
119 # define SS$_NOSUCHOBJECT 2696
122 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
123 #define PERLIO_NOT_STDIO 0
125 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
126 * code below needs to get to the underlying CRTL routines. */
127 #define DONT_MASK_RTL_CALLS
131 /* Anticipating future expansion in lexical warnings . . . */
132 #ifndef WARN_INTERNAL
133 # define WARN_INTERNAL WARN_MISC
136 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
137 # define RTL_USES_UTC 1
141 /* gcc's header files don't #define direct access macros
142 * corresponding to VAXC's variant structs */
144 # define uic$v_format uic$r_uic_form.uic$v_format
145 # define uic$v_group uic$r_uic_form.uic$v_group
146 # define uic$v_member uic$r_uic_form.uic$v_member
147 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
148 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
149 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
150 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
153 #if defined(NEED_AN_H_ERRNO)
158 #pragma message disable pragma
159 #pragma member_alignment save
160 #pragma nomember_alignment longword
162 #pragma message disable misalgndmem
165 unsigned short int buflen;
166 unsigned short int itmcode;
168 unsigned short int *retlen;
171 #pragma message restore
172 #pragma member_alignment restore
175 #define do_fileify_dirspec(a,b,c) mp_do_fileify_dirspec(aTHX_ a,b,c)
176 #define do_pathify_dirspec(a,b,c) mp_do_pathify_dirspec(aTHX_ a,b,c)
177 #define do_tovmsspec(a,b,c) mp_do_tovmsspec(aTHX_ a,b,c)
178 #define do_tovmspath(a,b,c) mp_do_tovmspath(aTHX_ a,b,c)
179 #define do_rmsexpand(a,b,c,d,e) mp_do_rmsexpand(aTHX_ a,b,c,d,e)
180 #define do_vms_realpath(a,b) mp_do_vms_realpath(aTHX_ a,b)
181 #define do_tounixspec(a,b,c) mp_do_tounixspec(aTHX_ a,b,c)
182 #define do_tounixpath(a,b,c) mp_do_tounixpath(aTHX_ a,b,c)
183 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
184 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
185 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
187 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts);
188 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts);
189 static char *mp_do_tounixspec(pTHX_ const char *, char *, int);
190 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts);
192 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
193 #define PERL_LNM_MAX_ALLOWED_INDEX 127
195 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
196 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
199 #define PERL_LNM_MAX_ITER 10
201 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
202 #if __CRTL_VER >= 70302000 && !defined(__VAX)
203 #define MAX_DCL_SYMBOL (8192)
204 #define MAX_DCL_LINE_LENGTH (4096 - 4)
206 #define MAX_DCL_SYMBOL (1024)
207 #define MAX_DCL_LINE_LENGTH (1024 - 4)
210 static char *__mystrtolower(char *str)
212 if (str) for (; *str; ++str) *str= tolower(*str);
216 static struct dsc$descriptor_s fildevdsc =
217 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
218 static struct dsc$descriptor_s crtlenvdsc =
219 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
220 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
221 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
222 static struct dsc$descriptor_s **env_tables = defenv;
223 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
225 /* True if we shouldn't treat barewords as logicals during directory */
227 static int no_translate_barewords;
230 static int tz_updated = 1;
233 /* DECC Features that may need to affect how Perl interprets
234 * displays filename information
236 static int decc_disable_to_vms_logname_translation = 1;
237 static int decc_disable_posix_root = 1;
238 int decc_efs_case_preserve = 0;
239 static int decc_efs_charset = 0;
240 static int decc_filename_unix_no_version = 0;
241 static int decc_filename_unix_only = 0;
242 int decc_filename_unix_report = 0;
243 int decc_posix_compliant_pathnames = 0;
244 int decc_readdir_dropdotnotype = 0;
245 static int vms_process_case_tolerant = 1;
247 /* bug workarounds if needed */
248 int decc_bug_readdir_efs1 = 0;
249 int decc_bug_devnull = 0;
250 int decc_bug_fgetname = 0;
251 int decc_dir_barename = 0;
253 /* Is this a UNIX file specification?
254 * No longer a simple check with EFS file specs
255 * For now, not a full check, but need to
256 * handle POSIX ^UP^ specifications
257 * Fixing to handle ^/ cases would require
258 * changes to many other conversion routines.
261 static is_unix_filespec(const char *path)
267 if (strncmp(path,"\"^UP^",5) != 0) {
268 pch1 = strchr(path, '/');
273 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
274 if (decc_filename_unix_report || decc_filename_unix_only) {
275 if (strcmp(path,".") == 0)
285 * Routine to retrieve the maximum equivalence index for an input
286 * logical name. Some calls to this routine have no knowledge if
287 * the variable is a logical or not. So on error we return a max
290 /*{{{int my_maxidx(const char *lnm) */
292 my_maxidx(const char *lnm)
296 int attr = LNM$M_CASE_BLIND;
297 struct dsc$descriptor lnmdsc;
298 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
301 lnmdsc.dsc$w_length = strlen(lnm);
302 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
303 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
304 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
306 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
307 if ((status & 1) == 0)
314 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
316 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
317 struct dsc$descriptor_s **tabvec, unsigned long int flags)
320 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
321 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
322 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
324 unsigned char acmode;
325 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
326 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
327 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
328 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
330 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
331 #if defined(PERL_IMPLICIT_CONTEXT)
334 aTHX = PERL_GET_INTERP;
340 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
341 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
343 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
344 *cp2 = _toupper(*cp1);
345 if (cp1 - lnm > LNM$C_NAMLENGTH) {
346 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
350 lnmdsc.dsc$w_length = cp1 - lnm;
351 lnmdsc.dsc$a_pointer = uplnm;
352 uplnm[lnmdsc.dsc$w_length] = '\0';
353 secure = flags & PERL__TRNENV_SECURE;
354 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
355 if (!tabvec || !*tabvec) tabvec = env_tables;
357 for (curtab = 0; tabvec[curtab]; curtab++) {
358 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
359 if (!ivenv && !secure) {
364 Perl_warn(aTHX_ "Can't read CRTL environ\n");
367 retsts = SS$_NOLOGNAM;
368 for (i = 0; environ[i]; i++) {
369 if ((eq = strchr(environ[i],'=')) &&
370 lnmdsc.dsc$w_length == (eq - environ[i]) &&
371 !strncmp(environ[i],uplnm,eq - environ[i])) {
373 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
374 if (!eqvlen) continue;
379 if (retsts != SS$_NOLOGNAM) break;
382 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
383 !str$case_blind_compare(&tmpdsc,&clisym)) {
384 if (!ivsym && !secure) {
385 unsigned short int deflen = LNM$C_NAMLENGTH;
386 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
387 /* dynamic dsc to accomodate possible long value */
388 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
389 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
391 if (eqvlen > MAX_DCL_SYMBOL) {
392 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
393 eqvlen = MAX_DCL_SYMBOL;
394 /* Special hack--we might be called before the interpreter's */
395 /* fully initialized, in which case either thr or PL_curcop */
396 /* might be bogus. We have to check, since ckWARN needs them */
397 /* both to be valid if running threaded */
398 if (ckWARN(WARN_MISC)) {
399 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
402 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
404 _ckvmssts(lib$sfree1_dd(&eqvdsc));
405 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
406 if (retsts == LIB$_NOSUCHSYM) continue;
411 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
412 midx = my_maxidx(lnm);
413 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
414 lnmlst[1].bufadr = cp2;
416 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
417 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
418 if (retsts == SS$_NOLOGNAM) break;
419 /* PPFs have a prefix */
422 *((int *)uplnm) == *((int *)"SYS$") &&
424 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
425 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
426 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
427 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
428 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
429 memmove(eqv,eqv+4,eqvlen-4);
435 if ((retsts == SS$_IVLOGNAM) ||
436 (retsts == SS$_NOLOGNAM)) { continue; }
439 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
440 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
441 if (retsts == SS$_NOLOGNAM) continue;
444 eqvlen = strlen(eqv);
448 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
449 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
450 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
451 retsts == SS$_NOLOGNAM) {
452 set_errno(EINVAL); set_vaxc_errno(retsts);
454 else _ckvmssts(retsts);
456 } /* end of vmstrnenv */
459 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
460 /* Define as a function so we can access statics. */
461 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
463 return vmstrnenv(lnm,eqv,idx,fildev,
464 #ifdef SECURE_INTERNAL_GETENV
465 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
474 * Note: Uses Perl temp to store result so char * can be returned to
475 * caller; this pointer will be invalidated at next Perl statement
477 * We define this as a function rather than a macro in terms of my_getenv_len()
478 * so that it'll work when PL_curinterp is undefined (and we therefore can't
481 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
483 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
486 static char *__my_getenv_eqv = NULL;
487 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
488 unsigned long int idx = 0;
489 int trnsuccess, success, secure, saverr, savvmserr;
493 midx = my_maxidx(lnm) + 1;
495 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
496 /* Set up a temporary buffer for the return value; Perl will
497 * clean it up at the next statement transition */
498 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
499 if (!tmpsv) return NULL;
503 /* Assume no interpreter ==> single thread */
504 if (__my_getenv_eqv != NULL) {
505 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
508 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
510 eqv = __my_getenv_eqv;
513 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
514 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
516 getcwd(eqv,LNM$C_NAMLENGTH);
520 /* Get rid of "000000/ in rooted filespecs */
523 zeros = strstr(eqv, "/000000/");
526 mlen = len - (zeros - eqv) - 7;
527 memmove(zeros, &zeros[7], mlen);
535 /* Impose security constraints only if tainting */
537 /* Impose security constraints only if tainting */
538 secure = PL_curinterp ? PL_tainting : will_taint;
539 saverr = errno; savvmserr = vaxc$errno;
546 #ifdef SECURE_INTERNAL_GETENV
547 secure ? PERL__TRNENV_SECURE : 0
553 /* For the getenv interface we combine all the equivalence names
554 * of a search list logical into one value to acquire a maximum
555 * value length of 255*128 (assuming %ENV is using logicals).
557 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
559 /* If the name contains a semicolon-delimited index, parse it
560 * off and make sure we only retrieve the equivalence name for
562 if ((cp2 = strchr(lnm,';')) != NULL) {
564 uplnm[cp2-lnm] = '\0';
565 idx = strtoul(cp2+1,NULL,0);
567 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
570 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
572 /* Discard NOLOGNAM on internal calls since we're often looking
573 * for an optional name, and this "error" often shows up as the
574 * (bogus) exit status for a die() call later on. */
575 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
576 return success ? eqv : Nullch;
579 } /* end of my_getenv() */
583 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
585 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
589 unsigned long idx = 0;
591 static char *__my_getenv_len_eqv = NULL;
592 int secure, saverr, savvmserr;
595 midx = my_maxidx(lnm) + 1;
597 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
598 /* Set up a temporary buffer for the return value; Perl will
599 * clean it up at the next statement transition */
600 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
601 if (!tmpsv) return NULL;
605 /* Assume no interpreter ==> single thread */
606 if (__my_getenv_len_eqv != NULL) {
607 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
610 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
612 buf = __my_getenv_len_eqv;
615 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
616 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
619 getcwd(buf,LNM$C_NAMLENGTH);
622 /* Get rid of "000000/ in rooted filespecs */
624 zeros = strstr(buf, "/000000/");
627 mlen = *len - (zeros - buf) - 7;
628 memmove(zeros, &zeros[7], mlen);
637 /* Impose security constraints only if tainting */
638 secure = PL_curinterp ? PL_tainting : will_taint;
639 saverr = errno; savvmserr = vaxc$errno;
646 #ifdef SECURE_INTERNAL_GETENV
647 secure ? PERL__TRNENV_SECURE : 0
653 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
655 if ((cp2 = strchr(lnm,';')) != NULL) {
658 idx = strtoul(cp2+1,NULL,0);
660 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
663 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
665 /* Get rid of "000000/ in rooted filespecs */
668 zeros = strstr(buf, "/000000/");
671 mlen = *len - (zeros - buf) - 7;
672 memmove(zeros, &zeros[7], mlen);
678 /* Discard NOLOGNAM on internal calls since we're often looking
679 * for an optional name, and this "error" often shows up as the
680 * (bogus) exit status for a die() call later on. */
681 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
682 return *len ? buf : Nullch;
685 } /* end of my_getenv_len() */
688 static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *);
690 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
692 /*{{{ void prime_env_iter() */
695 /* Fill the %ENV associative array with all logical names we can
696 * find, in preparation for iterating over it.
699 static int primed = 0;
700 HV *seenhv = NULL, *envhv;
702 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
703 unsigned short int chan;
704 #ifndef CLI$M_TRUSTED
705 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
707 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
708 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
710 bool have_sym = FALSE, have_lnm = FALSE;
711 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
712 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
713 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
714 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
715 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
716 #if defined(PERL_IMPLICIT_CONTEXT)
719 #if defined(USE_ITHREADS)
720 static perl_mutex primenv_mutex;
721 MUTEX_INIT(&primenv_mutex);
724 #if defined(PERL_IMPLICIT_CONTEXT)
725 /* We jump through these hoops because we can be called at */
726 /* platform-specific initialization time, which is before anything is */
727 /* set up--we can't even do a plain dTHX since that relies on the */
728 /* interpreter structure to be initialized */
730 aTHX = PERL_GET_INTERP;
736 if (primed || !PL_envgv) return;
737 MUTEX_LOCK(&primenv_mutex);
738 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
739 envhv = GvHVn(PL_envgv);
740 /* Perform a dummy fetch as an lval to insure that the hash table is
741 * set up. Otherwise, the hv_store() will turn into a nullop. */
742 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
744 for (i = 0; env_tables[i]; i++) {
745 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
746 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
747 if (!have_lnm && !str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
749 if (have_sym || have_lnm) {
750 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
751 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
752 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
753 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
756 for (i--; i >= 0; i--) {
757 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
760 for (j = 0; environ[j]; j++) {
761 if (!(start = strchr(environ[j],'='))) {
762 if (ckWARN(WARN_INTERNAL))
763 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
767 sv = newSVpv(start,0);
769 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
774 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
775 !str$case_blind_compare(&tmpdsc,&clisym)) {
776 strcpy(cmd,"Show Symbol/Global *");
777 cmddsc.dsc$w_length = 20;
778 if (env_tables[i]->dsc$w_length == 12 &&
779 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
780 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
781 flags = defflags | CLI$M_NOLOGNAM;
784 strcpy(cmd,"Show Logical *");
785 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
786 strcat(cmd," /Table=");
787 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
788 cmddsc.dsc$w_length = strlen(cmd);
790 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
791 flags = defflags | CLI$M_NOCLISYM;
794 /* Create a new subprocess to execute each command, to exclude the
795 * remote possibility that someone could subvert a mbx or file used
796 * to write multiple commands to a single subprocess.
799 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
800 0,&riseandshine,0,0,&clidsc,&clitabdsc);
801 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
802 defflags &= ~CLI$M_TRUSTED;
803 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
805 if (!buf) Newx(buf,mbxbufsiz + 1,char);
806 if (seenhv) SvREFCNT_dec(seenhv);
809 char *cp1, *cp2, *key;
810 unsigned long int sts, iosb[2], retlen, keylen;
813 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
814 if (sts & 1) sts = iosb[0] & 0xffff;
815 if (sts == SS$_ENDOFFILE) {
817 while (substs == 0) { sys$hiber(); wakect++;}
818 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
823 retlen = iosb[0] >> 16;
824 if (!retlen) continue; /* blank line */
826 if (iosb[1] != subpid) {
828 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
832 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
833 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
835 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
836 if (*cp1 == '(' || /* Logical name table name */
837 *cp1 == '=' /* Next eqv of searchlist */) continue;
838 if (*cp1 == '"') cp1++;
839 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
840 key = cp1; keylen = cp2 - cp1;
841 if (keylen && hv_exists(seenhv,key,keylen)) continue;
842 while (*cp2 && *cp2 != '=') cp2++;
843 while (*cp2 && *cp2 == '=') cp2++;
844 while (*cp2 && *cp2 == ' ') cp2++;
845 if (*cp2 == '"') { /* String translation; may embed "" */
846 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
847 cp2++; cp1--; /* Skip "" surrounding translation */
849 else { /* Numeric translation */
850 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
851 cp1--; /* stop on last non-space char */
853 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
854 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
857 PERL_HASH(hash,key,keylen);
859 if (cp1 == cp2 && *cp2 == '.') {
860 /* A single dot usually means an unprintable character, such as a null
861 * to indicate a zero-length value. Get the actual value to make sure.
863 char lnm[LNM$C_NAMLENGTH+1];
864 char eqv[MAX_DCL_SYMBOL+1];
865 strncpy(lnm, key, keylen);
866 int trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
867 sv = newSVpvn(eqv, strlen(eqv));
870 sv = newSVpvn(cp2,cp1 - cp2 + 1);
874 hv_store(envhv,key,keylen,sv,hash);
875 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
877 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
878 /* get the PPFs for this process, not the subprocess */
879 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
880 char eqv[LNM$C_NAMLENGTH+1];
882 for (i = 0; ppfs[i]; i++) {
883 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
884 sv = newSVpv(eqv,trnlen);
886 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
891 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
892 if (buf) Safefree(buf);
893 if (seenhv) SvREFCNT_dec(seenhv);
894 MUTEX_UNLOCK(&primenv_mutex);
897 } /* end of prime_env_iter */
901 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
902 /* Define or delete an element in the same "environment" as
903 * vmstrnenv(). If an element is to be deleted, it's removed from
904 * the first place it's found. If it's to be set, it's set in the
905 * place designated by the first element of the table vector.
906 * Like setenv() returns 0 for success, non-zero on error.
909 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
912 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
913 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
915 unsigned long int retsts, usermode = PSL$C_USER;
916 struct itmlst_3 *ile, *ilist;
917 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
918 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
919 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
920 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
921 $DESCRIPTOR(local,"_LOCAL");
924 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
928 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
929 *cp2 = _toupper(*cp1);
930 if (cp1 - lnm > LNM$C_NAMLENGTH) {
931 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
935 lnmdsc.dsc$w_length = cp1 - lnm;
936 if (!tabvec || !*tabvec) tabvec = env_tables;
938 if (!eqv) { /* we're deleting n element */
939 for (curtab = 0; tabvec[curtab]; curtab++) {
940 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
942 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
943 if ((cp1 = strchr(environ[i],'=')) &&
944 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
945 !strncmp(environ[i],lnm,cp1 - environ[i])) {
947 return setenv(lnm,"",1) ? vaxc$errno : 0;
950 ivenv = 1; retsts = SS$_NOLOGNAM;
952 if (ckWARN(WARN_INTERNAL))
953 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
954 ivenv = 1; retsts = SS$_NOSUCHPGM;
960 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
961 !str$case_blind_compare(&tmpdsc,&clisym)) {
962 unsigned int symtype;
963 if (tabvec[curtab]->dsc$w_length == 12 &&
964 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
965 !str$case_blind_compare(&tmpdsc,&local))
966 symtype = LIB$K_CLI_LOCAL_SYM;
967 else symtype = LIB$K_CLI_GLOBAL_SYM;
968 retsts = lib$delete_symbol(&lnmdsc,&symtype);
969 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
970 if (retsts == LIB$_NOSUCHSYM) continue;
974 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
975 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
976 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
977 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
978 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
982 else { /* we're defining a value */
983 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
985 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
987 if (ckWARN(WARN_INTERNAL))
988 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
989 retsts = SS$_NOSUCHPGM;
993 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
994 eqvdsc.dsc$w_length = strlen(eqv);
995 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
996 !str$case_blind_compare(&tmpdsc,&clisym)) {
997 unsigned int symtype;
998 if (tabvec[0]->dsc$w_length == 12 &&
999 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1000 !str$case_blind_compare(&tmpdsc,&local))
1001 symtype = LIB$K_CLI_LOCAL_SYM;
1002 else symtype = LIB$K_CLI_GLOBAL_SYM;
1003 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1006 if (!*eqv) eqvdsc.dsc$w_length = 1;
1007 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1009 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1010 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1011 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1012 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1013 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1014 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1017 Newx(ilist,nseg+1,struct itmlst_3);
1020 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1023 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1025 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1026 ile->itmcode = LNM$_STRING;
1028 if ((j+1) == nseg) {
1029 ile->buflen = strlen(c);
1030 /* in case we are truncating one that's too long */
1031 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1034 ile->buflen = LNM$C_NAMLENGTH;
1038 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1042 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1047 if (!(retsts & 1)) {
1049 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1050 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1051 set_errno(EVMSERR); break;
1052 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1053 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1054 set_errno(EINVAL); break;
1061 set_vaxc_errno(retsts);
1062 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1065 /* We reset error values on success because Perl does an hv_fetch()
1066 * before each hv_store(), and if the thing we're setting didn't
1067 * previously exist, we've got a leftover error message. (Of course,
1068 * this fails in the face of
1069 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1070 * in that the error reported in $! isn't spurious,
1071 * but it's right more often than not.)
1073 set_errno(0); set_vaxc_errno(retsts);
1077 } /* end of vmssetenv() */
1080 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1081 /* This has to be a function since there's a prototype for it in proto.h */
1083 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1086 int len = strlen(lnm);
1090 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1091 if (!strcmp(uplnm,"DEFAULT")) {
1092 if (eqv && *eqv) my_chdir(eqv);
1096 #ifndef RTL_USES_UTC
1097 if (len == 6 || len == 2) {
1100 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1102 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1103 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1107 (void) vmssetenv(lnm,eqv,NULL);
1111 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1113 * sets a user-mode logical in the process logical name table
1114 * used for redirection of sys$error
1117 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1119 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1120 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1121 unsigned long int iss, attr = LNM$M_CONFINE;
1122 unsigned char acmode = PSL$C_USER;
1123 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1125 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1126 d_name.dsc$w_length = strlen(name);
1128 lnmlst[0].buflen = strlen(eqv);
1129 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1131 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1132 if (!(iss&1)) lib$signal(iss);
1137 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1138 /* my_crypt - VMS password hashing
1139 * my_crypt() provides an interface compatible with the Unix crypt()
1140 * C library function, and uses sys$hash_password() to perform VMS
1141 * password hashing. The quadword hashed password value is returned
1142 * as a NUL-terminated 8 character string. my_crypt() does not change
1143 * the case of its string arguments; in order to match the behavior
1144 * of LOGINOUT et al., alphabetic characters in both arguments must
1145 * be upcased by the caller.
1147 * - fix me to call ACM services when available
1150 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1152 # ifndef UAI$C_PREFERRED_ALGORITHM
1153 # define UAI$C_PREFERRED_ALGORITHM 127
1155 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1156 unsigned short int salt = 0;
1157 unsigned long int sts;
1159 unsigned short int dsc$w_length;
1160 unsigned char dsc$b_type;
1161 unsigned char dsc$b_class;
1162 const char * dsc$a_pointer;
1163 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1164 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1165 struct itmlst_3 uailst[3] = {
1166 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1167 { sizeof salt, UAI$_SALT, &salt, 0},
1168 { 0, 0, NULL, NULL}};
1169 static char hash[9];
1171 usrdsc.dsc$w_length = strlen(usrname);
1172 usrdsc.dsc$a_pointer = usrname;
1173 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1175 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1179 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1184 set_vaxc_errno(sts);
1185 if (sts != RMS$_RNF) return NULL;
1188 txtdsc.dsc$w_length = strlen(textpasswd);
1189 txtdsc.dsc$a_pointer = textpasswd;
1190 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1191 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1194 return (char *) hash;
1196 } /* end of my_crypt() */
1200 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned);
1201 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int);
1202 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int);
1204 /* fixup barenames that are directories for internal use.
1205 * There have been problems with the consistent handling of UNIX
1206 * style directory names when routines are presented with a name that
1207 * has no directory delimitors at all. So this routine will eventually
1210 static char * fixup_bare_dirnames(const char * name)
1212 if (decc_disable_to_vms_logname_translation) {
1219 * A little hack to get around a bug in some implemenation of remove()
1220 * that do not know how to delete a directory
1222 * Delete any file to which user has control access, regardless of whether
1223 * delete access is explicitly allowed.
1224 * Limitations: User must have write access to parent directory.
1225 * Does not block signals or ASTs; if interrupted in midstream
1226 * may leave file with an altered ACL.
1229 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1231 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1233 char *vmsname, *rspec;
1235 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1236 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1237 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1239 unsigned char myace$b_length;
1240 unsigned char myace$b_type;
1241 unsigned short int myace$w_flags;
1242 unsigned long int myace$l_access;
1243 unsigned long int myace$l_ident;
1244 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1245 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1246 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1248 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1249 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1250 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1251 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1252 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1253 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1255 /* Expand the input spec using RMS, since the CRTL remove() and
1256 * system services won't do this by themselves, so we may miss
1257 * a file "hiding" behind a logical name or search list. */
1258 Newx(vmsname, NAM$C_MAXRSS+1, char);
1259 if (do_tovmsspec(name,vmsname,0) == NULL) {
1264 if (decc_posix_compliant_pathnames) {
1265 /* In POSIX mode, we prefer to remove the UNIX name */
1267 remove_name = (char *)name;
1270 Newx(rspec, NAM$C_MAXRSS+1, char);
1271 if (do_rmsexpand(vmsname, rspec, 1, NULL, PERL_RMSEXPAND_M_VMS) == NULL) {
1277 remove_name = rspec;
1280 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1282 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1283 Newx(remove_name, NAM$C_MAXRSS+1, char);
1284 do_pathify_dirspec(name, remove_name, 0);
1285 if (!rmdir(remove_name)) {
1287 Safefree(remove_name);
1289 return 0; /* Can we just get rid of it? */
1293 if (!rmdir(remove_name)) {
1295 return 0; /* Can we just get rid of it? */
1301 if (!remove(remove_name)) {
1303 return 0; /* Can we just get rid of it? */
1306 /* If not, can changing protections help? */
1307 if (vaxc$errno != RMS$_PRV) {
1312 /* No, so we get our own UIC to use as a rights identifier,
1313 * and the insert an ACE at the head of the ACL which allows us
1314 * to delete the file.
1316 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1317 fildsc.dsc$w_length = strlen(rspec);
1318 fildsc.dsc$a_pointer = rspec;
1320 newace.myace$l_ident = oldace.myace$l_ident;
1321 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1323 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1324 set_errno(ENOENT); break;
1326 set_errno(ENOTDIR); break;
1328 set_errno(ENODEV); break;
1329 case RMS$_SYN: case SS$_INVFILFOROP:
1330 set_errno(EINVAL); break;
1332 set_errno(EACCES); break;
1336 set_vaxc_errno(aclsts);
1340 /* Grab any existing ACEs with this identifier in case we fail */
1341 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1342 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1343 || fndsts == SS$_NOMOREACE ) {
1344 /* Add the new ACE . . . */
1345 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1348 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1350 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1351 Newx(remove_name, NAM$C_MAXRSS+1, char);
1352 do_pathify_dirspec(name, remove_name, 0);
1353 rmsts = rmdir(remove_name);
1354 Safefree(remove_name);
1357 rmsts = rmdir(remove_name);
1361 rmsts = remove(remove_name);
1363 /* We blew it - dir with files in it, no write priv for
1364 * parent directory, etc. Put things back the way they were. */
1365 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1368 addlst[0].bufadr = &oldace;
1369 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1376 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1377 /* We just deleted it, so of course it's not there. Some versions of
1378 * VMS seem to return success on the unlock operation anyhow (after all
1379 * the unlock is successful), but others don't.
1381 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1382 if (aclsts & 1) aclsts = fndsts;
1383 if (!(aclsts & 1)) {
1385 set_vaxc_errno(aclsts);
1393 } /* end of kill_file() */
1397 /*{{{int do_rmdir(char *name)*/
1399 Perl_do_rmdir(pTHX_ const char *name)
1401 char dirfile[NAM$C_MAXRSS+1];
1405 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
1406 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
1407 else retval = mp_do_kill_file(aTHX_ dirfile, 1);
1410 } /* end of do_rmdir */
1414 * Delete any file to which user has control access, regardless of whether
1415 * delete access is explicitly allowed.
1416 * Limitations: User must have write access to parent directory.
1417 * Does not block signals or ASTs; if interrupted in midstream
1418 * may leave file with an altered ACL.
1421 /*{{{int kill_file(char *name)*/
1423 Perl_kill_file(pTHX_ const char *name)
1425 char vmsname[NAM$C_MAXRSS+1], rspec[NAM$C_MAXRSS+1];
1426 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1427 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1428 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1430 unsigned char myace$b_length;
1431 unsigned char myace$b_type;
1432 unsigned short int myace$w_flags;
1433 unsigned long int myace$l_access;
1434 unsigned long int myace$l_ident;
1435 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1436 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1437 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1439 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1440 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1441 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1442 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1443 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1444 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1446 /* Expand the input spec using RMS, since the CRTL remove() and
1447 * system services won't do this by themselves, so we may miss
1448 * a file "hiding" behind a logical name or search list. */
1449 if (do_tovmsspec(name,vmsname,0) == NULL) return -1;
1450 if (do_rmsexpand(vmsname,rspec,1,NULL,0) == NULL) return -1;
1451 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
1452 /* If not, can changing protections help? */
1453 if (vaxc$errno != RMS$_PRV) return -1;
1455 /* No, so we get our own UIC to use as a rights identifier,
1456 * and the insert an ACE at the head of the ACL which allows us
1457 * to delete the file.
1459 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1460 fildsc.dsc$w_length = strlen(rspec);
1461 fildsc.dsc$a_pointer = rspec;
1463 newace.myace$l_ident = oldace.myace$l_ident;
1464 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1466 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1467 set_errno(ENOENT); break;
1469 set_errno(ENOTDIR); break;
1471 set_errno(ENODEV); break;
1472 case RMS$_SYN: case SS$_INVFILFOROP:
1473 set_errno(EINVAL); break;
1475 set_errno(EACCES); break;
1479 set_vaxc_errno(aclsts);
1482 /* Grab any existing ACEs with this identifier in case we fail */
1483 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1484 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1485 || fndsts == SS$_NOMOREACE ) {
1486 /* Add the new ACE . . . */
1487 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1489 if ((rmsts = remove(name))) {
1490 /* We blew it - dir with files in it, no write priv for
1491 * parent directory, etc. Put things back the way they were. */
1492 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1495 addlst[0].bufadr = &oldace;
1496 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1503 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1504 /* We just deleted it, so of course it's not there. Some versions of
1505 * VMS seem to return success on the unlock operation anyhow (after all
1506 * the unlock is successful), but others don't.
1508 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1509 if (aclsts & 1) aclsts = fndsts;
1510 if (!(aclsts & 1)) {
1512 set_vaxc_errno(aclsts);
1518 } /* end of kill_file() */
1522 /*{{{int my_mkdir(char *,Mode_t)*/
1524 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
1526 STRLEN dirlen = strlen(dir);
1528 /* zero length string sometimes gives ACCVIO */
1529 if (dirlen == 0) return -1;
1531 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
1532 * null file name/type. However, it's commonplace under Unix,
1533 * so we'll allow it for a gain in portability.
1535 if (dir[dirlen-1] == '/') {
1536 char *newdir = savepvn(dir,dirlen-1);
1537 int ret = mkdir(newdir,mode);
1541 else return mkdir(dir,mode);
1542 } /* end of my_mkdir */
1545 /*{{{int my_chdir(char *)*/
1547 Perl_my_chdir(pTHX_ const char *dir)
1549 STRLEN dirlen = strlen(dir);
1551 /* zero length string sometimes gives ACCVIO */
1552 if (dirlen == 0) return -1;
1555 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
1556 * This does not work if DECC$EFS_CHARSET is active. Hack it here
1557 * so that existing scripts do not need to be changed.
1560 while ((dirlen > 0) && (*dir1 == ' ')) {
1565 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
1567 * null file name/type. However, it's commonplace under Unix,
1568 * so we'll allow it for a gain in portability.
1570 * - Preview- '/' will be valid soon on VMS
1572 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
1573 char *newdir = savepvn(dir,dirlen-1);
1574 int ret = chdir(newdir);
1578 else return chdir(dir);
1579 } /* end of my_chdir */
1583 /*{{{FILE *my_tmpfile()*/
1590 if ((fp = tmpfile())) return fp;
1592 Newx(cp,L_tmpnam+24,char);
1593 if (decc_filename_unix_only == 0)
1594 strcpy(cp,"Sys$Scratch:");
1597 tmpnam(cp+strlen(cp));
1598 strcat(cp,".Perltmp");
1599 fp = fopen(cp,"w+","fop=dlt");
1606 #ifndef HOMEGROWN_POSIX_SIGNALS
1608 * The C RTL's sigaction fails to check for invalid signal numbers so we
1609 * help it out a bit. The docs are correct, but the actual routine doesn't
1610 * do what the docs say it will.
1612 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
1614 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
1615 struct sigaction* oact)
1617 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
1618 SETERRNO(EINVAL, SS$_INVARG);
1621 return sigaction(sig, act, oact);
1626 #ifdef KILL_BY_SIGPRC
1627 #include <errnodef.h>
1629 /* We implement our own kill() using the undocumented system service
1630 sys$sigprc for one of two reasons:
1632 1.) If the kill() in an older CRTL uses sys$forcex, causing the
1633 target process to do a sys$exit, which usually can't be handled
1634 gracefully...certainly not by Perl and the %SIG{} mechanism.
1636 2.) If the kill() in the CRTL can't be called from a signal
1637 handler without disappearing into the ether, i.e., the signal
1638 it purportedly sends is never trapped. Still true as of VMS 7.3.
1640 sys$sigprc has the same parameters as sys$forcex, but throws an exception
1641 in the target process rather than calling sys$exit.
1643 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
1644 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
1645 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
1646 with condition codes C$_SIG0+nsig*8, catching the exception on the
1647 target process and resignaling with appropriate arguments.
1649 But we don't have that VMS 7.0+ exception handler, so if you
1650 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
1652 Also note that SIGTERM is listed in the docs as being "unimplemented",
1653 yet always seems to be signaled with a VMS condition code of 4 (and
1654 correctly handled for that code). So we hardwire it in.
1656 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
1657 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
1658 than signalling with an unrecognized (and unhandled by CRTL) code.
1661 #define _MY_SIG_MAX 17
1664 Perl_sig_to_vmscondition(int sig)
1666 static unsigned int sig_code[_MY_SIG_MAX+1] =
1669 SS$_HANGUP, /* 1 SIGHUP */
1670 SS$_CONTROLC, /* 2 SIGINT */
1671 SS$_CONTROLY, /* 3 SIGQUIT */
1672 SS$_RADRMOD, /* 4 SIGILL */
1673 SS$_BREAK, /* 5 SIGTRAP */
1674 SS$_OPCCUS, /* 6 SIGABRT */
1675 SS$_COMPAT, /* 7 SIGEMT */
1677 SS$_FLTOVF, /* 8 SIGFPE VAX */
1679 SS$_HPARITH, /* 8 SIGFPE AXP */
1681 SS$_ABORT, /* 9 SIGKILL */
1682 SS$_ACCVIO, /* 10 SIGBUS */
1683 SS$_ACCVIO, /* 11 SIGSEGV */
1684 SS$_BADPARAM, /* 12 SIGSYS */
1685 SS$_NOMBX, /* 13 SIGPIPE */
1686 SS$_ASTFLT, /* 14 SIGALRM */
1692 #if __VMS_VER >= 60200000
1693 static int initted = 0;
1696 sig_code[16] = C$_SIGUSR1;
1697 sig_code[17] = C$_SIGUSR2;
1701 if (sig < _SIG_MIN) return 0;
1702 if (sig > _MY_SIG_MAX) return 0;
1703 return sig_code[sig];
1707 Perl_my_kill(int pid, int sig)
1712 int sys$sigprc(unsigned int *pidadr,
1713 struct dsc$descriptor_s *prcname,
1716 /* sig 0 means validate the PID */
1717 /*------------------------------*/
1719 const unsigned long int jpicode = JPI$_PID;
1722 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
1723 if ($VMS_STATUS_SUCCESS(status))
1726 case SS$_NOSUCHNODE:
1727 case SS$_UNREACHABLE:
1741 code = Perl_sig_to_vmscondition(sig);
1744 SETERRNO(EINVAL, SS$_BADPARAM);
1748 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
1749 * signals are to be sent to multiple processes.
1750 * pid = 0 - all processes in group except ones that the system exempts
1751 * pid = -1 - all processes except ones that the system exempts
1752 * pid = -n - all processes in group (abs(n)) except ...
1753 * For now, just report as not supported.
1757 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
1761 iss = sys$sigprc((unsigned int *)&pid,0,code);
1762 if (iss&1) return 0;
1766 set_errno(EPERM); break;
1768 case SS$_NOSUCHNODE:
1769 case SS$_UNREACHABLE:
1770 set_errno(ESRCH); break;
1772 set_errno(ENOMEM); break;
1777 set_vaxc_errno(iss);
1783 /* Routine to convert a VMS status code to a UNIX status code.
1784 ** More tricky than it appears because of conflicting conventions with
1787 ** VMS status codes are a bit mask, with the least significant bit set for
1790 ** Special UNIX status of EVMSERR indicates that no translation is currently
1791 ** available, and programs should check the VMS status code.
1793 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
1797 #ifndef C_FACILITY_NO
1798 #define C_FACILITY_NO 0x350000
1801 #define DCL_IVVERB 0x38090
1804 int Perl_vms_status_to_unix(int vms_status, int child_flag)
1812 /* Assume the best or the worst */
1813 if (vms_status & STS$M_SUCCESS)
1816 unix_status = EVMSERR;
1818 msg_status = vms_status & ~STS$M_CONTROL;
1820 facility = vms_status & STS$M_FAC_NO;
1821 fac_sp = vms_status & STS$M_FAC_SP;
1822 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
1824 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
1830 unix_status = EFAULT;
1832 case SS$_DEVOFFLINE:
1833 unix_status = EBUSY;
1836 unix_status = ENOTCONN;
1844 case SS$_INVFILFOROP:
1848 unix_status = EINVAL;
1850 case SS$_UNSUPPORTED:
1851 unix_status = ENOTSUP;
1856 unix_status = EACCES;
1858 case SS$_DEVICEFULL:
1859 unix_status = ENOSPC;
1862 unix_status = ENODEV;
1864 case SS$_NOSUCHFILE:
1865 case SS$_NOSUCHOBJECT:
1866 unix_status = ENOENT;
1868 case SS$_ABORT: /* Fatal case */
1869 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
1870 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
1871 unix_status = EINTR;
1874 unix_status = E2BIG;
1877 unix_status = ENOMEM;
1880 unix_status = EPERM;
1882 case SS$_NOSUCHNODE:
1883 case SS$_UNREACHABLE:
1884 unix_status = ESRCH;
1887 unix_status = ECHILD;
1890 if ((facility == 0) && (msg_no < 8)) {
1891 /* These are not real VMS status codes so assume that they are
1892 ** already UNIX status codes
1894 unix_status = msg_no;
1900 /* Translate a POSIX exit code to a UNIX exit code */
1901 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
1902 unix_status = (msg_no & 0x07F8) >> 3;
1906 /* Documented traditional behavior for handling VMS child exits */
1907 /*--------------------------------------------------------------*/
1908 if (child_flag != 0) {
1910 /* Success / Informational return 0 */
1911 /*----------------------------------*/
1912 if (msg_no & STS$K_SUCCESS)
1915 /* Warning returns 1 */
1916 /*-------------------*/
1917 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
1920 /* Everything else pass through the severity bits */
1921 /*------------------------------------------------*/
1922 return (msg_no & STS$M_SEVERITY);
1925 /* Normal VMS status to ERRNO mapping attempt */
1926 /*--------------------------------------------*/
1927 switch(msg_status) {
1928 /* case RMS$_EOF: */ /* End of File */
1929 case RMS$_FNF: /* File Not Found */
1930 case RMS$_DNF: /* Dir Not Found */
1931 unix_status = ENOENT;
1933 case RMS$_RNF: /* Record Not Found */
1934 unix_status = ESRCH;
1937 unix_status = ENOTDIR;
1940 unix_status = ENODEV;
1945 unix_status = EBADF;
1948 unix_status = EEXIST;
1952 case LIB$_INVSTRDES:
1954 case LIB$_NOSUCHSYM:
1955 case LIB$_INVSYMNAM:
1957 unix_status = EINVAL;
1963 unix_status = E2BIG;
1965 case RMS$_PRV: /* No privilege */
1966 case RMS$_ACC: /* ACP file access failed */
1967 case RMS$_WLK: /* Device write locked */
1968 unix_status = EACCES;
1970 /* case RMS$_NMF: */ /* No more files */
1978 /* Try to guess at what VMS error status should go with a UNIX errno
1979 * value. This is hard to do as there could be many possible VMS
1980 * error statuses that caused the errno value to be set.
1983 int Perl_unix_status_to_vms(int unix_status)
1985 int test_unix_status;
1987 /* Trivial cases first */
1988 /*---------------------*/
1989 if (unix_status == EVMSERR)
1992 /* Is vaxc$errno sane? */
1993 /*---------------------*/
1994 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
1995 if (test_unix_status == unix_status)
1998 /* If way out of range, must be VMS code already */
1999 /*-----------------------------------------------*/
2000 if (unix_status > EVMSERR)
2003 /* If out of range, punt */
2004 /*-----------------------*/
2005 if (unix_status > __ERRNO_MAX)
2009 /* Ok, now we have to do it the hard way. */
2010 /*----------------------------------------*/
2011 switch(unix_status) {
2012 case 0: return SS$_NORMAL;
2013 case EPERM: return SS$_NOPRIV;
2014 case ENOENT: return SS$_NOSUCHOBJECT;
2015 case ESRCH: return SS$_UNREACHABLE;
2016 case EINTR: return SS$_ABORT;
2019 case E2BIG: return SS$_BUFFEROVF;
2021 case EBADF: return RMS$_IFI;
2022 case ECHILD: return SS$_NONEXPR;
2024 case ENOMEM: return SS$_INSFMEM;
2025 case EACCES: return SS$_FILACCERR;
2026 case EFAULT: return SS$_ACCVIO;
2028 case EBUSY: return SS$_DEVOFFLINE;
2029 case EEXIST: return RMS$_FEX;
2031 case ENODEV: return SS$_NOSUCHDEV;
2032 case ENOTDIR: return RMS$_DIR;
2034 case EINVAL: return SS$_INVARG;
2040 case ENOSPC: return SS$_DEVICEFULL;
2041 case ESPIPE: return LIB$_INVARG;
2046 case ERANGE: return LIB$_INVARG;
2047 /* case EWOULDBLOCK */
2048 /* case EINPROGRESS */
2051 /* case EDESTADDRREQ */
2053 /* case EPROTOTYPE */
2054 /* case ENOPROTOOPT */
2055 /* case EPROTONOSUPPORT */
2056 /* case ESOCKTNOSUPPORT */
2057 /* case EOPNOTSUPP */
2058 /* case EPFNOSUPPORT */
2059 /* case EAFNOSUPPORT */
2060 /* case EADDRINUSE */
2061 /* case EADDRNOTAVAIL */
2063 /* case ENETUNREACH */
2064 /* case ENETRESET */
2065 /* case ECONNABORTED */
2066 /* case ECONNRESET */
2069 case ENOTCONN: return SS$_CLEARED;
2070 /* case ESHUTDOWN */
2071 /* case ETOOMANYREFS */
2072 /* case ETIMEDOUT */
2073 /* case ECONNREFUSED */
2075 /* case ENAMETOOLONG */
2076 /* case EHOSTDOWN */
2077 /* case EHOSTUNREACH */
2078 /* case ENOTEMPTY */
2090 /* case ECANCELED */
2094 return SS$_UNSUPPORTED;
2100 /* case EABANDONED */
2102 return SS$_ABORT; /* punt */
2105 return SS$_ABORT; /* Should not get here */
2109 /* default piping mailbox size */
2110 #define PERL_BUFSIZ 512
2114 create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2116 unsigned long int mbxbufsiz;
2117 static unsigned long int syssize = 0;
2118 unsigned long int dviitm = DVI$_DEVNAM;
2119 char csize[LNM$C_NAMLENGTH+1];
2123 unsigned long syiitm = SYI$_MAXBUF;
2125 * Get the SYSGEN parameter MAXBUF
2127 * If the logical 'PERL_MBX_SIZE' is defined
2128 * use the value of the logical instead of PERL_BUFSIZ, but
2129 * keep the size between 128 and MAXBUF.
2132 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2135 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2136 mbxbufsiz = atoi(csize);
2138 mbxbufsiz = PERL_BUFSIZ;
2140 if (mbxbufsiz < 128) mbxbufsiz = 128;
2141 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2143 _ckvmssts(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2145 _ckvmssts(sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
2146 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2148 } /* end of create_mbx() */
2151 /*{{{ my_popen and my_pclose*/
2153 typedef struct _iosb IOSB;
2154 typedef struct _iosb* pIOSB;
2155 typedef struct _pipe Pipe;
2156 typedef struct _pipe* pPipe;
2157 typedef struct pipe_details Info;
2158 typedef struct pipe_details* pInfo;
2159 typedef struct _srqp RQE;
2160 typedef struct _srqp* pRQE;
2161 typedef struct _tochildbuf CBuf;
2162 typedef struct _tochildbuf* pCBuf;
2165 unsigned short status;
2166 unsigned short count;
2167 unsigned long dvispec;
2170 #pragma member_alignment save
2171 #pragma nomember_alignment quadword
2172 struct _srqp { /* VMS self-relative queue entry */
2173 unsigned long qptr[2];
2175 #pragma member_alignment restore
2176 static RQE RQE_ZERO = {0,0};
2178 struct _tochildbuf {
2181 unsigned short size;
2189 unsigned short chan_in;
2190 unsigned short chan_out;
2192 unsigned int bufsize;
2204 #if defined(PERL_IMPLICIT_CONTEXT)
2205 void *thx; /* Either a thread or an interpreter */
2206 /* pointer, depending on how we're built */
2214 PerlIO *fp; /* file pointer to pipe mailbox */
2215 int useFILE; /* using stdio, not perlio */
2216 int pid; /* PID of subprocess */
2217 int mode; /* == 'r' if pipe open for reading */
2218 int done; /* subprocess has completed */
2219 int waiting; /* waiting for completion/closure */
2220 int closing; /* my_pclose is closing this pipe */
2221 unsigned long completion; /* termination status of subprocess */
2222 pPipe in; /* pipe in to sub */
2223 pPipe out; /* pipe out of sub */
2224 pPipe err; /* pipe of sub's sys$error */
2225 int in_done; /* true when in pipe finished */
2230 struct exit_control_block
2232 struct exit_control_block *flink;
2233 unsigned long int (*exit_routine)();
2234 unsigned long int arg_count;
2235 unsigned long int *status_address;
2236 unsigned long int exit_status;
2239 typedef struct _closed_pipes Xpipe;
2240 typedef struct _closed_pipes* pXpipe;
2242 struct _closed_pipes {
2243 int pid; /* PID of subprocess */
2244 unsigned long completion; /* termination status of subprocess */
2246 #define NKEEPCLOSED 50
2247 static Xpipe closed_list[NKEEPCLOSED];
2248 static int closed_index = 0;
2249 static int closed_num = 0;
2251 #define RETRY_DELAY "0 ::0.20"
2252 #define MAX_RETRY 50
2254 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2255 static unsigned long mypid;
2256 static unsigned long delaytime[2];
2258 static pInfo open_pipes = NULL;
2259 static $DESCRIPTOR(nl_desc, "NL:");
2261 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2265 static unsigned long int
2266 pipe_exit_routine(pTHX)
2269 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2270 int sts, did_stuff, need_eof, j;
2273 flush any pending i/o
2279 PerlIO_flush(info->fp); /* first, flush data */
2281 fflush((FILE *)info->fp);
2287 next we try sending an EOF...ignore if doesn't work, make sure we
2295 _ckvmssts(sys$setast(0));
2296 if (info->in && !info->in->shut_on_empty) {
2297 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2302 _ckvmssts(sys$setast(1));
2306 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2308 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2313 _ckvmssts(sys$setast(0));
2314 if (info->waiting && info->done)
2316 nwait += info->waiting;
2317 _ckvmssts(sys$setast(1));
2327 _ckvmssts(sys$setast(0));
2328 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2329 sts = sys$forcex(&info->pid,0,&abort);
2330 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
2333 _ckvmssts(sys$setast(1));
2337 /* again, wait for effect */
2339 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2344 _ckvmssts(sys$setast(0));
2345 if (info->waiting && info->done)
2347 nwait += info->waiting;
2348 _ckvmssts(sys$setast(1));
2357 _ckvmssts(sys$setast(0));
2358 if (!info->done) { /* We tried to be nice . . . */
2359 sts = sys$delprc(&info->pid,0);
2360 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts(sts);
2362 _ckvmssts(sys$setast(1));
2367 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
2368 else if (!(sts & 1)) retsts = sts;
2373 static struct exit_control_block pipe_exitblock =
2374 {(struct exit_control_block *) 0,
2375 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
2377 static void pipe_mbxtofd_ast(pPipe p);
2378 static void pipe_tochild1_ast(pPipe p);
2379 static void pipe_tochild2_ast(pPipe p);
2382 popen_completion_ast(pInfo info)
2384 pInfo i = open_pipes;
2389 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
2390 closed_list[closed_index].pid = info->pid;
2391 closed_list[closed_index].completion = info->completion;
2393 if (closed_index == NKEEPCLOSED)
2398 if (i == info) break;
2401 if (!i) return; /* unlinked, probably freed too */
2406 Writing to subprocess ...
2407 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
2409 chan_out may be waiting for "done" flag, or hung waiting
2410 for i/o completion to child...cancel the i/o. This will
2411 put it into "snarf mode" (done but no EOF yet) that discards
2414 Output from subprocess (stdout, stderr) needs to be flushed and
2415 shut down. We try sending an EOF, but if the mbx is full the pipe
2416 routine should still catch the "shut_on_empty" flag, telling it to
2417 use immediate-style reads so that "mbx empty" -> EOF.
2421 if (info->in && !info->in_done) { /* only for mode=w */
2422 if (info->in->shut_on_empty && info->in->need_wake) {
2423 info->in->need_wake = FALSE;
2424 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
2426 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
2430 if (info->out && !info->out_done) { /* were we also piping output? */
2431 info->out->shut_on_empty = TRUE;
2432 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2433 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
2434 _ckvmssts_noperl(iss);
2437 if (info->err && !info->err_done) { /* we were piping stderr */
2438 info->err->shut_on_empty = TRUE;
2439 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2440 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
2441 _ckvmssts_noperl(iss);
2443 _ckvmssts_noperl(sys$setef(pipe_ef));
2447 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
2448 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
2451 we actually differ from vmstrnenv since we use this to
2452 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
2453 are pointing to the same thing
2456 static unsigned short
2457 popen_translate(pTHX_ char *logical, char *result)
2460 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
2461 $DESCRIPTOR(d_log,"");
2463 unsigned short length;
2464 unsigned short code;
2466 unsigned short *retlenaddr;
2468 unsigned short l, ifi;
2470 d_log.dsc$a_pointer = logical;
2471 d_log.dsc$w_length = strlen(logical);
2473 itmlst[0].code = LNM$_STRING;
2474 itmlst[0].length = 255;
2475 itmlst[0].buffer_addr = result;
2476 itmlst[0].retlenaddr = &l;
2479 itmlst[1].length = 0;
2480 itmlst[1].buffer_addr = 0;
2481 itmlst[1].retlenaddr = 0;
2483 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
2484 if (iss == SS$_NOLOGNAM) {
2488 if (!(iss&1)) lib$signal(iss);
2491 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
2492 strip it off and return the ifi, if any
2495 if (result[0] == 0x1b && result[1] == 0x00) {
2496 memmove(&ifi,result+2,2);
2497 strcpy(result,result+4);
2499 return ifi; /* this is the RMS internal file id */
2502 static void pipe_infromchild_ast(pPipe p);
2505 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
2506 inside an AST routine without worrying about reentrancy and which Perl
2507 memory allocator is being used.
2509 We read data and queue up the buffers, then spit them out one at a
2510 time to the output mailbox when the output mailbox is ready for one.
2513 #define INITIAL_TOCHILDQUEUE 2
2516 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
2520 char mbx1[64], mbx2[64];
2521 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
2522 DSC$K_CLASS_S, mbx1},
2523 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
2524 DSC$K_CLASS_S, mbx2};
2525 unsigned int dviitm = DVI$_DEVBUFSIZ;
2530 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
2531 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
2532 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2535 p->shut_on_empty = FALSE;
2536 p->need_wake = FALSE;
2539 p->iosb.status = SS$_NORMAL;
2540 p->iosb2.status = SS$_NORMAL;
2546 #ifdef PERL_IMPLICIT_CONTEXT
2550 n = sizeof(CBuf) + p->bufsize;
2552 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
2553 _ckvmssts(lib$get_vm(&n, &b));
2554 b->buf = (char *) b + sizeof(CBuf);
2555 _ckvmssts(lib$insqhi(b, &p->free));
2558 pipe_tochild2_ast(p);
2559 pipe_tochild1_ast(p);
2565 /* reads the MBX Perl is writing, and queues */
2568 pipe_tochild1_ast(pPipe p)
2571 int iss = p->iosb.status;
2572 int eof = (iss == SS$_ENDOFFILE);
2574 #ifdef PERL_IMPLICIT_CONTEXT
2580 p->shut_on_empty = TRUE;
2582 _ckvmssts(sys$dassgn(p->chan_in));
2588 b->size = p->iosb.count;
2589 _ckvmssts(sts = lib$insqhi(b, &p->wait));
2591 p->need_wake = FALSE;
2592 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
2595 p->retry = 1; /* initial call */
2598 if (eof) { /* flush the free queue, return when done */
2599 int n = sizeof(CBuf) + p->bufsize;
2601 iss = lib$remqti(&p->free, &b);
2602 if (iss == LIB$_QUEWASEMP) return;
2604 _ckvmssts(lib$free_vm(&n, &b));
2608 iss = lib$remqti(&p->free, &b);
2609 if (iss == LIB$_QUEWASEMP) {
2610 int n = sizeof(CBuf) + p->bufsize;
2611 _ckvmssts(lib$get_vm(&n, &b));
2612 b->buf = (char *) b + sizeof(CBuf);
2618 iss = sys$qio(0,p->chan_in,
2619 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
2621 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
2622 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
2627 /* writes queued buffers to output, waits for each to complete before
2631 pipe_tochild2_ast(pPipe p)
2634 int iss = p->iosb2.status;
2635 int n = sizeof(CBuf) + p->bufsize;
2636 int done = (p->info && p->info->done) ||
2637 iss == SS$_CANCEL || iss == SS$_ABORT;
2638 #if defined(PERL_IMPLICIT_CONTEXT)
2643 if (p->type) { /* type=1 has old buffer, dispose */
2644 if (p->shut_on_empty) {
2645 _ckvmssts(lib$free_vm(&n, &b));
2647 _ckvmssts(lib$insqhi(b, &p->free));
2652 iss = lib$remqti(&p->wait, &b);
2653 if (iss == LIB$_QUEWASEMP) {
2654 if (p->shut_on_empty) {
2656 _ckvmssts(sys$dassgn(p->chan_out));
2657 *p->pipe_done = TRUE;
2658 _ckvmssts(sys$setef(pipe_ef));
2660 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
2661 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
2665 p->need_wake = TRUE;
2675 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
2676 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
2678 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
2679 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
2688 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
2691 char mbx1[64], mbx2[64];
2692 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
2693 DSC$K_CLASS_S, mbx1},
2694 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
2695 DSC$K_CLASS_S, mbx2};
2696 unsigned int dviitm = DVI$_DEVBUFSIZ;
2699 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
2700 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
2702 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2703 Newx(p->buf, p->bufsize, char);
2704 p->shut_on_empty = FALSE;
2707 p->iosb.status = SS$_NORMAL;
2708 #if defined(PERL_IMPLICIT_CONTEXT)
2711 pipe_infromchild_ast(p);
2719 pipe_infromchild_ast(pPipe p)
2721 int iss = p->iosb.status;
2722 int eof = (iss == SS$_ENDOFFILE);
2723 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
2724 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
2725 #if defined(PERL_IMPLICIT_CONTEXT)
2729 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
2730 _ckvmssts(sys$dassgn(p->chan_out));
2735 input shutdown if EOF from self (done or shut_on_empty)
2736 output shutdown if closing flag set (my_pclose)
2737 send data/eof from child or eof from self
2738 otherwise, re-read (snarf of data from child)
2743 if (myeof && p->chan_in) { /* input shutdown */
2744 _ckvmssts(sys$dassgn(p->chan_in));
2749 if (myeof || kideof) { /* pass EOF to parent */
2750 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
2751 pipe_infromchild_ast, p,
2754 } else if (eof) { /* eat EOF --- fall through to read*/
2756 } else { /* transmit data */
2757 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
2758 pipe_infromchild_ast,p,
2759 p->buf, p->iosb.count, 0, 0, 0, 0));
2765 /* everything shut? flag as done */
2767 if (!p->chan_in && !p->chan_out) {
2768 *p->pipe_done = TRUE;
2769 _ckvmssts(sys$setef(pipe_ef));
2773 /* write completed (or read, if snarfing from child)
2774 if still have input active,
2775 queue read...immediate mode if shut_on_empty so we get EOF if empty
2777 check if Perl reading, generate EOFs as needed
2783 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
2784 pipe_infromchild_ast,p,
2785 p->buf, p->bufsize, 0, 0, 0, 0);
2786 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
2788 } else { /* send EOFs for extra reads */
2789 p->iosb.status = SS$_ENDOFFILE;
2790 p->iosb.dvispec = 0;
2791 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
2793 pipe_infromchild_ast, p, 0, 0, 0, 0));
2799 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
2803 unsigned long dviitm = DVI$_DEVBUFSIZ;
2805 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
2806 DSC$K_CLASS_S, mbx};
2808 /* things like terminals and mbx's don't need this filter */
2809 if (fd && fstat(fd,&s) == 0) {
2810 unsigned long dviitm = DVI$_DEVCHAR, devchar;
2811 struct dsc$descriptor_s d_dev = {strlen(s.st_dev), DSC$K_DTYPE_T,
2812 DSC$K_CLASS_S, s.st_dev};
2814 _ckvmssts(lib$getdvi(&dviitm,0,&d_dev,&devchar,0,0));
2815 if (!(devchar & DEV$M_DIR)) { /* non directory structured...*/
2816 strcpy(out, s.st_dev);
2822 p->fd_out = dup(fd);
2823 create_mbx(aTHX_ &p->chan_in, &d_mbx);
2824 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2825 Newx(p->buf, p->bufsize+1, char);
2826 p->shut_on_empty = FALSE;
2831 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
2832 pipe_mbxtofd_ast, p,
2833 p->buf, p->bufsize, 0, 0, 0, 0));
2839 pipe_mbxtofd_ast(pPipe p)
2841 int iss = p->iosb.status;
2842 int done = p->info->done;
2844 int eof = (iss == SS$_ENDOFFILE);
2845 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
2846 int err = !(iss&1) && !eof;
2847 #if defined(PERL_IMPLICIT_CONTEXT)
2851 if (done && myeof) { /* end piping */
2853 sys$dassgn(p->chan_in);
2854 *p->pipe_done = TRUE;
2855 _ckvmssts(sys$setef(pipe_ef));
2859 if (!err && !eof) { /* good data to send to file */
2860 p->buf[p->iosb.count] = '\n';
2861 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
2864 if (p->retry < MAX_RETRY) {
2865 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
2875 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
2876 pipe_mbxtofd_ast, p,
2877 p->buf, p->bufsize, 0, 0, 0, 0);
2878 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
2883 typedef struct _pipeloc PLOC;
2884 typedef struct _pipeloc* pPLOC;
2888 char dir[NAM$C_MAXRSS+1];
2890 static pPLOC head_PLOC = 0;
2893 free_pipelocs(pTHX_ void *head)
2896 pPLOC *pHead = (pPLOC *)head;
2908 store_pipelocs(pTHX)
2917 char temp[NAM$C_MAXRSS+1];
2921 free_pipelocs(aTHX_ &head_PLOC);
2923 /* the . directory from @INC comes last */
2926 p->next = head_PLOC;
2928 strcpy(p->dir,"./");
2930 /* get the directory from $^X */
2932 #ifdef PERL_IMPLICIT_CONTEXT
2933 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
2935 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
2937 strcpy(temp, PL_origargv[0]);
2938 x = strrchr(temp,']');
2940 x = strrchr(temp,'>');
2942 /* It could be a UNIX path */
2943 x = strrchr(temp,'/');
2949 /* Got a bare name, so use default directory */
2954 if ((unixdir = tounixpath(temp, Nullch)) != Nullch) {
2956 p->next = head_PLOC;
2958 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2959 p->dir[NAM$C_MAXRSS] = '\0';
2963 /* reverse order of @INC entries, skip "." since entered above */
2965 #ifdef PERL_IMPLICIT_CONTEXT
2968 if (PL_incgv) av = GvAVn(PL_incgv);
2970 for (i = 0; av && i <= AvFILL(av); i++) {
2971 dirsv = *av_fetch(av,i,TRUE);
2973 if (SvROK(dirsv)) continue;
2974 dir = SvPVx(dirsv,n_a);
2975 if (strcmp(dir,".") == 0) continue;
2976 if ((unixdir = tounixpath(dir, Nullch)) == Nullch)
2980 p->next = head_PLOC;
2982 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2983 p->dir[NAM$C_MAXRSS] = '\0';
2986 /* most likely spot (ARCHLIB) put first in the list */
2989 if ((unixdir = tounixpath(ARCHLIB_EXP, Nullch)) != Nullch) {
2991 p->next = head_PLOC;
2993 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
2994 p->dir[NAM$C_MAXRSS] = '\0';
3003 static int vmspipe_file_status = 0;
3004 static char vmspipe_file[NAM$C_MAXRSS+1];
3006 /* already found? Check and use ... need read+execute permission */
3008 if (vmspipe_file_status == 1) {
3009 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
3010 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
3011 return vmspipe_file;
3013 vmspipe_file_status = 0;
3016 /* scan through stored @INC, $^X */
3018 if (vmspipe_file_status == 0) {
3019 char file[NAM$C_MAXRSS+1];
3020 pPLOC p = head_PLOC;
3023 strcpy(file, p->dir);
3024 strncat(file, "vmspipe.com",NAM$C_MAXRSS);
3025 file[NAM$C_MAXRSS] = '\0';
3028 if (!do_tovmsspec(file,vmspipe_file,0)) continue;
3030 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
3031 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
3032 vmspipe_file_status = 1;
3033 return vmspipe_file;
3036 vmspipe_file_status = -1; /* failed, use tempfiles */
3043 vmspipe_tempfile(pTHX)
3045 char file[NAM$C_MAXRSS+1];
3047 static int index = 0;
3051 /* create a tempfile */
3053 /* we can't go from W, shr=get to R, shr=get without
3054 an intermediate vulnerable state, so don't bother trying...
3056 and lib$spawn doesn't shr=put, so have to close the write
3058 So... match up the creation date/time and the FID to
3059 make sure we're dealing with the same file
3064 if (!decc_filename_unix_only) {
3065 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3066 fp = fopen(file,"w");
3068 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3069 fp = fopen(file,"w");
3071 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3072 fp = fopen(file,"w");
3077 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3078 fp = fopen(file,"w");
3080 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3081 fp = fopen(file,"w");
3083 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3084 fp = fopen(file,"w");
3088 if (!fp) return 0; /* we're hosed */
3090 fprintf(fp,"$! 'f$verify(0)'\n");
3091 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3092 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3093 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3094 fprintf(fp,"$ perl_on = \"set noon\"\n");
3095 fprintf(fp,"$ perl_exit = \"exit\"\n");
3096 fprintf(fp,"$ perl_del = \"delete\"\n");
3097 fprintf(fp,"$ pif = \"if\"\n");
3098 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3099 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3100 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3101 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3102 fprintf(fp,"$! --- build command line to get max possible length\n");
3103 fprintf(fp,"$c=perl_popen_cmd0\n");
3104 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3105 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3106 fprintf(fp,"$x=perl_popen_cmd3\n");
3107 fprintf(fp,"$c=c+x\n");
3108 fprintf(fp,"$ perl_on\n");
3109 fprintf(fp,"$ 'c'\n");
3110 fprintf(fp,"$ perl_status = $STATUS\n");
3111 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3112 fprintf(fp,"$ perl_exit 'perl_status'\n");
3115 fgetname(fp, file, 1);
3116 fstat(fileno(fp), (struct stat *)&s0);
3119 if (decc_filename_unix_only)
3120 do_tounixspec(file, file, 0);
3121 fp = fopen(file,"r","shr=get");
3123 fstat(fileno(fp), (struct stat *)&s1);
3125 #if defined(_USE_STD_STAT)
3126 cmp_result = s0.crtl_stat.st_ino != s1.crtl_stat.st_ino;
3128 cmp_result = memcmp(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino, 6);
3130 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3141 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
3143 static int handler_set_up = FALSE;
3144 unsigned long int sts, flags = CLI$M_NOWAIT;
3145 /* The use of a GLOBAL table (as was done previously) rendered
3146 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
3147 * environment. Hence we've switched to LOCAL symbol table.
3149 unsigned int table = LIB$K_CLI_LOCAL_SYM;
3151 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
3152 char in[512], out[512], err[512], mbx[512];
3154 char tfilebuf[NAM$C_MAXRSS+1];
3156 char cmd_sym_name[20];
3157 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
3158 DSC$K_CLASS_S, symbol};
3159 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
3161 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
3162 DSC$K_CLASS_S, cmd_sym_name};
3163 struct dsc$descriptor_s *vmscmd;
3164 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
3165 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
3166 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
3168 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
3170 /* once-per-program initialization...
3171 note that the SETAST calls and the dual test of pipe_ef
3172 makes sure that only the FIRST thread through here does
3173 the initialization...all other threads wait until it's
3176 Yeah, uglier than a pthread call, it's got all the stuff inline
3177 rather than in a separate routine.
3181 _ckvmssts(sys$setast(0));
3183 unsigned long int pidcode = JPI$_PID;
3184 $DESCRIPTOR(d_delay, RETRY_DELAY);
3185 _ckvmssts(lib$get_ef(&pipe_ef));
3186 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
3187 _ckvmssts(sys$bintim(&d_delay, delaytime));
3189 if (!handler_set_up) {
3190 _ckvmssts(sys$dclexh(&pipe_exitblock));
3191 handler_set_up = TRUE;
3193 _ckvmssts(sys$setast(1));
3196 /* see if we can find a VMSPIPE.COM */
3199 vmspipe = find_vmspipe(aTHX);
3201 strcpy(tfilebuf+1,vmspipe);
3202 } else { /* uh, oh...we're in tempfile hell */
3203 tpipe = vmspipe_tempfile(aTHX);
3204 if (!tpipe) { /* a fish popular in Boston */
3205 if (ckWARN(WARN_PIPE)) {
3206 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
3210 fgetname(tpipe,tfilebuf+1,1);
3212 vmspipedsc.dsc$a_pointer = tfilebuf;
3213 vmspipedsc.dsc$w_length = strlen(tfilebuf);
3215 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
3218 case RMS$_FNF: case RMS$_DNF:
3219 set_errno(ENOENT); break;
3221 set_errno(ENOTDIR); break;
3223 set_errno(ENODEV); break;
3225 set_errno(EACCES); break;
3227 set_errno(EINVAL); break;
3228 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
3229 set_errno(E2BIG); break;
3230 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
3231 _ckvmssts(sts); /* fall through */
3232 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
3235 set_vaxc_errno(sts);
3236 if (*mode != 'n' && ckWARN(WARN_PIPE)) {
3237 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
3244 strcpy(mode,in_mode);
3247 info->completion = 0;
3248 info->closing = FALSE;
3255 info->in_done = TRUE;
3256 info->out_done = TRUE;
3257 info->err_done = TRUE;
3258 in[0] = out[0] = err[0] = '\0';
3260 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
3264 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
3269 if (*mode == 'r') { /* piping from subroutine */
3271 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
3273 info->out->pipe_done = &info->out_done;
3274 info->out_done = FALSE;
3275 info->out->info = info;
3277 if (!info->useFILE) {
3278 info->fp = PerlIO_open(mbx, mode);
3280 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
3281 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
3284 if (!info->fp && info->out) {
3285 sys$cancel(info->out->chan_out);
3287 while (!info->out_done) {
3289 _ckvmssts(sys$setast(0));
3290 done = info->out_done;
3291 if (!done) _ckvmssts(sys$clref(pipe_ef));
3292 _ckvmssts(sys$setast(1));
3293 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3296 if (info->out->buf) Safefree(info->out->buf);
3297 Safefree(info->out);
3303 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3305 info->err->pipe_done = &info->err_done;
3306 info->err_done = FALSE;
3307 info->err->info = info;
3310 } else if (*mode == 'w') { /* piping to subroutine */
3312 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
3314 info->out->pipe_done = &info->out_done;
3315 info->out_done = FALSE;
3316 info->out->info = info;
3319 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3321 info->err->pipe_done = &info->err_done;
3322 info->err_done = FALSE;
3323 info->err->info = info;
3326 info->in = pipe_tochild_setup(aTHX_ in,mbx);
3327 if (!info->useFILE) {
3328 info->fp = PerlIO_open(mbx, mode);
3330 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
3331 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
3335 info->in->pipe_done = &info->in_done;
3336 info->in_done = FALSE;
3337 info->in->info = info;
3341 if (!info->fp && info->in) {
3343 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
3344 0, 0, 0, 0, 0, 0, 0, 0));
3346 while (!info->in_done) {
3348 _ckvmssts(sys$setast(0));
3349 done = info->in_done;
3350 if (!done) _ckvmssts(sys$clref(pipe_ef));
3351 _ckvmssts(sys$setast(1));
3352 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3355 if (info->in->buf) Safefree(info->in->buf);
3363 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
3364 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
3366 info->out->pipe_done = &info->out_done;
3367 info->out_done = FALSE;
3368 info->out->info = info;
3371 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3373 info->err->pipe_done = &info->err_done;
3374 info->err_done = FALSE;
3375 info->err->info = info;
3379 symbol[MAX_DCL_SYMBOL] = '\0';
3381 strncpy(symbol, in, MAX_DCL_SYMBOL);
3382 d_symbol.dsc$w_length = strlen(symbol);
3383 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
3385 strncpy(symbol, err, MAX_DCL_SYMBOL);
3386 d_symbol.dsc$w_length = strlen(symbol);
3387 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
3389 strncpy(symbol, out, MAX_DCL_SYMBOL);
3390 d_symbol.dsc$w_length = strlen(symbol);
3391 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table));
3393 p = vmscmd->dsc$a_pointer;
3394 while (*p && *p != '\n') p++;
3395 *p = '\0'; /* truncate on \n */
3396 p = vmscmd->dsc$a_pointer;
3397 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
3398 if (*p == '$') p++; /* remove leading $ */
3399 while (*p == ' ' || *p == '\t') p++;
3401 for (j = 0; j < 4; j++) {
3402 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
3403 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
3405 strncpy(symbol, p, MAX_DCL_SYMBOL);
3406 d_symbol.dsc$w_length = strlen(symbol);
3407 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
3409 if (strlen(p) > MAX_DCL_SYMBOL) {
3410 p += MAX_DCL_SYMBOL;
3415 _ckvmssts(sys$setast(0));
3416 info->next=open_pipes; /* prepend to list */
3418 _ckvmssts(sys$setast(1));
3419 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
3420 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
3421 * have SYS$COMMAND if we need it.
3423 _ckvmssts(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
3424 0, &info->pid, &info->completion,
3425 0, popen_completion_ast,info,0,0,0));
3427 /* if we were using a tempfile, close it now */
3429 if (tpipe) fclose(tpipe);
3431 /* once the subprocess is spawned, it has copied the symbols and
3432 we can get rid of ours */
3434 for (j = 0; j < 4; j++) {
3435 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
3436 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
3437 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
3439 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
3440 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
3441 _ckvmssts(lib$delete_symbol(&d_sym_out, &table));
3442 vms_execfree(vmscmd);
3444 #ifdef PERL_IMPLICIT_CONTEXT
3447 PL_forkprocess = info->pid;
3452 _ckvmssts(sys$setast(0));
3454 if (!done) _ckvmssts(sys$clref(pipe_ef));
3455 _ckvmssts(sys$setast(1));
3456 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3458 *psts = info->completion;
3459 /* Caller thinks it is open and tries to close it. */
3460 /* This causes some problems, as it changes the error status */
3461 /* my_pclose(info->fp); */
3466 } /* end of safe_popen */
3469 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
3471 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
3475 TAINT_PROPER("popen");
3476 PERL_FLUSHALL_FOR_CHILD;
3477 return safe_popen(aTHX_ cmd,mode,&sts);
3482 /*{{{ I32 my_pclose(PerlIO *fp)*/
3483 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
3485 pInfo info, last = NULL;
3486 unsigned long int retsts;
3489 for (info = open_pipes; info != NULL; last = info, info = info->next)
3490 if (info->fp == fp) break;
3492 if (info == NULL) { /* no such pipe open */
3493 set_errno(ECHILD); /* quoth POSIX */
3494 set_vaxc_errno(SS$_NONEXPR);
3498 /* If we were writing to a subprocess, insure that someone reading from
3499 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
3500 * produce an EOF record in the mailbox.
3502 * well, at least sometimes it *does*, so we have to watch out for
3503 * the first EOF closing the pipe (and DASSGN'ing the channel)...
3507 PerlIO_flush(info->fp); /* first, flush data */
3509 fflush((FILE *)info->fp);
3512 _ckvmssts(sys$setast(0));
3513 info->closing = TRUE;
3514 done = info->done && info->in_done && info->out_done && info->err_done;
3515 /* hanging on write to Perl's input? cancel it */
3516 if (info->mode == 'r' && info->out && !info->out_done) {
3517 if (info->out->chan_out) {
3518 _ckvmssts(sys$cancel(info->out->chan_out));
3519 if (!info->out->chan_in) { /* EOF generation, need AST */
3520 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
3524 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
3525 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3527 _ckvmssts(sys$setast(1));
3530 PerlIO_close(info->fp);
3532 fclose((FILE *)info->fp);
3535 we have to wait until subprocess completes, but ALSO wait until all
3536 the i/o completes...otherwise we'll be freeing the "info" structure
3537 that the i/o ASTs could still be using...
3541 _ckvmssts(sys$setast(0));
3542 done = info->done && info->in_done && info->out_done && info->err_done;
3543 if (!done) _ckvmssts(sys$clref(pipe_ef));
3544 _ckvmssts(sys$setast(1));
3545 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3547 retsts = info->completion;
3549 /* remove from list of open pipes */
3550 _ckvmssts(sys$setast(0));
3551 if (last) last->next = info->next;
3552 else open_pipes = info->next;
3553 _ckvmssts(sys$setast(1));
3555 /* free buffers and structures */
3558 if (info->in->buf) Safefree(info->in->buf);
3562 if (info->out->buf) Safefree(info->out->buf);
3563 Safefree(info->out);
3566 if (info->err->buf) Safefree(info->err->buf);
3567 Safefree(info->err);
3573 } /* end of my_pclose() */
3575 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
3576 /* Roll our own prototype because we want this regardless of whether
3577 * _VMS_WAIT is defined.
3579 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
3581 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
3582 created with popen(); otherwise partially emulate waitpid() unless
3583 we have a suitable one from the CRTL that came with VMS 7.2 and later.
3584 Also check processes not considered by the CRTL waitpid().
3586 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
3588 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
3595 if (statusp) *statusp = 0;
3597 for (info = open_pipes; info != NULL; info = info->next)
3598 if (info->pid == pid) break;
3600 if (info != NULL) { /* we know about this child */
3601 while (!info->done) {
3602 _ckvmssts(sys$setast(0));
3604 if (!done) _ckvmssts(sys$clref(pipe_ef));
3605 _ckvmssts(sys$setast(1));
3606 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3609 if (statusp) *statusp = info->completion;
3613 /* child that already terminated? */
3615 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
3616 if (closed_list[j].pid == pid) {
3617 if (statusp) *statusp = closed_list[j].completion;
3622 /* fall through if this child is not one of our own pipe children */
3624 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
3626 /* waitpid() became available in the CRTL as of VMS 7.0, but only
3627 * in 7.2 did we get a version that fills in the VMS completion
3628 * status as Perl has always tried to do.
3631 sts = __vms_waitpid( pid, statusp, flags );
3633 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
3636 /* If the real waitpid tells us the child does not exist, we
3637 * fall through here to implement waiting for a child that
3638 * was created by some means other than exec() (say, spawned
3639 * from DCL) or to wait for a process that is not a subprocess
3640 * of the current process.
3643 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
3646 $DESCRIPTOR(intdsc,"0 00:00:01");
3647 unsigned long int ownercode = JPI$_OWNER, ownerpid;
3648 unsigned long int pidcode = JPI$_PID, mypid;
3649 unsigned long int interval[2];
3650 unsigned int jpi_iosb[2];
3651 struct itmlst_3 jpilist[2] = {
3652 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
3657 /* Sorry folks, we don't presently implement rooting around for
3658 the first child we can find, and we definitely don't want to
3659 pass a pid of -1 to $getjpi, where it is a wildcard operation.
3665 /* Get the owner of the child so I can warn if it's not mine. If the
3666 * process doesn't exist or I don't have the privs to look at it,
3667 * I can go home early.
3669 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
3670 if (sts & 1) sts = jpi_iosb[0];
3682 set_vaxc_errno(sts);
3686 if (ckWARN(WARN_EXEC)) {
3687 /* remind folks they are asking for non-standard waitpid behavior */
3688 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
3689 if (ownerpid != mypid)
3690 Perl_warner(aTHX_ packWARN(WARN_EXEC),
3691 "waitpid: process %x is not a child of process %x",
3695 /* simply check on it once a second until it's not there anymore. */
3697 _ckvmssts(sys$bintim(&intdsc,interval));
3698 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
3699 _ckvmssts(sys$schdwk(0,0,interval,0));
3700 _ckvmssts(sys$hiber());
3702 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
3707 } /* end of waitpid() */
3712 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
3714 my_gconvert(double val, int ndig, int trail, char *buf)
3716 static char __gcvtbuf[DBL_DIG+1];
3719 loc = buf ? buf : __gcvtbuf;
3721 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
3723 sprintf(loc,"%.*g",ndig,val);
3729 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
3730 return gcvt(val,ndig,loc);
3733 loc[0] = '0'; loc[1] = '\0';
3741 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
3742 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
3743 * to expand file specification. Allows for a single default file
3744 * specification and a simple mask of options. If outbuf is non-NULL,
3745 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
3746 * the resultant file specification is placed. If outbuf is NULL, the
3747 * resultant file specification is placed into a static buffer.
3748 * The third argument, if non-NULL, is taken to be a default file
3749 * specification string. The fourth argument is unused at present.
3750 * rmesexpand() returns the address of the resultant string if
3751 * successful, and NULL on error.
3753 * New functionality for previously unused opts value:
3754 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
3756 static char *mp_do_tounixspec(pTHX_ const char *, char *, int);
3758 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
3759 /* ODS-2 only version */
3761 mp_do_rmsexpand(pTHX_ const char *filespec, char *outbuf, int ts, const char *defspec, unsigned opts)
3763 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
3764 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
3765 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
3766 struct FAB myfab = cc$rms_fab;
3767 struct NAM mynam = cc$rms_nam;
3769 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
3772 if (!filespec || !*filespec) {
3773 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
3777 if (ts) out = Newx(outbuf,NAM$C_MAXRSS+1,char);
3778 else outbuf = __rmsexpand_retbuf;
3780 isunix = is_unix_filespec(filespec);
3782 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) {
3787 filespec = vmsfspec;
3790 myfab.fab$l_fna = (char *)filespec; /* cast ok for read only pointer */
3791 myfab.fab$b_fns = strlen(filespec);
3792 myfab.fab$l_nam = &mynam;
3794 if (defspec && *defspec) {
3795 if (strchr(defspec,'/') != NULL) {
3796 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) {
3803 myfab.fab$l_dna = (char *)defspec; /* cast ok for read only pointer */
3804 myfab.fab$b_dns = strlen(defspec);
3807 mynam.nam$l_esa = esa;
3808 mynam.nam$b_ess = sizeof esa;
3809 mynam.nam$l_rsa = outbuf;
3810 mynam.nam$b_rss = NAM$C_MAXRSS;
3812 #ifdef NAM$M_NO_SHORT_UPCASE
3813 if (decc_efs_case_preserve)
3814 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
3817 retsts = sys$parse(&myfab,0,0);
3818 if (!(retsts & 1)) {
3819 mynam.nam$b_nop |= NAM$M_SYNCHK;
3820 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
3821 retsts = sys$parse(&myfab,0,0);
3822 if (retsts & 1) goto expanded;
3824 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
3825 sts = sys$parse(&myfab,0,0); /* Free search context */
3826 if (out) Safefree(out);
3827 set_vaxc_errno(retsts);
3828 if (retsts == RMS$_PRV) set_errno(EACCES);
3829 else if (retsts == RMS$_DEV) set_errno(ENODEV);
3830 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
3831 else set_errno(EVMSERR);
3834 retsts = sys$search(&myfab,0,0);
3835 if (!(retsts & 1) && retsts != RMS$_FNF) {
3836 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
3837 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); /* Free search context */
3838 if (out) Safefree(out);
3839 set_vaxc_errno(retsts);
3840 if (retsts == RMS$_PRV) set_errno(EACCES);
3841 else set_errno(EVMSERR);
3845 /* If the input filespec contained any lowercase characters,
3846 * downcase the result for compatibility with Unix-minded code. */
3848 if (!decc_efs_case_preserve) {
3849 for (out = myfab.fab$l_fna; *out; out++)
3850 if (islower(*out)) { haslower = 1; break; }
3852 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
3853 else { out = esa; speclen = mynam.nam$b_esl; }
3854 /* Trim off null fields added by $PARSE
3855 * If type > 1 char, must have been specified in original or default spec
3856 * (not true for version; $SEARCH may have added version of existing file).
3858 trimver = !(mynam.nam$l_fnb & NAM$M_EXP_VER);
3859 trimtype = !(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
3860 (mynam.nam$l_ver - mynam.nam$l_type == 1);
3861 if (trimver || trimtype) {
3862 if (defspec && *defspec) {
3863 char defesa[NAM$C_MAXRSS];
3864 struct FAB deffab = cc$rms_fab;
3865 struct NAM defnam = cc$rms_nam;
3867 deffab.fab$l_nam = &defnam;
3868 /* cast below ok for read only pointer */
3869 deffab.fab$l_fna = (char *)defspec; deffab.fab$b_fns = myfab.fab$b_dns;
3870 defnam.nam$l_esa = defesa; defnam.nam$b_ess = sizeof defesa;
3871 defnam.nam$b_nop = NAM$M_SYNCHK;
3872 #ifdef NAM$M_NO_SHORT_UPCASE
3873 if (decc_efs_case_preserve)
3874 defnam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
3876 if (sys$parse(&deffab,0,0) & 1) {
3877 if (trimver) trimver = !(defnam.nam$l_fnb & NAM$M_EXP_VER);
3878 if (trimtype) trimtype = !(defnam.nam$l_fnb & NAM$M_EXP_TYPE);
3882 if (*mynam.nam$l_ver != '\"')
3883 speclen = mynam.nam$l_ver - out;
3886 /* If we didn't already trim version, copy down */
3887 if (speclen > mynam.nam$l_ver - out)
3888 memmove(mynam.nam$l_type, mynam.nam$l_ver,
3889 speclen - (mynam.nam$l_ver - out));
3890 speclen -= mynam.nam$l_ver - mynam.nam$l_type;
3893 /* If we just had a directory spec on input, $PARSE "helpfully"
3894 * adds an empty name and type for us */
3895 if (mynam.nam$l_name == mynam.nam$l_type &&
3896 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
3897 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
3898 speclen = mynam.nam$l_name - out;
3900 /* Posix format specifications must have matching quotes */
3901 if (decc_posix_compliant_pathnames && (out[0] == '\"')) {
3902 if ((speclen > 1) && (out[speclen-1] != '\"')) {
3903 out[speclen] = '\"';
3908 out[speclen] = '\0';
3909 if (haslower && !decc_efs_case_preserve) __mystrtolower(out);
3911 /* Have we been working with an expanded, but not resultant, spec? */
3912 /* Also, convert back to Unix syntax if necessary. */
3913 if ((opts & PERL_RMSEXPAND_M_VMS) != 0)
3916 if (!mynam.nam$b_rsl) {
3918 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
3920 else strcpy(outbuf,esa);
3923 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
3924 strcpy(outbuf,tmpfspec);
3926 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
3927 mynam.nam$l_rsa = NULL;
3928 mynam.nam$b_rss = 0;
3929 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); /* Free search context */
3933 /* ODS-5 supporting routine */
3935 mp_do_rmsexpand(pTHX_ const char *filespec, char *outbuf, int ts, const char *defspec, unsigned opts)
3937 static char __rmsexpand_retbuf[NAML$C_MAXRSS+1];
3938 char * vmsfspec, *tmpfspec;
3939 char * esa, *cp, *out = NULL;
3942 struct FAB myfab = cc$rms_fab;
3943 struct NAML mynam = cc$rms_naml;
3945 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
3948 if (!filespec || !*filespec) {
3949 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
3953 if (ts) out = Newx(outbuf,VMS_MAXRSS,char);
3954 else outbuf = __rmsexpand_retbuf;
3960 isunix = is_unix_filespec(filespec);
3962 Newx(vmsfspec, VMS_MAXRSS, char);
3963 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) {
3969 filespec = vmsfspec;
3971 /* Unless we are forcing to VMS format, a UNIX input means
3972 * UNIX output, and that requires long names to be used
3974 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
3975 opts |= PERL_RMSEXPAND_M_LONG;
3981 myfab.fab$l_fna = (char *)-1; /* cast ok */
3982 myfab.fab$b_fns = 0;
3983 mynam.naml$l_long_filename = (char *)filespec; /* cast ok */
3984 mynam.naml$l_long_filename_size = strlen(filespec);
3985 myfab.fab$l_naml = &mynam;
3987 if (defspec && *defspec) {
3989 t_isunix = is_unix_filespec(defspec);
3991 Newx(tmpfspec, VMS_MAXRSS, char);
3992 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) {
3994 if (vmsfspec != NULL)
4002 myfab.fab$l_dna = (char *) -1; /* cast ok */
4003 myfab.fab$b_dns = 0;
4004 mynam.naml$l_long_defname = (char *)defspec; /* cast ok */
4005 mynam.naml$l_long_defname_size = strlen(defspec);
4008 Newx(esa, NAM$C_MAXRSS + 1, char);
4009 Newx(esal, NAML$C_MAXRSS + 1, char);
4010 mynam.naml$l_esa = esa;
4011 mynam.naml$b_ess = NAM$C_MAXRSS;
4012 mynam.naml$l_long_expand = esal;
4013 mynam.naml$l_long_expand_alloc = NAML$C_MAXRSS;
4015 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4016 mynam.naml$l_rsa = NULL;
4017 mynam.naml$b_rss = 0;
4018 mynam.naml$l_long_result = outbuf;
4019 mynam.naml$l_long_result_alloc = VMS_MAXRSS - 1;
4022 mynam.naml$l_rsa = outbuf;
4023 mynam.naml$b_rss = NAM$C_MAXRSS;
4024 Newx(outbufl, VMS_MAXRSS, char);
4025 mynam.naml$l_long_result = outbufl;
4026 mynam.naml$l_long_result_alloc = VMS_MAXRSS - 1;
4029 #ifdef NAM$M_NO_SHORT_UPCASE
4030 if (decc_efs_case_preserve)
4031 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
4034 /* First attempt to parse as an existing file */
4035 retsts = sys$parse(&myfab,0,0);
4036 if (!(retsts & STS$K_SUCCESS)) {
4038 /* Could not find the file, try as syntax only if error is not fatal */
4039 mynam.naml$b_nop |= NAM$M_SYNCHK;
4040 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
4041 retsts = sys$parse(&myfab,0,0);
4042 if (retsts & STS$K_SUCCESS) goto expanded;
4045 /* Still could not parse the file specification */
4046 /*----------------------------------------------*/
4047 mynam.naml$l_rlf = NULL;
4048 myfab.fab$b_dns = 0;
4049 mynam.naml$l_long_defname_size = 0;
4050 sts = sys$parse(&myfab,0,0); /* Free search context */
4051 if (out) Safefree(out);
4052 if (tmpfspec != NULL)
4054 if (vmsfspec != NULL)
4058 set_vaxc_errno(retsts);
4059 if (retsts == RMS$_PRV) set_errno(EACCES);
4060 else if (retsts == RMS$_DEV) set_errno(ENODEV);
4061 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
4062 else set_errno(EVMSERR);
4065 retsts = sys$search(&myfab,0,0);
4066 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
4067 mynam.naml$b_nop |= NAM$M_SYNCHK;
4068 mynam.naml$l_rlf = NULL;
4069 myfab.fab$b_dns = 0;
4070 mynam.naml$l_long_defname_size = 0;
4071 sts = sys$parse(&myfab,0,0); /* Free search context */
4072 if (out) Safefree(out);
4073 if (tmpfspec != NULL)
4075 if (vmsfspec != NULL)
4079 set_vaxc_errno(retsts);
4080 if (retsts == RMS$_PRV) set_errno(EACCES);
4081 else set_errno(EVMSERR);
4085 /* If the input filespec contained any lowercase characters,
4086 * downcase the result for compatibility with Unix-minded code. */
4088 if (!decc_efs_case_preserve) {
4089 for (out = mynam.naml$l_long_filename; *out; out++)
4090 if (islower(*out)) { haslower = 1; break; }
4093 /* Is a long or a short name expected */
4094 /*------------------------------------*/
4095 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4096 if (mynam.naml$l_long_result_size) {
4098 speclen = mynam.naml$l_long_result_size;
4101 out = esal; /* Not esa */
4102 speclen = mynam.naml$l_long_expand_size;
4106 if (mynam.naml$b_rsl) {
4108 speclen = mynam.naml$b_rsl;
4111 out = esa; /* Not esal */
4112 speclen = mynam.naml$b_esl;
4115 /* Trim off null fields added by $PARSE
4116 * If type > 1 char, must have been specified in original or default spec
4117 * (not true for version; $SEARCH may have added version of existing file).
4119 trimver = !(mynam.naml$l_fnb & NAM$M_EXP_VER);
4120 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4121 trimtype = !(mynam.naml$l_fnb & NAM$M_EXP_TYPE) &&
4122 (mynam.naml$l_long_ver - mynam.naml$l_long_type == 1);
4125 trimtype = !(mynam.naml$l_fnb & NAM$M_EXP_TYPE) &&
4126 (mynam.naml$l_ver - mynam.naml$l_type == 1);
4128 if (trimver || trimtype) {
4129 if (defspec && *defspec) {
4130 char *defesal = NULL;
4131 Newx(defesal, NAML$C_MAXRSS + 1, char);
4132 if (defesal != NULL) {
4133 struct FAB deffab = cc$rms_fab;
4134 struct NAML defnam = cc$rms_naml;
4136 deffab.fab$l_naml = &defnam;
4138 deffab.fab$l_fna = (char *) - 1; /* Cast ok */
4139 deffab.fab$b_fns = 0;
4140 defnam.naml$l_long_filename = (char *)defspec; /* Cast ok */
4141 defnam.naml$l_long_filename_size = mynam.naml$l_long_defname_size;
4142 defnam.naml$l_esa = NULL;
4143 defnam.naml$b_ess = 0;
4144 defnam.naml$l_long_expand = defesal;
4145 defnam.naml$l_long_expand_alloc = VMS_MAXRSS - 1;
4146 defnam.naml$b_nop = NAM$M_SYNCHK;
4147 #ifdef NAM$M_NO_SHORT_UPCASE
4148 if (decc_efs_case_preserve)
4149 defnam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
4151 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
4153 trimver = !(defnam.naml$l_fnb & NAM$M_EXP_VER);
4156 trimtype = !(defnam.naml$l_fnb & NAM$M_EXP_TYPE);
4163 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4164 if (*mynam.naml$l_long_ver != '\"')
4165 speclen = mynam.naml$l_long_ver - out;
4168 if (*mynam.naml$l_ver != '\"')
4169 speclen = mynam.naml$l_ver - out;
4173 /* If we didn't already trim version, copy down */
4174 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4175 if (speclen > mynam.naml$l_long_ver - out)
4177 (mynam.naml$l_long_type,
4178 mynam.naml$l_long_ver,
4179 speclen - (mynam.naml$l_long_ver - out));
4180 speclen -= mynam.naml$l_long_ver - mynam.naml$l_long_type;
4183 if (speclen > mynam.naml$l_ver - out)
4187 speclen - (mynam.naml$l_ver - out));
4188 speclen -= mynam.naml$l_ver - mynam.naml$l_type;
4193 /* Done with these copies of the input files */
4194 /*-------------------------------------------*/
4195 if (vmsfspec != NULL)
4197 if (tmpfspec != NULL)
4200 /* If we just had a directory spec on input, $PARSE "helpfully"
4201 * adds an empty name and type for us */
4202 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4203 if (mynam.naml$l_long_name == mynam.naml$l_long_type &&
4204 mynam.naml$l_long_ver == mynam.naml$l_long_type + 1 &&
4205 !(mynam.naml$l_fnb & NAM$M_EXP_NAME))
4206 speclen = mynam.naml$l_long_name - out;
4209 if (mynam.naml$l_name == mynam.naml$l_type &&
4210 mynam.naml$l_ver == mynam.naml$l_type + 1 &&
4211 !(mynam.naml$l_fnb & NAM$M_EXP_NAME))
4212 speclen = mynam.naml$l_name - out;
4215 /* Posix format specifications must have matching quotes */
4216 if (decc_posix_compliant_pathnames && (out[0] == '\"')) {
4217 if ((speclen > 1) && (out[speclen-1] != '\"')) {
4218 out[speclen] = '\"';
4222 out[speclen] = '\0';
4223 if (haslower && !decc_efs_case_preserve) __mystrtolower(out);
4225 /* Have we been working with an expanded, but not resultant, spec? */
4226 /* Also, convert back to Unix syntax if necessary. */
4228 if (!mynam.naml$l_long_result_size) {
4230 if (do_tounixspec(esa,outbuf,0) == NULL) {
4236 else strcpy(outbuf,esa);
4239 Newx(tmpfspec, VMS_MAXRSS, char);
4240 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) {
4246 strcpy(outbuf,tmpfspec);
4250 mynam.naml$b_nop |= NAM$M_SYNCHK;
4251 mynam.naml$l_rlf = NULL;
4252 mynam.naml$l_rsa = NULL;
4253 mynam.naml$b_rss = 0;
4254 mynam.naml$l_long_result = NULL;
4255 mynam.naml$l_long_result_size = 0;
4256 myfab.fab$b_dns = 0;
4257 mynam.naml$l_long_defname_size = 0;
4258 sts = sys$parse(&myfab,0,0); /* Free search context */
4265 /* External entry points */
4266 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
4267 { return do_rmsexpand(spec,buf,0,def,opt); }
4268 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
4269 { return do_rmsexpand(spec,buf,1,def,opt); }
4273 ** The following routines are provided to make life easier when
4274 ** converting among VMS-style and Unix-style directory specifications.
4275 ** All will take input specifications in either VMS or Unix syntax. On
4276 ** failure, all return NULL. If successful, the routines listed below
4277 ** return a pointer to a buffer containing the appropriately
4278 ** reformatted spec (and, therefore, subsequent calls to that routine
4279 ** will clobber the result), while the routines of the same names with
4280 ** a _ts suffix appended will return a pointer to a mallocd string
4281 ** containing the appropriately reformatted spec.
4282 ** In all cases, only explicit syntax is altered; no check is made that
4283 ** the resulting string is valid or that the directory in question
4286 ** fileify_dirspec() - convert a directory spec into the name of the
4287 ** directory file (i.e. what you can stat() to see if it's a dir).
4288 ** The style (VMS or Unix) of the result is the same as the style
4289 ** of the parameter passed in.
4290 ** pathify_dirspec() - convert a directory spec into a path (i.e.
4291 ** what you prepend to a filename to indicate what directory it's in).
4292 ** The style (VMS or Unix) of the result is the same as the style
4293 ** of the parameter passed in.
4294 ** tounixpath() - convert a directory spec into a Unix-style path.
4295 ** tovmspath() - convert a directory spec into a VMS-style path.
4296 ** tounixspec() - convert any file spec into a Unix-style file spec.
4297 ** tovmsspec() - convert any file spec into a VMS-style spec.
4299 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
4300 ** Permission is given to distribute this code as part of the Perl
4301 ** standard distribution under the terms of the GNU General Public
4302 ** License or the Perl Artistic License. Copies of each may be
4303 ** found in the Perl standard distribution.
4306 /*{{{ char *fileify_dirspec[_ts](char *path, char *buf)*/
4307 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts)
4309 static char __fileify_retbuf[NAM$C_MAXRSS+1];
4310 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
4311 char *retspec, *cp1, *cp2, *lastdir;
4312 char trndir[NAM$C_MAXRSS+2], vmsdir[NAM$C_MAXRSS+1];
4313 unsigned short int trnlnm_iter_count;
4316 if (!dir || !*dir) {
4317 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
4319 dirlen = strlen(dir);
4320 while (dirlen && dir[dirlen-1] == '/') --dirlen;
4321 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
4322 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
4329 if (dirlen > NAM$C_MAXRSS) {
4330 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN); return NULL;
4332 if (!strpbrk(dir+1,"/]>:") &&
4333 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
4334 strcpy(trndir,*dir == '/' ? dir + 1: dir);
4335 trnlnm_iter_count = 0;
4336 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) {
4337 trnlnm_iter_count++;
4338 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
4340 dirlen = strlen(trndir);
4343 strncpy(trndir,dir,dirlen);
4344 trndir[dirlen] = '\0';
4347 /* At this point we are done with *dir and use *trndir which is a
4348 * copy that can be modified. *dir must not be modified.
4351 /* If we were handed a rooted logical name or spec, treat it like a
4352 * simple directory, so that
4353 * $ Define myroot dev:[dir.]
4354 * ... do_fileify_dirspec("myroot",buf,1) ...
4355 * does something useful.
4357 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
4358 trndir[--dirlen] = '\0';
4359 trndir[dirlen-1] = ']';
4361 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
4362 trndir[--dirlen] = '\0';
4363 trndir[dirlen-1] = '>';
4366 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
4367 /* If we've got an explicit filename, we can just shuffle the string. */
4368 if (*(cp1+1)) hasfilename = 1;
4369 /* Similarly, we can just back up a level if we've got multiple levels
4370 of explicit directories in a VMS spec which ends with directories. */
4372 for (cp2 = cp1; cp2 > trndir; cp2--) {
4374 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
4375 *cp2 = *cp1; *cp1 = '\0';
4380 if (*cp2 == '[' || *cp2 == '<') break;
4385 cp1 = strpbrk(trndir,"]:>"); /* Prepare for future change */
4386 if (hasfilename || !cp1) { /* Unix-style path or filename */
4387 if (trndir[0] == '.') {
4388 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0'))
4389 return do_fileify_dirspec("[]",buf,ts);
4390 else if (trndir[1] == '.' &&
4391 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0')))
4392 return do_fileify_dirspec("[-]",buf,ts);
4394 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
4395 dirlen -= 1; /* to last element */
4396 lastdir = strrchr(trndir,'/');
4398 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
4399 /* If we have "/." or "/..", VMSify it and let the VMS code
4400 * below expand it, rather than repeating the code to handle
4401 * relative components of a filespec here */
4403 if (*(cp1+2) == '.') cp1++;
4404 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
4405 if (do_tovmsspec(trndir,vmsdir,0) == NULL) return NULL;
4406 if (strchr(vmsdir,'/') != NULL) {
4407 /* If do_tovmsspec() returned it, it must have VMS syntax
4408 * delimiters in it, so it's a mixed VMS/Unix spec. We take
4409 * the time to check this here only so we avoid a recursion
4410 * loop; otherwise, gigo.
4412 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); return NULL;
4414 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
4415 return do_tounixspec(trndir,buf,ts);
4418 } while ((cp1 = strstr(cp1,"/.")) != NULL);
4419 lastdir = strrchr(trndir,'/');
4421 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
4422 /* Ditto for specs that end in an MFD -- let the VMS code
4423 * figure out whether it's a real device or a rooted logical. */
4425 /* This should not happen any more. Allowing the fake /000000
4426 * in a UNIX pathname causes all sorts of problems when trying
4427 * to run in UNIX emulation. So the VMS to UNIX conversions
4428 * now remove the fake /000000 directories.
4431 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
4432 if (do_tovmsspec(trndir,vmsdir,0) == NULL) return NULL;
4433 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) return NULL;
4434 return do_tounixspec(trndir,buf,ts);
4438 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
4439 !(lastdir = cp1 = strrchr(trndir,']')) &&
4440 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
4441 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
4444 /* For EFS or ODS-5 look for the last dot */
4445 if (decc_efs_charset) {
4446 cp2 = strrchr(cp1,'.');
4448 if (vms_process_case_tolerant) {
4449 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
4450 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
4451 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
4452 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4453 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4454 (ver || *cp3)))))) {
4456 set_vaxc_errno(RMS$_DIR);
4461 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
4462 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
4463 !*(cp2+3) || *(cp2+3) != 'R' ||
4464 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4465 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4466 (ver || *cp3)))))) {
4468 set_vaxc_errno(RMS$_DIR);
4472 dirlen = cp2 - trndir;
4476 retlen = dirlen + 6;
4477 if (buf) retspec = buf;
4478 else if (ts) Newx(retspec,retlen+1,char);
4479 else retspec = __fileify_retbuf;
4480 memcpy(retspec,trndir,dirlen);
4481 retspec[dirlen] = '\0';
4483 /* We've picked up everything up to the directory file name.
4484 Now just add the type and version, and we're set. */
4485 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
4486 strcat(retspec,".dir;1");
4488 strcat(retspec,".DIR;1");
4491 else { /* VMS-style directory spec */
4492 char esa[NAM$C_MAXRSS+1], term, *cp;
4493 unsigned long int sts, cmplen, haslower = 0;
4494 struct FAB dirfab = cc$rms_fab;
4495 struct NAM savnam, dirnam = cc$rms_nam;
4497 dirfab.fab$b_fns = strlen(trndir);
4498 dirfab.fab$l_fna = trndir;
4499 dirfab.fab$l_nam = &dirnam;
4500 dirfab.fab$l_dna = ".DIR;1";
4501 dirfab.fab$b_dns = 6;
4502 dirnam.nam$b_ess = NAM$C_MAXRSS;
4503 dirnam.nam$l_esa = esa;
4504 #ifdef NAM$M_NO_SHORT_UPCASE
4505 if (decc_efs_case_preserve)
4506 dirnam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
4509 for (cp = trndir; *cp; cp++)
4510 if (islower(*cp)) { haslower = 1; break; }
4511 if (!((sts = sys$parse(&dirfab))&1)) {
4512 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
4513 dirnam.nam$b_nop |= NAM$M_SYNCHK;
4514 sts = sys$parse(&dirfab) & 1;
4518 set_vaxc_errno(dirfab.fab$l_sts);
4524 if (sys$search(&dirfab)&1) { /* Does the file really exist? */
4525 /* Yes; fake the fnb bits so we'll check type below */
4526 dirnam.nam$l_fnb |= NAM$M_EXP_TYPE | NAM$M_EXP_VER;
4528 else { /* No; just work with potential name */
4529 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
4531 set_errno(EVMSERR); set_vaxc_errno(dirfab.fab$l_sts);
4532 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4533 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4538 if (!(dirnam.nam$l_fnb & (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
4539 cp1 = strchr(esa,']');
4540 if (!cp1) cp1 = strchr(esa,'>');
4541 if (cp1) { /* Should always be true */
4542 dirnam.nam$b_esl -= cp1 - esa - 1;
4543 memmove(esa,cp1 + 1,dirnam.nam$b_esl);
4546 if (dirnam.nam$l_fnb & NAM$M_EXP_TYPE) { /* Was type specified? */
4547 /* Yep; check version while we're at it, if it's there. */
4548 cmplen = (dirnam.nam$l_fnb & NAM$M_EXP_VER) ? 6 : 4;
4549 if (strncmp(dirnam.nam$l_type,".DIR;1",cmplen)) {
4550 /* Something other than .DIR[;1]. Bzzt. */
4551 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4552 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4554 set_vaxc_errno(RMS$_DIR);
4558 esa[dirnam.nam$b_esl] = '\0';
4559 if (dirnam.nam$l_fnb & NAM$M_EXP_NAME) {
4560 /* They provided at least the name; we added the type, if necessary, */
4561 if (buf) retspec = buf; /* in sys$parse() */
4562 else if (ts) Newx(retspec,dirnam.nam$b_esl+1,char);
4563 else retspec = __fileify_retbuf;
4564 strcpy(retspec,esa);
4565 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4566 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4569 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
4570 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
4572 dirnam.nam$b_esl -= 9;
4574 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
4575 if (cp1 == NULL) { /* should never happen */
4576 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4577 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4582 retlen = strlen(esa);
4583 cp1 = strrchr(esa,'.');
4584 /* ODS-5 directory specifications can have extra "." in them. */
4585 while (cp1 != NULL) {
4586 if ((cp1-1 == esa) || (*(cp1-1) != '^'))
4590 while ((cp1 > esa) && (*cp1 != '.'))
4597 if ((cp1) != NULL) {
4598 /* There's more than one directory in the path. Just roll back. */
4600 if (buf) retspec = buf;
4601 else if (ts) Newx(retspec,retlen+7,char);
4602 else retspec = __fileify_retbuf;
4603 strcpy(retspec,esa);
4606 if (dirnam.nam$l_fnb & NAM$M_ROOT_DIR) {
4607 /* Go back and expand rooted logical name */
4608 dirnam.nam$b_nop = NAM$M_SYNCHK | NAM$M_NOCONCEAL;
4609 #ifdef NAM$M_NO_SHORT_UPCASE
4610 if (decc_efs_case_preserve)
4611 dirnam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
4613 if (!(sys$parse(&dirfab) & 1)) {
4614 dirnam.nam$l_rlf = NULL;
4615 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4617 set_vaxc_errno(dirfab.fab$l_sts);
4620 retlen = dirnam.nam$b_esl - 9; /* esa - '][' - '].DIR;1' */
4621 if (buf) retspec = buf;
4622 else if (ts) Newx(retspec,retlen+16,char);
4623 else retspec = __fileify_retbuf;
4624 cp1 = strstr(esa,"][");
4625 if (!cp1) cp1 = strstr(esa,"]<");
4627 memcpy(retspec,esa,dirlen);
4628 if (!strncmp(cp1+2,"000000]",7)) {
4629 retspec[dirlen-1] = '\0';
4630 /* Not full ODS-5, just extra dots in directories for now */
4631 cp1 = retspec + dirlen - 1;
4632 while (cp1 > retspec)
4637 if (*(cp1-1) != '^')
4642 if (*cp1 == '.') *cp1 = ']';
4644 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
4645 memmove(cp1+1,"000000]",7);
4649 memmove(retspec+dirlen,cp1+2,retlen-dirlen);
4650 retspec[retlen] = '\0';
4651 /* Convert last '.' to ']' */
4652 cp1 = retspec+retlen-1;
4653 while (*cp != '[') {
4656 /* Do not trip on extra dots in ODS-5 directories */
4657 if ((cp1 == retspec) || (*(cp1-1) != '^'))
4661 if (*cp1 == '.') *cp1 = ']';
4663 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
4664 memmove(cp1+1,"000000]",7);
4668 else { /* This is a top-level dir. Add the MFD to the path. */
4669 if (buf) retspec = buf;
4670 else if (ts) Newx(retspec,retlen+16,char);
4671 else retspec = __fileify_retbuf;
4674 while (*cp1 != ':') *(cp2++) = *(cp1++);
4675 strcpy(cp2,":[000000]");
4680 dirnam.nam$b_nop |= NAM$M_SYNCHK; dirnam.nam$l_rlf = NULL;
4681 dirfab.fab$b_dns = 0; sts = sys$parse(&dirfab,0,0);
4682 /* We've set up the string up through the filename. Add the
4683 type and version, and we're done. */
4684 strcat(retspec,".DIR;1");
4686 /* $PARSE may have upcased filespec, so convert output to lower
4687 * case if input contained any lowercase characters. */
4688 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec);
4691 } /* end of do_fileify_dirspec() */
4693 /* External entry points */
4694 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
4695 { return do_fileify_dirspec(dir,buf,0); }
4696 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
4697 { return do_fileify_dirspec(dir,buf,1); }
4699 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
4700 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts)
4702 static char __pathify_retbuf[NAM$C_MAXRSS+1];
4703 unsigned long int retlen;
4704 char *retpath, *cp1, *cp2, trndir[NAM$C_MAXRSS+1];
4705 unsigned short int trnlnm_iter_count;
4709 if (!dir || !*dir) {
4710 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
4713 if (*dir) strcpy(trndir,dir);
4714 else getcwd(trndir,sizeof trndir - 1);
4716 trnlnm_iter_count = 0;
4717 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
4718 && my_trnlnm(trndir,trndir,0)) {
4719 trnlnm_iter_count++;
4720 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
4721 trnlen = strlen(trndir);
4723 /* Trap simple rooted lnms, and return lnm:[000000] */
4724 if (!strcmp(trndir+trnlen-2,".]")) {
4725 if (buf) retpath = buf;
4726 else if (ts) Newx(retpath,strlen(dir)+10,char);
4727 else retpath = __pathify_retbuf;
4728 strcpy(retpath,dir);
4729 strcat(retpath,":[000000]");
4734 /* At this point we do not work with *dir, but the copy in
4735 * *trndir that is modifiable.
4738 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
4739 if (*trndir == '.' && (*(trndir+1) == '\0' ||
4740 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
4741 retlen = 2 + (*(trndir+1) != '\0');
4743 if ( !(cp1 = strrchr(trndir,'/')) &&
4744 !(cp1 = strrchr(trndir,']')) &&
4745 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
4746 if ((cp2 = strchr(cp1,'.')) != NULL &&
4747 (*(cp2-1) != '/' || /* Trailing '.', '..', */
4748 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
4749 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
4750 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
4753 /* For EFS or ODS-5 look for the last dot */
4754 if (decc_efs_charset) {
4755 cp2 = strrchr(cp1,'.');
4757 if (vms_process_case_tolerant) {
4758 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
4759 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
4760 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
4761 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4762 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4763 (ver || *cp3)))))) {
4765 set_vaxc_errno(RMS$_DIR);
4770 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
4771 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
4772 !*(cp2+3) || *(cp2+3) != 'R' ||
4773 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4774 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4775 (ver || *cp3)))))) {
4777 set_vaxc_errno(RMS$_DIR);
4781 retlen = cp2 - trndir + 1;
4783 else { /* No file type present. Treat the filename as a directory. */
4784 retlen = strlen(trndir) + 1;
4787 if (buf) retpath = buf;
4788 else if (ts) Newx(retpath,retlen+1,char);
4789 else retpath = __pathify_retbuf;
4790 strncpy(retpath, trndir, retlen-1);
4791 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
4792 retpath[retlen-1] = '/'; /* with '/', add it. */