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 struct filescan_itmlst_2 {
172 unsigned short length;
173 unsigned short itmcode;
178 unsigned short length;
183 #pragma message restore
184 #pragma member_alignment restore
187 #define do_fileify_dirspec(a,b,c) mp_do_fileify_dirspec(aTHX_ a,b,c)
188 #define do_pathify_dirspec(a,b,c) mp_do_pathify_dirspec(aTHX_ a,b,c)
189 #define do_tovmsspec(a,b,c) mp_do_tovmsspec(aTHX_ a,b,c)
190 #define do_tovmspath(a,b,c) mp_do_tovmspath(aTHX_ a,b,c)
191 #define do_rmsexpand(a,b,c,d,e) mp_do_rmsexpand(aTHX_ a,b,c,d,e)
192 #define do_vms_realpath(a,b) mp_do_vms_realpath(aTHX_ a,b)
193 #define do_tounixspec(a,b,c) mp_do_tounixspec(aTHX_ a,b,c)
194 #define do_tounixpath(a,b,c) mp_do_tounixpath(aTHX_ a,b,c)
195 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
196 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
197 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
199 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts);
200 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts);
201 static char *mp_do_tounixspec(pTHX_ const char *, char *, int);
202 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts);
204 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
205 #define PERL_LNM_MAX_ALLOWED_INDEX 127
207 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
208 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
211 #define PERL_LNM_MAX_ITER 10
213 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
214 #if __CRTL_VER >= 70302000 && !defined(__VAX)
215 #define MAX_DCL_SYMBOL (8192)
216 #define MAX_DCL_LINE_LENGTH (4096 - 4)
218 #define MAX_DCL_SYMBOL (1024)
219 #define MAX_DCL_LINE_LENGTH (1024 - 4)
222 static char *__mystrtolower(char *str)
224 if (str) for (; *str; ++str) *str= tolower(*str);
228 static struct dsc$descriptor_s fildevdsc =
229 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
230 static struct dsc$descriptor_s crtlenvdsc =
231 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
232 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
233 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
234 static struct dsc$descriptor_s **env_tables = defenv;
235 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
237 /* True if we shouldn't treat barewords as logicals during directory */
239 static int no_translate_barewords;
242 static int tz_updated = 1;
245 /* DECC Features that may need to affect how Perl interprets
246 * displays filename information
248 static int decc_disable_to_vms_logname_translation = 1;
249 static int decc_disable_posix_root = 1;
250 int decc_efs_case_preserve = 0;
251 static int decc_efs_charset = 0;
252 static int decc_filename_unix_no_version = 0;
253 static int decc_filename_unix_only = 0;
254 int decc_filename_unix_report = 0;
255 int decc_posix_compliant_pathnames = 0;
256 int decc_readdir_dropdotnotype = 0;
257 static int vms_process_case_tolerant = 1;
259 /* bug workarounds if needed */
260 int decc_bug_readdir_efs1 = 0;
261 int decc_bug_devnull = 1;
262 int decc_bug_fgetname = 0;
263 int decc_dir_barename = 0;
265 static int vms_debug_on_exception = 0;
267 /* Is this a UNIX file specification?
268 * No longer a simple check with EFS file specs
269 * For now, not a full check, but need to
270 * handle POSIX ^UP^ specifications
271 * Fixing to handle ^/ cases would require
272 * changes to many other conversion routines.
275 static int is_unix_filespec(const char *path)
281 if (strncmp(path,"\"^UP^",5) != 0) {
282 pch1 = strchr(path, '/');
287 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
288 if (decc_filename_unix_report || decc_filename_unix_only) {
289 if (strcmp(path,".") == 0)
297 /* This handles the expansion of a '^' prefix to the proper character
298 * in a UNIX file specification.
300 * The output count variable contains the number of characters added
301 * to the output string.
303 * The return value is the number of characters read from the input
306 static int copy_expand_vms_filename_escape
307 (char *outspec, const char *inspec, int *output_cnt)
314 if (*inspec == '^') {
318 /* Non trailing dots should just be passed through */
323 case '_': /* space */
329 case 'U': /* Unicode */
332 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
335 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
336 outspec[0] == c1 & 0xff;
337 outspec[1] == c2 & 0xff;
344 /* Error - do best we can to continue */
354 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
358 scnt = sscanf(inspec, "%2x", &c1);
359 outspec[0] = c1 & 0xff;
382 (const struct dsc$descriptor_s * srcstr,
383 struct filescan_itmlst_2 * valuelist,
384 unsigned long * fldflags,
385 struct dsc$descriptor_s *auxout,
386 unsigned short * retlen);
388 /* vms_split_path - Verify that the input file specification is a
389 * VMS format file specification, and provide pointers to the components of
390 * it. With EFS format filenames, this is virtually the only way to
391 * parse a VMS path specification into components.
393 * If the sum of the components do not add up to the length of the
394 * string, then the passed file specification is probably a UNIX style
397 static int vms_split_path
412 struct dsc$descriptor path_desc;
416 struct filescan_itmlst_2 item_list[9];
417 const int filespec = 0;
418 const int nodespec = 1;
419 const int devspec = 2;
420 const int rootspec = 3;
421 const int dirspec = 4;
422 const int namespec = 5;
423 const int typespec = 6;
424 const int verspec = 7;
426 /* Assume the worst for an easy exit */
441 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
442 path_desc.dsc$w_length = strlen(path);
443 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
444 path_desc.dsc$b_class = DSC$K_CLASS_S;
446 /* Get the total length, if it is shorter than the string passed
447 * then this was probably not a VMS formatted file specification
449 item_list[filespec].itmcode = FSCN$_FILESPEC;
450 item_list[filespec].length = 0;
451 item_list[filespec].component = NULL;
453 /* If the node is present, then it gets considered as part of the
454 * volume name to hopefully make things simple.
456 item_list[nodespec].itmcode = FSCN$_NODE;
457 item_list[nodespec].length = 0;
458 item_list[nodespec].component = NULL;
460 item_list[devspec].itmcode = FSCN$_DEVICE;
461 item_list[devspec].length = 0;
462 item_list[devspec].component = NULL;
464 /* root is a special case, adding it to either the directory or
465 * the device components will probalby complicate things for the
466 * callers of this routine, so leave it separate.
468 item_list[rootspec].itmcode = FSCN$_ROOT;
469 item_list[rootspec].length = 0;
470 item_list[rootspec].component = NULL;
472 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
473 item_list[dirspec].length = 0;
474 item_list[dirspec].component = NULL;
476 item_list[namespec].itmcode = FSCN$_NAME;
477 item_list[namespec].length = 0;
478 item_list[namespec].component = NULL;
480 item_list[typespec].itmcode = FSCN$_TYPE;
481 item_list[typespec].length = 0;
482 item_list[typespec].component = NULL;
484 item_list[verspec].itmcode = FSCN$_VERSION;
485 item_list[verspec].length = 0;
486 item_list[verspec].component = NULL;
488 item_list[8].itmcode = 0;
489 item_list[8].length = 0;
490 item_list[8].component = NULL;
492 status = SYS$FILESCAN
493 ((const struct dsc$descriptor_s *)&path_desc, item_list,
495 _ckvmssts(status); /* All failure status values indicate a coding error */
497 /* If we parsed it successfully these two lengths should be the same */
498 if (path_desc.dsc$w_length != item_list[filespec].length)
501 /* If we got here, then it is a VMS file specification */
504 /* set the volume name */
505 if (item_list[nodespec].length > 0) {
506 *volume = item_list[nodespec].component;
507 *vol_len = item_list[nodespec].length + item_list[devspec].length;
510 *volume = item_list[devspec].component;
511 *vol_len = item_list[devspec].length;
514 *root = item_list[rootspec].component;
515 *root_len = item_list[rootspec].length;
517 *dir = item_list[dirspec].component;
518 *dir_len = item_list[dirspec].length;
520 /* Now fun with versions and EFS file specifications
521 * The parser can not tell the difference when a "." is a version
522 * delimiter or a part of the file specification.
524 if ((decc_efs_charset) &&
525 (item_list[verspec].length > 0) &&
526 (item_list[verspec].component[0] == '.')) {
527 *name = item_list[namespec].component;
528 *name_len = item_list[namespec].length + item_list[typespec].length;
529 *ext = item_list[verspec].component;
530 *ext_len = item_list[verspec].length;
535 *name = item_list[namespec].component;
536 *name_len = item_list[namespec].length;
537 *ext = item_list[typespec].component;
538 *ext_len = item_list[typespec].length;
539 *version = item_list[verspec].component;
540 *ver_len = item_list[verspec].length;
547 * Routine to retrieve the maximum equivalence index for an input
548 * logical name. Some calls to this routine have no knowledge if
549 * the variable is a logical or not. So on error we return a max
552 /*{{{int my_maxidx(const char *lnm) */
554 my_maxidx(const char *lnm)
558 int attr = LNM$M_CASE_BLIND;
559 struct dsc$descriptor lnmdsc;
560 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
563 lnmdsc.dsc$w_length = strlen(lnm);
564 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
565 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
566 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
568 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
569 if ((status & 1) == 0)
576 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
578 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
579 struct dsc$descriptor_s **tabvec, unsigned long int flags)
582 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
583 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
584 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
586 unsigned char acmode;
587 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
588 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
589 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
590 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
592 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
593 #if defined(PERL_IMPLICIT_CONTEXT)
596 aTHX = PERL_GET_INTERP;
602 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
603 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
605 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
606 *cp2 = _toupper(*cp1);
607 if (cp1 - lnm > LNM$C_NAMLENGTH) {
608 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
612 lnmdsc.dsc$w_length = cp1 - lnm;
613 lnmdsc.dsc$a_pointer = uplnm;
614 uplnm[lnmdsc.dsc$w_length] = '\0';
615 secure = flags & PERL__TRNENV_SECURE;
616 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
617 if (!tabvec || !*tabvec) tabvec = env_tables;
619 for (curtab = 0; tabvec[curtab]; curtab++) {
620 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
621 if (!ivenv && !secure) {
626 Perl_warn(aTHX_ "Can't read CRTL environ\n");
629 retsts = SS$_NOLOGNAM;
630 for (i = 0; environ[i]; i++) {
631 if ((eq = strchr(environ[i],'=')) &&
632 lnmdsc.dsc$w_length == (eq - environ[i]) &&
633 !strncmp(environ[i],uplnm,eq - environ[i])) {
635 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
636 if (!eqvlen) continue;
641 if (retsts != SS$_NOLOGNAM) break;
644 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
645 !str$case_blind_compare(&tmpdsc,&clisym)) {
646 if (!ivsym && !secure) {
647 unsigned short int deflen = LNM$C_NAMLENGTH;
648 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
649 /* dynamic dsc to accomodate possible long value */
650 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
651 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
653 if (eqvlen > MAX_DCL_SYMBOL) {
654 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
655 eqvlen = MAX_DCL_SYMBOL;
656 /* Special hack--we might be called before the interpreter's */
657 /* fully initialized, in which case either thr or PL_curcop */
658 /* might be bogus. We have to check, since ckWARN needs them */
659 /* both to be valid if running threaded */
660 if (ckWARN(WARN_MISC)) {
661 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
664 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
666 _ckvmssts(lib$sfree1_dd(&eqvdsc));
667 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
668 if (retsts == LIB$_NOSUCHSYM) continue;
673 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
674 midx = my_maxidx(lnm);
675 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
676 lnmlst[1].bufadr = cp2;
678 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
679 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
680 if (retsts == SS$_NOLOGNAM) break;
681 /* PPFs have a prefix */
684 *((int *)uplnm) == *((int *)"SYS$") &&
686 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
687 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
688 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
689 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
690 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
691 memmove(eqv,eqv+4,eqvlen-4);
697 if ((retsts == SS$_IVLOGNAM) ||
698 (retsts == SS$_NOLOGNAM)) { continue; }
701 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
702 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
703 if (retsts == SS$_NOLOGNAM) continue;
706 eqvlen = strlen(eqv);
710 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
711 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
712 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
713 retsts == SS$_NOLOGNAM) {
714 set_errno(EINVAL); set_vaxc_errno(retsts);
716 else _ckvmssts(retsts);
718 } /* end of vmstrnenv */
721 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
722 /* Define as a function so we can access statics. */
723 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
725 return vmstrnenv(lnm,eqv,idx,fildev,
726 #ifdef SECURE_INTERNAL_GETENV
727 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
736 * Note: Uses Perl temp to store result so char * can be returned to
737 * caller; this pointer will be invalidated at next Perl statement
739 * We define this as a function rather than a macro in terms of my_getenv_len()
740 * so that it'll work when PL_curinterp is undefined (and we therefore can't
743 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
745 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
748 static char *__my_getenv_eqv = NULL;
749 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
750 unsigned long int idx = 0;
751 int trnsuccess, success, secure, saverr, savvmserr;
755 midx = my_maxidx(lnm) + 1;
757 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
758 /* Set up a temporary buffer for the return value; Perl will
759 * clean it up at the next statement transition */
760 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
761 if (!tmpsv) return NULL;
765 /* Assume no interpreter ==> single thread */
766 if (__my_getenv_eqv != NULL) {
767 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
770 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
772 eqv = __my_getenv_eqv;
775 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
776 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
778 getcwd(eqv,LNM$C_NAMLENGTH);
782 /* Get rid of "000000/ in rooted filespecs */
785 zeros = strstr(eqv, "/000000/");
788 mlen = len - (zeros - eqv) - 7;
789 memmove(zeros, &zeros[7], mlen);
797 /* Impose security constraints only if tainting */
799 /* Impose security constraints only if tainting */
800 secure = PL_curinterp ? PL_tainting : will_taint;
801 saverr = errno; savvmserr = vaxc$errno;
808 #ifdef SECURE_INTERNAL_GETENV
809 secure ? PERL__TRNENV_SECURE : 0
815 /* For the getenv interface we combine all the equivalence names
816 * of a search list logical into one value to acquire a maximum
817 * value length of 255*128 (assuming %ENV is using logicals).
819 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
821 /* If the name contains a semicolon-delimited index, parse it
822 * off and make sure we only retrieve the equivalence name for
824 if ((cp2 = strchr(lnm,';')) != NULL) {
826 uplnm[cp2-lnm] = '\0';
827 idx = strtoul(cp2+1,NULL,0);
829 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
832 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
834 /* Discard NOLOGNAM on internal calls since we're often looking
835 * for an optional name, and this "error" often shows up as the
836 * (bogus) exit status for a die() call later on. */
837 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
838 return success ? eqv : Nullch;
841 } /* end of my_getenv() */
845 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
847 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
851 unsigned long idx = 0;
853 static char *__my_getenv_len_eqv = NULL;
854 int secure, saverr, savvmserr;
857 midx = my_maxidx(lnm) + 1;
859 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
860 /* Set up a temporary buffer for the return value; Perl will
861 * clean it up at the next statement transition */
862 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
863 if (!tmpsv) return NULL;
867 /* Assume no interpreter ==> single thread */
868 if (__my_getenv_len_eqv != NULL) {
869 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
872 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
874 buf = __my_getenv_len_eqv;
877 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
878 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
881 getcwd(buf,LNM$C_NAMLENGTH);
884 /* Get rid of "000000/ in rooted filespecs */
886 zeros = strstr(buf, "/000000/");
889 mlen = *len - (zeros - buf) - 7;
890 memmove(zeros, &zeros[7], mlen);
899 /* Impose security constraints only if tainting */
900 secure = PL_curinterp ? PL_tainting : will_taint;
901 saverr = errno; savvmserr = vaxc$errno;
908 #ifdef SECURE_INTERNAL_GETENV
909 secure ? PERL__TRNENV_SECURE : 0
915 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
917 if ((cp2 = strchr(lnm,';')) != NULL) {
920 idx = strtoul(cp2+1,NULL,0);
922 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
925 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
927 /* Get rid of "000000/ in rooted filespecs */
930 zeros = strstr(buf, "/000000/");
933 mlen = *len - (zeros - buf) - 7;
934 memmove(zeros, &zeros[7], mlen);
940 /* Discard NOLOGNAM on internal calls since we're often looking
941 * for an optional name, and this "error" often shows up as the
942 * (bogus) exit status for a die() call later on. */
943 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
944 return *len ? buf : Nullch;
947 } /* end of my_getenv_len() */
950 static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *);
952 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
954 /*{{{ void prime_env_iter() */
957 /* Fill the %ENV associative array with all logical names we can
958 * find, in preparation for iterating over it.
961 static int primed = 0;
962 HV *seenhv = NULL, *envhv;
964 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = Nullch;
965 unsigned short int chan;
966 #ifndef CLI$M_TRUSTED
967 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
969 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
970 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
972 bool have_sym = FALSE, have_lnm = FALSE;
973 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
974 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
975 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
976 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
977 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
978 #if defined(PERL_IMPLICIT_CONTEXT)
981 #if defined(USE_ITHREADS)
982 static perl_mutex primenv_mutex;
983 MUTEX_INIT(&primenv_mutex);
986 #if defined(PERL_IMPLICIT_CONTEXT)
987 /* We jump through these hoops because we can be called at */
988 /* platform-specific initialization time, which is before anything is */
989 /* set up--we can't even do a plain dTHX since that relies on the */
990 /* interpreter structure to be initialized */
992 aTHX = PERL_GET_INTERP;
998 if (primed || !PL_envgv) return;
999 MUTEX_LOCK(&primenv_mutex);
1000 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1001 envhv = GvHVn(PL_envgv);
1002 /* Perform a dummy fetch as an lval to insure that the hash table is
1003 * set up. Otherwise, the hv_store() will turn into a nullop. */
1004 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1006 for (i = 0; env_tables[i]; i++) {
1007 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1008 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1009 if (!have_lnm && !str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1011 if (have_sym || have_lnm) {
1012 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1013 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1014 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1015 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1018 for (i--; i >= 0; i--) {
1019 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1022 for (j = 0; environ[j]; j++) {
1023 if (!(start = strchr(environ[j],'='))) {
1024 if (ckWARN(WARN_INTERNAL))
1025 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1029 sv = newSVpv(start,0);
1031 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1036 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1037 !str$case_blind_compare(&tmpdsc,&clisym)) {
1038 strcpy(cmd,"Show Symbol/Global *");
1039 cmddsc.dsc$w_length = 20;
1040 if (env_tables[i]->dsc$w_length == 12 &&
1041 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1042 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
1043 flags = defflags | CLI$M_NOLOGNAM;
1046 strcpy(cmd,"Show Logical *");
1047 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1048 strcat(cmd," /Table=");
1049 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1050 cmddsc.dsc$w_length = strlen(cmd);
1052 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1053 flags = defflags | CLI$M_NOCLISYM;
1056 /* Create a new subprocess to execute each command, to exclude the
1057 * remote possibility that someone could subvert a mbx or file used
1058 * to write multiple commands to a single subprocess.
1061 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1062 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1063 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1064 defflags &= ~CLI$M_TRUSTED;
1065 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1067 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1068 if (seenhv) SvREFCNT_dec(seenhv);
1071 char *cp1, *cp2, *key;
1072 unsigned long int sts, iosb[2], retlen, keylen;
1075 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1076 if (sts & 1) sts = iosb[0] & 0xffff;
1077 if (sts == SS$_ENDOFFILE) {
1079 while (substs == 0) { sys$hiber(); wakect++;}
1080 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1085 retlen = iosb[0] >> 16;
1086 if (!retlen) continue; /* blank line */
1088 if (iosb[1] != subpid) {
1090 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1094 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1095 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1097 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1098 if (*cp1 == '(' || /* Logical name table name */
1099 *cp1 == '=' /* Next eqv of searchlist */) continue;
1100 if (*cp1 == '"') cp1++;
1101 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1102 key = cp1; keylen = cp2 - cp1;
1103 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1104 while (*cp2 && *cp2 != '=') cp2++;
1105 while (*cp2 && *cp2 == '=') cp2++;
1106 while (*cp2 && *cp2 == ' ') cp2++;
1107 if (*cp2 == '"') { /* String translation; may embed "" */
1108 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1109 cp2++; cp1--; /* Skip "" surrounding translation */
1111 else { /* Numeric translation */
1112 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1113 cp1--; /* stop on last non-space char */
1115 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1116 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1119 PERL_HASH(hash,key,keylen);
1121 if (cp1 == cp2 && *cp2 == '.') {
1122 /* A single dot usually means an unprintable character, such as a null
1123 * to indicate a zero-length value. Get the actual value to make sure.
1125 char lnm[LNM$C_NAMLENGTH+1];
1126 char eqv[MAX_DCL_SYMBOL+1];
1127 strncpy(lnm, key, keylen);
1128 int trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1129 sv = newSVpvn(eqv, strlen(eqv));
1132 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1136 hv_store(envhv,key,keylen,sv,hash);
1137 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1139 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1140 /* get the PPFs for this process, not the subprocess */
1141 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1142 char eqv[LNM$C_NAMLENGTH+1];
1144 for (i = 0; ppfs[i]; i++) {
1145 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1146 sv = newSVpv(eqv,trnlen);
1148 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1153 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1154 if (buf) Safefree(buf);
1155 if (seenhv) SvREFCNT_dec(seenhv);
1156 MUTEX_UNLOCK(&primenv_mutex);
1159 } /* end of prime_env_iter */
1163 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1164 /* Define or delete an element in the same "environment" as
1165 * vmstrnenv(). If an element is to be deleted, it's removed from
1166 * the first place it's found. If it's to be set, it's set in the
1167 * place designated by the first element of the table vector.
1168 * Like setenv() returns 0 for success, non-zero on error.
1171 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1174 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1175 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1177 unsigned long int retsts, usermode = PSL$C_USER;
1178 struct itmlst_3 *ile, *ilist;
1179 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1180 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1181 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1182 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1183 $DESCRIPTOR(local,"_LOCAL");
1186 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1187 return SS$_IVLOGNAM;
1190 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1191 *cp2 = _toupper(*cp1);
1192 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1193 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1194 return SS$_IVLOGNAM;
1197 lnmdsc.dsc$w_length = cp1 - lnm;
1198 if (!tabvec || !*tabvec) tabvec = env_tables;
1200 if (!eqv) { /* we're deleting n element */
1201 for (curtab = 0; tabvec[curtab]; curtab++) {
1202 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1204 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1205 if ((cp1 = strchr(environ[i],'=')) &&
1206 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1207 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1209 return setenv(lnm,"",1) ? vaxc$errno : 0;
1212 ivenv = 1; retsts = SS$_NOLOGNAM;
1214 if (ckWARN(WARN_INTERNAL))
1215 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1216 ivenv = 1; retsts = SS$_NOSUCHPGM;
1222 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1223 !str$case_blind_compare(&tmpdsc,&clisym)) {
1224 unsigned int symtype;
1225 if (tabvec[curtab]->dsc$w_length == 12 &&
1226 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1227 !str$case_blind_compare(&tmpdsc,&local))
1228 symtype = LIB$K_CLI_LOCAL_SYM;
1229 else symtype = LIB$K_CLI_GLOBAL_SYM;
1230 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1231 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1232 if (retsts == LIB$_NOSUCHSYM) continue;
1236 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1237 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1238 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1239 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1240 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1244 else { /* we're defining a value */
1245 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1247 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1249 if (ckWARN(WARN_INTERNAL))
1250 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1251 retsts = SS$_NOSUCHPGM;
1255 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1256 eqvdsc.dsc$w_length = strlen(eqv);
1257 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1258 !str$case_blind_compare(&tmpdsc,&clisym)) {
1259 unsigned int symtype;
1260 if (tabvec[0]->dsc$w_length == 12 &&
1261 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1262 !str$case_blind_compare(&tmpdsc,&local))
1263 symtype = LIB$K_CLI_LOCAL_SYM;
1264 else symtype = LIB$K_CLI_GLOBAL_SYM;
1265 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1268 if (!*eqv) eqvdsc.dsc$w_length = 1;
1269 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1271 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1272 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1273 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1274 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1275 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1276 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1279 Newx(ilist,nseg+1,struct itmlst_3);
1282 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1285 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1287 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1288 ile->itmcode = LNM$_STRING;
1290 if ((j+1) == nseg) {
1291 ile->buflen = strlen(c);
1292 /* in case we are truncating one that's too long */
1293 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1296 ile->buflen = LNM$C_NAMLENGTH;
1300 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1304 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1309 if (!(retsts & 1)) {
1311 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1312 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1313 set_errno(EVMSERR); break;
1314 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1315 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1316 set_errno(EINVAL); break;
1323 set_vaxc_errno(retsts);
1324 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1327 /* We reset error values on success because Perl does an hv_fetch()
1328 * before each hv_store(), and if the thing we're setting didn't
1329 * previously exist, we've got a leftover error message. (Of course,
1330 * this fails in the face of
1331 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1332 * in that the error reported in $! isn't spurious,
1333 * but it's right more often than not.)
1335 set_errno(0); set_vaxc_errno(retsts);
1339 } /* end of vmssetenv() */
1342 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1343 /* This has to be a function since there's a prototype for it in proto.h */
1345 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1348 int len = strlen(lnm);
1352 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1353 if (!strcmp(uplnm,"DEFAULT")) {
1354 if (eqv && *eqv) my_chdir(eqv);
1358 #ifndef RTL_USES_UTC
1359 if (len == 6 || len == 2) {
1362 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1364 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1365 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1369 (void) vmssetenv(lnm,eqv,NULL);
1373 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1375 * sets a user-mode logical in the process logical name table
1376 * used for redirection of sys$error
1379 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1381 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1382 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1383 unsigned long int iss, attr = LNM$M_CONFINE;
1384 unsigned char acmode = PSL$C_USER;
1385 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1387 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1388 d_name.dsc$w_length = strlen(name);
1390 lnmlst[0].buflen = strlen(eqv);
1391 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1393 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1394 if (!(iss&1)) lib$signal(iss);
1399 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1400 /* my_crypt - VMS password hashing
1401 * my_crypt() provides an interface compatible with the Unix crypt()
1402 * C library function, and uses sys$hash_password() to perform VMS
1403 * password hashing. The quadword hashed password value is returned
1404 * as a NUL-terminated 8 character string. my_crypt() does not change
1405 * the case of its string arguments; in order to match the behavior
1406 * of LOGINOUT et al., alphabetic characters in both arguments must
1407 * be upcased by the caller.
1409 * - fix me to call ACM services when available
1412 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1414 # ifndef UAI$C_PREFERRED_ALGORITHM
1415 # define UAI$C_PREFERRED_ALGORITHM 127
1417 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1418 unsigned short int salt = 0;
1419 unsigned long int sts;
1421 unsigned short int dsc$w_length;
1422 unsigned char dsc$b_type;
1423 unsigned char dsc$b_class;
1424 const char * dsc$a_pointer;
1425 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1426 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1427 struct itmlst_3 uailst[3] = {
1428 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1429 { sizeof salt, UAI$_SALT, &salt, 0},
1430 { 0, 0, NULL, NULL}};
1431 static char hash[9];
1433 usrdsc.dsc$w_length = strlen(usrname);
1434 usrdsc.dsc$a_pointer = usrname;
1435 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1437 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1441 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1446 set_vaxc_errno(sts);
1447 if (sts != RMS$_RNF) return NULL;
1450 txtdsc.dsc$w_length = strlen(textpasswd);
1451 txtdsc.dsc$a_pointer = textpasswd;
1452 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1453 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1456 return (char *) hash;
1458 } /* end of my_crypt() */
1462 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned);
1463 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int);
1464 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int);
1466 /* fixup barenames that are directories for internal use.
1467 * There have been problems with the consistent handling of UNIX
1468 * style directory names when routines are presented with a name that
1469 * has no directory delimitors at all. So this routine will eventually
1472 static char * fixup_bare_dirnames(const char * name)
1474 if (decc_disable_to_vms_logname_translation) {
1481 * A little hack to get around a bug in some implemenation of remove()
1482 * that do not know how to delete a directory
1484 * Delete any file to which user has control access, regardless of whether
1485 * delete access is explicitly allowed.
1486 * Limitations: User must have write access to parent directory.
1487 * Does not block signals or ASTs; if interrupted in midstream
1488 * may leave file with an altered ACL.
1491 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1493 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1495 char *vmsname, *rspec;
1497 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1498 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1499 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1501 unsigned char myace$b_length;
1502 unsigned char myace$b_type;
1503 unsigned short int myace$w_flags;
1504 unsigned long int myace$l_access;
1505 unsigned long int myace$l_ident;
1506 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1507 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1508 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1510 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1511 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1512 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1513 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1514 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1515 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1517 /* Expand the input spec using RMS, since the CRTL remove() and
1518 * system services won't do this by themselves, so we may miss
1519 * a file "hiding" behind a logical name or search list. */
1520 Newx(vmsname, NAM$C_MAXRSS+1, char);
1521 if (do_rmsexpand(name, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS) == NULL) {
1526 if (decc_posix_compliant_pathnames) {
1527 /* In POSIX mode, we prefer to remove the UNIX name */
1529 remove_name = (char *)name;
1532 Newx(rspec, NAM$C_MAXRSS+1, char);
1533 if (do_rmsexpand(vmsname, rspec, 0, NULL, PERL_RMSEXPAND_M_VMS) == NULL) {
1539 remove_name = rspec;
1542 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1544 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1545 Newx(remove_name, NAM$C_MAXRSS+1, char);
1546 do_pathify_dirspec(name, remove_name, 0);
1547 if (!rmdir(remove_name)) {
1549 Safefree(remove_name);
1551 return 0; /* Can we just get rid of it? */
1555 if (!rmdir(remove_name)) {
1557 return 0; /* Can we just get rid of it? */
1563 if (!remove(remove_name)) {
1565 return 0; /* Can we just get rid of it? */
1568 /* If not, can changing protections help? */
1569 if (vaxc$errno != RMS$_PRV) {
1574 /* No, so we get our own UIC to use as a rights identifier,
1575 * and the insert an ACE at the head of the ACL which allows us
1576 * to delete the file.
1578 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1579 fildsc.dsc$w_length = strlen(rspec);
1580 fildsc.dsc$a_pointer = rspec;
1582 newace.myace$l_ident = oldace.myace$l_ident;
1583 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1585 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1586 set_errno(ENOENT); break;
1588 set_errno(ENOTDIR); break;
1590 set_errno(ENODEV); break;
1591 case RMS$_SYN: case SS$_INVFILFOROP:
1592 set_errno(EINVAL); break;
1594 set_errno(EACCES); break;
1598 set_vaxc_errno(aclsts);
1602 /* Grab any existing ACEs with this identifier in case we fail */
1603 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1604 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1605 || fndsts == SS$_NOMOREACE ) {
1606 /* Add the new ACE . . . */
1607 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1610 #if defined(__CRTL_VER) && __CRTL_VER >= 70000000
1612 if (decc_dir_barename && decc_posix_compliant_pathnames) {
1613 Newx(remove_name, NAM$C_MAXRSS+1, char);
1614 do_pathify_dirspec(name, remove_name, 0);
1615 rmsts = rmdir(remove_name);
1616 Safefree(remove_name);
1619 rmsts = rmdir(remove_name);
1623 rmsts = remove(remove_name);
1625 /* We blew it - dir with files in it, no write priv for
1626 * parent directory, etc. Put things back the way they were. */
1627 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1630 addlst[0].bufadr = &oldace;
1631 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1638 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1639 /* We just deleted it, so of course it's not there. Some versions of
1640 * VMS seem to return success on the unlock operation anyhow (after all
1641 * the unlock is successful), but others don't.
1643 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1644 if (aclsts & 1) aclsts = fndsts;
1645 if (!(aclsts & 1)) {
1647 set_vaxc_errno(aclsts);
1655 } /* end of kill_file() */
1659 /*{{{int do_rmdir(char *name)*/
1661 Perl_do_rmdir(pTHX_ const char *name)
1663 char dirfile[NAM$C_MAXRSS+1];
1667 if (do_fileify_dirspec(name,dirfile,0) == NULL) return -1;
1668 if (flex_stat(dirfile,&st) || !S_ISDIR(st.st_mode)) retval = -1;
1669 else retval = mp_do_kill_file(aTHX_ dirfile, 1);
1672 } /* end of do_rmdir */
1676 * Delete any file to which user has control access, regardless of whether
1677 * delete access is explicitly allowed.
1678 * Limitations: User must have write access to parent directory.
1679 * Does not block signals or ASTs; if interrupted in midstream
1680 * may leave file with an altered ACL.
1683 /*{{{int kill_file(char *name)*/
1685 Perl_kill_file(pTHX_ const char *name)
1687 char rspec[NAM$C_MAXRSS+1];
1689 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1690 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1691 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1693 unsigned char myace$b_length;
1694 unsigned char myace$b_type;
1695 unsigned short int myace$w_flags;
1696 unsigned long int myace$l_access;
1697 unsigned long int myace$l_ident;
1698 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1699 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1700 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1702 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1703 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1704 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1705 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1706 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1707 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1709 /* Expand the input spec using RMS, since the CRTL remove() and
1710 * system services won't do this by themselves, so we may miss
1711 * a file "hiding" behind a logical name or search list. */
1712 tspec = do_rmsexpand(name, rspec, 0, NULL, PERL_RMSEXPAND_M_VMS);
1713 if (tspec == NULL) return -1;
1714 if (!remove(rspec)) return 0; /* Can we just get rid of it? */
1715 /* If not, can changing protections help? */
1716 if (vaxc$errno != RMS$_PRV) return -1;
1718 /* No, so we get our own UIC to use as a rights identifier,
1719 * and the insert an ACE at the head of the ACL which allows us
1720 * to delete the file.
1722 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1723 fildsc.dsc$w_length = strlen(rspec);
1724 fildsc.dsc$a_pointer = rspec;
1726 newace.myace$l_ident = oldace.myace$l_ident;
1727 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1729 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1730 set_errno(ENOENT); break;
1732 set_errno(ENOTDIR); break;
1734 set_errno(ENODEV); break;
1735 case RMS$_SYN: case SS$_INVFILFOROP:
1736 set_errno(EINVAL); break;
1738 set_errno(EACCES); break;
1742 set_vaxc_errno(aclsts);
1745 /* Grab any existing ACEs with this identifier in case we fail */
1746 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1747 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1748 || fndsts == SS$_NOMOREACE ) {
1749 /* Add the new ACE . . . */
1750 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1752 if ((rmsts = remove(name))) {
1753 /* We blew it - dir with files in it, no write priv for
1754 * parent directory, etc. Put things back the way they were. */
1755 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1758 addlst[0].bufadr = &oldace;
1759 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1766 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1767 /* We just deleted it, so of course it's not there. Some versions of
1768 * VMS seem to return success on the unlock operation anyhow (after all
1769 * the unlock is successful), but others don't.
1771 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1772 if (aclsts & 1) aclsts = fndsts;
1773 if (!(aclsts & 1)) {
1775 set_vaxc_errno(aclsts);
1781 } /* end of kill_file() */
1785 /*{{{int my_mkdir(char *,Mode_t)*/
1787 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
1789 STRLEN dirlen = strlen(dir);
1791 /* zero length string sometimes gives ACCVIO */
1792 if (dirlen == 0) return -1;
1794 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
1795 * null file name/type. However, it's commonplace under Unix,
1796 * so we'll allow it for a gain in portability.
1798 if (dir[dirlen-1] == '/') {
1799 char *newdir = savepvn(dir,dirlen-1);
1800 int ret = mkdir(newdir,mode);
1804 else return mkdir(dir,mode);
1805 } /* end of my_mkdir */
1808 /*{{{int my_chdir(char *)*/
1810 Perl_my_chdir(pTHX_ const char *dir)
1812 STRLEN dirlen = strlen(dir);
1814 /* zero length string sometimes gives ACCVIO */
1815 if (dirlen == 0) return -1;
1818 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
1819 * This does not work if DECC$EFS_CHARSET is active. Hack it here
1820 * so that existing scripts do not need to be changed.
1823 while ((dirlen > 0) && (*dir1 == ' ')) {
1828 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
1830 * null file name/type. However, it's commonplace under Unix,
1831 * so we'll allow it for a gain in portability.
1833 * - Preview- '/' will be valid soon on VMS
1835 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
1836 char *newdir = savepvn(dir1,dirlen-1);
1837 int ret = chdir(newdir);
1841 else return chdir(dir1);
1842 } /* end of my_chdir */
1846 /*{{{FILE *my_tmpfile()*/
1853 if ((fp = tmpfile())) return fp;
1855 Newx(cp,L_tmpnam+24,char);
1856 if (decc_filename_unix_only == 0)
1857 strcpy(cp,"Sys$Scratch:");
1860 tmpnam(cp+strlen(cp));
1861 strcat(cp,".Perltmp");
1862 fp = fopen(cp,"w+","fop=dlt");
1869 #ifndef HOMEGROWN_POSIX_SIGNALS
1871 * The C RTL's sigaction fails to check for invalid signal numbers so we
1872 * help it out a bit. The docs are correct, but the actual routine doesn't
1873 * do what the docs say it will.
1875 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
1877 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
1878 struct sigaction* oact)
1880 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
1881 SETERRNO(EINVAL, SS$_INVARG);
1884 return sigaction(sig, act, oact);
1889 #ifdef KILL_BY_SIGPRC
1890 #include <errnodef.h>
1892 /* We implement our own kill() using the undocumented system service
1893 sys$sigprc for one of two reasons:
1895 1.) If the kill() in an older CRTL uses sys$forcex, causing the
1896 target process to do a sys$exit, which usually can't be handled
1897 gracefully...certainly not by Perl and the %SIG{} mechanism.
1899 2.) If the kill() in the CRTL can't be called from a signal
1900 handler without disappearing into the ether, i.e., the signal
1901 it purportedly sends is never trapped. Still true as of VMS 7.3.
1903 sys$sigprc has the same parameters as sys$forcex, but throws an exception
1904 in the target process rather than calling sys$exit.
1906 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
1907 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
1908 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
1909 with condition codes C$_SIG0+nsig*8, catching the exception on the
1910 target process and resignaling with appropriate arguments.
1912 But we don't have that VMS 7.0+ exception handler, so if you
1913 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
1915 Also note that SIGTERM is listed in the docs as being "unimplemented",
1916 yet always seems to be signaled with a VMS condition code of 4 (and
1917 correctly handled for that code). So we hardwire it in.
1919 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
1920 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
1921 than signalling with an unrecognized (and unhandled by CRTL) code.
1924 #define _MY_SIG_MAX 17
1927 Perl_sig_to_vmscondition_int(int sig)
1929 static unsigned int sig_code[_MY_SIG_MAX+1] =
1932 SS$_HANGUP, /* 1 SIGHUP */
1933 SS$_CONTROLC, /* 2 SIGINT */
1934 SS$_CONTROLY, /* 3 SIGQUIT */
1935 SS$_RADRMOD, /* 4 SIGILL */
1936 SS$_BREAK, /* 5 SIGTRAP */
1937 SS$_OPCCUS, /* 6 SIGABRT */
1938 SS$_COMPAT, /* 7 SIGEMT */
1940 SS$_FLTOVF, /* 8 SIGFPE VAX */
1942 SS$_HPARITH, /* 8 SIGFPE AXP */
1944 SS$_ABORT, /* 9 SIGKILL */
1945 SS$_ACCVIO, /* 10 SIGBUS */
1946 SS$_ACCVIO, /* 11 SIGSEGV */
1947 SS$_BADPARAM, /* 12 SIGSYS */
1948 SS$_NOMBX, /* 13 SIGPIPE */
1949 SS$_ASTFLT, /* 14 SIGALRM */
1955 #if __VMS_VER >= 60200000
1956 static int initted = 0;
1959 sig_code[16] = C$_SIGUSR1;
1960 sig_code[17] = C$_SIGUSR2;
1964 if (sig < _SIG_MIN) return 0;
1965 if (sig > _MY_SIG_MAX) return 0;
1966 return sig_code[sig];
1970 Perl_sig_to_vmscondition(int sig)
1973 if (vms_debug_on_exception != 0)
1974 lib$signal(SS$_DEBUG);
1976 return Perl_sig_to_vmscondition_int(sig);
1981 Perl_my_kill(int pid, int sig)
1986 int sys$sigprc(unsigned int *pidadr,
1987 struct dsc$descriptor_s *prcname,
1990 /* sig 0 means validate the PID */
1991 /*------------------------------*/
1993 const unsigned long int jpicode = JPI$_PID;
1996 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
1997 if ($VMS_STATUS_SUCCESS(status))
2000 case SS$_NOSUCHNODE:
2001 case SS$_UNREACHABLE:
2015 code = Perl_sig_to_vmscondition_int(sig);
2018 SETERRNO(EINVAL, SS$_BADPARAM);
2022 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2023 * signals are to be sent to multiple processes.
2024 * pid = 0 - all processes in group except ones that the system exempts
2025 * pid = -1 - all processes except ones that the system exempts
2026 * pid = -n - all processes in group (abs(n)) except ...
2027 * For now, just report as not supported.
2031 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2035 iss = sys$sigprc((unsigned int *)&pid,0,code);
2036 if (iss&1) return 0;
2040 set_errno(EPERM); break;
2042 case SS$_NOSUCHNODE:
2043 case SS$_UNREACHABLE:
2044 set_errno(ESRCH); break;
2046 set_errno(ENOMEM); break;
2051 set_vaxc_errno(iss);
2057 /* Routine to convert a VMS status code to a UNIX status code.
2058 ** More tricky than it appears because of conflicting conventions with
2061 ** VMS status codes are a bit mask, with the least significant bit set for
2064 ** Special UNIX status of EVMSERR indicates that no translation is currently
2065 ** available, and programs should check the VMS status code.
2067 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2071 #ifndef C_FACILITY_NO
2072 #define C_FACILITY_NO 0x350000
2075 #define DCL_IVVERB 0x38090
2078 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2086 /* Assume the best or the worst */
2087 if (vms_status & STS$M_SUCCESS)
2090 unix_status = EVMSERR;
2092 msg_status = vms_status & ~STS$M_CONTROL;
2094 facility = vms_status & STS$M_FAC_NO;
2095 fac_sp = vms_status & STS$M_FAC_SP;
2096 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2098 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2104 unix_status = EFAULT;
2106 case SS$_DEVOFFLINE:
2107 unix_status = EBUSY;
2110 unix_status = ENOTCONN;
2118 case SS$_INVFILFOROP:
2122 unix_status = EINVAL;
2124 case SS$_UNSUPPORTED:
2125 unix_status = ENOTSUP;
2130 unix_status = EACCES;
2132 case SS$_DEVICEFULL:
2133 unix_status = ENOSPC;
2136 unix_status = ENODEV;
2138 case SS$_NOSUCHFILE:
2139 case SS$_NOSUCHOBJECT:
2140 unix_status = ENOENT;
2142 case SS$_ABORT: /* Fatal case */
2143 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2144 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2145 unix_status = EINTR;
2148 unix_status = E2BIG;
2151 unix_status = ENOMEM;
2154 unix_status = EPERM;
2156 case SS$_NOSUCHNODE:
2157 case SS$_UNREACHABLE:
2158 unix_status = ESRCH;
2161 unix_status = ECHILD;
2164 if ((facility == 0) && (msg_no < 8)) {
2165 /* These are not real VMS status codes so assume that they are
2166 ** already UNIX status codes
2168 unix_status = msg_no;
2174 /* Translate a POSIX exit code to a UNIX exit code */
2175 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2176 unix_status = (msg_no & 0x07F8) >> 3;
2180 /* Documented traditional behavior for handling VMS child exits */
2181 /*--------------------------------------------------------------*/
2182 if (child_flag != 0) {
2184 /* Success / Informational return 0 */
2185 /*----------------------------------*/
2186 if (msg_no & STS$K_SUCCESS)
2189 /* Warning returns 1 */
2190 /*-------------------*/
2191 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2194 /* Everything else pass through the severity bits */
2195 /*------------------------------------------------*/
2196 return (msg_no & STS$M_SEVERITY);
2199 /* Normal VMS status to ERRNO mapping attempt */
2200 /*--------------------------------------------*/
2201 switch(msg_status) {
2202 /* case RMS$_EOF: */ /* End of File */
2203 case RMS$_FNF: /* File Not Found */
2204 case RMS$_DNF: /* Dir Not Found */
2205 unix_status = ENOENT;
2207 case RMS$_RNF: /* Record Not Found */
2208 unix_status = ESRCH;
2211 unix_status = ENOTDIR;
2214 unix_status = ENODEV;
2219 unix_status = EBADF;
2222 unix_status = EEXIST;
2226 case LIB$_INVSTRDES:
2228 case LIB$_NOSUCHSYM:
2229 case LIB$_INVSYMNAM:
2231 unix_status = EINVAL;
2237 unix_status = E2BIG;
2239 case RMS$_PRV: /* No privilege */
2240 case RMS$_ACC: /* ACP file access failed */
2241 case RMS$_WLK: /* Device write locked */
2242 unix_status = EACCES;
2244 /* case RMS$_NMF: */ /* No more files */
2252 /* Try to guess at what VMS error status should go with a UNIX errno
2253 * value. This is hard to do as there could be many possible VMS
2254 * error statuses that caused the errno value to be set.
2257 int Perl_unix_status_to_vms(int unix_status)
2259 int test_unix_status;
2261 /* Trivial cases first */
2262 /*---------------------*/
2263 if (unix_status == EVMSERR)
2266 /* Is vaxc$errno sane? */
2267 /*---------------------*/
2268 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2269 if (test_unix_status == unix_status)
2272 /* If way out of range, must be VMS code already */
2273 /*-----------------------------------------------*/
2274 if (unix_status > EVMSERR)
2277 /* If out of range, punt */
2278 /*-----------------------*/
2279 if (unix_status > __ERRNO_MAX)
2283 /* Ok, now we have to do it the hard way. */
2284 /*----------------------------------------*/
2285 switch(unix_status) {
2286 case 0: return SS$_NORMAL;
2287 case EPERM: return SS$_NOPRIV;
2288 case ENOENT: return SS$_NOSUCHOBJECT;
2289 case ESRCH: return SS$_UNREACHABLE;
2290 case EINTR: return SS$_ABORT;
2293 case E2BIG: return SS$_BUFFEROVF;
2295 case EBADF: return RMS$_IFI;
2296 case ECHILD: return SS$_NONEXPR;
2298 case ENOMEM: return SS$_INSFMEM;
2299 case EACCES: return SS$_FILACCERR;
2300 case EFAULT: return SS$_ACCVIO;
2302 case EBUSY: return SS$_DEVOFFLINE;
2303 case EEXIST: return RMS$_FEX;
2305 case ENODEV: return SS$_NOSUCHDEV;
2306 case ENOTDIR: return RMS$_DIR;
2308 case EINVAL: return SS$_INVARG;
2314 case ENOSPC: return SS$_DEVICEFULL;
2315 case ESPIPE: return LIB$_INVARG;
2320 case ERANGE: return LIB$_INVARG;
2321 /* case EWOULDBLOCK */
2322 /* case EINPROGRESS */
2325 /* case EDESTADDRREQ */
2327 /* case EPROTOTYPE */
2328 /* case ENOPROTOOPT */
2329 /* case EPROTONOSUPPORT */
2330 /* case ESOCKTNOSUPPORT */
2331 /* case EOPNOTSUPP */
2332 /* case EPFNOSUPPORT */
2333 /* case EAFNOSUPPORT */
2334 /* case EADDRINUSE */
2335 /* case EADDRNOTAVAIL */
2337 /* case ENETUNREACH */
2338 /* case ENETRESET */
2339 /* case ECONNABORTED */
2340 /* case ECONNRESET */
2343 case ENOTCONN: return SS$_CLEARED;
2344 /* case ESHUTDOWN */
2345 /* case ETOOMANYREFS */
2346 /* case ETIMEDOUT */
2347 /* case ECONNREFUSED */
2349 /* case ENAMETOOLONG */
2350 /* case EHOSTDOWN */
2351 /* case EHOSTUNREACH */
2352 /* case ENOTEMPTY */
2364 /* case ECANCELED */
2368 return SS$_UNSUPPORTED;
2374 /* case EABANDONED */
2376 return SS$_ABORT; /* punt */
2379 return SS$_ABORT; /* Should not get here */
2383 /* default piping mailbox size */
2384 #define PERL_BUFSIZ 512
2388 create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2390 unsigned long int mbxbufsiz;
2391 static unsigned long int syssize = 0;
2392 unsigned long int dviitm = DVI$_DEVNAM;
2393 char csize[LNM$C_NAMLENGTH+1];
2397 unsigned long syiitm = SYI$_MAXBUF;
2399 * Get the SYSGEN parameter MAXBUF
2401 * If the logical 'PERL_MBX_SIZE' is defined
2402 * use the value of the logical instead of PERL_BUFSIZ, but
2403 * keep the size between 128 and MAXBUF.
2406 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2409 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2410 mbxbufsiz = atoi(csize);
2412 mbxbufsiz = PERL_BUFSIZ;
2414 if (mbxbufsiz < 128) mbxbufsiz = 128;
2415 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2417 _ckvmssts(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2419 _ckvmssts(sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
2420 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2422 } /* end of create_mbx() */
2425 /*{{{ my_popen and my_pclose*/
2427 typedef struct _iosb IOSB;
2428 typedef struct _iosb* pIOSB;
2429 typedef struct _pipe Pipe;
2430 typedef struct _pipe* pPipe;
2431 typedef struct pipe_details Info;
2432 typedef struct pipe_details* pInfo;
2433 typedef struct _srqp RQE;
2434 typedef struct _srqp* pRQE;
2435 typedef struct _tochildbuf CBuf;
2436 typedef struct _tochildbuf* pCBuf;
2439 unsigned short status;
2440 unsigned short count;
2441 unsigned long dvispec;
2444 #pragma member_alignment save
2445 #pragma nomember_alignment quadword
2446 struct _srqp { /* VMS self-relative queue entry */
2447 unsigned long qptr[2];
2449 #pragma member_alignment restore
2450 static RQE RQE_ZERO = {0,0};
2452 struct _tochildbuf {
2455 unsigned short size;
2463 unsigned short chan_in;
2464 unsigned short chan_out;
2466 unsigned int bufsize;
2478 #if defined(PERL_IMPLICIT_CONTEXT)
2479 void *thx; /* Either a thread or an interpreter */
2480 /* pointer, depending on how we're built */
2488 PerlIO *fp; /* file pointer to pipe mailbox */
2489 int useFILE; /* using stdio, not perlio */
2490 int pid; /* PID of subprocess */
2491 int mode; /* == 'r' if pipe open for reading */
2492 int done; /* subprocess has completed */
2493 int waiting; /* waiting for completion/closure */
2494 int closing; /* my_pclose is closing this pipe */
2495 unsigned long completion; /* termination status of subprocess */
2496 pPipe in; /* pipe in to sub */
2497 pPipe out; /* pipe out of sub */
2498 pPipe err; /* pipe of sub's sys$error */
2499 int in_done; /* true when in pipe finished */
2504 struct exit_control_block
2506 struct exit_control_block *flink;
2507 unsigned long int (*exit_routine)();
2508 unsigned long int arg_count;
2509 unsigned long int *status_address;
2510 unsigned long int exit_status;
2513 typedef struct _closed_pipes Xpipe;
2514 typedef struct _closed_pipes* pXpipe;
2516 struct _closed_pipes {
2517 int pid; /* PID of subprocess */
2518 unsigned long completion; /* termination status of subprocess */
2520 #define NKEEPCLOSED 50
2521 static Xpipe closed_list[NKEEPCLOSED];
2522 static int closed_index = 0;
2523 static int closed_num = 0;
2525 #define RETRY_DELAY "0 ::0.20"
2526 #define MAX_RETRY 50
2528 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2529 static unsigned long mypid;
2530 static unsigned long delaytime[2];
2532 static pInfo open_pipes = NULL;
2533 static $DESCRIPTOR(nl_desc, "NL:");
2535 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2539 static unsigned long int
2540 pipe_exit_routine(pTHX)
2543 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2544 int sts, did_stuff, need_eof, j;
2547 flush any pending i/o
2553 PerlIO_flush(info->fp); /* first, flush data */
2555 fflush((FILE *)info->fp);
2561 next we try sending an EOF...ignore if doesn't work, make sure we
2569 _ckvmssts_noperl(sys$setast(0));
2570 if (info->in && !info->in->shut_on_empty) {
2571 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2576 _ckvmssts_noperl(sys$setast(1));
2580 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2582 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2587 _ckvmssts_noperl(sys$setast(0));
2588 if (info->waiting && info->done)
2590 nwait += info->waiting;
2591 _ckvmssts_noperl(sys$setast(1));
2601 _ckvmssts_noperl(sys$setast(0));
2602 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2603 sts = sys$forcex(&info->pid,0,&abort);
2604 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2607 _ckvmssts_noperl(sys$setast(1));
2611 /* again, wait for effect */
2613 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2618 _ckvmssts_noperl(sys$setast(0));
2619 if (info->waiting && info->done)
2621 nwait += info->waiting;
2622 _ckvmssts_noperl(sys$setast(1));
2631 _ckvmssts_noperl(sys$setast(0));
2632 if (!info->done) { /* We tried to be nice . . . */
2633 sts = sys$delprc(&info->pid,0);
2634 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2636 _ckvmssts_noperl(sys$setast(1));
2641 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
2642 else if (!(sts & 1)) retsts = sts;
2647 static struct exit_control_block pipe_exitblock =
2648 {(struct exit_control_block *) 0,
2649 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
2651 static void pipe_mbxtofd_ast(pPipe p);
2652 static void pipe_tochild1_ast(pPipe p);
2653 static void pipe_tochild2_ast(pPipe p);
2656 popen_completion_ast(pInfo info)
2658 pInfo i = open_pipes;
2663 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
2664 closed_list[closed_index].pid = info->pid;
2665 closed_list[closed_index].completion = info->completion;
2667 if (closed_index == NKEEPCLOSED)
2672 if (i == info) break;
2675 if (!i) return; /* unlinked, probably freed too */
2680 Writing to subprocess ...
2681 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
2683 chan_out may be waiting for "done" flag, or hung waiting
2684 for i/o completion to child...cancel the i/o. This will
2685 put it into "snarf mode" (done but no EOF yet) that discards
2688 Output from subprocess (stdout, stderr) needs to be flushed and
2689 shut down. We try sending an EOF, but if the mbx is full the pipe
2690 routine should still catch the "shut_on_empty" flag, telling it to
2691 use immediate-style reads so that "mbx empty" -> EOF.
2695 if (info->in && !info->in_done) { /* only for mode=w */
2696 if (info->in->shut_on_empty && info->in->need_wake) {
2697 info->in->need_wake = FALSE;
2698 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
2700 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
2704 if (info->out && !info->out_done) { /* were we also piping output? */
2705 info->out->shut_on_empty = TRUE;
2706 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2707 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
2708 _ckvmssts_noperl(iss);
2711 if (info->err && !info->err_done) { /* we were piping stderr */
2712 info->err->shut_on_empty = TRUE;
2713 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
2714 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
2715 _ckvmssts_noperl(iss);
2717 _ckvmssts_noperl(sys$setef(pipe_ef));
2721 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
2722 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
2725 we actually differ from vmstrnenv since we use this to
2726 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
2727 are pointing to the same thing
2730 static unsigned short
2731 popen_translate(pTHX_ char *logical, char *result)
2734 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
2735 $DESCRIPTOR(d_log,"");
2737 unsigned short length;
2738 unsigned short code;
2740 unsigned short *retlenaddr;
2742 unsigned short l, ifi;
2744 d_log.dsc$a_pointer = logical;
2745 d_log.dsc$w_length = strlen(logical);
2747 itmlst[0].code = LNM$_STRING;
2748 itmlst[0].length = 255;
2749 itmlst[0].buffer_addr = result;
2750 itmlst[0].retlenaddr = &l;
2753 itmlst[1].length = 0;
2754 itmlst[1].buffer_addr = 0;
2755 itmlst[1].retlenaddr = 0;
2757 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
2758 if (iss == SS$_NOLOGNAM) {
2762 if (!(iss&1)) lib$signal(iss);
2765 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
2766 strip it off and return the ifi, if any
2769 if (result[0] == 0x1b && result[1] == 0x00) {
2770 memmove(&ifi,result+2,2);
2771 strcpy(result,result+4);
2773 return ifi; /* this is the RMS internal file id */
2776 static void pipe_infromchild_ast(pPipe p);
2779 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
2780 inside an AST routine without worrying about reentrancy and which Perl
2781 memory allocator is being used.
2783 We read data and queue up the buffers, then spit them out one at a
2784 time to the output mailbox when the output mailbox is ready for one.
2787 #define INITIAL_TOCHILDQUEUE 2
2790 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
2794 char mbx1[64], mbx2[64];
2795 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
2796 DSC$K_CLASS_S, mbx1},
2797 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
2798 DSC$K_CLASS_S, mbx2};
2799 unsigned int dviitm = DVI$_DEVBUFSIZ;
2803 _ckvmssts(lib$get_vm(&n, &p));
2805 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
2806 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
2807 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2810 p->shut_on_empty = FALSE;
2811 p->need_wake = FALSE;
2814 p->iosb.status = SS$_NORMAL;
2815 p->iosb2.status = SS$_NORMAL;
2821 #ifdef PERL_IMPLICIT_CONTEXT
2825 n = sizeof(CBuf) + p->bufsize;
2827 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
2828 _ckvmssts(lib$get_vm(&n, &b));
2829 b->buf = (char *) b + sizeof(CBuf);
2830 _ckvmssts(lib$insqhi(b, &p->free));
2833 pipe_tochild2_ast(p);
2834 pipe_tochild1_ast(p);
2840 /* reads the MBX Perl is writing, and queues */
2843 pipe_tochild1_ast(pPipe p)
2846 int iss = p->iosb.status;
2847 int eof = (iss == SS$_ENDOFFILE);
2849 #ifdef PERL_IMPLICIT_CONTEXT
2855 p->shut_on_empty = TRUE;
2857 _ckvmssts(sys$dassgn(p->chan_in));
2863 b->size = p->iosb.count;
2864 _ckvmssts(sts = lib$insqhi(b, &p->wait));
2866 p->need_wake = FALSE;
2867 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
2870 p->retry = 1; /* initial call */
2873 if (eof) { /* flush the free queue, return when done */
2874 int n = sizeof(CBuf) + p->bufsize;
2876 iss = lib$remqti(&p->free, &b);
2877 if (iss == LIB$_QUEWASEMP) return;
2879 _ckvmssts(lib$free_vm(&n, &b));
2883 iss = lib$remqti(&p->free, &b);
2884 if (iss == LIB$_QUEWASEMP) {
2885 int n = sizeof(CBuf) + p->bufsize;
2886 _ckvmssts(lib$get_vm(&n, &b));
2887 b->buf = (char *) b + sizeof(CBuf);
2893 iss = sys$qio(0,p->chan_in,
2894 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
2896 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
2897 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
2902 /* writes queued buffers to output, waits for each to complete before
2906 pipe_tochild2_ast(pPipe p)
2909 int iss = p->iosb2.status;
2910 int n = sizeof(CBuf) + p->bufsize;
2911 int done = (p->info && p->info->done) ||
2912 iss == SS$_CANCEL || iss == SS$_ABORT;
2913 #if defined(PERL_IMPLICIT_CONTEXT)
2918 if (p->type) { /* type=1 has old buffer, dispose */
2919 if (p->shut_on_empty) {
2920 _ckvmssts(lib$free_vm(&n, &b));
2922 _ckvmssts(lib$insqhi(b, &p->free));
2927 iss = lib$remqti(&p->wait, &b);
2928 if (iss == LIB$_QUEWASEMP) {
2929 if (p->shut_on_empty) {
2931 _ckvmssts(sys$dassgn(p->chan_out));
2932 *p->pipe_done = TRUE;
2933 _ckvmssts(sys$setef(pipe_ef));
2935 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
2936 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
2940 p->need_wake = TRUE;
2950 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
2951 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
2953 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
2954 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
2963 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
2966 char mbx1[64], mbx2[64];
2967 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
2968 DSC$K_CLASS_S, mbx1},
2969 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
2970 DSC$K_CLASS_S, mbx2};
2971 unsigned int dviitm = DVI$_DEVBUFSIZ;
2973 int n = sizeof(Pipe);
2974 _ckvmssts(lib$get_vm(&n, &p));
2975 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
2976 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
2978 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
2979 n = p->bufsize * sizeof(char);
2980 _ckvmssts(lib$get_vm(&n, &p->buf));
2981 p->shut_on_empty = FALSE;
2984 p->iosb.status = SS$_NORMAL;
2985 #if defined(PERL_IMPLICIT_CONTEXT)
2988 pipe_infromchild_ast(p);
2996 pipe_infromchild_ast(pPipe p)
2998 int iss = p->iosb.status;
2999 int eof = (iss == SS$_ENDOFFILE);
3000 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3001 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3002 #if defined(PERL_IMPLICIT_CONTEXT)
3006 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3007 _ckvmssts(sys$dassgn(p->chan_out));
3012 input shutdown if EOF from self (done or shut_on_empty)
3013 output shutdown if closing flag set (my_pclose)
3014 send data/eof from child or eof from self
3015 otherwise, re-read (snarf of data from child)
3020 if (myeof && p->chan_in) { /* input shutdown */
3021 _ckvmssts(sys$dassgn(p->chan_in));
3026 if (myeof || kideof) { /* pass EOF to parent */
3027 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3028 pipe_infromchild_ast, p,
3031 } else if (eof) { /* eat EOF --- fall through to read*/
3033 } else { /* transmit data */
3034 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3035 pipe_infromchild_ast,p,
3036 p->buf, p->iosb.count, 0, 0, 0, 0));
3042 /* everything shut? flag as done */
3044 if (!p->chan_in && !p->chan_out) {
3045 *p->pipe_done = TRUE;
3046 _ckvmssts(sys$setef(pipe_ef));
3050 /* write completed (or read, if snarfing from child)
3051 if still have input active,
3052 queue read...immediate mode if shut_on_empty so we get EOF if empty
3054 check if Perl reading, generate EOFs as needed
3060 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3061 pipe_infromchild_ast,p,
3062 p->buf, p->bufsize, 0, 0, 0, 0);
3063 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3065 } else { /* send EOFs for extra reads */
3066 p->iosb.status = SS$_ENDOFFILE;
3067 p->iosb.dvispec = 0;
3068 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3070 pipe_infromchild_ast, p, 0, 0, 0, 0));
3076 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3080 unsigned long dviitm = DVI$_DEVBUFSIZ;
3082 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3083 DSC$K_CLASS_S, mbx};
3084 int n = sizeof(Pipe);
3086 /* things like terminals and mbx's don't need this filter */
3087 if (fd && fstat(fd,&s) == 0) {
3088 unsigned long dviitm = DVI$_DEVCHAR, devchar;
3089 struct dsc$descriptor_s d_dev = {strlen(s.st_dev), DSC$K_DTYPE_T,
3090 DSC$K_CLASS_S, s.st_dev};
3092 _ckvmssts(lib$getdvi(&dviitm,0,&d_dev,&devchar,0,0));
3093 if (!(devchar & DEV$M_DIR)) { /* non directory structured...*/
3094 strcpy(out, s.st_dev);
3099 _ckvmssts(lib$get_vm(&n, &p));
3100 p->fd_out = dup(fd);
3101 create_mbx(aTHX_ &p->chan_in, &d_mbx);
3102 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3103 n = (p->bufsize+1) * sizeof(char);
3104 _ckvmssts(lib$get_vm(&n, &p->buf));
3105 p->shut_on_empty = FALSE;
3110 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3111 pipe_mbxtofd_ast, p,
3112 p->buf, p->bufsize, 0, 0, 0, 0));
3118 pipe_mbxtofd_ast(pPipe p)
3120 int iss = p->iosb.status;
3121 int done = p->info->done;
3123 int eof = (iss == SS$_ENDOFFILE);
3124 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3125 int err = !(iss&1) && !eof;
3126 #if defined(PERL_IMPLICIT_CONTEXT)
3130 if (done && myeof) { /* end piping */
3132 sys$dassgn(p->chan_in);
3133 *p->pipe_done = TRUE;
3134 _ckvmssts(sys$setef(pipe_ef));
3138 if (!err && !eof) { /* good data to send to file */
3139 p->buf[p->iosb.count] = '\n';
3140 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3143 if (p->retry < MAX_RETRY) {
3144 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3154 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3155 pipe_mbxtofd_ast, p,
3156 p->buf, p->bufsize, 0, 0, 0, 0);
3157 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3162 typedef struct _pipeloc PLOC;
3163 typedef struct _pipeloc* pPLOC;
3167 char dir[NAM$C_MAXRSS+1];
3169 static pPLOC head_PLOC = 0;
3172 free_pipelocs(pTHX_ void *head)
3175 pPLOC *pHead = (pPLOC *)head;
3187 store_pipelocs(pTHX)
3196 char temp[NAM$C_MAXRSS+1];
3200 free_pipelocs(aTHX_ &head_PLOC);
3202 /* the . directory from @INC comes last */
3204 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3205 p->next = head_PLOC;
3207 strcpy(p->dir,"./");
3209 /* get the directory from $^X */
3211 #ifdef PERL_IMPLICIT_CONTEXT
3212 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3214 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3216 strcpy(temp, PL_origargv[0]);
3217 x = strrchr(temp,']');
3219 x = strrchr(temp,'>');
3221 /* It could be a UNIX path */
3222 x = strrchr(temp,'/');
3228 /* Got a bare name, so use default directory */
3233 if ((unixdir = tounixpath(temp, Nullch)) != Nullch) {
3234 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3235 p->next = head_PLOC;
3237 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3238 p->dir[NAM$C_MAXRSS] = '\0';
3242 /* reverse order of @INC entries, skip "." since entered above */
3244 #ifdef PERL_IMPLICIT_CONTEXT
3247 if (PL_incgv) av = GvAVn(PL_incgv);
3249 for (i = 0; av && i <= AvFILL(av); i++) {
3250 dirsv = *av_fetch(av,i,TRUE);
3252 if (SvROK(dirsv)) continue;
3253 dir = SvPVx(dirsv,n_a);
3254 if (strcmp(dir,".") == 0) continue;
3255 if ((unixdir = tounixpath(dir, Nullch)) == Nullch)
3258 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3259 p->next = head_PLOC;
3261 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3262 p->dir[NAM$C_MAXRSS] = '\0';
3265 /* most likely spot (ARCHLIB) put first in the list */
3268 if ((unixdir = tounixpath(ARCHLIB_EXP, Nullch)) != Nullch) {
3269 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3270 p->next = head_PLOC;
3272 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3273 p->dir[NAM$C_MAXRSS] = '\0';
3282 static int vmspipe_file_status = 0;
3283 static char vmspipe_file[NAM$C_MAXRSS+1];
3285 /* already found? Check and use ... need read+execute permission */
3287 if (vmspipe_file_status == 1) {
3288 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
3289 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
3290 return vmspipe_file;
3292 vmspipe_file_status = 0;
3295 /* scan through stored @INC, $^X */
3297 if (vmspipe_file_status == 0) {
3298 char file[NAM$C_MAXRSS+1];
3299 pPLOC p = head_PLOC;
3303 strcpy(file, p->dir);
3304 strncat(file, "vmspipe.com",NAM$C_MAXRSS);
3305 file[NAM$C_MAXRSS] = '\0';
3308 exp_res = do_rmsexpand
3309 (file, vmspipe_file, 0, NULL, PERL_RMSEXPAND_M_VMS);
3310 if (!exp_res) continue;
3312 if (cando_by_name(S_IRUSR, 0, vmspipe_file)
3313 && cando_by_name(S_IXUSR, 0, vmspipe_file)) {
3314 vmspipe_file_status = 1;
3315 return vmspipe_file;
3318 vmspipe_file_status = -1; /* failed, use tempfiles */
3325 vmspipe_tempfile(pTHX)
3327 char file[NAM$C_MAXRSS+1];
3329 static int index = 0;
3333 /* create a tempfile */
3335 /* we can't go from W, shr=get to R, shr=get without
3336 an intermediate vulnerable state, so don't bother trying...
3338 and lib$spawn doesn't shr=put, so have to close the write
3340 So... match up the creation date/time and the FID to
3341 make sure we're dealing with the same file
3346 if (!decc_filename_unix_only) {
3347 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3348 fp = fopen(file,"w");
3350 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3351 fp = fopen(file,"w");
3353 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3354 fp = fopen(file,"w");
3359 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3360 fp = fopen(file,"w");
3362 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3363 fp = fopen(file,"w");
3365 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3366 fp = fopen(file,"w");
3370 if (!fp) return 0; /* we're hosed */
3372 fprintf(fp,"$! 'f$verify(0)'\n");
3373 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3374 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3375 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3376 fprintf(fp,"$ perl_on = \"set noon\"\n");
3377 fprintf(fp,"$ perl_exit = \"exit\"\n");
3378 fprintf(fp,"$ perl_del = \"delete\"\n");
3379 fprintf(fp,"$ pif = \"if\"\n");
3380 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3381 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3382 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3383 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3384 fprintf(fp,"$! --- build command line to get max possible length\n");
3385 fprintf(fp,"$c=perl_popen_cmd0\n");
3386 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3387 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3388 fprintf(fp,"$x=perl_popen_cmd3\n");
3389 fprintf(fp,"$c=c+x\n");
3390 fprintf(fp,"$ perl_on\n");
3391 fprintf(fp,"$ 'c'\n");
3392 fprintf(fp,"$ perl_status = $STATUS\n");
3393 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3394 fprintf(fp,"$ perl_exit 'perl_status'\n");
3397 fgetname(fp, file, 1);
3398 fstat(fileno(fp), (struct stat *)&s0);
3401 if (decc_filename_unix_only)
3402 do_tounixspec(file, file, 0);
3403 fp = fopen(file,"r","shr=get");
3405 fstat(fileno(fp), (struct stat *)&s1);
3407 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3408 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3419 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
3421 static int handler_set_up = FALSE;
3422 unsigned long int sts, flags = CLI$M_NOWAIT;
3423 /* The use of a GLOBAL table (as was done previously) rendered
3424 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
3425 * environment. Hence we've switched to LOCAL symbol table.
3427 unsigned int table = LIB$K_CLI_LOCAL_SYM;
3429 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
3430 char in[512], out[512], err[512], mbx[512];
3432 char tfilebuf[NAM$C_MAXRSS+1];
3434 char cmd_sym_name[20];
3435 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
3436 DSC$K_CLASS_S, symbol};
3437 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
3439 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
3440 DSC$K_CLASS_S, cmd_sym_name};
3441 struct dsc$descriptor_s *vmscmd;
3442 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
3443 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
3444 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
3446 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
3448 /* once-per-program initialization...
3449 note that the SETAST calls and the dual test of pipe_ef
3450 makes sure that only the FIRST thread through here does
3451 the initialization...all other threads wait until it's
3454 Yeah, uglier than a pthread call, it's got all the stuff inline
3455 rather than in a separate routine.
3459 _ckvmssts(sys$setast(0));
3461 unsigned long int pidcode = JPI$_PID;
3462 $DESCRIPTOR(d_delay, RETRY_DELAY);
3463 _ckvmssts(lib$get_ef(&pipe_ef));
3464 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
3465 _ckvmssts(sys$bintim(&d_delay, delaytime));
3467 if (!handler_set_up) {
3468 _ckvmssts(sys$dclexh(&pipe_exitblock));
3469 handler_set_up = TRUE;
3471 _ckvmssts(sys$setast(1));
3474 /* see if we can find a VMSPIPE.COM */
3477 vmspipe = find_vmspipe(aTHX);
3479 strcpy(tfilebuf+1,vmspipe);
3480 } else { /* uh, oh...we're in tempfile hell */
3481 tpipe = vmspipe_tempfile(aTHX);
3482 if (!tpipe) { /* a fish popular in Boston */
3483 if (ckWARN(WARN_PIPE)) {
3484 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
3488 fgetname(tpipe,tfilebuf+1,1);
3490 vmspipedsc.dsc$a_pointer = tfilebuf;
3491 vmspipedsc.dsc$w_length = strlen(tfilebuf);
3493 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
3496 case RMS$_FNF: case RMS$_DNF:
3497 set_errno(ENOENT); break;
3499 set_errno(ENOTDIR); break;
3501 set_errno(ENODEV); break;
3503 set_errno(EACCES); break;
3505 set_errno(EINVAL); break;
3506 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
3507 set_errno(E2BIG); break;
3508 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
3509 _ckvmssts(sts); /* fall through */
3510 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
3513 set_vaxc_errno(sts);
3514 if (*mode != 'n' && ckWARN(WARN_PIPE)) {
3515 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
3521 _ckvmssts(lib$get_vm(&n, &info));
3523 strcpy(mode,in_mode);
3526 info->completion = 0;
3527 info->closing = FALSE;
3534 info->in_done = TRUE;
3535 info->out_done = TRUE;
3536 info->err_done = TRUE;
3537 in[0] = out[0] = err[0] = '\0';
3539 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
3543 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
3548 if (*mode == 'r') { /* piping from subroutine */
3550 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
3552 info->out->pipe_done = &info->out_done;
3553 info->out_done = FALSE;
3554 info->out->info = info;
3556 if (!info->useFILE) {
3557 info->fp = PerlIO_open(mbx, mode);
3559 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
3560 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
3563 if (!info->fp && info->out) {
3564 sys$cancel(info->out->chan_out);
3566 while (!info->out_done) {
3568 _ckvmssts(sys$setast(0));
3569 done = info->out_done;
3570 if (!done) _ckvmssts(sys$clref(pipe_ef));
3571 _ckvmssts(sys$setast(1));
3572 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3575 if (info->out->buf) {
3576 n = info->out->bufsize * sizeof(char);
3577 _ckvmssts(lib$free_vm(&n, &info->out->buf));
3580 _ckvmssts(lib$free_vm(&n, &info->out));
3582 _ckvmssts(lib$free_vm(&n, &info));
3587 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3589 info->err->pipe_done = &info->err_done;
3590 info->err_done = FALSE;
3591 info->err->info = info;
3594 } else if (*mode == 'w') { /* piping to subroutine */
3596 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
3598 info->out->pipe_done = &info->out_done;
3599 info->out_done = FALSE;
3600 info->out->info = info;
3603 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3605 info->err->pipe_done = &info->err_done;
3606 info->err_done = FALSE;
3607 info->err->info = info;
3610 info->in = pipe_tochild_setup(aTHX_ in,mbx);
3611 if (!info->useFILE) {
3612 info->fp = PerlIO_open(mbx, mode);
3614 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
3615 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
3619 info->in->pipe_done = &info->in_done;
3620 info->in_done = FALSE;
3621 info->in->info = info;
3625 if (!info->fp && info->in) {
3627 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
3628 0, 0, 0, 0, 0, 0, 0, 0));
3630 while (!info->in_done) {
3632 _ckvmssts(sys$setast(0));
3633 done = info->in_done;
3634 if (!done) _ckvmssts(sys$clref(pipe_ef));
3635 _ckvmssts(sys$setast(1));
3636 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3639 if (info->in->buf) {
3640 n = info->in->bufsize * sizeof(char);
3641 _ckvmssts(lib$free_vm(&n, &info->in->buf));
3644 _ckvmssts(lib$free_vm(&n, &info->in));
3646 _ckvmssts(lib$free_vm(&n, &info));
3652 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
3653 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
3655 info->out->pipe_done = &info->out_done;
3656 info->out_done = FALSE;
3657 info->out->info = info;
3660 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
3662 info->err->pipe_done = &info->err_done;
3663 info->err_done = FALSE;
3664 info->err->info = info;
3668 symbol[MAX_DCL_SYMBOL] = '\0';
3670 strncpy(symbol, in, MAX_DCL_SYMBOL);
3671 d_symbol.dsc$w_length = strlen(symbol);
3672 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
3674 strncpy(symbol, err, MAX_DCL_SYMBOL);
3675 d_symbol.dsc$w_length = strlen(symbol);
3676 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
3678 strncpy(symbol, out, MAX_DCL_SYMBOL);
3679 d_symbol.dsc$w_length = strlen(symbol);
3680 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table));
3682 p = vmscmd->dsc$a_pointer;
3683 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
3684 if (*p == '$') p++; /* remove leading $ */
3685 while (*p == ' ' || *p == '\t') p++;
3687 for (j = 0; j < 4; j++) {
3688 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
3689 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
3691 strncpy(symbol, p, MAX_DCL_SYMBOL);
3692 d_symbol.dsc$w_length = strlen(symbol);
3693 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
3695 if (strlen(p) > MAX_DCL_SYMBOL) {
3696 p += MAX_DCL_SYMBOL;
3701 _ckvmssts(sys$setast(0));
3702 info->next=open_pipes; /* prepend to list */
3704 _ckvmssts(sys$setast(1));
3705 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
3706 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
3707 * have SYS$COMMAND if we need it.
3709 _ckvmssts(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
3710 0, &info->pid, &info->completion,
3711 0, popen_completion_ast,info,0,0,0));
3713 /* if we were using a tempfile, close it now */
3715 if (tpipe) fclose(tpipe);
3717 /* once the subprocess is spawned, it has copied the symbols and
3718 we can get rid of ours */
3720 for (j = 0; j < 4; j++) {
3721 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
3722 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
3723 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
3725 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
3726 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
3727 _ckvmssts(lib$delete_symbol(&d_sym_out, &table));
3728 vms_execfree(vmscmd);
3730 #ifdef PERL_IMPLICIT_CONTEXT
3733 PL_forkprocess = info->pid;
3738 _ckvmssts(sys$setast(0));
3740 if (!done) _ckvmssts(sys$clref(pipe_ef));
3741 _ckvmssts(sys$setast(1));
3742 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3744 *psts = info->completion;
3745 /* Caller thinks it is open and tries to close it. */
3746 /* This causes some problems, as it changes the error status */
3747 /* my_pclose(info->fp); */
3752 } /* end of safe_popen */
3755 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
3757 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
3761 TAINT_PROPER("popen");
3762 PERL_FLUSHALL_FOR_CHILD;
3763 return safe_popen(aTHX_ cmd,mode,&sts);
3768 /*{{{ I32 my_pclose(PerlIO *fp)*/
3769 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
3771 pInfo info, last = NULL;
3772 unsigned long int retsts;
3775 for (info = open_pipes; info != NULL; last = info, info = info->next)
3776 if (info->fp == fp) break;
3778 if (info == NULL) { /* no such pipe open */
3779 set_errno(ECHILD); /* quoth POSIX */
3780 set_vaxc_errno(SS$_NONEXPR);
3784 /* If we were writing to a subprocess, insure that someone reading from
3785 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
3786 * produce an EOF record in the mailbox.
3788 * well, at least sometimes it *does*, so we have to watch out for
3789 * the first EOF closing the pipe (and DASSGN'ing the channel)...
3793 PerlIO_flush(info->fp); /* first, flush data */
3795 fflush((FILE *)info->fp);
3798 _ckvmssts(sys$setast(0));
3799 info->closing = TRUE;
3800 done = info->done && info->in_done && info->out_done && info->err_done;
3801 /* hanging on write to Perl's input? cancel it */
3802 if (info->mode == 'r' && info->out && !info->out_done) {
3803 if (info->out->chan_out) {
3804 _ckvmssts(sys$cancel(info->out->chan_out));
3805 if (!info->out->chan_in) { /* EOF generation, need AST */
3806 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
3810 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
3811 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3813 _ckvmssts(sys$setast(1));
3816 PerlIO_close(info->fp);
3818 fclose((FILE *)info->fp);
3821 we have to wait until subprocess completes, but ALSO wait until all
3822 the i/o completes...otherwise we'll be freeing the "info" structure
3823 that the i/o ASTs could still be using...
3827 _ckvmssts(sys$setast(0));
3828 done = info->done && info->in_done && info->out_done && info->err_done;
3829 if (!done) _ckvmssts(sys$clref(pipe_ef));
3830 _ckvmssts(sys$setast(1));
3831 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3833 retsts = info->completion;
3835 /* remove from list of open pipes */
3836 _ckvmssts(sys$setast(0));
3837 if (last) last->next = info->next;
3838 else open_pipes = info->next;
3839 _ckvmssts(sys$setast(1));
3841 /* free buffers and structures */
3844 if (info->in->buf) {
3845 n = info->in->bufsize * sizeof(char);
3846 _ckvmssts(lib$free_vm(&n, &info->in->buf));
3849 _ckvmssts(lib$free_vm(&n, &info->in));
3852 if (info->out->buf) {
3853 n = info->out->bufsize * sizeof(char);
3854 _ckvmssts(lib$free_vm(&n, &info->out->buf));
3857 _ckvmssts(lib$free_vm(&n, &info->out));
3860 if (info->err->buf) {
3861 n = info->err->bufsize * sizeof(char);
3862 _ckvmssts(lib$free_vm(&n, &info->err->buf));
3865 _ckvmssts(lib$free_vm(&n, &info->err));
3868 _ckvmssts(lib$free_vm(&n, &info));
3872 } /* end of my_pclose() */
3874 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
3875 /* Roll our own prototype because we want this regardless of whether
3876 * _VMS_WAIT is defined.
3878 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
3880 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
3881 created with popen(); otherwise partially emulate waitpid() unless
3882 we have a suitable one from the CRTL that came with VMS 7.2 and later.
3883 Also check processes not considered by the CRTL waitpid().
3885 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
3887 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
3894 if (statusp) *statusp = 0;
3896 for (info = open_pipes; info != NULL; info = info->next)
3897 if (info->pid == pid) break;
3899 if (info != NULL) { /* we know about this child */
3900 while (!info->done) {
3901 _ckvmssts(sys$setast(0));
3903 if (!done) _ckvmssts(sys$clref(pipe_ef));
3904 _ckvmssts(sys$setast(1));
3905 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
3908 if (statusp) *statusp = info->completion;
3912 /* child that already terminated? */
3914 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
3915 if (closed_list[j].pid == pid) {
3916 if (statusp) *statusp = closed_list[j].completion;
3921 /* fall through if this child is not one of our own pipe children */
3923 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
3925 /* waitpid() became available in the CRTL as of VMS 7.0, but only
3926 * in 7.2 did we get a version that fills in the VMS completion
3927 * status as Perl has always tried to do.
3930 sts = __vms_waitpid( pid, statusp, flags );
3932 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
3935 /* If the real waitpid tells us the child does not exist, we
3936 * fall through here to implement waiting for a child that
3937 * was created by some means other than exec() (say, spawned
3938 * from DCL) or to wait for a process that is not a subprocess
3939 * of the current process.
3942 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
3945 $DESCRIPTOR(intdsc,"0 00:00:01");
3946 unsigned long int ownercode = JPI$_OWNER, ownerpid;
3947 unsigned long int pidcode = JPI$_PID, mypid;
3948 unsigned long int interval[2];
3949 unsigned int jpi_iosb[2];
3950 struct itmlst_3 jpilist[2] = {
3951 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
3956 /* Sorry folks, we don't presently implement rooting around for
3957 the first child we can find, and we definitely don't want to
3958 pass a pid of -1 to $getjpi, where it is a wildcard operation.
3964 /* Get the owner of the child so I can warn if it's not mine. If the
3965 * process doesn't exist or I don't have the privs to look at it,
3966 * I can go home early.
3968 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
3969 if (sts & 1) sts = jpi_iosb[0];
3981 set_vaxc_errno(sts);
3985 if (ckWARN(WARN_EXEC)) {
3986 /* remind folks they are asking for non-standard waitpid behavior */
3987 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
3988 if (ownerpid != mypid)
3989 Perl_warner(aTHX_ packWARN(WARN_EXEC),
3990 "waitpid: process %x is not a child of process %x",
3994 /* simply check on it once a second until it's not there anymore. */
3996 _ckvmssts(sys$bintim(&intdsc,interval));
3997 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
3998 _ckvmssts(sys$schdwk(0,0,interval,0));
3999 _ckvmssts(sys$hiber());
4001 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4006 } /* end of waitpid() */
4011 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4013 my_gconvert(double val, int ndig, int trail, char *buf)
4015 static char __gcvtbuf[DBL_DIG+1];
4018 loc = buf ? buf : __gcvtbuf;
4020 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
4022 sprintf(loc,"%.*g",ndig,val);
4028 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4029 return gcvt(val,ndig,loc);
4032 loc[0] = '0'; loc[1] = '\0';
4039 #if 1 /* defined(__VAX) || !defined(NAML$C_MAXRSS) */
4040 static int rms_free_search_context(struct FAB * fab)
4044 nam = fab->fab$l_nam;
4045 nam->nam$b_nop |= NAM$M_SYNCHK;
4046 nam->nam$l_rlf = NULL;
4048 return sys$parse(fab, NULL, NULL);
4051 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4052 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4053 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4054 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4055 #define rms_nam_esll(nam) nam.nam$b_esl
4056 #define rms_nam_esl(nam) nam.nam$b_esl
4057 #define rms_nam_name(nam) nam.nam$l_name
4058 #define rms_nam_namel(nam) nam.nam$l_name
4059 #define rms_nam_type(nam) nam.nam$l_type
4060 #define rms_nam_typel(nam) nam.nam$l_type
4061 #define rms_nam_ver(nam) nam.nam$l_ver
4062 #define rms_nam_verl(nam) nam.nam$l_ver
4063 #define rms_nam_rsll(nam) nam.nam$b_rsl
4064 #define rms_nam_rsl(nam) nam.nam$b_rsl
4065 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4066 #define rms_set_fna(fab, nam, name, size) \
4067 fab.fab$b_fns = size; fab.fab$l_fna = name;
4068 #define rms_get_fna(fab, nam) fab.fab$l_fna
4069 #define rms_set_dna(fab, nam, name, size) \
4070 fab.fab$b_dns = size; fab.fab$l_dna = name;
4071 #define rms_nam_dns(fab, nam) fab.fab$b_dns;
4072 #define rms_set_esa(fab, nam, name, size) \
4073 nam.nam$b_ess = size; nam.nam$l_esa = name;
4074 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4075 nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;
4076 #define rms_set_rsa(nam, name, size) \
4077 nam.nam$l_rsa = name; nam.nam$b_rss = size;
4078 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4079 nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size;
4082 static int rms_free_search_context(struct FAB * fab)
4086 nam = fab->fab$l_naml;
4087 nam->naml$b_nop |= NAM$M_SYNCHK;
4088 nam->naml$l_rlf = NULL;
4089 nam->naml$l_long_defname_size = 0;
4091 return sys$parse(fab, NULL, NULL);
4094 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4095 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4096 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4097 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4098 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4099 #define rms_nam_esl(nam) nam.naml$b_esl
4100 #define rms_nam_name(nam) nam.naml$l_name
4101 #define rms_nam_namel(nam) nam.naml$l_long_name
4102 #define rms_nam_type(nam) nam.naml$l_type
4103 #define rms_nam_typel(nam) nam.naml$l_long_type
4104 #define rms_nam_ver(nam) nam.naml$l_ver
4105 #define rms_nam_verl(nam) nam.naml$l_long_ver
4106 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4107 #define rms_nam_rsl(nam) nam.naml$b_rsl
4108 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4109 #define rms_set_fna(fab, nam, name, size) \
4110 fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4111 nam.naml$l_long_filename_size = size; \
4112 nam.naml$l_long_filename = name
4113 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4114 #define rms_set_dna(fab, nam, name, size) \
4115 fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4116 nam.naml$l_long_defname_size = size; \
4117 nam.naml$l_long_defname = name
4118 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4119 #define rms_set_esa(fab, nam, name, size) \
4120 nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4121 nam.naml$l_long_expand_alloc = size; \
4122 nam.naml$l_long_expand = name
4123 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4124 nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4125 nam.naml$l_long_expand = l_name; \
4126 nam.naml$l_long_expand_alloc = l_size;
4127 #define rms_set_rsa(nam, name, size) \
4128 nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4129 nam.naml$l_long_result = name; \
4130 nam.naml$l_long_result_alloc = size;
4131 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4132 nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4133 nam.naml$l_long_result = l_name; \
4134 nam.naml$l_long_result_alloc = l_size;
4139 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
4140 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
4141 * to expand file specification. Allows for a single default file
4142 * specification and a simple mask of options. If outbuf is non-NULL,
4143 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
4144 * the resultant file specification is placed. If outbuf is NULL, the
4145 * resultant file specification is placed into a static buffer.
4146 * The third argument, if non-NULL, is taken to be a default file
4147 * specification string. The fourth argument is unused at present.
4148 * rmesexpand() returns the address of the resultant string if
4149 * successful, and NULL on error.
4151 * New functionality for previously unused opts value:
4152 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
4154 static char *mp_do_tounixspec(pTHX_ const char *, char *, int);
4156 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4157 /* ODS-2 only version */
4159 mp_do_rmsexpand(pTHX_ const char *filespec, char *outbuf, int ts, const char *defspec, unsigned opts)
4161 static char __rmsexpand_retbuf[NAM$C_MAXRSS+1];
4162 char vmsfspec[NAM$C_MAXRSS+1], tmpfspec[NAM$C_MAXRSS+1];
4163 char esa[NAM$C_MAXRSS], *cp, *out = NULL;
4164 struct FAB myfab = cc$rms_fab;
4165 struct NAM mynam = cc$rms_nam;
4167 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
4170 if (!filespec || !*filespec) {
4171 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
4175 if (ts) out = Newx(outbuf,NAM$C_MAXRSS+1,char);
4176 else outbuf = __rmsexpand_retbuf;
4178 isunix = is_unix_filespec(filespec);
4180 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) {
4185 filespec = vmsfspec;
4188 myfab.fab$l_fna = (char *)filespec; /* cast ok for read only pointer */
4189 myfab.fab$b_fns = strlen(filespec);
4190 myfab.fab$l_nam = &mynam;
4192 if (defspec && *defspec) {
4193 if (strchr(defspec,'/') != NULL) {
4194 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) {
4201 myfab.fab$l_dna = (char *)defspec; /* cast ok for read only pointer */
4202 myfab.fab$b_dns = strlen(defspec);
4205 mynam.nam$l_esa = esa;
4206 mynam.nam$b_ess = sizeof esa;
4207 mynam.nam$l_rsa = outbuf;
4208 mynam.nam$b_rss = NAM$C_MAXRSS;
4210 #ifdef NAM$M_NO_SHORT_UPCASE
4211 if (decc_efs_case_preserve)
4212 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
4215 retsts = sys$parse(&myfab,0,0);
4216 if (!(retsts & 1)) {
4217 mynam.nam$b_nop |= NAM$M_SYNCHK;
4218 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
4219 retsts = sys$parse(&myfab,0,0);
4220 if (retsts & 1) goto expanded;
4222 mynam.nam$l_rlf = NULL; myfab.fab$b_dns = 0;
4223 sts = sys$parse(&myfab,0,0); /* Free search context */
4224 if (out) Safefree(out);
4225 set_vaxc_errno(retsts);
4226 if (retsts == RMS$_PRV) set_errno(EACCES);
4227 else if (retsts == RMS$_DEV) set_errno(ENODEV);
4228 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
4229 else set_errno(EVMSERR);
4232 retsts = sys$search(&myfab,0,0);
4233 if (!(retsts & 1) && retsts != RMS$_FNF) {
4234 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
4235 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); /* Free search context */
4236 if (out) Safefree(out);
4237 set_vaxc_errno(retsts);
4238 if (retsts == RMS$_PRV) set_errno(EACCES);
4239 else set_errno(EVMSERR);
4243 /* If the input filespec contained any lowercase characters,
4244 * downcase the result for compatibility with Unix-minded code. */
4246 if (!decc_efs_case_preserve) {
4247 for (out = myfab.fab$l_fna; *out; out++)
4248 if (islower(*out)) { haslower = 1; break; }
4250 if (mynam.nam$b_rsl) { out = outbuf; speclen = mynam.nam$b_rsl; }
4251 else { out = esa; speclen = mynam.nam$b_esl; }
4252 /* Trim off null fields added by $PARSE
4253 * If type > 1 char, must have been specified in original or default spec
4254 * (not true for version; $SEARCH may have added version of existing file).
4256 trimver = !(mynam.nam$l_fnb & NAM$M_EXP_VER);
4257 trimtype = !(mynam.nam$l_fnb & NAM$M_EXP_TYPE) &&
4258 (mynam.nam$l_ver - mynam.nam$l_type == 1);
4259 if (trimver || trimtype) {
4260 if (defspec && *defspec) {
4261 char defesa[NAM$C_MAXRSS];
4262 struct FAB deffab = cc$rms_fab;
4263 struct NAM defnam = cc$rms_nam;
4265 deffab.fab$l_nam = &defnam;
4266 /* cast below ok for read only pointer */
4267 deffab.fab$l_fna = (char *)defspec; deffab.fab$b_fns = myfab.fab$b_dns;
4268 defnam.nam$l_esa = defesa; defnam.nam$b_ess = sizeof defesa;
4269 defnam.nam$b_nop = NAM$M_SYNCHK;
4270 #ifdef NAM$M_NO_SHORT_UPCASE
4271 if (decc_efs_case_preserve)
4272 defnam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
4274 if (sys$parse(&deffab,0,0) & 1) {
4275 if (trimver) trimver = !(defnam.nam$l_fnb & NAM$M_EXP_VER);
4276 if (trimtype) trimtype = !(defnam.nam$l_fnb & NAM$M_EXP_TYPE);
4280 if (*mynam.nam$l_ver != '\"')
4281 speclen = mynam.nam$l_ver - out;
4284 /* If we didn't already trim version, copy down */
4285 if (speclen > mynam.nam$l_ver - out)
4286 memmove(mynam.nam$l_type, mynam.nam$l_ver,
4287 speclen - (mynam.nam$l_ver - out));
4288 speclen -= mynam.nam$l_ver - mynam.nam$l_type;
4291 /* If we just had a directory spec on input, $PARSE "helpfully"
4292 * adds an empty name and type for us */
4293 if (mynam.nam$l_name == mynam.nam$l_type &&
4294 mynam.nam$l_ver == mynam.nam$l_type + 1 &&
4295 !(mynam.nam$l_fnb & NAM$M_EXP_NAME))
4296 speclen = mynam.nam$l_name - out;
4298 /* Posix format specifications must have matching quotes */
4299 if (decc_posix_compliant_pathnames && (out[0] == '\"')) {
4300 if ((speclen > 1) && (out[speclen-1] != '\"')) {
4301 out[speclen] = '\"';
4306 out[speclen] = '\0';
4307 if (haslower && !decc_efs_case_preserve) __mystrtolower(out);
4309 /* Have we been working with an expanded, but not resultant, spec? */
4310 /* Also, convert back to Unix syntax if necessary. */
4311 if ((opts & PERL_RMSEXPAND_M_VMS) != 0)
4314 if (!mynam.nam$b_rsl) {
4316 if (do_tounixspec(esa,outbuf,0) == NULL) return NULL;
4318 else strcpy(outbuf,esa);
4321 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) return NULL;
4322 strcpy(outbuf,tmpfspec);
4324 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
4325 mynam.nam$l_rsa = NULL;
4326 mynam.nam$b_rss = 0;
4327 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0); /* Free search context */
4331 /* ODS-5 supporting routine */
4333 mp_do_rmsexpand(pTHX_ const char *filespec, char *outbuf, int ts, const char *defspec, unsigned opts)
4335 static char __rmsexpand_retbuf[NAML$C_MAXRSS+1];
4336 char * vmsfspec, *tmpfspec;
4337 char * esa, *cp, *out = NULL;
4340 struct FAB myfab = cc$rms_fab;
4341 rms_setup_nam(mynam);
4343 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
4346 if (!filespec || !*filespec) {
4347 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
4351 if (ts) out = Newx(outbuf,VMS_MAXRSS,char);
4352 else outbuf = __rmsexpand_retbuf;
4358 isunix = is_unix_filespec(filespec);
4360 Newx(vmsfspec, VMS_MAXRSS, char);
4361 if (do_tovmsspec(filespec,vmsfspec,0) == NULL) {
4367 filespec = vmsfspec;
4369 /* Unless we are forcing to VMS format, a UNIX input means
4370 * UNIX output, and that requires long names to be used
4372 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
4373 opts |= PERL_RMSEXPAND_M_LONG;
4379 rms_set_fna(myfab, mynam, (char *)filespec, strlen(filespec)); /* cast ok */
4380 rms_bind_fab_nam(myfab, mynam);
4382 if (defspec && *defspec) {
4384 t_isunix = is_unix_filespec(defspec);
4386 Newx(tmpfspec, VMS_MAXRSS, char);
4387 if (do_tovmsspec(defspec,tmpfspec,0) == NULL) {
4389 if (vmsfspec != NULL)
4397 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
4400 Newx(esa, NAM$C_MAXRSS + 1, char);
4401 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
4402 Newx(esal, NAML$C_MAXRSS + 1, char);
4404 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, NAML$C_MAXRSS);
4406 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4407 rms_set_rsa(mynam, outbuf, (VMS_MAXRSS - 1));
4410 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
4411 Newx(outbufl, VMS_MAXRSS, char);
4412 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
4414 rms_set_rsa(mynam, outbuf, NAM$C_MAXRSS);
4418 #ifdef NAM$M_NO_SHORT_UPCASE
4419 if (decc_efs_case_preserve)
4420 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
4423 /* First attempt to parse as an existing file */
4424 retsts = sys$parse(&myfab,0,0);
4425 if (!(retsts & STS$K_SUCCESS)) {
4427 /* Could not find the file, try as syntax only if error is not fatal */
4428 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
4429 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
4430 retsts = sys$parse(&myfab,0,0);
4431 if (retsts & STS$K_SUCCESS) goto expanded;
4434 /* Still could not parse the file specification */
4435 /*----------------------------------------------*/
4436 sts = rms_free_search_context(&myfab); /* Free search context */
4437 if (out) Safefree(out);
4438 if (tmpfspec != NULL)
4440 if (vmsfspec != NULL)
4444 set_vaxc_errno(retsts);
4445 if (retsts == RMS$_PRV) set_errno(EACCES);
4446 else if (retsts == RMS$_DEV) set_errno(ENODEV);
4447 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
4448 else set_errno(EVMSERR);
4451 retsts = sys$search(&myfab,0,0);
4452 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
4453 sts = rms_free_search_context(&myfab); /* Free search context */
4454 if (out) Safefree(out);
4455 if (tmpfspec != NULL)
4457 if (vmsfspec != NULL)
4461 set_vaxc_errno(retsts);
4462 if (retsts == RMS$_PRV) set_errno(EACCES);
4463 else set_errno(EVMSERR);
4467 /* If the input filespec contained any lowercase characters,
4468 * downcase the result for compatibility with Unix-minded code. */
4470 if (!decc_efs_case_preserve) {
4471 for (out = rms_get_fna(myfab, mynam); *out; out++)
4472 if (islower(*out)) { haslower = 1; break; }
4475 /* Is a long or a short name expected */
4476 /*------------------------------------*/
4477 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4478 if (rms_nam_rsll(mynam)) {
4480 speclen = rms_nam_rsll(mynam);
4483 out = esal; /* Not esa */
4484 speclen = rms_nam_esll(mynam);
4488 if (rms_nam_rsl(mynam)) {
4490 speclen = rms_nam_rsl(mynam);
4493 out = esa; /* Not esal */
4494 speclen = rms_nam_esl(mynam);
4497 /* Trim off null fields added by $PARSE
4498 * If type > 1 char, must have been specified in original or default spec
4499 * (not true for version; $SEARCH may have added version of existing file).
4501 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
4502 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4503 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
4504 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
4507 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
4508 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
4510 if (trimver || trimtype) {
4511 if (defspec && *defspec) {
4512 char *defesal = NULL;
4513 Newx(defesal, NAML$C_MAXRSS + 1, char);
4514 if (defesal != NULL) {
4515 struct FAB deffab = cc$rms_fab;
4516 rms_setup_nam(defnam);
4518 rms_bind_fab_nam(deffab, defnam);
4522 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
4524 rms_set_esa(deffab, defnam, defesal, VMS_MAXRSS - 1);
4526 rms_set_nam_nop(defnam, 0);
4527 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
4528 #ifdef NAM$M_NO_SHORT_UPCASE
4529 if (decc_efs_case_preserve)
4530 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
4532 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
4534 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
4537 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
4544 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4545 if (*(rms_nam_verl(mynam)) != '\"')
4546 speclen = rms_nam_verl(mynam) - out;
4549 if (*(rms_nam_ver(mynam)) != '\"')
4550 speclen = rms_nam_ver(mynam) - out;
4554 /* If we didn't already trim version, copy down */
4555 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4556 if (speclen > rms_nam_verl(mynam) - out)
4558 (rms_nam_typel(mynam),
4559 rms_nam_verl(mynam),
4560 speclen - (rms_nam_verl(mynam) - out));
4561 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
4564 if (speclen > rms_nam_ver(mynam) - out)
4566 (rms_nam_type(mynam),
4568 speclen - (rms_nam_ver(mynam) - out));
4569 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
4574 /* Done with these copies of the input files */
4575 /*-------------------------------------------*/
4576 if (vmsfspec != NULL)
4578 if (tmpfspec != NULL)
4581 /* If we just had a directory spec on input, $PARSE "helpfully"
4582 * adds an empty name and type for us */
4583 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
4584 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
4585 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
4586 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
4587 speclen = rms_nam_namel(mynam) - out;
4590 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
4591 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
4592 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
4593 speclen = rms_nam_name(mynam) - out;
4596 /* Posix format specifications must have matching quotes */
4597 if (decc_posix_compliant_pathnames && (out[0] == '\"')) {
4598 if ((speclen > 1) && (out[speclen-1] != '\"')) {
4599 out[speclen] = '\"';
4603 out[speclen] = '\0';
4604 if (haslower && !decc_efs_case_preserve) __mystrtolower(out);
4606 /* Have we been working with an expanded, but not resultant, spec? */
4607 /* Also, convert back to Unix syntax if necessary. */
4609 if (!rms_nam_rsll(mynam)) {
4611 if (do_tounixspec(esa,outbuf,0) == NULL) {
4617 else strcpy(outbuf,esa);
4620 Newx(tmpfspec, VMS_MAXRSS, char);
4621 if (do_tounixspec(outbuf,tmpfspec,0) == NULL) {
4627 strcpy(outbuf,tmpfspec);
4631 rms_set_rsal(mynam, NULL, 0, NULL, 0);
4632 sts = rms_free_search_context(&myfab); /* Free search context */
4639 /* External entry points */
4640 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
4641 { return do_rmsexpand(spec,buf,0,def,opt); }
4642 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
4643 { return do_rmsexpand(spec,buf,1,def,opt); }
4647 ** The following routines are provided to make life easier when
4648 ** converting among VMS-style and Unix-style directory specifications.
4649 ** All will take input specifications in either VMS or Unix syntax. On
4650 ** failure, all return NULL. If successful, the routines listed below
4651 ** return a pointer to a buffer containing the appropriately
4652 ** reformatted spec (and, therefore, subsequent calls to that routine
4653 ** will clobber the result), while the routines of the same names with
4654 ** a _ts suffix appended will return a pointer to a mallocd string
4655 ** containing the appropriately reformatted spec.
4656 ** In all cases, only explicit syntax is altered; no check is made that
4657 ** the resulting string is valid or that the directory in question
4660 ** fileify_dirspec() - convert a directory spec into the name of the
4661 ** directory file (i.e. what you can stat() to see if it's a dir).
4662 ** The style (VMS or Unix) of the result is the same as the style
4663 ** of the parameter passed in.
4664 ** pathify_dirspec() - convert a directory spec into a path (i.e.
4665 ** what you prepend to a filename to indicate what directory it's in).
4666 ** The style (VMS or Unix) of the result is the same as the style
4667 ** of the parameter passed in.
4668 ** tounixpath() - convert a directory spec into a Unix-style path.
4669 ** tovmspath() - convert a directory spec into a VMS-style path.
4670 ** tounixspec() - convert any file spec into a Unix-style file spec.
4671 ** tovmsspec() - convert any file spec into a VMS-style spec.
4673 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
4674 ** Permission is given to distribute this code as part of the Perl
4675 ** standard distribution under the terms of the GNU General Public
4676 ** License or the Perl Artistic License. Copies of each may be
4677 ** found in the Perl standard distribution.
4680 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf)*/
4681 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts)
4683 static char __fileify_retbuf[VMS_MAXRSS];
4684 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
4685 char *retspec, *cp1, *cp2, *lastdir;
4686 char *trndir, *vmsdir;
4687 unsigned short int trnlnm_iter_count;
4690 if (!dir || !*dir) {
4691 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
4693 dirlen = strlen(dir);
4694 while (dirlen && dir[dirlen-1] == '/') --dirlen;
4695 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
4696 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
4703 if (dirlen > (VMS_MAXRSS - 1)) {
4704 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
4707 Newx(trndir, VMS_MAXRSS + 1, char);
4708 if (!strpbrk(dir+1,"/]>:") &&
4709 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
4710 strcpy(trndir,*dir == '/' ? dir + 1: dir);
4711 trnlnm_iter_count = 0;
4712 while (!strpbrk(trndir,"/]>:>") && my_trnlnm(trndir,trndir,0)) {
4713 trnlnm_iter_count++;
4714 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
4716 dirlen = strlen(trndir);
4719 strncpy(trndir,dir,dirlen);
4720 trndir[dirlen] = '\0';
4723 /* At this point we are done with *dir and use *trndir which is a
4724 * copy that can be modified. *dir must not be modified.
4727 /* If we were handed a rooted logical name or spec, treat it like a
4728 * simple directory, so that
4729 * $ Define myroot dev:[dir.]
4730 * ... do_fileify_dirspec("myroot",buf,1) ...
4731 * does something useful.
4733 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
4734 trndir[--dirlen] = '\0';
4735 trndir[dirlen-1] = ']';
4737 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
4738 trndir[--dirlen] = '\0';
4739 trndir[dirlen-1] = '>';
4742 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
4743 /* If we've got an explicit filename, we can just shuffle the string. */
4744 if (*(cp1+1)) hasfilename = 1;
4745 /* Similarly, we can just back up a level if we've got multiple levels
4746 of explicit directories in a VMS spec which ends with directories. */
4748 for (cp2 = cp1; cp2 > trndir; cp2--) {
4750 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
4751 /* fix-me, can not scan EFS file specs backward like this */
4752 *cp2 = *cp1; *cp1 = '\0';
4757 if (*cp2 == '[' || *cp2 == '<') break;
4762 Newx(vmsdir, VMS_MAXRSS + 1, char);
4763 cp1 = strpbrk(trndir,"]:>");
4764 if (hasfilename || !cp1) { /* Unix-style path or filename */
4765 if (trndir[0] == '.') {
4766 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
4769 return do_fileify_dirspec("[]",buf,ts);
4771 else if (trndir[1] == '.' &&
4772 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
4775 return do_fileify_dirspec("[-]",buf,ts);
4778 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
4779 dirlen -= 1; /* to last element */
4780 lastdir = strrchr(trndir,'/');
4782 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
4783 /* If we have "/." or "/..", VMSify it and let the VMS code
4784 * below expand it, rather than repeating the code to handle
4785 * relative components of a filespec here */
4787 if (*(cp1+2) == '.') cp1++;
4788 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
4790 if (do_tovmsspec(trndir,vmsdir,0) == NULL) {
4795 if (strchr(vmsdir,'/') != NULL) {
4796 /* If do_tovmsspec() returned it, it must have VMS syntax
4797 * delimiters in it, so it's a mixed VMS/Unix spec. We take
4798 * the time to check this here only so we avoid a recursion
4799 * loop; otherwise, gigo.
4803 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
4806 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) {
4811 ret_chr = do_tounixspec(trndir,buf,ts);
4817 } while ((cp1 = strstr(cp1,"/.")) != NULL);
4818 lastdir = strrchr(trndir,'/');
4820 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
4822 /* Ditto for specs that end in an MFD -- let the VMS code
4823 * figure out whether it's a real device or a rooted logical. */
4825 /* This should not happen any more. Allowing the fake /000000
4826 * in a UNIX pathname causes all sorts of problems when trying
4827 * to run in UNIX emulation. So the VMS to UNIX conversions
4828 * now remove the fake /000000 directories.
4831 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
4832 if (do_tovmsspec(trndir,vmsdir,0) == NULL) {
4837 if (do_fileify_dirspec(vmsdir,trndir,0) == NULL) {
4842 ret_chr = do_tounixspec(trndir,buf,ts);
4849 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
4850 !(lastdir = cp1 = strrchr(trndir,']')) &&
4851 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
4852 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
4855 /* For EFS or ODS-5 look for the last dot */
4856 if (decc_efs_charset) {
4857 cp2 = strrchr(cp1,'.');
4859 if (vms_process_case_tolerant) {
4860 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
4861 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
4862 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
4863 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4864 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4865 (ver || *cp3)))))) {
4869 set_vaxc_errno(RMS$_DIR);
4874 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
4875 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
4876 !*(cp2+3) || *(cp2+3) != 'R' ||
4877 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
4878 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
4879 (ver || *cp3)))))) {
4883 set_vaxc_errno(RMS$_DIR);
4887 dirlen = cp2 - trndir;
4891 retlen = dirlen + 6;
4892 if (buf) retspec = buf;
4893 else if (ts) Newx(retspec,retlen+1,char);
4894 else retspec = __fileify_retbuf;
4895 memcpy(retspec,trndir,dirlen);
4896 retspec[dirlen] = '\0';
4898 /* We've picked up everything up to the directory file name.
4899 Now just add the type and version, and we're set. */
4900 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
4901 strcat(retspec,".dir;1");
4903 strcat(retspec,".DIR;1");
4908 else { /* VMS-style directory spec */
4910 char *esa, term, *cp;
4911 unsigned long int sts, cmplen, haslower = 0;
4912 unsigned int nam_fnb;
4914 struct FAB dirfab = cc$rms_fab;
4915 rms_setup_nam(savnam);
4916 rms_setup_nam(dirnam);
4918 Newx(esa, VMS_MAXRSS + 1, char);
4919 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
4920 rms_bind_fab_nam(dirfab, dirnam);
4921 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
4922 rms_set_esa(dirfab, dirnam, esa, (VMS_MAXRSS - 1));
4923 #ifdef NAM$M_NO_SHORT_UPCASE
4924 if (decc_efs_case_preserve)
4925 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
4928 for (cp = trndir; *cp; cp++)
4929 if (islower(*cp)) { haslower = 1; break; }
4930 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
4931 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
4932 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
4933 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
4940 set_vaxc_errno(dirfab.fab$l_sts);
4946 /* Does the file really exist? */
4947 if (sys$search(&dirfab)& STS$K_SUCCESS) {
4948 /* Yes; fake the fnb bits so we'll check type below */
4949 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
4951 else { /* No; just work with potential name */
4952 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
4957 set_errno(EVMSERR); set_vaxc_errno(dirfab.fab$l_sts);
4958 sts = rms_free_search_context(&dirfab);
4963 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
4964 cp1 = strchr(esa,']');
4965 if (!cp1) cp1 = strchr(esa,'>');
4966 if (cp1) { /* Should always be true */
4967 rms_nam_esll(dirnam) -= cp1 - esa - 1;
4968 memmove(esa,cp1 + 1, rms_nam_esll(dirnam));
4971 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
4972 /* Yep; check version while we're at it, if it's there. */
4973 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
4974 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
4975 /* Something other than .DIR[;1]. Bzzt. */
4976 sts = rms_free_search_context(&dirfab);
4981 set_vaxc_errno(RMS$_DIR);
4985 esa[rms_nam_esll(dirnam)] = '\0';
4986 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
4987 /* They provided at least the name; we added the type, if necessary, */
4988 if (buf) retspec = buf; /* in sys$parse() */
4989 else if (ts) Newx(retspec, rms_nam_esll(dirnam)+1, char);
4990 else retspec = __fileify_retbuf;
4991 strcpy(retspec,esa);
4992 sts = rms_free_search_context(&dirfab);
4998 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
4999 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
5001 rms_nam_esll(dirnam) -= 9;
5003 if ((cp1 = strrchr(esa,']')) == NULL) cp1 = strrchr(esa,'>');
5004 if (cp1 == NULL) { /* should never happen */
5005 sts = rms_free_search_context(&dirfab);
5013 retlen = strlen(esa);
5014 cp1 = strrchr(esa,'.');
5015 /* ODS-5 directory specifications can have extra "." in them. */
5016 /* Fix-me, can not scan EFS file specifications backwards */
5017 while (cp1 != NULL) {
5018 if ((cp1-1 == esa) || (*(cp1-1) != '^'))
5022 while ((cp1 > esa) && (*cp1 != '.'))
5029 if ((cp1) != NULL) {
5030 /* There's more than one directory in the path. Just roll back. */
5032 if (buf) retspec = buf;
5033 else if (ts) Newx(retspec,retlen+7,char);
5034 else retspec = __fileify_retbuf;
5035 strcpy(retspec,esa);
5038 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
5039 /* Go back and expand rooted logical name */
5040 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
5041 #ifdef NAM$M_NO_SHORT_UPCASE
5042 if (decc_efs_case_preserve)
5043 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5045 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
5046 sts = rms_free_search_context(&dirfab);
5051 set_vaxc_errno(dirfab.fab$l_sts);
5054 retlen = rms_nam_esll(dirnam) - 9; /* esa - '][' - '].DIR;1' */
5055 if (buf) retspec = buf;
5056 else if (ts) Newx(retspec,retlen+16,char);
5057 else retspec = __fileify_retbuf;
5058 cp1 = strstr(esa,"][");
5059 if (!cp1) cp1 = strstr(esa,"]<");
5061 memcpy(retspec,esa,dirlen);
5062 if (!strncmp(cp1+2,"000000]",7)) {
5063 retspec[dirlen-1] = '\0';
5064 /* fix-me Not full ODS-5, just extra dots in directories for now */
5065 cp1 = retspec + dirlen - 1;
5066 while (cp1 > retspec)
5071 if (*(cp1-1) != '^')
5076 if (*cp1 == '.') *cp1 = ']';
5078 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
5079 memmove(cp1+1,"000000]",7);
5083 memmove(retspec+dirlen,cp1+2,retlen-dirlen);
5084 retspec[retlen] = '\0';
5085 /* Convert last '.' to ']' */
5086 cp1 = retspec+retlen-1;
5087 while (*cp != '[') {
5090 /* Do not trip on extra dots in ODS-5 directories */
5091 if ((cp1 == retspec) || (*(cp1-1) != '^'))
5095 if (*cp1 == '.') *cp1 = ']';
5097 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
5098 memmove(cp1+1,"000000]",7);
5102 else { /* This is a top-level dir. Add the MFD to the path. */
5103 if (buf) retspec = buf;
5104 else if (ts) Newx(retspec,retlen+16,char);
5105 else retspec = __fileify_retbuf;
5108 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
5109 strcpy(cp2,":[000000]");
5114 sts = rms_free_search_context(&dirfab);
5115 /* We've set up the string up through the filename. Add the
5116 type and version, and we're done. */
5117 strcat(retspec,".DIR;1");
5119 /* $PARSE may have upcased filespec, so convert output to lower
5120 * case if input contained any lowercase characters. */
5121 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec);
5127 } /* end of do_fileify_dirspec() */
5129 /* External entry points */
5130 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
5131 { return do_fileify_dirspec(dir,buf,0); }
5132 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
5133 { return do_fileify_dirspec(dir,buf,1); }
5135 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
5136 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts)
5138 static char __pathify_retbuf[VMS_MAXRSS];
5139 unsigned long int retlen;
5140 char *retpath, *cp1, *cp2, *trndir;
5141 unsigned short int trnlnm_iter_count;
5145 if (!dir || !*dir) {
5146 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5149 Newx(trndir, VMS_MAXRSS, char);
5150 if (*dir) strcpy(trndir,dir);
5151 else getcwd(trndir,VMS_MAXRSS - 1);
5153 trnlnm_iter_count = 0;
5154 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
5155 && my_trnlnm(trndir,trndir,0)) {
5156 trnlnm_iter_count++;
5157 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5158 trnlen = strlen(trndir);
5160 /* Trap simple rooted lnms, and return lnm:[000000] */
5161 if (!strcmp(trndir+trnlen-2,".]")) {
5162 if (buf) retpath = buf;
5163 else if (ts) Newx(retpath,strlen(dir)+10,char);
5164 else retpath = __pathify_retbuf;
5165 strcpy(retpath,dir);
5166 strcat(retpath,":[000000]");
5172 /* At this point we do not work with *dir, but the copy in
5173 * *trndir that is modifiable.
5176 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
5177 if (*trndir == '.' && (*(trndir+1) == '\0' ||
5178 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
5179 retlen = 2 + (*(trndir+1) != '\0');
5181 if ( !(cp1 = strrchr(trndir,'/')) &&
5182 !(cp1 = strrchr(trndir,']')) &&
5183 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
5184 if ((cp2 = strchr(cp1,'.')) != NULL &&
5185 (*(cp2-1) != '/' || /* Trailing '.', '..', */
5186 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
5187 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
5188 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
5191 /* For EFS or ODS-5 look for the last dot */
5192 if (decc_efs_charset) {
5193 cp2 = strrchr(cp1,'.');
5195 if (vms_process_case_tolerant) {
5196 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5197 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5198 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5199 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5200 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5201 (ver || *cp3)))))) {
5204 set_vaxc_errno(RMS$_DIR);
5209 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5210 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5211 !*(cp2+3) || *(cp2+3) != 'R' ||
5212 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5213 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5214 (ver || *cp3)))))) {
5217 set_vaxc_errno(RMS$_DIR);
5221 retlen = cp2 - trndir + 1;
5223 else { /* No file type present. Treat the filename as a directory. */
5224 retlen = strlen(trndir) + 1;
5227 if (buf) retpath = buf;
5228 else if (ts) Newx(retpath,retlen+1,char);
5229 else retpath = __pathify_retbuf;
5230 strncpy(retpath, trndir, retlen-1);
5231 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
5232 retpath[retlen-1] = '/'; /* with '/', add it. */
5233 retpath[retlen] = '\0';
5235 else retpath[retlen-1] = '\0';
5237 else { /* VMS-style directory spec */
5239 unsigned long int sts, cmplen, haslower;
5240 struct FAB dirfab = cc$rms_fab;
5242 rms_setup_nam(savnam);
5243 rms_setup_nam(dirnam);
5245 /* If we've got an explicit filename, we can just shuffle the string. */
5246 if ( ( (cp1 = strrchr(trndir,']')) != NULL ||
5247 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) {
5248 if ((cp2 = strchr(cp1,'.')) != NULL) {
5250 if (vms_process_case_tolerant) {
5251 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5252 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5253 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5254 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5255 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5256 (ver || *cp3)))))) {
5259 set_vaxc_errno(RMS$_DIR);
5264 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5265 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5266 !*(cp2+3) || *(cp2+3) != 'R' ||
5267 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5268 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5269 (ver || *cp3)))))) {
5272 set_vaxc_errno(RMS$_DIR);
5277 else { /* No file type, so just draw name into directory part */
5278 for (cp2 = cp1; *cp2; cp2++) ;
5281 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
5283 /* We've now got a VMS 'path'; fall through */
5286 dirlen = strlen(trndir);
5287 if (trndir[dirlen-1] == ']' ||
5288 trndir[dirlen-1] == '>' ||
5289 trndir[dirlen-1] == ':') { /* It's already a VMS 'path' */
5290 if (buf) retpath = buf;
5291 else if (ts) Newx(retpath,strlen(trndir)+1,char);
5292 else retpath = __pathify_retbuf;
5293 strcpy(retpath,trndir);
5297 rms_set_fna(dirfab, dirnam, trndir, dirlen);
5298 Newx(esa, VMS_MAXRSS, char);
5299 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
5300 rms_bind_fab_nam(dirfab, dirnam);
5301 rms_set_esa(dirfab, dirnam, esa, VMS_MAXRSS - 1);
5302 #ifdef NAM$M_NO_SHORT_UPCASE
5303 if (decc_efs_case_preserve)
5304 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5307 for (cp = trndir; *cp; cp++)
5308 if (islower(*cp)) { haslower = 1; break; }
5310 if (!(sts = (sys$parse(&dirfab)& STS$K_SUCCESS))) {
5311 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
5312 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
5313 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
5319 set_vaxc_errno(dirfab.fab$l_sts);
5325 /* Does the file really exist? */
5326 if (!(sys$search(&dirfab)&STS$K_SUCCESS)) {
5327 if (dirfab.fab$l_sts != RMS$_FNF) {
5329 sts1 = rms_free_search_context(&dirfab);
5333 set_vaxc_errno(dirfab.fab$l_sts);
5336 dirnam = savnam; /* No; just work with potential name */
5339 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
5340 /* Yep; check version while we're at it, if it's there. */
5341 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
5342 if (strncmp(rms_nam_typel(dirnam),".DIR;1",cmplen)) {
5344 /* Something other than .DIR[;1]. Bzzt. */
5345 sts2 = rms_free_search_context(&dirfab);
5349 set_vaxc_errno(RMS$_DIR);
5353 /* OK, the type was fine. Now pull any file name into the
5355 if ((cp1 = strrchr(esa,']'))) *(rms_nam_typel(dirnam)) = ']';
5357 cp1 = strrchr(esa,'>');
5358 *(rms_nam_typel(dirnam)) = '>';
5361 *(rms_nam_typel(dirnam) + 1) = '\0';
5362 retlen = (rms_nam_typel(dirnam)) - esa + 2;
5363 if (buf) retpath = buf;
5364 else if (ts) Newx(retpath,retlen,char);
5365 else retpath = __pathify_retbuf;
5366 strcpy(retpath,esa);
5368 sts = rms_free_search_context(&dirfab);
5369 /* $PARSE may have upcased filespec, so convert output to lower
5370 * case if input contained any lowercase characters. */
5371 if (haslower && !decc_efs_case_preserve) __mystrtolower(retpath);
5376 } /* end of do_pathify_dirspec() */
5378 /* External entry points */
5379 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
5380 { return do_pathify_dirspec(dir,buf,0); }
5381 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
5382 { return do_pathify_dirspec(dir,buf,1); }
5384 /*{{{ char *tounixspec[_ts](char *spec, char *buf)*/
5385 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts)
5387 static char __tounixspec_retbuf[VMS_MAXRSS];
5388 char *dirend, *rslt, *cp1, *cp3, *tmp;
5390 int devlen, dirlen, retlen = VMS_MAXRSS;
5391 int expand = 1; /* guarantee room for leading and trailing slashes */
5392 unsigned short int trnlnm_iter_count;
5395 if (spec == NULL) return NULL;
5396 if (strlen(spec) > NAM$C_MAXRSS) return NULL;
5397 if (buf) rslt = buf;
5399 retlen = strlen(spec);
5400 cp1 = strchr(spec,'[');
5401 if (!cp1) cp1 = strchr(spec,'<');
5403 for (cp1++; *cp1; cp1++) {
5404 if (*cp1 == '-') expand++; /* VMS '-' ==> Unix '../' */
5405 if (*cp1 == '.' && *(cp1+1) == '.' && *(cp1+2) == '.')
5406 { expand++; cp1 +=2; } /* VMS '...' ==> Unix '/.../' */
5409 Newx(rslt,retlen+2+2*expand,char);
5411 else rslt = __tounixspec_retbuf;
5413 /* New VMS specific format needs translation
5414 * glob passes filenames with trailing '\n' and expects this preserved.
5416 if (decc_posix_compliant_pathnames) {
5417 if (strncmp(spec, "\"^UP^", 5) == 0) {
5423 Newx(tunix, VMS_MAXRSS + 1,char);
5424 strcpy(tunix, spec);
5425 tunix_len = strlen(tunix);
5427 if (tunix[tunix_len - 1] == '\n') {
5428 tunix[tunix_len - 1] = '\"';
5429 tunix[tunix_len] = '\0';
5433 uspec = decc$translate_vms(tunix);
5435 if ((int)uspec > 0) {
5441 /* If we can not translate it, makemaker wants as-is */
5449 cmp_rslt = 0; /* Presume VMS */
5450 cp1 = strchr(spec, '/');
5454 /* Look for EFS ^/ */
5455 if (decc_efs_charset) {
5456 while (cp1 != NULL) {
5459 /* Found illegal VMS, assume UNIX */
5464 cp1 = strchr(cp1, '/');
5468 /* Look for "." and ".." */
5469 if (decc_filename_unix_report) {
5470 if (spec[0] == '.') {
5471 if ((spec[1] == '\0') || (spec[1] == '\n')) {
5475 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
5481 /* This is already UNIX or at least nothing VMS understands */
5489 dirend = strrchr(spec,']');
5490 if (dirend == NULL) dirend = strrchr(spec,'>');
5491 if (dirend == NULL) dirend = strchr(spec,':');
5492 if (dirend == NULL) {
5497 /* Special case 1 - sys$posix_root = / */
5498 #if __CRTL_VER >= 70000000
5499 if (!decc_disable_posix_root) {
5500 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
5508 /* Special case 2 - Convert NLA0: to /dev/null */
5509 #if __CRTL_VER < 70000000
5510 cmp_rslt = strncmp(spec,"NLA0:", 5);
5512 cmp_rslt = strncmp(spec,"nla0:", 5);
5514 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
5516 if (cmp_rslt == 0) {
5517 strcpy(rslt, "/dev/null");
5520 if (spec[6] != '\0') {
5527 /* Also handle special case "SYS$SCRATCH:" */
5528 #if __CRTL_VER < 70000000
5529 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
5531 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
5533 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
5535 Newx(tmp, VMS_MAXRSS, char);
5536 if (cmp_rslt == 0) {
5539 islnm = my_trnlnm(tmp, "TMP", 0);
5541 strcpy(rslt, "/tmp");
5544 if (spec[12] != '\0') {
5552 if (*cp2 != '[' && *cp2 != '<') {
5555 else { /* the VMS spec begins with directories */
5557 if (*cp2 == ']' || *cp2 == '>') {
5558 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
5562 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
5563 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
5564 if (ts) Safefree(rslt);
5568 trnlnm_iter_count = 0;
5571 while (*cp3 != ':' && *cp3) cp3++;
5573 if (strchr(cp3,']') != NULL) break;
5574 trnlnm_iter_count++;
5575 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
5576 } while (vmstrnenv(tmp,tmp,0,fildev,0));
5578 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
5579 retlen = devlen + dirlen;
5580 Renew(rslt,retlen+1+2*expand,char);
5586 *(cp1++) = *(cp3++);
5587 if (cp1 - rslt > (VMS_MAXRSS - 1) && !ts && !buf) {
5589 return NULL; /* No room */
5594 if ((*cp2 == '^')) {
5595 /* EFS file escape, pass the next character as is */
5596 /* Fix me: HEX encoding for UNICODE not implemented */
5599 else if ( *cp2 == '.') {
5600 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
5601 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
5608 for (; cp2 <= dirend; cp2++) {
5609 if ((*cp2 == '^')) {
5610 /* EFS file escape, pass the next character as is */
5611 /* Fix me: HEX encoding for UNICODE not implemented */
5617 if (*(cp2+1) == '[') cp2++;
5619 else if (*cp2 == ']' || *cp2 == '>') {
5620 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
5622 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
5624 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
5625 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
5626 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
5627 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
5628 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
5630 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
5631 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
5635 else if (*cp2 == '-') {
5636 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
5637 while (*cp2 == '-') {
5639 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
5641 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
5642 if (ts) Safefree(rslt); /* filespecs like */
5643 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
5647 else *(cp1++) = *cp2;
5649 else *(cp1++) = *cp2;
5651 while (*cp2) *(cp1++) = *(cp2++);
5654 /* This still leaves /000000/ when working with a
5655 * VMS device root or concealed root.
5661 ulen = strlen(rslt);
5663 /* Get rid of "000000/ in rooted filespecs */
5665 zeros = strstr(rslt, "/000000/");
5666 if (zeros != NULL) {
5668 mlen = ulen - (zeros - rslt) - 7;
5669 memmove(zeros, &zeros[7], mlen);
5678 } /* end of do_tounixspec() */
5680 /* External entry points */
5681 char *Perl_tounixspec(pTHX_ const char *spec, char *buf) { return do_tounixspec(spec,buf,0); }
5682 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf) { return do_tounixspec(spec,buf,1); }
5684 #if __CRTL_VER >= 80200000 && !defined(__VAX)
5686 static int posix_to_vmsspec
5687 (char *vmspath, int vmspath_len, const char *unixpath) {
5689 struct FAB myfab = cc$rms_fab;
5690 struct NAML mynam = cc$rms_naml;
5691 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5692 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5698 /* If not a posix spec already, convert it */
5700 unixlen = strlen(unixpath);
5705 if (strncmp(unixpath,"\"^UP^",5) != 0) {
5706 sprintf(vmspath,"\"^UP^%s\"",unixpath);
5709 /* This is already a VMS specification, no conversion */
5711 strncpy(vmspath,unixpath, vmspath_len);
5713 vmspath[vmspath_len] = 0;
5714 if (unixpath[unixlen - 1] == '/')
5716 Newx(esa, VMS_MAXRSS, char);
5717 myfab.fab$l_fna = vmspath;
5718 myfab.fab$b_fns = strlen(vmspath);
5719 myfab.fab$l_naml = &mynam;
5720 mynam.naml$l_esa = NULL;
5721 mynam.naml$b_ess = 0;
5722 mynam.naml$l_long_expand = esa;
5723 mynam.naml$l_long_expand_alloc = (unsigned char) VMS_MAXRSS - 1;
5724 mynam.naml$l_rsa = NULL;
5725 mynam.naml$b_rss = 0;
5726 if (decc_efs_case_preserve)
5727 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
5728 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
5730 /* Set up the remaining naml fields */
5731 sts = sys$parse(&myfab);
5733 /* It failed! Try again as a UNIX filespec */
5739 /* get the Device ID and the FID */
5740 sts = sys$search(&myfab);
5741 /* on any failure, returned the POSIX ^UP^ filespec */
5746 specdsc.dsc$a_pointer = vmspath;
5747 specdsc.dsc$w_length = vmspath_len;
5749 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
5750 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
5751 sts = lib$fid_to_name
5752 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
5754 /* on any failure, returned the POSIX ^UP^ filespec */
5756 /* This can happen if user does not have permission to read directories */
5757 if (strncmp(unixpath,"\"^UP^",5) != 0)
5758 sprintf(vmspath,"\"^UP^%s\"",unixpath);
5760 strcpy(vmspath, unixpath);
5763 vmspath[specdsc.dsc$w_length] = 0;
5765 /* Are we expecting a directory? */
5766 if (dir_flag != 0) {
5772 i = specdsc.dsc$w_length - 1;
5776 /* Version must be '1' */
5777 if (vmspath[i--] != '1')
5779 /* Version delimiter is one of ".;" */
5780 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
5783 if (vmspath[i--] != 'R')
5785 if (vmspath[i--] != 'I')
5787 if (vmspath[i--] != 'D')
5789 if (vmspath[i--] != '.')
5791 eptr = &vmspath[i+1];
5793 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
5794 if (vmspath[i-1] != '^') {
5802 /* Get rid of 6 imaginary zero directory filename */
5803 vmspath[i+1] = '\0';
5807 if (vmspath[i] == '0')
5821 /* Can not use LIB$FID_TO_NAME, so doing a manual conversion */
5822 static int posix_to_vmsspec_hardway
5823 (char *vmspath, int vmspath_len, const char *unixpath) {
5826 const char *unixptr;
5828 const char *lastslash;
5829 const char *lastdot;
5840 /* Ignore leading "/" characters */
5841 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
5844 unixlen = strlen(unixptr);
5846 /* Do nothing with blank paths */
5852 lastslash = strrchr(unixptr,'/');
5853 lastdot = strrchr(unixptr,'.');
5856 /* last dot is last dot or past end of string */
5857 if (lastdot == NULL)
5858 lastdot = unixptr + unixlen;
5860 /* if no directories, set last slash to beginning of string */
5861 if (lastslash == NULL) {
5862 lastslash = unixptr;
5865 /* Watch out for trailing "." after last slash, still a directory */
5866 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
5867 lastslash = unixptr + unixlen;
5870 /* Watch out for traiing ".." after last slash, still a directory */
5871 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
5872 lastslash = unixptr + unixlen;
5875 /* dots in directories are aways escaped */
5876 if (lastdot < lastslash)
5877 lastdot = unixptr + unixlen;
5880 /* if (unixptr < lastslash) then we are in a directory */
5888 /* This could have a "^UP^ on the front */
5889 if (strncmp(unixptr,"\"^UP^",5) == 0) {
5894 /* Start with the UNIX path */
5895 if (*unixptr != '/') {
5896 /* relative paths */
5897 if (lastslash > unixptr) {
5900 /* skip leading ./ */
5902 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
5908 /* Are we still in a directory? */
5909 if (unixptr <= lastslash) {
5914 /* if not backing up, then it is relative forward. */
5915 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
5916 ((unixptr[2] == '/') || (unixptr[2] == '\0')))) {
5924 /* Perl wants an empty directory here to tell the difference
5925 * between a DCL commmand and a filename
5934 /* Handle two special files . and .. */
5935 if (unixptr[0] == '.') {
5936 if (unixptr[1] == '\0') {
5943 if ((unixptr[1] == '.') && (unixptr[2] == '\0')) {
5954 else { /* Absolute PATH handling */
5958 /* Need to find out where root is */
5960 /* In theory, this procedure should never get an absolute POSIX pathname
5961 * that can not be found on the POSIX root.
5962 * In practice, that can not be relied on, and things will show up
5963 * here that are a VMS device name or concealed logical name instead.
5964 * So to make things work, this procedure must be tolerant.
5966 Newx(esa, vmspath_len, char);
5969 nextslash = strchr(&unixptr[1],'/');
5971 if (nextslash != NULL) {
5972 seg_len = nextslash - &unixptr[1];
5973 strncpy(vmspath, unixptr, seg_len + 1);
5974 vmspath[seg_len+1] = 0;
5975 sts = posix_to_vmsspec(esa, vmspath_len, vmspath);
5979 /* This is verified to be a real path */
5981 sts = posix_to_vmsspec(esa, vmspath_len, "/");
5982 strcpy(vmspath, esa);
5983 vmslen = strlen(vmspath);
5984 vmsptr = vmspath + vmslen;
5986 if (unixptr < lastslash) {
5995 cmp = strcmp(rptr,"000000.");
6000 } /* removing 6 zeros */
6001 } /* vmslen < 7, no 6 zeros possible */
6002 } /* Not in a directory */
6003 } /* end of verified real path handling */
6008 /* Ok, we have a device or a concealed root that is not in POSIX
6009 * or we have garbage. Make the best of it.
6012 /* Posix to VMS destroyed this, so copy it again */
6013 strncpy(vmspath, &unixptr[1], seg_len);
6014 vmspath[seg_len] = 0;
6016 vmsptr = &vmsptr[vmslen];
6019 /* Now do we need to add the fake 6 zero directory to it? */
6021 if ((*lastslash == '/') && (nextslash < lastslash)) {
6022 /* No there is another directory */
6028 /* now we have foo:bar or foo:[000000]bar to decide from */
6029 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
6030 trnend = islnm ? islnm - 1 : 0;
6032 /* if this was a logical name, ']' or '>' must be present */
6033 /* if not a logical name, then assume a device and hope. */
6034 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
6036 /* if log name and trailing '.' then rooted - treat as device */
6037 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
6039 /* Fix me, if not a logical name, a device lookup should be
6040 * done to see if the device is file structured. If the device
6041 * is not file structured, the 6 zeros should not be put on.
6043 * As it is, perl is occasionally looking for dev:[000000]tty.
6044 * which looks a little strange.
6047 if ((add_6zero == 0) && (*nextslash == '/') && (nextslash[1] == '\0')) {
6048 /* No real directory present */
6053 /* Put the device delimiter on */
6056 unixptr = nextslash;
6059 /* Start directory if needed */
6060 if (!islnm || add_6zero) {
6066 /* add fake 000000] if needed */
6079 } /* non-POSIX translation */
6081 } /* End of relative/absolute path handling */
6083 while ((*unixptr) && (vmslen < vmspath_len)){
6088 if (dir_start != 0) {
6090 /* First characters in a directory are handled special */
6091 while ((*unixptr == '/') ||
6092 ((*unixptr == '.') &&
6093 ((unixptr[1]=='.') || (unixptr[1]=='/') || (unixptr[1]=='\0')))) {
6098 /* Skip redundant / in specification */
6099 while ((*unixptr == '/') && (dir_start != 0)) {
6102 if (unixptr == lastslash)
6105 if (unixptr == lastslash)
6108 /* Skip redundant ./ characters */
6109 while ((*unixptr == '.') &&
6110 ((unixptr[1] == '/')||(unixptr[1] == '\0'))) {
6113 if (unixptr == lastslash)
6115 if (*unixptr == '/')
6118 if (unixptr == lastslash)
6121 /* Skip redundant ../ characters */
6122 while ((*unixptr == '.') && (unixptr[1] == '.') &&
6123 ((unixptr[2] == '/') || (unixptr[2] == '\0'))) {
6124 /* Set the backing up flag */
6130 unixptr++; /* first . */
6131 unixptr++; /* second . */
6132 if (unixptr == lastslash)
6134 if (*unixptr == '/') /* The slash */
6137 if (unixptr == lastslash)
6140 /* To do: Perl expects /.../ to be translated to [...] on VMS */
6141 /* Not needed when VMS is pretending to be UNIX. */
6143 /* Is this loop stuck because of too many dots? */
6144 if (loop_flag == 0) {
6145 /* Exit the loop and pass the rest through */
6150 /* Are we done with directories yet? */
6151 if (unixptr >= lastslash) {
6153 /* Watch out for trailing dots */
6162 if (*unixptr == '/')
6166 /* Have we stopped backing up? */
6171 /* dir_start continues to be = 1 */
6173 if (*unixptr == '-') {
6175 *vmsptr++ = *unixptr++;
6179 /* Now are we done with directories yet? */
6180 if (unixptr >= lastslash) {
6182 /* Watch out for trailing dots */
6198 if (*unixptr == '\0')
6201 /* Normal characters - More EFS work probably needed */
6207 /* remove multiple / */
6208 while (unixptr[1] == '/') {
6211 if (unixptr == lastslash) {
6212 /* Watch out for trailing dots */
6224 /* To do: Perl expects /.../ to be translated to [...] on VMS */
6225 /* Not needed when VMS is pretending to be UNIX. */
6229 if (*unixptr != '\0')
6245 if ((unixptr < lastdot) || (unixptr[1] == '\0')) {
6251 /* trailing dot ==> '^..' on VMS */
6252 if (*unixptr == '\0') {
6256 *vmsptr++ = *unixptr++;
6259 if (quoted && (unixptr[1] == '\0')) {
6264 *vmsptr++ = *unixptr++;
6271 *vmsptr++ = *unixptr++;
6275 if (*unixptr != '\0') {
6276 *vmsptr++ = *unixptr++;
6283 /* Make sure directory is closed */
6284 if (unixptr == lastslash) {
6286 vmsptr2 = vmsptr - 1;
6288 if (*vmsptr2 != ']') {
6291 /* directories do not end in a dot bracket */
6292 if (*vmsptr2 == '.') {
6296 if (*vmsptr2 != '^') {
6297 vmsptr--; /* back up over the dot */
6305 /* Add a trailing dot if a file with no extension */
6306 vmsptr2 = vmsptr - 1;
6307 if ((*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
6308 (*lastdot != '.')) {
6319 /*{{{ char *tovmsspec[_ts](char *path, char *buf)*/
6320 static char *mp_do_tovmsspec(pTHX_ const char *path, char *buf, int ts) {
6321 static char __tovmsspec_retbuf[VMS_MAXRSS];
6322 char *rslt, *dirend;
6327 unsigned long int infront = 0, hasdir = 1;
6331 if (path == NULL) return NULL;
6332 rslt_len = VMS_MAXRSS;
6333 if (buf) rslt = buf;
6334 else if (ts) Newx(rslt, VMS_MAXRSS, char);
6335 else rslt = __tovmsspec_retbuf;
6336 if (strpbrk(path,"]:>") ||
6337 (dirend = strrchr(path,'/')) == NULL) {
6338 if (path[0] == '.') {
6339 if (path[1] == '\0') strcpy(rslt,"[]");
6340 else if (path[1] == '.' && path[2] == '\0') strcpy(rslt,"[-]");
6341 else strcpy(rslt,path); /* probably garbage */
6343 else strcpy(rslt,path);
6347 /* Posix specifications are now a native VMS format */
6348 /*--------------------------------------------------*/
6349 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6350 if (decc_posix_compliant_pathnames) {
6351 if (strncmp(path,"\"^UP^",5) == 0) {
6352 posix_to_vmsspec_hardway(rslt, rslt_len, path);
6358 vms_delim = strpbrk(path,"]:>");
6360 if ((vms_delim != NULL) ||
6361 ((dirend = strrchr(path,'/')) == NULL)) {
6363 /* VMS special characters found! */
6365 if (path[0] == '.') {
6366 if (path[1] == '\0') strcpy(rslt,"[]");
6367 else if (path[1] == '.' && path[2] == '\0')
6370 /* Dot preceeding a device or directory ? */
6372 /* If not in POSIX mode, pass it through and hope it works */
6373 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6374 if (!decc_posix_compliant_pathnames)
6375 strcpy(rslt,path); /* probably garbage */
6377 posix_to_vmsspec_hardway(rslt, rslt_len, path);
6379 strcpy(rslt,path); /* probably garbage */
6385 /* If no VMS characters and in POSIX mode, convert it!
6386 * This is the easiest way to get directory specifications
6387 * handled correctly in POSIX mode
6389 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6390 if ((vms_delim == NULL) && decc_posix_compliant_pathnames)
6391 posix_to_vmsspec_hardway(rslt, rslt_len, path);
6393 /* No unix path separators - presume VMS already */
6397 strcpy(rslt,path); /* probably garbage */
6403 /* If POSIX mode active, handle the conversion */
6404 #if __CRTL_VER >= 80200000 && !defined(__VAX)
6405 if (decc_posix_compliant_pathnames) {
6406 posix_to_vmsspec_hardway(rslt, rslt_len, path);
6411 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
6412 if (!*(dirend+2)) dirend +=2;
6413 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
6414 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
6419 lastdot = strrchr(cp2,'.');
6425 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
6427 if (decc_disable_posix_root) {
6428 strcpy(rslt,"sys$disk:[000000]");
6431 strcpy(rslt,"sys$posix_root:[000000]");
6435 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
6437 Newx(trndev, VMS_MAXRSS, char);
6438 islnm = my_trnlnm(rslt,trndev,0);
6440 /* DECC special handling */
6442 if (strcmp(rslt,"bin") == 0) {
6443 strcpy(rslt,"sys$system");
6446 islnm = my_trnlnm(rslt,trndev,0);
6448 else if (strcmp(rslt,"tmp") == 0) {
6449 strcpy(rslt,"sys$scratch");
6452 islnm = my_trnlnm(rslt,trndev,0);
6454 else if (!decc_disable_posix_root) {
6455 strcpy(rslt, "sys$posix_root");
6459 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
6460 islnm = my_trnlnm(rslt,trndev,0);
6462 else if (strcmp(rslt,"dev") == 0) {
6463 if (strncmp(cp2,"/null", 5) == 0) {
6464 if ((cp2[5] == 0) || (cp2[5] == '/')) {
6465 strcpy(rslt,"NLA0");
6469 islnm = my_trnlnm(rslt,trndev,0);
6475 trnend = islnm ? strlen(trndev) - 1 : 0;
6476 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
6477 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
6478 /* If the first element of the path is a logical name, determine
6479 * whether it has to be translated so we can add more directories. */
6480 if (!islnm || rooted) {
6483 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
6487 if (cp2 != dirend) {
6488 strcpy(rslt,trndev);
6489 cp1 = rslt + trnend;
6496 if (decc_disable_posix_root) {
6507 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
6508 cp2 += 2; /* skip over "./" - it's redundant */
6509 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
6511 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
6512 *(cp1++) = '-'; /* "../" --> "-" */
6515 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
6516 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
6517 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
6518 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
6521 else if ((cp2 != lastdot) || (lastdot < dirend)) {
6522 /* Escape the extra dots in EFS file specifications */
6525 if (cp2 > dirend) cp2 = dirend;
6527 else *(cp1++) = '.';
6529 for (; cp2 < dirend; cp2++) {
6531 if (*(cp2-1) == '/') continue;
6532 if (*(cp1-1) != '.') *(cp1++) = '.';
6535 else if (!infront && *cp2 == '.') {
6536 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
6537 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
6538 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
6539 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
6540 else if (*(cp1-2) == '[') *(cp1-1) = '-';
6541 else { /* back up over previous directory name */
6543 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
6544 if (*(cp1-1) == '[') {
6545 memcpy(cp1,"000000.",7);
6550 if (cp2 == dirend) break;
6552 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
6553 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
6554 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
6555 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
6557 *(cp1++) = '.'; /* Simulate trailing '/' */
6558 cp2 += 2; /* for loop will incr this to == dirend */
6560 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
6563 if (decc_efs_charset == 0)
6564 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
6566 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
6572 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
6574 if (decc_efs_charset == 0)
6581 else *(cp1++) = *cp2;
6585 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
6586 if (hasdir) *(cp1++) = ']';
6587 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
6588 /* fixme for ODS5 */
6603 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
6604 decc_readdir_dropdotnotype) {
6609 /* trailing dot ==> '^..' on VMS */
6616 *(cp1++) = *(cp2++);
6644 *(cp1++) = *(cp2++);
6647 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
6648 * which is wrong. UNIX notation should be ".dir." unless
6649 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
6650 * changing this behavior could break more things at this time.
6651 * efs character set effectively does not allow "." to be a version
6652 * delimiter as a further complication about changing this.
6654 if (decc_filename_unix_report != 0) {
6657 *(cp1++) = *(cp2++);
6660 *(cp1++) = *(cp2++);
6663 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
6667 /* Fix me for "^]", but that requires making sure that you do
6668 * not back up past the start of the filename
6670 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
6677 } /* end of do_tovmsspec() */
6679 /* External entry points */
6680 char *Perl_tovmsspec(pTHX_ const char *path, char *buf) { return do_tovmsspec(path,buf,0); }
6681 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf) { return do_tovmsspec(path,buf,1); }
6683 /*{{{ char *tovmspath[_ts](char *path, char *buf)*/
6684 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts) {
6685 static char __tovmspath_retbuf[VMS_MAXRSS];
6687 char *pathified, *vmsified, *cp;
6689 if (path == NULL) return NULL;
6690 Newx(pathified, VMS_MAXRSS, char);
6691 if (do_pathify_dirspec(path,pathified,0) == NULL) {
6692 Safefree(pathified);
6695 Newx(vmsified, VMS_MAXRSS, char);
6696 if (do_tovmsspec(pathified,buf ? buf : vmsified,0) == NULL) {
6697 Safefree(pathified);
6701 Safefree(pathified);
6707 vmslen = strlen(vmsified);
6708 Newx(cp,vmslen+1,char);
6709 memcpy(cp,vmsified,vmslen);
6715 strcpy(__tovmspath_retbuf,vmsified);
6717 return __tovmspath_retbuf;
6720 } /* end of do_tovmspath() */
6722 /* External entry points */
6723 char *Perl_tovmspath(pTHX_ const char *path, char *buf) { return do_tovmspath(path,buf,0); }
6724 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf) { return do_tovmspath(path,buf,1); }
6727 /*{{{ char *tounixpath[_ts](char *path, char *buf)*/
6728 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts) {
6729 static char __tounixpath_retbuf[VMS_MAXRSS];
6731 char *pathified, *unixified, *cp;
6733 if (path == NULL) return NULL;
6734 Newx(pathified, VMS_MAXRSS, char);
6735 if (do_pathify_dirspec(path,pathified,0) == NULL) {
6736 Safefree(pathified);
6739 Newx(unixified, VMS_MAXRSS, char);
6740 if (do_tounixspec(pathified,buf ? buf : unixified,0) == NULL) {
6741 Safefree(pathified);
6742 Safefree(unixified);
6745 Safefree(pathified);
6747 Safefree(unixified);
6751 unixlen = strlen(unixified);
6752 Newx(cp,unixlen+1,char);
6753 memcpy(cp,unixified,unixlen);
6755 Safefree(unixified);
6759 strcpy(__tounixpath_retbuf,unixified);
6760 Safefree(unixified);
6761 return __tounixpath_retbuf;
6764 } /* end of do_tounixpath() */
6766 /* External entry points */
6767 char *Perl_tounixpath(pTHX_ const char *path, char *buf) { return do_tounixpath(path,buf,0); }
6768 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf) { return do_tounixpath(path,buf,1); }
6771 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark@infocomm.com)
6773 *****************************************************************************
6775 * Copyright (C) 1989-1994 by *
6776 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
6778 * Permission is hereby granted for the reproduction of this software, *
6779 * on condition that this copyright notice is included in the reproduction, *
6780 * and that such reproduction is not for purposes of profit or material *
6783 * 27-Aug-1994 Modified for inclusion in perl5 *
6784 * by Charles Bailey bailey@newman.upenn.edu *
6785 *****************************************************************************
6789 * getredirection() is intended to aid in porting C programs
6790 * to VMS (Vax-11 C). The native VMS environment does not support
6791 * '>' and '<' I/O redirection, or command line wild card expansion,
6792 * or a command line pipe mechanism using the '|' AND background
6793 * command execution '&'. All of these capabilities are provided to any
6794 * C program which calls this procedure as the first thing in the
6796 * The piping mechanism will probably work with almost any 'filter' type
6797 * of program. With suitable modification, it may useful for other
6798 * portability problems as well.
6800 * Author: Mark Pizzolato mark@infocomm.com
6804 struct list_item *next;
6808 static void add_item(struct list_item **head,
6809 struct list_item **tail,
6813 static void mp_expand_wild_cards(pTHX_ char *item,
6814 struct list_item **head,
6815 struct list_item **tail,
6818 static int background_process(pTHX_ int argc, char **argv);
6820 static void pipe_and_fork(pTHX_ char **cmargv);
6822 /*{{{ void getredirection(int *ac, char ***av)*/
6824 mp_getredirection(pTHX_ int *ac, char ***av)
6826 * Process vms redirection arg's. Exit if any error is seen.
6827 * If getredirection() processes an argument, it is erased
6828 * from the vector. getredirection() returns a new argc and argv value.
6829 * In the event that a background command is requested (by a trailing "&"),
6830 * this routine creates a background subprocess, and simply exits the program.
6832 * Warning: do not try to simplify the code for vms. The code
6833 * presupposes that getredirection() is called before any data is
6834 * read from stdin or written to stdout.
6836 * Normal usage is as follows:
6842 * getredirection(&argc, &argv);
6846 int argc = *ac; /* Argument Count */
6847 char **argv = *av; /* Argument Vector */
6848 char *ap; /* Argument pointer */
6849 int j; /* argv[] index */
6850 int item_count = 0; /* Count of Items in List */
6851 struct list_item *list_head = 0; /* First Item in List */
6852 struct list_item *list_tail; /* Last Item in List */
6853 char *in = NULL; /* Input File Name */
6854 char *out = NULL; /* Output File Name */
6855 char *outmode = "w"; /* Mode to Open Output File */
6856 char *err = NULL; /* Error File Name */
6857 char *errmode = "w"; /* Mode to Open Error File */
6858 int cmargc = 0; /* Piped Command Arg Count */
6859 char **cmargv = NULL;/* Piped Command Arg Vector */
6862 * First handle the case where the last thing on the line ends with
6863 * a '&'. This indicates the desire for the command to be run in a
6864 * subprocess, so we satisfy that desire.
6867 if (0 == strcmp("&", ap))
6868 exit(background_process(aTHX_ --argc, argv));
6869 if (*ap && '&' == ap[strlen(ap)-1])
6871 ap[strlen(ap)-1] = '\0';
6872 exit(background_process(aTHX_ argc, argv));
6875 * Now we handle the general redirection cases that involve '>', '>>',
6876 * '<', and pipes '|'.
6878 for (j = 0; j < argc; ++j)
6880 if (0 == strcmp("<", argv[j]))
6884 fprintf(stderr,"No input file after < on command line");
6885 exit(LIB$_WRONUMARG);
6890 if ('<' == *(ap = argv[j]))
6895 if (0 == strcmp(">", ap))
6899 fprintf(stderr,"No output file after > on command line");
6900 exit(LIB$_WRONUMARG);
6919 fprintf(stderr,"No output file after > or >> on command line");
6920 exit(LIB$_WRONUMARG);
6924 if (('2' == *ap) && ('>' == ap[1]))
6941 fprintf(stderr,"No output file after 2> or 2>> on command line");
6942 exit(LIB$_WRONUMARG);
6946 if (0 == strcmp("|", argv[j]))
6950 fprintf(stderr,"No command into which to pipe on command line");
6951 exit(LIB$_WRONUMARG);
6953 cmargc = argc-(j+1);
6954 cmargv = &argv[j+1];
6958 if ('|' == *(ap = argv[j]))
6966 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
6969 * Allocate and fill in the new argument vector, Some Unix's terminate
6970 * the list with an extra null pointer.
6972 Newx(argv, item_count+1, char *);
6973 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
6975 for (j = 0; j < item_count; ++j, list_head = list_head->next)
6976 argv[j] = list_head->value;
6982 fprintf(stderr,"'|' and '>' may not both be specified on command line");
6983 exit(LIB$_INVARGORD);
6985 pipe_and_fork(aTHX_ cmargv);
6988 /* Check for input from a pipe (mailbox) */
6990 if (in == NULL && 1 == isapipe(0))
6992 char mbxname[L_tmpnam];
6994 long int dvi_item = DVI$_DEVBUFSIZ;
6995 $DESCRIPTOR(mbxnam, "");
6996 $DESCRIPTOR(mbxdevnam, "");
6998 /* Input from a pipe, reopen it in binary mode to disable */
6999 /* carriage control processing. */
7001 fgetname(stdin, mbxname);
7002 mbxnam.dsc$a_pointer = mbxname;
7003 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
7004 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
7005 mbxdevnam.dsc$a_pointer = mbxname;
7006 mbxdevnam.dsc$w_length = sizeof(mbxname);
7007 dvi_item = DVI$_DEVNAM;
7008 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
7009 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
7012 freopen(mbxname, "rb", stdin);
7015 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
7019 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
7021 fprintf(stderr,"Can't open input file %s as stdin",in);
7024 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
7026 fprintf(stderr,"Can't open output file %s as stdout",out);
7029 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
7032 if (strcmp(err,"&1") == 0) {
7033 dup2(fileno(stdout), fileno(stderr));
7034 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
7037 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
7039 fprintf(stderr,"Can't open error file %s as stderr",err);
7043 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
7047 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
7050 #ifdef ARGPROC_DEBUG
7051 PerlIO_printf(Perl_debug_log, "Arglist:\n");
7052 for (j = 0; j < *ac; ++j)
7053 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
7055 /* Clear errors we may have hit expanding wildcards, so they don't
7056 show up in Perl's $! later */
7057 set_errno(0); set_vaxc_errno(1);
7058 } /* end of getredirection() */
7061 static void add_item(struct list_item **head,
7062 struct list_item **tail,
7068 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
7072 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
7073 *tail = (*tail)->next;
7075 (*tail)->value = value;
7079 static void mp_expand_wild_cards(pTHX_ char *item,
7080 struct list_item **head,
7081 struct list_item **tail,
7085 unsigned long int context = 0;
7093 $DESCRIPTOR(filespec, "");
7094 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
7095 $DESCRIPTOR(resultspec, "");
7096 unsigned long int lff_flags = 0;
7100 #ifdef VMS_LONGNAME_SUPPORT
7101 lff_flags = LIB$M_FIL_LONG_NAMES;
7104 for (cp = item; *cp; cp++) {
7105 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
7106 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
7108 if (!*cp || isspace(*cp))
7110 add_item(head, tail, item, count);
7115 /* "double quoted" wild card expressions pass as is */
7116 /* From DCL that means using e.g.: */
7117 /* perl program """perl.*""" */
7118 item_len = strlen(item);
7119 if ( '"' == *item && '"' == item[item_len-1] )
7122 item[item_len-2] = '\0';
7123 add_item(head, tail, item, count);
7127 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
7128 resultspec.dsc$b_class = DSC$K_CLASS_D;
7129 resultspec.dsc$a_pointer = NULL;
7130 Newx(vmsspec, VMS_MAXRSS, char);
7131 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
7132 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0);
7133 if (!isunix || !filespec.dsc$a_pointer)
7134 filespec.dsc$a_pointer = item;
7135 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
7137 * Only return version specs, if the caller specified a version
7139 had_version = strchr(item, ';');
7141 * Only return device and directory specs, if the caller specifed either.
7143 had_device = strchr(item, ':');
7144 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
7146 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
7147 (&filespec, &resultspec, &context,
7148 &defaultspec, 0, &rms_sts, &lff_flags)))
7153 Newx(string,resultspec.dsc$w_length+1,char);
7154 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
7155 string[resultspec.dsc$w_length] = '\0';
7156 if (NULL == had_version)
7157 *(strrchr(string, ';')) = '\0';
7158 if ((!had_directory) && (had_device == NULL))
7160 if (NULL == (devdir = strrchr(string, ']')))
7161 devdir = strrchr(string, '>');
7162 strcpy(string, devdir + 1);
7165 * Be consistent with what the C RTL has already done to the rest of
7166 * the argv items and lowercase all of these names.
7168 if (!decc_efs_case_preserve) {
7169 for (c = string; *c; ++c)
7173 if (isunix) trim_unixpath(string,item,1);
7174 add_item(head, tail, string, count);
7178 if (sts != RMS$_NMF)
7180 set_vaxc_errno(sts);
7183 case RMS$_FNF: case RMS$_DNF:
7184 set_errno(ENOENT); break;
7186 set_errno(ENOTDIR); break;
7188 set_errno(ENODEV); break;
7189 case RMS$_FNM: case RMS$_SYN:
7190 set_errno(EINVAL); break;
7192 set_errno(EACCES); break;
7194 _ckvmssts_noperl(sts);
7198 add_item(head, tail, item, count);
7199 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
7200 _ckvmssts_noperl(lib$find_file_end(&context));
7203 static int child_st[2];/* Event Flag set when child process completes */
7205 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
7207 static unsigned long int exit_handler(int *status)
7211 if (0 == child_st[0])
7213 #ifdef ARGPROC_DEBUG
7214 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
7216 fflush(stdout); /* Have to flush pipe for binary data to */
7217 /* terminate properly -- <tp@mccall.com> */
7218 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
7219 sys$dassgn(child_chan);
7221 sys$synch(0, child_st);
7226 static void sig_child(int chan)
7228 #ifdef ARGPROC_DEBUG
7229 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
7231 if (child_st[0] == 0)
7235 static struct exit_control_block exit_block =
7240 &exit_block.exit_status,
7245 pipe_and_fork(pTHX_ char **cmargv)
7248 struct dsc$descriptor_s *vmscmd;
7249 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
7250 int sts, j, l, ismcr, quote, tquote = 0;
7252 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
7253 vms_execfree(vmscmd);
7258 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
7259 && toupper(*(q+2)) == 'R' && !*(q+3);
7261 while (q && l < MAX_DCL_LINE_LENGTH) {
7263 if (j > 0 && quote) {
7269 if (ismcr && j > 1) quote = 1;
7270 tquote = (strchr(q,' ')) != NULL || *q == '\0';
7273 if (quote || tquote) {
7279 if ((quote||tquote) && *q == '"') {
7289 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
7291 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
7295 static int background_process(pTHX_ int argc, char **argv)
7297 char command[MAX_DCL_SYMBOL + 1] = "$";
7298 $DESCRIPTOR(value, "");
7299 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
7300 static $DESCRIPTOR(null, "NLA0:");
7301 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
7303 $DESCRIPTOR(pidstr, "");
7305 unsigned long int flags = 17, one = 1, retsts;
7308 strcat(command, argv[0]);
7309 len = strlen(command);
7310 while (--argc && (len < MAX_DCL_SYMBOL))
7312 strcat(command, " \"");
7313 strcat(command, *(++argv));
7314 strcat(command, "\"");
7315 len = strlen(command);
7317 value.dsc$a_pointer = command;
7318 value.dsc$w_length = strlen(value.dsc$a_pointer);
7319 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
7320 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
7321 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
7322 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
7325 _ckvmssts_noperl(retsts);
7327 #ifdef ARGPROC_DEBUG
7328 PerlIO_printf(Perl_debug_log, "%s\n", command);
7330 sprintf(pidstring, "%08X", pid);
7331 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
7332 pidstr.dsc$a_pointer = pidstring;
7333 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
7334 lib$set_symbol(&pidsymbol, &pidstr);
7338 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
7341 /* OS-specific initialization at image activation (not thread startup) */
7342 /* Older VAXC header files lack these constants */
7343 #ifndef JPI$_RIGHTS_SIZE
7344 # define JPI$_RIGHTS_SIZE 817
7346 #ifndef KGB$M_SUBSYSTEM
7347 # define KGB$M_SUBSYSTEM 0x8
7350 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
7352 /*{{{void vms_image_init(int *, char ***)*/
7354 vms_image_init(int *argcp, char ***argvp)
7356 char eqv[LNM$C_NAMLENGTH+1] = "";
7357 unsigned int len, tabct = 8, tabidx = 0;
7358 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
7359 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
7360 unsigned short int dummy, rlen;
7361 struct dsc$descriptor_s **tabvec;
7362 #if defined(PERL_IMPLICIT_CONTEXT)
7365 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
7366 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
7367 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
7370 #ifdef KILL_BY_SIGPRC
7371 Perl_csighandler_init();
7374 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
7375 _ckvmssts_noperl(iosb[0]);
7376 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
7377 if (iprv[i]) { /* Running image installed with privs? */
7378 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
7383 /* Rights identifiers might trigger tainting as well. */
7384 if (!will_taint && (rlen || rsz)) {
7385 while (rlen < rsz) {
7386 /* We didn't get all the identifiers on the first pass. Allocate a
7387 * buffer much larger than $GETJPI wants (rsz is size in bytes that
7388 * were needed to hold all identifiers at time of last call; we'll
7389 * allocate that many unsigned long ints), and go back and get 'em.
7390 * If it gave us less than it wanted to despite ample buffer space,
7391 * something's broken. Is your system missing a system identifier?
7393 if (rsz <= jpilist[1].buflen) {
7394 /* Perl_croak accvios when used this early in startup. */
7395 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
7396 rsz, (unsigned long) jpilist[1].buflen,
7397 "Check your rights database for corruption.\n");
7400 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
7401 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
7402 jpilist[1].buflen = rsz * sizeof(unsigned long int);
7403 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
7404 _ckvmssts_noperl(iosb[0]);
7406 mask = jpilist[1].bufadr;
7407 /* Check attribute flags for each identifier (2nd longword); protected
7408 * subsystem identifiers trigger tainting.
7410 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
7411 if (mask[i] & KGB$M_SUBSYSTEM) {
7416 if (mask != rlst) Safefree(mask);
7419 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
7420 * logical, some versions of the CRTL will add a phanthom /000000/
7421 * directory. This needs to be removed.
7423 if (decc_filename_unix_report) {
7426 ulen = strlen(argvp[0][0]);
7428 zeros = strstr(argvp[0][0], "/000000/");
7429 if (zeros != NULL) {
7431 mlen = ulen - (zeros - argvp[0][0]) - 7;
7432 memmove(zeros, &zeros[7], mlen);
7434 argvp[0][0][ulen] = '\0';
7437 /* It also may have a trailing dot that needs to be removed otherwise
7438 * it will be converted to VMS mode incorrectly.
7441 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
7442 argvp[0][0][ulen] = '\0';
7445 /* We need to use this hack to tell Perl it should run with tainting,
7446 * since its tainting flag may be part of the PL_curinterp struct, which
7447 * hasn't been allocated when vms_image_init() is called.
7450 char **newargv, **oldargv;
7452 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
7453 newargv[0] = oldargv[0];
7454 newargv[1] = (char *) PerlMem_malloc(3 * sizeof(char));
7455 strcpy(newargv[1], "-T");
7456 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
7458 newargv[*argcp] = NULL;
7459 /* We orphan the old argv, since we don't know where it's come from,
7460 * so we don't know how to free it.
7464 else { /* Did user explicitly request tainting? */
7466 char *cp, **av = *argvp;
7467 for (i = 1; i < *argcp; i++) {
7468 if (*av[i] != '-') break;
7469 for (cp = av[i]+1; *cp; cp++) {
7470 if (*cp == 'T') { will_taint = 1; break; }
7471 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
7472 strchr("DFIiMmx",*cp)) break;
7474 if (will_taint) break;
7479 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
7481 if (!tabidx) tabvec = (struct dsc$descriptor_s **) PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
7482 else if (tabidx >= tabct) {
7484 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
7486 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
7487 tabvec[tabidx]->dsc$w_length = 0;
7488 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
7489 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
7490 tabvec[tabidx]->dsc$a_pointer = NULL;
7491 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
7493 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
7495 getredirection(argcp,argvp);
7496 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
7498 # include <reentrancy.h>
7499 decc$set_reentrancy(C$C_MULTITHREAD);
7508 * Trim Unix-style prefix off filespec, so it looks like what a shell
7509 * glob expansion would return (i.e. from specified prefix on, not
7510 * full path). Note that returned filespec is Unix-style, regardless
7511 * of whether input filespec was VMS-style or Unix-style.
7513 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
7514 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
7515 * vector of options; at present, only bit 0 is used, and if set tells
7516 * trim unixpath to try the current default directory as a prefix when
7517 * presented with a possibly ambiguous ... wildcard.
7519 * Returns !=0 on success, with trimmed filespec replacing contents of
7520 * fspec, and 0 on failure, with contents of fpsec unchanged.
7522 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
7524 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
7526 char *unixified, *unixwild,
7527 *template, *base, *end, *cp1, *cp2;
7528 register int tmplen, reslen = 0, dirs = 0;
7530 Newx(unixwild, VMS_MAXRSS, char);
7531 if (!wildspec || !fspec) return 0;
7532 template = unixwild;
7533 if (strpbrk(wildspec,"]>:") != NULL) {
7534 if (do_tounixspec(wildspec,unixwild,0) == NULL) {
7540 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
7541 unixwild[VMS_MAXRSS-1] = 0;
7543 Newx(unixified, VMS_MAXRSS, char);
7544 if (strpbrk(fspec,"]>:") != NULL) {
7545 if (do_tounixspec(fspec,unixified,0) == NULL) {
7547 Safefree(unixified);
7550 else base = unixified;
7551 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
7552 * check to see that final result fits into (isn't longer than) fspec */
7553 reslen = strlen(fspec);
7557 /* No prefix or absolute path on wildcard, so nothing to remove */
7558 if (!*template || *template == '/') {
7560 if (base == fspec) {
7561 Safefree(unixified);
7564 tmplen = strlen(unixified);
7565 if (tmplen > reslen) {
7566 Safefree(unixified);
7567 return 0; /* not enough space */
7569 /* Copy unixified resultant, including trailing NUL */
7570 memmove(fspec,unixified,tmplen+1);
7571 Safefree(unixified);
7575 for (end = base; *end; end++) ; /* Find end of resultant filespec */
7576 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
7577 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
7578 for (cp1 = end ;cp1 >= base; cp1--)
7579 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
7581 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
7582 Safefree(unixified);
7588 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
7589 int ells = 1, totells, segdirs, match;
7590 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
7591 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7593 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
7595 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
7596 Newx(tpl, VMS_MAXRSS, char);
7597 if (ellipsis == template && opts & 1) {
7598 /* Template begins with an ellipsis. Since we can't tell how many
7599 * directory names at the front of the resultant to keep for an
7600 * arbitrary starting point, we arbitrarily choose the current
7601 * default directory as a starting point. If it's there as a prefix,
7602 * clip it off. If not, fall through and act as if the leading
7603 * ellipsis weren't there (i.e. return shortest possible path that
7604 * could match template).
7606 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
7608 Safefree(unixified);
7612 if (!decc_efs_case_preserve) {
7613 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
7614 if (_tolower(*cp1) != _tolower(*cp2)) break;
7616 segdirs = dirs - totells; /* Min # of dirs we must have left */
7617 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
7618 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
7619 memmove(fspec,cp2+1,end - cp2);
7620 Safefree(unixified);
7626 /* First off, back up over constant elements at end of path */
7628 for (front = end ; front >= base; front--)
7629 if (*front == '/' && !dirs--) { front++; break; }
7631 Newx(lcres, VMS_MAXRSS, char);
7632 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
7634 if (!decc_efs_case_preserve) {
7635 *cp2 = _tolower(*cp1); /* Make lc copy for match */
7642 Safefree(unixified);
7646 return 0; /* Path too long. */
7649 *cp2 = '\0'; /* Pick up with memcpy later */
7650 lcfront = lcres + (front - base);
7651 /* Now skip over each ellipsis and try to match the path in front of it. */
7653 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
7654 if (*(cp1) == '.' && *(cp1+1) == '.' &&
7655 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
7656 if (cp1 < template) break; /* template started with an ellipsis */
7657 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
7658 ellipsis = cp1; continue;
7660 wilddsc.dsc$a_pointer = tpl;
7661 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
7663 for (segdirs = 0, cp2 = tpl;
7664 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
7666 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
7668 if (!decc_efs_case_preserve) {
7669 *cp2 = _tolower(*cp1); /* else lowercase for match */
7672 *cp2 = *cp1; /* else preserve case for match */
7675 if (*cp2 == '/') segdirs++;
7677 if (cp1 != ellipsis - 1) {
7678 Safefree(unixified);
7682 return 0; /* Path too long */
7684 /* Back up at least as many dirs as in template before matching */
7685 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
7686 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
7687 for (match = 0; cp1 > lcres;) {
7688 resdsc.dsc$a_pointer = cp1;
7689 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
7691 if (match == 1) lcfront = cp1;
7693 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
7696 Safefree(unixified);
7700 return 0; /* Can't find prefix ??? */
7702 if (match > 1 && opts & 1) {
7703 /* This ... wildcard could cover more than one set of dirs (i.e.
7704 * a set of similar dir names is repeated). If the template
7705 * contains more than 1 ..., upstream elements could resolve the
7706 * ambiguity, but it's not worth a full backtracking setup here.
7707 * As a quick heuristic, clip off the current default directory
7708 * if it's present to find the trimmed spec, else use the
7709 * shortest string that this ... could cover.
7711 char def[NAM$C_MAXRSS+1], *st;
7713 if (getcwd(def, sizeof def,0) == NULL) {
7714 Safefree(unixified);
7720 if (!decc_efs_case_preserve) {
7721 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
7722 if (_tolower(*cp1) != _tolower(*cp2)) break;
7724 segdirs = dirs - totells; /* Min # of dirs we must have left */
7725 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
7726 if (*cp1 == '\0' && *cp2 == '/') {
7727 memmove(fspec,cp2+1,end - cp2);
7729 Safefree(unixified);
7734 /* Nope -- stick with lcfront from above and keep going. */
7737 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
7738 Safefree(unixified);
7746 } /* end of trim_unixpath() */
7751 * VMS readdir() routines.
7752 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
7754 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
7755 * Minor modifications to original routines.
7758 /* readdir may have been redefined by reentr.h, so make sure we get
7759 * the local version for what we do here.
7764 #if !defined(PERL_IMPLICIT_CONTEXT)
7765 # define readdir Perl_readdir
7767 # define readdir(a) Perl_readdir(aTHX_ a)
7770 /* Number of elements in vms_versions array */
7771 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
7774 * Open a directory, return a handle for later use.
7776 /*{{{ DIR *opendir(char*name) */
7778 Perl_opendir(pTHX_ const char *name)
7786 if (decc_efs_charset) {
7787 unix_flag = is_unix_filespec(name);
7790 Newx(dir, VMS_MAXRSS, char);
7791 if (do_tovmspath(name,dir,0) == NULL) {
7795 /* Check access before stat; otherwise stat does not
7796 * accurately report whether it's a directory.
7798 if (!cando_by_name(S_IRUSR,0,dir)) {
7799 /* cando_by_name has already set errno */
7803 if (flex_stat(dir,&sb) == -1) return NULL;
7804 if (!S_ISDIR(sb.st_mode)) {
7806 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
7809 /* Get memory for the handle, and the pattern. */
7811 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
7813 /* Fill in the fields; mainly playing with the descriptor. */
7814 sprintf(dd->pattern, "%s*.*",dir);
7820 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
7821 dd->pat.dsc$a_pointer = dd->pattern;
7822 dd->pat.dsc$w_length = strlen(dd->pattern);
7823 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
7824 dd->pat.dsc$b_class = DSC$K_CLASS_S;
7825 #if defined(USE_ITHREADS)
7826 Newx(dd->mutex,1,perl_mutex);
7827 MUTEX_INIT( (perl_mutex *) dd->mutex );
7833 } /* end of opendir() */
7837 * Set the flag to indicate we want versions or not.
7839 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
7841 vmsreaddirversions(DIR *dd, int flag)
7844 dd->flags |= PERL_VMSDIR_M_VERSIONS;
7846 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
7851 * Free up an opened directory.
7853 /*{{{ void closedir(DIR *dd)*/
7855 Perl_closedir(DIR *dd)
7859 sts = lib$find_file_end(&dd->context);
7860 Safefree(dd->pattern);
7861 #if defined(USE_ITHREADS)
7862 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
7863 Safefree(dd->mutex);
7870 * Collect all the version numbers for the current file.
7873 collectversions(pTHX_ DIR *dd)
7875 struct dsc$descriptor_s pat;
7876 struct dsc$descriptor_s res;
7878 char *p, *text, *buff;
7880 unsigned long context, tmpsts;
7882 /* Convenient shorthand. */
7885 /* Add the version wildcard, ignoring the "*.*" put on before */
7886 i = strlen(dd->pattern);
7887 Newx(text,i + e->d_namlen + 3,char);
7888 strcpy(text, dd->pattern);
7889 sprintf(&text[i - 3], "%s;*", e->d_name);
7891 /* Set up the pattern descriptor. */
7892 pat.dsc$a_pointer = text;
7893 pat.dsc$w_length = i + e->d_namlen - 1;
7894 pat.dsc$b_dtype = DSC$K_DTYPE_T;
7895 pat.dsc$b_class = DSC$K_CLASS_S;
7897 /* Set up result descriptor. */
7898 Newx(buff, VMS_MAXRSS, char);
7899 res.dsc$a_pointer = buff;
7900 res.dsc$w_length = VMS_MAXRSS - 1;
7901 res.dsc$b_dtype = DSC$K_DTYPE_T;
7902 res.dsc$b_class = DSC$K_CLASS_S;
7904 /* Read files, collecting versions. */
7905 for (context = 0, e->vms_verscount = 0;
7906 e->vms_verscount < VERSIZE(e);
7907 e->vms_verscount++) {
7909 unsigned long flags = 0;
7911 #ifdef VMS_LONGNAME_SUPPORT
7912 flags = LIB$M_FIL_LONG_NAMES
7914 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
7915 if (tmpsts == RMS$_NMF || context == 0) break;
7917 buff[VMS_MAXRSS - 1] = '\0';
7918 if ((p = strchr(buff, ';')))
7919 e->vms_versions[e->vms_verscount] = atoi(p + 1);
7921 e->vms_versions[e->vms_verscount] = -1;
7924 _ckvmssts(lib$find_file_end(&context));
7928 } /* end of collectversions() */
7931 * Read the next entry from the directory.
7933 /*{{{ struct dirent *readdir(DIR *dd)*/
7935 Perl_readdir(pTHX_ DIR *dd)
7937 struct dsc$descriptor_s res;
7939 unsigned long int tmpsts;
7941 unsigned long flags = 0;
7942 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7943 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7945 /* Set up result descriptor, and get next file. */
7946 Newx(buff, VMS_MAXRSS, char);
7947 res.dsc$a_pointer = buff;
7948 res.dsc$w_length = VMS_MAXRSS - 1;
7949 res.dsc$b_dtype = DSC$K_DTYPE_T;
7950 res.dsc$b_class = DSC$K_CLASS_S;
7952 #ifdef VMS_LONGNAME_SUPPORT
7953 flags = LIB$M_FIL_LONG_NAMES
7956 tmpsts = lib$find_file
7957 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
7958 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
7959 if (!(tmpsts & 1)) {
7960 set_vaxc_errno(tmpsts);
7963 set_errno(EACCES); break;
7965 set_errno(ENODEV); break;
7967 set_errno(ENOTDIR); break;
7968 case RMS$_FNF: case RMS$_DNF:
7969 set_errno(ENOENT); break;
7977 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
7978 if (!decc_efs_case_preserve) {
7979 buff[VMS_MAXRSS - 1] = '\0';
7980 for (p = buff; *p; p++) *p = _tolower(*p);
7983 /* we don't want to force to lowercase, just null terminate */
7984 buff[res.dsc$w_length] = '\0';
7986 while (--p >= buff) if (!isspace(*p)) break; /* Do we really need this? */
7989 /* Skip any directory component and just copy the name. */
7990 sts = vms_split_path
8005 /* Drop NULL extensions on UNIX file specification */
8006 if ((dd->flags & PERL_VMSDIR_M_UNIXSPECS &&
8007 (e_len == 1) && decc_readdir_dropdotnotype)) {
8012 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
8013 dd->entry.d_name[n_len + e_len] = '\0';
8014 dd->entry.d_namlen = strlen(dd->entry.d_name);
8016 /* Convert the filename to UNIX format if needed */
8017 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
8019 /* Translate the encoded characters. */
8020 /* Fixme: unicode handling could result in embedded 0 characters */
8021 if (strchr(dd->entry.d_name, '^') != NULL) {
8025 p = dd->entry.d_name;
8029 x = copy_expand_vms_filename_escape(q, p, &y);
8033 /* if y > 1, then this is a wide file specification */
8034 /* Wide file specifications need to be passed in Perl */
8035 /* counted strings apparently with a unicode flag */
8038 strcpy(dd->entry.d_name, new_name);
8042 dd->entry.vms_verscount = 0;
8043 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
8047 } /* end of readdir() */
8051 * Read the next entry from the directory -- thread-safe version.
8053 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
8055 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
8059 MUTEX_LOCK( (perl_mutex *) dd->mutex );
8061 entry = readdir(dd);
8063 retval = ( *result == NULL ? errno : 0 );
8065 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
8069 } /* end of readdir_r() */
8073 * Return something that can be used in a seekdir later.
8075 /*{{{ long telldir(DIR *dd)*/
8077 Perl_telldir(DIR *dd)
8084 * Return to a spot where we used to be. Brute force.
8086 /*{{{ void seekdir(DIR *dd,long count)*/
8088 Perl_seekdir(pTHX_ DIR *dd, long count)
8092 /* If we haven't done anything yet... */
8096 /* Remember some state, and clear it. */
8097 old_flags = dd->flags;
8098 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
8099 _ckvmssts(lib$find_file_end(&dd->context));
8102 /* The increment is in readdir(). */
8103 for (dd->count = 0; dd->count < count; )
8106 dd->flags = old_flags;
8108 } /* end of seekdir() */
8111 /* VMS subprocess management
8113 * my_vfork() - just a vfork(), after setting a flag to record that
8114 * the current script is trying a Unix-style fork/exec.
8116 * vms_do_aexec() and vms_do_exec() are called in response to the
8117 * perl 'exec' function. If this follows a vfork call, then they
8118 * call out the regular perl routines in doio.c which do an
8119 * execvp (for those who really want to try this under VMS).
8120 * Otherwise, they do exactly what the perl docs say exec should
8121 * do - terminate the current script and invoke a new command
8122 * (See below for notes on command syntax.)
8124 * do_aspawn() and do_spawn() implement the VMS side of the perl
8125 * 'system' function.
8127 * Note on command arguments to perl 'exec' and 'system': When handled
8128 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
8129 * are concatenated to form a DCL command string. If the first arg
8130 * begins with '$' (i.e. the perl script had "\$ Type" or some such),
8131 * the command string is handed off to DCL directly. Otherwise,
8132 * the first token of the command is taken as the filespec of an image
8133 * to run. The filespec is expanded using a default type of '.EXE' and
8134 * the process defaults for device, directory, etc., and if found, the resultant
8135 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
8136 * the command string as parameters. This is perhaps a bit complicated,
8137 * but I hope it will form a happy medium between what VMS folks expect
8138 * from lib$spawn and what Unix folks expect from exec.
8141 static int vfork_called;
8143 /*{{{int my_vfork()*/
8154 vms_execfree(struct dsc$descriptor_s *vmscmd)
8157 if (vmscmd->dsc$a_pointer) {
8158 Safefree(vmscmd->dsc$a_pointer);
8165 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
8167 char *junk, *tmps = Nullch;
8168 register size_t cmdlen = 0;
8175 tmps = SvPV(really,rlen);
8182 for (idx++; idx <= sp; idx++) {
8184 junk = SvPVx(*idx,rlen);
8185 cmdlen += rlen ? rlen + 1 : 0;
8188 Newx(PL_Cmd,cmdlen+1,char);
8190 if (tmps && *tmps) {
8191 strcpy(PL_Cmd,tmps);
8194 else *PL_Cmd = '\0';
8195 while (++mark <= sp) {
8197 char *s = SvPVx(*mark,n_a);
8199 if (*PL_Cmd) strcat(PL_Cmd," ");
8205 } /* end of setup_argstr() */
8208 static unsigned long int
8209 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
8210 struct dsc$descriptor_s **pvmscmd)
8212 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
8213 char image_name[NAM$C_MAXRSS+1];
8214 char image_argv[NAM$C_MAXRSS+1];
8215 $DESCRIPTOR(defdsc,".EXE");
8216 $DESCRIPTOR(defdsc2,".");
8217 $DESCRIPTOR(resdsc,resspec);
8218 struct dsc$descriptor_s *vmscmd;
8219 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
8220 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
8221 register char *s, *rest, *cp, *wordbreak;
8226 Newx(vmscmd,sizeof(struct dsc$descriptor_s),struct dsc$descriptor_s);
8228 /* Make a copy for modification */
8229 cmdlen = strlen(incmd);
8230 Newx(cmd, cmdlen+1, char);
8231 strncpy(cmd, incmd, cmdlen);
8236 vmscmd->dsc$a_pointer = NULL;
8237 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
8238 vmscmd->dsc$b_class = DSC$K_CLASS_S;
8239 vmscmd->dsc$w_length = 0;
8240 if (pvmscmd) *pvmscmd = vmscmd;
8242 if (suggest_quote) *suggest_quote = 0;
8244 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
8245 return CLI$_BUFOVF; /* continuation lines currently unsupported */
8251 while (*s && isspace(*s)) s++;
8253 if (*s == '@' || *s == '$') {
8254 vmsspec[0] = *s; rest = s + 1;
8255 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
8257 else { cp = vmsspec; rest = s; }
8258 if (*rest == '.' || *rest == '/') {
8261 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
8262 rest++, cp2++) *cp2 = *rest;
8264 if (do_tovmsspec(resspec,cp,0)) {
8267 for (cp2 = vmsspec + strlen(vmsspec);
8268 *rest && cp2 - vmsspec < sizeof vmsspec;
8269 rest++, cp2++) *cp2 = *rest;
8274 /* Intuit whether verb (first word of cmd) is a DCL command:
8275 * - if first nonspace char is '@', it's a DCL indirection
8277 * - if verb contains a filespec separator, it's not a DCL command
8278 * - if it doesn't, caller tells us whether to default to a DCL
8279 * command, or to a local image unless told it's DCL (by leading '$')
8283 if (suggest_quote) *suggest_quote = 1;
8285 register char *filespec = strpbrk(s,":<[.;");
8286 rest = wordbreak = strpbrk(s," \"\t/");
8287 if (!wordbreak) wordbreak = s + strlen(s);
8288 if (*s == '$') check_img = 0;
8289 if (filespec && (filespec < wordbreak)) isdcl = 0;
8290 else isdcl = !check_img;
8295 imgdsc.dsc$a_pointer = s;
8296 imgdsc.dsc$w_length = wordbreak - s;
8297 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
8299 _ckvmssts(lib$find_file_end(&cxt));
8300 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
8301 if (!(retsts & 1) && *s == '$') {
8302 _ckvmssts(lib$find_file_end(&cxt));
8303 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
8304 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
8306 _ckvmssts(lib$find_file_end(&cxt));
8307 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
8311 _ckvmssts(lib$find_file_end(&cxt));
8316 while (*s && !isspace(*s)) s++;
8319 /* check that it's really not DCL with no file extension */
8320 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
8322 char b[256] = {0,0,0,0};
8323 read(fileno(fp), b, 256);
8324 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
8328 /* Check for script */
8330 if ((b[0] == '#') && (b[1] == '!'))
8332 #ifdef ALTERNATE_SHEBANG
8334 shebang_len = strlen(ALTERNATE_SHEBANG);
8335 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
8337 perlstr = strstr("perl",b);
8338 if (perlstr == NULL)
8346 if (shebang_len > 0) {
8349 char tmpspec[NAM$C_MAXRSS + 1];
8352 /* Image is following after white space */
8353 /*--------------------------------------*/
8354 while (isprint(b[i]) && isspace(b[i]))
8358 while (isprint(b[i]) && !isspace(b[i])) {
8359 tmpspec[j++] = b[i++];
8360 if (j >= NAM$C_MAXRSS)
8365 /* There may be some default parameters to the image */
8366 /*---------------------------------------------------*/
8368 while (isprint(b[i])) {
8369 image_argv[j++] = b[i++];
8370 if (j >= NAM$C_MAXRSS)
8373 while ((j > 0) && !isprint(image_argv[j-1]))
8377 /* It will need to be converted to VMS format and validated */
8378 if (tmpspec[0] != '\0') {
8381 /* Try to find the exact program requested to be run */
8382 /*---------------------------------------------------*/
8383 iname = do_rmsexpand
8384 (tmpspec, image_name, 0, ".exe", PERL_RMSEXPAND_M_VMS);
8385 if (iname != NULL) {
8386 if (cando_by_name(S_IXUSR,0,image_name)) {
8387 /* MCR prefix needed */
8391 /* Try again with a null type */
8392 /*----------------------------*/
8393 iname = do_rmsexpand
8394 (tmpspec, image_name, 0, ".", PERL_RMSEXPAND_M_VMS);
8395 if (iname != NULL) {
8396 if (cando_by_name(S_IXUSR,0,image_name)) {
8397 /* MCR prefix needed */
8403 /* Did we find the image to run the script? */
8404 /*------------------------------------------*/
8408 /* Assume DCL or foreign command exists */
8409 /*--------------------------------------*/
8410 tchr = strrchr(tmpspec, '/');
8417 strcpy(image_name, tchr);
8425 if (check_img && isdcl) return RMS$_FNF;
8427 if (cando_by_name(S_IXUSR,0,resspec)) {
8428 Newx(vmscmd->dsc$a_pointer, MAX_DCL_LINE_LENGTH ,char);
8430 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
8431 if (image_name[0] != 0) {
8432 strcat(vmscmd->dsc$a_pointer, image_name);
8433 strcat(vmscmd->dsc$a_pointer, " ");
8435 } else if (image_name[0] != 0) {
8436 strcpy(vmscmd->dsc$a_pointer, image_name);
8437 strcat(vmscmd->dsc$a_pointer, " ");
8439 strcpy(vmscmd->dsc$a_pointer,"@");
8441 if (suggest_quote) *suggest_quote = 1;
8443 /* If there is an image name, use original command */
8444 if (image_name[0] == 0)
8445 strcat(vmscmd->dsc$a_pointer,resspec);
8448 while (*rest && isspace(*rest)) rest++;
8451 if (image_argv[0] != 0) {
8452 strcat(vmscmd->dsc$a_pointer,image_argv);
8453 strcat(vmscmd->dsc$a_pointer, " ");
8459 rest_len = strlen(rest);
8460 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
8461 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
8462 strcat(vmscmd->dsc$a_pointer,rest);
8464 retsts = CLI$_BUFOVF;
8466 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
8468 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
8470 else retsts = RMS$_PRV;
8473 /* It's either a DCL command or we couldn't find a suitable image */
8474 vmscmd->dsc$w_length = strlen(cmd);
8475 /* if (cmd == PL_Cmd) {
8476 vmscmd->dsc$a_pointer = PL_Cmd;
8477 if (suggest_quote) *suggest_quote = 1;
8480 vmscmd->dsc$a_pointer = savepvn(cmd,vmscmd->dsc$w_length);
8484 /* check if it's a symbol (for quoting purposes) */
8485 if (suggest_quote && !*suggest_quote) {
8487 char equiv[LNM$C_NAMLENGTH];
8488 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
8489 eqvdsc.dsc$a_pointer = equiv;
8491 iss = lib$get_symbol(vmscmd,&eqvdsc);
8492 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
8494 if (!(retsts & 1)) {
8495 /* just hand off status values likely to be due to user error */
8496 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
8497 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
8498 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
8499 else { _ckvmssts(retsts); }
8502 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
8504 } /* end of setup_cmddsc() */
8507 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
8509 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
8512 if (vfork_called) { /* this follows a vfork - act Unixish */
8514 if (vfork_called < 0) {
8515 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
8518 else return do_aexec(really,mark,sp);
8520 /* no vfork - act VMSish */
8521 return vms_do_exec(setup_argstr(aTHX_ really,mark,sp));
8526 } /* end of vms_do_aexec() */
8529 /* {{{bool vms_do_exec(char *cmd) */
8531 Perl_vms_do_exec(pTHX_ const char *cmd)
8533 struct dsc$descriptor_s *vmscmd;
8535 if (vfork_called) { /* this follows a vfork - act Unixish */
8537 if (vfork_called < 0) {
8538 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
8541 else return do_exec(cmd);
8544 { /* no vfork - act VMSish */
8545 unsigned long int retsts;
8548 TAINT_PROPER("exec");
8549 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
8550 retsts = lib$do_command(vmscmd);
8553 case RMS$_FNF: case RMS$_DNF:
8554 set_errno(ENOENT); break;
8556 set_errno(ENOTDIR); break;
8558 set_errno(ENODEV); break;
8560 set_errno(EACCES); break;
8562 set_errno(EINVAL); break;
8563 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
8564 set_errno(E2BIG); break;
8565 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
8566 _ckvmssts(retsts); /* fall through */
8567 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
8570 set_vaxc_errno(retsts);
8571 if (ckWARN(WARN_EXEC)) {
8572 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
8573 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
8575 vms_execfree(vmscmd);
8580 } /* end of vms_do_exec() */
8583 unsigned long int Perl_do_spawn(pTHX_ const char *);
8585 /* {{{ unsigned long int do_aspawn(void *really,void **mark,void **sp) */
8587 Perl_do_aspawn(pTHX_ void *really,void **mark,void **sp)
8589 if (sp > mark) return do_spawn(setup_argstr(aTHX_ (SV *)really,(SV **)mark,(SV **)sp));
8592 } /* end of do_aspawn() */
8595 /* {{{unsigned long int do_spawn(char *cmd) */
8597 Perl_do_spawn(pTHX_ const char *cmd)
8599 unsigned long int sts, substs;
8602 TAINT_PROPER("spawn");
8603 if (!cmd || !*cmd) {
8604 sts = lib$spawn(0,0,0,0,0,0,&substs,0,0,0,0,0,0);
8607 case RMS$_FNF: case RMS$_DNF:
8608 set_errno(ENOENT); break;
8610 set_errno(ENOTDIR); break;
8612 set_errno(ENODEV); break;
8614 set_errno(EACCES); break;
8616 set_errno(EINVAL); break;
8617 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
8618 set_errno(E2BIG); break;
8619 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
8620 _ckvmssts(sts); /* fall through */
8621 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
8624 set_vaxc_errno(sts);
8625 if (ckWARN(WARN_EXEC)) {
8626 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
8634 fp = safe_popen(aTHX_ cmd, "nW", (int *)&sts);
8639 } /* end of do_spawn() */
8643 static unsigned int *sockflags, sockflagsize;
8646 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
8647 * routines found in some versions of the CRTL can't deal with sockets.
8648 * We don't shim the other file open routines since a socket isn't
8649 * likely to be opened by a name.
8651 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
8652 FILE *my_fdopen(int fd, const char *mode)
8654 FILE *fp = fdopen(fd, mode);
8657 unsigned int fdoff = fd / sizeof(unsigned int);
8658 Stat_t sbuf; /* native stat; we don't need flex_stat */
8659 if (!sockflagsize || fdoff > sockflagsize) {
8660 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
8661 else Newx (sockflags,fdoff+2,unsigned int);
8662 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
8663 sockflagsize = fdoff + 2;
8665 if (fstat(fd, (struct stat *)&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
8666 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
8675 * Clear the corresponding bit when the (possibly) socket stream is closed.
8676 * There still a small hole: we miss an implicit close which might occur
8677 * via freopen(). >> Todo
8679 /*{{{ int my_fclose(FILE *fp)*/
8680 int my_fclose(FILE *fp) {
8682 unsigned int fd = fileno(fp);
8683 unsigned int fdoff = fd / sizeof(unsigned int);
8685 if (sockflagsize && fdoff <= sockflagsize)
8686 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
8694 * A simple fwrite replacement which outputs itmsz*nitm chars without
8695 * introducing record boundaries every itmsz chars.
8696 * We are using fputs, which depends on a terminating null. We may
8697 * well be writing binary data, so we need to accommodate not only
8698 * data with nulls sprinkled in the middle but also data with no null
8701 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
8703 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
8705 register char *cp, *end, *cpd, *data;
8706 register unsigned int fd = fileno(dest);
8707 register unsigned int fdoff = fd / sizeof(unsigned int);
8709 int bufsize = itmsz * nitm + 1;
8711 if (fdoff < sockflagsize &&
8712 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
8713 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
8717 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
8718 memcpy( data, src, itmsz*nitm );
8719 data[itmsz*nitm] = '\0';
8721 end = data + itmsz * nitm;
8722 retval = (int) nitm; /* on success return # items written */
8725 while (cpd <= end) {
8726 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
8727 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
8729 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
8733 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
8736 } /* end of my_fwrite() */
8739 /*{{{ int my_flush(FILE *fp)*/
8741 Perl_my_flush(pTHX_ FILE *fp)
8744 if ((res = fflush(fp)) == 0 && fp) {
8745 #ifdef VMS_DO_SOCKETS
8747 if (Fstat(fileno(fp), &s) == 0 && !S_ISSOCK(s.st_mode))
8749 res = fsync(fileno(fp));
8752 * If the flush succeeded but set end-of-file, we need to clear
8753 * the error because our caller may check ferror(). BTW, this
8754 * probably means we just flushed an empty file.
8756 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
8763 * Here are replacements for the following Unix routines in the VMS environment:
8764 * getpwuid Get information for a particular UIC or UID
8765 * getpwnam Get information for a named user
8766 * getpwent Get information for each user in the rights database
8767 * setpwent Reset search to the start of the rights database
8768 * endpwent Finish searching for users in the rights database
8770 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
8771 * (defined in pwd.h), which contains the following fields:-
8773 * char *pw_name; Username (in lower case)
8774 * char *pw_passwd; Hashed password
8775 * unsigned int pw_uid; UIC
8776 * unsigned int pw_gid; UIC group number
8777 * char *pw_unixdir; Default device/directory (VMS-style)
8778 * char *pw_gecos; Owner name
8779 * char *pw_dir; Default device/directory (Unix-style)
8780 * char *pw_shell; Default CLI name (eg. DCL)
8782 * If the specified user does not exist, getpwuid and getpwnam return NULL.
8784 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
8785 * not the UIC member number (eg. what's returned by getuid()),
8786 * getpwuid() can accept either as input (if uid is specified, the caller's
8787 * UIC group is used), though it won't recognise gid=0.
8789 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
8790 * information about other users in your group or in other groups, respectively.
8791 * If the required privilege is not available, then these routines fill only
8792 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
8795 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
8798 /* sizes of various UAF record fields */
8799 #define UAI$S_USERNAME 12
8800 #define UAI$S_IDENT 31
8801 #define UAI$S_OWNER 31
8802 #define UAI$S_DEFDEV 31
8803 #define UAI$S_DEFDIR 63
8804 #define UAI$S_DEFCLI 31
8807 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
8808 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
8809 (uic).uic$v_group != UIC$K_WILD_GROUP)
8811 static char __empty[]= "";
8812 static struct passwd __passwd_empty=
8813 {(char *) __empty, (char *) __empty, 0, 0,
8814 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
8815 static int contxt= 0;
8816 static struct passwd __pwdcache;
8817 static char __pw_namecache[UAI$S_IDENT+1];
8820 * This routine does most of the work extracting the user information.
8822 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
8825 unsigned char length;
8826 char pw_gecos[UAI$S_OWNER+1];
8828 static union uicdef uic;
8830 unsigned char length;
8831 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
8834 unsigned char length;
8835 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
8838 unsigned char length;
8839 char pw_shell[UAI$S_DEFCLI+1];
8841 static char pw_passwd[UAI$S_PWD+1];
8843 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
8844 struct dsc$descriptor_s name_desc;
8845 unsigned long int sts;
8847 static struct itmlst_3 itmlst[]= {
8848 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
8849 {sizeof(uic), UAI$_UIC, &uic, &luic},
8850 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
8851 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
8852 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
8853 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
8854 {0, 0, NULL, NULL}};
8856 name_desc.dsc$w_length= strlen(name);
8857 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
8858 name_desc.dsc$b_class= DSC$K_CLASS_S;
8859 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
8861 /* Note that sys$getuai returns many fields as counted strings. */
8862 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
8863 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
8864 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
8866 else { _ckvmssts(sts); }
8867 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
8869 if ((int) owner.length < lowner) lowner= (int) owner.length;
8870 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
8871 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
8872 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
8873 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
8874 owner.pw_gecos[lowner]= '\0';
8875 defdev.pw_dir[ldefdev+ldefdir]= '\0';
8876 defcli.pw_shell[ldefcli]= '\0';
8877 if (valid_uic(uic)) {
8878 pwd->pw_uid= uic.uic$l_uic;
8879 pwd->pw_gid= uic.uic$v_group;
8882 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
8883 pwd->pw_passwd= pw_passwd;
8884 pwd->pw_gecos= owner.pw_gecos;
8885 pwd->pw_dir= defdev.pw_dir;
8886 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1);
8887 pwd->pw_shell= defcli.pw_shell;
8888 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
8890 ldir= strlen(pwd->pw_unixdir) - 1;
8891 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
8894 strcpy(pwd->pw_unixdir, pwd->pw_dir);
8895 if (!decc_efs_case_preserve)
8896 __mystrtolower(pwd->pw_unixdir);
8901 * Get information for a named user.
8903 /*{{{struct passwd *getpwnam(char *name)*/
8904 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
8906 struct dsc$descriptor_s name_desc;
8908 unsigned long int status, sts;
8910 __pwdcache = __passwd_empty;
8911 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
8912 /* We still may be able to determine pw_uid and pw_gid */
8913 name_desc.dsc$w_length= strlen(name);
8914 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
8915 name_desc.dsc$b_class= DSC$K_CLASS_S;
8916 name_desc.dsc$a_pointer= (char *) name;
8917 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
8918 __pwdcache.pw_uid= uic.uic$l_uic;
8919 __pwdcache.pw_gid= uic.uic$v_group;
8922 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
8923 set_vaxc_errno(sts);
8924 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
8927 else { _ckvmssts(sts); }
8930 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
8931 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
8932 __pwdcache.pw_name= __pw_namecache;
8934 } /* end of my_getpwnam() */
8938 * Get information for a particular UIC or UID.
8939 * Called by my_getpwent with uid=-1 to list all users.
8941 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
8942 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
8944 const $DESCRIPTOR(name_desc,__pw_namecache);
8945 unsigned short lname;
8947 unsigned long int status;
8949 if (uid == (unsigned int) -1) {
8951 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
8952 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
8953 set_vaxc_errno(status);
8954 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
8958 else { _ckvmssts(status); }
8959 } while (!valid_uic (uic));
8963 if (!uic.uic$v_group)
8964 uic.uic$v_group= PerlProc_getgid();
8966 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
8967 else status = SS$_IVIDENT;
8968 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
8969 status == RMS$_PRV) {
8970 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
8973 else { _ckvmssts(status); }
8975 __pw_namecache[lname]= '\0';
8976 __mystrtolower(__pw_namecache);
8978 __pwdcache = __passwd_empty;
8979 __pwdcache.pw_name = __pw_namecache;
8981 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
8982 The identifier's value is usually the UIC, but it doesn't have to be,
8983 so if we can, we let fillpasswd update this. */
8984 __pwdcache.pw_uid = uic.uic$l_uic;
8985 __pwdcache.pw_gid = uic.uic$v_group;
8987 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
8990 } /* end of my_getpwuid() */
8994 * Get information for next user.
8996 /*{{{struct passwd *my_getpwent()*/
8997 struct passwd *Perl_my_getpwent(pTHX)
8999 return (my_getpwuid((unsigned int) -1));
9004 * Finish searching rights database for users.
9006 /*{{{void my_endpwent()*/
9007 void Perl_my_endpwent(pTHX)
9010 _ckvmssts(sys$finish_rdb(&contxt));
9016 #ifdef HOMEGROWN_POSIX_SIGNALS
9017 /* Signal handling routines, pulled into the core from POSIX.xs.
9019 * We need these for threads, so they've been rolled into the core,
9020 * rather than left in POSIX.xs.
9022 * (DRS, Oct 23, 1997)
9025 /* sigset_t is atomic under VMS, so these routines are easy */
9026 /*{{{int my_sigemptyset(sigset_t *) */
9027 int my_sigemptyset(sigset_t *set) {
9028 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9034 /*{{{int my_sigfillset(sigset_t *)*/
9035 int my_sigfillset(sigset_t *set) {
9037 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9038 for (i = 0; i < NSIG; i++) *set |= (1 << i);
9044 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
9045 int my_sigaddset(sigset_t *set, int sig) {
9046 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9047 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
9048 *set |= (1 << (sig - 1));
9054 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
9055 int my_sigdelset(sigset_t *set, int sig) {
9056 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9057 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
9058 *set &= ~(1 << (sig - 1));
9064 /*{{{int my_sigismember(sigset_t *set, int sig)*/
9065 int my_sigismember(sigset_t *set, int sig) {
9066 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
9067 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
9068 return *set & (1 << (sig - 1));
9073 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
9074 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
9077 /* If set and oset are both null, then things are badly wrong. Bail out. */
9078 if ((oset == NULL) && (set == NULL)) {
9079 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
9083 /* If set's null, then we're just handling a fetch. */
9085 tempmask = sigblock(0);
9090 tempmask = sigsetmask(*set);
9093 tempmask = sigblock(*set);
9096 tempmask = sigblock(0);
9097 sigsetmask(*oset & ~tempmask);
9100 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9105 /* Did they pass us an oset? If so, stick our holding mask into it */
9112 #endif /* HOMEGROWN_POSIX_SIGNALS */
9115 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
9116 * my_utime(), and flex_stat(), all of which operate on UTC unless
9117 * VMSISH_TIMES is true.
9119 /* method used to handle UTC conversions:
9120 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
9122 static int gmtime_emulation_type;
9123 /* number of secs to add to UTC POSIX-style time to get local time */
9124 static long int utc_offset_secs;
9126 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
9127 * in vmsish.h. #undef them here so we can call the CRTL routines
9136 * DEC C previous to 6.0 corrupts the behavior of the /prefix
9137 * qualifier with the extern prefix pragma. This provisional
9138 * hack circumvents this prefix pragma problem in previous
9141 #if defined(__VMS_VER) && __VMS_VER >= 70000000
9142 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
9143 # pragma __extern_prefix save
9144 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
9145 # define gmtime decc$__utctz_gmtime
9146 # define localtime decc$__utctz_localtime
9147 # define time decc$__utc_time
9148 # pragma __extern_prefix restore
9150 struct tm *gmtime(), *localtime();
9156 static time_t toutc_dst(time_t loc) {
9159 if ((rsltmp = localtime(&loc)) == NULL) return -1;
9160 loc -= utc_offset_secs;
9161 if (rsltmp->tm_isdst) loc -= 3600;
9164 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
9165 ((gmtime_emulation_type || my_time(NULL)), \
9166 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
9167 ((secs) - utc_offset_secs))))
9169 static time_t toloc_dst(time_t utc) {
9172 utc += utc_offset_secs;
9173 if ((rsltmp = localtime(&utc)) == NULL) return -1;
9174 if (rsltmp->tm_isdst) utc += 3600;
9177 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
9178 ((gmtime_emulation_type || my_time(NULL)), \
9179 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
9180 ((secs) + utc_offset_secs))))
9182 #ifndef RTL_USES_UTC
9185 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
9186 DST starts on 1st sun of april at 02:00 std time
9187 ends on last sun of october at 02:00 dst time
9188 see the UCX management command reference, SET CONFIG TIMEZONE
9189 for formatting info.
9191 No, it's not as general as it should be, but then again, NOTHING
9192 will handle UK times in a sensible way.
9197 parse the DST start/end info:
9198 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
9202 tz_parse_startend(char *s, struct tm *w, int *past)
9204 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
9205 int ly, dozjd, d, m, n, hour, min, sec, j, k;
9210 if (!past) return 0;
9213 if (w->tm_year % 4 == 0) ly = 1;
9214 if (w->tm_year % 100 == 0) ly = 0;
9215 if (w->tm_year+1900 % 400 == 0) ly = 1;
9218 dozjd = isdigit(*s);
9219 if (*s == 'J' || *s == 'j' || dozjd) {
9220 if (!dozjd && !isdigit(*++s)) return 0;
9223 d = d*10 + *s++ - '0';
9225 d = d*10 + *s++ - '0';
9228 if (d == 0) return 0;
9229 if (d > 366) return 0;
9231 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
9234 } else if (*s == 'M' || *s == 'm') {
9235 if (!isdigit(*++s)) return 0;
9237 if (isdigit(*s)) m = 10*m + *s++ - '0';
9238 if (*s != '.') return 0;
9239 if (!isdigit(*++s)) return 0;
9241 if (n < 1 || n > 5) return 0;
9242 if (*s != '.') return 0;
9243 if (!isdigit(*++s)) return 0;
9245 if (d > 6) return 0;
9249 if (!isdigit(*++s)) return 0;
9251 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
9253 if (!isdigit(*++s)) return 0;
9255 if (isdigit(*s)) min = 10*min + *s++ - '0';
9257 if (!isdigit(*++s)) return 0;
9259 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
9269 if (w->tm_yday < d) goto before;
9270 if (w->tm_yday > d) goto after;
9272 if (w->tm_mon+1 < m) goto before;
9273 if (w->tm_mon+1 > m) goto after;
9275 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
9276 k = d - j; /* mday of first d */
9278 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
9279 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
9280 if (w->tm_mday < k) goto before;
9281 if (w->tm_mday > k) goto after;
9284 if (w->tm_hour < hour) goto before;
9285 if (w->tm_hour > hour) goto after;
9286 if (w->tm_min < min) goto before;
9287 if (w->tm_min > min) goto after;
9288 if (w->tm_sec < sec) goto before;
9302 /* parse the offset: (+|-)hh[:mm[:ss]] */
9305 tz_parse_offset(char *s, int *offset)
9307 int hour = 0, min = 0, sec = 0;
9310 if (!offset) return 0;
9312 if (*s == '-') {neg++; s++;}
9314 if (!isdigit(*s)) return 0;
9316 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
9317 if (hour > 24) return 0;
9319 if (!isdigit(*++s)) return 0;
9321 if (isdigit(*s)) min = min*10 + (*s++ - '0');
9322 if (min > 59) return 0;
9324 if (!isdigit(*++s)) return 0;
9326 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
9327 if (sec > 59) return 0;
9331 *offset = (hour*60+min)*60 + sec;
9332 if (neg) *offset = -*offset;
9337 input time is w, whatever type of time the CRTL localtime() uses.
9338 sets dst, the zone, and the gmtoff (seconds)
9340 caches the value of TZ and UCX$TZ env variables; note that
9341 my_setenv looks for these and sets a flag if they're changed
9344 We have to watch out for the "australian" case (dst starts in
9345 october, ends in april)...flagged by "reverse" and checked by
9346 scanning through the months of the previous year.
9351 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
9356 char *dstzone, *tz, *s_start, *s_end;
9357 int std_off, dst_off, isdst;
9358 int y, dststart, dstend;
9359 static char envtz[1025]; /* longer than any logical, symbol, ... */
9360 static char ucxtz[1025];
9361 static char reversed = 0;
9367 reversed = -1; /* flag need to check */
9368 envtz[0] = ucxtz[0] = '\0';
9369 tz = my_getenv("TZ",0);
9370 if (tz) strcpy(envtz, tz);
9371 tz = my_getenv("UCX$TZ",0);
9372 if (tz) strcpy(ucxtz, tz);
9373 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
9376 if (!*tz) tz = ucxtz;
9379 while (isalpha(*s)) s++;
9380 s = tz_parse_offset(s, &std_off);
9382 if (!*s) { /* no DST, hurray we're done! */
9388 while (isalpha(*s)) s++;
9389 s2 = tz_parse_offset(s, &dst_off);
9393 dst_off = std_off - 3600;
9396 if (!*s) { /* default dst start/end?? */
9397 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
9398 s = strchr(ucxtz,',');
9400 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
9402 if (*s != ',') return 0;
9405 when = _toutc(when); /* convert to utc */
9406 when = when - std_off; /* convert to pseudolocal time*/
9408 w2 = localtime(&when);
9411 s = tz_parse_startend(s_start,w2,&dststart);
9413 if (*s != ',') return 0;
9416 when = _toutc(when); /* convert to utc */
9417 when = when - dst_off; /* convert to pseudolocal time*/
9418 w2 = localtime(&when);
9419 if (w2->tm_year != y) { /* spans a year, just check one time */
9420 when += dst_off - std_off;
9421 w2 = localtime(&when);
9424 s = tz_parse_startend(s_end,w2,&dstend);
9427 if (reversed == -1) { /* need to check if start later than end */
9431 if (when < 2*365*86400) {
9432 when += 2*365*86400;
9436 w2 =localtime(&when);
9437 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
9439 for (j = 0; j < 12; j++) {
9440 w2 =localtime(&when);
9441 tz_parse_startend(s_start,w2,&ds);
9442 tz_parse_startend(s_end,w2,&de);
9443 if (ds != de) break;
9447 if (de && !ds) reversed = 1;
9450 isdst = dststart && !dstend;
9451 if (reversed) isdst = dststart || !dstend;
9454 if (dst) *dst = isdst;
9455 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
9456 if (isdst) tz = dstzone;
9458 while(isalpha(*tz)) *zone++ = *tz++;
9464 #endif /* !RTL_USES_UTC */
9466 /* my_time(), my_localtime(), my_gmtime()
9467 * By default traffic in UTC time values, using CRTL gmtime() or
9468 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
9469 * Note: We need to use these functions even when the CRTL has working
9470 * UTC support, since they also handle C<use vmsish qw(times);>
9472 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
9473 * Modified by Charles Bailey <bailey@newman.upenn.edu>
9476 /*{{{time_t my_time(time_t *timep)*/
9477 time_t Perl_my_time(pTHX_ time_t *timep)
9482 if (gmtime_emulation_type == 0) {
9484 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
9485 /* results of calls to gmtime() and localtime() */
9486 /* for same &base */
9488 gmtime_emulation_type++;
9489 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
9490 char off[LNM$C_NAMLENGTH+1];;
9492 gmtime_emulation_type++;
9493 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
9494 gmtime_emulation_type++;
9495 utc_offset_secs = 0;
9496 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
9498 else { utc_offset_secs = atol(off); }
9500 else { /* We've got a working gmtime() */
9501 struct tm gmt, local;
9504 tm_p = localtime(&base);
9506 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
9507 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
9508 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
9509 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
9515 # ifdef RTL_USES_UTC
9516 if (VMSISH_TIME) when = _toloc(when);
9518 if (!VMSISH_TIME) when = _toutc(when);
9521 if (timep != NULL) *timep = when;
9524 } /* end of my_time() */
9528 /*{{{struct tm *my_gmtime(const time_t *timep)*/
9530 Perl_my_gmtime(pTHX_ const time_t *timep)
9536 if (timep == NULL) {
9537 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9540 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
9544 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
9546 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
9547 return gmtime(&when);
9549 /* CRTL localtime() wants local time as input, so does no tz correction */
9550 rsltmp = localtime(&when);
9551 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
9554 } /* end of my_gmtime() */
9558 /*{{{struct tm *my_localtime(const time_t *timep)*/
9560 Perl_my_localtime(pTHX_ const time_t *timep)
9562 time_t when, whenutc;
9566 if (timep == NULL) {
9567 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
9570 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
9571 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
9574 # ifdef RTL_USES_UTC
9576 if (VMSISH_TIME) when = _toutc(when);
9578 /* CRTL localtime() wants UTC as input, does tz correction itself */
9579 return localtime(&when);
9581 # else /* !RTL_USES_UTC */
9584 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
9585 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
9588 #ifndef RTL_USES_UTC
9589 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
9590 when = whenutc - offset; /* pseudolocal time*/
9593 /* CRTL localtime() wants local time as input, so does no tz correction */
9594 rsltmp = localtime(&when);
9595 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
9599 } /* end of my_localtime() */
9602 /* Reset definitions for later calls */
9603 #define gmtime(t) my_gmtime(t)
9604 #define localtime(t) my_localtime(t)
9605 #define time(t) my_time(t)
9608 /* my_utime - update modification time of a file
9609 * calling sequence is identical to POSIX utime(), but under
9610 * VMS only the modification time is changed; ODS-2 does not
9611 * maintain access times. Restrictions differ from the POSIX
9612 * definition in that the time can be changed as long as the
9613 * caller has permission to execute the necessary IO$_MODIFY $QIO;
9614 * no separate checks are made to insure that the caller is the
9615 * owner of the file or has special privs enabled.
9616 * Code here is based on Joe Meadows' FILE utility.
9619 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
9620 * to VMS epoch (01-JAN-1858 00:00:00.00)
9621 * in 100 ns intervals.
9623 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
9625 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
9626 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
9630 long int bintime[2], len = 2, lowbit, unixtime,
9631 secscale = 10000000; /* seconds --> 100 ns intervals */
9632 unsigned long int chan, iosb[2], retsts;
9633 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
9634 struct FAB myfab = cc$rms_fab;
9635 struct NAM mynam = cc$rms_nam;
9636 #if defined (__DECC) && defined (__VAX)
9637 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
9638 * at least through VMS V6.1, which causes a type-conversion warning.
9640 # pragma message save
9641 # pragma message disable cvtdiftypes
9643 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
9644 struct fibdef myfib;
9645 #if defined (__DECC) && defined (__VAX)
9646 /* This should be right after the declaration of myatr, but due
9647 * to a bug in VAX DEC C, this takes effect a statement early.
9649 # pragma message restore
9651 /* cast ok for read only parameter */
9652 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
9653 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
9654 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
9656 if (file == NULL || *file == '\0') {
9658 set_vaxc_errno(LIB$_INVARG);
9661 if (do_tovmsspec(file,vmsspec,0) == NULL) return -1;
9663 if (utimes != NULL) {
9664 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
9665 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
9666 * Since time_t is unsigned long int, and lib$emul takes a signed long int
9667 * as input, we force the sign bit to be clear by shifting unixtime right
9668 * one bit, then multiplying by an extra factor of 2 in lib$emul().
9670 lowbit = (utimes->modtime & 1) ? secscale : 0;
9671 unixtime = (long int) utimes->modtime;
9673 /* If input was UTC; convert to local for sys svc */
9674 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
9676 unixtime >>= 1; secscale <<= 1;
9677 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
9678 if (!(retsts & 1)) {
9680 set_vaxc_errno(retsts);
9683 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
9684 if (!(retsts & 1)) {
9686 set_vaxc_errno(retsts);
9691 /* Just get the current time in VMS format directly */
9692 retsts = sys$gettim(bintime);
9693 if (!(retsts & 1)) {
9695 set_vaxc_errno(retsts);
9700 myfab.fab$l_fna = vmsspec;
9701 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
9702 myfab.fab$l_nam = &mynam;
9703 mynam.nam$l_esa = esa;
9704 mynam.nam$b_ess = (unsigned char) sizeof esa;
9705 mynam.nam$l_rsa = rsa;
9706 mynam.nam$b_rss = (unsigned char) sizeof rsa;
9707 if (decc_efs_case_preserve)
9708 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
9710 /* Look for the file to be affected, letting RMS parse the file
9711 * specification for us as well. I have set errno using only
9712 * values documented in the utime() man page for VMS POSIX.
9714 retsts = sys$parse(&myfab,0,0);
9715 if (!(retsts & 1)) {
9716 set_vaxc_errno(retsts);
9717 if (retsts == RMS$_PRV) set_errno(EACCES);
9718 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
9719 else set_errno(EVMSERR);
9722 retsts = sys$search(&myfab,0,0);
9723 if (!(retsts & 1)) {
9724 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
9725 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
9726 set_vaxc_errno(retsts);
9727 if (retsts == RMS$_PRV) set_errno(EACCES);
9728 else if (retsts == RMS$_FNF) set_errno(ENOENT);
9729 else set_errno(EVMSERR);
9733 devdsc.dsc$w_length = mynam.nam$b_dev;
9734 /* cast ok for read only parameter */
9735 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
9737 retsts = sys$assign(&devdsc,&chan,0,0);
9738 if (!(retsts & 1)) {
9739 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
9740 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
9741 set_vaxc_errno(retsts);
9742 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
9743 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
9744 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
9745 else set_errno(EVMSERR);
9749 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
9750 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
9752 memset((void *) &myfib, 0, sizeof myfib);
9753 #if defined(__DECC) || defined(__DECCXX)
9754 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
9755 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
9756 /* This prevents the revision time of the file being reset to the current
9757 * time as a result of our IO$_MODIFY $QIO. */
9758 myfib.fib$l_acctl = FIB$M_NORECORD;
9760 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
9761 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
9762 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
9764 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
9765 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
9766 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
9767 _ckvmssts(sys$dassgn(chan));
9768 if (retsts & 1) retsts = iosb[0];
9769 if (!(retsts & 1)) {
9770 set_vaxc_errno(retsts);
9771 if (retsts == SS$_NOPRIV) set_errno(EACCES);
9772 else set_errno(EVMSERR);
9777 } /* end of my_utime() */
9781 * flex_stat, flex_lstat, flex_fstat
9782 * basic stat, but gets it right when asked to stat
9783 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
9786 #ifndef _USE_STD_STAT
9787 /* encode_dev packs a VMS device name string into an integer to allow
9788 * simple comparisons. This can be used, for example, to check whether two
9789 * files are located on the same device, by comparing their encoded device
9790 * names. Even a string comparison would not do, because stat() reuses the
9791 * device name buffer for each call; so without encode_dev, it would be
9792 * necessary to save the buffer and use strcmp (this would mean a number of
9793 * changes to the standard Perl code, to say nothing of what a Perl script
9796 * The device lock id, if it exists, should be unique (unless perhaps compared
9797 * with lock ids transferred from other nodes). We have a lock id if the disk is
9798 * mounted cluster-wide, which is when we tend to get long (host-qualified)
9799 * device names. Thus we use the lock id in preference, and only if that isn't
9800 * available, do we try to pack the device name into an integer (flagged by
9801 * the sign bit (LOCKID_MASK) being set).
9803 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
9804 * name and its encoded form, but it seems very unlikely that we will find
9805 * two files on different disks that share the same encoded device names,
9806 * and even more remote that they will share the same file id (if the test
9807 * is to check for the same file).
9809 * A better method might be to use sys$device_scan on the first call, and to
9810 * search for the device, returning an index into the cached array.
9811 * The number returned would be more intelligable.
9812 * This is probably not worth it, and anyway would take quite a bit longer
9813 * on the first call.
9815 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
9816 static mydev_t encode_dev (pTHX_ const char *dev)
9819 unsigned long int f;
9824 if (!dev || !dev[0]) return 0;
9828 struct dsc$descriptor_s dev_desc;
9829 unsigned long int status, lockid, item = DVI$_LOCKID;
9831 /* For cluster-mounted disks, the disk lock identifier is unique, so we
9832 can try that first. */
9833 dev_desc.dsc$w_length = strlen (dev);
9834 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
9835 dev_desc.dsc$b_class = DSC$K_CLASS_S;
9836 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
9837 _ckvmssts(lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0));
9838 if (lockid) return (lockid & ~LOCKID_MASK);
9842 /* Otherwise we try to encode the device name */
9846 for (q = dev + strlen(dev); q--; q >= dev) {
9849 else if (isalpha (toupper (*q)))
9850 c= toupper (*q) - 'A' + (char)10;
9852 continue; /* Skip '$'s */
9854 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
9856 enc += f * (unsigned long int) c;
9858 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
9860 } /* end of encode_dev() */
9863 static char namecache[NAM$C_MAXRSS+1];
9866 is_null_device(name)
9869 if (decc_bug_devnull != 0) {
9870 if (strncmp("/dev/null", name, 9) == 0)
9873 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
9874 The underscore prefix, controller letter, and unit number are
9875 independently optional; for our purposes, the colon punctuation
9876 is not. The colon can be trailed by optional directory and/or
9877 filename, but two consecutive colons indicates a nodename rather
9878 than a device. [pr] */
9879 if (*name == '_') ++name;
9880 if (tolower(*name++) != 'n') return 0;
9881 if (tolower(*name++) != 'l') return 0;
9882 if (tolower(*name) == 'a') ++name;
9883 if (*name == '0') ++name;
9884 return (*name++ == ':') && (*name != ':');
9887 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
9888 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
9889 * subset of the applicable information.
9892 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
9894 char fname_phdev[NAM$C_MAXRSS+1];
9895 #if __CRTL_VER >= 80200000 && !defined(__VAX)
9896 /* Namecache not workable with symbolic links, as symbolic links do
9897 * not have extensions and directories do in VMS mode. So in order
9898 * to test this, the did and ino_t must be used.
9900 * Fix-me - Hide the information in the new stat structure
9901 * Get rid of the namecache.
9903 if (decc_posix_compliant_pathnames == 0)
9905 if (statbufp == &PL_statcache)
9906 return cando_by_name(bit,effective,namecache);
9908 char fname[NAM$C_MAXRSS+1];
9909 unsigned long int retsts;
9910 struct dsc$descriptor_s devdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
9911 namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9913 /* If the struct mystat is stale, we're OOL; stat() overwrites the
9914 device name on successive calls */
9915 devdsc.dsc$a_pointer = ((Stat_t *)statbufp)->st_devnam;
9916 devdsc.dsc$w_length = strlen(((Stat_t *)statbufp)->st_devnam);
9917 namdsc.dsc$a_pointer = fname;
9918 namdsc.dsc$w_length = sizeof fname - 1;
9920 retsts = lib$fid_to_name(&devdsc,&(((Stat_t *)statbufp)->st_ino),
9921 &namdsc,&namdsc.dsc$w_length,0,0);
9923 fname[namdsc.dsc$w_length] = '\0';
9925 * lib$fid_to_name returns DVI$_LOGVOLNAM as the device part of the name,
9926 * but if someone has redefined that logical, Perl gets very lost. Since
9927 * we have the physical device name from the stat buffer, just paste it on.
9929 strcpy( fname_phdev, statbufp->st_devnam );
9930 strcat( fname_phdev, strrchr(fname, ':') );
9932 return cando_by_name(bit,effective,fname_phdev);
9934 else if (retsts == SS$_NOSUCHDEV || retsts == SS$_NOSUCHFILE) {
9935 Perl_warn(aTHX_ "Can't get filespec - stale stat buffer?\n");
9939 return FALSE; /* Should never get to here */
9941 } /* end of cando() */
9945 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
9947 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
9949 static char usrname[L_cuserid];
9950 static struct dsc$descriptor_s usrdsc =
9951 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
9952 char vmsname[NAM$C_MAXRSS+1];
9954 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2];
9955 unsigned short int retlen, trnlnm_iter_count;
9956 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9957 union prvdef curprv;
9958 struct itmlst_3 armlst[3] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
9959 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
9960 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
9961 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
9963 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
9965 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9967 if (!fname || !*fname) return FALSE;
9968 /* Make sure we expand logical names, since sys$check_access doesn't */
9969 Newx(fileified, VMS_MAXRSS, char);
9970 if (!strpbrk(fname,"/]>:")) {
9971 strcpy(fileified,fname);
9972 trnlnm_iter_count = 0;
9973 while (!strpbrk(fileified,"/]>:>") && my_trnlnm(fileified,fileified,0)) {
9974 trnlnm_iter_count++;
9975 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
9979 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS)) {
9980 Safefree(fileified);
9983 retlen = namdsc.dsc$w_length = strlen(vmsname);
9984 namdsc.dsc$a_pointer = vmsname;
9985 if (vmsname[retlen-1] == ']' || vmsname[retlen-1] == '>' ||
9986 vmsname[retlen-1] == ':') {
9987 if (!do_fileify_dirspec(vmsname,fileified,1)) return FALSE;
9988 namdsc.dsc$w_length = strlen(fileified);
9989 namdsc.dsc$a_pointer = fileified;
9993 case S_IXUSR: case S_IXGRP: case S_IXOTH:
9994 access = ARM$M_EXECUTE; break;
9995 case S_IRUSR: case S_IRGRP: case S_IROTH:
9996 access = ARM$M_READ; break;
9997 case S_IWUSR: case S_IWGRP: case S_IWOTH:
9998 access = ARM$M_WRITE; break;
9999 case S_IDUSR: case S_IDGRP: case S_IDOTH:
10000 access = ARM$M_DELETE; break;
10002 Safefree(fileified);
10006 /* Before we call $check_access, create a user profile with the current
10007 * process privs since otherwise it just uses the default privs from the
10008 * UAF and might give false positives or negatives. This only works on
10009 * VMS versions v6.0 and later since that's when sys$create_user_profile
10010 * became available.
10013 /* get current process privs and username */
10014 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
10015 _ckvmssts(iosb[0]);
10017 #if defined(__VMS_VER) && __VMS_VER >= 60000000
10019 /* find out the space required for the profile */
10020 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
10021 &usrprodsc.dsc$w_length,0));
10023 /* allocate space for the profile and get it filled in */
10024 Newx(usrprodsc.dsc$a_pointer,usrprodsc.dsc$w_length,char);
10025 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
10026 &usrprodsc.dsc$w_length,0));
10028 /* use the profile to check access to the file; free profile & analyze results */
10029 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,0,0,0,&usrprodsc);
10030 Safefree(usrprodsc.dsc$a_pointer);
10031 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
10035 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
10039 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
10040 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
10041 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
10042 set_vaxc_errno(retsts);
10043 if (retsts == SS$_NOPRIV) set_errno(EACCES);
10044 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
10045 else set_errno(ENOENT);
10046 Safefree(fileified);
10049 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
10050 Safefree(fileified);
10055 Safefree(fileified);
10056 return FALSE; /* Should never get here */
10058 } /* end of cando_by_name() */
10062 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
10064 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
10066 if (!fstat(fd,(stat_t *) statbufp)) {
10067 if (statbufp == (Stat_t *) &PL_statcache) {
10070 /* Save name for cando by name in VMS format */
10071 cptr = getname(fd, namecache, 1);
10073 /* This should not happen, but just in case */
10075 namecache[0] = '\0';
10078 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
10079 #ifndef _USE_STD_STAT
10080 strncpy(statbufp->st_devnam, statbufp->crtl_stat.st_dev, 63);
10081 statbufp->st_devnam[63] = 0;
10082 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
10085 * The device is only encoded so that Perl_cando can use it to
10086 * look up ACLS. So rmsexpand it to the 255 character version
10087 * and store it in ->st_devnam. rmsexpand needs to be fixed
10088 * for long filenames and symbolic links first. This also seems
10089 * to remove the need for a namecache that could be stale.
10093 # ifdef RTL_USES_UTC
10094 # ifdef VMSISH_TIME
10096 statbufp->st_mtime = _toloc(statbufp->st_mtime);
10097 statbufp->st_atime = _toloc(statbufp->st_atime);
10098 statbufp->st_ctime = _toloc(statbufp->st_ctime);
10102 # ifdef VMSISH_TIME
10103 if (!VMSISH_TIME) { /* Return UTC instead of local time */
10107 statbufp->st_mtime = _toutc(statbufp->st_mtime);
10108 statbufp->st_atime = _toutc(statbufp->st_atime);
10109 statbufp->st_ctime = _toutc(statbufp->st_ctime);
10116 } /* end of flex_fstat() */
10119 #if !defined(__VAX) && __CRTL_VER >= 80200000
10127 #define lstat(_x, _y) stat(_x, _y)
10130 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
10133 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
10135 char fileified[NAM$C_MAXRSS+1];
10136 char temp_fspec[NAM$C_MAXRSS+300];
10138 int saved_errno, saved_vaxc_errno;
10140 if (!fspec) return retval;
10141 saved_errno = errno; saved_vaxc_errno = vaxc$errno;
10142 strcpy(temp_fspec, fspec);
10143 if (statbufp == (Stat_t *) &PL_statcache)
10144 do_tovmsspec(temp_fspec,namecache,0);
10145 if (decc_bug_devnull != 0) {
10146 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
10147 memset(statbufp,0,sizeof *statbufp);
10148 statbufp->st_dev = encode_dev(aTHX_ "_NLA0:");
10149 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
10150 statbufp->st_uid = 0x00010001;
10151 statbufp->st_gid = 0x0001;
10152 time((time_t *)&statbufp->st_mtime);
10153 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
10158 /* Try for a directory name first. If fspec contains a filename without
10159 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
10160 * and sea:[wine.dark]water. exist, we prefer the directory here.
10161 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
10162 * not sea:[wine.dark]., if the latter exists. If the intended target is
10163 * the file with null type, specify this by calling flex_stat() with
10164 * a '.' at the end of fspec.
10166 * If we are in Posix filespec mode, accept the filename as is.
10168 #if __CRTL_VER >= 80200000 && !defined(__VAX)
10169 if (decc_posix_compliant_pathnames == 0) {
10171 if (do_fileify_dirspec(temp_fspec,fileified,0) != NULL) {
10172 if (lstat_flag == 0)
10173 retval = stat(fileified,(stat_t *) statbufp);
10175 retval = lstat(fileified,(stat_t *) statbufp);
10176 if (!retval && statbufp == (Stat_t *) &PL_statcache)
10177 strcpy(namecache,fileified);
10180 if (lstat_flag == 0)
10181 retval = stat(temp_fspec,(stat_t *) statbufp);
10183 retval = lstat(temp_fspec,(stat_t *) statbufp);
10185 #if __CRTL_VER >= 80200000 && !defined(__VAX)
10187 if (lstat_flag == 0)
10188 retval = stat(temp_fspec,(stat_t *) statbufp);
10190 retval = lstat(temp_fspec,(stat_t *) statbufp);
10194 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
10195 #ifndef _USE_STD_STAT
10196 strncpy(statbufp->st_devnam, statbufp->crtl_stat.st_dev, 63);
10197 statbufp->st_devnam[63] = 0;
10198 statbufp->st_dev = encode_dev(aTHX_ statbufp->st_devnam);
10201 * The device is only encoded so that Perl_cando can use it to
10202 * look up ACLS. So rmsexpand it to the 255 character version
10203 * and store it in ->st_devnam. rmsexpand needs to be fixed
10204 * for long filenames and symbolic links first. This also seems
10205 * to remove the need for a namecache that could be stale.
10208 # ifdef RTL_USES_UTC
10209 # ifdef VMSISH_TIME
10211 statbufp->st_mtime = _toloc(statbufp->st_mtime);
10212 statbufp->st_atime = _toloc(statbufp->st_atime);
10213 statbufp->st_ctime = _toloc(statbufp->st_ctime);
10217 # ifdef VMSISH_TIME
10218 if (!VMSISH_TIME) { /* Return UTC instead of local time */
10222 statbufp->st_mtime = _toutc(statbufp->st_mtime);
10223 statbufp->st_atime = _toutc(statbufp->st_atime);
10224 statbufp->st_ctime = _toutc(statbufp->st_ctime);
10228 /* If we were successful, leave errno where we found it */
10229 if (retval == 0) { errno = saved_errno; vaxc$errno = saved_vaxc_errno; }
10232 } /* end of flex_stat_int() */
10235 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
10237 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
10239 return flex_stat_int(fspec, statbufp, 0);
10243 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
10245 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
10247 return flex_stat_int(fspec, statbufp, 1);
10252 /*{{{char *my_getlogin()*/
10253 /* VMS cuserid == Unix getlogin, except calling sequence */
10257 static char user[L_cuserid];
10258 return cuserid(user);
10263 /* rmscopy - copy a file using VMS RMS routines
10265 * Copies contents and attributes of spec_in to spec_out, except owner
10266 * and protection information. Name and type of spec_in are used as
10267 * defaults for spec_out. The third parameter specifies whether rmscopy()
10268 * should try to propagate timestamps from the input file to the output file.
10269 * If it is less than 0, no timestamps are preserved. If it is 0, then
10270 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
10271 * propagated to the output file at creation iff the output file specification
10272 * did not contain an explicit name or type, and the revision date is always
10273 * updated at the end of the copy operation. If it is greater than 0, then
10274 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
10275 * other than the revision date should be propagated, and bit 1 indicates
10276 * that the revision date should be propagated.
10278 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
10280 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
10281 * Incorporates, with permission, some code from EZCOPY by Tim Adye
10282 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
10283 * as part of the Perl standard distribution under the terms of the
10284 * GNU General Public License or the Perl Artistic License. Copies
10285 * of each may be found in the Perl standard distribution.
10287 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
10288 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
10290 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
10292 char vmsin[NAM$C_MAXRSS+1], vmsout[NAM$C_MAXRSS+1], esa[NAM$C_MAXRSS],
10293 rsa[NAM$C_MAXRSS], ubf[32256];
10294 unsigned long int i, sts, sts2;
10295 struct FAB fab_in, fab_out;
10296 struct RAB rab_in, rab_out;
10298 struct XABDAT xabdat;
10299 struct XABFHC xabfhc;
10300 struct XABRDT xabrdt;
10301 struct XABSUM xabsum;
10303 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
10304 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
10305 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10309 fab_in = cc$rms_fab;
10310 fab_in.fab$l_fna = vmsin;
10311 fab_in.fab$b_fns = strlen(vmsin);
10312 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
10313 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
10314 fab_in.fab$l_fop = FAB$M_SQO;
10315 fab_in.fab$l_nam = &nam;
10316 fab_in.fab$l_xab = (void *) &xabdat;
10319 nam.nam$l_rsa = rsa;
10320 nam.nam$b_rss = sizeof(rsa);
10321 nam.nam$l_esa = esa;
10322 nam.nam$b_ess = sizeof (esa);
10323 nam.nam$b_esl = nam.nam$b_rsl = 0;
10324 #ifdef NAM$M_NO_SHORT_UPCASE
10325 if (decc_efs_case_preserve)
10326 nam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
10329 xabdat = cc$rms_xabdat; /* To get creation date */
10330 xabdat.xab$l_nxt = (void *) &xabfhc;
10332 xabfhc = cc$rms_xabfhc; /* To get record length */
10333 xabfhc.xab$l_nxt = (void *) &xabsum;
10335 xabsum = cc$rms_xabsum; /* To get key and area information */
10337 if (!((sts = sys$open(&fab_in)) & 1)) {
10338 set_vaxc_errno(sts);
10340 case RMS$_FNF: case RMS$_DNF:
10341 set_errno(ENOENT); break;
10343 set_errno(ENOTDIR); break;
10345 set_errno(ENODEV); break;
10347 set_errno(EINVAL); break;
10349 set_errno(EACCES); break;
10351 set_errno(EVMSERR);
10357 fab_out.fab$w_ifi = 0;
10358 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
10359 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
10360 fab_out.fab$l_fop = FAB$M_SQO;
10361 fab_out.fab$l_fna = vmsout;
10362 fab_out.fab$b_fns = strlen(vmsout);
10363 fab_out.fab$l_dna = nam.nam$l_name;
10364 fab_out.fab$b_dns = nam.nam$l_name ? nam.nam$b_name + nam.nam$b_type : 0;
10366 if (preserve_dates == 0) { /* Act like DCL COPY */
10367 nam.nam$b_nop |= NAM$M_SYNCHK;
10368 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
10369 if (!((sts = sys$parse(&fab_out)) & 1)) {
10370 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
10371 set_vaxc_errno(sts);
10374 fab_out.fab$l_xab = (void *) &xabdat;
10375 if (nam.nam$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
10377 fab_out.fab$l_nam = (void *) 0; /* Done with NAM block */
10378 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
10379 preserve_dates =0; /* bitmask from this point forward */
10381 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
10382 if (!((sts = sys$create(&fab_out)) & 1)) {
10383 set_vaxc_errno(sts);
10386 set_errno(ENOENT); break;
10388 set_errno(ENOTDIR); break;
10390 set_errno(ENODEV); break;
10392 set_errno(EINVAL); break;
10394 set_errno(EACCES); break;
10396 set_errno(EVMSERR);
10400 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
10401 if (preserve_dates & 2) {
10402 /* sys$close() will process xabrdt, not xabdat */
10403 xabrdt = cc$rms_xabrdt;
10405 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
10407 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
10408 * is unsigned long[2], while DECC & VAXC use a struct */
10409 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
10411 fab_out.fab$l_xab = (void *) &xabrdt;
10414 rab_in = cc$rms_rab;
10415 rab_in.rab$l_fab = &fab_in;
10416 rab_in.rab$l_rop = RAB$M_BIO;
10417 rab_in.rab$l_ubf = ubf;
10418 rab_in.rab$w_usz = sizeof ubf;
10419 if (!((sts = sys$connect(&rab_in)) & 1)) {
10420 sys$close(&fab_in); sys$close(&fab_out);
10421 set_errno(EVMSERR); set_vaxc_errno(sts);
10425 rab_out = cc$rms_rab;
10426 rab_out.rab$l_fab = &fab_out;
10427 rab_out.rab$l_rbf = ubf;
10428 if (!((sts = sys$connect(&rab_out)) & 1)) {
10429 sys$close(&fab_in); sys$close(&fab_out);
10430 set_errno(EVMSERR); set_vaxc_errno(sts);
10434 while ((sts = sys$read(&rab_in))) { /* always true */
10435 if (sts == RMS$_EOF) break;
10436 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
10437 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
10438 sys$close(&fab_in); sys$close(&fab_out);
10439 set_errno(EVMSERR); set_vaxc_errno(sts);
10444 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
10445 sys$close(&fab_in); sys$close(&fab_out);
10446 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
10448 set_errno(EVMSERR); set_vaxc_errno(sts);
10454 } /* end of rmscopy() */
10456 /* ODS-5 support version */
10458 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
10460 char *vmsin, * vmsout, *esa, *esa_out,
10462 unsigned long int i, sts, sts2;
10463 struct FAB fab_in, fab_out;
10464 struct RAB rab_in, rab_out;
10466 struct NAML nam_out;
10467 struct XABDAT xabdat;
10468 struct XABFHC xabfhc;
10469 struct XABRDT xabrdt;
10470 struct XABSUM xabsum;
10472 Newx(vmsin, VMS_MAXRSS, char);
10473 Newx(vmsout, VMS_MAXRSS, char);
10474 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1) ||
10475 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1)) {
10478 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10482 Newx(esa, VMS_MAXRSS, char);
10484 fab_in = cc$rms_fab;
10485 fab_in.fab$l_fna = (char *) -1;
10486 fab_in.fab$b_fns = 0;
10487 nam.naml$l_long_filename = vmsin;
10488 nam.naml$l_long_filename_size = strlen(vmsin);
10489 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
10490 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
10491 fab_in.fab$l_fop = FAB$M_SQO;
10492 fab_in.fab$l_naml = &nam;
10493 fab_in.fab$l_xab = (void *) &xabdat;
10495 Newx(rsa, VMS_MAXRSS, char);
10496 nam.naml$l_rsa = NULL;
10497 nam.naml$b_rss = 0;
10498 nam.naml$l_long_result = rsa;
10499 nam.naml$l_long_result_alloc = VMS_MAXRSS - 1;
10500 nam.naml$l_esa = NULL;
10501 nam.naml$b_ess = 0;
10502 nam.naml$l_long_expand = esa;
10503 nam.naml$l_long_expand_alloc = VMS_MAXRSS - 1;
10504 nam.naml$b_esl = nam.naml$b_rsl = 0;
10505 nam.naml$l_long_expand_size = 0;
10506 nam.naml$l_long_result_size = 0;
10507 #ifdef NAM$M_NO_SHORT_UPCASE
10508 if (decc_efs_case_preserve)
10509 nam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
10512 xabdat = cc$rms_xabdat; /* To get creation date */
10513 xabdat.xab$l_nxt = (void *) &xabfhc;
10515 xabfhc = cc$rms_xabfhc; /* To get record length */
10516 xabfhc.xab$l_nxt = (void *) &xabsum;
10518 xabsum = cc$rms_xabsum; /* To get key and area information */
10520 if (!((sts = sys$open(&fab_in)) & 1)) {
10525 set_vaxc_errno(sts);
10527 case RMS$_FNF: case RMS$_DNF:
10528 set_errno(ENOENT); break;
10530 set_errno(ENOTDIR); break;
10532 set_errno(ENODEV); break;
10534 set_errno(EINVAL); break;
10536 set_errno(EACCES); break;
10538 set_errno(EVMSERR);
10545 fab_out.fab$w_ifi = 0;
10546 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
10547 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
10548 fab_out.fab$l_fop = FAB$M_SQO;
10549 fab_out.fab$l_naml = &nam_out;
10550 fab_out.fab$l_fna = (char *) -1;
10551 fab_out.fab$b_fns = 0;
10552 nam_out.naml$l_long_filename = vmsout;
10553 nam_out.naml$l_long_filename_size = strlen(vmsout);
10554 fab_out.fab$l_dna = (char *) -1;
10555 fab_out.fab$b_dns = 0;
10556 nam_out.naml$l_long_defname = nam.naml$l_long_name;
10557 nam_out.naml$l_long_defname_size =
10558 nam.naml$l_long_name ?
10559 nam.naml$l_long_name_size + nam.naml$l_long_type_size : 0;
10561 Newx(esa_out, VMS_MAXRSS, char);
10562 nam_out.naml$l_rsa = NULL;
10563 nam_out.naml$b_rss = 0;
10564 nam_out.naml$l_long_result = NULL;
10565 nam_out.naml$l_long_result_alloc = 0;
10566 nam_out.naml$l_esa = NULL;
10567 nam_out.naml$b_ess = 0;
10568 nam_out.naml$l_long_expand = esa_out;
10569 nam_out.naml$l_long_expand_alloc = VMS_MAXRSS - 1;
10571 if (preserve_dates == 0) { /* Act like DCL COPY */
10572 nam_out.naml$b_nop |= NAM$M_SYNCHK;
10573 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
10574 if (!((sts = sys$parse(&fab_out)) & 1)) {
10580 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
10581 set_vaxc_errno(sts);
10584 fab_out.fab$l_xab = (void *) &xabdat;
10585 if (nam.naml$l_fnb & (NAM$M_EXP_NAME | NAM$M_EXP_TYPE)) preserve_dates = 1;
10587 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
10588 preserve_dates =0; /* bitmask from this point forward */
10590 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
10591 if (!((sts = sys$create(&fab_out)) & 1)) {
10597 set_vaxc_errno(sts);
10600 set_errno(ENOENT); break;
10602 set_errno(ENOTDIR); break;
10604 set_errno(ENODEV); break;
10606 set_errno(EINVAL); break;
10608 set_errno(EACCES); break;
10610 set_errno(EVMSERR);
10614 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
10615 if (preserve_dates & 2) {
10616 /* sys$close() will process xabrdt, not xabdat */
10617 xabrdt = cc$rms_xabrdt;
10619 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
10621 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
10622 * is unsigned long[2], while DECC & VAXC use a struct */
10623 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
10625 fab_out.fab$l_xab = (void *) &xabrdt;
10628 Newx(ubf, 32256, char);
10629 rab_in = cc$rms_rab;
10630 rab_in.rab$l_fab = &fab_in;
10631 rab_in.rab$l_rop = RAB$M_BIO;
10632 rab_in.rab$l_ubf = ubf;
10633 rab_in.rab$w_usz = 32256;
10634 if (!((sts = sys$connect(&rab_in)) & 1)) {
10635 sys$close(&fab_in); sys$close(&fab_out);
10642 set_errno(EVMSERR); set_vaxc_errno(sts);
10646 rab_out = cc$rms_rab;
10647 rab_out.rab$l_fab = &fab_out;
10648 rab_out.rab$l_rbf = ubf;
10649 if (!((sts = sys$connect(&rab_out)) & 1)) {
10650 sys$close(&fab_in); sys$close(&fab_out);
10657 set_errno(EVMSERR); set_vaxc_errno(sts);
10661 while ((sts = sys$read(&rab_in))) { /* always true */
10662 if (sts == RMS$_EOF) break;
10663 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
10664 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
10665 sys$close(&fab_in); sys$close(&fab_out);
10672 set_errno(EVMSERR); set_vaxc_errno(sts);
10678 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
10679 sys$close(&fab_in); sys$close(&fab_out);
10680 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
10688 set_errno(EVMSERR); set_vaxc_errno(sts);
10700 } /* end of rmscopy() */
10705 /*** The following glue provides 'hooks' to make some of the routines
10706 * from this file available from Perl. These routines are sufficiently
10707 * basic, and are required sufficiently early in the build process,
10708 * that's it's nice to have them available to miniperl as well as the
10709 * full Perl, so they're set up here instead of in an extension. The
10710 * Perl code which handles importation of these names into a given
10711 * package lives in [.VMS]Filespec.pm in @INC.
10715 rmsexpand_fromperl(pTHX_ CV *cv)
10718 char *fspec, *defspec = NULL, *rslt;
10721 if (!items || items > 2)
10722 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
10723 fspec = SvPV(ST(0),n_a);
10724 if (!fspec || !*fspec) XSRETURN_UNDEF;
10725 if (items == 2) defspec = SvPV(ST(1),n_a);
10727 rslt = do_rmsexpand(fspec,NULL,1,defspec,0);
10728 ST(0) = sv_newmortal();
10729 if (rslt != NULL) sv_usepvn(ST(0),rslt,strlen(rslt));
10734 vmsify_fromperl(pTHX_ CV *cv)
10740 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
10741 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1);
10742 ST(0) = sv_newmortal();
10743 if (vmsified != NULL) sv_usepvn(ST(0),vmsified,strlen(vmsified));
10748 unixify_fromperl(pTHX_ CV *cv)
10754 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
10755 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1);
10756 ST(0) = sv_newmortal();
10757 if (unixified != NULL) sv_usepvn(ST(0),unixified,strlen(unixified));
10762 fileify_fromperl(pTHX_ CV *cv)
10768 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
10769 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1);
10770 ST(0) = sv_newmortal();
10771 if (fileified != NULL) sv_usepvn(ST(0),fileified,strlen(fileified));
10776 pathify_fromperl(pTHX_ CV *cv)
10782 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
10783 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1);
10784 ST(0) = sv_newmortal();
10785 if (pathified != NULL) sv_usepvn(ST(0),pathified,strlen(pathified));
10790 vmspath_fromperl(pTHX_ CV *cv)
10796 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
10797 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1);
10798 ST(0) = sv_newmortal();
10799 if (vmspath != NULL) sv_usepvn(ST(0),vmspath,strlen(vmspath));
10804 unixpath_fromperl(pTHX_ CV *cv)
10810 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
10811 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1);
10812 ST(0) = sv_newmortal();
10813 if (unixpath != NULL) sv_usepvn(ST(0),unixpath,strlen(unixpath));
10818 candelete_fromperl(pTHX_ CV *cv)
10821 char fspec[NAM$C_MAXRSS+1], *fsp;
10826 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
10828 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
10829 if (SvTYPE(mysv) == SVt_PVGV) {
10830 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
10831 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10838 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
10839 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10845 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
10850 rmscopy_fromperl(pTHX_ CV *cv)
10853 char *inspec, *outspec, *inp, *outp;
10855 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
10856 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10857 unsigned long int sts;
10862 if (items < 2 || items > 3)
10863 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
10865 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
10866 Newx(inspec, VMS_MAXRSS, char);
10867 if (SvTYPE(mysv) == SVt_PVGV) {
10868 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
10869 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10877 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
10878 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10884 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
10885 Newx(outspec, VMS_MAXRSS, char);
10886 if (SvTYPE(mysv) == SVt_PVGV) {
10887 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
10888 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10897 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
10898 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10905 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
10907 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
10913 /* The mod2fname is limited to shorter filenames by design, so it should
10914 * not be modified to support longer EFS pathnames
10917 mod2fname(pTHX_ CV *cv)
10920 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
10921 workbuff[NAM$C_MAXRSS*1 + 1];
10922 int total_namelen = 3, counter, num_entries;
10923 /* ODS-5 ups this, but we want to be consistent, so... */
10924 int max_name_len = 39;
10925 AV *in_array = (AV *)SvRV(ST(0));
10927 num_entries = av_len(in_array);
10929 /* All the names start with PL_. */
10930 strcpy(ultimate_name, "PL_");
10932 /* Clean up our working buffer */
10933 Zero(work_name, sizeof(work_name), char);
10935 /* Run through the entries and build up a working name */
10936 for(counter = 0; counter <= num_entries; counter++) {
10937 /* If it's not the first name then tack on a __ */
10939 strcat(work_name, "__");
10941 strcat(work_name, SvPV(*av_fetch(in_array, counter, FALSE),
10945 /* Check to see if we actually have to bother...*/
10946 if (strlen(work_name) + 3 <= max_name_len) {
10947 strcat(ultimate_name, work_name);
10949 /* It's too darned big, so we need to go strip. We use the same */
10950 /* algorithm as xsubpp does. First, strip out doubled __ */
10951 char *source, *dest, last;
10954 for (source = work_name; *source; source++) {
10955 if (last == *source && last == '_') {
10961 /* Go put it back */
10962 strcpy(work_name, workbuff);
10963 /* Is it still too big? */
10964 if (strlen(work_name) + 3 > max_name_len) {
10965 /* Strip duplicate letters */
10968 for (source = work_name; *source; source++) {
10969 if (last == toupper(*source)) {
10973 last = toupper(*source);
10975 strcpy(work_name, workbuff);
10978 /* Is it *still* too big? */
10979 if (strlen(work_name) + 3 > max_name_len) {
10980 /* Too bad, we truncate */
10981 work_name[max_name_len - 2] = 0;
10983 strcat(ultimate_name, work_name);
10986 /* Okay, return it */
10987 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
10992 hushexit_fromperl(pTHX_ CV *cv)
10997 VMSISH_HUSHED = SvTRUE(ST(0));
10999 ST(0) = boolSV(VMSISH_HUSHED);
11005 Perl_vms_start_glob
11006 (pTHX_ SV *tmpglob,
11010 struct vs_str_st *rslt;
11014 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
11017 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11018 struct dsc$descriptor_vs rsdsc;
11019 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
11020 unsigned long hasver = 0, isunix = 0;
11021 unsigned long int lff_flags = 0;
11024 #ifdef VMS_LONGNAME_SUPPORT
11025 lff_flags = LIB$M_FIL_LONG_NAMES;
11027 /* The Newx macro will not allow me to assign a smaller array
11028 * to the rslt pointer, so we will assign it to the begin char pointer
11029 * and then copy the value into the rslt pointer.
11031 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
11032 rslt = (struct vs_str_st *)begin;
11034 rstr = &rslt->str[0];
11035 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
11036 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
11037 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
11038 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
11040 Newx(vmsspec, VMS_MAXRSS, char);
11042 /* We could find out if there's an explicit dev/dir or version
11043 by peeking into lib$find_file's internal context at
11044 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
11045 but that's unsupported, so I don't want to do it now and
11046 have it bite someone in the future. */
11047 /* Fix-me: vms_split_path() is the only way to do this, the
11048 existing method will fail with many legal EFS or UNIX specifications
11051 cp = SvPV(tmpglob,i);
11054 if (cp[i] == ';') hasver = 1;
11055 if (cp[i] == '.') {
11056 if (sts) hasver = 1;
11059 if (cp[i] == '/') {
11060 hasdir = isunix = 1;
11063 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
11068 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
11071 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
11072 if (!stat_sts && S_ISDIR(st.st_mode)) {
11073 wilddsc.dsc$a_pointer = tovmspath(SvPVX(tmpglob),vmsspec);
11074 ok = (wilddsc.dsc$a_pointer != NULL);
11077 wilddsc.dsc$a_pointer = tovmsspec(SvPVX(tmpglob),vmsspec);
11078 ok = (wilddsc.dsc$a_pointer != NULL);
11081 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
11083 /* If not extended character set, replace ? with % */
11084 /* With extended character set, ? is a wildcard single character */
11085 if (!decc_efs_case_preserve) {
11086 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++)
11087 if (*cp == '?') *cp = '%';
11090 while (ok && $VMS_STATUS_SUCCESS(sts)) {
11091 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
11092 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
11094 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
11095 &dfltdsc,NULL,&rms_sts,&lff_flags);
11096 if (!$VMS_STATUS_SUCCESS(sts))
11099 /* with varying string, 1st word of buffer contains result length */
11100 rstr[rslt->length] = '\0';
11102 /* Find where all the components are */
11103 v_sts = vms_split_path
11118 /* If no version on input, truncate the version on output */
11119 if (!hasver && (vs_len > 0)) {
11123 /* No version & a null extension on UNIX handling */
11124 if (isunix && (e_len == 1) && decc_readdir_dropdotnotype) {
11130 if (!decc_efs_case_preserve) {
11131 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
11135 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
11139 /* Start with the name */
11142 strcat(begin,"\n");
11143 ok = (PerlIO_puts(tmpfp,begin) != EOF);
11145 if (cxt) (void)lib$find_file_end(&cxt);
11146 if (ok && sts != RMS$_NMF &&
11147 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
11150 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
11152 PerlIO_close(tmpfp);
11156 PerlIO_rewind(tmpfp);
11157 IoTYPE(io) = IoTYPE_RDONLY;
11158 IoIFP(io) = fp = tmpfp;
11159 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
11169 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec);
11172 vms_realpath_fromperl(pTHX_ CV *cv)
11175 char *fspec, *rslt_spec, *rslt;
11178 if (!items || items != 1)
11179 Perl_croak(aTHX_ "Usage: VMS::Filespec::vms_realpath(spec)");
11181 fspec = SvPV(ST(0),n_a);
11182 if (!fspec || !*fspec) XSRETURN_UNDEF;
11184 Newx(rslt_spec, VMS_MAXRSS + 1, char);
11185 rslt = do_vms_realpath(fspec, rslt_spec);
11186 ST(0) = sv_newmortal();
11188 sv_usepvn(ST(0),rslt,strlen(rslt));
11190 Safefree(rslt_spec);
11195 #if __CRTL_VER >= 70301000 && !defined(__VAX)
11196 int do_vms_case_tolerant(void);
11199 vms_case_tolerant_fromperl(pTHX_ CV *cv)
11202 ST(0) = boolSV(do_vms_case_tolerant());
11208 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
11209 struct interp_intern *dst)
11211 memcpy(dst,src,sizeof(struct interp_intern));
11215 Perl_sys_intern_clear(pTHX)
11220 Perl_sys_intern_init(pTHX)
11222 unsigned int ix = RAND_MAX;
11227 /* fix me later to track running under GNV */
11228 /* this allows some limited testing */
11229 MY_POSIX_EXIT = decc_filename_unix_report;
11232 MY_INV_RAND_MAX = 1./x;
11236 init_os_extras(void)
11239 char* file = __FILE__;
11240 char temp_buff[512];
11241 if (my_trnlnm("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", temp_buff, 0)) {
11242 no_translate_barewords = TRUE;
11244 no_translate_barewords = FALSE;
11247 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
11248 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
11249 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
11250 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
11251 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
11252 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
11253 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
11254 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
11255 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
11256 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
11257 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
11259 newXSproto("VMS::Filespec::vms_realpath",vms_realpath_fromperl,file,"$;$");
11261 #if __CRTL_VER >= 70301000 && !defined(__VAX)
11262 newXSproto("VMS::Filespec::case_tolerant",vms_case_tolerant_fromperl,file,"$;$");
11265 store_pipelocs(aTHX); /* will redo any earlier attempts */
11272 #if __CRTL_VER == 80200000
11273 /* This missed getting in to the DECC SDK for 8.2 */
11274 char *realpath(const char *file_name, char * resolved_name, ...);
11277 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
11278 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
11279 * The perl fallback routine to provide realpath() is not as efficient
11283 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf)
11285 return realpath(filespec, outbuf);
11289 /* External entry points */
11290 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf)
11291 { return do_vms_realpath(filespec, outbuf); }
11293 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf)
11298 #if __CRTL_VER >= 70301000 && !defined(__VAX)
11299 /* case_tolerant */
11301 /*{{{int do_vms_case_tolerant(void)*/
11302 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
11303 * controlled by a process setting.
11305 int do_vms_case_tolerant(void)
11307 return vms_process_case_tolerant;
11310 /* External entry points */
11311 int Perl_vms_case_tolerant(void)
11312 { return do_vms_case_tolerant(); }
11314 int Perl_vms_case_tolerant(void)
11315 { return vms_process_case_tolerant; }
11319 /* Start of DECC RTL Feature handling */
11321 static int sys_trnlnm
11322 (const char * logname,
11326 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
11327 const unsigned long attr = LNM$M_CASE_BLIND;
11328 struct dsc$descriptor_s name_dsc;
11330 unsigned short result;
11331 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
11334 name_dsc.dsc$w_length = strlen(logname);
11335 name_dsc.dsc$a_pointer = (char *)logname;
11336 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
11337 name_dsc.dsc$b_class = DSC$K_CLASS_S;
11339 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
11341 if ($VMS_STATUS_SUCCESS(status)) {
11343 /* Null terminate and return the string */
11344 /*--------------------------------------*/
11351 static int sys_crelnm
11352 (const char * logname,
11353 const char * value)
11356 const char * proc_table = "LNM$PROCESS_TABLE";
11357 struct dsc$descriptor_s proc_table_dsc;
11358 struct dsc$descriptor_s logname_dsc;
11359 struct itmlst_3 item_list[2];
11361 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
11362 proc_table_dsc.dsc$w_length = strlen(proc_table);
11363 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
11364 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
11366 logname_dsc.dsc$a_pointer = (char *) logname;
11367 logname_dsc.dsc$w_length = strlen(logname);
11368 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
11369 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
11371 item_list[0].buflen = strlen(value);
11372 item_list[0].itmcode = LNM$_STRING;
11373 item_list[0].bufadr = (char *)value;
11374 item_list[0].retlen = NULL;
11376 item_list[1].buflen = 0;
11377 item_list[1].itmcode = 0;
11379 ret_val = sys$crelnm
11381 (const struct dsc$descriptor_s *)&proc_table_dsc,
11382 (const struct dsc$descriptor_s *)&logname_dsc,
11384 (const struct item_list_3 *) item_list);
11390 /* C RTL Feature settings */
11392 static int set_features
11393 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
11394 int (* cli_routine)(void), /* Not documented */
11395 void *image_info) /* Not documented */
11402 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
11403 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
11404 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
11405 unsigned long case_perm;
11406 unsigned long case_image;
11409 /* Allow an exception to bring Perl into the VMS debugger */
11410 vms_debug_on_exception = 0;
11411 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
11412 if ($VMS_STATUS_SUCCESS(status)) {
11413 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
11414 vms_debug_on_exception = 1;
11416 vms_debug_on_exception = 0;
11420 /* hacks to see if known bugs are still present for testing */
11422 /* Readdir is returning filenames in VMS syntax always */
11423 decc_bug_readdir_efs1 = 1;
11424 status = sys_trnlnm("DECC_BUG_READDIR_EFS1", val_str, sizeof(val_str));
11425 if ($VMS_STATUS_SUCCESS(status)) {
11426 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
11427 decc_bug_readdir_efs1 = 1;
11429 decc_bug_readdir_efs1 = 0;
11432 /* PCP mode requires creating /dev/null special device file */
11433 decc_bug_devnull = 1;
11434 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
11435 if ($VMS_STATUS_SUCCESS(status)) {
11436 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
11437 decc_bug_devnull = 1;
11439 decc_bug_devnull = 0;
11442 /* fgetname returning a VMS name in UNIX mode */
11443 decc_bug_fgetname = 1;
11444 status = sys_trnlnm("DECC_BUG_FGETNAME", val_str, sizeof(val_str));
11445 if ($VMS_STATUS_SUCCESS(status)) {
11446 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
11447 decc_bug_fgetname = 1;
11449 decc_bug_fgetname = 0;
11452 /* UNIX directory names with no paths are broken in a lot of places */
11453 decc_dir_barename = 1;
11454 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
11455 if ($VMS_STATUS_SUCCESS(status)) {
11456 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
11457 decc_dir_barename = 1;
11459 decc_dir_barename = 0;
11462 #if __CRTL_VER >= 70300000 && !defined(__VAX)
11463 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
11465 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
11466 if (decc_disable_to_vms_logname_translation < 0)
11467 decc_disable_to_vms_logname_translation = 0;
11470 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
11472 decc_efs_case_preserve = decc$feature_get_value(s, 1);
11473 if (decc_efs_case_preserve < 0)
11474 decc_efs_case_preserve = 0;
11477 s = decc$feature_get_index("DECC$EFS_CHARSET");
11479 decc_efs_charset = decc$feature_get_value(s, 1);
11480 if (decc_efs_charset < 0)
11481 decc_efs_charset = 0;
11484 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
11486 decc_filename_unix_report = decc$feature_get_value(s, 1);
11487 if (decc_filename_unix_report > 0)
11488 decc_filename_unix_report = 1;
11490 decc_filename_unix_report = 0;
11493 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
11495 decc_filename_unix_only = decc$feature_get_value(s, 1);
11496 if (decc_filename_unix_only > 0) {
11497 decc_filename_unix_only = 1;
11500 decc_filename_unix_only = 0;
11504 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
11506 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
11507 if (decc_filename_unix_no_version < 0)
11508 decc_filename_unix_no_version = 0;
11511 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
11513 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
11514 if (decc_readdir_dropdotnotype < 0)
11515 decc_readdir_dropdotnotype = 0;
11518 status = sys_trnlnm("SYS$POSIX_ROOT", val_str, sizeof(val_str));
11519 if ($VMS_STATUS_SUCCESS(status)) {
11520 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
11522 dflt = decc$feature_get_value(s, 4);
11524 decc_disable_posix_root = decc$feature_get_value(s, 1);
11525 if (decc_disable_posix_root <= 0) {
11526 decc$feature_set_value(s, 1, 1);
11527 decc_disable_posix_root = 1;
11531 /* Traditionally Perl assumes this is off */
11532 decc_disable_posix_root = 1;
11533 decc$feature_set_value(s, 1, 1);
11538 #if __CRTL_VER >= 80200000
11539 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
11541 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
11542 if (decc_posix_compliant_pathnames < 0)
11543 decc_posix_compliant_pathnames = 0;
11544 if (decc_posix_compliant_pathnames > 4)
11545 decc_posix_compliant_pathnames = 0;
11550 status = sys_trnlnm
11551 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
11552 if ($VMS_STATUS_SUCCESS(status)) {
11553 val_str[0] = _toupper(val_str[0]);
11554 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11555 decc_disable_to_vms_logname_translation = 1;
11560 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
11561 if ($VMS_STATUS_SUCCESS(status)) {
11562 val_str[0] = _toupper(val_str[0]);
11563 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11564 decc_efs_case_preserve = 1;
11569 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
11570 if ($VMS_STATUS_SUCCESS(status)) {
11571 val_str[0] = _toupper(val_str[0]);
11572 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11573 decc_filename_unix_report = 1;
11576 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
11577 if ($VMS_STATUS_SUCCESS(status)) {
11578 val_str[0] = _toupper(val_str[0]);
11579 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11580 decc_filename_unix_only = 1;
11581 decc_filename_unix_report = 1;
11584 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
11585 if ($VMS_STATUS_SUCCESS(status)) {
11586 val_str[0] = _toupper(val_str[0]);
11587 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11588 decc_filename_unix_no_version = 1;
11591 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
11592 if ($VMS_STATUS_SUCCESS(status)) {
11593 val_str[0] = _toupper(val_str[0]);
11594 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
11595 decc_readdir_dropdotnotype = 1;
11600 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
11602 /* Report true case tolerance */
11603 /*----------------------------*/
11604 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
11605 if (!$VMS_STATUS_SUCCESS(status))
11606 case_perm = PPROP$K_CASE_BLIND;
11607 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
11608 if (!$VMS_STATUS_SUCCESS(status))
11609 case_image = PPROP$K_CASE_BLIND;
11610 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
11611 (case_image == PPROP$K_CASE_SENSITIVE))
11612 vms_process_case_tolerant = 0;
11617 /* CRTL can be initialized past this point, but not before. */
11618 /* DECC$CRTL_INIT(); */
11624 /* DECC dependent attributes */
11625 #if __DECC_VER < 60560002
11627 #define not_executable
11629 #define relative ,rel
11630 #define not_executable ,noexe
11633 #pragma extern_model save
11634 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
11636 const __align (LONGWORD) int spare[8] = {0};
11637 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, */
11640 #pragma extern_model strict_refdef "LIB$INITIALIZE" con, gbl,noshr, \
11641 nowrt,noshr relative not_executable
11643 const long vms_cc_features = (const long)set_features;
11646 ** Force a reference to LIB$INITIALIZE to ensure it
11647 ** exists in the image.
11649 int lib$initialize(void);
11651 #pragma extern_model strict_refdef
11653 int lib_init_ref = (int) lib$initialize;
11656 #pragma extern_model restore