3 * VMS-specific routines for perl5
5 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
6 * 2002, 2003, 2004, 2005, 2006, 2007 by Charles Bailey and others.
8 * You may distribute under the terms of either the GNU General Public
9 * License or the Artistic License, as specified in the README file.
11 * Please see Changes*.* or the Perl Repository Browser for revision history.
15 * Yet small as was their hunted band
16 * still fell and fearless was each hand,
17 * and strong deeds they wrought yet oft,
18 * and loved the woods, whose ways more soft
19 * them seemed than thralls of that black throne
20 * to live and languish in halls of stone.
21 * "The Lay of Leithian", Canto II, lines 135-40
23 * [p.162 of _The Lays of Beleriand_]
29 #if __CRTL_VER < 70300000
30 /* needed for home-rolled utime() */
36 #include <climsgdef.h>
46 #include <libclidef.h>
48 #include <lib$routines.h>
51 #if __CRTL_VER >= 70301000 && !defined(__VAX)
61 #include <str$routines.h>
67 #define NO_EFN EFN$C_ENF
69 #if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
70 int decc$feature_get_index(const char *name);
71 char* decc$feature_get_name(int index);
72 int decc$feature_get_value(int index, int mode);
73 int decc$feature_set_value(int index, int mode, int value);
78 #pragma member_alignment save
79 #pragma nomember_alignment longword
84 unsigned short * retadr;
86 #pragma member_alignment restore
88 /* Older versions of ssdef.h don't have these */
89 #ifndef SS$_INVFILFOROP
90 # define SS$_INVFILFOROP 3930
92 #ifndef SS$_NOSUCHOBJECT
93 # define SS$_NOSUCHOBJECT 2696
96 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
97 #define PERLIO_NOT_STDIO 0
99 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
100 * code below needs to get to the underlying CRTL routines. */
101 #define DONT_MASK_RTL_CALLS
105 /* Anticipating future expansion in lexical warnings . . . */
106 #ifndef WARN_INTERNAL
107 # define WARN_INTERNAL WARN_MISC
110 #ifdef VMS_LONGNAME_SUPPORT
111 #include <libfildef.h>
114 #if !defined(__VAX) && __CRTL_VER >= 80200000
122 #define lstat(_x, _y) stat(_x, _y)
125 /* Routine to create a decterm for use with the Perl debugger */
126 /* No headers, this information was found in the Programming Concepts Manual */
128 static int (*decw_term_port)
129 (const struct dsc$descriptor_s * display,
130 const struct dsc$descriptor_s * setup_file,
131 const struct dsc$descriptor_s * customization,
132 struct dsc$descriptor_s * result_device_name,
133 unsigned short * result_device_name_length,
136 void * char_change_buffer) = 0;
138 /* gcc's header files don't #define direct access macros
139 * corresponding to VAXC's variant structs */
141 # define uic$v_format uic$r_uic_form.uic$v_format
142 # define uic$v_group uic$r_uic_form.uic$v_group
143 # define uic$v_member uic$r_uic_form.uic$v_member
144 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
145 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
146 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
147 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
150 #if defined(NEED_AN_H_ERRNO)
154 #if defined(__DECC) || defined(__DECCXX)
155 #pragma member_alignment save
156 #pragma nomember_alignment longword
158 #pragma message disable misalgndmem
161 unsigned short int buflen;
162 unsigned short int itmcode;
164 unsigned short int *retlen;
167 struct filescan_itmlst_2 {
168 unsigned short length;
169 unsigned short itmcode;
174 unsigned short length;
175 char str[VMS_MAXRSS];
176 unsigned short pad; /* for longword struct alignment */
179 #if defined(__DECC) || defined(__DECCXX)
180 #pragma message restore
181 #pragma member_alignment restore
184 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
185 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
186 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
187 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
188 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
189 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
190 #define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c)
191 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
192 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
193 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
194 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
195 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
197 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
198 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
199 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
200 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
202 static char * int_rmsexpand_vms(
203 const char * filespec, char * outbuf, unsigned opts);
204 static char * int_rmsexpand_tovms(
205 const char * filespec, char * outbuf, unsigned opts);
206 static char *int_tovmsspec
207 (const char *path, char *buf, int dir_flag, int * utf8_flag);
208 static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl);
209 static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
210 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl);
212 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
213 #define PERL_LNM_MAX_ALLOWED_INDEX 127
215 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
216 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
219 #define PERL_LNM_MAX_ITER 10
221 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
222 #if __CRTL_VER >= 70302000 && !defined(__VAX)
223 #define MAX_DCL_SYMBOL (8192)
224 #define MAX_DCL_LINE_LENGTH (4096 - 4)
226 #define MAX_DCL_SYMBOL (1024)
227 #define MAX_DCL_LINE_LENGTH (1024 - 4)
230 static char *__mystrtolower(char *str)
232 if (str) for (; *str; ++str) *str= tolower(*str);
236 static struct dsc$descriptor_s fildevdsc =
237 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
238 static struct dsc$descriptor_s crtlenvdsc =
239 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
240 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
241 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
242 static struct dsc$descriptor_s **env_tables = defenv;
243 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
245 /* True if we shouldn't treat barewords as logicals during directory */
247 static int no_translate_barewords;
249 /* DECC Features that may need to affect how Perl interprets
250 * displays filename information
252 static int decc_disable_to_vms_logname_translation = 1;
253 static int decc_disable_posix_root = 1;
254 int decc_efs_case_preserve = 0;
255 static int decc_efs_charset = 0;
256 static int decc_efs_charset_index = -1;
257 static int decc_filename_unix_no_version = 0;
258 static int decc_filename_unix_only = 0;
259 int decc_filename_unix_report = 0;
260 int decc_posix_compliant_pathnames = 0;
261 int decc_readdir_dropdotnotype = 0;
262 static int vms_process_case_tolerant = 1;
263 int vms_vtf7_filenames = 0;
264 int gnv_unix_shell = 0;
265 static int vms_unlink_all_versions = 0;
266 static int vms_posix_exit = 0;
268 /* bug workarounds if needed */
269 int decc_bug_devnull = 1;
270 int decc_dir_barename = 0;
271 int vms_bug_stat_filename = 0;
273 static int vms_debug_on_exception = 0;
274 static int vms_debug_fileify = 0;
276 /* Simple logical name translation */
277 static int simple_trnlnm
278 (const char * logname,
282 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
283 const unsigned long attr = LNM$M_CASE_BLIND;
284 struct dsc$descriptor_s name_dsc;
286 unsigned short result;
287 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
290 name_dsc.dsc$w_length = strlen(logname);
291 name_dsc.dsc$a_pointer = (char *)logname;
292 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
293 name_dsc.dsc$b_class = DSC$K_CLASS_S;
295 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
297 if ($VMS_STATUS_SUCCESS(status)) {
299 /* Null terminate and return the string */
300 /*--------------------------------------*/
309 /* Is this a UNIX file specification?
310 * No longer a simple check with EFS file specs
311 * For now, not a full check, but need to
312 * handle POSIX ^UP^ specifications
313 * Fixing to handle ^/ cases would require
314 * changes to many other conversion routines.
317 static int is_unix_filespec(const char *path)
323 if (strncmp(path,"\"^UP^",5) != 0) {
324 pch1 = strchr(path, '/');
329 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
330 if (decc_filename_unix_report || decc_filename_unix_only) {
331 if (strcmp(path,".") == 0)
339 /* This routine converts a UCS-2 character to be VTF-7 encoded.
342 static void ucs2_to_vtf7
344 unsigned long ucs2_char,
347 unsigned char * ucs_ptr;
350 ucs_ptr = (unsigned char *)&ucs2_char;
354 hex = (ucs_ptr[1] >> 4) & 0xf;
356 outspec[2] = hex + '0';
358 outspec[2] = (hex - 9) + 'A';
359 hex = ucs_ptr[1] & 0xF;
361 outspec[3] = hex + '0';
363 outspec[3] = (hex - 9) + 'A';
365 hex = (ucs_ptr[0] >> 4) & 0xf;
367 outspec[4] = hex + '0';
369 outspec[4] = (hex - 9) + 'A';
370 hex = ucs_ptr[1] & 0xF;
372 outspec[5] = hex + '0';
374 outspec[5] = (hex - 9) + 'A';
380 /* This handles the conversion of a UNIX extended character set to a ^
381 * escaped VMS character.
382 * in a UNIX file specification.
384 * The output count variable contains the number of characters added
385 * to the output string.
387 * The return value is the number of characters read from the input string
389 static int copy_expand_unix_filename_escape
390 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
397 utf8_flag = *utf8_fl;
401 if (*inspec >= 0x80) {
402 if (utf8_fl && vms_vtf7_filenames) {
403 unsigned long ucs_char;
407 if ((*inspec & 0xE0) == 0xC0) {
409 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
410 if (ucs_char >= 0x80) {
411 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
414 } else if ((*inspec & 0xF0) == 0xE0) {
416 ucs_char = ((inspec[0] & 0xF) << 12) +
417 ((inspec[1] & 0x3f) << 6) +
419 if (ucs_char >= 0x800) {
420 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
424 #if 0 /* I do not see longer sequences supported by OpenVMS */
425 /* Maybe some one can fix this later */
426 } else if ((*inspec & 0xF8) == 0xF0) {
429 } else if ((*inspec & 0xFC) == 0xF8) {
432 } else if ((*inspec & 0xFE) == 0xFC) {
439 /* High bit set, but not a Unicode character! */
441 /* Non printing DECMCS or ISO Latin-1 character? */
442 if ((unsigned char)*inspec <= 0x9F) {
446 hex = (*inspec >> 4) & 0xF;
448 outspec[1] = hex + '0';
450 outspec[1] = (hex - 9) + 'A';
454 outspec[2] = hex + '0';
456 outspec[2] = (hex - 9) + 'A';
460 } else if ((unsigned char)*inspec == 0xA0) {
466 } else if ((unsigned char)*inspec == 0xFF) {
478 /* Is this a macro that needs to be passed through?
479 * Macros start with $( and an alpha character, followed
480 * by a string of alpha numeric characters ending with a )
481 * If this does not match, then encode it as ODS-5.
483 if ((inspec[0] == '$') && (inspec[1] == '(')) {
486 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
488 outspec[0] = inspec[0];
489 outspec[1] = inspec[1];
490 outspec[2] = inspec[2];
492 while(isalnum(inspec[tcnt]) ||
493 (inspec[2] == '.') || (inspec[2] == '_')) {
494 outspec[tcnt] = inspec[tcnt];
497 if (inspec[tcnt] == ')') {
498 outspec[tcnt] = inspec[tcnt];
515 if (decc_efs_charset == 0)
542 /* Don't escape again if following character is
543 * already something we escape.
545 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
551 /* But otherwise fall through and escape it. */
553 /* Assume that this is to be escaped */
555 outspec[1] = *inspec;
559 case ' ': /* space */
560 /* Assume that this is to be escaped */
576 /* This handles the expansion of a '^' prefix to the proper character
577 * in a UNIX file specification.
579 * The output count variable contains the number of characters added
580 * to the output string.
582 * The return value is the number of characters read from the input
585 static int copy_expand_vms_filename_escape
586 (char *outspec, const char *inspec, int *output_cnt)
593 if (*inspec == '^') {
596 /* Spaces and non-trailing dots should just be passed through,
597 * but eat the escape character.
604 case '_': /* space */
610 /* Hmm. Better leave the escape escaped. */
616 case 'U': /* Unicode - FIX-ME this is wrong. */
619 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
622 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
623 outspec[0] = c1 & 0xff;
624 outspec[1] = c2 & 0xff;
631 /* Error - do best we can to continue */
641 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
645 scnt = sscanf(inspec, "%2x", &c1);
646 outspec[0] = c1 & 0xff;
667 /* vms_split_path - Verify that the input file specification is a
668 * VMS format file specification, and provide pointers to the components of
669 * it. With EFS format filenames, this is virtually the only way to
670 * parse a VMS path specification into components.
672 * If the sum of the components do not add up to the length of the
673 * string, then the passed file specification is probably a UNIX style
676 static int vms_split_path
691 struct dsc$descriptor path_desc;
695 struct filescan_itmlst_2 item_list[9];
696 const int filespec = 0;
697 const int nodespec = 1;
698 const int devspec = 2;
699 const int rootspec = 3;
700 const int dirspec = 4;
701 const int namespec = 5;
702 const int typespec = 6;
703 const int verspec = 7;
705 /* Assume the worst for an easy exit */
719 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
720 path_desc.dsc$w_length = strlen(path);
721 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
722 path_desc.dsc$b_class = DSC$K_CLASS_S;
724 /* Get the total length, if it is shorter than the string passed
725 * then this was probably not a VMS formatted file specification
727 item_list[filespec].itmcode = FSCN$_FILESPEC;
728 item_list[filespec].length = 0;
729 item_list[filespec].component = NULL;
731 /* If the node is present, then it gets considered as part of the
732 * volume name to hopefully make things simple.
734 item_list[nodespec].itmcode = FSCN$_NODE;
735 item_list[nodespec].length = 0;
736 item_list[nodespec].component = NULL;
738 item_list[devspec].itmcode = FSCN$_DEVICE;
739 item_list[devspec].length = 0;
740 item_list[devspec].component = NULL;
742 /* root is a special case, adding it to either the directory or
743 * the device components will probably complicate things for the
744 * callers of this routine, so leave it separate.
746 item_list[rootspec].itmcode = FSCN$_ROOT;
747 item_list[rootspec].length = 0;
748 item_list[rootspec].component = NULL;
750 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
751 item_list[dirspec].length = 0;
752 item_list[dirspec].component = NULL;
754 item_list[namespec].itmcode = FSCN$_NAME;
755 item_list[namespec].length = 0;
756 item_list[namespec].component = NULL;
758 item_list[typespec].itmcode = FSCN$_TYPE;
759 item_list[typespec].length = 0;
760 item_list[typespec].component = NULL;
762 item_list[verspec].itmcode = FSCN$_VERSION;
763 item_list[verspec].length = 0;
764 item_list[verspec].component = NULL;
766 item_list[8].itmcode = 0;
767 item_list[8].length = 0;
768 item_list[8].component = NULL;
770 status = sys$filescan
771 ((const struct dsc$descriptor_s *)&path_desc, item_list,
773 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
775 /* If we parsed it successfully these two lengths should be the same */
776 if (path_desc.dsc$w_length != item_list[filespec].length)
779 /* If we got here, then it is a VMS file specification */
782 /* set the volume name */
783 if (item_list[nodespec].length > 0) {
784 *volume = item_list[nodespec].component;
785 *vol_len = item_list[nodespec].length + item_list[devspec].length;
788 *volume = item_list[devspec].component;
789 *vol_len = item_list[devspec].length;
792 *root = item_list[rootspec].component;
793 *root_len = item_list[rootspec].length;
795 *dir = item_list[dirspec].component;
796 *dir_len = item_list[dirspec].length;
798 /* Now fun with versions and EFS file specifications
799 * The parser can not tell the difference when a "." is a version
800 * delimiter or a part of the file specification.
802 if ((decc_efs_charset) &&
803 (item_list[verspec].length > 0) &&
804 (item_list[verspec].component[0] == '.')) {
805 *name = item_list[namespec].component;
806 *name_len = item_list[namespec].length + item_list[typespec].length;
807 *ext = item_list[verspec].component;
808 *ext_len = item_list[verspec].length;
813 *name = item_list[namespec].component;
814 *name_len = item_list[namespec].length;
815 *ext = item_list[typespec].component;
816 *ext_len = item_list[typespec].length;
817 *version = item_list[verspec].component;
818 *ver_len = item_list[verspec].length;
823 /* Routine to determine if the file specification ends with .dir */
824 static int is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len) {
826 /* e_len must be 4, and version must be <= 2 characters */
827 if (e_len != 4 || vs_len > 2)
830 /* If a version number is present, it needs to be one */
831 if ((vs_len == 2) && (vs_spec[1] != '1'))
834 /* Look for the DIR on the extension */
835 if (vms_process_case_tolerant) {
836 if ((toupper(e_spec[1]) == 'D') &&
837 (toupper(e_spec[2]) == 'I') &&
838 (toupper(e_spec[3]) == 'R')) {
842 /* Directory extensions are supposed to be in upper case only */
843 /* I would not be surprised if this rule can not be enforced */
844 /* if and when someone fully debugs the case sensitive mode */
845 if ((e_spec[1] == 'D') &&
846 (e_spec[2] == 'I') &&
847 (e_spec[3] == 'R')) {
856 * Routine to retrieve the maximum equivalence index for an input
857 * logical name. Some calls to this routine have no knowledge if
858 * the variable is a logical or not. So on error we return a max
861 /*{{{int my_maxidx(const char *lnm) */
863 my_maxidx(const char *lnm)
867 int attr = LNM$M_CASE_BLIND;
868 struct dsc$descriptor lnmdsc;
869 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
872 lnmdsc.dsc$w_length = strlen(lnm);
873 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
874 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
875 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
877 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
878 if ((status & 1) == 0)
885 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
887 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
888 struct dsc$descriptor_s **tabvec, unsigned long int flags)
891 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
892 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
893 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
895 unsigned char acmode;
896 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
897 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
898 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
899 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
901 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
902 #if defined(PERL_IMPLICIT_CONTEXT)
905 aTHX = PERL_GET_INTERP;
911 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
912 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
914 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
915 *cp2 = _toupper(*cp1);
916 if (cp1 - lnm > LNM$C_NAMLENGTH) {
917 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
921 lnmdsc.dsc$w_length = cp1 - lnm;
922 lnmdsc.dsc$a_pointer = uplnm;
923 uplnm[lnmdsc.dsc$w_length] = '\0';
924 secure = flags & PERL__TRNENV_SECURE;
925 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
926 if (!tabvec || !*tabvec) tabvec = env_tables;
928 for (curtab = 0; tabvec[curtab]; curtab++) {
929 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
930 if (!ivenv && !secure) {
935 #if defined(PERL_IMPLICIT_CONTEXT)
938 "Can't read CRTL environ\n");
941 Perl_warn(aTHX_ "Can't read CRTL environ\n");
944 retsts = SS$_NOLOGNAM;
945 for (i = 0; environ[i]; i++) {
946 if ((eq = strchr(environ[i],'=')) &&
947 lnmdsc.dsc$w_length == (eq - environ[i]) &&
948 !strncmp(environ[i],uplnm,eq - environ[i])) {
950 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
951 if (!eqvlen) continue;
956 if (retsts != SS$_NOLOGNAM) break;
959 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
960 !str$case_blind_compare(&tmpdsc,&clisym)) {
961 if (!ivsym && !secure) {
962 unsigned short int deflen = LNM$C_NAMLENGTH;
963 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
964 /* dynamic dsc to accommodate possible long value */
965 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
966 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
968 if (eqvlen > MAX_DCL_SYMBOL) {
969 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
970 eqvlen = MAX_DCL_SYMBOL;
971 /* Special hack--we might be called before the interpreter's */
972 /* fully initialized, in which case either thr or PL_curcop */
973 /* might be bogus. We have to check, since ckWARN needs them */
974 /* both to be valid if running threaded */
975 #if defined(PERL_IMPLICIT_CONTEXT)
978 "Value of CLI symbol \"%s\" too long",lnm);
981 if (ckWARN(WARN_MISC)) {
982 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
985 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
987 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
988 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
989 if (retsts == LIB$_NOSUCHSYM) continue;
994 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
995 midx = my_maxidx(lnm);
996 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
997 lnmlst[1].bufadr = cp2;
999 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1000 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1001 if (retsts == SS$_NOLOGNAM) break;
1002 /* PPFs have a prefix */
1005 *((int *)uplnm) == *((int *)"SYS$") &&
1007 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
1008 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
1009 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
1010 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
1011 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
1012 memmove(eqv,eqv+4,eqvlen-4);
1018 if ((retsts == SS$_IVLOGNAM) ||
1019 (retsts == SS$_NOLOGNAM)) { continue; }
1022 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1023 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1024 if (retsts == SS$_NOLOGNAM) continue;
1027 eqvlen = strlen(eqv);
1031 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1032 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1033 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
1034 retsts == SS$_NOLOGNAM) {
1035 set_errno(EINVAL); set_vaxc_errno(retsts);
1037 else _ckvmssts_noperl(retsts);
1039 } /* end of vmstrnenv */
1042 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1043 /* Define as a function so we can access statics. */
1044 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1048 #if defined(PERL_IMPLICIT_CONTEXT)
1051 #ifdef SECURE_INTERNAL_GETENV
1052 flags = (PL_curinterp ? TAINTING_get : will_taint) ?
1053 PERL__TRNENV_SECURE : 0;
1056 return vmstrnenv(lnm, eqv, idx, fildev, flags);
1061 * Note: Uses Perl temp to store result so char * can be returned to
1062 * caller; this pointer will be invalidated at next Perl statement
1064 * We define this as a function rather than a macro in terms of my_getenv_len()
1065 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1068 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1070 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1073 static char *__my_getenv_eqv = NULL;
1074 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1075 unsigned long int idx = 0;
1076 int success, secure, saverr, savvmserr;
1080 midx = my_maxidx(lnm) + 1;
1082 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1083 /* Set up a temporary buffer for the return value; Perl will
1084 * clean it up at the next statement transition */
1085 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1086 if (!tmpsv) return NULL;
1090 /* Assume no interpreter ==> single thread */
1091 if (__my_getenv_eqv != NULL) {
1092 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1095 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1097 eqv = __my_getenv_eqv;
1100 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1101 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1103 getcwd(eqv,LNM$C_NAMLENGTH);
1107 /* Get rid of "000000/ in rooted filespecs */
1110 zeros = strstr(eqv, "/000000/");
1111 if (zeros != NULL) {
1113 mlen = len - (zeros - eqv) - 7;
1114 memmove(zeros, &zeros[7], mlen);
1122 /* Impose security constraints only if tainting */
1124 /* Impose security constraints only if tainting */
1125 secure = PL_curinterp ? TAINTING_get : will_taint;
1126 saverr = errno; savvmserr = vaxc$errno;
1133 #ifdef SECURE_INTERNAL_GETENV
1134 secure ? PERL__TRNENV_SECURE : 0
1140 /* For the getenv interface we combine all the equivalence names
1141 * of a search list logical into one value to acquire a maximum
1142 * value length of 255*128 (assuming %ENV is using logicals).
1144 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1146 /* If the name contains a semicolon-delimited index, parse it
1147 * off and make sure we only retrieve the equivalence name for
1149 if ((cp2 = strchr(lnm,';')) != NULL) {
1150 my_strlcpy(uplnm, lnm, cp2 - lnm + 1);
1151 idx = strtoul(cp2+1,NULL,0);
1153 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1156 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1158 /* Discard NOLOGNAM on internal calls since we're often looking
1159 * for an optional name, and this "error" often shows up as the
1160 * (bogus) exit status for a die() call later on. */
1161 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1162 return success ? eqv : NULL;
1165 } /* end of my_getenv() */
1169 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1171 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1175 unsigned long idx = 0;
1177 static char *__my_getenv_len_eqv = NULL;
1178 int secure, saverr, savvmserr;
1181 midx = my_maxidx(lnm) + 1;
1183 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1184 /* Set up a temporary buffer for the return value; Perl will
1185 * clean it up at the next statement transition */
1186 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1187 if (!tmpsv) return NULL;
1191 /* Assume no interpreter ==> single thread */
1192 if (__my_getenv_len_eqv != NULL) {
1193 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1196 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1198 buf = __my_getenv_len_eqv;
1201 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1202 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1205 getcwd(buf,LNM$C_NAMLENGTH);
1208 /* Get rid of "000000/ in rooted filespecs */
1210 zeros = strstr(buf, "/000000/");
1211 if (zeros != NULL) {
1213 mlen = *len - (zeros - buf) - 7;
1214 memmove(zeros, &zeros[7], mlen);
1223 /* Impose security constraints only if tainting */
1224 secure = PL_curinterp ? TAINTING_get : will_taint;
1225 saverr = errno; savvmserr = vaxc$errno;
1232 #ifdef SECURE_INTERNAL_GETENV
1233 secure ? PERL__TRNENV_SECURE : 0
1239 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1241 if ((cp2 = strchr(lnm,';')) != NULL) {
1242 my_strlcpy(buf, lnm, cp2 - lnm + 1);
1243 idx = strtoul(cp2+1,NULL,0);
1245 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1248 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1250 /* Get rid of "000000/ in rooted filespecs */
1253 zeros = strstr(buf, "/000000/");
1254 if (zeros != NULL) {
1256 mlen = *len - (zeros - buf) - 7;
1257 memmove(zeros, &zeros[7], mlen);
1263 /* Discard NOLOGNAM on internal calls since we're often looking
1264 * for an optional name, and this "error" often shows up as the
1265 * (bogus) exit status for a die() call later on. */
1266 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1267 return *len ? buf : NULL;
1270 } /* end of my_getenv_len() */
1273 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1275 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1277 /*{{{ void prime_env_iter() */
1279 prime_env_iter(void)
1280 /* Fill the %ENV associative array with all logical names we can
1281 * find, in preparation for iterating over it.
1284 static int primed = 0;
1285 HV *seenhv = NULL, *envhv;
1287 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1288 unsigned short int chan;
1289 #ifndef CLI$M_TRUSTED
1290 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1292 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1293 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0;
1295 bool have_sym = FALSE, have_lnm = FALSE;
1296 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1297 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1298 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1299 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1300 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1301 #if defined(PERL_IMPLICIT_CONTEXT)
1304 #if defined(USE_ITHREADS)
1305 static perl_mutex primenv_mutex;
1306 MUTEX_INIT(&primenv_mutex);
1309 #if defined(PERL_IMPLICIT_CONTEXT)
1310 /* We jump through these hoops because we can be called at */
1311 /* platform-specific initialization time, which is before anything is */
1312 /* set up--we can't even do a plain dTHX since that relies on the */
1313 /* interpreter structure to be initialized */
1315 aTHX = PERL_GET_INTERP;
1317 /* we never get here because the NULL pointer will cause the */
1318 /* several of the routines called by this routine to access violate */
1320 /* This routine is only called by hv.c/hv_iterinit which has a */
1321 /* context, so the real fix may be to pass it through instead of */
1322 /* the hoops above */
1327 if (primed || !PL_envgv) return;
1328 MUTEX_LOCK(&primenv_mutex);
1329 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1330 envhv = GvHVn(PL_envgv);
1331 /* Perform a dummy fetch as an lval to insure that the hash table is
1332 * set up. Otherwise, the hv_store() will turn into a nullop. */
1333 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1335 for (i = 0; env_tables[i]; i++) {
1336 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1337 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1338 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1340 if (have_sym || have_lnm) {
1341 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1342 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1343 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1344 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1347 for (i--; i >= 0; i--) {
1348 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1351 for (j = 0; environ[j]; j++) {
1352 if (!(start = strchr(environ[j],'='))) {
1353 if (ckWARN(WARN_INTERNAL))
1354 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1358 sv = newSVpv(start,0);
1360 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1365 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1366 !str$case_blind_compare(&tmpdsc,&clisym)) {
1367 my_strlcpy(cmd, "Show Symbol/Global *", sizeof(cmd));
1368 cmddsc.dsc$w_length = 20;
1369 if (env_tables[i]->dsc$w_length == 12 &&
1370 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1371 !str$case_blind_compare(&tmpdsc,&local)) my_strlcpy(cmd+12, "Local *", sizeof(cmd)-12);
1372 flags = defflags | CLI$M_NOLOGNAM;
1375 my_strlcpy(cmd, "Show Logical *", sizeof(cmd));
1376 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1377 my_strlcat(cmd," /Table=", sizeof(cmd));
1378 cmddsc.dsc$w_length = my_strlcat(cmd, env_tables[i]->dsc$a_pointer, env_tables[i]->dsc$w_length + 1);
1380 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1381 flags = defflags | CLI$M_NOCLISYM;
1384 /* Create a new subprocess to execute each command, to exclude the
1385 * remote possibility that someone could subvert a mbx or file used
1386 * to write multiple commands to a single subprocess.
1389 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1390 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1391 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1392 defflags &= ~CLI$M_TRUSTED;
1393 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1395 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1396 if (seenhv) SvREFCNT_dec(seenhv);
1399 char *cp1, *cp2, *key;
1400 unsigned long int sts, iosb[2], retlen, keylen;
1403 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1404 if (sts & 1) sts = iosb[0] & 0xffff;
1405 if (sts == SS$_ENDOFFILE) {
1407 while (substs == 0) { sys$hiber(); wakect++;}
1408 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1413 retlen = iosb[0] >> 16;
1414 if (!retlen) continue; /* blank line */
1416 if (iosb[1] != subpid) {
1418 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1422 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1423 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1425 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1426 if (*cp1 == '(' || /* Logical name table name */
1427 *cp1 == '=' /* Next eqv of searchlist */) continue;
1428 if (*cp1 == '"') cp1++;
1429 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1430 key = cp1; keylen = cp2 - cp1;
1431 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1432 while (*cp2 && *cp2 != '=') cp2++;
1433 while (*cp2 && *cp2 == '=') cp2++;
1434 while (*cp2 && *cp2 == ' ') cp2++;
1435 if (*cp2 == '"') { /* String translation; may embed "" */
1436 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1437 cp2++; cp1--; /* Skip "" surrounding translation */
1439 else { /* Numeric translation */
1440 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1441 cp1--; /* stop on last non-space char */
1443 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1444 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1447 PERL_HASH(hash,key,keylen);
1449 if (cp1 == cp2 && *cp2 == '.') {
1450 /* A single dot usually means an unprintable character, such as a null
1451 * to indicate a zero-length value. Get the actual value to make sure.
1453 char lnm[LNM$C_NAMLENGTH+1];
1454 char eqv[MAX_DCL_SYMBOL+1];
1456 strncpy(lnm, key, keylen);
1457 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1458 sv = newSVpvn(eqv, strlen(eqv));
1461 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1465 hv_store(envhv,key,keylen,sv,hash);
1466 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1468 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1469 /* get the PPFs for this process, not the subprocess */
1470 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1471 char eqv[LNM$C_NAMLENGTH+1];
1473 for (i = 0; ppfs[i]; i++) {
1474 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1475 sv = newSVpv(eqv,trnlen);
1477 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1482 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1483 if (buf) Safefree(buf);
1484 if (seenhv) SvREFCNT_dec(seenhv);
1485 MUTEX_UNLOCK(&primenv_mutex);
1488 } /* end of prime_env_iter */
1492 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1493 /* Define or delete an element in the same "environment" as
1494 * vmstrnenv(). If an element is to be deleted, it's removed from
1495 * the first place it's found. If it's to be set, it's set in the
1496 * place designated by the first element of the table vector.
1497 * Like setenv() returns 0 for success, non-zero on error.
1500 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1503 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1504 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1506 unsigned long int retsts, usermode = PSL$C_USER;
1507 struct itmlst_3 *ile, *ilist;
1508 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1509 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1510 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1511 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1512 $DESCRIPTOR(local,"_LOCAL");
1515 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1516 return SS$_IVLOGNAM;
1519 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1520 *cp2 = _toupper(*cp1);
1521 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1522 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1523 return SS$_IVLOGNAM;
1526 lnmdsc.dsc$w_length = cp1 - lnm;
1527 if (!tabvec || !*tabvec) tabvec = env_tables;
1529 if (!eqv) { /* we're deleting n element */
1530 for (curtab = 0; tabvec[curtab]; curtab++) {
1531 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1533 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1534 if ((cp1 = strchr(environ[i],'=')) &&
1535 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1536 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1538 return setenv(lnm,"",1) ? vaxc$errno : 0;
1541 ivenv = 1; retsts = SS$_NOLOGNAM;
1543 if (ckWARN(WARN_INTERNAL))
1544 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1545 ivenv = 1; retsts = SS$_NOSUCHPGM;
1551 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1552 !str$case_blind_compare(&tmpdsc,&clisym)) {
1553 unsigned int symtype;
1554 if (tabvec[curtab]->dsc$w_length == 12 &&
1555 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1556 !str$case_blind_compare(&tmpdsc,&local))
1557 symtype = LIB$K_CLI_LOCAL_SYM;
1558 else symtype = LIB$K_CLI_GLOBAL_SYM;
1559 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1560 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1561 if (retsts == LIB$_NOSUCHSYM) continue;
1565 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1566 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1567 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1568 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1569 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1573 else { /* we're defining a value */
1574 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1576 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1578 if (ckWARN(WARN_INTERNAL))
1579 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1580 retsts = SS$_NOSUCHPGM;
1584 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1585 eqvdsc.dsc$w_length = strlen(eqv);
1586 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1587 !str$case_blind_compare(&tmpdsc,&clisym)) {
1588 unsigned int symtype;
1589 if (tabvec[0]->dsc$w_length == 12 &&
1590 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1591 !str$case_blind_compare(&tmpdsc,&local))
1592 symtype = LIB$K_CLI_LOCAL_SYM;
1593 else symtype = LIB$K_CLI_GLOBAL_SYM;
1594 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1597 if (!*eqv) eqvdsc.dsc$w_length = 1;
1598 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1600 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1601 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1602 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1603 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1604 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1605 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1608 Newx(ilist,nseg+1,struct itmlst_3);
1611 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1614 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1616 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1617 ile->itmcode = LNM$_STRING;
1619 if ((j+1) == nseg) {
1620 ile->buflen = strlen(c);
1621 /* in case we are truncating one that's too long */
1622 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1625 ile->buflen = LNM$C_NAMLENGTH;
1629 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1633 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1638 if (!(retsts & 1)) {
1640 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1641 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1642 set_errno(EVMSERR); break;
1643 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1644 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1645 set_errno(EINVAL); break;
1647 set_errno(EACCES); break;
1652 set_vaxc_errno(retsts);
1653 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1656 /* We reset error values on success because Perl does an hv_fetch()
1657 * before each hv_store(), and if the thing we're setting didn't
1658 * previously exist, we've got a leftover error message. (Of course,
1659 * this fails in the face of
1660 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1661 * in that the error reported in $! isn't spurious,
1662 * but it's right more often than not.)
1664 set_errno(0); set_vaxc_errno(retsts);
1668 } /* end of vmssetenv() */
1671 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1672 /* This has to be a function since there's a prototype for it in proto.h */
1674 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1677 int len = strlen(lnm);
1681 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1682 if (!strcmp(uplnm,"DEFAULT")) {
1683 if (eqv && *eqv) my_chdir(eqv);
1688 (void) vmssetenv(lnm,eqv,NULL);
1692 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1694 * sets a user-mode logical in the process logical name table
1695 * used for redirection of sys$error
1698 Perl_vmssetuserlnm(const char *name, const char *eqv)
1700 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1701 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1702 unsigned long int iss, attr = LNM$M_CONFINE;
1703 unsigned char acmode = PSL$C_USER;
1704 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1706 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1707 d_name.dsc$w_length = strlen(name);
1709 lnmlst[0].buflen = strlen(eqv);
1710 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1712 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1713 if (!(iss&1)) lib$signal(iss);
1718 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1719 /* my_crypt - VMS password hashing
1720 * my_crypt() provides an interface compatible with the Unix crypt()
1721 * C library function, and uses sys$hash_password() to perform VMS
1722 * password hashing. The quadword hashed password value is returned
1723 * as a NUL-terminated 8 character string. my_crypt() does not change
1724 * the case of its string arguments; in order to match the behavior
1725 * of LOGINOUT et al., alphabetic characters in both arguments must
1726 * be upcased by the caller.
1728 * - fix me to call ACM services when available
1731 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1733 # ifndef UAI$C_PREFERRED_ALGORITHM
1734 # define UAI$C_PREFERRED_ALGORITHM 127
1736 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1737 unsigned short int salt = 0;
1738 unsigned long int sts;
1740 unsigned short int dsc$w_length;
1741 unsigned char dsc$b_type;
1742 unsigned char dsc$b_class;
1743 const char * dsc$a_pointer;
1744 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1745 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1746 struct itmlst_3 uailst[3] = {
1747 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1748 { sizeof salt, UAI$_SALT, &salt, 0},
1749 { 0, 0, NULL, NULL}};
1750 static char hash[9];
1752 usrdsc.dsc$w_length = strlen(usrname);
1753 usrdsc.dsc$a_pointer = usrname;
1754 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1756 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1760 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1765 set_vaxc_errno(sts);
1766 if (sts != RMS$_RNF) return NULL;
1769 txtdsc.dsc$w_length = strlen(textpasswd);
1770 txtdsc.dsc$a_pointer = textpasswd;
1771 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1772 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1775 return (char *) hash;
1777 } /* end of my_crypt() */
1781 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1782 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1783 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1785 /* fixup barenames that are directories for internal use.
1786 * There have been problems with the consistent handling of UNIX
1787 * style directory names when routines are presented with a name that
1788 * has no directory delimiters at all. So this routine will eventually
1791 static char * fixup_bare_dirnames(const char * name)
1793 if (decc_disable_to_vms_logname_translation) {
1799 /* 8.3, remove() is now broken on symbolic links */
1800 static int rms_erase(const char * vmsname);
1804 * A little hack to get around a bug in some implementation of remove()
1805 * that do not know how to delete a directory
1807 * Delete any file to which user has control access, regardless of whether
1808 * delete access is explicitly allowed.
1809 * Limitations: User must have write access to parent directory.
1810 * Does not block signals or ASTs; if interrupted in midstream
1811 * may leave file with an altered ACL.
1814 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1816 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1820 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1821 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1822 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1824 unsigned char myace$b_length;
1825 unsigned char myace$b_type;
1826 unsigned short int myace$w_flags;
1827 unsigned long int myace$l_access;
1828 unsigned long int myace$l_ident;
1829 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1830 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1831 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1833 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1834 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1835 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1836 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1837 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1838 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1840 /* Expand the input spec using RMS, since the CRTL remove() and
1841 * system services won't do this by themselves, so we may miss
1842 * a file "hiding" behind a logical name or search list. */
1843 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
1844 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1846 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
1848 PerlMem_free(vmsname);
1852 /* Erase the file */
1853 rmsts = rms_erase(vmsname);
1855 /* Did it succeed */
1856 if ($VMS_STATUS_SUCCESS(rmsts)) {
1857 PerlMem_free(vmsname);
1861 /* If not, can changing protections help? */
1862 if (rmsts != RMS$_PRV) {
1863 set_vaxc_errno(rmsts);
1864 PerlMem_free(vmsname);
1868 /* No, so we get our own UIC to use as a rights identifier,
1869 * and the insert an ACE at the head of the ACL which allows us
1870 * to delete the file.
1872 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1873 fildsc.dsc$w_length = strlen(vmsname);
1874 fildsc.dsc$a_pointer = vmsname;
1876 newace.myace$l_ident = oldace.myace$l_ident;
1878 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1880 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1881 set_errno(ENOENT); break;
1883 set_errno(ENOTDIR); break;
1885 set_errno(ENODEV); break;
1886 case RMS$_SYN: case SS$_INVFILFOROP:
1887 set_errno(EINVAL); break;
1889 set_errno(EACCES); break;
1891 _ckvmssts_noperl(aclsts);
1893 set_vaxc_errno(aclsts);
1894 PerlMem_free(vmsname);
1897 /* Grab any existing ACEs with this identifier in case we fail */
1898 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1899 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1900 || fndsts == SS$_NOMOREACE ) {
1901 /* Add the new ACE . . . */
1902 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1905 rmsts = rms_erase(vmsname);
1906 if ($VMS_STATUS_SUCCESS(rmsts)) {
1911 /* We blew it - dir with files in it, no write priv for
1912 * parent directory, etc. Put things back the way they were. */
1913 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1916 addlst[0].bufadr = &oldace;
1917 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1924 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1925 /* We just deleted it, so of course it's not there. Some versions of
1926 * VMS seem to return success on the unlock operation anyhow (after all
1927 * the unlock is successful), but others don't.
1929 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1930 if (aclsts & 1) aclsts = fndsts;
1931 if (!(aclsts & 1)) {
1933 set_vaxc_errno(aclsts);
1936 PerlMem_free(vmsname);
1939 } /* end of kill_file() */
1943 /*{{{int do_rmdir(char *name)*/
1945 Perl_do_rmdir(pTHX_ const char *name)
1951 /* lstat returns a VMS fileified specification of the name */
1952 /* that is looked up, and also lets verifies that this is a directory */
1954 retval = flex_lstat(name, &st);
1958 /* Due to a historical feature, flex_stat/lstat can not see some */
1959 /* Unix format file names that the rest of the CRTL can see */
1960 /* Fixing that feature will cause some perl tests to fail */
1961 /* So try this one more time. */
1963 retval = lstat(name, &st.crtl_stat);
1967 /* force it to a file spec for the kill file to work. */
1968 ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
1969 if (ret_spec == NULL) {
1975 if (!S_ISDIR(st.st_mode)) {
1980 dirfile = st.st_devnam;
1982 /* It may be possible for flex_stat to find a file and vmsify() to */
1983 /* fail with ODS-2 specifications. mp_do_kill_file can not deal */
1984 /* with that case, so fail it */
1985 if (dirfile[0] == 0) {
1990 retval = mp_do_kill_file(aTHX_ dirfile, 1);
1995 } /* end of do_rmdir */
1999 * Delete any file to which user has control access, regardless of whether
2000 * delete access is explicitly allowed.
2001 * Limitations: User must have write access to parent directory.
2002 * Does not block signals or ASTs; if interrupted in midstream
2003 * may leave file with an altered ACL.
2006 /*{{{int kill_file(char *name)*/
2008 Perl_kill_file(pTHX_ const char *name)
2014 /* Convert the filename to VMS format and see if it is a directory */
2015 /* flex_lstat returns a vmsified file specification */
2016 rmsts = flex_lstat(name, &st);
2019 /* Due to a historical feature, flex_stat/lstat can not see some */
2020 /* Unix format file names that the rest of the CRTL can see when */
2021 /* ODS-2 file specifications are in use. */
2022 /* Fixing that feature will cause some perl tests to fail */
2023 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2025 vmsfile = (char *) name; /* cast ok */
2028 vmsfile = st.st_devnam;
2029 if (vmsfile[0] == 0) {
2030 /* It may be possible for flex_stat to find a file and vmsify() */
2031 /* to fail with ODS-2 specifications. mp_do_kill_file can not */
2032 /* deal with that case, so fail it */
2038 /* Remove() is allowed to delete directories, according to the X/Open
2040 * This may need special handling to work with the ACL hacks.
2042 if (S_ISDIR(st.st_mode)) {
2043 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2047 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2049 /* Need to delete all versions ? */
2050 if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2053 /* Just use lstat() here as do not need st_dev */
2054 /* and we know that the file is in VMS format or that */
2055 /* because of a historical bug, flex_stat can not see the file */
2056 while (lstat(vmsfile, (stat_t *)&st) == 0) {
2057 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2062 /* Make sure that we do not loop forever */
2073 } /* end of kill_file() */
2077 /*{{{int my_mkdir(char *,Mode_t)*/
2079 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2081 STRLEN dirlen = strlen(dir);
2083 /* zero length string sometimes gives ACCVIO */
2084 if (dirlen == 0) return -1;
2086 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2087 * null file name/type. However, it's commonplace under Unix,
2088 * so we'll allow it for a gain in portability.
2090 if (dir[dirlen-1] == '/') {
2091 char *newdir = savepvn(dir,dirlen-1);
2092 int ret = mkdir(newdir,mode);
2096 else return mkdir(dir,mode);
2097 } /* end of my_mkdir */
2100 /*{{{int my_chdir(char *)*/
2102 Perl_my_chdir(pTHX_ const char *dir)
2104 STRLEN dirlen = strlen(dir);
2106 /* zero length string sometimes gives ACCVIO */
2107 if (dirlen == 0) return -1;
2110 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2111 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2112 * so that existing scripts do not need to be changed.
2115 while ((dirlen > 0) && (*dir1 == ' ')) {
2120 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2122 * null file name/type. However, it's commonplace under Unix,
2123 * so we'll allow it for a gain in portability.
2125 * '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
2127 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2130 newdir = (char *)PerlMem_malloc(dirlen);
2132 _ckvmssts_noperl(SS$_INSFMEM);
2133 memcpy(newdir, dir1, dirlen-1);
2134 newdir[dirlen-1] = '\0';
2135 ret = chdir(newdir);
2136 PerlMem_free(newdir);
2139 else return chdir(dir1);
2140 } /* end of my_chdir */
2144 /*{{{int my_chmod(char *, mode_t)*/
2146 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2151 STRLEN speclen = strlen(file_spec);
2153 /* zero length string sometimes gives ACCVIO */
2154 if (speclen == 0) return -1;
2156 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2157 * that implies null file name/type. However, it's commonplace under Unix,
2158 * so we'll allow it for a gain in portability.
2160 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2161 * in VMS file.dir notation.
2163 changefile = (char *) file_spec; /* cast ok */
2164 ret = flex_lstat(file_spec, &st);
2167 /* Due to a historical feature, flex_stat/lstat can not see some */
2168 /* Unix format file names that the rest of the CRTL can see when */
2169 /* ODS-2 file specifications are in use. */
2170 /* Fixing that feature will cause some perl tests to fail */
2171 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2175 /* It may be possible to get here with nothing in st_devname */
2176 /* chmod still may work though */
2177 if (st.st_devnam[0] != 0) {
2178 changefile = st.st_devnam;
2181 ret = chmod(changefile, mode);
2183 } /* end of my_chmod */
2187 /*{{{FILE *my_tmpfile()*/
2194 if ((fp = tmpfile())) return fp;
2196 cp = (char *)PerlMem_malloc(L_tmpnam+24);
2197 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2199 if (decc_filename_unix_only == 0)
2200 strcpy(cp,"Sys$Scratch:");
2203 tmpnam(cp+strlen(cp));
2204 strcat(cp,".Perltmp");
2205 fp = fopen(cp,"w+","fop=dlt");
2213 * The C RTL's sigaction fails to check for invalid signal numbers so we
2214 * help it out a bit. The docs are correct, but the actual routine doesn't
2215 * do what the docs say it will.
2217 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2219 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2220 struct sigaction* oact)
2222 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2223 SETERRNO(EINVAL, SS$_INVARG);
2226 return sigaction(sig, act, oact);
2230 #ifdef KILL_BY_SIGPRC
2231 #include <errnodef.h>
2233 /* We implement our own kill() using the undocumented system service
2234 sys$sigprc for one of two reasons:
2236 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2237 target process to do a sys$exit, which usually can't be handled
2238 gracefully...certainly not by Perl and the %SIG{} mechanism.
2240 2.) If the kill() in the CRTL can't be called from a signal
2241 handler without disappearing into the ether, i.e., the signal
2242 it purportedly sends is never trapped. Still true as of VMS 7.3.
2244 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2245 in the target process rather than calling sys$exit.
2247 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2248 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2249 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2250 with condition codes C$_SIG0+nsig*8, catching the exception on the
2251 target process and resignaling with appropriate arguments.
2253 But we don't have that VMS 7.0+ exception handler, so if you
2254 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2256 Also note that SIGTERM is listed in the docs as being "unimplemented",
2257 yet always seems to be signaled with a VMS condition code of 4 (and
2258 correctly handled for that code). So we hardwire it in.
2260 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2261 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2262 than signalling with an unrecognized (and unhandled by CRTL) code.
2265 #define _MY_SIG_MAX 28
2268 Perl_sig_to_vmscondition_int(int sig)
2270 static unsigned int sig_code[_MY_SIG_MAX+1] =
2273 SS$_HANGUP, /* 1 SIGHUP */
2274 SS$_CONTROLC, /* 2 SIGINT */
2275 SS$_CONTROLY, /* 3 SIGQUIT */
2276 SS$_RADRMOD, /* 4 SIGILL */
2277 SS$_BREAK, /* 5 SIGTRAP */
2278 SS$_OPCCUS, /* 6 SIGABRT */
2279 SS$_COMPAT, /* 7 SIGEMT */
2281 SS$_FLTOVF, /* 8 SIGFPE VAX */
2283 SS$_HPARITH, /* 8 SIGFPE AXP */
2285 SS$_ABORT, /* 9 SIGKILL */
2286 SS$_ACCVIO, /* 10 SIGBUS */
2287 SS$_ACCVIO, /* 11 SIGSEGV */
2288 SS$_BADPARAM, /* 12 SIGSYS */
2289 SS$_NOMBX, /* 13 SIGPIPE */
2290 SS$_ASTFLT, /* 14 SIGALRM */
2307 static int initted = 0;
2310 sig_code[16] = C$_SIGUSR1;
2311 sig_code[17] = C$_SIGUSR2;
2312 sig_code[20] = C$_SIGCHLD;
2313 #if __CRTL_VER >= 70300000
2314 sig_code[28] = C$_SIGWINCH;
2318 if (sig < _SIG_MIN) return 0;
2319 if (sig > _MY_SIG_MAX) return 0;
2320 return sig_code[sig];
2324 Perl_sig_to_vmscondition(int sig)
2327 if (vms_debug_on_exception != 0)
2328 lib$signal(SS$_DEBUG);
2330 return Perl_sig_to_vmscondition_int(sig);
2334 #define sys$sigprc SYS$SIGPRC
2338 int sys$sigprc(unsigned int *pidadr,
2339 struct dsc$descriptor_s *prcname,
2346 Perl_my_kill(int pid, int sig)
2351 /* sig 0 means validate the PID */
2352 /*------------------------------*/
2354 const unsigned long int jpicode = JPI$_PID;
2357 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2358 if ($VMS_STATUS_SUCCESS(status))
2361 case SS$_NOSUCHNODE:
2362 case SS$_UNREACHABLE:
2376 code = Perl_sig_to_vmscondition_int(sig);
2379 SETERRNO(EINVAL, SS$_BADPARAM);
2383 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2384 * signals are to be sent to multiple processes.
2385 * pid = 0 - all processes in group except ones that the system exempts
2386 * pid = -1 - all processes except ones that the system exempts
2387 * pid = -n - all processes in group (abs(n)) except ...
2388 * For now, just report as not supported.
2392 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2396 iss = sys$sigprc((unsigned int *)&pid,0,code);
2397 if (iss&1) return 0;
2401 set_errno(EPERM); break;
2403 case SS$_NOSUCHNODE:
2404 case SS$_UNREACHABLE:
2405 set_errno(ESRCH); break;
2407 set_errno(ENOMEM); break;
2409 _ckvmssts_noperl(iss);
2412 set_vaxc_errno(iss);
2418 /* Routine to convert a VMS status code to a UNIX status code.
2419 ** More tricky than it appears because of conflicting conventions with
2422 ** VMS status codes are a bit mask, with the least significant bit set for
2425 ** Special UNIX status of EVMSERR indicates that no translation is currently
2426 ** available, and programs should check the VMS status code.
2428 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2432 #ifndef C_FACILITY_NO
2433 #define C_FACILITY_NO 0x350000
2436 #define DCL_IVVERB 0x38090
2439 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2447 /* Assume the best or the worst */
2448 if (vms_status & STS$M_SUCCESS)
2451 unix_status = EVMSERR;
2453 msg_status = vms_status & ~STS$M_CONTROL;
2455 facility = vms_status & STS$M_FAC_NO;
2456 fac_sp = vms_status & STS$M_FAC_SP;
2457 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2459 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2465 unix_status = EFAULT;
2467 case SS$_DEVOFFLINE:
2468 unix_status = EBUSY;
2471 unix_status = ENOTCONN;
2479 case SS$_INVFILFOROP:
2483 unix_status = EINVAL;
2485 case SS$_UNSUPPORTED:
2486 unix_status = ENOTSUP;
2491 unix_status = EACCES;
2493 case SS$_DEVICEFULL:
2494 unix_status = ENOSPC;
2497 unix_status = ENODEV;
2499 case SS$_NOSUCHFILE:
2500 case SS$_NOSUCHOBJECT:
2501 unix_status = ENOENT;
2503 case SS$_ABORT: /* Fatal case */
2504 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2505 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2506 unix_status = EINTR;
2509 unix_status = E2BIG;
2512 unix_status = ENOMEM;
2515 unix_status = EPERM;
2517 case SS$_NOSUCHNODE:
2518 case SS$_UNREACHABLE:
2519 unix_status = ESRCH;
2522 unix_status = ECHILD;
2525 if ((facility == 0) && (msg_no < 8)) {
2526 /* These are not real VMS status codes so assume that they are
2527 ** already UNIX status codes
2529 unix_status = msg_no;
2535 /* Translate a POSIX exit code to a UNIX exit code */
2536 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2537 unix_status = (msg_no & 0x07F8) >> 3;
2541 /* Documented traditional behavior for handling VMS child exits */
2542 /*--------------------------------------------------------------*/
2543 if (child_flag != 0) {
2545 /* Success / Informational return 0 */
2546 /*----------------------------------*/
2547 if (msg_no & STS$K_SUCCESS)
2550 /* Warning returns 1 */
2551 /*-------------------*/
2552 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2555 /* Everything else pass through the severity bits */
2556 /*------------------------------------------------*/
2557 return (msg_no & STS$M_SEVERITY);
2560 /* Normal VMS status to ERRNO mapping attempt */
2561 /*--------------------------------------------*/
2562 switch(msg_status) {
2563 /* case RMS$_EOF: */ /* End of File */
2564 case RMS$_FNF: /* File Not Found */
2565 case RMS$_DNF: /* Dir Not Found */
2566 unix_status = ENOENT;
2568 case RMS$_RNF: /* Record Not Found */
2569 unix_status = ESRCH;
2572 unix_status = ENOTDIR;
2575 unix_status = ENODEV;
2580 unix_status = EBADF;
2583 unix_status = EEXIST;
2587 case LIB$_INVSTRDES:
2589 case LIB$_NOSUCHSYM:
2590 case LIB$_INVSYMNAM:
2592 unix_status = EINVAL;
2598 unix_status = E2BIG;
2600 case RMS$_PRV: /* No privilege */
2601 case RMS$_ACC: /* ACP file access failed */
2602 case RMS$_WLK: /* Device write locked */
2603 unix_status = EACCES;
2605 case RMS$_MKD: /* Failed to mark for delete */
2606 unix_status = EPERM;
2608 /* case RMS$_NMF: */ /* No more files */
2616 /* Try to guess at what VMS error status should go with a UNIX errno
2617 * value. This is hard to do as there could be many possible VMS
2618 * error statuses that caused the errno value to be set.
2621 int Perl_unix_status_to_vms(int unix_status)
2623 int test_unix_status;
2625 /* Trivial cases first */
2626 /*---------------------*/
2627 if (unix_status == EVMSERR)
2630 /* Is vaxc$errno sane? */
2631 /*---------------------*/
2632 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2633 if (test_unix_status == unix_status)
2636 /* If way out of range, must be VMS code already */
2637 /*-----------------------------------------------*/
2638 if (unix_status > EVMSERR)
2641 /* If out of range, punt */
2642 /*-----------------------*/
2643 if (unix_status > __ERRNO_MAX)
2647 /* Ok, now we have to do it the hard way. */
2648 /*----------------------------------------*/
2649 switch(unix_status) {
2650 case 0: return SS$_NORMAL;
2651 case EPERM: return SS$_NOPRIV;
2652 case ENOENT: return SS$_NOSUCHOBJECT;
2653 case ESRCH: return SS$_UNREACHABLE;
2654 case EINTR: return SS$_ABORT;
2657 case E2BIG: return SS$_BUFFEROVF;
2659 case EBADF: return RMS$_IFI;
2660 case ECHILD: return SS$_NONEXPR;
2662 case ENOMEM: return SS$_INSFMEM;
2663 case EACCES: return SS$_FILACCERR;
2664 case EFAULT: return SS$_ACCVIO;
2666 case EBUSY: return SS$_DEVOFFLINE;
2667 case EEXIST: return RMS$_FEX;
2669 case ENODEV: return SS$_NOSUCHDEV;
2670 case ENOTDIR: return RMS$_DIR;
2672 case EINVAL: return SS$_INVARG;
2678 case ENOSPC: return SS$_DEVICEFULL;
2679 case ESPIPE: return LIB$_INVARG;
2684 case ERANGE: return LIB$_INVARG;
2685 /* case EWOULDBLOCK */
2686 /* case EINPROGRESS */
2689 /* case EDESTADDRREQ */
2691 /* case EPROTOTYPE */
2692 /* case ENOPROTOOPT */
2693 /* case EPROTONOSUPPORT */
2694 /* case ESOCKTNOSUPPORT */
2695 /* case EOPNOTSUPP */
2696 /* case EPFNOSUPPORT */
2697 /* case EAFNOSUPPORT */
2698 /* case EADDRINUSE */
2699 /* case EADDRNOTAVAIL */
2701 /* case ENETUNREACH */
2702 /* case ENETRESET */
2703 /* case ECONNABORTED */
2704 /* case ECONNRESET */
2707 case ENOTCONN: return SS$_CLEARED;
2708 /* case ESHUTDOWN */
2709 /* case ETOOMANYREFS */
2710 /* case ETIMEDOUT */
2711 /* case ECONNREFUSED */
2713 /* case ENAMETOOLONG */
2714 /* case EHOSTDOWN */
2715 /* case EHOSTUNREACH */
2716 /* case ENOTEMPTY */
2728 /* case ECANCELED */
2732 return SS$_UNSUPPORTED;
2738 /* case EABANDONED */
2740 return SS$_ABORT; /* punt */
2745 /* default piping mailbox size */
2747 # define PERL_BUFSIZ 512
2749 # define PERL_BUFSIZ 8192
2754 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2756 unsigned long int mbxbufsiz;
2757 static unsigned long int syssize = 0;
2758 unsigned long int dviitm = DVI$_DEVNAM;
2759 char csize[LNM$C_NAMLENGTH+1];
2763 unsigned long syiitm = SYI$_MAXBUF;
2765 * Get the SYSGEN parameter MAXBUF
2767 * If the logical 'PERL_MBX_SIZE' is defined
2768 * use the value of the logical instead of PERL_BUFSIZ, but
2769 * keep the size between 128 and MAXBUF.
2772 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2775 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2776 mbxbufsiz = atoi(csize);
2778 mbxbufsiz = PERL_BUFSIZ;
2780 if (mbxbufsiz < 128) mbxbufsiz = 128;
2781 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2783 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2785 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2786 _ckvmssts_noperl(sts);
2787 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2789 } /* end of create_mbx() */
2792 /*{{{ my_popen and my_pclose*/
2794 typedef struct _iosb IOSB;
2795 typedef struct _iosb* pIOSB;
2796 typedef struct _pipe Pipe;
2797 typedef struct _pipe* pPipe;
2798 typedef struct pipe_details Info;
2799 typedef struct pipe_details* pInfo;
2800 typedef struct _srqp RQE;
2801 typedef struct _srqp* pRQE;
2802 typedef struct _tochildbuf CBuf;
2803 typedef struct _tochildbuf* pCBuf;
2806 unsigned short status;
2807 unsigned short count;
2808 unsigned long dvispec;
2811 #pragma member_alignment save
2812 #pragma nomember_alignment quadword
2813 struct _srqp { /* VMS self-relative queue entry */
2814 unsigned long qptr[2];
2816 #pragma member_alignment restore
2817 static RQE RQE_ZERO = {0,0};
2819 struct _tochildbuf {
2822 unsigned short size;
2830 unsigned short chan_in;
2831 unsigned short chan_out;
2833 unsigned int bufsize;
2845 #if defined(PERL_IMPLICIT_CONTEXT)
2846 void *thx; /* Either a thread or an interpreter */
2847 /* pointer, depending on how we're built */
2855 PerlIO *fp; /* file pointer to pipe mailbox */
2856 int useFILE; /* using stdio, not perlio */
2857 int pid; /* PID of subprocess */
2858 int mode; /* == 'r' if pipe open for reading */
2859 int done; /* subprocess has completed */
2860 int waiting; /* waiting for completion/closure */
2861 int closing; /* my_pclose is closing this pipe */
2862 unsigned long completion; /* termination status of subprocess */
2863 pPipe in; /* pipe in to sub */
2864 pPipe out; /* pipe out of sub */
2865 pPipe err; /* pipe of sub's sys$error */
2866 int in_done; /* true when in pipe finished */
2869 unsigned short xchan; /* channel to debug xterm */
2870 unsigned short xchan_valid; /* channel is assigned */
2873 struct exit_control_block
2875 struct exit_control_block *flink;
2876 unsigned long int (*exit_routine)(void);
2877 unsigned long int arg_count;
2878 unsigned long int *status_address;
2879 unsigned long int exit_status;
2882 typedef struct _closed_pipes Xpipe;
2883 typedef struct _closed_pipes* pXpipe;
2885 struct _closed_pipes {
2886 int pid; /* PID of subprocess */
2887 unsigned long completion; /* termination status of subprocess */
2889 #define NKEEPCLOSED 50
2890 static Xpipe closed_list[NKEEPCLOSED];
2891 static int closed_index = 0;
2892 static int closed_num = 0;
2894 #define RETRY_DELAY "0 ::0.20"
2895 #define MAX_RETRY 50
2897 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2898 static unsigned long mypid;
2899 static unsigned long delaytime[2];
2901 static pInfo open_pipes = NULL;
2902 static $DESCRIPTOR(nl_desc, "NL:");
2904 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2908 static unsigned long int
2909 pipe_exit_routine(void)
2912 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2913 int sts, did_stuff, j;
2916 * Flush any pending i/o, but since we are in process run-down, be
2917 * careful about referencing PerlIO structures that may already have
2918 * been deallocated. We may not even have an interpreter anymore.
2923 #if defined(PERL_IMPLICIT_CONTEXT)
2924 /* We need to use the Perl context of the thread that created */
2928 aTHX = info->err->thx;
2930 aTHX = info->out->thx;
2932 aTHX = info->in->thx;
2935 #if defined(USE_ITHREADS)
2939 && PL_perlio_fd_refcnt
2942 PerlIO_flush(info->fp);
2944 fflush((FILE *)info->fp);
2950 next we try sending an EOF...ignore if doesn't work, make sure we
2957 _ckvmssts_noperl(sys$setast(0));
2958 if (info->in && !info->in->shut_on_empty) {
2959 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2964 _ckvmssts_noperl(sys$setast(1));
2968 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2970 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2975 _ckvmssts_noperl(sys$setast(0));
2976 if (info->waiting && info->done)
2978 nwait += info->waiting;
2979 _ckvmssts_noperl(sys$setast(1));
2989 _ckvmssts_noperl(sys$setast(0));
2990 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2991 sts = sys$forcex(&info->pid,0,&abort);
2992 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2995 _ckvmssts_noperl(sys$setast(1));
2999 /* again, wait for effect */
3001 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3006 _ckvmssts_noperl(sys$setast(0));
3007 if (info->waiting && info->done)
3009 nwait += info->waiting;
3010 _ckvmssts_noperl(sys$setast(1));
3019 _ckvmssts_noperl(sys$setast(0));
3020 if (!info->done) { /* We tried to be nice . . . */
3021 sts = sys$delprc(&info->pid,0);
3022 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3023 info->done = 1; /* sys$delprc is as done as we're going to get. */
3025 _ckvmssts_noperl(sys$setast(1));
3031 #if defined(PERL_IMPLICIT_CONTEXT)
3032 /* We need to use the Perl context of the thread that created */
3035 if (open_pipes->err)
3036 aTHX = open_pipes->err->thx;
3037 else if (open_pipes->out)
3038 aTHX = open_pipes->out->thx;
3039 else if (open_pipes->in)
3040 aTHX = open_pipes->in->thx;
3042 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3043 else if (!(sts & 1)) retsts = sts;
3048 static struct exit_control_block pipe_exitblock =
3049 {(struct exit_control_block *) 0,
3050 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3052 static void pipe_mbxtofd_ast(pPipe p);
3053 static void pipe_tochild1_ast(pPipe p);
3054 static void pipe_tochild2_ast(pPipe p);
3057 popen_completion_ast(pInfo info)
3059 pInfo i = open_pipes;
3062 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3063 closed_list[closed_index].pid = info->pid;
3064 closed_list[closed_index].completion = info->completion;
3066 if (closed_index == NKEEPCLOSED)
3071 if (i == info) break;
3074 if (!i) return; /* unlinked, probably freed too */
3079 Writing to subprocess ...
3080 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3082 chan_out may be waiting for "done" flag, or hung waiting
3083 for i/o completion to child...cancel the i/o. This will
3084 put it into "snarf mode" (done but no EOF yet) that discards
3087 Output from subprocess (stdout, stderr) needs to be flushed and
3088 shut down. We try sending an EOF, but if the mbx is full the pipe
3089 routine should still catch the "shut_on_empty" flag, telling it to
3090 use immediate-style reads so that "mbx empty" -> EOF.
3094 if (info->in && !info->in_done) { /* only for mode=w */
3095 if (info->in->shut_on_empty && info->in->need_wake) {
3096 info->in->need_wake = FALSE;
3097 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3099 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3103 if (info->out && !info->out_done) { /* were we also piping output? */
3104 info->out->shut_on_empty = TRUE;
3105 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3106 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3107 _ckvmssts_noperl(iss);
3110 if (info->err && !info->err_done) { /* we were piping stderr */
3111 info->err->shut_on_empty = TRUE;
3112 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3113 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3114 _ckvmssts_noperl(iss);
3116 _ckvmssts_noperl(sys$setef(pipe_ef));
3120 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3121 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3122 static void pipe_infromchild_ast(pPipe p);
3125 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3126 inside an AST routine without worrying about reentrancy and which Perl
3127 memory allocator is being used.
3129 We read data and queue up the buffers, then spit them out one at a
3130 time to the output mailbox when the output mailbox is ready for one.
3133 #define INITIAL_TOCHILDQUEUE 2
3136 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3140 char mbx1[64], mbx2[64];
3141 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3142 DSC$K_CLASS_S, mbx1},
3143 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3144 DSC$K_CLASS_S, mbx2};
3145 unsigned int dviitm = DVI$_DEVBUFSIZ;
3149 _ckvmssts_noperl(lib$get_vm(&n, &p));
3151 create_mbx(&p->chan_in , &d_mbx1);
3152 create_mbx(&p->chan_out, &d_mbx2);
3153 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3156 p->shut_on_empty = FALSE;
3157 p->need_wake = FALSE;
3160 p->iosb.status = SS$_NORMAL;
3161 p->iosb2.status = SS$_NORMAL;
3167 #ifdef PERL_IMPLICIT_CONTEXT
3171 n = sizeof(CBuf) + p->bufsize;
3173 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3174 _ckvmssts_noperl(lib$get_vm(&n, &b));
3175 b->buf = (char *) b + sizeof(CBuf);
3176 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3179 pipe_tochild2_ast(p);
3180 pipe_tochild1_ast(p);
3186 /* reads the MBX Perl is writing, and queues */
3189 pipe_tochild1_ast(pPipe p)
3192 int iss = p->iosb.status;
3193 int eof = (iss == SS$_ENDOFFILE);
3195 #ifdef PERL_IMPLICIT_CONTEXT
3201 p->shut_on_empty = TRUE;
3203 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3205 _ckvmssts_noperl(iss);
3209 b->size = p->iosb.count;
3210 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3212 p->need_wake = FALSE;
3213 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3216 p->retry = 1; /* initial call */
3219 if (eof) { /* flush the free queue, return when done */
3220 int n = sizeof(CBuf) + p->bufsize;
3222 iss = lib$remqti(&p->free, &b);
3223 if (iss == LIB$_QUEWASEMP) return;
3224 _ckvmssts_noperl(iss);
3225 _ckvmssts_noperl(lib$free_vm(&n, &b));
3229 iss = lib$remqti(&p->free, &b);
3230 if (iss == LIB$_QUEWASEMP) {
3231 int n = sizeof(CBuf) + p->bufsize;
3232 _ckvmssts_noperl(lib$get_vm(&n, &b));
3233 b->buf = (char *) b + sizeof(CBuf);
3235 _ckvmssts_noperl(iss);
3239 iss = sys$qio(0,p->chan_in,
3240 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3242 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3243 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3244 _ckvmssts_noperl(iss);
3248 /* writes queued buffers to output, waits for each to complete before
3252 pipe_tochild2_ast(pPipe p)
3255 int iss = p->iosb2.status;
3256 int n = sizeof(CBuf) + p->bufsize;
3257 int done = (p->info && p->info->done) ||
3258 iss == SS$_CANCEL || iss == SS$_ABORT;
3259 #if defined(PERL_IMPLICIT_CONTEXT)
3264 if (p->type) { /* type=1 has old buffer, dispose */
3265 if (p->shut_on_empty) {
3266 _ckvmssts_noperl(lib$free_vm(&n, &b));
3268 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3273 iss = lib$remqti(&p->wait, &b);
3274 if (iss == LIB$_QUEWASEMP) {
3275 if (p->shut_on_empty) {
3277 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3278 *p->pipe_done = TRUE;
3279 _ckvmssts_noperl(sys$setef(pipe_ef));
3281 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3282 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3286 p->need_wake = TRUE;
3289 _ckvmssts_noperl(iss);
3296 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3297 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3299 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3300 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3309 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3312 char mbx1[64], mbx2[64];
3313 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3314 DSC$K_CLASS_S, mbx1},
3315 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3316 DSC$K_CLASS_S, mbx2};
3317 unsigned int dviitm = DVI$_DEVBUFSIZ;
3319 int n = sizeof(Pipe);
3320 _ckvmssts_noperl(lib$get_vm(&n, &p));
3321 create_mbx(&p->chan_in , &d_mbx1);
3322 create_mbx(&p->chan_out, &d_mbx2);
3324 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3325 n = p->bufsize * sizeof(char);
3326 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3327 p->shut_on_empty = FALSE;
3330 p->iosb.status = SS$_NORMAL;
3331 #if defined(PERL_IMPLICIT_CONTEXT)
3334 pipe_infromchild_ast(p);
3342 pipe_infromchild_ast(pPipe p)
3344 int iss = p->iosb.status;
3345 int eof = (iss == SS$_ENDOFFILE);
3346 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3347 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3348 #if defined(PERL_IMPLICIT_CONTEXT)
3352 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3353 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3358 input shutdown if EOF from self (done or shut_on_empty)
3359 output shutdown if closing flag set (my_pclose)
3360 send data/eof from child or eof from self
3361 otherwise, re-read (snarf of data from child)
3366 if (myeof && p->chan_in) { /* input shutdown */
3367 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3372 if (myeof || kideof) { /* pass EOF to parent */
3373 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3374 pipe_infromchild_ast, p,
3377 } else if (eof) { /* eat EOF --- fall through to read*/
3379 } else { /* transmit data */
3380 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3381 pipe_infromchild_ast,p,
3382 p->buf, p->iosb.count, 0, 0, 0, 0));
3388 /* everything shut? flag as done */
3390 if (!p->chan_in && !p->chan_out) {
3391 *p->pipe_done = TRUE;
3392 _ckvmssts_noperl(sys$setef(pipe_ef));
3396 /* write completed (or read, if snarfing from child)
3397 if still have input active,
3398 queue read...immediate mode if shut_on_empty so we get EOF if empty
3400 check if Perl reading, generate EOFs as needed
3406 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3407 pipe_infromchild_ast,p,
3408 p->buf, p->bufsize, 0, 0, 0, 0);
3409 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3410 _ckvmssts_noperl(iss);
3411 } else { /* send EOFs for extra reads */
3412 p->iosb.status = SS$_ENDOFFILE;
3413 p->iosb.dvispec = 0;
3414 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3416 pipe_infromchild_ast, p, 0, 0, 0, 0));
3422 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3426 unsigned long dviitm = DVI$_DEVBUFSIZ;
3428 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3429 DSC$K_CLASS_S, mbx};
3430 int n = sizeof(Pipe);
3432 /* things like terminals and mbx's don't need this filter */
3433 if (fd && fstat(fd,&s) == 0) {
3434 unsigned long devchar;
3436 unsigned short dev_len;
3437 struct dsc$descriptor_s d_dev;
3439 struct item_list_3 items[3];
3441 unsigned short dvi_iosb[4];
3443 cptr = getname(fd, out, 1);
3444 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3445 d_dev.dsc$a_pointer = out;
3446 d_dev.dsc$w_length = strlen(out);
3447 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3448 d_dev.dsc$b_class = DSC$K_CLASS_S;
3451 items[0].code = DVI$_DEVCHAR;
3452 items[0].bufadr = &devchar;
3453 items[0].retadr = NULL;
3455 items[1].code = DVI$_FULLDEVNAM;
3456 items[1].bufadr = device;
3457 items[1].retadr = &dev_len;
3461 status = sys$getdviw
3462 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3463 _ckvmssts_noperl(status);
3464 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3465 device[dev_len] = 0;
3467 if (!(devchar & DEV$M_DIR)) {
3468 strcpy(out, device);
3474 _ckvmssts_noperl(lib$get_vm(&n, &p));
3475 p->fd_out = dup(fd);
3476 create_mbx(&p->chan_in, &d_mbx);
3477 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3478 n = (p->bufsize+1) * sizeof(char);
3479 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3480 p->shut_on_empty = FALSE;
3485 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3486 pipe_mbxtofd_ast, p,
3487 p->buf, p->bufsize, 0, 0, 0, 0));
3493 pipe_mbxtofd_ast(pPipe p)
3495 int iss = p->iosb.status;
3496 int done = p->info->done;
3498 int eof = (iss == SS$_ENDOFFILE);
3499 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3500 int err = !(iss&1) && !eof;
3501 #if defined(PERL_IMPLICIT_CONTEXT)
3505 if (done && myeof) { /* end piping */
3507 sys$dassgn(p->chan_in);
3508 *p->pipe_done = TRUE;
3509 _ckvmssts_noperl(sys$setef(pipe_ef));
3513 if (!err && !eof) { /* good data to send to file */
3514 p->buf[p->iosb.count] = '\n';
3515 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3518 if (p->retry < MAX_RETRY) {
3519 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3525 _ckvmssts_noperl(iss);
3529 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3530 pipe_mbxtofd_ast, p,
3531 p->buf, p->bufsize, 0, 0, 0, 0);
3532 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3533 _ckvmssts_noperl(iss);
3537 typedef struct _pipeloc PLOC;
3538 typedef struct _pipeloc* pPLOC;
3542 char dir[NAM$C_MAXRSS+1];
3544 static pPLOC head_PLOC = 0;
3547 free_pipelocs(pTHX_ void *head)
3550 pPLOC *pHead = (pPLOC *)head;
3562 store_pipelocs(pTHX)
3570 char temp[NAM$C_MAXRSS+1];
3574 free_pipelocs(aTHX_ &head_PLOC);
3576 /* the . directory from @INC comes last */
3578 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3579 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3580 p->next = head_PLOC;
3582 strcpy(p->dir,"./");
3584 /* get the directory from $^X */
3586 unixdir = (char *)PerlMem_malloc(VMS_MAXRSS);
3587 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3589 #ifdef PERL_IMPLICIT_CONTEXT
3590 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3592 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3594 my_strlcpy(temp, PL_origargv[0], sizeof(temp));
3595 x = strrchr(temp,']');
3597 x = strrchr(temp,'>');
3599 /* It could be a UNIX path */
3600 x = strrchr(temp,'/');
3606 /* Got a bare name, so use default directory */
3611 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3612 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3613 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3614 p->next = head_PLOC;
3616 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3620 /* reverse order of @INC entries, skip "." since entered above */
3622 #ifdef PERL_IMPLICIT_CONTEXT
3625 if (PL_incgv) av = GvAVn(PL_incgv);
3627 for (i = 0; av && i <= AvFILL(av); i++) {
3628 dirsv = *av_fetch(av,i,TRUE);
3630 if (SvROK(dirsv)) continue;
3631 dir = SvPVx(dirsv,n_a);
3632 if (strcmp(dir,".") == 0) continue;
3633 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3636 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3637 p->next = head_PLOC;
3639 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3642 /* most likely spot (ARCHLIB) put first in the list */
3645 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3646 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3647 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3648 p->next = head_PLOC;
3650 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3653 PerlMem_free(unixdir);
3657 Perl_cando_by_name_int
3658 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3659 #if !defined(PERL_IMPLICIT_CONTEXT)
3660 #define cando_by_name_int Perl_cando_by_name_int
3662 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3668 static int vmspipe_file_status = 0;
3669 static char vmspipe_file[NAM$C_MAXRSS+1];
3671 /* already found? Check and use ... need read+execute permission */
3673 if (vmspipe_file_status == 1) {
3674 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3675 && cando_by_name_int
3676 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3677 return vmspipe_file;
3679 vmspipe_file_status = 0;
3682 /* scan through stored @INC, $^X */
3684 if (vmspipe_file_status == 0) {
3685 char file[NAM$C_MAXRSS+1];
3686 pPLOC p = head_PLOC;
3691 dirlen = my_strlcpy(file, p->dir, sizeof(file));
3692 my_strlcat(file, "vmspipe.com", sizeof(file));
3695 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3696 if (!exp_res) continue;
3698 if (cando_by_name_int
3699 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3700 && cando_by_name_int
3701 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3702 vmspipe_file_status = 1;
3703 return vmspipe_file;
3706 vmspipe_file_status = -1; /* failed, use tempfiles */
3713 vmspipe_tempfile(pTHX)
3715 char file[NAM$C_MAXRSS+1];
3717 static int index = 0;
3721 /* create a tempfile */
3723 /* we can't go from W, shr=get to R, shr=get without
3724 an intermediate vulnerable state, so don't bother trying...
3726 and lib$spawn doesn't shr=put, so have to close the write
3728 So... match up the creation date/time and the FID to
3729 make sure we're dealing with the same file
3734 if (!decc_filename_unix_only) {
3735 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3736 fp = fopen(file,"w");
3738 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3739 fp = fopen(file,"w");
3741 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3742 fp = fopen(file,"w");
3747 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3748 fp = fopen(file,"w");
3750 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3751 fp = fopen(file,"w");
3753 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3754 fp = fopen(file,"w");
3758 if (!fp) return 0; /* we're hosed */
3760 fprintf(fp,"$! 'f$verify(0)'\n");
3761 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3762 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3763 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3764 fprintf(fp,"$ perl_on = \"set noon\"\n");
3765 fprintf(fp,"$ perl_exit = \"exit\"\n");
3766 fprintf(fp,"$ perl_del = \"delete\"\n");
3767 fprintf(fp,"$ pif = \"if\"\n");
3768 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3769 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3770 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3771 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3772 fprintf(fp,"$! --- build command line to get max possible length\n");
3773 fprintf(fp,"$c=perl_popen_cmd0\n");
3774 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3775 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3776 fprintf(fp,"$x=perl_popen_cmd3\n");
3777 fprintf(fp,"$c=c+x\n");
3778 fprintf(fp,"$ perl_on\n");
3779 fprintf(fp,"$ 'c'\n");
3780 fprintf(fp,"$ perl_status = $STATUS\n");
3781 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3782 fprintf(fp,"$ perl_exit 'perl_status'\n");
3785 fgetname(fp, file, 1);
3786 fstat(fileno(fp), &s0.crtl_stat);
3789 if (decc_filename_unix_only)
3790 int_tounixspec(file, file, NULL);
3791 fp = fopen(file,"r","shr=get");
3793 fstat(fileno(fp), &s1.crtl_stat);
3795 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3796 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3805 static int vms_is_syscommand_xterm(void)
3807 const static struct dsc$descriptor_s syscommand_dsc =
3808 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3810 const static struct dsc$descriptor_s decwdisplay_dsc =
3811 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3813 struct item_list_3 items[2];
3814 unsigned short dvi_iosb[4];
3815 unsigned long devchar;
3816 unsigned long devclass;
3819 /* Very simple check to guess if sys$command is a decterm? */
3820 /* First see if the DECW$DISPLAY: device exists */
3822 items[0].code = DVI$_DEVCHAR;
3823 items[0].bufadr = &devchar;
3824 items[0].retadr = NULL;
3828 status = sys$getdviw
3829 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3831 if ($VMS_STATUS_SUCCESS(status)) {
3832 status = dvi_iosb[0];
3835 if (!$VMS_STATUS_SUCCESS(status)) {
3836 SETERRNO(EVMSERR, status);
3840 /* If it does, then for now assume that we are on a workstation */
3841 /* Now verify that SYS$COMMAND is a terminal */
3842 /* for creating the debugger DECTerm */
3845 items[0].code = DVI$_DEVCLASS;
3846 items[0].bufadr = &devclass;
3847 items[0].retadr = NULL;
3851 status = sys$getdviw
3852 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3854 if ($VMS_STATUS_SUCCESS(status)) {
3855 status = dvi_iosb[0];
3858 if (!$VMS_STATUS_SUCCESS(status)) {
3859 SETERRNO(EVMSERR, status);
3863 if (devclass == DC$_TERM) {
3870 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3871 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3876 char device_name[65];
3877 unsigned short device_name_len;
3878 struct dsc$descriptor_s customization_dsc;
3879 struct dsc$descriptor_s device_name_dsc;
3881 char customization[200];
3885 unsigned short p_chan;
3887 unsigned short iosb[4];
3888 const char * cust_str =
3889 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3890 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3891 DSC$K_CLASS_S, mbx1};
3893 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
3894 /*---------------------------------------*/
3895 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
3898 /* Make sure that this is from the Perl debugger */
3899 ret_char = strstr(cmd," xterm ");
3900 if (ret_char == NULL)
3902 cptr = ret_char + 7;
3903 ret_char = strstr(cmd,"tty");
3904 if (ret_char == NULL)
3906 ret_char = strstr(cmd,"sleep");
3907 if (ret_char == NULL)
3910 if (decw_term_port == 0) {
3911 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
3912 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
3913 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
3915 status = lib$find_image_symbol
3917 &decw_term_port_dsc,
3918 (void *)&decw_term_port,
3922 /* Try again with the other image name */
3923 if (!$VMS_STATUS_SUCCESS(status)) {
3925 status = lib$find_image_symbol
3927 &decw_term_port_dsc,
3928 (void *)&decw_term_port,
3937 /* No decw$term_port, give it up */
3938 if (!$VMS_STATUS_SUCCESS(status))
3941 /* Are we on a workstation? */
3942 /* to do: capture the rows / columns and pass their properties */
3943 ret_stat = vms_is_syscommand_xterm();
3947 /* Make the title: */
3948 ret_char = strstr(cptr,"-title");
3949 if (ret_char != NULL) {
3950 while ((*cptr != 0) && (*cptr != '\"')) {
3956 while ((*cptr != 0) && (*cptr != '\"')) {
3969 strcpy(title,"Perl Debug DECTerm");
3971 sprintf(customization, cust_str, title);
3973 customization_dsc.dsc$a_pointer = customization;
3974 customization_dsc.dsc$w_length = strlen(customization);
3975 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3976 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
3978 device_name_dsc.dsc$a_pointer = device_name;
3979 device_name_dsc.dsc$w_length = sizeof device_name -1;
3980 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3981 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
3983 device_name_len = 0;
3985 /* Try to create the window */
3986 status = (*decw_term_port)
3995 if (!$VMS_STATUS_SUCCESS(status)) {
3996 SETERRNO(EVMSERR, status);
4000 device_name[device_name_len] = '\0';
4002 /* Need to set this up to look like a pipe for cleanup */
4004 status = lib$get_vm(&n, &info);
4005 if (!$VMS_STATUS_SUCCESS(status)) {
4006 SETERRNO(ENOMEM, status);
4012 info->completion = 0;
4013 info->closing = FALSE;
4020 info->in_done = TRUE;
4021 info->out_done = TRUE;
4022 info->err_done = TRUE;
4024 /* Assign a channel on this so that it will persist, and not login */
4025 /* We stash this channel in the info structure for reference. */
4026 /* The created xterm self destructs when the last channel is removed */
4027 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4028 /* So leave this assigned. */
4029 device_name_dsc.dsc$w_length = device_name_len;
4030 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4031 if (!$VMS_STATUS_SUCCESS(status)) {
4032 SETERRNO(EVMSERR, status);
4035 info->xchan_valid = 1;
4037 /* Now create a mailbox to be read by the application */
4039 create_mbx(&p_chan, &d_mbx1);
4041 /* write the name of the created terminal to the mailbox */
4042 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4043 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4045 if (!$VMS_STATUS_SUCCESS(status)) {
4046 SETERRNO(EVMSERR, status);
4050 info->fp = PerlIO_open(mbx1, mode);
4052 /* Done with this channel */
4055 /* If any errors, then clean up */
4058 _ckvmssts_noperl(lib$free_vm(&n, &info));
4066 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4069 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4071 static int handler_set_up = FALSE;
4073 unsigned long int sts, flags = CLI$M_NOWAIT;
4074 /* The use of a GLOBAL table (as was done previously) rendered
4075 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4076 * environment. Hence we've switched to LOCAL symbol table.
4078 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4080 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4081 char *in, *out, *err, mbx[512];
4083 char tfilebuf[NAM$C_MAXRSS+1];
4085 char cmd_sym_name[20];
4086 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4087 DSC$K_CLASS_S, symbol};
4088 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4090 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4091 DSC$K_CLASS_S, cmd_sym_name};
4092 struct dsc$descriptor_s *vmscmd;
4093 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4094 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4095 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4097 /* Check here for Xterm create request. This means looking for
4098 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4099 * is possible to create an xterm.
4101 if (*in_mode == 'r') {
4104 #if defined(PERL_IMPLICIT_CONTEXT)
4105 /* Can not fork an xterm with a NULL context */
4106 /* This probably could never happen */
4110 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4111 if (xterm_fd != NULL)
4115 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4117 /* once-per-program initialization...
4118 note that the SETAST calls and the dual test of pipe_ef
4119 makes sure that only the FIRST thread through here does
4120 the initialization...all other threads wait until it's
4123 Yeah, uglier than a pthread call, it's got all the stuff inline
4124 rather than in a separate routine.
4128 _ckvmssts_noperl(sys$setast(0));
4130 unsigned long int pidcode = JPI$_PID;
4131 $DESCRIPTOR(d_delay, RETRY_DELAY);
4132 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4133 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4134 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4136 if (!handler_set_up) {
4137 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4138 handler_set_up = TRUE;
4140 _ckvmssts_noperl(sys$setast(1));
4143 /* see if we can find a VMSPIPE.COM */
4146 vmspipe = find_vmspipe(aTHX);
4148 vmspipedsc.dsc$w_length = my_strlcpy(tfilebuf+1, vmspipe, sizeof(tfilebuf)-1) + 1;
4149 } else { /* uh, oh...we're in tempfile hell */
4150 tpipe = vmspipe_tempfile(aTHX);
4151 if (!tpipe) { /* a fish popular in Boston */
4152 if (ckWARN(WARN_PIPE)) {
4153 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4157 fgetname(tpipe,tfilebuf+1,1);
4158 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4160 vmspipedsc.dsc$a_pointer = tfilebuf;
4162 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4165 case RMS$_FNF: case RMS$_DNF:
4166 set_errno(ENOENT); break;
4168 set_errno(ENOTDIR); break;
4170 set_errno(ENODEV); break;
4172 set_errno(EACCES); break;
4174 set_errno(EINVAL); break;
4175 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4176 set_errno(E2BIG); break;
4177 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4178 _ckvmssts_noperl(sts); /* fall through */
4179 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4182 set_vaxc_errno(sts);
4183 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4184 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4190 _ckvmssts_noperl(lib$get_vm(&n, &info));
4192 my_strlcpy(mode, in_mode, sizeof(mode));
4195 info->completion = 0;
4196 info->closing = FALSE;
4203 info->in_done = TRUE;
4204 info->out_done = TRUE;
4205 info->err_done = TRUE;
4207 info->xchan_valid = 0;
4209 in = (char *)PerlMem_malloc(VMS_MAXRSS);
4210 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4211 out = (char *)PerlMem_malloc(VMS_MAXRSS);
4212 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4213 err = (char *)PerlMem_malloc(VMS_MAXRSS);
4214 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4216 in[0] = out[0] = err[0] = '\0';
4218 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4222 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4227 if (*mode == 'r') { /* piping from subroutine */
4229 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4231 info->out->pipe_done = &info->out_done;
4232 info->out_done = FALSE;
4233 info->out->info = info;
4235 if (!info->useFILE) {
4236 info->fp = PerlIO_open(mbx, mode);
4238 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4239 vmssetuserlnm("SYS$INPUT", mbx);
4242 if (!info->fp && info->out) {
4243 sys$cancel(info->out->chan_out);
4245 while (!info->out_done) {
4247 _ckvmssts_noperl(sys$setast(0));
4248 done = info->out_done;
4249 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4250 _ckvmssts_noperl(sys$setast(1));
4251 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4254 if (info->out->buf) {
4255 n = info->out->bufsize * sizeof(char);
4256 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4259 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4261 _ckvmssts_noperl(lib$free_vm(&n, &info));
4266 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4268 info->err->pipe_done = &info->err_done;
4269 info->err_done = FALSE;
4270 info->err->info = info;
4273 } else if (*mode == 'w') { /* piping to subroutine */
4275 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4277 info->out->pipe_done = &info->out_done;
4278 info->out_done = FALSE;
4279 info->out->info = info;
4282 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4284 info->err->pipe_done = &info->err_done;
4285 info->err_done = FALSE;
4286 info->err->info = info;
4289 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4290 if (!info->useFILE) {
4291 info->fp = PerlIO_open(mbx, mode);
4293 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4294 vmssetuserlnm("SYS$OUTPUT", mbx);
4298 info->in->pipe_done = &info->in_done;
4299 info->in_done = FALSE;
4300 info->in->info = info;
4304 if (!info->fp && info->in) {
4306 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4307 0, 0, 0, 0, 0, 0, 0, 0));
4309 while (!info->in_done) {
4311 _ckvmssts_noperl(sys$setast(0));
4312 done = info->in_done;
4313 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4314 _ckvmssts_noperl(sys$setast(1));
4315 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4318 if (info->in->buf) {
4319 n = info->in->bufsize * sizeof(char);
4320 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4323 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4325 _ckvmssts_noperl(lib$free_vm(&n, &info));
4331 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4332 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4334 info->out->pipe_done = &info->out_done;
4335 info->out_done = FALSE;
4336 info->out->info = info;
4339 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4341 info->err->pipe_done = &info->err_done;
4342 info->err_done = FALSE;
4343 info->err->info = info;
4347 d_symbol.dsc$w_length = my_strlcpy(symbol, in, sizeof(symbol));
4348 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4350 d_symbol.dsc$w_length = my_strlcpy(symbol, err, sizeof(symbol));
4351 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4353 d_symbol.dsc$w_length = my_strlcpy(symbol, out, sizeof(symbol));
4354 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4356 /* Done with the names for the pipes */
4361 p = vmscmd->dsc$a_pointer;
4362 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4363 if (*p == '$') p++; /* remove leading $ */
4364 while (*p == ' ' || *p == '\t') p++;
4366 for (j = 0; j < 4; j++) {
4367 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4368 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4370 d_symbol.dsc$w_length = my_strlcpy(symbol, p, sizeof(symbol));
4371 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4373 if (strlen(p) > MAX_DCL_SYMBOL) {
4374 p += MAX_DCL_SYMBOL;
4379 _ckvmssts_noperl(sys$setast(0));
4380 info->next=open_pipes; /* prepend to list */
4382 _ckvmssts_noperl(sys$setast(1));
4383 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4384 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4385 * have SYS$COMMAND if we need it.
4387 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4388 0, &info->pid, &info->completion,
4389 0, popen_completion_ast,info,0,0,0));
4391 /* if we were using a tempfile, close it now */
4393 if (tpipe) fclose(tpipe);
4395 /* once the subprocess is spawned, it has copied the symbols and
4396 we can get rid of ours */
4398 for (j = 0; j < 4; j++) {
4399 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4400 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4401 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4403 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4404 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4405 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4406 vms_execfree(vmscmd);
4408 #ifdef PERL_IMPLICIT_CONTEXT
4411 PL_forkprocess = info->pid;
4418 _ckvmssts_noperl(sys$setast(0));
4420 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4421 _ckvmssts_noperl(sys$setast(1));
4422 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4424 *psts = info->completion;
4425 /* Caller thinks it is open and tries to close it. */
4426 /* This causes some problems, as it changes the error status */
4427 /* my_pclose(info->fp); */
4429 /* If we did not have a file pointer open, then we have to */
4430 /* clean up here or eventually we will run out of something */
4432 if (info->fp == NULL) {
4433 my_pclose_pinfo(aTHX_ info);
4441 } /* end of safe_popen */
4444 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4446 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4450 TAINT_PROPER("popen");
4451 PERL_FLUSHALL_FOR_CHILD;
4452 return safe_popen(aTHX_ cmd,mode,&sts);
4458 /* Routine to close and cleanup a pipe info structure */
4460 static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4462 unsigned long int retsts;
4466 /* If we were writing to a subprocess, insure that someone reading from
4467 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4468 * produce an EOF record in the mailbox.
4470 * well, at least sometimes it *does*, so we have to watch out for
4471 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4475 #if defined(USE_ITHREADS)
4479 && PL_perlio_fd_refcnt
4482 PerlIO_flush(info->fp);
4484 fflush((FILE *)info->fp);
4487 _ckvmssts(sys$setast(0));
4488 info->closing = TRUE;
4489 done = info->done && info->in_done && info->out_done && info->err_done;
4490 /* hanging on write to Perl's input? cancel it */
4491 if (info->mode == 'r' && info->out && !info->out_done) {
4492 if (info->out->chan_out) {
4493 _ckvmssts(sys$cancel(info->out->chan_out));
4494 if (!info->out->chan_in) { /* EOF generation, need AST */
4495 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4499 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4500 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4502 _ckvmssts(sys$setast(1));
4505 #if defined(USE_ITHREADS)
4509 && PL_perlio_fd_refcnt
4512 PerlIO_close(info->fp);
4514 fclose((FILE *)info->fp);
4517 we have to wait until subprocess completes, but ALSO wait until all
4518 the i/o completes...otherwise we'll be freeing the "info" structure
4519 that the i/o ASTs could still be using...
4523 _ckvmssts(sys$setast(0));
4524 done = info->done && info->in_done && info->out_done && info->err_done;
4525 if (!done) _ckvmssts(sys$clref(pipe_ef));
4526 _ckvmssts(sys$setast(1));
4527 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4529 retsts = info->completion;
4531 /* remove from list of open pipes */
4532 _ckvmssts(sys$setast(0));
4534 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4540 last->next = info->next;
4542 open_pipes = info->next;
4543 _ckvmssts(sys$setast(1));
4545 /* free buffers and structures */
4548 if (info->in->buf) {
4549 n = info->in->bufsize * sizeof(char);
4550 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4553 _ckvmssts(lib$free_vm(&n, &info->in));
4556 if (info->out->buf) {
4557 n = info->out->bufsize * sizeof(char);
4558 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4561 _ckvmssts(lib$free_vm(&n, &info->out));
4564 if (info->err->buf) {
4565 n = info->err->bufsize * sizeof(char);
4566 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4569 _ckvmssts(lib$free_vm(&n, &info->err));
4572 _ckvmssts(lib$free_vm(&n, &info));
4578 /*{{{ I32 my_pclose(PerlIO *fp)*/
4579 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4581 pInfo info, last = NULL;
4584 /* Fixme - need ast and mutex protection here */
4585 for (info = open_pipes; info != NULL; last = info, info = info->next)
4586 if (info->fp == fp) break;
4588 if (info == NULL) { /* no such pipe open */
4589 set_errno(ECHILD); /* quoth POSIX */
4590 set_vaxc_errno(SS$_NONEXPR);
4594 ret_status = my_pclose_pinfo(aTHX_ info);
4598 } /* end of my_pclose() */
4600 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4601 /* Roll our own prototype because we want this regardless of whether
4602 * _VMS_WAIT is defined.
4608 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4614 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4615 created with popen(); otherwise partially emulate waitpid() unless
4616 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4617 Also check processes not considered by the CRTL waitpid().
4619 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4621 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4628 if (statusp) *statusp = 0;
4630 for (info = open_pipes; info != NULL; info = info->next)
4631 if (info->pid == pid) break;
4633 if (info != NULL) { /* we know about this child */
4634 while (!info->done) {
4635 _ckvmssts(sys$setast(0));
4637 if (!done) _ckvmssts(sys$clref(pipe_ef));
4638 _ckvmssts(sys$setast(1));
4639 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4642 if (statusp) *statusp = info->completion;
4646 /* child that already terminated? */
4648 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4649 if (closed_list[j].pid == pid) {
4650 if (statusp) *statusp = closed_list[j].completion;
4655 /* fall through if this child is not one of our own pipe children */
4657 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4659 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4660 * in 7.2 did we get a version that fills in the VMS completion
4661 * status as Perl has always tried to do.
4664 sts = __vms_waitpid( pid, statusp, flags );
4666 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4669 /* If the real waitpid tells us the child does not exist, we
4670 * fall through here to implement waiting for a child that
4671 * was created by some means other than exec() (say, spawned
4672 * from DCL) or to wait for a process that is not a subprocess
4673 * of the current process.
4676 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4679 $DESCRIPTOR(intdsc,"0 00:00:01");
4680 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4681 unsigned long int pidcode = JPI$_PID, mypid;
4682 unsigned long int interval[2];
4683 unsigned int jpi_iosb[2];
4684 struct itmlst_3 jpilist[2] = {
4685 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4690 /* Sorry folks, we don't presently implement rooting around for
4691 the first child we can find, and we definitely don't want to
4692 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4698 /* Get the owner of the child so I can warn if it's not mine. If the
4699 * process doesn't exist or I don't have the privs to look at it,
4700 * I can go home early.
4702 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4703 if (sts & 1) sts = jpi_iosb[0];
4715 set_vaxc_errno(sts);
4719 if (ckWARN(WARN_EXEC)) {
4720 /* remind folks they are asking for non-standard waitpid behavior */
4721 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4722 if (ownerpid != mypid)
4723 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4724 "waitpid: process %x is not a child of process %x",
4728 /* simply check on it once a second until it's not there anymore. */
4730 _ckvmssts(sys$bintim(&intdsc,interval));
4731 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4732 _ckvmssts(sys$schdwk(0,0,interval,0));
4733 _ckvmssts(sys$hiber());
4735 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4740 } /* end of waitpid() */
4745 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4747 my_gconvert(double val, int ndig, int trail, char *buf)
4749 static char __gcvtbuf[DBL_DIG+1];
4752 loc = buf ? buf : __gcvtbuf;
4755 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4756 return gcvt(val,ndig,loc);
4759 loc[0] = '0'; loc[1] = '\0';
4766 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4767 static int rms_free_search_context(struct FAB * fab)
4771 nam = fab->fab$l_nam;
4772 nam->nam$b_nop |= NAM$M_SYNCHK;
4773 nam->nam$l_rlf = NULL;
4775 return sys$parse(fab, NULL, NULL);
4778 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4779 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4780 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4781 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4782 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4783 #define rms_nam_esll(nam) nam.nam$b_esl
4784 #define rms_nam_esl(nam) nam.nam$b_esl
4785 #define rms_nam_name(nam) nam.nam$l_name
4786 #define rms_nam_namel(nam) nam.nam$l_name
4787 #define rms_nam_type(nam) nam.nam$l_type
4788 #define rms_nam_typel(nam) nam.nam$l_type
4789 #define rms_nam_ver(nam) nam.nam$l_ver
4790 #define rms_nam_verl(nam) nam.nam$l_ver
4791 #define rms_nam_rsll(nam) nam.nam$b_rsl
4792 #define rms_nam_rsl(nam) nam.nam$b_rsl
4793 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4794 #define rms_set_fna(fab, nam, name, size) \
4795 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4796 #define rms_get_fna(fab, nam) fab.fab$l_fna
4797 #define rms_set_dna(fab, nam, name, size) \
4798 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4799 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4800 #define rms_set_esa(nam, name, size) \
4801 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4802 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4803 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4804 #define rms_set_rsa(nam, name, size) \
4805 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4806 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4807 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4808 #define rms_nam_name_type_l_size(nam) \
4809 (nam.nam$b_name + nam.nam$b_type)
4811 static int rms_free_search_context(struct FAB * fab)
4815 nam = fab->fab$l_naml;
4816 nam->naml$b_nop |= NAM$M_SYNCHK;
4817 nam->naml$l_rlf = NULL;
4818 nam->naml$l_long_defname_size = 0;
4821 return sys$parse(fab, NULL, NULL);
4824 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4825 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4826 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4827 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4828 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4829 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4830 #define rms_nam_esl(nam) nam.naml$b_esl
4831 #define rms_nam_name(nam) nam.naml$l_name
4832 #define rms_nam_namel(nam) nam.naml$l_long_name
4833 #define rms_nam_type(nam) nam.naml$l_type
4834 #define rms_nam_typel(nam) nam.naml$l_long_type
4835 #define rms_nam_ver(nam) nam.naml$l_ver
4836 #define rms_nam_verl(nam) nam.naml$l_long_ver
4837 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4838 #define rms_nam_rsl(nam) nam.naml$b_rsl
4839 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4840 #define rms_set_fna(fab, nam, name, size) \
4841 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4842 nam.naml$l_long_filename_size = size; \
4843 nam.naml$l_long_filename = name;}
4844 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4845 #define rms_set_dna(fab, nam, name, size) \
4846 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4847 nam.naml$l_long_defname_size = size; \
4848 nam.naml$l_long_defname = name; }
4849 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4850 #define rms_set_esa(nam, name, size) \
4851 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4852 nam.naml$l_long_expand_alloc = size; \
4853 nam.naml$l_long_expand = name; }
4854 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4855 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4856 nam.naml$l_long_expand = l_name; \
4857 nam.naml$l_long_expand_alloc = l_size; }
4858 #define rms_set_rsa(nam, name, size) \
4859 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4860 nam.naml$l_long_result = name; \
4861 nam.naml$l_long_result_alloc = size; }
4862 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4863 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4864 nam.naml$l_long_result = l_name; \
4865 nam.naml$l_long_result_alloc = l_size; }
4866 #define rms_nam_name_type_l_size(nam) \
4867 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4872 * The CRTL for 8.3 and later can create symbolic links in any mode,
4873 * however in 8.3 the unlink/remove/delete routines will only properly handle
4874 * them if one of the PCP modes is active.
4876 static int rms_erase(const char * vmsname)
4879 struct FAB myfab = cc$rms_fab;
4880 rms_setup_nam(mynam);
4882 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4883 rms_bind_fab_nam(myfab, mynam);
4885 #ifdef NAML$M_OPEN_SPECIAL
4886 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
4889 status = sys$erase(&myfab, 0, 0);
4896 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
4897 const struct dsc$descriptor_s * vms_dst_dsc,
4898 unsigned long flags)
4900 /* VMS and UNIX handle file permissions differently and the
4901 * the same ACL trick may be needed for renaming files,
4902 * especially if they are directories.
4905 /* todo: get kill_file and rename to share common code */
4906 /* I can not find online documentation for $change_acl
4907 * it appears to be replaced by $set_security some time ago */
4909 const unsigned int access_mode = 0;
4910 $DESCRIPTOR(obj_file_dsc,"FILE");
4913 unsigned long int jpicode = JPI$_UIC;
4914 int aclsts, fndsts, rnsts = -1;
4915 unsigned int ctx = 0;
4916 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4917 struct dsc$descriptor_s * clean_dsc;
4920 unsigned char myace$b_length;
4921 unsigned char myace$b_type;
4922 unsigned short int myace$w_flags;
4923 unsigned long int myace$l_access;
4924 unsigned long int myace$l_ident;
4925 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
4926 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
4928 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
4931 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
4932 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
4934 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
4935 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
4939 /* Expand the input spec using RMS, since we do not want to put
4940 * ACLs on the target of a symbolic link */
4941 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
4942 if (vmsname == NULL)
4945 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
4947 PERL_RMSEXPAND_M_SYMLINK);
4949 PerlMem_free(vmsname);
4953 /* So we get our own UIC to use as a rights identifier,
4954 * and the insert an ACE at the head of the ACL which allows us
4955 * to delete the file.
4957 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
4959 fildsc.dsc$w_length = strlen(vmsname);
4960 fildsc.dsc$a_pointer = vmsname;
4962 newace.myace$l_ident = oldace.myace$l_ident;
4965 /* Grab any existing ACEs with this identifier in case we fail */
4966 clean_dsc = &fildsc;
4967 aclsts = fndsts = sys$get_security(&obj_file_dsc,
4975 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
4976 /* Add the new ACE . . . */
4978 /* if the sys$get_security succeeded, then ctx is valid, and the
4979 * object/file descriptors will be ignored. But otherwise they
4982 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
4983 OSS$M_RELCTX, addlst, &ctx, &access_mode);
4984 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
4986 set_vaxc_errno(aclsts);
4987 PerlMem_free(vmsname);
4991 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
4994 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
4996 if ($VMS_STATUS_SUCCESS(rnsts)) {
4997 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5000 /* Put things back the way they were. */
5002 aclsts = sys$get_security(&obj_file_dsc,
5010 if ($VMS_STATUS_SUCCESS(aclsts)) {
5014 if (!$VMS_STATUS_SUCCESS(fndsts))
5015 sec_flags = OSS$M_RELCTX;
5017 /* Get rid of the new ACE */
5018 aclsts = sys$set_security(NULL, NULL, NULL,
5019 sec_flags, dellst, &ctx, &access_mode);
5021 /* If there was an old ACE, put it back */
5022 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5023 addlst[0].bufadr = &oldace;
5024 aclsts = sys$set_security(NULL, NULL, NULL,
5025 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5026 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5028 set_vaxc_errno(aclsts);
5034 /* Try to clear the lock on the ACL list */
5035 aclsts2 = sys$set_security(NULL, NULL, NULL,
5036 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5038 /* Rename errors are most important */
5039 if (!$VMS_STATUS_SUCCESS(rnsts))
5042 set_vaxc_errno(aclsts);
5047 if (aclsts != SS$_ACLEMPTY)
5054 PerlMem_free(vmsname);
5059 /*{{{int rename(const char *, const char * */
5060 /* Not exactly what X/Open says to do, but doing it absolutely right
5061 * and efficiently would require a lot more work. This should be close
5062 * enough to pass all but the most strict X/Open compliance test.
5065 Perl_rename(pTHX_ const char *src, const char * dst)
5074 /* Validate the source file */
5075 src_sts = flex_lstat(src, &src_st);
5078 /* No source file or other problem */
5081 if (src_st.st_devnam[0] == 0) {
5082 /* This may be possible so fail if it is seen. */
5087 dst_sts = flex_lstat(dst, &dst_st);
5090 if (dst_st.st_dev != src_st.st_dev) {
5091 /* Must be on the same device */
5096 /* VMS_INO_T_COMPARE is true if the inodes are different
5097 * to match the output of memcmp
5100 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5101 /* That was easy, the files are the same! */
5105 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5106 /* If source is a directory, so must be dest */
5114 if ((dst_sts == 0) &&
5115 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5117 /* We have issues here if vms_unlink_all_versions is set
5118 * If the destination exists, and is not a directory, then
5119 * we must delete in advance.
5121 * If the src is a directory, then we must always pre-delete
5124 * If we successfully delete the dst in advance, and the rename fails
5125 * X/Open requires that errno be EIO.
5129 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5131 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
5132 S_ISDIR(dst_st.st_mode));
5134 /* Need to delete all versions ? */
5135 if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5138 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
5139 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
5144 /* Make sure that we do not loop forever */
5156 /* We killed the destination, so only errno now is EIO */
5161 /* Originally the idea was to call the CRTL rename() and only
5162 * try the lib$rename_file if it failed.
5163 * It turns out that there are too many variants in what the
5164 * the CRTL rename might do, so only use lib$rename_file
5169 /* Is the source and dest both in VMS format */
5170 /* if the source is a directory, then need to fileify */
5171 /* and dest must be a directory or non-existent. */
5176 unsigned long flags;
5177 struct dsc$descriptor_s old_file_dsc;
5178 struct dsc$descriptor_s new_file_dsc;
5180 /* We need to modify the src and dst depending
5181 * on if one or more of them are directories.
5184 vms_dst = (char *)PerlMem_malloc(VMS_MAXRSS);
5185 if (vms_dst == NULL)
5186 _ckvmssts_noperl(SS$_INSFMEM);
5188 if (S_ISDIR(src_st.st_mode)) {
5190 char * vms_dir_file;
5192 vms_dir_file = (char *)PerlMem_malloc(VMS_MAXRSS);
5193 if (vms_dir_file == NULL)
5194 _ckvmssts_noperl(SS$_INSFMEM);
5196 /* If the dest is a directory, we must remove it */
5199 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
5201 PerlMem_free(vms_dst);
5209 /* The dest must be a VMS file specification */
5210 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5211 if (ret_str == NULL) {
5212 PerlMem_free(vms_dst);
5217 /* The source must be a file specification */
5218 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5219 if (ret_str == NULL) {
5220 PerlMem_free(vms_dst);
5221 PerlMem_free(vms_dir_file);
5225 PerlMem_free(vms_dst);
5226 vms_dst = vms_dir_file;
5229 /* File to file or file to new dir */
5231 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5232 /* VMS pathify a dir target */
5233 ret_str = int_tovmspath(dst, vms_dst, NULL);
5234 if (ret_str == NULL) {
5235 PerlMem_free(vms_dst);
5240 char * v_spec, * r_spec, * d_spec, * n_spec;
5241 char * e_spec, * vs_spec;
5242 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5244 /* fileify a target VMS file specification */
5245 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5246 if (ret_str == NULL) {
5247 PerlMem_free(vms_dst);
5252 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5253 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5254 &e_len, &vs_spec, &vs_len);
5257 /* Get rid of the version */
5261 /* Need to specify a '.' so that the extension */
5262 /* is not inherited */
5263 strcat(vms_dst,".");
5269 old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5270 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
5271 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5272 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5274 new_file_dsc.dsc$a_pointer = vms_dst;
5275 new_file_dsc.dsc$w_length = strlen(vms_dst);
5276 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5277 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5280 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5281 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5284 sts = lib$rename_file(&old_file_dsc,
5288 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5289 if (!$VMS_STATUS_SUCCESS(sts)) {
5291 /* We could have failed because VMS style permissions do not
5292 * permit renames that UNIX will allow. Just like the hack
5295 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5298 PerlMem_free(vms_dst);
5299 if (!$VMS_STATUS_SUCCESS(sts)) {
5306 if (vms_unlink_all_versions) {
5307 /* Now get rid of any previous versions of the source file that
5313 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5314 S_ISDIR(src_st.st_mode));
5315 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
5316 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5317 S_ISDIR(src_st.st_mode));
5322 /* Make sure that we do not loop forever */
5331 /* We deleted the destination, so must force the error to be EIO */
5332 if ((retval != 0) && (pre_delete != 0))
5340 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5341 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5342 * to expand file specification. Allows for a single default file
5343 * specification and a simple mask of options. If outbuf is non-NULL,
5344 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5345 * the resultant file specification is placed. If outbuf is NULL, the
5346 * resultant file specification is placed into a static buffer.
5347 * The third argument, if non-NULL, is taken to be a default file
5348 * specification string. The fourth argument is unused at present.
5349 * rmesexpand() returns the address of the resultant string if
5350 * successful, and NULL on error.
5352 * New functionality for previously unused opts value:
5353 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5354 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5355 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5356 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5358 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5362 (const char *filespec,
5364 const char *defspec,
5370 const char * in_spec;
5372 const char * def_spec;
5373 char * vmsfspec, *vmsdefspec;
5377 struct FAB myfab = cc$rms_fab;
5378 rms_setup_nam(mynam);
5380 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5383 /* temp hack until UTF8 is actually implemented */
5384 if (fs_utf8 != NULL)
5387 if (!filespec || !*filespec) {
5388 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5398 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5399 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5400 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5402 /* If this is a UNIX file spec, convert it to VMS */
5403 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5404 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5405 &e_len, &vs_spec, &vs_len);
5410 vmsfspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5411 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5412 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5413 if (ret_spec == NULL) {
5414 PerlMem_free(vmsfspec);
5417 in_spec = (const char *)vmsfspec;
5419 /* Unless we are forcing to VMS format, a UNIX input means
5420 * UNIX output, and that requires long names to be used
5422 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5423 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5424 opts |= PERL_RMSEXPAND_M_LONG;
5434 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5435 rms_bind_fab_nam(myfab, mynam);
5437 /* Process the default file specification if present */
5439 if (defspec && *defspec) {
5441 t_isunix = is_unix_filespec(defspec);
5443 vmsdefspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5444 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5445 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5447 if (ret_spec == NULL) {
5448 /* Clean up and bail */
5449 PerlMem_free(vmsdefspec);
5450 if (vmsfspec != NULL)
5451 PerlMem_free(vmsfspec);
5454 def_spec = (const char *)vmsdefspec;
5456 rms_set_dna(myfab, mynam,
5457 (char *)def_spec, strlen(def_spec)); /* cast ok */
5460 /* Now we need the expansion buffers */
5461 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
5462 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5463 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5464 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
5465 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5467 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5469 /* If a NAML block is used RMS always writes to the long and short
5470 * addresses unless you suppress the short name.
5472 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5473 outbufl = (char *)PerlMem_malloc(VMS_MAXRSS);
5474 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5476 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5478 #ifdef NAM$M_NO_SHORT_UPCASE
5479 if (decc_efs_case_preserve)
5480 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5483 /* We may not want to follow symbolic links */
5484 #ifdef NAML$M_OPEN_SPECIAL
5485 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5486 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5489 /* First attempt to parse as an existing file */
5490 retsts = sys$parse(&myfab,0,0);
5491 if (!(retsts & STS$K_SUCCESS)) {
5493 /* Could not find the file, try as syntax only if error is not fatal */
5494 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5495 if (retsts == RMS$_DNF ||
5496 retsts == RMS$_DIR ||
5497 retsts == RMS$_DEV ||
5498 retsts == RMS$_PRV) {
5499 retsts = sys$parse(&myfab,0,0);
5500 if (retsts & STS$K_SUCCESS) goto int_expanded;
5503 /* Still could not parse the file specification */
5504 /*----------------------------------------------*/
5505 sts = rms_free_search_context(&myfab); /* Free search context */
5506 if (vmsdefspec != NULL)
5507 PerlMem_free(vmsdefspec);
5508 if (vmsfspec != NULL)
5509 PerlMem_free(vmsfspec);
5510 if (outbufl != NULL)
5511 PerlMem_free(outbufl);
5515 set_vaxc_errno(retsts);
5516 if (retsts == RMS$_PRV) set_errno(EACCES);
5517 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5518 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5519 else set_errno(EVMSERR);
5522 retsts = sys$search(&myfab,0,0);
5523 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5524 sts = rms_free_search_context(&myfab); /* Free search context */
5525 if (vmsdefspec != NULL)
5526 PerlMem_free(vmsdefspec);
5527 if (vmsfspec != NULL)
5528 PerlMem_free(vmsfspec);
5529 if (outbufl != NULL)
5530 PerlMem_free(outbufl);
5534 set_vaxc_errno(retsts);
5535 if (retsts == RMS$_PRV) set_errno(EACCES);
5536 else set_errno(EVMSERR);
5540 /* If the input filespec contained any lowercase characters,
5541 * downcase the result for compatibility with Unix-minded code. */
5543 if (!decc_efs_case_preserve) {
5545 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5546 if (islower(*tbuf)) { haslower = 1; break; }
5549 /* Is a long or a short name expected */
5550 /*------------------------------------*/
5552 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5553 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5554 if (rms_nam_rsll(mynam)) {
5556 speclen = rms_nam_rsll(mynam);
5559 spec_buf = esal; /* Not esa */
5560 speclen = rms_nam_esll(mynam);
5565 if (rms_nam_rsl(mynam)) {
5567 speclen = rms_nam_rsl(mynam);
5570 spec_buf = esa; /* Not esal */
5571 speclen = rms_nam_esl(mynam);
5573 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5576 spec_buf[speclen] = '\0';
5578 /* Trim off null fields added by $PARSE
5579 * If type > 1 char, must have been specified in original or default spec
5580 * (not true for version; $SEARCH may have added version of existing file).
5582 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5583 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5584 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5585 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5588 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5589 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5591 if (trimver || trimtype) {
5592 if (defspec && *defspec) {
5593 char *defesal = NULL;
5594 char *defesa = NULL;
5595 defesa = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5596 if (defesa != NULL) {
5597 struct FAB deffab = cc$rms_fab;
5598 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5599 defesal = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5600 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5602 rms_setup_nam(defnam);
5604 rms_bind_fab_nam(deffab, defnam);
5608 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5610 /* RMS needs the esa/esal as a work area if wildcards are involved */
5611 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5613 rms_clear_nam_nop(defnam);
5614 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5615 #ifdef NAM$M_NO_SHORT_UPCASE
5616 if (decc_efs_case_preserve)
5617 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5619 #ifdef NAML$M_OPEN_SPECIAL
5620 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5621 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5623 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5625 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5628 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5631 if (defesal != NULL)
5632 PerlMem_free(defesal);
5633 PerlMem_free(defesa);
5635 _ckvmssts_noperl(SS$_INSFMEM);
5639 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5640 if (*(rms_nam_verl(mynam)) != '\"')
5641 speclen = rms_nam_verl(mynam) - spec_buf;
5644 if (*(rms_nam_ver(mynam)) != '\"')
5645 speclen = rms_nam_ver(mynam) - spec_buf;
5649 /* If we didn't already trim version, copy down */
5650 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5651 if (speclen > rms_nam_verl(mynam) - spec_buf)
5653 (rms_nam_typel(mynam),
5654 rms_nam_verl(mynam),
5655 speclen - (rms_nam_verl(mynam) - spec_buf));
5656 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5659 if (speclen > rms_nam_ver(mynam) - spec_buf)
5661 (rms_nam_type(mynam),
5663 speclen - (rms_nam_ver(mynam) - spec_buf));
5664 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5669 /* Done with these copies of the input files */
5670 /*-------------------------------------------*/
5671 if (vmsfspec != NULL)
5672 PerlMem_free(vmsfspec);
5673 if (vmsdefspec != NULL)
5674 PerlMem_free(vmsdefspec);
5676 /* If we just had a directory spec on input, $PARSE "helpfully"
5677 * adds an empty name and type for us */
5678 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5679 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5680 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5681 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5682 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5683 speclen = rms_nam_namel(mynam) - spec_buf;
5688 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5689 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5690 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5691 speclen = rms_nam_name(mynam) - spec_buf;
5694 /* Posix format specifications must have matching quotes */
5695 if (speclen < (VMS_MAXRSS - 1)) {
5696 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5697 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5698 spec_buf[speclen] = '\"';
5703 spec_buf[speclen] = '\0';
5704 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5706 /* Have we been working with an expanded, but not resultant, spec? */
5707 /* Also, convert back to Unix syntax if necessary. */
5711 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5712 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5713 rsl = rms_nam_rsll(mynam);
5717 rsl = rms_nam_rsl(mynam);
5720 /* rsl is not present, it means that spec_buf is either */
5721 /* esa or esal, and needs to be copied to outbuf */
5722 /* convert to Unix if desired */
5724 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5726 /* VMS file specs are not in UTF-8 */
5727 if (fs_utf8 != NULL)
5729 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5734 /* Now spec_buf is either outbuf or outbufl */
5735 /* We need the result into outbuf */
5737 /* If we need this in UNIX, then we need another buffer */
5738 /* to keep things in order */
5740 char * new_src = NULL;
5741 if (spec_buf == outbuf) {
5742 new_src = (char *)PerlMem_malloc(VMS_MAXRSS);
5743 my_strlcpy(new_src, spec_buf, VMS_MAXRSS);
5747 ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5749 PerlMem_free(new_src);
5752 /* VMS file specs are not in UTF-8 */
5753 if (fs_utf8 != NULL)
5756 /* Copy the buffer if needed */
5757 if (outbuf != spec_buf)
5758 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5764 /* Need to clean up the search context */
5765 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5766 sts = rms_free_search_context(&myfab); /* Free search context */
5768 /* Clean up the extra buffers */
5772 if (outbufl != NULL)
5773 PerlMem_free(outbufl);
5775 /* Return the result */
5779 /* Common simple case - Expand an already VMS spec */
5781 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5782 opts |= PERL_RMSEXPAND_M_VMS_IN;
5783 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5786 /* Common simple case - Expand to a VMS spec */
5788 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5789 opts |= PERL_RMSEXPAND_M_VMS;
5790 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5794 /* Entry point used by perl routines */
5797 (pTHX_ const char *filespec,
5800 const char *defspec,
5805 static char __rmsexpand_retbuf[VMS_MAXRSS];
5806 char * expanded, *ret_spec, *ret_buf;
5810 if (ret_buf == NULL) {
5812 Newx(expanded, VMS_MAXRSS, char);
5813 if (expanded == NULL)
5814 _ckvmssts(SS$_INSFMEM);
5817 ret_buf = __rmsexpand_retbuf;
5822 ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5823 opts, fs_utf8, dfs_utf8);
5825 if (ret_spec == NULL) {
5826 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5834 /* External entry points */
5835 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5836 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5837 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5838 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5839 char *Perl_rmsexpand_utf8
5840 (pTHX_ const char *spec, char *buf, const char *def,
5841 unsigned opt, int * fs_utf8, int * dfs_utf8)
5842 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5843 char *Perl_rmsexpand_utf8_ts
5844 (pTHX_ const char *spec, char *buf, const char *def,
5845 unsigned opt, int * fs_utf8, int * dfs_utf8)
5846 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
5850 ** The following routines are provided to make life easier when
5851 ** converting among VMS-style and Unix-style directory specifications.
5852 ** All will take input specifications in either VMS or Unix syntax. On
5853 ** failure, all return NULL. If successful, the routines listed below
5854 ** return a pointer to a buffer containing the appropriately
5855 ** reformatted spec (and, therefore, subsequent calls to that routine
5856 ** will clobber the result), while the routines of the same names with
5857 ** a _ts suffix appended will return a pointer to a mallocd string
5858 ** containing the appropriately reformatted spec.
5859 ** In all cases, only explicit syntax is altered; no check is made that
5860 ** the resulting string is valid or that the directory in question
5863 ** fileify_dirspec() - convert a directory spec into the name of the
5864 ** directory file (i.e. what you can stat() to see if it's a dir).
5865 ** The style (VMS or Unix) of the result is the same as the style
5866 ** of the parameter passed in.
5867 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5868 ** what you prepend to a filename to indicate what directory it's in).
5869 ** The style (VMS or Unix) of the result is the same as the style
5870 ** of the parameter passed in.
5871 ** tounixpath() - convert a directory spec into a Unix-style path.
5872 ** tovmspath() - convert a directory spec into a VMS-style path.
5873 ** tounixspec() - convert any file spec into a Unix-style file spec.
5874 ** tovmsspec() - convert any file spec into a VMS-style spec.
5875 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5877 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
5878 ** Permission is given to distribute this code as part of the Perl
5879 ** standard distribution under the terms of the GNU General Public
5880 ** License or the Perl Artistic License. Copies of each may be
5881 ** found in the Perl standard distribution.
5884 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5886 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
5888 unsigned long int dirlen, retlen, hasfilename = 0;
5889 char *cp1, *cp2, *lastdir;
5890 char *trndir, *vmsdir;
5891 unsigned short int trnlnm_iter_count;
5893 if (utf8_fl != NULL)
5896 if (!dir || !*dir) {
5897 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5899 dirlen = strlen(dir);
5900 while (dirlen && dir[dirlen-1] == '/') --dirlen;
5901 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
5902 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
5909 if (dirlen > (VMS_MAXRSS - 1)) {
5910 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
5913 trndir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5914 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5915 if (!strpbrk(dir+1,"/]>:") &&
5916 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
5917 strcpy(trndir,*dir == '/' ? dir + 1: dir);
5918 trnlnm_iter_count = 0;
5919 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
5920 trnlnm_iter_count++;
5921 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5923 dirlen = strlen(trndir);
5926 memcpy(trndir, dir, dirlen);
5927 trndir[dirlen] = '\0';
5930 /* At this point we are done with *dir and use *trndir which is a
5931 * copy that can be modified. *dir must not be modified.
5934 /* If we were handed a rooted logical name or spec, treat it like a
5935 * simple directory, so that
5936 * $ Define myroot dev:[dir.]
5937 * ... do_fileify_dirspec("myroot",buf,1) ...
5938 * does something useful.
5940 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
5941 trndir[--dirlen] = '\0';
5942 trndir[dirlen-1] = ']';
5944 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
5945 trndir[--dirlen] = '\0';
5946 trndir[dirlen-1] = '>';
5949 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
5950 /* If we've got an explicit filename, we can just shuffle the string. */
5951 if (*(cp1+1)) hasfilename = 1;
5952 /* Similarly, we can just back up a level if we've got multiple levels
5953 of explicit directories in a VMS spec which ends with directories. */
5955 for (cp2 = cp1; cp2 > trndir; cp2--) {
5957 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
5958 /* fix-me, can not scan EFS file specs backward like this */
5959 *cp2 = *cp1; *cp1 = '\0';
5964 if (*cp2 == '[' || *cp2 == '<') break;
5969 vmsdir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5970 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5971 cp1 = strpbrk(trndir,"]:>");
5972 if (hasfilename || !cp1) { /* filename present or not VMS */
5974 if (trndir[0] == '.') {
5975 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
5976 PerlMem_free(trndir);
5977 PerlMem_free(vmsdir);
5978 return int_fileify_dirspec("[]", buf, NULL);
5980 else if (trndir[1] == '.' &&
5981 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
5982 PerlMem_free(trndir);
5983 PerlMem_free(vmsdir);
5984 return int_fileify_dirspec("[-]", buf, NULL);
5987 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
5988 dirlen -= 1; /* to last element */
5989 lastdir = strrchr(trndir,'/');
5991 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
5992 /* If we have "/." or "/..", VMSify it and let the VMS code
5993 * below expand it, rather than repeating the code to handle
5994 * relative components of a filespec here */
5996 if (*(cp1+2) == '.') cp1++;
5997 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
5999 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6000 PerlMem_free(trndir);
6001 PerlMem_free(vmsdir);
6004 if (strchr(vmsdir,'/') != NULL) {
6005 /* If int_tovmsspec() returned it, it must have VMS syntax
6006 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6007 * the time to check this here only so we avoid a recursion
6008 * loop; otherwise, gigo.
6010 PerlMem_free(trndir);
6011 PerlMem_free(vmsdir);
6012 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6015 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6016 PerlMem_free(trndir);
6017 PerlMem_free(vmsdir);
6020 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6021 PerlMem_free(trndir);
6022 PerlMem_free(vmsdir);
6026 } while ((cp1 = strstr(cp1,"/.")) != NULL);
6027 lastdir = strrchr(trndir,'/');
6029 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6031 /* Ditto for specs that end in an MFD -- let the VMS code
6032 * figure out whether it's a real device or a rooted logical. */
6034 /* This should not happen any more. Allowing the fake /000000
6035 * in a UNIX pathname causes all sorts of problems when trying
6036 * to run in UNIX emulation. So the VMS to UNIX conversions
6037 * now remove the fake /000000 directories.
6040 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6041 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6042 PerlMem_free(trndir);
6043 PerlMem_free(vmsdir);
6046 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6047 PerlMem_free(trndir);
6048 PerlMem_free(vmsdir);
6051 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6052 PerlMem_free(trndir);
6053 PerlMem_free(vmsdir);
6058 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6059 !(lastdir = cp1 = strrchr(trndir,']')) &&
6060 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6062 cp2 = strrchr(cp1,'.');
6064 int e_len, vs_len = 0;
6067 cp3 = strchr(cp2,';');
6068 e_len = strlen(cp2);
6070 vs_len = strlen(cp3);
6071 e_len = e_len - vs_len;
6073 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6075 if (!decc_efs_charset) {
6076 /* If this is not EFS, then not a directory */
6077 PerlMem_free(trndir);
6078 PerlMem_free(vmsdir);
6080 set_vaxc_errno(RMS$_DIR);
6084 /* Ok, here we have an issue, technically if a .dir shows */
6085 /* from inside a directory, then we should treat it as */
6086 /* xxx^.dir.dir. But we do not have that context at this */
6087 /* point unless this is totally restructured, so we remove */
6088 /* The .dir for now, and fix this better later */
6089 dirlen = cp2 - trndir;
6091 if (decc_efs_charset && !strchr(trndir,'/')) {
6092 /* Dots are allowed in dir names, so escape them if input not in Unix syntax. */
6093 char *cp4 = is_dir ? (cp2 - 1) : cp2;
6095 for (; cp4 > cp1; cp4--) {
6097 if ((cp4 - 1 > trndir) && (*(cp4 - 1) != '^')) {
6098 memmove(cp4 + 1, cp4, trndir + dirlen - cp4 + 1);
6109 retlen = dirlen + 6;
6110 memcpy(buf, trndir, dirlen);
6113 /* We've picked up everything up to the directory file name.
6114 Now just add the type and version, and we're set. */
6115 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
6116 strcat(buf,".dir;1");
6118 strcat(buf,".DIR;1");
6119 PerlMem_free(trndir);
6120 PerlMem_free(vmsdir);
6123 else { /* VMS-style directory spec */
6125 char *esa, *esal, term, *cp;
6128 unsigned long int cmplen, haslower = 0;
6129 struct FAB dirfab = cc$rms_fab;
6130 rms_setup_nam(savnam);
6131 rms_setup_nam(dirnam);
6133 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
6134 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6136 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6137 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
6138 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6140 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6141 rms_bind_fab_nam(dirfab, dirnam);
6142 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6143 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6144 #ifdef NAM$M_NO_SHORT_UPCASE
6145 if (decc_efs_case_preserve)
6146 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6149 for (cp = trndir; *cp; cp++)
6150 if (islower(*cp)) { haslower = 1; break; }
6151 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6152 if ((dirfab.fab$l_sts == RMS$_DIR) ||
6153 (dirfab.fab$l_sts == RMS$_DNF) ||
6154 (dirfab.fab$l_sts == RMS$_PRV)) {
6155 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6156 sts = sys$parse(&dirfab);
6162 PerlMem_free(trndir);
6163 PerlMem_free(vmsdir);
6165 set_vaxc_errno(dirfab.fab$l_sts);
6171 /* Does the file really exist? */
6172 if (sys$search(&dirfab)& STS$K_SUCCESS) {
6173 /* Yes; fake the fnb bits so we'll check type below */
6174 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6176 else { /* No; just work with potential name */
6177 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6180 fab_sts = dirfab.fab$l_sts;
6181 sts = rms_free_search_context(&dirfab);
6185 PerlMem_free(trndir);
6186 PerlMem_free(vmsdir);
6187 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
6193 /* Make sure we are using the right buffer */
6194 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6197 my_esa_len = rms_nam_esll(dirnam);
6201 my_esa_len = rms_nam_esl(dirnam);
6202 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6205 my_esa[my_esa_len] = '\0';
6206 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6207 cp1 = strchr(my_esa,']');
6208 if (!cp1) cp1 = strchr(my_esa,'>');
6209 if (cp1) { /* Should always be true */
6210 my_esa_len -= cp1 - my_esa - 1;
6211 memmove(my_esa, cp1 + 1, my_esa_len);
6214 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6215 /* Yep; check version while we're at it, if it's there. */
6216 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6217 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6218 /* Something other than .DIR[;1]. Bzzt. */
6219 sts = rms_free_search_context(&dirfab);
6223 PerlMem_free(trndir);
6224 PerlMem_free(vmsdir);
6226 set_vaxc_errno(RMS$_DIR);
6231 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6232 /* They provided at least the name; we added the type, if necessary, */
6233 my_strlcpy(buf, my_esa, VMS_MAXRSS);
6234 sts = rms_free_search_context(&dirfab);
6235 PerlMem_free(trndir);
6239 PerlMem_free(vmsdir);
6242 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6243 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6247 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6248 if (cp1 == NULL) { /* should never happen */
6249 sts = rms_free_search_context(&dirfab);
6250 PerlMem_free(trndir);
6254 PerlMem_free(vmsdir);
6259 retlen = strlen(my_esa);
6260 cp1 = strrchr(my_esa,'.');
6261 /* ODS-5 directory specifications can have extra "." in them. */
6262 /* Fix-me, can not scan EFS file specifications backwards */
6263 while (cp1 != NULL) {
6264 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6268 while ((cp1 > my_esa) && (*cp1 != '.'))
6275 if ((cp1) != NULL) {
6276 /* There's more than one directory in the path. Just roll back. */
6278 my_strlcpy(buf, my_esa, VMS_MAXRSS);
6281 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6282 /* Go back and expand rooted logical name */
6283 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6284 #ifdef NAM$M_NO_SHORT_UPCASE
6285 if (decc_efs_case_preserve)
6286 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6288 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6289 sts = rms_free_search_context(&dirfab);
6293 PerlMem_free(trndir);
6294 PerlMem_free(vmsdir);
6296 set_vaxc_errno(dirfab.fab$l_sts);
6300 /* This changes the length of the string of course */
6302 my_esa_len = rms_nam_esll(dirnam);
6304 my_esa_len = rms_nam_esl(dirnam);
6307 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6308 cp1 = strstr(my_esa,"][");
6309 if (!cp1) cp1 = strstr(my_esa,"]<");
6310 dirlen = cp1 - my_esa;
6311 memcpy(buf, my_esa, dirlen);
6312 if (!strncmp(cp1+2,"000000]",7)) {
6313 buf[dirlen-1] = '\0';
6314 /* fix-me Not full ODS-5, just extra dots in directories for now */
6315 cp1 = buf + dirlen - 1;
6321 if (*(cp1-1) != '^')
6326 if (*cp1 == '.') *cp1 = ']';
6328 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6329 memmove(cp1+1,"000000]",7);
6333 memmove(buf+dirlen, cp1+2, retlen-dirlen);
6335 /* Convert last '.' to ']' */
6337 while (*cp != '[') {
6340 /* Do not trip on extra dots in ODS-5 directories */
6341 if ((cp1 == buf) || (*(cp1-1) != '^'))
6345 if (*cp1 == '.') *cp1 = ']';
6347 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6348 memmove(cp1+1,"000000]",7);
6352 else { /* This is a top-level dir. Add the MFD to the path. */
6355 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
6356 strcpy(cp2,":[000000]");
6361 sts = rms_free_search_context(&dirfab);
6362 /* We've set up the string up through the filename. Add the
6363 type and version, and we're done. */
6364 strcat(buf,".DIR;1");
6366 /* $PARSE may have upcased filespec, so convert output to lower
6367 * case if input contained any lowercase characters. */
6368 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
6369 PerlMem_free(trndir);
6373 PerlMem_free(vmsdir);
6376 } /* end of int_fileify_dirspec() */
6379 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6380 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6382 static char __fileify_retbuf[VMS_MAXRSS];
6383 char * fileified, *ret_spec, *ret_buf;
6387 if (ret_buf == NULL) {
6389 Newx(fileified, VMS_MAXRSS, char);
6390 if (fileified == NULL)
6391 _ckvmssts(SS$_INSFMEM);
6392 ret_buf = fileified;
6394 ret_buf = __fileify_retbuf;
6398 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6400 if (ret_spec == NULL) {
6401 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6403 Safefree(fileified);
6407 } /* end of do_fileify_dirspec() */
6410 /* External entry points */
6411 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6412 { return do_fileify_dirspec(dir,buf,0,NULL); }
6413 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6414 { return do_fileify_dirspec(dir,buf,1,NULL); }
6415 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6416 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6417 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6418 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6420 static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6421 char * v_spec, int v_len, char * r_spec, int r_len,
6422 char * d_spec, int d_len, char * n_spec, int n_len,
6423 char * e_spec, int e_len, char * vs_spec, int vs_len) {
6425 /* VMS specification - Try to do this the simple way */
6426 if ((v_len + r_len > 0) || (d_len > 0)) {
6429 /* No name or extension component, already a directory */
6430 if ((n_len + e_len + vs_len) == 0) {
6435 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6436 /* This results from catfile() being used instead of catdir() */
6437 /* So even though it should not work, we need to allow it */
6439 /* If this is .DIR;1 then do a simple conversion */
6440 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6441 if (is_dir || (e_len == 0) && (d_len > 0)) {
6443 len = v_len + r_len + d_len - 1;
6444 char dclose = d_spec[d_len - 1];
6445 memcpy(buf, dir, len);
6448 memcpy(&buf[len], n_spec, n_len);
6451 buf[len + 1] = '\0';
6456 else if (d_len > 0) {
6457 /* In the olden days, a directory needed to have a .DIR */
6458 /* extension to be a valid directory, but now it could */
6459 /* be a symbolic link */
6461 len = v_len + r_len + d_len - 1;
6462 char dclose = d_spec[d_len - 1];
6463 memcpy(buf, dir, len);
6466 memcpy(&buf[len], n_spec, n_len);
6469 if (decc_efs_charset) {
6472 memcpy(&buf[len], e_spec, e_len);
6475 set_vaxc_errno(RMS$_DIR);
6481 buf[len + 1] = '\0';
6486 set_vaxc_errno(RMS$_DIR);
6492 set_vaxc_errno(RMS$_DIR);
6498 /* Internal routine to make sure or convert a directory to be in a */
6499 /* path specification. No utf8 flag because it is not changed or used */
6500 static char *int_pathify_dirspec(const char *dir, char *buf)
6502 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6503 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6504 char * exp_spec, *ret_spec;
6506 unsigned short int trnlnm_iter_count;
6510 if (vms_debug_fileify) {
6512 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6514 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6517 /* We may need to lower case the result if we translated */
6518 /* a logical name or got the current working directory */
6521 if (!dir || !*dir) {
6523 set_vaxc_errno(SS$_BADPARAM);
6527 trndir = (char *)PerlMem_malloc(VMS_MAXRSS);
6529 _ckvmssts_noperl(SS$_INSFMEM);
6531 /* If no directory specified use the current default */
6533 my_strlcpy(trndir, dir, VMS_MAXRSS);
6535 getcwd(trndir, VMS_MAXRSS - 1);
6539 /* now deal with bare names that could be logical names */
6540 trnlnm_iter_count = 0;
6541 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6542 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6543 trnlnm_iter_count++;
6545 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6547 trnlen = strlen(trndir);
6549 /* Trap simple rooted lnms, and return lnm:[000000] */
6550 if (!strcmp(trndir+trnlen-2,".]")) {
6551 my_strlcpy(buf, dir, VMS_MAXRSS);
6552 strcat(buf, ":[000000]");
6553 PerlMem_free(trndir);
6555 if (vms_debug_fileify) {
6556 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6562 /* At this point we do not work with *dir, but the copy in *trndir */
6564 if (need_to_lower && !decc_efs_case_preserve) {
6565 /* Legacy mode, lower case the returned value */
6566 __mystrtolower(trndir);
6570 /* Some special cases, '..', '.' */
6572 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6573 /* Force UNIX filespec */
6577 /* Is this Unix or VMS format? */
6578 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6579 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6580 &e_len, &vs_spec, &vs_len);
6583 /* Just a filename? */
6584 if ((v_len + r_len + d_len) == 0) {
6586 /* Now we have a problem, this could be Unix or VMS */
6587 /* We have to guess. .DIR usually means VMS */
6589 /* In UNIX report mode, the .DIR extension is removed */
6590 /* if one shows up, it is for a non-directory or a directory */
6591 /* in EFS charset mode */
6593 /* So if we are in Unix report mode, assume that this */
6594 /* is a relative Unix directory specification */
6597 if (!decc_filename_unix_report && decc_efs_charset) {
6599 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6602 /* Traditional mode, assume .DIR is directory */
6605 memcpy(&buf[2], n_spec, n_len);
6606 buf[n_len + 2] = ']';
6607 buf[n_len + 3] = '\0';
6608 PerlMem_free(trndir);
6609 if (vms_debug_fileify) {
6611 "int_pathify_dirspec: buf = %s\n",
6621 ret_spec = int_pathify_dirspec_simple(trndir, buf,
6622 v_spec, v_len, r_spec, r_len,
6623 d_spec, d_len, n_spec, n_len,
6624 e_spec, e_len, vs_spec, vs_len);
6626 if (ret_spec != NULL) {
6627 PerlMem_free(trndir);
6628 if (vms_debug_fileify) {
6630 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6635 /* Simple way did not work, which means that a logical name */
6636 /* was present for the directory specification. */
6637 /* Need to use an rmsexpand variant to decode it completely */
6638 exp_spec = (char *)PerlMem_malloc(VMS_MAXRSS);
6639 if (exp_spec == NULL)
6640 _ckvmssts_noperl(SS$_INSFMEM);
6642 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6643 if (ret_spec != NULL) {
6644 sts = vms_split_path(exp_spec, &v_spec, &v_len,
6645 &r_spec, &r_len, &d_spec, &d_len,
6646 &n_spec, &n_len, &e_spec,
6647 &e_len, &vs_spec, &vs_len);
6649 ret_spec = int_pathify_dirspec_simple(
6650 exp_spec, buf, v_spec, v_len, r_spec, r_len,
6651 d_spec, d_len, n_spec, n_len,
6652 e_spec, e_len, vs_spec, vs_len);
6654 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6655 /* Legacy mode, lower case the returned value */
6656 __mystrtolower(ret_spec);
6659 set_vaxc_errno(RMS$_DIR);
6664 PerlMem_free(exp_spec);
6665 PerlMem_free(trndir);
6666 if (vms_debug_fileify) {
6667 if (ret_spec == NULL)
6668 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6671 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6676 /* Unix specification, Could be trivial conversion, */
6677 /* but have to deal with trailing '.dir' or extra '.' */
6682 STRLEN dir_len = strlen(trndir);
6684 lastslash = strrchr(trndir, '/');
6685 if (lastslash == NULL)
6692 /* '..' or '.' are valid directory components */
6694 if (lastslash[0] == '.') {
6695 if (lastslash[1] == '\0') {
6697 } else if (lastslash[1] == '.') {
6698 if (lastslash[2] == '\0') {
6701 /* And finally allow '...' */
6702 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6710 lastdot = strrchr(lastslash, '.');
6712 if (lastdot != NULL) {
6714 /* '.dir' is discarded, and any other '.' is invalid */
6715 e_len = strlen(lastdot);
6717 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6720 dir_len = dir_len - 4;
6724 my_strlcpy(buf, trndir, VMS_MAXRSS);
6725 if (buf[dir_len - 1] != '/') {
6727 buf[dir_len + 1] = '\0';
6730 /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6731 if (!decc_efs_charset) {
6734 if (str[0] == '.') {
6737 while ((dots[cnt] == '.') && (cnt < 3))
6740 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6746 for (; *str; ++str) {
6747 while (*str == '/') {
6753 /* Have to skip up to three dots which could be */
6754 /* directories, 3 dots being a VMS extension for Perl */
6757 while ((dots[cnt] == '.') && (cnt < 3)) {
6760 if (dots[cnt] == '\0')
6762 if ((cnt > 1) && (dots[cnt] != '/')) {
6768 /* too many dots? */
6769 if ((cnt == 0) || (cnt > 3)) {
6773 if (!dir_start && (*str == '.')) {
6778 PerlMem_free(trndir);
6780 if (vms_debug_fileify) {
6781 if (ret_spec == NULL)
6782 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6785 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6791 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6792 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6794 static char __pathify_retbuf[VMS_MAXRSS];
6795 char * pathified, *ret_spec, *ret_buf;
6799 if (ret_buf == NULL) {
6801 Newx(pathified, VMS_MAXRSS, char);
6802 if (pathified == NULL)
6803 _ckvmssts(SS$_INSFMEM);
6804 ret_buf = pathified;
6806 ret_buf = __pathify_retbuf;
6810 ret_spec = int_pathify_dirspec(dir, ret_buf);
6812 if (ret_spec == NULL) {
6813 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6815 Safefree(pathified);
6820 } /* end of do_pathify_dirspec() */
6823 /* External entry points */
6824 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6825 { return do_pathify_dirspec(dir,buf,0,NULL); }
6826 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
6827 { return do_pathify_dirspec(dir,buf,1,NULL); }
6828 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
6829 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
6830 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
6831 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
6833 /* Internal tounixspec routine that does not use a thread context */
6834 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
6835 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
6837 char *dirend, *cp1, *cp3, *tmp;
6840 unsigned short int trnlnm_iter_count;
6842 if (utf8_fl != NULL)
6845 if (vms_debug_fileify) {
6847 fprintf(stderr, "int_tounixspec: spec = NULL\n");
6849 fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
6855 set_vaxc_errno(SS$_BADPARAM);
6858 if (strlen(spec) > (VMS_MAXRSS-1)) {
6860 set_vaxc_errno(SS$_BUFFEROVF);
6864 /* New VMS specific format needs translation
6865 * glob passes filenames with trailing '\n' and expects this preserved.
6867 if (decc_posix_compliant_pathnames) {
6868 if (strncmp(spec, "\"^UP^", 5) == 0) {
6874 tunix = (char *)PerlMem_malloc(VMS_MAXRSS);
6875 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6876 tunix_len = my_strlcpy(tunix, spec, VMS_MAXRSS);
6878 if (tunix[tunix_len - 1] == '\n') {
6879 tunix[tunix_len - 1] = '\"';
6880 tunix[tunix_len] = '\0';
6884 uspec = decc$translate_vms(tunix);
6885 PerlMem_free(tunix);
6886 if ((int)uspec > 0) {
6887 my_strlcpy(rslt, uspec, VMS_MAXRSS);
6892 /* If we can not translate it, makemaker wants as-is */
6893 my_strlcpy(rslt, spec, VMS_MAXRSS);
6900 cmp_rslt = 0; /* Presume VMS */
6901 cp1 = strchr(spec, '/');
6905 /* Look for EFS ^/ */
6906 if (decc_efs_charset) {
6907 while (cp1 != NULL) {
6910 /* Found illegal VMS, assume UNIX */
6915 cp1 = strchr(cp1, '/');
6919 /* Look for "." and ".." */
6920 if (decc_filename_unix_report) {
6921 if (spec[0] == '.') {
6922 if ((spec[1] == '\0') || (spec[1] == '\n')) {
6926 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
6932 /* This is already UNIX or at least nothing VMS understands */
6934 my_strlcpy(rslt, spec, VMS_MAXRSS);
6935 if (vms_debug_fileify) {
6936 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6943 dirend = strrchr(spec,']');
6944 if (dirend == NULL) dirend = strrchr(spec,'>');
6945 if (dirend == NULL) dirend = strchr(spec,':');
6946 if (dirend == NULL) {
6948 if (vms_debug_fileify) {
6949 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6954 /* Special case 1 - sys$posix_root = / */
6955 if (!decc_disable_posix_root) {
6956 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
6963 /* Special case 2 - Convert NLA0: to /dev/null */
6964 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
6965 if (cmp_rslt == 0) {
6966 strcpy(rslt, "/dev/null");
6969 if (spec[6] != '\0') {
6976 /* Also handle special case "SYS$SCRATCH:" */
6977 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
6978 tmp = (char *)PerlMem_malloc(VMS_MAXRSS);
6979 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6980 if (cmp_rslt == 0) {
6983 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
6985 strcpy(rslt, "/tmp");
6988 if (spec[12] != '\0') {
6996 if (*cp2 != '[' && *cp2 != '<') {
6999 else { /* the VMS spec begins with directories */
7001 if (*cp2 == ']' || *cp2 == '>') {
7002 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
7006 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7007 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7009 if (vms_debug_fileify) {
7010 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7014 trnlnm_iter_count = 0;
7017 while (*cp3 != ':' && *cp3) cp3++;
7019 if (strchr(cp3,']') != NULL) break;
7020 trnlnm_iter_count++;
7021 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7022 } while (vmstrnenv(tmp,tmp,0,fildev,0));
7027 *(cp1++) = *(cp3++);
7028 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7030 set_errno(ENAMETOOLONG);
7031 set_vaxc_errno(SS$_BUFFEROVF);
7032 if (vms_debug_fileify) {
7033 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7035 return NULL; /* No room */
7040 if ((*cp2 == '^')) {
7041 /* EFS file escape, pass the next character as is */
7042 /* Fix me: HEX encoding for Unicode not implemented */
7045 else if ( *cp2 == '.') {
7046 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7047 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7054 for (; cp2 <= dirend; cp2++) {
7055 if ((*cp2 == '^')) {
7056 /* EFS file escape, pass the next character as is */
7057 /* Fix me: HEX encoding for Unicode not implemented */
7058 *(cp1++) = *(++cp2);
7059 /* An escaped dot stays as is -- don't convert to slash */
7060 if (*cp2 == '.') cp2++;
7064 if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7066 else if (*cp2 == ']' || *cp2 == '>') {
7067 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7069 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7071 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7072 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7073 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7074 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7075 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7077 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7078 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7082 else if (*cp2 == '-') {
7083 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7084 while (*cp2 == '-') {
7086 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7088 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7089 /* filespecs like */
7090 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
7091 if (vms_debug_fileify) {
7092 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7097 else *(cp1++) = *cp2;
7099 else *(cp1++) = *cp2;
7101 /* Translate the rest of the filename. */
7106 /* Fixme - for compatibility with the CRTL we should be removing */
7107 /* spaces from the file specifications, but this may show that */
7108 /* some tests that were appearing to pass are not really passing */
7114 /* Fix me hex expansions not implemented */
7115 cp2++; /* '^.' --> '.' and other. */
7121 *(cp1++) = *(cp2++);
7126 if (decc_filename_unix_no_version) {
7127 /* Easy, drop the version */
7132 /* Punt - passing the version as a dot will probably */
7133 /* break perl in weird ways, but so did passing */
7134 /* through the ; as a version. Follow the CRTL and */
7135 /* hope for the best. */
7142 /* We will need to fix this properly later */
7143 /* As Perl may be installed on an ODS-5 volume, but not */
7144 /* have the EFS_CHARSET enabled, it still may encounter */
7145 /* filenames with extra dots in them, and a precedent got */
7146 /* set which allowed them to work, that we will uphold here */
7147 /* If extra dots are present in a name and no ^ is on them */
7148 /* VMS assumes that the first one is the extension delimiter */
7149 /* the rest have an implied ^. */
7151 /* this is also a conflict as the . is also a version */
7152 /* delimiter in VMS, */
7154 *(cp1++) = *(cp2++);
7158 /* This is an extension */
7159 if (decc_readdir_dropdotnotype) {
7161 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7162 /* Drop the dot for the extension */
7170 *(cp1++) = *(cp2++);
7175 /* This still leaves /000000/ when working with a
7176 * VMS device root or concealed root.
7182 ulen = strlen(rslt);
7184 /* Get rid of "000000/ in rooted filespecs */
7186 zeros = strstr(rslt, "/000000/");
7187 if (zeros != NULL) {
7189 mlen = ulen - (zeros - rslt) - 7;
7190 memmove(zeros, &zeros[7], mlen);
7197 if (vms_debug_fileify) {
7198 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7202 } /* end of int_tounixspec() */
7205 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7206 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7208 static char __tounixspec_retbuf[VMS_MAXRSS];
7209 char * unixspec, *ret_spec, *ret_buf;
7213 if (ret_buf == NULL) {
7215 Newx(unixspec, VMS_MAXRSS, char);
7216 if (unixspec == NULL)
7217 _ckvmssts(SS$_INSFMEM);
7220 ret_buf = __tounixspec_retbuf;
7224 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7226 if (ret_spec == NULL) {
7227 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7234 } /* end of do_tounixspec() */
7236 /* External entry points */
7237 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7238 { return do_tounixspec(spec,buf,0, NULL); }
7239 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7240 { return do_tounixspec(spec,buf,1, NULL); }
7241 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7242 { return do_tounixspec(spec,buf,0, utf8_fl); }
7243 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7244 { return do_tounixspec(spec,buf,1, utf8_fl); }
7246 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7249 This procedure is used to identify if a path is based in either
7250 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7251 it returns the OpenVMS format directory for it.
7253 It is expecting specifications of only '/' or '/xxxx/'
7255 If a posix root does not exist, or 'xxxx' is not a directory
7256 in the posix root, it returns a failure.
7258 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7260 It is used only internally by posix_to_vmsspec_hardway().
7263 static int posix_root_to_vms
7264 (char *vmspath, int vmspath_len,
7265 const char *unixpath,
7266 const int * utf8_fl)
7269 struct FAB myfab = cc$rms_fab;
7270 rms_setup_nam(mynam);
7271 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7272 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7273 char * esa, * esal, * rsa, * rsal;
7279 unixlen = strlen(unixpath);
7284 #if __CRTL_VER >= 80200000
7285 /* If not a posix spec already, convert it */
7286 if (decc_posix_compliant_pathnames) {
7287 if (strncmp(unixpath,"\"^UP^",5) != 0) {
7288 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7291 /* This is already a VMS specification, no conversion */
7293 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7302 /* Check to see if this is under the POSIX root */
7303 if (decc_disable_posix_root) {
7307 /* Skip leading / */
7308 if (unixpath[0] == '/') {
7314 strcpy(vmspath,"SYS$POSIX_ROOT:");
7316 /* If this is only the / , or blank, then... */
7317 if (unixpath[0] == '\0') {
7318 /* by definition, this is the answer */
7322 /* Need to look up a directory */
7326 /* Copy and add '^' escape characters as needed */
7329 while (unixpath[i] != 0) {
7332 j += copy_expand_unix_filename_escape
7333 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7337 path_len = strlen(vmspath);
7338 if (vmspath[path_len - 1] == '/')
7340 vmspath[path_len] = ']';
7342 vmspath[path_len] = '\0';
7345 vmspath[vmspath_len] = 0;
7346 if (unixpath[unixlen - 1] == '/')
7348 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
7349 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7350 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7351 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7352 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
7353 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7354 rsa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7355 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7356 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7357 rms_bind_fab_nam(myfab, mynam);
7358 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7359 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7360 if (decc_efs_case_preserve)
7361 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7362 #ifdef NAML$M_OPEN_SPECIAL
7363 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7366 /* Set up the remaining naml fields */
7367 sts = sys$parse(&myfab);
7369 /* It failed! Try again as a UNIX filespec */
7378 /* get the Device ID and the FID */
7379 sts = sys$search(&myfab);
7381 /* These are no longer needed */
7386 /* on any failure, returned the POSIX ^UP^ filespec */
7391 specdsc.dsc$a_pointer = vmspath;
7392 specdsc.dsc$w_length = vmspath_len;
7394 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7395 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7396 sts = lib$fid_to_name
7397 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7399 /* on any failure, returned the POSIX ^UP^ filespec */
7401 /* This can happen if user does not have permission to read directories */
7402 if (strncmp(unixpath,"\"^UP^",5) != 0)
7403 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7405 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7408 vmspath[specdsc.dsc$w_length] = 0;
7410 /* Are we expecting a directory? */
7411 if (dir_flag != 0) {
7417 i = specdsc.dsc$w_length - 1;
7421 /* Version must be '1' */
7422 if (vmspath[i--] != '1')
7424 /* Version delimiter is one of ".;" */
7425 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7428 if (vmspath[i--] != 'R')
7430 if (vmspath[i--] != 'I')
7432 if (vmspath[i--] != 'D')
7434 if (vmspath[i--] != '.')
7436 eptr = &vmspath[i+1];
7438 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7439 if (vmspath[i-1] != '^') {
7447 /* Get rid of 6 imaginary zero directory filename */
7448 vmspath[i+1] = '\0';
7452 if (vmspath[i] == '0')
7466 /* /dev/mumble needs to be handled special.
7467 /dev/null becomes NLA0:, And there is the potential for other stuff
7468 like /dev/tty which may need to be mapped to something.
7472 slash_dev_special_to_vms
7473 (const char * unixptr,
7482 nextslash = strchr(unixptr, '/');
7483 len = strlen(unixptr);
7484 if (nextslash != NULL)
7485 len = nextslash - unixptr;
7486 cmp = strncmp("null", unixptr, 5);
7488 if (vmspath_len >= 6) {
7489 strcpy(vmspath, "_NLA0:");
7497 /* The built in routines do not understand perl's special needs, so
7498 doing a manual conversion from UNIX to VMS
7500 If the utf8_fl is not null and points to a non-zero value, then
7501 treat 8 bit characters as UTF-8.
7503 The sequence starting with '$(' and ending with ')' will be passed
7504 through with out interpretation instead of being escaped.
7507 static int posix_to_vmsspec_hardway
7508 (char *vmspath, int vmspath_len,
7509 const char *unixpath,
7514 const char *unixptr;
7515 const char *unixend;
7517 const char *lastslash;
7518 const char *lastdot;
7524 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7525 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7527 if (utf8_fl != NULL)
7533 /* Ignore leading "/" characters */
7534 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7537 unixlen = strlen(unixptr);
7539 /* Do nothing with blank paths */
7546 /* This could have a "^UP^ on the front */
7547 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7553 lastslash = strrchr(unixptr,'/');
7554 lastdot = strrchr(unixptr,'.');
7555 unixend = strrchr(unixptr,'\"');
7556 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7557 unixend = unixptr + unixlen;
7560 /* last dot is last dot or past end of string */
7561 if (lastdot == NULL)
7562 lastdot = unixptr + unixlen;
7564 /* if no directories, set last slash to beginning of string */
7565 if (lastslash == NULL) {
7566 lastslash = unixptr;
7569 /* Watch out for trailing "." after last slash, still a directory */
7570 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7571 lastslash = unixptr + unixlen;
7574 /* Watch out for trailing ".." after last slash, still a directory */
7575 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7576 lastslash = unixptr + unixlen;
7579 /* dots in directories are aways escaped */
7580 if (lastdot < lastslash)
7581 lastdot = unixptr + unixlen;
7584 /* if (unixptr < lastslash) then we are in a directory */
7591 /* Start with the UNIX path */
7592 if (*unixptr != '/') {
7593 /* relative paths */
7595 /* If allowing logical names on relative pathnames, then handle here */
7596 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7597 !decc_posix_compliant_pathnames) {
7603 /* Find the next slash */
7604 nextslash = strchr(unixptr,'/');
7606 esa = (char *)PerlMem_malloc(vmspath_len);
7607 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7609 trn = (char *)PerlMem_malloc(VMS_MAXRSS);
7610 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7612 if (nextslash != NULL) {
7614 seg_len = nextslash - unixptr;
7615 memcpy(esa, unixptr, seg_len);
7619 seg_len = my_strlcpy(esa, unixptr, sizeof(esa));
7621 /* trnlnm(section) */
7622 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7625 /* Now fix up the directory */
7627 /* Split up the path to find the components */
7628 sts = vms_split_path
7646 /* A logical name must be a directory or the full
7647 specification. It is only a full specification if
7648 it is the only component */
7649 if ((unixptr[seg_len] == '\0') ||
7650 (unixptr[seg_len+1] == '\0')) {
7652 /* Is a directory being required? */
7653 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7654 /* Not a logical name */
7659 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7660 /* This must be a directory */
7661 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7662 vmslen = my_strlcpy(vmsptr, esa, vmspath_len - 1);
7663 vmsptr[vmslen] = ':';
7665 vmsptr[vmslen] = '\0';
7673 /* must be dev/directory - ignore version */
7674 if ((n_len + e_len) != 0)
7677 /* transfer the volume */
7678 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7679 memcpy(vmsptr, v_spec, v_len);
7685 /* unroot the rooted directory */
7686 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7688 r_spec[r_len - 1] = ']';
7690 /* This should not be there, but nothing is perfect */
7692 cmp = strcmp(&r_spec[1], "000000.");
7702 memcpy(vmsptr, r_spec, r_len);
7708 /* Bring over the directory. */
7710 ((d_len + vmslen) < vmspath_len)) {
7712 d_spec[d_len - 1] = ']';
7714 cmp = strcmp(&d_spec[1], "000000.");
7725 /* Remove the redundant root */
7733 memcpy(vmsptr, d_spec, d_len);
7747 if (lastslash > unixptr) {
7750 /* skip leading ./ */
7752 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7758 /* Are we still in a directory? */
7759 if (unixptr <= lastslash) {
7764 /* if not backing up, then it is relative forward. */
7765 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7766 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7774 /* Perl wants an empty directory here to tell the difference
7775 * between a DCL command and a filename
7784 /* Handle two special files . and .. */
7785 if (unixptr[0] == '.') {
7786 if (&unixptr[1] == unixend) {
7793 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7804 else { /* Absolute PATH handling */
7808 /* Need to find out where root is */
7810 /* In theory, this procedure should never get an absolute POSIX pathname
7811 * that can not be found on the POSIX root.
7812 * In practice, that can not be relied on, and things will show up
7813 * here that are a VMS device name or concealed logical name instead.
7814 * So to make things work, this procedure must be tolerant.
7816 esa = (char *)PerlMem_malloc(vmspath_len);
7817 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7820 nextslash = strchr(&unixptr[1],'/');
7822 if (nextslash != NULL) {
7824 seg_len = nextslash - &unixptr[1];
7825 my_strlcpy(vmspath, unixptr, seg_len + 2);
7828 cmp = strncmp(vmspath, "dev", 4);
7830 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
7831 if (sts == SS$_NORMAL)
7835 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
7838 if ($VMS_STATUS_SUCCESS(sts)) {
7839 /* This is verified to be a real path */
7841 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
7842 if ($VMS_STATUS_SUCCESS(sts)) {
7843 vmslen = my_strlcpy(vmspath, esa, vmspath_len + 1);
7844 vmsptr = vmspath + vmslen;
7846 if (unixptr < lastslash) {
7855 cmp = strcmp(rptr,"000000.");
7860 } /* removing 6 zeros */
7861 } /* vmslen < 7, no 6 zeros possible */
7862 } /* Not in a directory */
7863 } /* Posix root found */
7865 /* No posix root, fall back to default directory */
7866 strcpy(vmspath, "SYS$DISK:[");
7867 vmsptr = &vmspath[10];
7869 if (unixptr > lastslash) {
7878 } /* end of verified real path handling */
7883 /* Ok, we have a device or a concealed root that is not in POSIX
7884 * or we have garbage. Make the best of it.
7887 /* Posix to VMS destroyed this, so copy it again */
7888 my_strlcpy(vmspath, &unixptr[1], seg_len + 1);
7889 vmslen = strlen(vmspath); /* We know we're truncating. */
7890 vmsptr = &vmsptr[vmslen];
7893 /* Now do we need to add the fake 6 zero directory to it? */
7895 if ((*lastslash == '/') && (nextslash < lastslash)) {
7896 /* No there is another directory */
7903 /* now we have foo:bar or foo:[000000]bar to decide from */
7904 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
7906 if (!islnm && !decc_posix_compliant_pathnames) {
7908 cmp = strncmp("bin", vmspath, 4);
7910 /* bin => SYS$SYSTEM: */
7911 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
7914 /* tmp => SYS$SCRATCH: */
7915 cmp = strncmp("tmp", vmspath, 4);
7917 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
7922 trnend = islnm ? islnm - 1 : 0;
7924 /* if this was a logical name, ']' or '>' must be present */
7925 /* if not a logical name, then assume a device and hope. */
7926 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
7928 /* if log name and trailing '.' then rooted - treat as device */
7929 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
7931 /* Fix me, if not a logical name, a device lookup should be
7932 * done to see if the device is file structured. If the device
7933 * is not file structured, the 6 zeros should not be put on.
7935 * As it is, perl is occasionally looking for dev:[000000]tty.
7936 * which looks a little strange.
7938 * Not that easy to detect as "/dev" may be file structured with
7939 * special device files.
7942 if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
7943 (&nextslash[1] == unixend)) {
7944 /* No real directory present */
7949 /* Put the device delimiter on */
7952 unixptr = nextslash;
7955 /* Start directory if needed */
7956 if (!islnm || add_6zero) {
7962 /* add fake 000000] if needed */
7975 } /* non-POSIX translation */
7977 } /* End of relative/absolute path handling */
7979 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
7986 if (dir_start != 0) {
7988 /* First characters in a directory are handled special */
7989 while ((*unixptr == '/') ||
7990 ((*unixptr == '.') &&
7991 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
7992 (&unixptr[1]==unixend)))) {
7997 /* Skip redundant / in specification */
7998 while ((*unixptr == '/') && (dir_start != 0)) {
8001 if (unixptr == lastslash)
8004 if (unixptr == lastslash)
8007 /* Skip redundant ./ characters */
8008 while ((*unixptr == '.') &&
8009 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8012 if (unixptr == lastslash)
8014 if (*unixptr == '/')
8017 if (unixptr == lastslash)
8020 /* Skip redundant ../ characters */
8021 while ((*unixptr == '.') && (unixptr[1] == '.') &&
8022 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8023 /* Set the backing up flag */
8029 unixptr++; /* first . */
8030 unixptr++; /* second . */
8031 if (unixptr == lastslash)
8033 if (*unixptr == '/') /* The slash */
8036 if (unixptr == lastslash)
8039 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8040 /* Not needed when VMS is pretending to be UNIX. */
8042 /* Is this loop stuck because of too many dots? */
8043 if (loop_flag == 0) {
8044 /* Exit the loop and pass the rest through */
8049 /* Are we done with directories yet? */
8050 if (unixptr >= lastslash) {
8052 /* Watch out for trailing dots */
8061 if (*unixptr == '/')
8065 /* Have we stopped backing up? */
8070 /* dir_start continues to be = 1 */
8072 if (*unixptr == '-') {
8074 *vmsptr++ = *unixptr++;
8078 /* Now are we done with directories yet? */
8079 if (unixptr >= lastslash) {
8081 /* Watch out for trailing dots */
8097 if (unixptr >= unixend)
8100 /* Normal characters - More EFS work probably needed */
8106 /* remove multiple / */
8107 while (unixptr[1] == '/') {
8110 if (unixptr == lastslash) {
8111 /* Watch out for trailing dots */
8123 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8124 /* Not needed when VMS is pretending to be UNIX. */
8128 if (unixptr != unixend)
8133 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8134 (&unixptr[1] == unixend)) {
8140 /* trailing dot ==> '^..' on VMS */
8141 if (unixptr == unixend) {
8149 *vmsptr++ = *unixptr++;
8153 if (quoted && (&unixptr[1] == unixend)) {
8157 in_cnt = copy_expand_unix_filename_escape
8158 (vmsptr, unixptr, &out_cnt, utf8_fl);
8168 in_cnt = copy_expand_unix_filename_escape
8169 (vmsptr, unixptr, &out_cnt, utf8_fl);
8176 /* Make sure directory is closed */
8177 if (unixptr == lastslash) {
8179 vmsptr2 = vmsptr - 1;
8181 if (*vmsptr2 != ']') {
8184 /* directories do not end in a dot bracket */
8185 if (*vmsptr2 == '.') {
8189 if (*vmsptr2 != '^') {
8190 vmsptr--; /* back up over the dot */
8198 /* Add a trailing dot if a file with no extension */
8199 vmsptr2 = vmsptr - 1;
8201 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8202 (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
8213 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8214 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8219 /* If a UTF8 flag is being passed, honor it */
8221 if (utf8_fl != NULL) {
8222 utf8_flag = *utf8_fl;
8227 /* If there is a possibility of UTF8, then if any UTF8 characters
8228 are present, then they must be converted to VTF-7
8230 result = strcpy(rslt, path); /* FIX-ME */
8233 result = strcpy(rslt, path);
8240 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8241 static char *int_tovmsspec
8242 (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8247 unsigned long int infront = 0, hasdir = 1;
8250 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8251 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8253 if (vms_debug_fileify) {
8255 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8257 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8261 /* If we fail, we should be setting errno */
8263 set_vaxc_errno(SS$_BADPARAM);
8266 rslt_len = VMS_MAXRSS-1;
8268 /* '.' and '..' are "[]" and "[-]" for a quick check */
8269 if (path[0] == '.') {
8270 if (path[1] == '\0') {
8272 if (utf8_flag != NULL)
8277 if (path[1] == '.' && path[2] == '\0') {
8279 if (utf8_flag != NULL)
8286 /* Posix specifications are now a native VMS format */
8287 /*--------------------------------------------------*/
8288 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8289 if (decc_posix_compliant_pathnames) {
8290 if (strncmp(path,"\"^UP^",5) == 0) {
8291 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8297 /* This is really the only way to see if this is already in VMS format */
8298 sts = vms_split_path
8313 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8314 replacement, because the above parse just took care of most of
8315 what is needed to do vmspath when the specification is already
8318 And if it is not already, it is easier to do the conversion as
8319 part of this routine than to call this routine and then work on
8323 /* If VMS punctuation was found, it is already VMS format */
8324 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8325 if (utf8_flag != NULL)
8327 my_strlcpy(rslt, path, VMS_MAXRSS);
8328 if (vms_debug_fileify) {
8329 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8333 /* Now, what to do with trailing "." cases where there is no
8334 extension? If this is a UNIX specification, and EFS characters
8335 are enabled, then the trailing "." should be converted to a "^.".
8336 But if this was already a VMS specification, then it should be
8339 So in the case of ambiguity, leave the specification alone.
8343 /* If there is a possibility of UTF8, then if any UTF8 characters
8344 are present, then they must be converted to VTF-7
8346 if (utf8_flag != NULL)
8348 my_strlcpy(rslt, path, VMS_MAXRSS);
8349 if (vms_debug_fileify) {
8350 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8355 dirend = strrchr(path,'/');
8357 if (dirend == NULL) {
8361 /* If we get here with no UNIX directory delimiters, then this is
8362 not a complete file specification, either garbage a UNIX glob
8363 specification that can not be converted to a VMS wildcard, or
8364 it a UNIX shell macro. MakeMaker wants shell macros passed
8367 utf8 flag setting needs to be preserved.
8372 macro_start = strchr(path,'$');
8373 if (macro_start != NULL) {
8374 if (macro_start[1] == '(') {
8378 if ((decc_efs_charset == 0) || (has_macro)) {
8379 my_strlcpy(rslt, path, VMS_MAXRSS);
8380 if (vms_debug_fileify) {
8381 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8386 else if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
8387 if (!*(dirend+2)) dirend +=2;
8388 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8389 if (decc_efs_charset == 0) {
8390 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8396 lastdot = strrchr(cp2,'.');
8402 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8404 if (decc_disable_posix_root) {
8405 strcpy(rslt,"sys$disk:[000000]");
8408 strcpy(rslt,"sys$posix_root:[000000]");
8410 if (utf8_flag != NULL)
8412 if (vms_debug_fileify) {
8413 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8417 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8419 trndev = (char *)PerlMem_malloc(VMS_MAXRSS);
8420 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8421 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8423 /* DECC special handling */
8425 if (strcmp(rslt,"bin") == 0) {
8426 strcpy(rslt,"sys$system");
8429 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8431 else if (strcmp(rslt,"tmp") == 0) {
8432 strcpy(rslt,"sys$scratch");
8435 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8437 else if (!decc_disable_posix_root) {
8438 strcpy(rslt, "sys$posix_root");
8442 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8443 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8445 else if (strcmp(rslt,"dev") == 0) {
8446 if (strncmp(cp2,"/null", 5) == 0) {
8447 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8448 strcpy(rslt,"NLA0");
8452 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8458 trnend = islnm ? strlen(trndev) - 1 : 0;
8459 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8460 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8461 /* If the first element of the path is a logical name, determine
8462 * whether it has to be translated so we can add more directories. */
8463 if (!islnm || rooted) {
8466 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8470 if (cp2 != dirend) {
8471 my_strlcpy(rslt, trndev, VMS_MAXRSS);
8472 cp1 = rslt + trnend;
8479 if (decc_disable_posix_root) {
8485 PerlMem_free(trndev);
8490 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8491 cp2 += 2; /* skip over "./" - it's redundant */
8492 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8494 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8495 *(cp1++) = '-'; /* "../" --> "-" */
8498 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8499 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8500 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8501 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8504 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8505 /* Escape the extra dots in EFS file specifications */
8508 if (cp2 > dirend) cp2 = dirend;
8510 else *(cp1++) = '.';
8512 for (; cp2 < dirend; cp2++) {
8514 if (*(cp2-1) == '/') continue;
8515 if (*(cp1-1) != '.') *(cp1++) = '.';
8518 else if (!infront && *cp2 == '.') {
8519 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8520 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
8521 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8522 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
8523 else if (*(cp1-2) == '[') *(cp1-1) = '-';
8528 if (cp2 == dirend) break;
8530 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8531 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8532 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8533 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8535 *(cp1++) = '.'; /* Simulate trailing '/' */
8536 cp2 += 2; /* for loop will incr this to == dirend */
8538 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8541 if (decc_efs_charset == 0)
8542 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8544 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
8550 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
8552 if (decc_efs_charset == 0)
8559 else *(cp1++) = *cp2;
8563 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8564 if (hasdir) *(cp1++) = ']';
8565 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
8566 /* fixme for ODS5 */
8573 if (decc_efs_charset == 0)
8584 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8585 decc_readdir_dropdotnotype) {
8590 /* trailing dot ==> '^..' on VMS */
8597 *(cp1++) = *(cp2++);
8602 /* This could be a macro to be passed through */
8603 *(cp1++) = *(cp2++);
8605 const char * save_cp2;
8609 /* paranoid check */
8615 *(cp1++) = *(cp2++);
8616 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8617 *(cp1++) = *(cp2++);
8618 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8619 *(cp1++) = *(cp2++);
8622 *(cp1++) = *(cp2++);
8626 if (is_macro == 0) {
8627 /* Not really a macro - never mind */
8640 /* Don't escape again if following character is
8641 * already something we escape.
8643 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8644 *(cp1++) = *(cp2++);
8647 /* But otherwise fall through and escape it. */
8665 *(cp1++) = *(cp2++);
8668 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
8669 * which is wrong. UNIX notation should be ".dir." unless
8670 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8671 * changing this behavior could break more things at this time.
8672 * efs character set effectively does not allow "." to be a version
8673 * delimiter as a further complication about changing this.
8675 if (decc_filename_unix_report != 0) {
8678 *(cp1++) = *(cp2++);
8681 *(cp1++) = *(cp2++);
8684 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8688 /* Fix me for "^]", but that requires making sure that you do
8689 * not back up past the start of the filename
8691 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8696 if (utf8_flag != NULL)
8698 if (vms_debug_fileify) {
8699 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8703 } /* end of int_tovmsspec() */
8706 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8707 static char *mp_do_tovmsspec
8708 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
8709 static char __tovmsspec_retbuf[VMS_MAXRSS];
8710 char * vmsspec, *ret_spec, *ret_buf;
8714 if (ret_buf == NULL) {
8716 Newx(vmsspec, VMS_MAXRSS, char);
8717 if (vmsspec == NULL)
8718 _ckvmssts(SS$_INSFMEM);
8721 ret_buf = __tovmsspec_retbuf;
8725 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8727 if (ret_spec == NULL) {
8728 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8735 } /* end of mp_do_tovmsspec() */
8737 /* External entry points */
8738 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8739 { return do_tovmsspec(path,buf,0,NULL); }
8740 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8741 { return do_tovmsspec(path,buf,1,NULL); }
8742 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8743 { return do_tovmsspec(path,buf,0,utf8_fl); }
8744 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8745 { return do_tovmsspec(path,buf,1,utf8_fl); }
8747 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
8748 /* Internal routine for use with out an explicit context present */
8749 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
8751 char * ret_spec, *pathified;
8756 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8757 if (pathified == NULL)
8758 _ckvmssts_noperl(SS$_INSFMEM);
8760 ret_spec = int_pathify_dirspec(path, pathified);
8762 if (ret_spec == NULL) {
8763 PerlMem_free(pathified);
8767 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
8769 PerlMem_free(pathified);
8774 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8775 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8776 static char __tovmspath_retbuf[VMS_MAXRSS];
8778 char *pathified, *vmsified, *cp;
8780 if (path == NULL) return NULL;
8781 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8782 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8783 if (int_pathify_dirspec(path, pathified) == NULL) {
8784 PerlMem_free(pathified);
8790 Newx(vmsified, VMS_MAXRSS, char);
8791 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8792 PerlMem_free(pathified);
8793 if (vmsified) Safefree(vmsified);
8796 PerlMem_free(pathified);
8801 vmslen = strlen(vmsified);
8802 Newx(cp,vmslen+1,char);
8803 memcpy(cp,vmsified,vmslen);
8809 my_strlcpy(__tovmspath_retbuf, vmsified, sizeof(__tovmspath_retbuf));
8811 return __tovmspath_retbuf;
8814 } /* end of do_tovmspath() */
8816 /* External entry points */
8817 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
8818 { return do_tovmspath(path,buf,0, NULL); }
8819 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
8820 { return do_tovmspath(path,buf,1, NULL); }
8821 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
8822 { return do_tovmspath(path,buf,0,utf8_fl); }
8823 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
8824 { return do_tovmspath(path,buf,1,utf8_fl); }
8827 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
8828 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8829 static char __tounixpath_retbuf[VMS_MAXRSS];
8831 char *pathified, *unixified, *cp;
8833 if (path == NULL) return NULL;
8834 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8835 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8836 if (int_pathify_dirspec(path, pathified) == NULL) {
8837 PerlMem_free(pathified);
8843 Newx(unixified, VMS_MAXRSS, char);
8845 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
8846 PerlMem_free(pathified);
8847 if (unixified) Safefree(unixified);
8850 PerlMem_free(pathified);
8855 unixlen = strlen(unixified);
8856 Newx(cp,unixlen+1,char);
8857 memcpy(cp,unixified,unixlen);
8859 Safefree(unixified);
8863 my_strlcpy(__tounixpath_retbuf, unixified, sizeof(__tounixpath_retbuf));
8864 Safefree(unixified);
8865 return __tounixpath_retbuf;
8868 } /* end of do_tounixpath() */
8870 /* External entry points */
8871 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
8872 { return do_tounixpath(path,buf,0,NULL); }
8873 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
8874 { return do_tounixpath(path,buf,1,NULL); }
8875 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8876 { return do_tounixpath(path,buf,0,utf8_fl); }
8877 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8878 { return do_tounixpath(path,buf,1,utf8_fl); }
8881 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
8883 *****************************************************************************
8885 * Copyright (C) 1989-1994, 2007 by *
8886 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
8888 * Permission is hereby granted for the reproduction of this software *
8889 * on condition that this copyright notice is included in source *
8890 * distributions of the software. The code may be modified and *
8891 * distributed under the same terms as Perl itself. *
8893 * 27-Aug-1994 Modified for inclusion in perl5 *
8894 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
8895 *****************************************************************************
8899 * getredirection() is intended to aid in porting C programs
8900 * to VMS (Vax-11 C). The native VMS environment does not support
8901 * '>' and '<' I/O redirection, or command line wild card expansion,
8902 * or a command line pipe mechanism using the '|' AND background
8903 * command execution '&'. All of these capabilities are provided to any
8904 * C program which calls this procedure as the first thing in the
8906 * The piping mechanism will probably work with almost any 'filter' type
8907 * of program. With suitable modification, it may useful for other
8908 * portability problems as well.
8910 * Author: Mark Pizzolato (mark AT infocomm DOT com)
8914 struct list_item *next;
8918 static void add_item(struct list_item **head,
8919 struct list_item **tail,
8923 static void mp_expand_wild_cards(pTHX_ char *item,
8924 struct list_item **head,
8925 struct list_item **tail,
8928 static int background_process(pTHX_ int argc, char **argv);
8930 static void pipe_and_fork(pTHX_ char **cmargv);
8932 /*{{{ void getredirection(int *ac, char ***av)*/
8934 mp_getredirection(pTHX_ int *ac, char ***av)
8936 * Process vms redirection arg's. Exit if any error is seen.
8937 * If getredirection() processes an argument, it is erased
8938 * from the vector. getredirection() returns a new argc and argv value.
8939 * In the event that a background command is requested (by a trailing "&"),
8940 * this routine creates a background subprocess, and simply exits the program.
8942 * Warning: do not try to simplify the code for vms. The code
8943 * presupposes that getredirection() is called before any data is
8944 * read from stdin or written to stdout.
8946 * Normal usage is as follows:
8952 * getredirection(&argc, &argv);
8956 int argc = *ac; /* Argument Count */
8957 char **argv = *av; /* Argument Vector */
8958 char *ap; /* Argument pointer */
8959 int j; /* argv[] index */
8960 int item_count = 0; /* Count of Items in List */
8961 struct list_item *list_head = 0; /* First Item in List */
8962 struct list_item *list_tail; /* Last Item in List */
8963 char *in = NULL; /* Input File Name */
8964 char *out = NULL; /* Output File Name */
8965 char *outmode = "w"; /* Mode to Open Output File */
8966 char *err = NULL; /* Error File Name */
8967 char *errmode = "w"; /* Mode to Open Error File */
8968 int cmargc = 0; /* Piped Command Arg Count */
8969 char **cmargv = NULL;/* Piped Command Arg Vector */
8972 * First handle the case where the last thing on the line ends with
8973 * a '&'. This indicates the desire for the command to be run in a
8974 * subprocess, so we satisfy that desire.
8977 if (0 == strcmp("&", ap))
8978 exit(background_process(aTHX_ --argc, argv));
8979 if (*ap && '&' == ap[strlen(ap)-1])
8981 ap[strlen(ap)-1] = '\0';
8982 exit(background_process(aTHX_ argc, argv));
8985 * Now we handle the general redirection cases that involve '>', '>>',
8986 * '<', and pipes '|'.
8988 for (j = 0; j < argc; ++j)
8990 if (0 == strcmp("<", argv[j]))
8994 fprintf(stderr,"No input file after < on command line");
8995 exit(LIB$_WRONUMARG);
9000 if ('<' == *(ap = argv[j]))
9005 if (0 == strcmp(">", ap))
9009 fprintf(stderr,"No output file after > on command line");
9010 exit(LIB$_WRONUMARG);
9029 fprintf(stderr,"No output file after > or >> on command line");
9030 exit(LIB$_WRONUMARG);
9034 if (('2' == *ap) && ('>' == ap[1]))
9051 fprintf(stderr,"No output file after 2> or 2>> on command line");
9052 exit(LIB$_WRONUMARG);
9056 if (0 == strcmp("|", argv[j]))
9060 fprintf(stderr,"No command into which to pipe on command line");
9061 exit(LIB$_WRONUMARG);
9063 cmargc = argc-(j+1);
9064 cmargv = &argv[j+1];
9068 if ('|' == *(ap = argv[j]))
9076 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9079 * Allocate and fill in the new argument vector, Some Unix's terminate
9080 * the list with an extra null pointer.
9082 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9083 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9085 for (j = 0; j < item_count; ++j, list_head = list_head->next)
9086 argv[j] = list_head->value;
9092 fprintf(stderr,"'|' and '>' may not both be specified on command line");
9093 exit(LIB$_INVARGORD);
9095 pipe_and_fork(aTHX_ cmargv);
9098 /* Check for input from a pipe (mailbox) */
9100 if (in == NULL && 1 == isapipe(0))
9102 char mbxname[L_tmpnam];
9104 long int dvi_item = DVI$_DEVBUFSIZ;
9105 $DESCRIPTOR(mbxnam, "");
9106 $DESCRIPTOR(mbxdevnam, "");
9108 /* Input from a pipe, reopen it in binary mode to disable */
9109 /* carriage control processing. */
9111 fgetname(stdin, mbxname, 1);
9112 mbxnam.dsc$a_pointer = mbxname;
9113 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9114 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9115 mbxdevnam.dsc$a_pointer = mbxname;
9116 mbxdevnam.dsc$w_length = sizeof(mbxname);
9117 dvi_item = DVI$_DEVNAM;
9118 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9119 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9122 freopen(mbxname, "rb", stdin);
9125 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9129 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9131 fprintf(stderr,"Can't open input file %s as stdin",in);
9134 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9136 fprintf(stderr,"Can't open output file %s as stdout",out);
9139 if (out != NULL) vmssetuserlnm("SYS$OUTPUT", out);
9142 if (strcmp(err,"&1") == 0) {
9143 dup2(fileno(stdout), fileno(stderr));
9144 vmssetuserlnm("SYS$ERROR", "SYS$OUTPUT");
9147 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9149 fprintf(stderr,"Can't open error file %s as stderr",err);
9153 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9157 vmssetuserlnm("SYS$ERROR", err);
9160 #ifdef ARGPROC_DEBUG
9161 PerlIO_printf(Perl_debug_log, "Arglist:\n");
9162 for (j = 0; j < *ac; ++j)
9163 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9165 /* Clear errors we may have hit expanding wildcards, so they don't
9166 show up in Perl's $! later */
9167 set_errno(0); set_vaxc_errno(1);
9168 } /* end of getredirection() */
9171 static void add_item(struct list_item **head,
9172 struct list_item **tail,
9178 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9179 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9183 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9184 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9185 *tail = (*tail)->next;
9187 (*tail)->value = value;
9191 static void mp_expand_wild_cards(pTHX_ char *item,
9192 struct list_item **head,
9193 struct list_item **tail,
9197 unsigned long int context = 0;
9205 $DESCRIPTOR(filespec, "");
9206 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9207 $DESCRIPTOR(resultspec, "");
9208 unsigned long int lff_flags = 0;
9212 #ifdef VMS_LONGNAME_SUPPORT
9213 lff_flags = LIB$M_FIL_LONG_NAMES;
9216 for (cp = item; *cp; cp++) {
9217 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9218 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9220 if (!*cp || isspace(*cp))
9222 add_item(head, tail, item, count);
9227 /* "double quoted" wild card expressions pass as is */
9228 /* From DCL that means using e.g.: */
9229 /* perl program """perl.*""" */
9230 item_len = strlen(item);
9231 if ( '"' == *item && '"' == item[item_len-1] )
9234 item[item_len-2] = '\0';
9235 add_item(head, tail, item, count);
9239 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9240 resultspec.dsc$b_class = DSC$K_CLASS_D;
9241 resultspec.dsc$a_pointer = NULL;
9242 vmsspec = (char *)PerlMem_malloc(VMS_MAXRSS);
9243 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9244 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9245 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9246 if (!isunix || !filespec.dsc$a_pointer)
9247 filespec.dsc$a_pointer = item;
9248 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9250 * Only return version specs, if the caller specified a version
9252 had_version = strchr(item, ';');
9254 * Only return device and directory specs, if the caller specified either.
9256 had_device = strchr(item, ':');
9257 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9259 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9260 (&filespec, &resultspec, &context,
9261 &defaultspec, 0, &rms_sts, &lff_flags)))
9266 string = (char *)PerlMem_malloc(resultspec.dsc$w_length+1);
9267 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9268 my_strlcpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length+1);
9269 if (NULL == had_version)
9270 *(strrchr(string, ';')) = '\0';
9271 if ((!had_directory) && (had_device == NULL))
9273 if (NULL == (devdir = strrchr(string, ']')))
9274 devdir = strrchr(string, '>');
9275 my_strlcpy(string, devdir + 1, resultspec.dsc$w_length+1);
9278 * Be consistent with what the C RTL has already done to the rest of
9279 * the argv items and lowercase all of these names.
9281 if (!decc_efs_case_preserve) {
9282 for (c = string; *c; ++c)
9286 if (isunix) trim_unixpath(string,item,1);
9287 add_item(head, tail, string, count);
9290 PerlMem_free(vmsspec);
9291 if (sts != RMS$_NMF)
9293 set_vaxc_errno(sts);
9296 case RMS$_FNF: case RMS$_DNF:
9297 set_errno(ENOENT); break;
9299 set_errno(ENOTDIR); break;
9301 set_errno(ENODEV); break;
9302 case RMS$_FNM: case RMS$_SYN:
9303 set_errno(EINVAL); break;
9305 set_errno(EACCES); break;
9307 _ckvmssts_noperl(sts);
9311 add_item(head, tail, item, count);
9312 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9313 _ckvmssts_noperl(lib$find_file_end(&context));
9316 static int child_st[2];/* Event Flag set when child process completes */
9318 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
9320 static unsigned long int exit_handler(void)
9324 if (0 == child_st[0])
9326 #ifdef ARGPROC_DEBUG
9327 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
9329 fflush(stdout); /* Have to flush pipe for binary data to */
9330 /* terminate properly -- <tp@mccall.com> */
9331 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9332 sys$dassgn(child_chan);
9334 sys$synch(0, child_st);
9339 static void sig_child(int chan)
9341 #ifdef ARGPROC_DEBUG
9342 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
9344 if (child_st[0] == 0)
9348 static struct exit_control_block exit_block =
9353 &exit_block.exit_status,
9358 pipe_and_fork(pTHX_ char **cmargv)
9361 struct dsc$descriptor_s *vmscmd;
9362 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9363 int sts, j, l, ismcr, quote, tquote = 0;
9365 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
9366 vms_execfree(vmscmd);
9371 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
9372 && toupper(*(q+2)) == 'R' && !*(q+3);
9374 while (q && l < MAX_DCL_LINE_LENGTH) {
9376 if (j > 0 && quote) {
9382 if (ismcr && j > 1) quote = 1;
9383 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9386 if (quote || tquote) {
9392 if ((quote||tquote) && *q == '"') {
9402 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9404 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9408 static int background_process(pTHX_ int argc, char **argv)
9410 char command[MAX_DCL_SYMBOL + 1] = "$";
9411 $DESCRIPTOR(value, "");
9412 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9413 static $DESCRIPTOR(null, "NLA0:");
9414 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9416 $DESCRIPTOR(pidstr, "");
9418 unsigned long int flags = 17, one = 1, retsts;
9421 len = my_strlcat(command, argv[0], sizeof(command));
9422 while (--argc && (len < MAX_DCL_SYMBOL))
9424 my_strlcat(command, " \"", sizeof(command));
9425 my_strlcat(command, *(++argv), sizeof(command));
9426 len = my_strlcat(command, "\"", sizeof(command));
9428 value.dsc$a_pointer = command;
9429 value.dsc$w_length = strlen(value.dsc$a_pointer);
9430 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9431 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9432 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9433 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9436 _ckvmssts_noperl(retsts);
9438 #ifdef ARGPROC_DEBUG
9439 PerlIO_printf(Perl_debug_log, "%s\n", command);
9441 sprintf(pidstring, "%08X", pid);
9442 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9443 pidstr.dsc$a_pointer = pidstring;
9444 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9445 lib$set_symbol(&pidsymbol, &pidstr);
9449 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9452 /* OS-specific initialization at image activation (not thread startup) */
9453 /* Older VAXC header files lack these constants */
9454 #ifndef JPI$_RIGHTS_SIZE
9455 # define JPI$_RIGHTS_SIZE 817
9457 #ifndef KGB$M_SUBSYSTEM
9458 # define KGB$M_SUBSYSTEM 0x8
9461 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9463 /*{{{void vms_image_init(int *, char ***)*/
9465 vms_image_init(int *argcp, char ***argvp)
9468 char eqv[LNM$C_NAMLENGTH+1] = "";
9469 unsigned int len, tabct = 8, tabidx = 0;
9470 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9471 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9472 unsigned short int dummy, rlen;
9473 struct dsc$descriptor_s **tabvec;
9474 #if defined(PERL_IMPLICIT_CONTEXT)
9477 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9478 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9479 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9482 #ifdef KILL_BY_SIGPRC
9483 Perl_csighandler_init();
9486 #if __CRTL_VER >= 70300000 && !defined(__VAX)
9487 /* This was moved from the pre-image init handler because on threaded */
9488 /* Perl it was always returning 0 for the default value. */
9489 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9492 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9495 initial = decc$feature_get_value(s, 4);
9497 /* initial is: 0 if nothing has set the feature */
9498 /* -1 if initialized to default */
9499 /* 1 if set by logical name */
9500 /* 2 if set by decc$feature_set_value */
9501 decc_disable_posix_root = decc$feature_get_value(s, 1);
9503 /* If the value is not valid, force the feature off */
9504 if (decc_disable_posix_root < 0) {
9505 decc$feature_set_value(s, 1, 1);
9506 decc_disable_posix_root = 1;
9510 /* Nothing has asked for it explicitly, so use our own default. */
9511 decc_disable_posix_root = 1;
9512 decc$feature_set_value(s, 1, 1);
9518 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9519 _ckvmssts_noperl(iosb[0]);
9520 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9521 if (iprv[i]) { /* Running image installed with privs? */
9522 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
9527 /* Rights identifiers might trigger tainting as well. */
9528 if (!will_taint && (rlen || rsz)) {
9529 while (rlen < rsz) {
9530 /* We didn't get all the identifiers on the first pass. Allocate a
9531 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9532 * were needed to hold all identifiers at time of last call; we'll
9533 * allocate that many unsigned long ints), and go back and get 'em.
9534 * If it gave us less than it wanted to despite ample buffer space,
9535 * something's broken. Is your system missing a system identifier?
9537 if (rsz <= jpilist[1].buflen) {
9538 /* Perl_croak accvios when used this early in startup. */
9539 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9540 rsz, (unsigned long) jpilist[1].buflen,
9541 "Check your rights database for corruption.\n");
9544 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9545 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9546 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9547 jpilist[1].buflen = rsz * sizeof(unsigned long int);
9548 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9549 _ckvmssts_noperl(iosb[0]);
9551 mask = (unsigned long int *)jpilist[1].bufadr;
9552 /* Check attribute flags for each identifier (2nd longword); protected
9553 * subsystem identifiers trigger tainting.
9555 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9556 if (mask[i] & KGB$M_SUBSYSTEM) {
9561 if (mask != rlst) PerlMem_free(mask);
9564 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9565 * logical, some versions of the CRTL will add a phanthom /000000/
9566 * directory. This needs to be removed.
9568 if (decc_filename_unix_report) {
9571 ulen = strlen(argvp[0][0]);
9573 zeros = strstr(argvp[0][0], "/000000/");
9574 if (zeros != NULL) {
9576 mlen = ulen - (zeros - argvp[0][0]) - 7;
9577 memmove(zeros, &zeros[7], mlen);
9579 argvp[0][0][ulen] = '\0';
9582 /* It also may have a trailing dot that needs to be removed otherwise
9583 * it will be converted to VMS mode incorrectly.
9586 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9587 argvp[0][0][ulen] = '\0';
9590 /* We need to use this hack to tell Perl it should run with tainting,
9591 * since its tainting flag may be part of the PL_curinterp struct, which
9592 * hasn't been allocated when vms_image_init() is called.
9595 char **newargv, **oldargv;
9597 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9598 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9599 newargv[0] = oldargv[0];
9600 newargv[1] = (char *)PerlMem_malloc(3 * sizeof(char));
9601 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9602 strcpy(newargv[1], "-T");
9603 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9605 newargv[*argcp] = NULL;
9606 /* We orphan the old argv, since we don't know where it's come from,
9607 * so we don't know how to free it.
9611 else { /* Did user explicitly request tainting? */
9613 char *cp, **av = *argvp;
9614 for (i = 1; i < *argcp; i++) {
9615 if (*av[i] != '-') break;
9616 for (cp = av[i]+1; *cp; cp++) {
9617 if (*cp == 'T') { will_taint = 1; break; }
9618 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9619 strchr("DFIiMmx",*cp)) break;
9621 if (will_taint) break;
9626 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9629 tabvec = (struct dsc$descriptor_s **)
9630 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9631 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9633 else if (tabidx >= tabct) {
9635 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9636 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9638 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9639 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9640 tabvec[tabidx]->dsc$w_length = 0;
9641 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9642 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
9643 tabvec[tabidx]->dsc$a_pointer = NULL;
9644 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
9646 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9648 getredirection(argcp,argvp);
9649 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9651 # include <reentrancy.h>
9652 decc$set_reentrancy(C$C_MULTITHREAD);
9661 * Trim Unix-style prefix off filespec, so it looks like what a shell
9662 * glob expansion would return (i.e. from specified prefix on, not
9663 * full path). Note that returned filespec is Unix-style, regardless
9664 * of whether input filespec was VMS-style or Unix-style.
9666 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9667 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9668 * vector of options; at present, only bit 0 is used, and if set tells
9669 * trim unixpath to try the current default directory as a prefix when
9670 * presented with a possibly ambiguous ... wildcard.
9672 * Returns !=0 on success, with trimmed filespec replacing contents of
9673 * fspec, and 0 on failure, with contents of fpsec unchanged.
9675 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9677 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9679 char *unixified, *unixwild, *tplate, *base, *end, *cp1, *cp2;
9680 int tmplen, reslen = 0, dirs = 0;
9682 if (!wildspec || !fspec) return 0;
9684 unixwild = (char *)PerlMem_malloc(VMS_MAXRSS);
9685 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9687 if (strpbrk(wildspec,"]>:") != NULL) {
9688 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
9689 PerlMem_free(unixwild);
9694 my_strlcpy(unixwild, wildspec, VMS_MAXRSS);
9696 unixified = (char *)PerlMem_malloc(VMS_MAXRSS);
9697 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9698 if (strpbrk(fspec,"]>:") != NULL) {
9699 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
9700 PerlMem_free(unixwild);
9701 PerlMem_free(unixified);
9704 else base = unixified;
9705 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9706 * check to see that final result fits into (isn't longer than) fspec */
9707 reslen = strlen(fspec);
9711 /* No prefix or absolute path on wildcard, so nothing to remove */
9712 if (!*tplate || *tplate == '/') {
9713 PerlMem_free(unixwild);
9714 if (base == fspec) {
9715 PerlMem_free(unixified);
9718 tmplen = strlen(unixified);
9719 if (tmplen > reslen) {
9720 PerlMem_free(unixified);
9721 return 0; /* not enough space */
9723 /* Copy unixified resultant, including trailing NUL */
9724 memmove(fspec,unixified,tmplen+1);
9725 PerlMem_free(unixified);
9729 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9730 if ((cp1 = strstr(tplate,".../")) == NULL) { /* No ...; just count elts */
9731 for (cp1 = tplate; *cp1; cp1++) if (*cp1 == '/') dirs++;
9732 for (cp1 = end ;cp1 >= base; cp1--)
9733 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9735 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9736 PerlMem_free(unixified);
9737 PerlMem_free(unixwild);
9742 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9743 int ells = 1, totells, segdirs, match;
9744 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9745 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9747 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9749 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9750 tpl = (char *)PerlMem_malloc(VMS_MAXRSS);
9751 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9752 if (ellipsis == tplate && opts & 1) {
9753 /* Template begins with an ellipsis. Since we can't tell how many
9754 * directory names at the front of the resultant to keep for an
9755 * arbitrary starting point, we arbitrarily choose the current
9756 * default directory as a starting point. If it's there as a prefix,
9757 * clip it off. If not, fall through and act as if the leading
9758 * ellipsis weren't there (i.e. return shortest possible path that
9759 * could match template).
9761 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9763 PerlMem_free(unixified);
9764 PerlMem_free(unixwild);
9767 if (!decc_efs_case_preserve) {
9768 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9769 if (_tolower(*cp1) != _tolower(*cp2)) break;
9771 segdirs = dirs - totells; /* Min # of dirs we must have left */
9772 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9773 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9774 memmove(fspec,cp2+1,end - cp2);
9776 PerlMem_free(unixified);
9777 PerlMem_free(unixwild);
9781 /* First off, back up over constant elements at end of path */
9783 for (front = end ; front >= base; front--)
9784 if (*front == '/' && !dirs--) { front++; break; }
9786 lcres = (char *)PerlMem_malloc(VMS_MAXRSS);
9787 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9788 for (cp1=tplate,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9790 if (!decc_efs_case_preserve) {
9791 *cp2 = _tolower(*cp1); /* Make lc copy for match */
9799 PerlMem_free(unixified);
9800 PerlMem_free(unixwild);
9801 PerlMem_free(lcres);
9802 return 0; /* Path too long. */
9805 *cp2 = '\0'; /* Pick up with memcpy later */
9806 lcfront = lcres + (front - base);
9807 /* Now skip over each ellipsis and try to match the path in front of it. */
9809 for (cp1 = ellipsis - 2; cp1 >= tplate; cp1--)
9810 if (*(cp1) == '.' && *(cp1+1) == '.' &&
9811 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
9812 if (cp1 < tplate) break; /* template started with an ellipsis */
9813 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9814 ellipsis = cp1; continue;
9816 wilddsc.dsc$a_pointer = tpl;
9817 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9819 for (segdirs = 0, cp2 = tpl;
9820 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
9822 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
9824 if (!decc_efs_case_preserve) {
9825 *cp2 = _tolower(*cp1); /* else lowercase for match */
9828 *cp2 = *cp1; /* else preserve case for match */
9831 if (*cp2 == '/') segdirs++;
9833 if (cp1 != ellipsis - 1) {
9835 PerlMem_free(unixified);
9836 PerlMem_free(unixwild);
9837 PerlMem_free(lcres);
9838 return 0; /* Path too long */
9840 /* Back up at least as many dirs as in template before matching */
9841 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
9842 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
9843 for (match = 0; cp1 > lcres;) {
9844 resdsc.dsc$a_pointer = cp1;
9845 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
9847 if (match == 1) lcfront = cp1;
9849 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
9853 PerlMem_free(unixified);
9854 PerlMem_free(unixwild);
9855 PerlMem_free(lcres);
9856 return 0; /* Can't find prefix ??? */
9858 if (match > 1 && opts & 1) {
9859 /* This ... wildcard could cover more than one set of dirs (i.e.
9860 * a set of similar dir names is repeated). If the template
9861 * contains more than 1 ..., upstream elements could resolve the
9862 * ambiguity, but it's not worth a full backtracking setup here.
9863 * As a quick heuristic, clip off the current default directory
9864 * if it's present to find the trimmed spec, else use the
9865 * shortest string that this ... could cover.
9867 char def[NAM$C_MAXRSS+1], *st;
9869 if (getcwd(def, sizeof def,0) == NULL) {
9870 PerlMem_free(unixified);
9871 PerlMem_free(unixwild);
9872 PerlMem_free(lcres);
9876 if (!decc_efs_case_preserve) {
9877 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9878 if (_tolower(*cp1) != _tolower(*cp2)) break;
9880 segdirs = dirs - totells; /* Min # of dirs we must have left */
9881 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
9882 if (*cp1 == '\0' && *cp2 == '/') {
9883 memmove(fspec,cp2+1,end - cp2);
9885 PerlMem_free(unixified);
9886 PerlMem_free(unixwild);
9887 PerlMem_free(lcres);
9890 /* Nope -- stick with lcfront from above and keep going. */
9893 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
9895 PerlMem_free(unixified);
9896 PerlMem_free(unixwild);
9897 PerlMem_free(lcres);
9901 } /* end of trim_unixpath() */
9906 * VMS readdir() routines.
9907 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
9909 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
9910 * Minor modifications to original routines.
9913 /* readdir may have been redefined by reentr.h, so make sure we get
9914 * the local version for what we do here.
9919 #if !defined(PERL_IMPLICIT_CONTEXT)
9920 # define readdir Perl_readdir
9922 # define readdir(a) Perl_readdir(aTHX_ a)
9925 /* Number of elements in vms_versions array */
9926 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
9929 * Open a directory, return a handle for later use.
9931 /*{{{ DIR *opendir(char*name) */
9933 Perl_opendir(pTHX_ const char *name)
9939 Newx(dir, VMS_MAXRSS, char);
9940 if (int_tovmspath(name, dir, NULL) == NULL) {
9944 /* Check access before stat; otherwise stat does not
9945 * accurately report whether it's a directory.
9947 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
9948 /* cando_by_name has already set errno */
9952 if (flex_stat(dir,&sb) == -1) return NULL;
9953 if (!S_ISDIR(sb.st_mode)) {
9955 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
9958 /* Get memory for the handle, and the pattern. */
9960 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
9962 /* Fill in the fields; mainly playing with the descriptor. */
9963 sprintf(dd->pattern, "%s*.*",dir);
9968 /* By saying we always want the result of readdir() in unix format, we
9969 * are really saying we want all the escapes removed. Otherwise the caller,
9970 * having no way to know whether it's already in VMS format, might send it
9971 * through tovmsspec again, thus double escaping.
9973 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
9974 dd->pat.dsc$a_pointer = dd->pattern;
9975 dd->pat.dsc$w_length = strlen(dd->pattern);
9976 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
9977 dd->pat.dsc$b_class = DSC$K_CLASS_S;
9978 #if defined(USE_ITHREADS)
9979 Newx(dd->mutex,1,perl_mutex);
9980 MUTEX_INIT( (perl_mutex *) dd->mutex );
9986 } /* end of opendir() */
9990 * Set the flag to indicate we want versions or not.
9992 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
9994 vmsreaddirversions(DIR *dd, int flag)
9997 dd->flags |= PERL_VMSDIR_M_VERSIONS;
9999 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10004 * Free up an opened directory.
10006 /*{{{ void closedir(DIR *dd)*/
10008 Perl_closedir(DIR *dd)
10012 sts = lib$find_file_end(&dd->context);
10013 Safefree(dd->pattern);
10014 #if defined(USE_ITHREADS)
10015 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10016 Safefree(dd->mutex);
10023 * Collect all the version numbers for the current file.
10026 collectversions(pTHX_ DIR *dd)
10028 struct dsc$descriptor_s pat;
10029 struct dsc$descriptor_s res;
10031 char *p, *text, *buff;
10033 unsigned long context, tmpsts;
10035 /* Convenient shorthand. */
10038 /* Add the version wildcard, ignoring the "*.*" put on before */
10039 i = strlen(dd->pattern);
10040 Newx(text,i + e->d_namlen + 3,char);
10041 my_strlcpy(text, dd->pattern, i + 1);
10042 sprintf(&text[i - 3], "%s;*", e->d_name);
10044 /* Set up the pattern descriptor. */
10045 pat.dsc$a_pointer = text;
10046 pat.dsc$w_length = i + e->d_namlen - 1;
10047 pat.dsc$b_dtype = DSC$K_DTYPE_T;
10048 pat.dsc$b_class = DSC$K_CLASS_S;
10050 /* Set up result descriptor. */
10051 Newx(buff, VMS_MAXRSS, char);
10052 res.dsc$a_pointer = buff;
10053 res.dsc$w_length = VMS_MAXRSS - 1;
10054 res.dsc$b_dtype = DSC$K_DTYPE_T;
10055 res.dsc$b_class = DSC$K_CLASS_S;
10057 /* Read files, collecting versions. */
10058 for (context = 0, e->vms_verscount = 0;
10059 e->vms_verscount < VERSIZE(e);
10060 e->vms_verscount++) {
10061 unsigned long rsts;
10062 unsigned long flags = 0;
10064 #ifdef VMS_LONGNAME_SUPPORT
10065 flags = LIB$M_FIL_LONG_NAMES;
10067 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10068 if (tmpsts == RMS$_NMF || context == 0) break;
10070 buff[VMS_MAXRSS - 1] = '\0';
10071 if ((p = strchr(buff, ';')))
10072 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10074 e->vms_versions[e->vms_verscount] = -1;
10077 _ckvmssts(lib$find_file_end(&context));
10081 } /* end of collectversions() */
10084 * Read the next entry from the directory.
10086 /*{{{ struct dirent *readdir(DIR *dd)*/
10088 Perl_readdir(pTHX_ DIR *dd)
10090 struct dsc$descriptor_s res;
10092 unsigned long int tmpsts;
10093 unsigned long rsts;
10094 unsigned long flags = 0;
10095 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10096 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10098 /* Set up result descriptor, and get next file. */
10099 Newx(buff, VMS_MAXRSS, char);
10100 res.dsc$a_pointer = buff;
10101 res.dsc$w_length = VMS_MAXRSS - 1;
10102 res.dsc$b_dtype = DSC$K_DTYPE_T;
10103 res.dsc$b_class = DSC$K_CLASS_S;
10105 #ifdef VMS_LONGNAME_SUPPORT
10106 flags = LIB$M_FIL_LONG_NAMES;
10109 tmpsts = lib$find_file
10110 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10111 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
10112 if (!(tmpsts & 1)) {
10113 set_vaxc_errno(tmpsts);
10116 set_errno(EACCES); break;
10118 set_errno(ENODEV); break;
10120 set_errno(ENOTDIR); break;
10121 case RMS$_FNF: case RMS$_DNF:
10122 set_errno(ENOENT); break;
10124 set_errno(EVMSERR);
10130 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10131 buff[res.dsc$w_length] = '\0';
10132 p = buff + res.dsc$w_length;
10133 while (--p >= buff) if (!isspace(*p)) break;
10135 if (!decc_efs_case_preserve) {
10136 for (p = buff; *p; p++) *p = _tolower(*p);
10139 /* Skip any directory component and just copy the name. */
10140 sts = vms_split_path
10155 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10157 /* In Unix report mode, remove the ".dir;1" from the name */
10158 /* if it is a real directory. */
10159 if (decc_filename_unix_report || decc_efs_charset) {
10160 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10164 ret_sts = flex_lstat(buff, &statbuf);
10165 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10172 /* Drop NULL extensions on UNIX file specification */
10173 if ((e_len == 1) && decc_readdir_dropdotnotype) {
10179 memcpy(dd->entry.d_name, n_spec, n_len + e_len);
10180 dd->entry.d_name[n_len + e_len] = '\0';
10181 dd->entry.d_namlen = strlen(dd->entry.d_name);
10183 /* Convert the filename to UNIX format if needed */
10184 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10186 /* Translate the encoded characters. */
10187 /* Fixme: Unicode handling could result in embedded 0 characters */
10188 if (strchr(dd->entry.d_name, '^') != NULL) {
10189 char new_name[256];
10191 p = dd->entry.d_name;
10194 int inchars_read, outchars_added;
10195 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10197 q += outchars_added;
10199 /* if outchars_added > 1, then this is a wide file specification */
10200 /* Wide file specifications need to be passed in Perl */
10201 /* counted strings apparently with a Unicode flag */
10204 dd->entry.d_namlen = my_strlcpy(dd->entry.d_name, new_name, sizeof(dd->entry.d_name));
10208 dd->entry.vms_verscount = 0;
10209 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10213 } /* end of readdir() */
10217 * Read the next entry from the directory -- thread-safe version.
10219 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10221 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10225 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10227 entry = readdir(dd);
10229 retval = ( *result == NULL ? errno : 0 );
10231 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10235 } /* end of readdir_r() */
10239 * Return something that can be used in a seekdir later.
10241 /*{{{ long telldir(DIR *dd)*/
10243 Perl_telldir(DIR *dd)
10250 * Return to a spot where we used to be. Brute force.
10252 /*{{{ void seekdir(DIR *dd,long count)*/
10254 Perl_seekdir(pTHX_ DIR *dd, long count)
10258 /* If we haven't done anything yet... */
10259 if (dd->count == 0)
10262 /* Remember some state, and clear it. */
10263 old_flags = dd->flags;
10264 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10265 _ckvmssts(lib$find_file_end(&dd->context));
10268 /* The increment is in readdir(). */
10269 for (dd->count = 0; dd->count < count; )
10272 dd->flags = old_flags;
10274 } /* end of seekdir() */
10277 /* VMS subprocess management
10279 * my_vfork() - just a vfork(), after setting a flag to record that
10280 * the current script is trying a Unix-style fork/exec.
10282 * vms_do_aexec() and vms_do_exec() are called in response to the
10283 * perl 'exec' function. If this follows a vfork call, then they
10284 * call out the regular perl routines in doio.c which do an
10285 * execvp (for those who really want to try this under VMS).
10286 * Otherwise, they do exactly what the perl docs say exec should
10287 * do - terminate the current script and invoke a new command
10288 * (See below for notes on command syntax.)
10290 * do_aspawn() and do_spawn() implement the VMS side of the perl
10291 * 'system' function.
10293 * Note on command arguments to perl 'exec' and 'system': When handled
10294 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10295 * are concatenated to form a DCL command string. If the first non-numeric
10296 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10297 * the command string is handed off to DCL directly. Otherwise,
10298 * the first token of the command is taken as the filespec of an image
10299 * to run. The filespec is expanded using a default type of '.EXE' and
10300 * the process defaults for device, directory, etc., and if found, the resultant
10301 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10302 * the command string as parameters. This is perhaps a bit complicated,
10303 * but I hope it will form a happy medium between what VMS folks expect
10304 * from lib$spawn and what Unix folks expect from exec.
10307 static int vfork_called;
10309 /*{{{int my_vfork(void)*/
10320 vms_execfree(struct dsc$descriptor_s *vmscmd)
10323 if (vmscmd->dsc$a_pointer) {
10324 PerlMem_free(vmscmd->dsc$a_pointer);
10326 PerlMem_free(vmscmd);
10331 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10333 char *junk, *tmps = NULL;
10341 tmps = SvPV(really,rlen);
10343 cmdlen += rlen + 1;
10348 for (idx++; idx <= sp; idx++) {
10350 junk = SvPVx(*idx,rlen);
10351 cmdlen += rlen ? rlen + 1 : 0;
10354 Newx(PL_Cmd, cmdlen+1, char);
10356 if (tmps && *tmps) {
10357 my_strlcpy(PL_Cmd, tmps, cmdlen + 1);
10360 else *PL_Cmd = '\0';
10361 while (++mark <= sp) {
10363 char *s = SvPVx(*mark,n_a);
10365 if (*PL_Cmd) my_strlcat(PL_Cmd, " ", cmdlen+1);
10366 my_strlcat(PL_Cmd, s, cmdlen+1);
10371 } /* end of setup_argstr() */
10374 static unsigned long int
10375 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10376 struct dsc$descriptor_s **pvmscmd)
10380 char image_name[NAM$C_MAXRSS+1];
10381 char image_argv[NAM$C_MAXRSS+1];
10382 $DESCRIPTOR(defdsc,".EXE");
10383 $DESCRIPTOR(defdsc2,".");
10384 struct dsc$descriptor_s resdsc;
10385 struct dsc$descriptor_s *vmscmd;
10386 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10387 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10388 char *s, *rest, *cp, *wordbreak;
10393 vmscmd = (struct dsc$descriptor_s *)PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10394 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10396 /* vmsspec is a DCL command buffer, not just a filename */
10397 vmsspec = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10398 if (vmsspec == NULL)
10399 _ckvmssts_noperl(SS$_INSFMEM);
10401 resspec = (char *)PerlMem_malloc(VMS_MAXRSS);
10402 if (resspec == NULL)
10403 _ckvmssts_noperl(SS$_INSFMEM);
10405 /* Make a copy for modification */
10406 cmdlen = strlen(incmd);
10407 cmd = (char *)PerlMem_malloc(cmdlen+1);
10408 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10409 my_strlcpy(cmd, incmd, cmdlen + 1);
10413 resdsc.dsc$a_pointer = resspec;
10414 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10415 resdsc.dsc$b_class = DSC$K_CLASS_S;
10416 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10418 vmscmd->dsc$a_pointer = NULL;
10419 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10420 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10421 vmscmd->dsc$w_length = 0;
10422 if (pvmscmd) *pvmscmd = vmscmd;
10424 if (suggest_quote) *suggest_quote = 0;
10426 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10428 PerlMem_free(vmsspec);
10429 PerlMem_free(resspec);
10430 return CLI$_BUFOVF; /* continuation lines currently unsupported */
10435 while (*s && isspace(*s)) s++;
10437 if (*s == '@' || *s == '$') {
10438 vmsspec[0] = *s; rest = s + 1;
10439 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10441 else { cp = vmsspec; rest = s; }
10443 /* If the first word is quoted, then we need to unquote it and
10444 * escape spaces within it. We'll expand into the resspec buffer,
10445 * then copy back into the cmd buffer, expanding the latter if
10448 if (*rest == '"') {
10453 int soff = s - cmd;
10455 for (cp2 = resspec;
10456 *rest && cp2 - resspec < (VMS_MAXRSS - 1);
10459 if (*rest == ' ') { /* Escape ' ' to '^_'. */
10465 else if (*rest == '"') {
10467 if (in_quote) { /* Must be closing quote. */
10480 /* Expand the command buffer if necessary. */
10481 if (clen > cmdlen) {
10482 cmd = (char *)PerlMem_realloc(cmd, clen);
10484 _ckvmssts_noperl(SS$_INSFMEM);
10485 /* Where we are may have changed, so recompute offsets */
10486 r = cmd + (r - s - soff);
10487 rest = cmd + (rest - s - soff);
10491 /* Shift the non-verb portion of the command (if any) up or
10492 * down as necessary.
10495 memmove(rest + clen - cmdlen, rest, s - soff + cmdlen - rest);
10497 /* Copy the unquoted and escaped command verb into place. */
10498 memcpy(r, resspec, cp2 - resspec);
10501 rest = r; /* Rewind for subsequent operations. */
10504 if (*rest == '.' || *rest == '/') {
10506 for (cp2 = resspec;
10507 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10508 rest++, cp2++) *cp2 = *rest;
10510 if (int_tovmsspec(resspec, cp, 0, NULL)) {
10513 /* When a UNIX spec with no file type is translated to VMS, */
10514 /* A trailing '.' is appended under ODS-5 rules. */
10515 /* Here we do not want that trailing "." as it prevents */
10516 /* Looking for a implied ".exe" type. */
10517 if (decc_efs_charset) {
10519 i = strlen(vmsspec);
10520 if (vmsspec[i-1] == '.') {
10521 vmsspec[i-1] = '\0';
10526 for (cp2 = vmsspec + strlen(vmsspec);
10527 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10528 rest++, cp2++) *cp2 = *rest;
10533 /* Intuit whether verb (first word of cmd) is a DCL command:
10534 * - if first nonspace char is '@', it's a DCL indirection
10536 * - if verb contains a filespec separator, it's not a DCL command
10537 * - if it doesn't, caller tells us whether to default to a DCL
10538 * command, or to a local image unless told it's DCL (by leading '$')
10542 if (suggest_quote) *suggest_quote = 1;
10544 char *filespec = strpbrk(s,":<[.;");
10545 rest = wordbreak = strpbrk(s," \"\t/");
10546 if (!wordbreak) wordbreak = s + strlen(s);
10547 if (*s == '$') check_img = 0;
10548 if (filespec && (filespec < wordbreak)) isdcl = 0;
10549 else isdcl = !check_img;
10554 imgdsc.dsc$a_pointer = s;
10555 imgdsc.dsc$w_length = wordbreak - s;
10556 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10558 _ckvmssts_noperl(lib$find_file_end(&cxt));
10559 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10560 if (!(retsts & 1) && *s == '$') {
10561 _ckvmssts_noperl(lib$find_file_end(&cxt));
10562 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10563 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10565 _ckvmssts_noperl(lib$find_file_end(&cxt));
10566 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10570 _ckvmssts_noperl(lib$find_file_end(&cxt));
10575 while (*s && !isspace(*s)) s++;
10578 /* check that it's really not DCL with no file extension */
10579 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10581 char b[256] = {0,0,0,0};
10582 read(fileno(fp), b, 256);
10583 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10587 /* Check for script */
10589 if ((b[0] == '#') && (b[1] == '!'))
10591 #ifdef ALTERNATE_SHEBANG
10593 shebang_len = strlen(ALTERNATE_SHEBANG);
10594 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10596 perlstr = strstr("perl",b);
10597 if (perlstr == NULL)
10605 if (shebang_len > 0) {
10608 char tmpspec[NAM$C_MAXRSS + 1];
10611 /* Image is following after white space */
10612 /*--------------------------------------*/
10613 while (isprint(b[i]) && isspace(b[i]))
10617 while (isprint(b[i]) && !isspace(b[i])) {
10618 tmpspec[j++] = b[i++];
10619 if (j >= NAM$C_MAXRSS)
10624 /* There may be some default parameters to the image */
10625 /*---------------------------------------------------*/
10627 while (isprint(b[i])) {
10628 image_argv[j++] = b[i++];
10629 if (j >= NAM$C_MAXRSS)
10632 while ((j > 0) && !isprint(image_argv[j-1]))
10636 /* It will need to be converted to VMS format and validated */
10637 if (tmpspec[0] != '\0') {
10640 /* Try to find the exact program requested to be run */
10641 /*---------------------------------------------------*/
10642 iname = int_rmsexpand
10643 (tmpspec, image_name, ".exe",
10644 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10645 if (iname != NULL) {
10646 if (cando_by_name_int
10647 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10648 /* MCR prefix needed */
10652 /* Try again with a null type */
10653 /*----------------------------*/
10654 iname = int_rmsexpand
10655 (tmpspec, image_name, ".",
10656 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10657 if (iname != NULL) {
10658 if (cando_by_name_int
10659 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10660 /* MCR prefix needed */
10666 /* Did we find the image to run the script? */
10667 /*------------------------------------------*/
10671 /* Assume DCL or foreign command exists */
10672 /*--------------------------------------*/
10673 tchr = strrchr(tmpspec, '/');
10674 if (tchr != NULL) {
10680 my_strlcpy(image_name, tchr, sizeof(image_name));
10688 if (check_img && isdcl) {
10690 PerlMem_free(resspec);
10691 PerlMem_free(vmsspec);
10695 if (cando_by_name(S_IXUSR,0,resspec)) {
10696 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10697 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10699 my_strlcpy(vmscmd->dsc$a_pointer,"$ MCR ", MAX_DCL_LINE_LENGTH);
10700 if (image_name[0] != 0) {
10701 my_strlcat(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10702 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10704 } else if (image_name[0] != 0) {
10705 my_strlcpy(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10706 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10708 my_strlcpy(vmscmd->dsc$a_pointer, "@", MAX_DCL_LINE_LENGTH);
10710 if (suggest_quote) *suggest_quote = 1;
10712 /* If there is an image name, use original command */
10713 if (image_name[0] == 0)
10714 my_strlcat(vmscmd->dsc$a_pointer, resspec, MAX_DCL_LINE_LENGTH);
10717 while (*rest && isspace(*rest)) rest++;
10720 if (image_argv[0] != 0) {
10721 my_strlcat(vmscmd->dsc$a_pointer, image_argv, MAX_DCL_LINE_LENGTH);
10722 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10728 rest_len = strlen(rest);
10729 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10730 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10731 my_strlcat(vmscmd->dsc$a_pointer, rest, MAX_DCL_LINE_LENGTH);
10733 retsts = CLI$_BUFOVF;
10735 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10737 PerlMem_free(vmsspec);
10738 PerlMem_free(resspec);
10739 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10745 /* It's either a DCL command or we couldn't find a suitable image */
10746 vmscmd->dsc$w_length = strlen(cmd);
10748 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(vmscmd->dsc$w_length + 1);
10749 my_strlcpy(vmscmd->dsc$a_pointer, cmd, vmscmd->dsc$w_length + 1);
10752 PerlMem_free(resspec);
10753 PerlMem_free(vmsspec);
10755 /* check if it's a symbol (for quoting purposes) */
10756 if (suggest_quote && !*suggest_quote) {
10758 char equiv[LNM$C_NAMLENGTH];
10759 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10760 eqvdsc.dsc$a_pointer = equiv;
10762 iss = lib$get_symbol(vmscmd,&eqvdsc);
10763 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10765 if (!(retsts & 1)) {
10766 /* just hand off status values likely to be due to user error */
10767 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10768 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10769 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10770 else { _ckvmssts_noperl(retsts); }
10773 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10775 } /* end of setup_cmddsc() */
10778 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10780 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10786 if (vfork_called) { /* this follows a vfork - act Unixish */
10788 if (vfork_called < 0) {
10789 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10792 else return do_aexec(really,mark,sp);
10794 /* no vfork - act VMSish */
10795 cmd = setup_argstr(aTHX_ really,mark,sp);
10796 exec_sts = vms_do_exec(cmd);
10797 Safefree(cmd); /* Clean up from setup_argstr() */
10802 } /* end of vms_do_aexec() */
10805 /* {{{bool vms_do_exec(char *cmd) */
10807 Perl_vms_do_exec(pTHX_ const char *cmd)
10809 struct dsc$descriptor_s *vmscmd;
10811 if (vfork_called) { /* this follows a vfork - act Unixish */
10813 if (vfork_called < 0) {
10814 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10817 else return do_exec(cmd);
10820 { /* no vfork - act VMSish */
10821 unsigned long int retsts;
10824 TAINT_PROPER("exec");
10825 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10826 retsts = lib$do_command(vmscmd);
10829 case RMS$_FNF: case RMS$_DNF:
10830 set_errno(ENOENT); break;
10832 set_errno(ENOTDIR); break;
10834 set_errno(ENODEV); break;
10836 set_errno(EACCES); break;
10838 set_errno(EINVAL); break;
10839 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10840 set_errno(E2BIG); break;
10841 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10842 _ckvmssts_noperl(retsts); /* fall through */
10843 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10844 set_errno(EVMSERR);
10846 set_vaxc_errno(retsts);
10847 if (ckWARN(WARN_EXEC)) {
10848 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
10849 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
10851 vms_execfree(vmscmd);
10856 } /* end of vms_do_exec() */
10859 int do_spawn2(pTHX_ const char *, int);
10862 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
10864 unsigned long int sts;
10870 /* We'll copy the (undocumented?) Win32 behavior and allow a
10871 * numeric first argument. But the only value we'll support
10872 * through do_aspawn is a value of 1, which means spawn without
10873 * waiting for completion -- other values are ignored.
10875 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
10877 flags = SvIVx(*mark);
10880 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
10881 flags = CLI$M_NOWAIT;
10885 cmd = setup_argstr(aTHX_ really, mark, sp);
10886 sts = do_spawn2(aTHX_ cmd, flags);
10887 /* pp_sys will clean up cmd */
10891 } /* end of do_aspawn() */
10895 /* {{{int do_spawn(char* cmd) */
10897 Perl_do_spawn(pTHX_ char* cmd)
10899 PERL_ARGS_ASSERT_DO_SPAWN;
10901 return do_spawn2(aTHX_ cmd, 0);
10905 /* {{{int do_spawn_nowait(char* cmd) */
10907 Perl_do_spawn_nowait(pTHX_ char* cmd)
10909 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
10911 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
10915 /* {{{int do_spawn2(char *cmd) */
10917 do_spawn2(pTHX_ const char *cmd, int flags)
10919 unsigned long int sts, substs;
10921 /* The caller of this routine expects to Safefree(PL_Cmd) */
10922 Newx(PL_Cmd,10,char);
10925 TAINT_PROPER("spawn");
10926 if (!cmd || !*cmd) {
10927 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
10930 case RMS$_FNF: case RMS$_DNF:
10931 set_errno(ENOENT); break;
10933 set_errno(ENOTDIR); break;
10935 set_errno(ENODEV); break;
10937 set_errno(EACCES); break;
10939 set_errno(EINVAL); break;
10940 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10941 set_errno(E2BIG); break;
10942 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10943 _ckvmssts_noperl(sts); /* fall through */
10944 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10945 set_errno(EVMSERR);
10947 set_vaxc_errno(sts);
10948 if (ckWARN(WARN_EXEC)) {
10949 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
10958 if (flags & CLI$M_NOWAIT)
10961 strcpy(mode, "nW");
10963 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
10966 /* sts will be the pid in the nowait case */
10969 } /* end of do_spawn2() */
10973 static unsigned int *sockflags, sockflagsize;
10976 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
10977 * routines found in some versions of the CRTL can't deal with sockets.
10978 * We don't shim the other file open routines since a socket isn't
10979 * likely to be opened by a name.
10981 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
10982 FILE *my_fdopen(int fd, const char *mode)
10984 FILE *fp = fdopen(fd, mode);
10987 unsigned int fdoff = fd / sizeof(unsigned int);
10988 Stat_t sbuf; /* native stat; we don't need flex_stat */
10989 if (!sockflagsize || fdoff > sockflagsize) {
10990 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
10991 else Newx (sockflags,fdoff+2,unsigned int);
10992 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
10993 sockflagsize = fdoff + 2;
10995 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
10996 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11005 * Clear the corresponding bit when the (possibly) socket stream is closed.
11006 * There still a small hole: we miss an implicit close which might occur
11007 * via freopen(). >> Todo
11009 /*{{{ int my_fclose(FILE *fp)*/
11010 int my_fclose(FILE *fp) {
11012 unsigned int fd = fileno(fp);
11013 unsigned int fdoff = fd / sizeof(unsigned int);
11015 if (sockflagsize && fdoff < sockflagsize)
11016 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11024 * A simple fwrite replacement which outputs itmsz*nitm chars without
11025 * introducing record boundaries every itmsz chars.
11026 * We are using fputs, which depends on a terminating null. We may
11027 * well be writing binary data, so we need to accommodate not only
11028 * data with nulls sprinkled in the middle but also data with no null
11031 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11033 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11035 char *cp, *end, *cpd;
11037 unsigned int fd = fileno(dest);
11038 unsigned int fdoff = fd / sizeof(unsigned int);
11040 int bufsize = itmsz * nitm + 1;
11042 if (fdoff < sockflagsize &&
11043 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11044 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11048 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11049 memcpy( data, src, itmsz*nitm );
11050 data[itmsz*nitm] = '\0';
11052 end = data + itmsz * nitm;
11053 retval = (int) nitm; /* on success return # items written */
11056 while (cpd <= end) {
11057 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11058 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11060 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11064 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11067 } /* end of my_fwrite() */
11070 /*{{{ int my_flush(FILE *fp)*/
11072 Perl_my_flush(pTHX_ FILE *fp)
11075 if ((res = fflush(fp)) == 0 && fp) {
11076 #ifdef VMS_DO_SOCKETS
11078 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11080 res = fsync(fileno(fp));
11083 * If the flush succeeded but set end-of-file, we need to clear
11084 * the error because our caller may check ferror(). BTW, this
11085 * probably means we just flushed an empty file.
11087 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11093 /* fgetname() is not returning the correct file specifications when
11094 * decc_filename_unix_report mode is active. So we have to have it
11095 * aways return filenames in VMS mode and convert it ourselves.
11098 /*{{{ char * my_fgetname(FILE *fp, buf)*/
11100 Perl_my_fgetname(FILE *fp, char * buf) {
11104 retname = fgetname(fp, buf, 1);
11106 /* If we are in VMS mode, then we are done */
11107 if (!decc_filename_unix_report || (retname == NULL)) {
11111 /* Convert this to Unix format */
11112 vms_name = (char *)PerlMem_malloc(VMS_MAXRSS);
11113 my_strlcpy(vms_name, retname, VMS_MAXRSS);
11114 retname = int_tounixspec(vms_name, buf, NULL);
11115 PerlMem_free(vms_name);
11122 * Here are replacements for the following Unix routines in the VMS environment:
11123 * getpwuid Get information for a particular UIC or UID
11124 * getpwnam Get information for a named user
11125 * getpwent Get information for each user in the rights database
11126 * setpwent Reset search to the start of the rights database
11127 * endpwent Finish searching for users in the rights database
11129 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11130 * (defined in pwd.h), which contains the following fields:-
11132 * char *pw_name; Username (in lower case)
11133 * char *pw_passwd; Hashed password
11134 * unsigned int pw_uid; UIC
11135 * unsigned int pw_gid; UIC group number
11136 * char *pw_unixdir; Default device/directory (VMS-style)
11137 * char *pw_gecos; Owner name
11138 * char *pw_dir; Default device/directory (Unix-style)
11139 * char *pw_shell; Default CLI name (eg. DCL)
11141 * If the specified user does not exist, getpwuid and getpwnam return NULL.
11143 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11144 * not the UIC member number (eg. what's returned by getuid()),
11145 * getpwuid() can accept either as input (if uid is specified, the caller's
11146 * UIC group is used), though it won't recognise gid=0.
11148 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11149 * information about other users in your group or in other groups, respectively.
11150 * If the required privilege is not available, then these routines fill only
11151 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11154 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11157 /* sizes of various UAF record fields */
11158 #define UAI$S_USERNAME 12
11159 #define UAI$S_IDENT 31
11160 #define UAI$S_OWNER 31
11161 #define UAI$S_DEFDEV 31
11162 #define UAI$S_DEFDIR 63
11163 #define UAI$S_DEFCLI 31
11164 #define UAI$S_PWD 8
11166 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
11167 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11168 (uic).uic$v_group != UIC$K_WILD_GROUP)
11170 static char __empty[]= "";
11171 static struct passwd __passwd_empty=
11172 {(char *) __empty, (char *) __empty, 0, 0,
11173 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11174 static int contxt= 0;
11175 static struct passwd __pwdcache;
11176 static char __pw_namecache[UAI$S_IDENT+1];
11179 * This routine does most of the work extracting the user information.
11181 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11184 unsigned char length;
11185 char pw_gecos[UAI$S_OWNER+1];
11187 static union uicdef uic;
11189 unsigned char length;
11190 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11193 unsigned char length;
11194 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11197 unsigned char length;
11198 char pw_shell[UAI$S_DEFCLI+1];
11200 static char pw_passwd[UAI$S_PWD+1];
11202 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11203 struct dsc$descriptor_s name_desc;
11204 unsigned long int sts;
11206 static struct itmlst_3 itmlst[]= {
11207 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
11208 {sizeof(uic), UAI$_UIC, &uic, &luic},
11209 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
11210 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
11211 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
11212 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
11213 {0, 0, NULL, NULL}};
11215 name_desc.dsc$w_length= strlen(name);
11216 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11217 name_desc.dsc$b_class= DSC$K_CLASS_S;
11218 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11220 /* Note that sys$getuai returns many fields as counted strings. */
11221 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11222 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11223 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11225 else { _ckvmssts(sts); }
11226 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
11228 if ((int) owner.length < lowner) lowner= (int) owner.length;
11229 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11230 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11231 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11232 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11233 owner.pw_gecos[lowner]= '\0';
11234 defdev.pw_dir[ldefdev+ldefdir]= '\0';
11235 defcli.pw_shell[ldefcli]= '\0';
11236 if (valid_uic(uic)) {
11237 pwd->pw_uid= uic.uic$l_uic;
11238 pwd->pw_gid= uic.uic$v_group;
11241 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11242 pwd->pw_passwd= pw_passwd;
11243 pwd->pw_gecos= owner.pw_gecos;
11244 pwd->pw_dir= defdev.pw_dir;
11245 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11246 pwd->pw_shell= defcli.pw_shell;
11247 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11249 ldir= strlen(pwd->pw_unixdir) - 1;
11250 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11253 my_strlcpy(pwd->pw_unixdir, pwd->pw_dir, sizeof(pwd->pw_unixdir));
11254 if (!decc_efs_case_preserve)
11255 __mystrtolower(pwd->pw_unixdir);
11260 * Get information for a named user.
11262 /*{{{struct passwd *getpwnam(char *name)*/
11263 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
11265 struct dsc$descriptor_s name_desc;
11267 unsigned long int sts;
11269 __pwdcache = __passwd_empty;
11270 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11271 /* We still may be able to determine pw_uid and pw_gid */
11272 name_desc.dsc$w_length= strlen(name);
11273 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11274 name_desc.dsc$b_class= DSC$K_CLASS_S;
11275 name_desc.dsc$a_pointer= (char *) name;
11276 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11277 __pwdcache.pw_uid= uic.uic$l_uic;
11278 __pwdcache.pw_gid= uic.uic$v_group;
11281 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11282 set_vaxc_errno(sts);
11283 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11286 else { _ckvmssts(sts); }
11289 my_strlcpy(__pw_namecache, name, sizeof(__pw_namecache));
11290 __pwdcache.pw_name= __pw_namecache;
11291 return &__pwdcache;
11292 } /* end of my_getpwnam() */
11296 * Get information for a particular UIC or UID.
11297 * Called by my_getpwent with uid=-1 to list all users.
11299 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11300 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
11302 const $DESCRIPTOR(name_desc,__pw_namecache);
11303 unsigned short lname;
11305 unsigned long int status;
11307 if (uid == (unsigned int) -1) {
11309 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11310 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11311 set_vaxc_errno(status);
11312 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11316 else { _ckvmssts(status); }
11317 } while (!valid_uic (uic));
11320 uic.uic$l_uic= uid;
11321 if (!uic.uic$v_group)
11322 uic.uic$v_group= PerlProc_getgid();
11323 if (valid_uic(uic))
11324 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11325 else status = SS$_IVIDENT;
11326 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11327 status == RMS$_PRV) {
11328 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11331 else { _ckvmssts(status); }
11333 __pw_namecache[lname]= '\0';
11334 __mystrtolower(__pw_namecache);
11336 __pwdcache = __passwd_empty;
11337 __pwdcache.pw_name = __pw_namecache;
11339 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11340 The identifier's value is usually the UIC, but it doesn't have to be,
11341 so if we can, we let fillpasswd update this. */
11342 __pwdcache.pw_uid = uic.uic$l_uic;
11343 __pwdcache.pw_gid = uic.uic$v_group;
11345 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11346 return &__pwdcache;
11348 } /* end of my_getpwuid() */
11352 * Get information for next user.
11354 /*{{{struct passwd *my_getpwent()*/
11355 struct passwd *Perl_my_getpwent(pTHX)
11357 return (my_getpwuid((unsigned int) -1));
11362 * Finish searching rights database for users.
11364 /*{{{void my_endpwent()*/
11365 void Perl_my_endpwent(pTHX)
11368 _ckvmssts(sys$finish_rdb(&contxt));
11374 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11375 * my_utime(), and flex_stat(), all of which operate on UTC unless
11376 * VMSISH_TIMES is true.
11378 /* method used to handle UTC conversions:
11379 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
11381 static int gmtime_emulation_type;
11382 /* number of secs to add to UTC POSIX-style time to get local time */
11383 static long int utc_offset_secs;
11385 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11386 * in vmsish.h. #undef them here so we can call the CRTL routines
11394 static time_t toutc_dst(time_t loc) {
11397 if ((rsltmp = localtime(&loc)) == NULL) return -1u;
11398 loc -= utc_offset_secs;
11399 if (rsltmp->tm_isdst) loc -= 3600;
11402 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11403 ((gmtime_emulation_type || my_time(NULL)), \
11404 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11405 ((secs) - utc_offset_secs))))
11407 static time_t toloc_dst(time_t utc) {
11410 utc += utc_offset_secs;
11411 if ((rsltmp = localtime(&utc)) == NULL) return -1u;
11412 if (rsltmp->tm_isdst) utc += 3600;
11415 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11416 ((gmtime_emulation_type || my_time(NULL)), \
11417 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11418 ((secs) + utc_offset_secs))))
11420 /* my_time(), my_localtime(), my_gmtime()
11421 * By default traffic in UTC time values, using CRTL gmtime() or
11422 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11423 * Note: We need to use these functions even when the CRTL has working
11424 * UTC support, since they also handle C<use vmsish qw(times);>
11426 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
11427 * Modified by Charles Bailey <bailey@newman.upenn.edu>
11430 /*{{{time_t my_time(time_t *timep)*/
11431 time_t Perl_my_time(pTHX_ time_t *timep)
11436 if (gmtime_emulation_type == 0) {
11437 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
11438 /* results of calls to gmtime() and localtime() */
11439 /* for same &base */
11441 gmtime_emulation_type++;
11442 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11443 char off[LNM$C_NAMLENGTH+1];;
11445 gmtime_emulation_type++;
11446 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11447 gmtime_emulation_type++;
11448 utc_offset_secs = 0;
11449 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11451 else { utc_offset_secs = atol(off); }
11453 else { /* We've got a working gmtime() */
11454 struct tm gmt, local;
11457 tm_p = localtime(&base);
11459 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
11460 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11461 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
11462 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
11467 # ifdef VMSISH_TIME
11468 if (VMSISH_TIME) when = _toloc(when);
11470 if (timep != NULL) *timep = when;
11473 } /* end of my_time() */
11477 /*{{{struct tm *my_gmtime(const time_t *timep)*/
11479 Perl_my_gmtime(pTHX_ const time_t *timep)
11484 if (timep == NULL) {
11485 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11488 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11491 # ifdef VMSISH_TIME
11492 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11494 return gmtime(&when);
11495 } /* end of my_gmtime() */
11499 /*{{{struct tm *my_localtime(const time_t *timep)*/
11501 Perl_my_localtime(pTHX_ const time_t *timep)
11505 if (timep == NULL) {
11506 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11509 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11510 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
11513 # ifdef VMSISH_TIME
11514 if (VMSISH_TIME) when = _toutc(when);
11516 /* CRTL localtime() wants UTC as input, does tz correction itself */
11517 return localtime(&when);
11518 } /* end of my_localtime() */
11521 /* Reset definitions for later calls */
11522 #define gmtime(t) my_gmtime(t)
11523 #define localtime(t) my_localtime(t)
11524 #define time(t) my_time(t)
11527 /* my_utime - update modification/access time of a file
11529 * VMS 7.3 and later implementation
11530 * Only the UTC translation is home-grown. The rest is handled by the
11531 * CRTL utime(), which will take into account the relevant feature
11532 * logicals and ODS-5 volume characteristics for true access times.
11534 * pre VMS 7.3 implementation:
11535 * The calling sequence is identical to POSIX utime(), but under
11536 * VMS with ODS-2, only the modification time is changed; ODS-2 does
11537 * not maintain access times. Restrictions differ from the POSIX
11538 * definition in that the time can be changed as long as the
11539 * caller has permission to execute the necessary IO$_MODIFY $QIO;
11540 * no separate checks are made to insure that the caller is the
11541 * owner of the file or has special privs enabled.
11542 * Code here is based on Joe Meadows' FILE utility.
11546 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11547 * to VMS epoch (01-JAN-1858 00:00:00.00)
11548 * in 100 ns intervals.
11550 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11552 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11553 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
11555 #if __CRTL_VER >= 70300000
11556 struct utimbuf utc_utimes, *utc_utimesp;
11558 if (utimes != NULL) {
11559 utc_utimes.actime = utimes->actime;
11560 utc_utimes.modtime = utimes->modtime;
11561 # ifdef VMSISH_TIME
11562 /* If input was local; convert to UTC for sys svc */
11564 utc_utimes.actime = _toutc(utimes->actime);
11565 utc_utimes.modtime = _toutc(utimes->modtime);
11568 utc_utimesp = &utc_utimes;
11571 utc_utimesp = NULL;
11574 return utime(file, utc_utimesp);
11576 #else /* __CRTL_VER < 70300000 */
11580 long int bintime[2], len = 2, lowbit, unixtime,
11581 secscale = 10000000; /* seconds --> 100 ns intervals */
11582 unsigned long int chan, iosb[2], retsts;
11583 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
11584 struct FAB myfab = cc$rms_fab;
11585 struct NAM mynam = cc$rms_nam;
11586 #if defined (__DECC) && defined (__VAX)
11587 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
11588 * at least through VMS V6.1, which causes a type-conversion warning.
11590 # pragma message save
11591 # pragma message disable cvtdiftypes
11593 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
11594 struct fibdef myfib;
11595 #if defined (__DECC) && defined (__VAX)
11596 /* This should be right after the declaration of myatr, but due
11597 * to a bug in VAX DEC C, this takes effect a statement early.
11599 # pragma message restore
11601 /* cast ok for read only parameter */
11602 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
11603 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
11604 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
11606 if (file == NULL || *file == '\0') {
11607 SETERRNO(ENOENT, LIB$_INVARG);
11611 /* Convert to VMS format ensuring that it will fit in 255 characters */
11612 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
11613 SETERRNO(ENOENT, LIB$_INVARG);
11616 if (utimes != NULL) {
11617 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
11618 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
11619 * Since time_t is unsigned long int, and lib$emul takes a signed long int
11620 * as input, we force the sign bit to be clear by shifting unixtime right
11621 * one bit, then multiplying by an extra factor of 2 in lib$emul().
11623 lowbit = (utimes->modtime & 1) ? secscale : 0;
11624 unixtime = (long int) utimes->modtime;
11625 # ifdef VMSISH_TIME
11626 /* If input was UTC; convert to local for sys svc */
11627 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
11629 unixtime >>= 1; secscale <<= 1;
11630 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
11631 if (!(retsts & 1)) {
11632 SETERRNO(EVMSERR, retsts);
11635 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
11636 if (!(retsts & 1)) {
11637 SETERRNO(EVMSERR, retsts);
11642 /* Just get the current time in VMS format directly */
11643 retsts = sys$gettim(bintime);
11644 if (!(retsts & 1)) {
11645 SETERRNO(EVMSERR, retsts);
11650 myfab.fab$l_fna = vmsspec;
11651 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
11652 myfab.fab$l_nam = &mynam;
11653 mynam.nam$l_esa = esa;
11654 mynam.nam$b_ess = (unsigned char) sizeof esa;
11655 mynam.nam$l_rsa = rsa;
11656 mynam.nam$b_rss = (unsigned char) sizeof rsa;
11657 if (decc_efs_case_preserve)
11658 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
11660 /* Look for the file to be affected, letting RMS parse the file
11661 * specification for us as well. I have set errno using only
11662 * values documented in the utime() man page for VMS POSIX.
11664 retsts = sys$parse(&myfab,0,0);
11665 if (!(retsts & 1)) {
11666 set_vaxc_errno(retsts);
11667 if (retsts == RMS$_PRV) set_errno(EACCES);
11668 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
11669 else set_errno(EVMSERR);
11672 retsts = sys$search(&myfab,0,0);
11673 if (!(retsts & 1)) {
11674 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11675 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11676 set_vaxc_errno(retsts);
11677 if (retsts == RMS$_PRV) set_errno(EACCES);
11678 else if (retsts == RMS$_FNF) set_errno(ENOENT);
11679 else set_errno(EVMSERR);
11683 devdsc.dsc$w_length = mynam.nam$b_dev;
11684 /* cast ok for read only parameter */
11685 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
11687 retsts = sys$assign(&devdsc,&chan,0,0);
11688 if (!(retsts & 1)) {
11689 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11690 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11691 set_vaxc_errno(retsts);
11692 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
11693 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
11694 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
11695 else set_errno(EVMSERR);
11699 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
11700 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
11702 memset((void *) &myfib, 0, sizeof myfib);
11703 #if defined(__DECC) || defined(__DECCXX)
11704 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
11705 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
11706 /* This prevents the revision time of the file being reset to the current
11707 * time as a result of our IO$_MODIFY $QIO. */
11708 myfib.fib$l_acctl = FIB$M_NORECORD;
11710 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
11711 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
11712 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
11714 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
11715 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11716 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11717 _ckvmssts(sys$dassgn(chan));
11718 if (retsts & 1) retsts = iosb[0];
11719 if (!(retsts & 1)) {
11720 set_vaxc_errno(retsts);
11721 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11722 else set_errno(EVMSERR);
11728 #endif /* #if __CRTL_VER >= 70300000 */
11730 } /* end of my_utime() */
11734 * flex_stat, flex_lstat, flex_fstat
11735 * basic stat, but gets it right when asked to stat
11736 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
11739 #ifndef _USE_STD_STAT
11740 /* encode_dev packs a VMS device name string into an integer to allow
11741 * simple comparisons. This can be used, for example, to check whether two
11742 * files are located on the same device, by comparing their encoded device
11743 * names. Even a string comparison would not do, because stat() reuses the
11744 * device name buffer for each call; so without encode_dev, it would be
11745 * necessary to save the buffer and use strcmp (this would mean a number of
11746 * changes to the standard Perl code, to say nothing of what a Perl script
11747 * would have to do.
11749 * The device lock id, if it exists, should be unique (unless perhaps compared
11750 * with lock ids transferred from other nodes). We have a lock id if the disk is
11751 * mounted cluster-wide, which is when we tend to get long (host-qualified)
11752 * device names. Thus we use the lock id in preference, and only if that isn't
11753 * available, do we try to pack the device name into an integer (flagged by
11754 * the sign bit (LOCKID_MASK) being set).
11756 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
11757 * name and its encoded form, but it seems very unlikely that we will find
11758 * two files on different disks that share the same encoded device names,
11759 * and even more remote that they will share the same file id (if the test
11760 * is to check for the same file).
11762 * A better method might be to use sys$device_scan on the first call, and to
11763 * search for the device, returning an index into the cached array.
11764 * The number returned would be more intelligible.
11765 * This is probably not worth it, and anyway would take quite a bit longer
11766 * on the first call.
11768 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
11769 static mydev_t encode_dev (pTHX_ const char *dev)
11772 unsigned long int f;
11777 if (!dev || !dev[0]) return 0;
11781 struct dsc$descriptor_s dev_desc;
11782 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
11784 /* For cluster-mounted disks, the disk lock identifier is unique, so we
11785 can try that first. */
11786 dev_desc.dsc$w_length = strlen (dev);
11787 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
11788 dev_desc.dsc$b_class = DSC$K_CLASS_S;
11789 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
11790 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
11791 if (!$VMS_STATUS_SUCCESS(status)) {
11793 case SS$_NOSUCHDEV:
11794 SETERRNO(ENODEV, status);
11800 if (lockid) return (lockid & ~LOCKID_MASK);
11804 /* Otherwise we try to encode the device name */
11808 for (q = dev + strlen(dev); q--; q >= dev) {
11813 else if (isalpha (toupper (*q)))
11814 c= toupper (*q) - 'A' + (char)10;
11816 continue; /* Skip '$'s */
11818 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
11820 enc += f * (unsigned long int) c;
11822 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
11824 } /* end of encode_dev() */
11825 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11826 device_no = encode_dev(aTHX_ devname)
11828 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11829 device_no = new_dev_no
11833 is_null_device(const char *name)
11835 if (decc_bug_devnull != 0) {
11836 if (strncmp("/dev/null", name, 9) == 0)
11839 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
11840 The underscore prefix, controller letter, and unit number are
11841 independently optional; for our purposes, the colon punctuation
11842 is not. The colon can be trailed by optional directory and/or
11843 filename, but two consecutive colons indicates a nodename rather
11844 than a device. [pr] */
11845 if (*name == '_') ++name;
11846 if (tolower(*name++) != 'n') return 0;
11847 if (tolower(*name++) != 'l') return 0;
11848 if (tolower(*name) == 'a') ++name;
11849 if (*name == '0') ++name;
11850 return (*name++ == ':') && (*name != ':');
11854 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
11856 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
11859 Perl_cando_by_name_int
11860 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
11862 char usrname[L_cuserid];
11863 struct dsc$descriptor_s usrdsc =
11864 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
11865 char *vmsname = NULL, *fileified = NULL;
11866 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
11867 unsigned short int retlen, trnlnm_iter_count;
11868 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11869 union prvdef curprv;
11870 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
11871 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
11872 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
11873 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
11874 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
11876 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
11878 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11880 static int profile_context = -1;
11882 if (!fname || !*fname) return FALSE;
11884 /* Make sure we expand logical names, since sys$check_access doesn't */
11885 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
11886 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11887 if (!strpbrk(fname,"/]>:")) {
11888 my_strlcpy(fileified, fname, VMS_MAXRSS);
11889 trnlnm_iter_count = 0;
11890 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
11891 trnlnm_iter_count++;
11892 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
11897 vmsname = (char *)PerlMem_malloc(VMS_MAXRSS);
11898 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11899 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
11900 /* Don't know if already in VMS format, so make sure */
11901 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
11902 PerlMem_free(fileified);
11903 PerlMem_free(vmsname);
11908 my_strlcpy(vmsname, fname, VMS_MAXRSS);
11911 /* sys$check_access needs a file spec, not a directory spec.
11912 * flex_stat now will handle a null thread context during startup.
11915 retlen = namdsc.dsc$w_length = strlen(vmsname);
11916 if (vmsname[retlen-1] == ']'
11917 || vmsname[retlen-1] == '>'
11918 || vmsname[retlen-1] == ':'
11919 || (!flex_stat_int(vmsname, &st, 1) &&
11920 S_ISDIR(st.st_mode))) {
11922 if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
11923 PerlMem_free(fileified);
11924 PerlMem_free(vmsname);
11933 retlen = namdsc.dsc$w_length = strlen(fname);
11934 namdsc.dsc$a_pointer = (char *)fname;
11937 case S_IXUSR: case S_IXGRP: case S_IXOTH:
11938 access = ARM$M_EXECUTE;
11939 flags = CHP$M_READ;
11941 case S_IRUSR: case S_IRGRP: case S_IROTH:
11942 access = ARM$M_READ;
11943 flags = CHP$M_READ | CHP$M_USEREADALL;
11945 case S_IWUSR: case S_IWGRP: case S_IWOTH:
11946 access = ARM$M_WRITE;
11947 flags = CHP$M_READ | CHP$M_WRITE;
11949 case S_IDUSR: case S_IDGRP: case S_IDOTH:
11950 access = ARM$M_DELETE;
11951 flags = CHP$M_READ | CHP$M_WRITE;
11954 if (fileified != NULL)
11955 PerlMem_free(fileified);
11956 if (vmsname != NULL)
11957 PerlMem_free(vmsname);
11961 /* Before we call $check_access, create a user profile with the current
11962 * process privs since otherwise it just uses the default privs from the
11963 * UAF and might give false positives or negatives. This only works on
11964 * VMS versions v6.0 and later since that's when sys$create_user_profile
11965 * became available.
11968 /* get current process privs and username */
11969 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
11970 _ckvmssts_noperl(iosb[0]);
11972 /* find out the space required for the profile */
11973 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
11974 &usrprodsc.dsc$w_length,&profile_context));
11976 /* allocate space for the profile and get it filled in */
11977 usrprodsc.dsc$a_pointer = (char *)PerlMem_malloc(usrprodsc.dsc$w_length);
11978 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11979 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
11980 &usrprodsc.dsc$w_length,&profile_context));
11982 /* use the profile to check access to the file; free profile & analyze results */
11983 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
11984 PerlMem_free(usrprodsc.dsc$a_pointer);
11985 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
11987 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
11988 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
11989 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
11990 set_vaxc_errno(retsts);
11991 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11992 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
11993 else set_errno(ENOENT);
11994 if (fileified != NULL)
11995 PerlMem_free(fileified);
11996 if (vmsname != NULL)
11997 PerlMem_free(vmsname);
12000 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12001 if (fileified != NULL)
12002 PerlMem_free(fileified);
12003 if (vmsname != NULL)
12004 PerlMem_free(vmsname);
12007 _ckvmssts_noperl(retsts);
12009 if (fileified != NULL)
12010 PerlMem_free(fileified);
12011 if (vmsname != NULL)
12012 PerlMem_free(vmsname);
12013 return FALSE; /* Should never get here */
12017 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
12018 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12019 * subset of the applicable information.
12022 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12024 return cando_by_name_int
12025 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12026 } /* end of cando() */
12030 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12032 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12034 return cando_by_name_int(bit, effective, fname, 0);
12036 } /* end of cando_by_name() */
12040 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12042 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12044 if (!fstat(fd, &statbufp->crtl_stat)) {
12046 char *vms_filename;
12047 vms_filename = (char *)PerlMem_malloc(VMS_MAXRSS);
12048 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12050 /* Save name for cando by name in VMS format */
12051 cptr = getname(fd, vms_filename, 1);
12053 /* This should not happen, but just in case */
12054 if (cptr == NULL) {
12055 statbufp->st_devnam[0] = 0;
12058 /* Make sure that the saved name fits in 255 characters */
12059 cptr = int_rmsexpand_vms
12061 statbufp->st_devnam,
12064 statbufp->st_devnam[0] = 0;
12066 PerlMem_free(vms_filename);
12068 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12070 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12072 # ifdef VMSISH_TIME
12074 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12075 statbufp->st_atime = _toloc(statbufp->st_atime);
12076 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12083 } /* end of flex_fstat() */
12087 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12089 char *temp_fspec = NULL;
12090 char *fileified = NULL;
12091 const char *save_spec;
12095 char already_fileified = 0;
12103 if (decc_bug_devnull != 0) {
12104 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12105 memset(statbufp,0,sizeof *statbufp);
12106 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12107 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12108 statbufp->st_uid = 0x00010001;
12109 statbufp->st_gid = 0x0001;
12110 time((time_t *)&statbufp->st_mtime);
12111 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12118 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12120 * If we are in POSIX filespec mode, accept the filename as is.
12122 if (decc_posix_compliant_pathnames == 0) {
12125 /* Try for a simple stat first. If fspec contains a filename without
12126 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12127 * and sea:[wine.dark]water. exist, the CRTL prefers the directory here.
12128 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12129 * not sea:[wine.dark]., if the latter exists. If the intended target is
12130 * the file with null type, specify this by calling flex_stat() with
12131 * a '.' at the end of fspec.
12134 if (lstat_flag == 0)
12135 retval = stat(fspec, &statbufp->crtl_stat);
12137 retval = lstat(fspec, &statbufp->crtl_stat);
12143 /* In the odd case where we have write but not read access
12144 * to a directory, stat('foo.DIR') works but stat('foo') doesn't.
12146 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12147 if (fileified == NULL)
12148 _ckvmssts_noperl(SS$_INSFMEM);
12150 ret_spec = int_fileify_dirspec(fspec, fileified, NULL);
12151 if (ret_spec != NULL) {
12152 if (lstat_flag == 0)
12153 retval = stat(fileified, &statbufp->crtl_stat);
12155 retval = lstat(fileified, &statbufp->crtl_stat);
12156 save_spec = fileified;
12157 already_fileified = 1;
12161 if (retval && vms_bug_stat_filename) {
12163 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
12164 if (temp_fspec == NULL)
12165 _ckvmssts_noperl(SS$_INSFMEM);
12167 /* We should try again as a vmsified file specification. */
12169 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12170 if (ret_spec != NULL) {
12171 if (lstat_flag == 0)
12172 retval = stat(temp_fspec, &statbufp->crtl_stat);
12174 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12175 save_spec = temp_fspec;
12180 /* Last chance - allow multiple dots without EFS CHARSET */
12181 /* The CRTL stat() falls down hard on multi-dot filenames in unix
12182 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12183 * enable it if it isn't already.
12185 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12186 if (!decc_efs_charset && (decc_efs_charset_index > 0))
12187 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12189 if (lstat_flag == 0)
12190 retval = stat(fspec, &statbufp->crtl_stat);
12192 retval = lstat(fspec, &statbufp->crtl_stat);
12194 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12195 if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12196 decc$feature_set_value(decc_efs_charset_index, 1, 0);
12202 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12204 if (lstat_flag == 0)
12205 retval = stat(temp_fspec, &statbufp->crtl_stat);
12207 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12208 save_spec = temp_fspec;
12212 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12213 /* As you were... */
12214 if (!decc_efs_charset)
12215 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12220 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12222 /* If this is an lstat, do not follow the link */
12224 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12226 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12227 /* If we used the efs_hack above, we must also use it here for */
12228 /* perl_cando to work */
12229 if (efs_hack && (decc_efs_charset_index > 0)) {
12230 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12234 /* If we've got a directory, save a fileified, expanded version of it
12235 * in st_devnam. If not a directory, just an expanded version.
12237 if (S_ISDIR(statbufp->st_mode) && !already_fileified) {
12238 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12239 if (fileified == NULL)
12240 _ckvmssts_noperl(SS$_INSFMEM);
12242 cptr = do_fileify_dirspec(save_spec, fileified, 0, NULL);
12244 save_spec = fileified;
12247 cptr = int_rmsexpand(save_spec,
12248 statbufp->st_devnam,
12254 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12255 if (efs_hack && (decc_efs_charset_index > 0)) {
12256 decc$feature_set_value(decc_efs_charset, 1, 0);
12260 /* Fix me: If this is NULL then stat found a file, and we could */
12261 /* not convert the specification to VMS - Should never happen */
12263 statbufp->st_devnam[0] = 0;
12265 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12267 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12268 # ifdef VMSISH_TIME
12270 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12271 statbufp->st_atime = _toloc(statbufp->st_atime);
12272 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12276 /* If we were successful, leave errno where we found it */
12277 if (retval == 0) RESTORE_ERRNO;
12279 PerlMem_free(temp_fspec);
12281 PerlMem_free(fileified);
12284 } /* end of flex_stat_int() */
12287 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12289 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12291 return flex_stat_int(fspec, statbufp, 0);
12295 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12297 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12299 return flex_stat_int(fspec, statbufp, 1);
12304 /*{{{char *my_getlogin()*/
12305 /* VMS cuserid == Unix getlogin, except calling sequence */
12309 static char user[L_cuserid];
12310 return cuserid(user);
12315 /* rmscopy - copy a file using VMS RMS routines
12317 * Copies contents and attributes of spec_in to spec_out, except owner
12318 * and protection information. Name and type of spec_in are used as
12319 * defaults for spec_out. The third parameter specifies whether rmscopy()
12320 * should try to propagate timestamps from the input file to the output file.
12321 * If it is less than 0, no timestamps are preserved. If it is 0, then
12322 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12323 * propagated to the output file at creation iff the output file specification
12324 * did not contain an explicit name or type, and the revision date is always
12325 * updated at the end of the copy operation. If it is greater than 0, then
12326 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12327 * other than the revision date should be propagated, and bit 1 indicates
12328 * that the revision date should be propagated.
12330 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12332 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12333 * Incorporates, with permission, some code from EZCOPY by Tim Adye
12334 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12335 * as part of the Perl standard distribution under the terms of the
12336 * GNU General Public License or the Perl Artistic License. Copies
12337 * of each may be found in the Perl standard distribution.
12339 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12341 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12343 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12344 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12345 unsigned long int sts;
12347 struct FAB fab_in, fab_out;
12348 struct RAB rab_in, rab_out;
12349 rms_setup_nam(nam);
12350 rms_setup_nam(nam_out);
12351 struct XABDAT xabdat;
12352 struct XABFHC xabfhc;
12353 struct XABRDT xabrdt;
12354 struct XABSUM xabsum;
12356 vmsin = (char *)PerlMem_malloc(VMS_MAXRSS);
12357 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12358 vmsout = (char *)PerlMem_malloc(VMS_MAXRSS);
12359 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12360 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12361 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
12362 PerlMem_free(vmsin);
12363 PerlMem_free(vmsout);
12364 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12368 esa = (char *)PerlMem_malloc(VMS_MAXRSS);
12369 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12371 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12372 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
12373 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12375 fab_in = cc$rms_fab;
12376 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12377 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12378 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12379 fab_in.fab$l_fop = FAB$M_SQO;
12380 rms_bind_fab_nam(fab_in, nam);
12381 fab_in.fab$l_xab = (void *) &xabdat;
12383 rsa = (char *)PerlMem_malloc(VMS_MAXRSS);
12384 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12386 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12387 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
12388 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12390 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12391 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12392 rms_nam_esl(nam) = 0;
12393 rms_nam_rsl(nam) = 0;
12394 rms_nam_esll(nam) = 0;
12395 rms_nam_rsll(nam) = 0;
12396 #ifdef NAM$M_NO_SHORT_UPCASE
12397 if (decc_efs_case_preserve)
12398 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
12401 xabdat = cc$rms_xabdat; /* To get creation date */
12402 xabdat.xab$l_nxt = (void *) &xabfhc;
12404 xabfhc = cc$rms_xabfhc; /* To get record length */
12405 xabfhc.xab$l_nxt = (void *) &xabsum;
12407 xabsum = cc$rms_xabsum; /* To get key and area information */
12409 if (!((sts = sys$open(&fab_in)) & 1)) {
12410 PerlMem_free(vmsin);
12411 PerlMem_free(vmsout);
12414 PerlMem_free(esal);
12417 PerlMem_free(rsal);
12418 set_vaxc_errno(sts);
12420 case RMS$_FNF: case RMS$_DNF:
12421 set_errno(ENOENT); break;
12423 set_errno(ENOTDIR); break;
12425 set_errno(ENODEV); break;
12427 set_errno(EINVAL); break;
12429 set_errno(EACCES); break;
12431 set_errno(EVMSERR);
12438 fab_out.fab$w_ifi = 0;
12439 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12440 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12441 fab_out.fab$l_fop = FAB$M_SQO;
12442 rms_bind_fab_nam(fab_out, nam_out);
12443 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12444 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12445 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
12446 esa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12447 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12448 rsa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12449 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12452 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12453 esal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12454 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12455 rsal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12456 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12458 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
12459 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
12461 if (preserve_dates == 0) { /* Act like DCL COPY */
12462 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
12463 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
12464 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
12465 PerlMem_free(vmsin);
12466 PerlMem_free(vmsout);
12469 PerlMem_free(esal);
12472 PerlMem_free(rsal);
12473 PerlMem_free(esa_out);
12474 if (esal_out != NULL)
12475 PerlMem_free(esal_out);
12476 PerlMem_free(rsa_out);
12477 if (rsal_out != NULL)
12478 PerlMem_free(rsal_out);
12479 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12480 set_vaxc_errno(sts);
12483 fab_out.fab$l_xab = (void *) &xabdat;
12484 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12485 preserve_dates = 1;
12487 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
12488 preserve_dates =0; /* bitmask from this point forward */
12490 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
12491 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
12492 PerlMem_free(vmsin);
12493 PerlMem_free(vmsout);
12496 PerlMem_free(esal);
12499 PerlMem_free(rsal);
12500 PerlMem_free(esa_out);
12501 if (esal_out != NULL)
12502 PerlMem_free(esal_out);
12503 PerlMem_free(rsa_out);
12504 if (rsal_out != NULL)
12505 PerlMem_free(rsal_out);
12506 set_vaxc_errno(sts);
12509 set_errno(ENOENT); break;
12511 set_errno(ENOTDIR); break;
12513 set_errno(ENODEV); break;
12515 set_errno(EINVAL); break;
12517 set_errno(EACCES); break;
12519 set_errno(EVMSERR);
12523 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
12524 if (preserve_dates & 2) {
12525 /* sys$close() will process xabrdt, not xabdat */
12526 xabrdt = cc$rms_xabrdt;
12528 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12530 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
12531 * is unsigned long[2], while DECC & VAXC use a struct */
12532 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
12534 fab_out.fab$l_xab = (void *) &xabrdt;
12537 ubf = (char *)PerlMem_malloc(32256);
12538 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12539 rab_in = cc$rms_rab;
12540 rab_in.rab$l_fab = &fab_in;
12541 rab_in.rab$l_rop = RAB$M_BIO;
12542 rab_in.rab$l_ubf = ubf;
12543 rab_in.rab$w_usz = 32256;
12544 if (!((sts = sys$connect(&rab_in)) & 1)) {
12545 sys$close(&fab_in); sys$close(&fab_out);
12546 PerlMem_free(vmsin);
12547 PerlMem_free(vmsout);
12551 PerlMem_free(esal);
12554 PerlMem_free(rsal);
12555 PerlMem_free(esa_out);
12556 if (esal_out != NULL)
12557 PerlMem_free(esal_out);
12558 PerlMem_free(rsa_out);
12559 if (rsal_out != NULL)
12560 PerlMem_free(rsal_out);
12561 set_errno(EVMSERR); set_vaxc_errno(sts);
12565 rab_out = cc$rms_rab;
12566 rab_out.rab$l_fab = &fab_out;
12567 rab_out.rab$l_rbf = ubf;
12568 if (!((sts = sys$connect(&rab_out)) & 1)) {
12569 sys$close(&fab_in); sys$close(&fab_out);
12570 PerlMem_free(vmsin);
12571 PerlMem_free(vmsout);
12575 PerlMem_free(esal);
12578 PerlMem_free(rsal);
12579 PerlMem_free(esa_out);
12580 if (esal_out != NULL)
12581 PerlMem_free(esal_out);
12582 PerlMem_free(rsa_out);
12583 if (rsal_out != NULL)
12584 PerlMem_free(rsal_out);
12585 set_errno(EVMSERR); set_vaxc_errno(sts);
12589 while ((sts = sys$read(&rab_in))) { /* always true */
12590 if (sts == RMS$_EOF) break;
12591 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12592 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
12593 sys$close(&fab_in); sys$close(&fab_out);
12594 PerlMem_free(vmsin);
12595 PerlMem_free(vmsout);
12599 PerlMem_free(esal);
12602 PerlMem_free(rsal);
12603 PerlMem_free(esa_out);
12604 if (esal_out != NULL)
12605 PerlMem_free(esal_out);
12606 PerlMem_free(rsa_out);
12607 if (rsal_out != NULL)
12608 PerlMem_free(rsal_out);
12609 set_errno(EVMSERR); set_vaxc_errno(sts);
12615 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
12616 sys$close(&fab_in); sys$close(&fab_out);
12617 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
12619 PerlMem_free(vmsin);
12620 PerlMem_free(vmsout);
12624 PerlMem_free(esal);
12627 PerlMem_free(rsal);
12628 PerlMem_free(esa_out);
12629 if (esal_out != NULL)
12630 PerlMem_free(esal_out);
12631 PerlMem_free(rsa_out);
12632 if (rsal_out != NULL)
12633 PerlMem_free(rsal_out);
12636 set_errno(EVMSERR); set_vaxc_errno(sts);
12642 } /* end of rmscopy() */
12646 /*** The following glue provides 'hooks' to make some of the routines
12647 * from this file available from Perl. These routines are sufficiently
12648 * basic, and are required sufficiently early in the build process,
12649 * that's it's nice to have them available to miniperl as well as the
12650 * full Perl, so they're set up here instead of in an extension. The
12651 * Perl code which handles importation of these names into a given
12652 * package lives in [.VMS]Filespec.pm in @INC.
12656 rmsexpand_fromperl(pTHX_ CV *cv)
12659 char *fspec, *defspec = NULL, *rslt;
12661 int fs_utf8, dfs_utf8;
12665 if (!items || items > 2)
12666 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
12667 fspec = SvPV(ST(0),n_a);
12668 fs_utf8 = SvUTF8(ST(0));
12669 if (!fspec || !*fspec) XSRETURN_UNDEF;
12671 defspec = SvPV(ST(1),n_a);
12672 dfs_utf8 = SvUTF8(ST(1));
12674 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
12675 ST(0) = sv_newmortal();
12676 if (rslt != NULL) {
12677 sv_usepvn(ST(0),rslt,strlen(rslt));
12686 vmsify_fromperl(pTHX_ CV *cv)
12693 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
12694 utf8_fl = SvUTF8(ST(0));
12695 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12696 ST(0) = sv_newmortal();
12697 if (vmsified != NULL) {
12698 sv_usepvn(ST(0),vmsified,strlen(vmsified));
12707 unixify_fromperl(pTHX_ CV *cv)
12714 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
12715 utf8_fl = SvUTF8(ST(0));
12716 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12717 ST(0) = sv_newmortal();
12718 if (unixified != NULL) {
12719 sv_usepvn(ST(0),unixified,strlen(unixified));
12728 fileify_fromperl(pTHX_ CV *cv)
12735 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
12736 utf8_fl = SvUTF8(ST(0));
12737 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12738 ST(0) = sv_newmortal();
12739 if (fileified != NULL) {
12740 sv_usepvn(ST(0),fileified,strlen(fileified));
12749 pathify_fromperl(pTHX_ CV *cv)
12756 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
12757 utf8_fl = SvUTF8(ST(0));
12758 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12759 ST(0) = sv_newmortal();
12760 if (pathified != NULL) {
12761 sv_usepvn(ST(0),pathified,strlen(pathified));
12770 vmspath_fromperl(pTHX_ CV *cv)
12777 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
12778 utf8_fl = SvUTF8(ST(0));
12779 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12780 ST(0) = sv_newmortal();
12781 if (vmspath != NULL) {
12782 sv_usepvn(ST(0),vmspath,strlen(vmspath));
12791 unixpath_fromperl(pTHX_ CV *cv)
12798 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
12799 utf8_fl = SvUTF8(ST(0));
12800 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12801 ST(0) = sv_newmortal();
12802 if (unixpath != NULL) {
12803 sv_usepvn(ST(0),unixpath,strlen(unixpath));
12812 candelete_fromperl(pTHX_ CV *cv)
12820 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
12822 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12823 Newx(fspec, VMS_MAXRSS, char);
12824 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
12825 if (isGV_with_GP(mysv)) {
12826 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
12827 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12835 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
12836 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12843 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
12849 rmscopy_fromperl(pTHX_ CV *cv)
12852 char *inspec, *outspec, *inp, *outp;
12858 if (items < 2 || items > 3)
12859 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
12861 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12862 Newx(inspec, VMS_MAXRSS, char);
12863 if (isGV_with_GP(mysv)) {
12864 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
12865 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12866 ST(0) = sv_2mortal(newSViv(0));
12873 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
12874 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12875 ST(0) = sv_2mortal(newSViv(0));
12880 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
12881 Newx(outspec, VMS_MAXRSS, char);
12882 if (isGV_with_GP(mysv)) {
12883 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
12884 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12885 ST(0) = sv_2mortal(newSViv(0));
12893 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
12894 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12895 ST(0) = sv_2mortal(newSViv(0));
12901 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
12903 ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag)));
12909 /* The mod2fname is limited to shorter filenames by design, so it should
12910 * not be modified to support longer EFS pathnames
12913 mod2fname(pTHX_ CV *cv)
12916 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
12917 workbuff[NAM$C_MAXRSS*1 + 1];
12918 int counter, num_entries;
12919 /* ODS-5 ups this, but we want to be consistent, so... */
12920 int max_name_len = 39;
12921 AV *in_array = (AV *)SvRV(ST(0));
12923 num_entries = av_len(in_array);
12925 /* All the names start with PL_. */
12926 strcpy(ultimate_name, "PL_");
12928 /* Clean up our working buffer */
12929 Zero(work_name, sizeof(work_name), char);
12931 /* Run through the entries and build up a working name */
12932 for(counter = 0; counter <= num_entries; counter++) {
12933 /* If it's not the first name then tack on a __ */
12935 my_strlcat(work_name, "__", sizeof(work_name));
12937 my_strlcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)), sizeof(work_name));
12940 /* Check to see if we actually have to bother...*/
12941 if (strlen(work_name) + 3 <= max_name_len) {
12942 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
12944 /* It's too darned big, so we need to go strip. We use the same */
12945 /* algorithm as xsubpp does. First, strip out doubled __ */
12946 char *source, *dest, last;
12949 for (source = work_name; *source; source++) {
12950 if (last == *source && last == '_') {
12956 /* Go put it back */
12957 my_strlcpy(work_name, workbuff, sizeof(work_name));
12958 /* Is it still too big? */
12959 if (strlen(work_name) + 3 > max_name_len) {
12960 /* Strip duplicate letters */
12963 for (source = work_name; *source; source++) {
12964 if (last == toupper(*source)) {
12968 last = toupper(*source);
12970 my_strlcpy(work_name, workbuff, sizeof(work_name));
12973 /* Is it *still* too big? */
12974 if (strlen(work_name) + 3 > max_name_len) {
12975 /* Too bad, we truncate */
12976 work_name[max_name_len - 2] = 0;
12978 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
12981 /* Okay, return it */
12982 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
12987 hushexit_fromperl(pTHX_ CV *cv)
12992 VMSISH_HUSHED = SvTRUE(ST(0));
12994 ST(0) = boolSV(VMSISH_HUSHED);
13000 Perl_vms_start_glob
13001 (pTHX_ SV *tmpglob,
13005 struct vs_str_st *rslt;
13009 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13012 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13013 struct dsc$descriptor_vs rsdsc;
13014 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13015 unsigned long hasver = 0, isunix = 0;
13016 unsigned long int lff_flags = 0;
13018 int vms_old_glob = 1;
13020 if (!SvOK(tmpglob)) {
13021 SETERRNO(ENOENT,RMS$_FNF);
13025 vms_old_glob = !decc_filename_unix_report;
13027 #ifdef VMS_LONGNAME_SUPPORT
13028 lff_flags = LIB$M_FIL_LONG_NAMES;
13030 /* The Newx macro will not allow me to assign a smaller array
13031 * to the rslt pointer, so we will assign it to the begin char pointer
13032 * and then copy the value into the rslt pointer.
13034 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13035 rslt = (struct vs_str_st *)begin;
13037 rstr = &rslt->str[0];
13038 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13039 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13040 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13041 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13043 Newx(vmsspec, VMS_MAXRSS, char);
13045 /* We could find out if there's an explicit dev/dir or version
13046 by peeking into lib$find_file's internal context at
13047 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13048 but that's unsupported, so I don't want to do it now and
13049 have it bite someone in the future. */
13050 /* Fix-me: vms_split_path() is the only way to do this, the
13051 existing method will fail with many legal EFS or UNIX specifications
13054 cp = SvPV(tmpglob,i);
13057 if (cp[i] == ';') hasver = 1;
13058 if (cp[i] == '.') {
13059 if (sts) hasver = 1;
13062 if (cp[i] == '/') {
13063 hasdir = isunix = 1;
13066 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13072 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13073 if ((hasdir == 0) && decc_filename_unix_report) {
13077 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13078 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13079 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13085 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13086 if (!stat_sts && S_ISDIR(st.st_mode)) {
13088 const char * fname;
13091 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13092 /* path delimiter of ':>]', if so, then the old behavior has */
13093 /* obviously been specifically requested */
13095 fname = SvPVX_const(tmpglob);
13096 fname_len = strlen(fname);
13097 vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13098 if (vms_old_glob || (vms_dir != NULL)) {
13099 wilddsc.dsc$a_pointer = tovmspath_utf8(
13100 SvPVX(tmpglob),vmsspec,NULL);
13101 ok = (wilddsc.dsc$a_pointer != NULL);
13102 /* maybe passed 'foo' rather than '[.foo]', thus not
13106 /* Operate just on the directory, the special stat/fstat for */
13107 /* leaves the fileified specification in the st_devnam */
13109 wilddsc.dsc$a_pointer = st.st_devnam;
13114 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13115 ok = (wilddsc.dsc$a_pointer != NULL);
13118 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13120 /* If not extended character set, replace ? with % */
13121 /* With extended character set, ? is a wildcard single character */
13122 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13125 if (!decc_efs_charset)
13127 } else if (*cp == '%') {
13129 } else if (*cp == '*') {
13135 wv_sts = vms_split_path(
13136 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13137 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13138 &wvs_spec, &wvs_len);
13147 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13148 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13149 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13153 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13154 &dfltdsc,NULL,&rms_sts,&lff_flags);
13155 if (!$VMS_STATUS_SUCCESS(sts))
13158 /* with varying string, 1st word of buffer contains result length */
13159 rstr[rslt->length] = '\0';
13161 /* Find where all the components are */
13162 v_sts = vms_split_path
13177 /* If no version on input, truncate the version on output */
13178 if (!hasver && (vs_len > 0)) {
13185 /* In Unix report mode, remove the ".dir;1" from the name */
13186 /* if it is a real directory */
13187 if (decc_filename_unix_report || decc_efs_charset) {
13188 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13192 ret_sts = flex_lstat(rstr, &statbuf);
13193 if ((ret_sts == 0) &&
13194 S_ISDIR(statbuf.st_mode)) {
13201 /* No version & a null extension on UNIX handling */
13202 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13208 if (!decc_efs_case_preserve) {
13209 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13212 /* Find File treats a Null extension as return all extensions */
13213 /* This is contrary to Perl expectations */
13215 if (wildstar || wildquery || vms_old_glob) {
13216 /* really need to see if the returned file name matched */
13217 /* but for now will assume that it matches */
13220 /* Exact Match requested */
13221 /* How are directories handled? - like a file */
13222 if ((e_len == we_len) && (n_len == wn_len)) {
13226 t1 = strncmp(e_spec, we_spec, e_len);
13230 t1 = strncmp(n_spec, we_spec, n_len);
13241 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13245 /* Start with the name */
13248 strcat(begin,"\n");
13249 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13252 if (cxt) (void)lib$find_file_end(&cxt);
13255 /* Be POSIXish: return the input pattern when no matches */
13256 my_strlcpy(rstr, SvPVX(tmpglob), VMS_MAXRSS);
13258 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13261 if (ok && sts != RMS$_NMF &&
13262 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13265 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13267 PerlIO_close(tmpfp);
13271 PerlIO_rewind(tmpfp);
13272 IoTYPE(io) = IoTYPE_RDONLY;
13273 IoIFP(io) = fp = tmpfp;
13274 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13284 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13288 unixrealpath_fromperl(pTHX_ CV *cv)
13291 char *fspec, *rslt_spec, *rslt;
13294 if (!items || items != 1)
13295 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13297 fspec = SvPV(ST(0),n_a);
13298 if (!fspec || !*fspec) XSRETURN_UNDEF;
13300 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13301 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13303 ST(0) = sv_newmortal();
13305 sv_usepvn(ST(0),rslt,strlen(rslt));
13307 Safefree(rslt_spec);
13312 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13316 vmsrealpath_fromperl(pTHX_ CV *cv)
13319 char *fspec, *rslt_spec, *rslt;
13322 if (!items || items != 1)
13323 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13325 fspec = SvPV(ST(0),n_a);
13326 if (!fspec || !*fspec) XSRETURN_UNDEF;
13328 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13329 rslt = do_vms_realname(fspec, rslt_spec, NULL);
13331 ST(0) = sv_newmortal();
13333 sv_usepvn(ST(0),rslt,strlen(rslt));
13335 Safefree(rslt_spec);
13341 * A thin wrapper around decc$symlink to make sure we follow the
13342 * standard and do not create a symlink with a zero-length name,
13343 * and convert the target to Unix format, as the CRTL can't handle
13344 * targets in VMS format.
13346 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13348 Perl_my_symlink(pTHX_ const char *contents, const char *link_name)
13353 if (!link_name || !*link_name) {
13354 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13358 utarget = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
13359 /* An untranslatable filename should be passed through. */
13360 (void) int_tounixspec(contents, utarget, NULL);
13361 sts = symlink(utarget, link_name);
13362 PerlMem_free(utarget);
13367 #endif /* HAS_SYMLINK */
13369 int do_vms_case_tolerant(void);
13372 case_tolerant_process_fromperl(pTHX_ CV *cv)
13375 ST(0) = boolSV(do_vms_case_tolerant());
13379 #ifdef USE_ITHREADS
13382 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
13383 struct interp_intern *dst)
13385 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13387 memcpy(dst,src,sizeof(struct interp_intern));
13393 Perl_sys_intern_clear(pTHX)
13398 Perl_sys_intern_init(pTHX)
13400 unsigned int ix = RAND_MAX;
13405 MY_POSIX_EXIT = vms_posix_exit;
13408 MY_INV_RAND_MAX = 1./x;
13412 init_os_extras(void)
13415 char* file = __FILE__;
13416 if (decc_disable_to_vms_logname_translation) {
13417 no_translate_barewords = TRUE;
13419 no_translate_barewords = FALSE;
13422 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
13423 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
13424 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
13425 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
13426 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
13427 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
13428 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
13429 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
13430 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
13431 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
13432 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
13433 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
13434 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
13435 newXSproto("VMS::Filespec::case_tolerant_process",
13436 case_tolerant_process_fromperl,file,"");
13438 store_pipelocs(aTHX); /* will redo any earlier attempts */
13443 #if __CRTL_VER == 80200000
13444 /* This missed getting in to the DECC SDK for 8.2 */
13445 char *realpath(const char *file_name, char * resolved_name, ...);
13448 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
13449 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
13450 * The perl fallback routine to provide realpath() is not as efficient
13458 /* Hack, use old stat() as fastest way of getting ino_t and device */
13459 int decc$stat(const char *name, void * statbuf);
13460 #if !defined(__VAX) && __CRTL_VER >= 80200000
13461 int decc$lstat(const char *name, void * statbuf);
13463 #define decc$lstat decc$stat
13471 /* Realpath is fragile. In 8.3 it does not work if the feature
13472 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
13473 * links are implemented in RMS, not the CRTL. It also can fail if the
13474 * user does not have read/execute access to some of the directories.
13475 * So in order for Do What I Mean mode to work, if realpath() fails,
13476 * fall back to looking up the filename by the device name and FID.
13479 int vms_fid_to_name(char * outname, int outlen,
13480 const char * name, int lstat_flag, mode_t * mode)
13482 #pragma message save
13483 #pragma message disable MISALGNDSTRCT
13484 #pragma message disable MISALGNDMEM
13485 #pragma member_alignment save
13486 #pragma nomember_alignment
13489 unsigned short st_ino[3];
13490 unsigned short old_st_mode;
13491 unsigned long padl[30]; /* plenty of room */
13493 #pragma message restore
13494 #pragma member_alignment restore
13497 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13498 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13503 /* Need to follow the mostly the same rules as flex_stat_int, or we may get
13504 * unexpected answers
13507 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
13508 if (fileified == NULL)
13509 _ckvmssts_noperl(SS$_INSFMEM);
13511 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
13512 if (temp_fspec == NULL)
13513 _ckvmssts_noperl(SS$_INSFMEM);
13516 /* First need to try as a directory */
13517 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13518 if (ret_spec != NULL) {
13519 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
13520 if (ret_spec != NULL) {
13521 if (lstat_flag == 0)
13522 sts = decc$stat(fileified, &statbuf);
13524 sts = decc$lstat(fileified, &statbuf);
13528 /* Then as a VMS file spec */
13530 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
13531 if (ret_spec != NULL) {
13532 if (lstat_flag == 0) {
13533 sts = decc$stat(temp_fspec, &statbuf);
13535 sts = decc$lstat(temp_fspec, &statbuf);
13541 /* Next try - allow multiple dots with out EFS CHARSET */
13542 /* The CRTL stat() falls down hard on multi-dot filenames in unix
13543 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
13544 * enable it if it isn't already.
13546 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13547 if (!decc_efs_charset && (decc_efs_charset_index > 0))
13548 decc$feature_set_value(decc_efs_charset_index, 1, 1);
13550 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13551 if (lstat_flag == 0) {
13552 sts = decc$stat(name, &statbuf);
13554 sts = decc$lstat(name, &statbuf);
13556 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13557 if (!decc_efs_charset && (decc_efs_charset_index > 0))
13558 decc$feature_set_value(decc_efs_charset_index, 1, 0);
13563 /* and then because the Perl Unix to VMS conversion is not perfect */
13564 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
13565 /* characters from filenames so we need to try it as-is */
13567 if (lstat_flag == 0) {
13568 sts = decc$stat(name, &statbuf);
13570 sts = decc$lstat(name, &statbuf);
13577 dvidsc.dsc$a_pointer=statbuf.st_dev;
13578 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
13580 specdsc.dsc$a_pointer = outname;
13581 specdsc.dsc$w_length = outlen-1;
13583 vms_sts = lib$fid_to_name
13584 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
13585 if ($VMS_STATUS_SUCCESS(vms_sts)) {
13586 outname[specdsc.dsc$w_length] = 0;
13588 /* Return the mode */
13590 *mode = statbuf.old_st_mode;
13594 PerlMem_free(temp_fspec);
13595 PerlMem_free(fileified);
13602 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
13605 char * rslt = NULL;
13608 if (decc_posix_compliant_pathnames > 0 ) {
13609 /* realpath currently only works if posix compliant pathnames are
13610 * enabled. It may start working when they are not, but in that
13611 * case we still want the fallback behavior for backwards compatibility
13613 rslt = realpath(filespec, outbuf);
13617 if (rslt == NULL) {
13619 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13620 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13623 /* Fall back to fid_to_name */
13625 Newx(vms_spec, VMS_MAXRSS + 1, char);
13627 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
13631 /* Now need to trim the version off */
13632 sts = vms_split_path
13652 /* Trim off the version */
13653 int file_len = v_len + r_len + d_len + n_len + e_len;
13654 vms_spec[file_len] = 0;
13656 /* Trim off the .DIR if this is a directory */
13657 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13658 if (S_ISDIR(my_mode)) {
13664 /* Drop NULL extensions on UNIX file specification */
13665 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13670 /* The result is expected to be in UNIX format */
13671 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
13673 /* Downcase if input had any lower case letters and
13674 * case preservation is not in effect.
13676 if (!decc_efs_case_preserve) {
13677 for (cp = filespec; *cp; cp++)
13678 if (islower(*cp)) { haslower = 1; break; }
13680 if (haslower) __mystrtolower(rslt);
13685 /* Now for some hacks to deal with backwards and forward */
13686 /* compatibility */
13687 if (!decc_efs_charset) {
13689 /* 1. ODS-2 mode wants to do a syntax only translation */
13690 rslt = int_rmsexpand(filespec, outbuf,
13691 NULL, 0, NULL, utf8_fl);
13694 if (decc_filename_unix_report) {
13696 char * vms_dir_name;
13699 /* 2. ODS-5 / UNIX report mode should return a failure */
13700 /* if the parent directory also does not exist */
13701 /* Otherwise, get the real path for the parent */
13702 /* and add the child to it. */
13704 /* basename / dirname only available for VMS 7.0+ */
13705 /* So we may need to implement them as common routines */
13707 Newx(dir_name, VMS_MAXRSS + 1, char);
13708 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
13709 dir_name[0] = '\0';
13712 /* First try a VMS parse */
13713 sts = vms_split_path
13731 int dir_len = v_len + r_len + d_len + n_len;
13733 memcpy(dir_name, filespec, dir_len);
13734 dir_name[dir_len] = '\0';
13735 file_name = (char *)&filespec[dir_len + 1];
13738 /* This must be UNIX */
13741 tchar = strrchr(filespec, '/');
13743 if (tchar != NULL) {
13744 int dir_len = tchar - filespec;
13745 memcpy(dir_name, filespec, dir_len);
13746 dir_name[dir_len] = '\0';
13747 file_name = (char *) &filespec[dir_len + 1];
13751 /* Dir name is defaulted */
13752 if (dir_name[0] == 0) {
13754 dir_name[1] = '\0';
13757 /* Need realpath for the directory */
13758 sts = vms_fid_to_name(vms_dir_name,
13760 dir_name, 0, NULL);
13763 /* Now need to pathify it. */
13764 char *tdir = int_pathify_dirspec(vms_dir_name,
13767 /* And now add the original filespec to it */
13768 if (file_name != NULL) {
13769 my_strlcat(outbuf, file_name, VMS_MAXRSS);
13773 Safefree(vms_dir_name);
13774 Safefree(dir_name);
13778 Safefree(vms_spec);
13784 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
13787 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13788 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13790 /* Fall back to fid_to_name */
13792 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
13799 /* Now need to trim the version off */
13800 sts = vms_split_path
13820 /* Trim off the version */
13821 int file_len = v_len + r_len + d_len + n_len + e_len;
13822 outbuf[file_len] = 0;
13824 /* Downcase if input had any lower case letters and
13825 * case preservation is not in effect.
13827 if (!decc_efs_case_preserve) {
13828 for (cp = filespec; *cp; cp++)
13829 if (islower(*cp)) { haslower = 1; break; }
13831 if (haslower) __mystrtolower(outbuf);
13840 /* External entry points */
13841 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13842 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
13844 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13845 { return do_vms_realname(filespec, outbuf, utf8_fl); }
13847 /* case_tolerant */
13849 /*{{{int do_vms_case_tolerant(void)*/
13850 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
13851 * controlled by a process setting.
13853 int do_vms_case_tolerant(void)
13855 return vms_process_case_tolerant;
13858 /* External entry points */
13859 #if __CRTL_VER >= 70301000 && !defined(__VAX)
13860 int Perl_vms_case_tolerant(void)
13861 { return do_vms_case_tolerant(); }
13863 int Perl_vms_case_tolerant(void)
13864 { return vms_process_case_tolerant; }
13868 /* Start of DECC RTL Feature handling */
13870 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13873 set_feature_default(const char *name, int value)
13878 index = decc$feature_get_index(name);
13880 status = decc$feature_set_value(index, 1, value);
13881 if (index == -1 || (status == -1)) {
13885 status = decc$feature_get_value(index, 1);
13886 if (status != value) {
13890 /* Various things may check for an environment setting
13891 * rather than the feature directly, so set that too.
13893 vmssetuserlnm(name, value ? "ENABLE" : "DISABLE");
13900 /* C RTL Feature settings */
13902 #if defined(__DECC) || defined(__DECCXX)
13909 vmsperl_set_features(void)
13914 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
13915 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
13916 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
13917 unsigned long case_perm;
13918 unsigned long case_image;
13921 /* Allow an exception to bring Perl into the VMS debugger */
13922 vms_debug_on_exception = 0;
13923 status = simple_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
13924 if ($VMS_STATUS_SUCCESS(status)) {
13925 val_str[0] = _toupper(val_str[0]);
13926 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13927 vms_debug_on_exception = 1;
13929 vms_debug_on_exception = 0;
13932 /* Debug unix/vms file translation routines */
13933 vms_debug_fileify = 0;
13934 status = simple_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
13935 if ($VMS_STATUS_SUCCESS(status)) {
13936 val_str[0] = _toupper(val_str[0]);
13937 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13938 vms_debug_fileify = 1;
13940 vms_debug_fileify = 0;
13944 /* Historically PERL has been doing vmsify / stat differently than */
13945 /* the CRTL. In particular, under some conditions the CRTL will */
13946 /* remove some illegal characters like spaces from filenames */
13947 /* resulting in some differences. The stat()/lstat() wrapper has */
13948 /* been reporting such file names as invalid and fails to stat them */
13949 /* fixing this bug so that stat()/lstat() accept these like the */
13950 /* CRTL does will result in several tests failing. */
13951 /* This should really be fixed, but for now, set up a feature to */
13952 /* enable it so that the impact can be studied. */
13953 vms_bug_stat_filename = 0;
13954 status = simple_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
13955 if ($VMS_STATUS_SUCCESS(status)) {
13956 val_str[0] = _toupper(val_str[0]);
13957 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13958 vms_bug_stat_filename = 1;
13960 vms_bug_stat_filename = 0;
13964 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
13965 vms_vtf7_filenames = 0;
13966 status = simple_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
13967 if ($VMS_STATUS_SUCCESS(status)) {
13968 val_str[0] = _toupper(val_str[0]);
13969 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13970 vms_vtf7_filenames = 1;
13972 vms_vtf7_filenames = 0;
13975 /* unlink all versions on unlink() or rename() */
13976 vms_unlink_all_versions = 0;
13977 status = simple_trnlnm
13978 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
13979 if ($VMS_STATUS_SUCCESS(status)) {
13980 val_str[0] = _toupper(val_str[0]);
13981 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13982 vms_unlink_all_versions = 1;
13984 vms_unlink_all_versions = 0;
13987 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13988 /* Detect running under GNV Bash or other UNIX like shell */
13989 gnv_unix_shell = 0;
13990 status = simple_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
13991 if ($VMS_STATUS_SUCCESS(status)) {
13992 gnv_unix_shell = 1;
13993 set_feature_default("DECC$EFS_CHARSET", 1);
13994 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
13995 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
13996 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
13997 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
13998 vms_unlink_all_versions = 1;
13999 vms_posix_exit = 1;
14001 /* Some reasonable defaults that are not CRTL defaults */
14002 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14005 /* hacks to see if known bugs are still present for testing */
14007 /* PCP mode requires creating /dev/null special device file */
14008 decc_bug_devnull = 0;
14009 status = simple_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14010 if ($VMS_STATUS_SUCCESS(status)) {
14011 val_str[0] = _toupper(val_str[0]);
14012 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14013 decc_bug_devnull = 1;
14015 decc_bug_devnull = 0;
14018 /* UNIX directory names with no paths are broken in a lot of places */
14019 decc_dir_barename = 1;
14020 status = simple_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
14021 if ($VMS_STATUS_SUCCESS(status)) {
14022 val_str[0] = _toupper(val_str[0]);
14023 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14024 decc_dir_barename = 1;
14026 decc_dir_barename = 0;
14029 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14030 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14032 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14033 if (decc_disable_to_vms_logname_translation < 0)
14034 decc_disable_to_vms_logname_translation = 0;
14037 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14039 decc_efs_case_preserve = decc$feature_get_value(s, 1);
14040 if (decc_efs_case_preserve < 0)
14041 decc_efs_case_preserve = 0;
14044 s = decc$feature_get_index("DECC$EFS_CHARSET");
14045 decc_efs_charset_index = s;
14047 decc_efs_charset = decc$feature_get_value(s, 1);
14048 if (decc_efs_charset < 0)
14049 decc_efs_charset = 0;
14052 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14054 decc_filename_unix_report = decc$feature_get_value(s, 1);
14055 if (decc_filename_unix_report > 0) {
14056 decc_filename_unix_report = 1;
14057 vms_posix_exit = 1;
14060 decc_filename_unix_report = 0;
14063 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14065 decc_filename_unix_only = decc$feature_get_value(s, 1);
14066 if (decc_filename_unix_only > 0) {
14067 decc_filename_unix_only = 1;
14070 decc_filename_unix_only = 0;
14074 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14076 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14077 if (decc_filename_unix_no_version < 0)
14078 decc_filename_unix_no_version = 0;
14081 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14083 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14084 if (decc_readdir_dropdotnotype < 0)
14085 decc_readdir_dropdotnotype = 0;
14088 #if __CRTL_VER >= 80200000
14089 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14091 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14092 if (decc_posix_compliant_pathnames < 0)
14093 decc_posix_compliant_pathnames = 0;
14094 if (decc_posix_compliant_pathnames > 4)
14095 decc_posix_compliant_pathnames = 0;
14100 status = simple_trnlnm
14101 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14102 if ($VMS_STATUS_SUCCESS(status)) {
14103 val_str[0] = _toupper(val_str[0]);
14104 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14105 decc_disable_to_vms_logname_translation = 1;
14110 status = simple_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14111 if ($VMS_STATUS_SUCCESS(status)) {
14112 val_str[0] = _toupper(val_str[0]);
14113 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14114 decc_efs_case_preserve = 1;
14119 status = simple_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14120 if ($VMS_STATUS_SUCCESS(status)) {
14121 val_str[0] = _toupper(val_str[0]);
14122 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14123 decc_filename_unix_report = 1;
14126 status = simple_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14127 if ($VMS_STATUS_SUCCESS(status)) {
14128 val_str[0] = _toupper(val_str[0]);
14129 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14130 decc_filename_unix_only = 1;
14131 decc_filename_unix_report = 1;
14134 status = simple_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14135 if ($VMS_STATUS_SUCCESS(status)) {
14136 val_str[0] = _toupper(val_str[0]);
14137 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14138 decc_filename_unix_no_version = 1;
14141 status = simple_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14142 if ($VMS_STATUS_SUCCESS(status)) {
14143 val_str[0] = _toupper(val_str[0]);
14144 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14145 decc_readdir_dropdotnotype = 1;
14150 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14152 /* Report true case tolerance */
14153 /*----------------------------*/
14154 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14155 if (!$VMS_STATUS_SUCCESS(status))
14156 case_perm = PPROP$K_CASE_BLIND;
14157 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14158 if (!$VMS_STATUS_SUCCESS(status))
14159 case_image = PPROP$K_CASE_BLIND;
14160 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14161 (case_image == PPROP$K_CASE_SENSITIVE))
14162 vms_process_case_tolerant = 0;
14166 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
14167 /* for strict backward compatibility */
14168 status = simple_trnlnm
14169 ("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14170 if ($VMS_STATUS_SUCCESS(status)) {
14171 val_str[0] = _toupper(val_str[0]);
14172 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14173 vms_posix_exit = 1;
14175 vms_posix_exit = 0;
14179 /* Use 32-bit pointers because that's what the image activator
14180 * assumes for the LIB$INITIALZE psect.
14182 #if __INITIAL_POINTER_SIZE
14183 #pragma pointer_size save
14184 #pragma pointer_size 32
14187 /* Create a reference to the LIB$INITIALIZE function. */
14188 extern void LIB$INITIALIZE(void);
14189 extern void (*vmsperl_unused_global_1)(void) = LIB$INITIALIZE;
14191 /* Create an array of pointers to the init functions in the special
14192 * LIB$INITIALIZE section. In our case, the array only has one entry.
14194 #pragma extern_model save
14195 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic,gbl,nowrt,noshr,long
14196 extern void (* const vmsperl_unused_global_2[])() =
14198 vmsperl_set_features,
14200 #pragma extern_model restore
14202 #if __INITIAL_POINTER_SIZE
14203 #pragma pointer_size restore
14210 #endif /* defined(__DECC) || defined(__DECCXX) */