3 * VMS-specific routines for perl5
5 * Copyright (C) 1993-2015 by Charles Bailey and others.
7 * You may distribute under the terms of either the GNU General Public
8 * License or the Artistic License, as specified in the README file.
12 * Yet small as was their hunted band
13 * still fell and fearless was each hand,
14 * and strong deeds they wrought yet oft,
15 * and loved the woods, whose ways more soft
16 * them seemed than thralls of that black throne
17 * to live and languish in halls of stone.
18 * "The Lay of Leithian", Canto II, lines 135-40
20 * [p.162 of _The Lays of Beleriand_]
28 #include <climsgdef.h>
38 #include <libclidef.h>
40 #include <lib$routines.h>
52 #include <str$routines.h>
58 #define NO_EFN EFN$C_ENF
62 #pragma member_alignment save
63 #pragma nomember_alignment longword
68 unsigned short * retadr;
70 #pragma member_alignment restore
72 /* Older versions of ssdef.h don't have these */
73 #ifndef SS$_INVFILFOROP
74 # define SS$_INVFILFOROP 3930
76 #ifndef SS$_NOSUCHOBJECT
77 # define SS$_NOSUCHOBJECT 2696
80 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
81 #define PERLIO_NOT_STDIO 0
83 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
84 * code below needs to get to the underlying CRTL routines. */
85 #define DONT_MASK_RTL_CALLS
89 /* Anticipating future expansion in lexical warnings . . . */
91 # define WARN_INTERNAL WARN_MISC
94 #ifdef VMS_LONGNAME_SUPPORT
95 #include <libfildef.h>
98 #if __CRTL_VER >= 80200000
106 #define lstat(_x, _y) stat(_x, _y)
109 /* Routine to create a decterm for use with the Perl debugger */
110 /* No headers, this information was found in the Programming Concepts Manual */
112 static int (*decw_term_port)
113 (const struct dsc$descriptor_s * display,
114 const struct dsc$descriptor_s * setup_file,
115 const struct dsc$descriptor_s * customization,
116 struct dsc$descriptor_s * result_device_name,
117 unsigned short * result_device_name_length,
120 void * char_change_buffer) = 0;
122 #if defined(NEED_AN_H_ERRNO)
126 #if defined(__DECC) || defined(__DECCXX)
127 #pragma member_alignment save
128 #pragma nomember_alignment longword
130 #pragma message disable misalgndmem
133 unsigned short int buflen;
134 unsigned short int itmcode;
136 unsigned short int *retlen;
139 struct filescan_itmlst_2 {
140 unsigned short length;
141 unsigned short itmcode;
146 unsigned short length;
147 char str[VMS_MAXRSS];
148 unsigned short pad; /* for longword struct alignment */
151 #if defined(__DECC) || defined(__DECCXX)
152 #pragma message restore
153 #pragma member_alignment restore
156 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
157 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
158 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
159 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
160 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
161 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
162 #define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c)
163 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
164 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
165 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
166 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
167 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
169 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
170 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
171 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
172 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
174 static char * int_rmsexpand_vms(
175 const char * filespec, char * outbuf, unsigned opts);
176 static char * int_rmsexpand_tovms(
177 const char * filespec, char * outbuf, unsigned opts);
178 static char *int_tovmsspec
179 (const char *path, char *buf, int dir_flag, int * utf8_flag);
180 static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl);
181 static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
182 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl);
184 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
185 #define PERL_LNM_MAX_ALLOWED_INDEX 127
187 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
188 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
191 #define PERL_LNM_MAX_ITER 10
193 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
194 #define MAX_DCL_SYMBOL (8192)
195 #define MAX_DCL_LINE_LENGTH (4096 - 4)
197 static char *__mystrtolower(char *str)
199 if (str) for (; *str; ++str) *str= toLOWER_L1(*str);
203 static struct dsc$descriptor_s fildevdsc =
204 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
205 static struct dsc$descriptor_s crtlenvdsc =
206 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
207 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
208 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
209 static struct dsc$descriptor_s **env_tables = defenv;
210 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
212 /* True if we shouldn't treat barewords as logicals during directory */
214 static int no_translate_barewords;
216 /* DECC feature indexes. We grab the indexes at start-up
217 * time for later use with decc$feature_get_value.
219 static int disable_to_vms_logname_translation_index = -1;
220 static int disable_posix_root_index = -1;
221 static int efs_case_preserve_index = -1;
222 static int efs_charset_index = -1;
223 static int filename_unix_no_version_index = -1;
224 static int filename_unix_only_index = -1;
225 static int filename_unix_report_index = -1;
226 static int posix_compliant_pathnames_index = -1;
227 static int readdir_dropdotnotype_index = -1;
229 #define DECC_DISABLE_TO_VMS_LOGNAME_TRANSLATION \
230 (decc$feature_get_value(disable_to_vms_logname_translation_index,__FEATURE_MODE_CURVAL)>0)
231 #define DECC_DISABLE_POSIX_ROOT \
232 (decc$feature_get_value(disable_posix_root_index,__FEATURE_MODE_CURVAL)>0)
233 #define DECC_EFS_CASE_PRESERVE \
234 (decc$feature_get_value(efs_case_preserve_index,__FEATURE_MODE_CURVAL)>0)
235 #define DECC_EFS_CHARSET \
236 (decc$feature_get_value(efs_charset_index,__FEATURE_MODE_CURVAL)>0)
237 #define DECC_FILENAME_UNIX_NO_VERSION \
238 (decc$feature_get_value(filename_unix_no_version_index,__FEATURE_MODE_CURVAL)>0)
239 #define DECC_FILENAME_UNIX_ONLY \
240 (decc$feature_get_value(filename_unix_only_index,__FEATURE_MODE_CURVAL)>0)
241 #define DECC_FILENAME_UNIX_REPORT \
242 (decc$feature_get_value(filename_unix_report_index,__FEATURE_MODE_CURVAL)>0)
243 #define DECC_POSIX_COMPLIANT_PATHNAMES \
244 (decc$feature_get_value(posix_compliant_pathnames_index,__FEATURE_MODE_CURVAL)>0)
245 #define DECC_READDIR_DROPDOTNOTYPE \
246 (decc$feature_get_value(readdir_dropdotnotype_index,__FEATURE_MODE_CURVAL)>0)
248 static int vms_process_case_tolerant = 1;
249 int vms_vtf7_filenames = 0;
250 int gnv_unix_shell = 0;
251 static int vms_unlink_all_versions = 0;
252 static int vms_posix_exit = 0;
254 /* bug workarounds if needed */
255 int decc_bug_devnull = 1;
256 int vms_bug_stat_filename = 0;
258 static int vms_debug_on_exception = 0;
259 static int vms_debug_fileify = 0;
261 /* Simple logical name translation */
263 simple_trnlnm(const char * logname, char * value, int value_len)
265 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
266 const unsigned long attr = LNM$M_CASE_BLIND;
267 struct dsc$descriptor_s name_dsc;
269 unsigned short result;
270 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
273 name_dsc.dsc$w_length = strlen(logname);
274 name_dsc.dsc$a_pointer = (char *)logname;
275 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
276 name_dsc.dsc$b_class = DSC$K_CLASS_S;
278 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
280 if ($VMS_STATUS_SUCCESS(status)) {
282 /* Null terminate and return the string */
283 /*--------------------------------------*/
292 /* Is this a UNIX file specification?
293 * No longer a simple check with EFS file specs
294 * For now, not a full check, but need to
295 * handle POSIX ^UP^ specifications
296 * Fixing to handle ^/ cases would require
297 * changes to many other conversion routines.
301 is_unix_filespec(const char *path)
307 if (! strBEGINs(path,"\"^UP^")) {
308 pch1 = strchr(path, '/');
313 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
314 if (DECC_FILENAME_UNIX_REPORT || DECC_FILENAME_UNIX_ONLY) {
323 /* This routine converts a UCS-2 character to be VTF-7 encoded.
327 ucs2_to_vtf7(char *outspec, unsigned long ucs2_char, int * output_cnt)
329 unsigned char * ucs_ptr;
332 ucs_ptr = (unsigned char *)&ucs2_char;
336 hex = (ucs_ptr[1] >> 4) & 0xf;
338 outspec[2] = hex + '0';
340 outspec[2] = (hex - 9) + 'A';
341 hex = ucs_ptr[1] & 0xF;
343 outspec[3] = hex + '0';
345 outspec[3] = (hex - 9) + 'A';
347 hex = (ucs_ptr[0] >> 4) & 0xf;
349 outspec[4] = hex + '0';
351 outspec[4] = (hex - 9) + 'A';
352 hex = ucs_ptr[1] & 0xF;
354 outspec[5] = hex + '0';
356 outspec[5] = (hex - 9) + 'A';
362 /* This handles the conversion of a UNIX extended character set to a ^
363 * escaped VMS character.
364 * in a UNIX file specification.
366 * The output count variable contains the number of characters added
367 * to the output string.
369 * The return value is the number of characters read from the input string
372 copy_expand_unix_filename_escape(char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
379 utf8_flag = *utf8_fl;
383 if (*inspec >= 0x80) {
384 if (utf8_fl && vms_vtf7_filenames) {
385 unsigned long ucs_char;
389 if ((*inspec & 0xE0) == 0xC0) {
391 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
392 if (ucs_char >= 0x80) {
393 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
396 } else if ((*inspec & 0xF0) == 0xE0) {
398 ucs_char = ((inspec[0] & 0xF) << 12) +
399 ((inspec[1] & 0x3f) << 6) +
401 if (ucs_char >= 0x800) {
402 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
406 #if 0 /* I do not see longer sequences supported by OpenVMS */
407 /* Maybe some one can fix this later */
408 } else if ((*inspec & 0xF8) == 0xF0) {
411 } else if ((*inspec & 0xFC) == 0xF8) {
414 } else if ((*inspec & 0xFE) == 0xFC) {
421 /* High bit set, but not a Unicode character! */
423 /* Non printing DECMCS or ISO Latin-1 character? */
424 if ((unsigned char)*inspec <= 0x9F) {
428 hex = (*inspec >> 4) & 0xF;
430 outspec[1] = hex + '0';
432 outspec[1] = (hex - 9) + 'A';
436 outspec[2] = hex + '0';
438 outspec[2] = (hex - 9) + 'A';
442 } else if ((unsigned char)*inspec == 0xA0) {
448 } else if ((unsigned char)*inspec == 0xFF) {
460 /* Is this a macro that needs to be passed through?
461 * Macros start with $( and an alpha character, followed
462 * by a string of alpha numeric characters ending with a )
463 * If this does not match, then encode it as ODS-5.
465 if ((inspec[0] == '$') && (inspec[1] == '(')) {
468 if (isALPHA_L1(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
470 outspec[0] = inspec[0];
471 outspec[1] = inspec[1];
472 outspec[2] = inspec[2];
474 while(isALPHA_L1(inspec[tcnt]) ||
475 (inspec[2] == '.') || (inspec[2] == '_')) {
476 outspec[tcnt] = inspec[tcnt];
479 if (inspec[tcnt] == ')') {
480 outspec[tcnt] = inspec[tcnt];
497 if (!DECC_EFS_CHARSET)
523 /* Don't escape again if following character is
524 * already something we escape.
526 if (strchr(".!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
532 /* But otherwise fall through and escape it. */
534 /* Assume that this is to be escaped */
536 outspec[1] = *inspec;
540 case ' ': /* space */
541 /* Assume that this is to be escaped */
557 /* This handles the expansion of a '^' prefix to the proper character
558 * in a UNIX file specification.
560 * The output count variable contains the number of characters added
561 * to the output string.
563 * The return value is the number of characters read from the input
567 copy_expand_vms_filename_escape(char *outspec, const char *inspec, int *output_cnt)
574 if (*inspec == '^') {
577 /* Spaces and non-trailing dots should just be passed through,
578 * but eat the escape character.
585 case '_': /* space */
591 /* Hmm. Better leave the escape escaped. */
597 case 'U': /* Unicode - FIX-ME this is wrong. */
600 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
603 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
604 outspec[0] = c1 & 0xff;
605 outspec[1] = c2 & 0xff;
612 /* Error - do best we can to continue */
622 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
626 scnt = sscanf(inspec, "%2x", &c1);
627 outspec[0] = c1 & 0xff;
648 /* vms_split_path - Verify that the input file specification is a
649 * VMS format file specification, and provide pointers to the components of
650 * it. With EFS format filenames, this is virtually the only way to
651 * parse a VMS path specification into components.
653 * If the sum of the components do not add up to the length of the
654 * string, then the passed file specification is probably a UNIX style
658 vms_split_path(const char * path, char * * volume, int * vol_len, char * * root, int * root_len,
659 char * * dir, int * dir_len, char * * name, int * name_len,
660 char * * ext, int * ext_len, char * * version, int * ver_len)
662 struct dsc$descriptor path_desc;
666 struct filescan_itmlst_2 item_list[9];
667 const int filespec = 0;
668 const int nodespec = 1;
669 const int devspec = 2;
670 const int rootspec = 3;
671 const int dirspec = 4;
672 const int namespec = 5;
673 const int typespec = 6;
674 const int verspec = 7;
676 /* Assume the worst for an easy exit */
690 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
691 path_desc.dsc$w_length = strlen(path);
692 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
693 path_desc.dsc$b_class = DSC$K_CLASS_S;
695 /* Get the total length, if it is shorter than the string passed
696 * then this was probably not a VMS formatted file specification
698 item_list[filespec].itmcode = FSCN$_FILESPEC;
699 item_list[filespec].length = 0;
700 item_list[filespec].component = NULL;
702 /* If the node is present, then it gets considered as part of the
703 * volume name to hopefully make things simple.
705 item_list[nodespec].itmcode = FSCN$_NODE;
706 item_list[nodespec].length = 0;
707 item_list[nodespec].component = NULL;
709 item_list[devspec].itmcode = FSCN$_DEVICE;
710 item_list[devspec].length = 0;
711 item_list[devspec].component = NULL;
713 /* root is a special case, adding it to either the directory or
714 * the device components will probably complicate things for the
715 * callers of this routine, so leave it separate.
717 item_list[rootspec].itmcode = FSCN$_ROOT;
718 item_list[rootspec].length = 0;
719 item_list[rootspec].component = NULL;
721 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
722 item_list[dirspec].length = 0;
723 item_list[dirspec].component = NULL;
725 item_list[namespec].itmcode = FSCN$_NAME;
726 item_list[namespec].length = 0;
727 item_list[namespec].component = NULL;
729 item_list[typespec].itmcode = FSCN$_TYPE;
730 item_list[typespec].length = 0;
731 item_list[typespec].component = NULL;
733 item_list[verspec].itmcode = FSCN$_VERSION;
734 item_list[verspec].length = 0;
735 item_list[verspec].component = NULL;
737 item_list[8].itmcode = 0;
738 item_list[8].length = 0;
739 item_list[8].component = NULL;
741 status = sys$filescan
742 ((const struct dsc$descriptor_s *)&path_desc, item_list,
744 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
746 /* If we parsed it successfully these two lengths should be the same */
747 if (path_desc.dsc$w_length != item_list[filespec].length)
750 /* If we got here, then it is a VMS file specification */
753 /* set the volume name */
754 if (item_list[nodespec].length > 0) {
755 *volume = item_list[nodespec].component;
756 *vol_len = item_list[nodespec].length + item_list[devspec].length;
759 *volume = item_list[devspec].component;
760 *vol_len = item_list[devspec].length;
763 *root = item_list[rootspec].component;
764 *root_len = item_list[rootspec].length;
766 *dir = item_list[dirspec].component;
767 *dir_len = item_list[dirspec].length;
769 /* Now fun with versions and EFS file specifications
770 * The parser can not tell the difference when a "." is a version
771 * delimiter or a part of the file specification.
773 if ((DECC_EFS_CHARSET) &&
774 (item_list[verspec].length > 0) &&
775 (item_list[verspec].component[0] == '.')) {
776 *name = item_list[namespec].component;
777 *name_len = item_list[namespec].length + item_list[typespec].length;
778 *ext = item_list[verspec].component;
779 *ext_len = item_list[verspec].length;
784 *name = item_list[namespec].component;
785 *name_len = item_list[namespec].length;
786 *ext = item_list[typespec].component;
787 *ext_len = item_list[typespec].length;
788 *version = item_list[verspec].component;
789 *ver_len = item_list[verspec].length;
794 /* Routine to determine if the file specification ends with .dir */
796 is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len)
799 /* e_len must be 4, and version must be <= 2 characters */
800 if (e_len != 4 || vs_len > 2)
803 /* If a version number is present, it needs to be one */
804 if ((vs_len == 2) && (vs_spec[1] != '1'))
807 /* Look for the DIR on the extension */
808 if (vms_process_case_tolerant) {
809 if ((toUPPER_A(e_spec[1]) == 'D') &&
810 (toUPPER_A(e_spec[2]) == 'I') &&
811 (toUPPER_A(e_spec[3]) == 'R')) {
815 /* Directory extensions are supposed to be in upper case only */
816 /* I would not be surprised if this rule can not be enforced */
817 /* if and when someone fully debugs the case sensitive mode */
818 if ((e_spec[1] == 'D') &&
819 (e_spec[2] == 'I') &&
820 (e_spec[3] == 'R')) {
829 * Routine to retrieve the maximum equivalence index for an input
830 * logical name. Some calls to this routine have no knowledge if
831 * the variable is a logical or not. So on error we return a max
834 /*{{{int my_maxidx(const char *lnm) */
836 my_maxidx(const char *lnm)
840 int attr = LNM$M_CASE_BLIND;
841 struct dsc$descriptor lnmdsc;
842 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
845 lnmdsc.dsc$w_length = strlen(lnm);
846 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
847 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
848 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
850 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
851 if ((status & 1) == 0)
858 /* Routine to remove the 2-byte prefix from the translation of a
859 * process-permanent file (PPF).
861 static inline unsigned short int
862 S_remove_ppf_prefix(const char *lnm, char *eqv, unsigned short int eqvlen)
864 if (*((int *)lnm) == *((int *)"SYS$") &&
865 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
866 ( (lnm[4] == 'O' && strEQ(lnm,"SYS$OUTPUT")) ||
867 (lnm[4] == 'I' && strEQ(lnm,"SYS$INPUT")) ||
868 (lnm[4] == 'E' && strEQ(lnm,"SYS$ERROR")) ||
869 (lnm[4] == 'C' && strEQ(lnm,"SYS$COMMAND")) ) ) {
871 memmove(eqv, eqv+4, eqvlen-4);
877 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
879 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
880 struct dsc$descriptor_s **tabvec, unsigned long int flags)
883 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
884 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
885 bool found_in_crtlenv = 0, found_in_clisym = 0;
886 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
888 unsigned char acmode;
889 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
890 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
891 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
892 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
894 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
895 #if defined(PERL_IMPLICIT_CONTEXT)
898 aTHX = PERL_GET_INTERP;
904 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
905 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
907 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
908 *cp2 = toUPPER_A(*cp1);
909 if (cp1 - lnm > LNM$C_NAMLENGTH) {
910 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
914 lnmdsc.dsc$w_length = cp1 - lnm;
915 lnmdsc.dsc$a_pointer = uplnm;
916 uplnm[lnmdsc.dsc$w_length] = '\0';
917 secure = flags & PERL__TRNENV_SECURE;
918 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
919 if (!tabvec || !*tabvec) tabvec = env_tables;
921 for (curtab = 0; tabvec[curtab]; curtab++) {
922 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
923 if (!ivenv && !secure) {
928 #if defined(PERL_IMPLICIT_CONTEXT)
931 "Can't read CRTL environ\n");
934 Perl_warn(aTHX_ "Can't read CRTL environ\n");
937 retsts = SS$_NOLOGNAM;
938 for (i = 0; environ[i]; i++) {
939 if ((eq = strchr(environ[i],'=')) &&
940 lnmdsc.dsc$w_length == (eq - environ[i]) &&
941 strnEQ(environ[i],lnm,eq - environ[i])) {
943 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
944 if (!eqvlen) continue;
949 if (retsts != SS$_NOLOGNAM) {
950 found_in_crtlenv = 1;
955 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
956 !str$case_blind_compare(&tmpdsc,&clisym)) {
957 if (!ivsym && !secure) {
958 unsigned short int deflen = LNM$C_NAMLENGTH;
959 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
960 /* dynamic dsc to accommodate possible long value */
961 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
962 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
964 if (eqvlen > MAX_DCL_SYMBOL) {
965 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
966 eqvlen = MAX_DCL_SYMBOL;
967 /* Special hack--we might be called before the interpreter's */
968 /* fully initialized, in which case either thr or PL_curcop */
969 /* might be bogus. We have to check, since ckWARN needs them */
970 /* both to be valid if running threaded */
971 #if defined(PERL_IMPLICIT_CONTEXT)
974 "Value of CLI symbol \"%s\" too long",lnm);
977 if (ckWARN(WARN_MISC)) {
978 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
981 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
983 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
984 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
985 if (retsts == LIB$_NOSUCHSYM) continue;
991 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
992 midx = my_maxidx(lnm);
993 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
994 lnmlst[1].bufadr = cp2;
996 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
997 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
998 if (retsts == SS$_NOLOGNAM) break;
999 eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen);
1003 if ((retsts == SS$_IVLOGNAM) ||
1004 (retsts == SS$_NOLOGNAM)) { continue; }
1005 eqvlen = strlen(eqv);
1008 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1009 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1010 if (retsts == SS$_NOLOGNAM) continue;
1011 eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen);
1017 /* An index only makes sense for logical names, so make sure we aren't
1018 * iterating over an index for an environ var or DCL symbol and getting
1019 * the same answer ad infinitum.
1021 if (idx > 0 && (found_in_crtlenv || found_in_clisym)) {
1024 else if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1025 else if (retsts == LIB$_NOSUCHSYM ||
1026 retsts == SS$_NOLOGNAM) {
1027 /* Unsuccessful lookup is normal -- no need to set errno */
1030 else if (retsts == LIB$_INVSYMNAM ||
1031 retsts == SS$_IVLOGNAM ||
1032 retsts == SS$_IVLOGTAB) {
1033 set_errno(EINVAL); set_vaxc_errno(retsts);
1035 else _ckvmssts_noperl(retsts);
1037 } /* end of vmstrnenv */
1040 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1041 /* Define as a function so we can access statics. */
1043 Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1047 #if defined(PERL_IMPLICIT_CONTEXT)
1050 #ifdef SECURE_INTERNAL_GETENV
1051 flags = (PL_curinterp ? TAINTING_get : will_taint) ?
1052 PERL__TRNENV_SECURE : 0;
1055 return vmstrnenv(lnm, eqv, idx, fildev, flags);
1060 * Note: Uses Perl temp to store result so char * can be returned to
1061 * caller; this pointer will be invalidated at next Perl statement
1063 * We define this as a function rather than a macro in terms of my_getenv_len()
1064 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1067 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1069 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1072 static char *__my_getenv_eqv = NULL;
1073 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1074 unsigned long int idx = 0;
1075 int success, secure;
1079 midx = my_maxidx(lnm) + 1;
1081 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1082 /* Set up a temporary buffer for the return value; Perl will
1083 * clean it up at the next statement transition */
1084 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1085 if (!tmpsv) return NULL;
1089 /* Assume no interpreter ==> single thread */
1090 if (__my_getenv_eqv != NULL) {
1091 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1094 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1096 eqv = __my_getenv_eqv;
1099 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = toUPPER_A(*cp1);
1100 if (memEQs(eqv, cp1 - lnm, "DEFAULT")) {
1102 getcwd(eqv,LNM$C_NAMLENGTH);
1106 /* Get rid of "000000/ in rooted filespecs */
1109 zeros = strstr(eqv, "/000000/");
1110 if (zeros != NULL) {
1112 mlen = len - (zeros - eqv) - 7;
1113 memmove(zeros, &zeros[7], mlen);
1121 /* Impose security constraints only if tainting */
1123 /* Impose security constraints only if tainting */
1124 secure = PL_curinterp ? TAINTING_get : will_taint;
1131 #ifdef SECURE_INTERNAL_GETENV
1132 secure ? PERL__TRNENV_SECURE : 0
1138 /* For the getenv interface we combine all the equivalence names
1139 * of a search list logical into one value to acquire a maximum
1140 * value length of 255*128 (assuming %ENV is using logicals).
1142 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1144 /* If the name contains a semicolon-delimited index, parse it
1145 * off and make sure we only retrieve the equivalence name for
1147 if ((cp2 = strchr(lnm,';')) != NULL) {
1148 my_strlcpy(uplnm, lnm, cp2 - lnm + 1);
1149 idx = strtoul(cp2+1,NULL,0);
1151 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1154 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1156 return success ? eqv : NULL;
1159 } /* end of my_getenv() */
1163 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1165 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1169 unsigned long idx = 0;
1171 static char *__my_getenv_len_eqv = NULL;
1175 midx = my_maxidx(lnm) + 1;
1177 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1178 /* Set up a temporary buffer for the return value; Perl will
1179 * clean it up at the next statement transition */
1180 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1181 if (!tmpsv) return NULL;
1185 /* Assume no interpreter ==> single thread */
1186 if (__my_getenv_len_eqv != NULL) {
1187 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1190 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1192 buf = __my_getenv_len_eqv;
1195 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = toUPPER_A(*cp1);
1196 if (memEQs(buf, cp1 - lnm, "DEFAULT")) {
1199 getcwd(buf,LNM$C_NAMLENGTH);
1202 /* Get rid of "000000/ in rooted filespecs */
1204 zeros = strstr(buf, "/000000/");
1205 if (zeros != NULL) {
1207 mlen = *len - (zeros - buf) - 7;
1208 memmove(zeros, &zeros[7], mlen);
1217 /* Impose security constraints only if tainting */
1218 secure = PL_curinterp ? TAINTING_get : will_taint;
1225 #ifdef SECURE_INTERNAL_GETENV
1226 secure ? PERL__TRNENV_SECURE : 0
1232 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1234 if ((cp2 = strchr(lnm,';')) != NULL) {
1235 my_strlcpy(buf, lnm, cp2 - lnm + 1);
1236 idx = strtoul(cp2+1,NULL,0);
1238 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1241 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1243 /* Get rid of "000000/ in rooted filespecs */
1246 zeros = strstr(buf, "/000000/");
1247 if (zeros != NULL) {
1249 mlen = *len - (zeros - buf) - 7;
1250 memmove(zeros, &zeros[7], mlen);
1256 return *len ? buf : NULL;
1259 } /* end of my_getenv_len() */
1262 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1264 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1266 /*{{{ void prime_env_iter() */
1268 prime_env_iter(void)
1269 /* Fill the %ENV associative array with all logical names we can
1270 * find, in preparation for iterating over it.
1273 static int primed = 0;
1274 HV *seenhv = NULL, *envhv;
1276 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1277 unsigned short int chan;
1278 #ifndef CLI$M_TRUSTED
1279 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1281 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1282 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0;
1284 bool have_sym = FALSE, have_lnm = FALSE;
1285 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1286 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1287 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1288 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1289 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1290 #if defined(PERL_IMPLICIT_CONTEXT)
1293 #if defined(USE_ITHREADS)
1294 static perl_mutex primenv_mutex;
1295 MUTEX_INIT(&primenv_mutex);
1298 #if defined(PERL_IMPLICIT_CONTEXT)
1299 /* We jump through these hoops because we can be called at */
1300 /* platform-specific initialization time, which is before anything is */
1301 /* set up--we can't even do a plain dTHX since that relies on the */
1302 /* interpreter structure to be initialized */
1304 aTHX = PERL_GET_INTERP;
1306 /* we never get here because the NULL pointer will cause the */
1307 /* several of the routines called by this routine to access violate */
1309 /* This routine is only called by hv.c/hv_iterinit which has a */
1310 /* context, so the real fix may be to pass it through instead of */
1311 /* the hoops above */
1316 if (primed || !PL_envgv) return;
1317 MUTEX_LOCK(&primenv_mutex);
1318 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1319 envhv = GvHVn(PL_envgv);
1320 /* Perform a dummy fetch as an lval to insure that the hash table is
1321 * set up. Otherwise, the hv_store() will turn into a nullop. */
1322 (void) hv_fetchs(envhv,"DEFAULT",TRUE);
1324 for (i = 0; env_tables[i]; i++) {
1325 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1326 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1327 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1329 if (have_sym || have_lnm) {
1330 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1331 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1332 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1333 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1336 for (i--; i >= 0; i--) {
1337 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1340 /* Start at the end, so if there is a duplicate we keep the first one. */
1341 for (j = 0; environ[j]; j++);
1342 for (j--; j >= 0; j--) {
1343 if (!(start = strchr(environ[j],'='))) {
1344 if (ckWARN(WARN_INTERNAL))
1345 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1349 sv = newSVpv(start,0);
1351 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1356 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1357 !str$case_blind_compare(&tmpdsc,&clisym)) {
1358 my_strlcpy(cmd, "Show Symbol/Global *", sizeof(cmd));
1359 cmddsc.dsc$w_length = 20;
1360 if (env_tables[i]->dsc$w_length == 12 &&
1361 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1362 !str$case_blind_compare(&tmpdsc,&local)) my_strlcpy(cmd+12, "Local *", sizeof(cmd)-12);
1363 flags = defflags | CLI$M_NOLOGNAM;
1366 my_strlcpy(cmd, "Show Logical *", sizeof(cmd));
1367 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1368 my_strlcat(cmd," /Table=", sizeof(cmd));
1369 cmddsc.dsc$w_length = my_strlcat(cmd, env_tables[i]->dsc$a_pointer, sizeof(cmd));
1371 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1372 flags = defflags | CLI$M_NOCLISYM;
1375 /* Create a new subprocess to execute each command, to exclude the
1376 * remote possibility that someone could subvert a mbx or file used
1377 * to write multiple commands to a single subprocess.
1380 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1381 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1382 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1383 defflags &= ~CLI$M_TRUSTED;
1384 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1386 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1387 if (seenhv) SvREFCNT_dec(seenhv);
1390 char *cp1, *cp2, *key;
1391 unsigned long int sts, iosb[2], retlen, keylen;
1394 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1395 if (sts & 1) sts = iosb[0] & 0xffff;
1396 if (sts == SS$_ENDOFFILE) {
1398 while (substs == 0) { sys$hiber(); wakect++;}
1399 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1404 retlen = iosb[0] >> 16;
1405 if (!retlen) continue; /* blank line */
1407 if (iosb[1] != subpid) {
1409 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1413 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1414 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1416 for (cp1 = buf; *cp1 && isSPACE_L1(*cp1); cp1++) ;
1417 if (*cp1 == '(' || /* Logical name table name */
1418 *cp1 == '=' /* Next eqv of searchlist */) continue;
1419 if (*cp1 == '"') cp1++;
1420 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1421 key = cp1; keylen = cp2 - cp1;
1422 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1423 while (*cp2 && *cp2 != '=') cp2++;
1424 while (*cp2 && *cp2 == '=') cp2++;
1425 while (*cp2 && *cp2 == ' ') cp2++;
1426 if (*cp2 == '"') { /* String translation; may embed "" */
1427 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1428 cp2++; cp1--; /* Skip "" surrounding translation */
1430 else { /* Numeric translation */
1431 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1432 cp1--; /* stop on last non-space char */
1434 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1435 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1438 PERL_HASH(hash,key,keylen);
1440 if (cp1 == cp2 && *cp2 == '.') {
1441 /* A single dot usually means an unprintable character, such as a null
1442 * to indicate a zero-length value. Get the actual value to make sure.
1444 char lnm[LNM$C_NAMLENGTH+1];
1445 char eqv[MAX_DCL_SYMBOL+1];
1447 strncpy(lnm, key, keylen);
1448 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1449 sv = newSVpvn(eqv, strlen(eqv));
1452 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1456 hv_store(envhv,key,keylen,sv,hash);
1457 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1459 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1460 /* get the PPFs for this process, not the subprocess */
1461 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1462 char eqv[LNM$C_NAMLENGTH+1];
1464 for (i = 0; ppfs[i]; i++) {
1465 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1466 sv = newSVpv(eqv,trnlen);
1468 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1473 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1474 if (buf) Safefree(buf);
1475 if (seenhv) SvREFCNT_dec(seenhv);
1476 MUTEX_UNLOCK(&primenv_mutex);
1479 } /* end of prime_env_iter */
1483 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1484 /* Define or delete an element in the same "environment" as
1485 * vmstrnenv(). If an element is to be deleted, it's removed from
1486 * the first place it's found. If it's to be set, it's set in the
1487 * place designated by the first element of the table vector.
1488 * Like setenv() returns 0 for success, non-zero on error.
1491 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1494 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1495 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1497 unsigned long int retsts, usermode = PSL$C_USER;
1498 struct itmlst_3 *ile, *ilist;
1499 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1500 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1501 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1502 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1503 $DESCRIPTOR(local,"_LOCAL");
1506 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1507 return SS$_IVLOGNAM;
1510 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1511 *cp2 = toUPPER_A(*cp1);
1512 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1513 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1514 return SS$_IVLOGNAM;
1517 lnmdsc.dsc$w_length = cp1 - lnm;
1518 if (!tabvec || !*tabvec) tabvec = env_tables;
1520 if (!eqv) { /* we're deleting n element */
1521 for (curtab = 0; tabvec[curtab]; curtab++) {
1522 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1524 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1525 if ((cp1 = strchr(environ[i],'=')) &&
1526 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1527 strnEQ(environ[i],lnm,cp1 - environ[i])) {
1532 ivenv = 1; retsts = SS$_NOLOGNAM;
1534 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1535 !str$case_blind_compare(&tmpdsc,&clisym)) {
1536 unsigned int symtype;
1537 if (tabvec[curtab]->dsc$w_length == 12 &&
1538 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1539 !str$case_blind_compare(&tmpdsc,&local))
1540 symtype = LIB$K_CLI_LOCAL_SYM;
1541 else symtype = LIB$K_CLI_GLOBAL_SYM;
1542 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1543 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1544 if (retsts == LIB$_NOSUCHSYM) continue;
1548 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1549 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1550 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1551 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1552 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1556 else { /* we're defining a value */
1557 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1558 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1561 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1562 eqvdsc.dsc$w_length = strlen(eqv);
1563 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1564 !str$case_blind_compare(&tmpdsc,&clisym)) {
1565 unsigned int symtype;
1566 if (tabvec[0]->dsc$w_length == 12 &&
1567 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1568 !str$case_blind_compare(&tmpdsc,&local))
1569 symtype = LIB$K_CLI_LOCAL_SYM;
1570 else symtype = LIB$K_CLI_GLOBAL_SYM;
1571 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1574 if (!*eqv) eqvdsc.dsc$w_length = 1;
1575 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1577 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1578 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1579 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1580 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1581 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1582 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1585 Newx(ilist,nseg+1,struct itmlst_3);
1588 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1591 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1593 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1594 ile->itmcode = LNM$_STRING;
1596 if ((j+1) == nseg) {
1597 ile->buflen = strlen(c);
1598 /* in case we are truncating one that's too long */
1599 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1602 ile->buflen = LNM$C_NAMLENGTH;
1606 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1610 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1615 if (!(retsts & 1)) {
1617 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1618 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1619 set_errno(EVMSERR); break;
1620 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1621 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1622 set_errno(EINVAL); break;
1624 set_errno(EACCES); break;
1629 set_vaxc_errno(retsts);
1630 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1633 /* We reset error values on success because Perl does an hv_fetch()
1634 * before each hv_store(), and if the thing we're setting didn't
1635 * previously exist, we've got a leftover error message. (Of course,
1636 * this fails in the face of
1637 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1638 * in that the error reported in $! isn't spurious,
1639 * but it's right more often than not.)
1641 set_errno(0); set_vaxc_errno(retsts);
1645 } /* end of vmssetenv() */
1648 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1649 /* This has to be a function since there's a prototype for it in proto.h */
1651 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1654 int len = strlen(lnm);
1658 for (i = 0; lnm[i]; i++) uplnm[i] = toUPPER_A(lnm[i]);
1659 if (strEQ(uplnm,"DEFAULT")) {
1660 if (eqv && *eqv) my_chdir(eqv);
1665 (void) vmssetenv(lnm,eqv,NULL);
1669 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1671 * sets a user-mode logical in the process logical name table
1672 * used for redirection of sys$error
1675 Perl_vmssetuserlnm(const char *name, const char *eqv)
1677 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1678 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1679 unsigned long int iss, attr = LNM$M_CONFINE;
1680 unsigned char acmode = PSL$C_USER;
1681 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1683 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1684 d_name.dsc$w_length = strlen(name);
1686 lnmlst[0].buflen = strlen(eqv);
1687 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1689 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1690 if (!(iss&1)) lib$signal(iss);
1695 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1696 /* my_crypt - VMS password hashing
1697 * my_crypt() provides an interface compatible with the Unix crypt()
1698 * C library function, and uses sys$hash_password() to perform VMS
1699 * password hashing. The quadword hashed password value is returned
1700 * as a NUL-terminated 8 character string. my_crypt() does not change
1701 * the case of its string arguments; in order to match the behavior
1702 * of LOGINOUT et al., alphabetic characters in both arguments must
1703 * be upcased by the caller.
1705 * - fix me to call ACM services when available
1708 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1710 # ifndef UAI$C_PREFERRED_ALGORITHM
1711 # define UAI$C_PREFERRED_ALGORITHM 127
1713 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1714 unsigned short int salt = 0;
1715 unsigned long int sts;
1717 unsigned short int dsc$w_length;
1718 unsigned char dsc$b_type;
1719 unsigned char dsc$b_class;
1720 const char * dsc$a_pointer;
1721 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1722 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1723 struct itmlst_3 uailst[3] = {
1724 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1725 { sizeof salt, UAI$_SALT, &salt, 0},
1726 { 0, 0, NULL, NULL}};
1727 static char hash[9];
1729 usrdsc.dsc$w_length = strlen(usrname);
1730 usrdsc.dsc$a_pointer = usrname;
1731 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1733 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1737 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1742 set_vaxc_errno(sts);
1743 if (sts != RMS$_RNF) return NULL;
1746 txtdsc.dsc$w_length = strlen(textpasswd);
1747 txtdsc.dsc$a_pointer = textpasswd;
1748 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1749 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1752 return (char *) hash;
1754 } /* end of my_crypt() */
1758 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1759 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1760 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1762 /* 8.3, remove() is now broken on symbolic links */
1763 static int rms_erase(const char * vmsname);
1767 * A little hack to get around a bug in some implementation of remove()
1768 * that do not know how to delete a directory
1770 * Delete any file to which user has control access, regardless of whether
1771 * delete access is explicitly allowed.
1772 * Limitations: User must have write access to parent directory.
1773 * Does not block signals or ASTs; if interrupted in midstream
1774 * may leave file with an altered ACL.
1777 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1779 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1783 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1784 unsigned long int cxt = 0, aclsts, fndsts;
1786 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1788 unsigned char myace$b_length;
1789 unsigned char myace$b_type;
1790 unsigned short int myace$w_flags;
1791 unsigned long int myace$l_access;
1792 unsigned long int myace$l_ident;
1793 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1794 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1795 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1797 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1798 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1799 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1800 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1801 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1802 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1804 /* Expand the input spec using RMS, since the CRTL remove() and
1805 * system services won't do this by themselves, so we may miss
1806 * a file "hiding" behind a logical name or search list. */
1807 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
1808 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1810 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
1812 PerlMem_free(vmsname);
1816 /* Erase the file */
1817 rmsts = rms_erase(vmsname);
1819 /* Did it succeed */
1820 if ($VMS_STATUS_SUCCESS(rmsts)) {
1821 PerlMem_free(vmsname);
1825 /* If not, can changing protections help? */
1826 if (rmsts != RMS$_PRV) {
1827 set_vaxc_errno(rmsts);
1828 PerlMem_free(vmsname);
1832 /* No, so we get our own UIC to use as a rights identifier,
1833 * and the insert an ACE at the head of the ACL which allows us
1834 * to delete the file.
1836 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1837 fildsc.dsc$w_length = strlen(vmsname);
1838 fildsc.dsc$a_pointer = vmsname;
1840 newace.myace$l_ident = oldace.myace$l_ident;
1842 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1844 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1845 set_errno(ENOENT); break;
1847 set_errno(ENOTDIR); break;
1849 set_errno(ENODEV); break;
1850 case RMS$_SYN: case SS$_INVFILFOROP:
1851 set_errno(EINVAL); break;
1853 set_errno(EACCES); break;
1855 _ckvmssts_noperl(aclsts);
1857 set_vaxc_errno(aclsts);
1858 PerlMem_free(vmsname);
1861 /* Grab any existing ACEs with this identifier in case we fail */
1862 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1863 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1864 || fndsts == SS$_NOMOREACE ) {
1865 /* Add the new ACE . . . */
1866 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1869 rmsts = rms_erase(vmsname);
1870 if ($VMS_STATUS_SUCCESS(rmsts)) {
1875 /* We blew it - dir with files in it, no write priv for
1876 * parent directory, etc. Put things back the way they were. */
1877 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1880 addlst[0].bufadr = &oldace;
1881 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1888 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1889 /* We just deleted it, so of course it's not there. Some versions of
1890 * VMS seem to return success on the unlock operation anyhow (after all
1891 * the unlock is successful), but others don't.
1893 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1894 if (aclsts & 1) aclsts = fndsts;
1895 if (!(aclsts & 1)) {
1897 set_vaxc_errno(aclsts);
1900 PerlMem_free(vmsname);
1903 } /* end of kill_file() */
1907 /*{{{int do_rmdir(char *name)*/
1909 Perl_do_rmdir(pTHX_ const char *name)
1915 /* lstat returns a VMS fileified specification of the name */
1916 /* that is looked up, and also lets verifies that this is a directory */
1918 retval = flex_lstat(name, &st);
1922 /* Due to a historical feature, flex_stat/lstat can not see some */
1923 /* Unix format file names that the rest of the CRTL can see */
1924 /* Fixing that feature will cause some perl tests to fail */
1925 /* So try this one more time. */
1927 retval = lstat(name, &st.crtl_stat);
1931 /* force it to a file spec for the kill file to work. */
1932 ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
1933 if (ret_spec == NULL) {
1939 if (!S_ISDIR(st.st_mode)) {
1944 dirfile = st.st_devnam;
1946 /* It may be possible for flex_stat to find a file and vmsify() to */
1947 /* fail with ODS-2 specifications. mp_do_kill_file can not deal */
1948 /* with that case, so fail it */
1949 if (dirfile[0] == 0) {
1954 retval = mp_do_kill_file(aTHX_ dirfile, 1);
1959 } /* end of do_rmdir */
1963 * Delete any file to which user has control access, regardless of whether
1964 * delete access is explicitly allowed.
1965 * Limitations: User must have write access to parent directory.
1966 * Does not block signals or ASTs; if interrupted in midstream
1967 * may leave file with an altered ACL.
1970 /*{{{int kill_file(char *name)*/
1972 Perl_kill_file(pTHX_ const char *name)
1978 /* Convert the filename to VMS format and see if it is a directory */
1979 /* flex_lstat returns a vmsified file specification */
1980 rmsts = flex_lstat(name, &st);
1983 /* Due to a historical feature, flex_stat/lstat can not see some */
1984 /* Unix format file names that the rest of the CRTL can see when */
1985 /* ODS-2 file specifications are in use. */
1986 /* Fixing that feature will cause some perl tests to fail */
1987 /* [.lib.ExtUtils.t]Manifest.t is one of them */
1989 vmsfile = (char *) name; /* cast ok */
1992 vmsfile = st.st_devnam;
1993 if (vmsfile[0] == 0) {
1994 /* It may be possible for flex_stat to find a file and vmsify() */
1995 /* to fail with ODS-2 specifications. mp_do_kill_file can not */
1996 /* deal with that case, so fail it */
2002 /* Remove() is allowed to delete directories, according to the X/Open
2004 * This may need special handling to work with the ACL hacks.
2006 if (S_ISDIR(st.st_mode)) {
2007 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2011 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2013 /* Need to delete all versions ? */
2014 if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2017 /* Just use lstat() here as do not need st_dev */
2018 /* and we know that the file is in VMS format or that */
2019 /* because of a historical bug, flex_stat can not see the file */
2020 while (lstat(vmsfile, (stat_t *)&st) == 0) {
2021 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2026 /* Make sure that we do not loop forever */
2037 } /* end of kill_file() */
2041 /*{{{int my_mkdir(char *,Mode_t)*/
2043 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2045 STRLEN dirlen = strlen(dir);
2047 /* zero length string sometimes gives ACCVIO */
2048 if (dirlen == 0) return -1;
2050 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2051 * null file name/type. However, it's commonplace under Unix,
2052 * so we'll allow it for a gain in portability.
2054 if (dir[dirlen-1] == '/') {
2055 char *newdir = savepvn(dir,dirlen-1);
2056 int ret = mkdir(newdir,mode);
2060 else return mkdir(dir,mode);
2061 } /* end of my_mkdir */
2064 /*{{{int my_chdir(char *)*/
2066 Perl_my_chdir(pTHX_ const char *dir)
2068 STRLEN dirlen = strlen(dir);
2069 const char *dir1 = dir;
2071 /* POSIX says we should set ENOENT for zero length string. */
2073 SETERRNO(ENOENT, RMS$_DNF);
2077 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2078 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2079 * so that existing scripts do not need to be changed.
2081 while ((dirlen > 0) && (*dir1 == ' ')) {
2086 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2088 * null file name/type. However, it's commonplace under Unix,
2089 * so we'll allow it for a gain in portability.
2091 * '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
2093 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2096 newdir = (char *)PerlMem_malloc(dirlen);
2098 _ckvmssts_noperl(SS$_INSFMEM);
2099 memcpy(newdir, dir1, dirlen-1);
2100 newdir[dirlen-1] = '\0';
2101 ret = chdir(newdir);
2102 PerlMem_free(newdir);
2105 else return chdir(dir1);
2106 } /* end of my_chdir */
2110 /*{{{int my_chmod(char *, mode_t)*/
2112 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2117 STRLEN speclen = strlen(file_spec);
2119 /* zero length string sometimes gives ACCVIO */
2120 if (speclen == 0) return -1;
2122 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2123 * that implies null file name/type. However, it's commonplace under Unix,
2124 * so we'll allow it for a gain in portability.
2126 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2127 * in VMS file.dir notation.
2129 changefile = (char *) file_spec; /* cast ok */
2130 ret = flex_lstat(file_spec, &st);
2133 /* Due to a historical feature, flex_stat/lstat can not see some */
2134 /* Unix format file names that the rest of the CRTL can see when */
2135 /* ODS-2 file specifications are in use. */
2136 /* Fixing that feature will cause some perl tests to fail */
2137 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2141 /* It may be possible to get here with nothing in st_devname */
2142 /* chmod still may work though */
2143 if (st.st_devnam[0] != 0) {
2144 changefile = st.st_devnam;
2147 ret = chmod(changefile, mode);
2149 } /* end of my_chmod */
2153 /*{{{FILE *my_tmpfile()*/
2160 if ((fp = tmpfile())) return fp;
2162 cp = (char *)PerlMem_malloc(L_tmpnam+24);
2163 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2165 if (DECC_FILENAME_UNIX_ONLY == 0)
2166 strcpy(cp,"Sys$Scratch:");
2169 tmpnam(cp+strlen(cp));
2170 strcat(cp,".Perltmp");
2171 fp = fopen(cp,"w+","fop=dlt");
2179 * The C RTL's sigaction fails to check for invalid signal numbers so we
2180 * help it out a bit. The docs are correct, but the actual routine doesn't
2181 * do what the docs say it will.
2183 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2185 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2186 struct sigaction* oact)
2188 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2189 SETERRNO(EINVAL, SS$_INVARG);
2192 return sigaction(sig, act, oact);
2196 #include <errnodef.h>
2198 /* We implement our own kill() using the undocumented system service
2199 sys$sigprc for one of two reasons:
2201 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2202 target process to do a sys$exit, which usually can't be handled
2203 gracefully...certainly not by Perl and the %SIG{} mechanism.
2205 2.) If the kill() in the CRTL can't be called from a signal
2206 handler without disappearing into the ether, i.e., the signal
2207 it purportedly sends is never trapped. Still true as of VMS 7.3.
2209 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2210 in the target process rather than calling sys$exit.
2212 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2213 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2214 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2215 with condition codes C$_SIG0+nsig*8, catching the exception on the
2216 target process and resignaling with appropriate arguments.
2218 But we don't have that VMS 7.0+ exception handler, so if you
2219 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2221 Also note that SIGTERM is listed in the docs as being "unimplemented",
2222 yet always seems to be signaled with a VMS condition code of 4 (and
2223 correctly handled for that code). So we hardwire it in.
2225 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2226 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2227 than signalling with an unrecognized (and unhandled by CRTL) code.
2230 #define _MY_SIG_MAX 28
2233 Perl_sig_to_vmscondition_int(int sig)
2235 static unsigned int sig_code[_MY_SIG_MAX+1] =
2238 SS$_HANGUP, /* 1 SIGHUP */
2239 SS$_CONTROLC, /* 2 SIGINT */
2240 SS$_CONTROLY, /* 3 SIGQUIT */
2241 SS$_RADRMOD, /* 4 SIGILL */
2242 SS$_BREAK, /* 5 SIGTRAP */
2243 SS$_OPCCUS, /* 6 SIGABRT */
2244 SS$_COMPAT, /* 7 SIGEMT */
2245 SS$_HPARITH, /* 8 SIGFPE AXP */
2246 SS$_ABORT, /* 9 SIGKILL */
2247 SS$_ACCVIO, /* 10 SIGBUS */
2248 SS$_ACCVIO, /* 11 SIGSEGV */
2249 SS$_BADPARAM, /* 12 SIGSYS */
2250 SS$_NOMBX, /* 13 SIGPIPE */
2251 SS$_ASTFLT, /* 14 SIGALRM */
2268 static int initted = 0;
2271 sig_code[16] = C$_SIGUSR1;
2272 sig_code[17] = C$_SIGUSR2;
2273 sig_code[20] = C$_SIGCHLD;
2274 sig_code[28] = C$_SIGWINCH;
2277 if (sig < _SIG_MIN) return 0;
2278 if (sig > _MY_SIG_MAX) return 0;
2279 return sig_code[sig];
2283 Perl_sig_to_vmscondition(int sig)
2286 if (vms_debug_on_exception != 0)
2287 lib$signal(SS$_DEBUG);
2289 return Perl_sig_to_vmscondition_int(sig);
2293 #ifdef KILL_BY_SIGPRC
2294 #define sys$sigprc SYS$SIGPRC
2298 int sys$sigprc(unsigned int *pidadr,
2299 struct dsc$descriptor_s *prcname,
2306 Perl_my_kill(int pid, int sig)
2311 /* sig 0 means validate the PID */
2312 /*------------------------------*/
2314 const unsigned long int jpicode = JPI$_PID;
2317 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2318 if ($VMS_STATUS_SUCCESS(status))
2321 case SS$_NOSUCHNODE:
2322 case SS$_UNREACHABLE:
2336 code = Perl_sig_to_vmscondition_int(sig);
2339 SETERRNO(EINVAL, SS$_BADPARAM);
2343 /* Per official UNIX specification: If pid = 0, or negative then
2344 * signals are to be sent to multiple processes.
2345 * pid = 0 - all processes in group except ones that the system exempts
2346 * pid = -1 - all processes except ones that the system exempts
2347 * pid = -n - all processes in group (abs(n)) except ...
2349 * Handle these via killpg, which is redundant for the -n case, since OP_KILL
2350 * in doio.c already does that. killpg currently does not support the -1 case.
2354 return killpg(-pid, sig);
2357 iss = sys$sigprc((unsigned int *)&pid,0,code);
2358 if (iss&1) return 0;
2362 set_errno(EPERM); break;
2364 case SS$_NOSUCHNODE:
2365 case SS$_UNREACHABLE:
2366 set_errno(ESRCH); break;
2368 set_errno(ENOMEM); break;
2370 _ckvmssts_noperl(iss);
2373 set_vaxc_errno(iss);
2380 Perl_my_killpg(pid_t master_pid, int signum)
2383 unsigned long int jpi_context;
2384 unsigned short int iosb[4];
2385 struct itmlst_3 il3[3];
2387 /* All processes on the system? Seems dangerous, but it looks
2388 * like we could implement this pretty easily with a wildcard
2389 * input to sys$process_scan.
2391 if (master_pid == -1) {
2392 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2396 /* All processes in the current process group; find the master
2397 * pid for the current process.
2399 if (master_pid == 0) {
2401 il3[i].buflen = sizeof( int );
2402 il3[i].itmcode = JPI$_MASTER_PID;
2403 il3[i].bufadr = &master_pid;
2404 il3[i++].retlen = NULL;
2408 il3[i].bufadr = NULL;
2409 il3[i++].retlen = NULL;
2411 status = sys$getjpiw(EFN$C_ENF, NULL, NULL, il3, iosb, NULL, 0);
2412 if ($VMS_STATUS_SUCCESS(status))
2420 SETERRNO(EPERM, status);
2422 case SS$_NOMOREPROC:
2424 case SS$_NOSUCHNODE:
2425 case SS$_UNREACHABLE:
2426 SETERRNO(ESRCH, status);
2430 SETERRNO(EINVAL, status);
2433 SETERRNO(EVMSERR, status);
2435 if (!$VMS_STATUS_SUCCESS(status))
2439 /* Set up a process context for those processes we will scan
2440 * with sys$getjpiw. Ask for all processes belonging to the
2446 il3[i].itmcode = PSCAN$_MASTER_PID;
2447 il3[i].bufadr = (void *)master_pid;
2448 il3[i++].retlen = NULL;
2452 il3[i].bufadr = NULL;
2453 il3[i++].retlen = NULL;
2455 status = sys$process_scan(&jpi_context, il3);
2463 SETERRNO(EINVAL, status);
2466 SETERRNO(EVMSERR, status);
2468 if (!$VMS_STATUS_SUCCESS(status))
2472 il3[i].buflen = sizeof(int);
2473 il3[i].itmcode = JPI$_PID;
2474 il3[i].bufadr = &pid;
2475 il3[i++].retlen = NULL;
2479 il3[i].bufadr = NULL;
2480 il3[i++].retlen = NULL;
2482 /* Loop through the processes matching our specified criteria
2486 /* Find the next process...
2488 status = sys$getjpiw( EFN$C_ENF, &jpi_context, NULL, il3, iosb, NULL, 0);
2489 if ($VMS_STATUS_SUCCESS(status)) status = iosb[0];
2493 if (kill(pid, signum) == -1)
2496 continue; /* next process */
2499 SETERRNO(EPERM, status);
2501 case SS$_NOMOREPROC:
2504 case SS$_NOSUCHNODE:
2505 case SS$_UNREACHABLE:
2506 SETERRNO(ESRCH, status);
2510 SETERRNO(EINVAL, status);
2513 SETERRNO(EVMSERR, status);
2516 if (!$VMS_STATUS_SUCCESS(status))
2520 /* Release context-related resources.
2522 (void) sys$process_scan(&jpi_context);
2524 if (status != SS$_NOMOREPROC)
2530 /* Routine to convert a VMS status code to a UNIX status code.
2531 ** More tricky than it appears because of conflicting conventions with
2534 ** VMS status codes are a bit mask, with the least significant bit set for
2537 ** Special UNIX status of EVMSERR indicates that no translation is currently
2538 ** available, and programs should check the VMS status code.
2540 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2544 #ifndef C_FACILITY_NO
2545 #define C_FACILITY_NO 0x350000
2548 #define DCL_IVVERB 0x38090
2552 Perl_vms_status_to_unix(int vms_status, int child_flag)
2560 /* Assume the best or the worst */
2561 if (vms_status & STS$M_SUCCESS)
2564 unix_status = EVMSERR;
2566 msg_status = vms_status & ~STS$M_CONTROL;
2568 facility = vms_status & STS$M_FAC_NO;
2569 fac_sp = vms_status & STS$M_FAC_SP;
2570 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2572 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2578 unix_status = EFAULT;
2580 case SS$_DEVOFFLINE:
2581 unix_status = EBUSY;
2584 unix_status = ENOTCONN;
2592 case SS$_INVFILFOROP:
2596 unix_status = EINVAL;
2598 case SS$_UNSUPPORTED:
2599 unix_status = ENOTSUP;
2604 unix_status = EACCES;
2606 case SS$_DEVICEFULL:
2607 unix_status = ENOSPC;
2610 unix_status = ENODEV;
2612 case SS$_NOSUCHFILE:
2613 case SS$_NOSUCHOBJECT:
2614 unix_status = ENOENT;
2616 case SS$_ABORT: /* Fatal case */
2617 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2618 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2619 unix_status = EINTR;
2622 unix_status = E2BIG;
2625 unix_status = ENOMEM;
2628 unix_status = EPERM;
2630 case SS$_NOSUCHNODE:
2631 case SS$_UNREACHABLE:
2632 unix_status = ESRCH;
2635 unix_status = ECHILD;
2638 if ((facility == 0) && (msg_no < 8)) {
2639 /* These are not real VMS status codes so assume that they are
2640 ** already UNIX status codes
2642 unix_status = msg_no;
2648 /* Translate a POSIX exit code to a UNIX exit code */
2649 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2650 unix_status = (msg_no & 0x07F8) >> 3;
2654 /* Documented traditional behavior for handling VMS child exits */
2655 /*--------------------------------------------------------------*/
2656 if (child_flag != 0) {
2658 /* Success / Informational return 0 */
2659 /*----------------------------------*/
2660 if (msg_no & STS$K_SUCCESS)
2663 /* Warning returns 1 */
2664 /*-------------------*/
2665 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2668 /* Everything else pass through the severity bits */
2669 /*------------------------------------------------*/
2670 return (msg_no & STS$M_SEVERITY);
2673 /* Normal VMS status to ERRNO mapping attempt */
2674 /*--------------------------------------------*/
2675 switch(msg_status) {
2676 /* case RMS$_EOF: */ /* End of File */
2677 case RMS$_FNF: /* File Not Found */
2678 case RMS$_DNF: /* Dir Not Found */
2679 unix_status = ENOENT;
2681 case RMS$_RNF: /* Record Not Found */
2682 unix_status = ESRCH;
2685 unix_status = ENOTDIR;
2688 unix_status = ENODEV;
2693 unix_status = EBADF;
2696 unix_status = EEXIST;
2700 case LIB$_INVSTRDES:
2702 case LIB$_NOSUCHSYM:
2703 case LIB$_INVSYMNAM:
2705 unix_status = EINVAL;
2711 unix_status = E2BIG;
2713 case RMS$_PRV: /* No privilege */
2714 case RMS$_ACC: /* ACP file access failed */
2715 case RMS$_WLK: /* Device write locked */
2716 unix_status = EACCES;
2718 case RMS$_MKD: /* Failed to mark for delete */
2719 unix_status = EPERM;
2721 /* case RMS$_NMF: */ /* No more files */
2729 /* Try to guess at what VMS error status should go with a UNIX errno
2730 * value. This is hard to do as there could be many possible VMS
2731 * error statuses that caused the errno value to be set.
2735 Perl_unix_status_to_vms(int unix_status)
2737 int test_unix_status;
2739 /* Trivial cases first */
2740 /*---------------------*/
2741 if (unix_status == EVMSERR)
2744 /* Is vaxc$errno sane? */
2745 /*---------------------*/
2746 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2747 if (test_unix_status == unix_status)
2750 /* If way out of range, must be VMS code already */
2751 /*-----------------------------------------------*/
2752 if (unix_status > EVMSERR)
2755 /* If out of range, punt */
2756 /*-----------------------*/
2757 if (unix_status > __ERRNO_MAX)
2761 /* Ok, now we have to do it the hard way. */
2762 /*----------------------------------------*/
2763 switch(unix_status) {
2764 case 0: return SS$_NORMAL;
2765 case EPERM: return SS$_NOPRIV;
2766 case ENOENT: return SS$_NOSUCHOBJECT;
2767 case ESRCH: return SS$_UNREACHABLE;
2768 case EINTR: return SS$_ABORT;
2771 case E2BIG: return SS$_BUFFEROVF;
2773 case EBADF: return RMS$_IFI;
2774 case ECHILD: return SS$_NONEXPR;
2776 case ENOMEM: return SS$_INSFMEM;
2777 case EACCES: return SS$_FILACCERR;
2778 case EFAULT: return SS$_ACCVIO;
2780 case EBUSY: return SS$_DEVOFFLINE;
2781 case EEXIST: return RMS$_FEX;
2783 case ENODEV: return SS$_NOSUCHDEV;
2784 case ENOTDIR: return RMS$_DIR;
2786 case EINVAL: return SS$_INVARG;
2792 case ENOSPC: return SS$_DEVICEFULL;
2793 case ESPIPE: return LIB$_INVARG;
2798 case ERANGE: return LIB$_INVARG;
2799 /* case EWOULDBLOCK */
2800 /* case EINPROGRESS */
2803 /* case EDESTADDRREQ */
2805 /* case EPROTOTYPE */
2806 /* case ENOPROTOOPT */
2807 /* case EPROTONOSUPPORT */
2808 /* case ESOCKTNOSUPPORT */
2809 /* case EOPNOTSUPP */
2810 /* case EPFNOSUPPORT */
2811 /* case EAFNOSUPPORT */
2812 /* case EADDRINUSE */
2813 /* case EADDRNOTAVAIL */
2815 /* case ENETUNREACH */
2816 /* case ENETRESET */
2817 /* case ECONNABORTED */
2818 /* case ECONNRESET */
2821 case ENOTCONN: return SS$_CLEARED;
2822 /* case ESHUTDOWN */
2823 /* case ETOOMANYREFS */
2824 /* case ETIMEDOUT */
2825 /* case ECONNREFUSED */
2827 /* case ENAMETOOLONG */
2828 /* case EHOSTDOWN */
2829 /* case EHOSTUNREACH */
2830 /* case ENOTEMPTY */
2842 /* case ECANCELED */
2846 return SS$_UNSUPPORTED;
2852 /* case EABANDONED */
2854 return SS$_ABORT; /* punt */
2859 /* default piping mailbox size */
2860 #define PERL_BUFSIZ 8192
2864 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2866 unsigned long int mbxbufsiz;
2867 static unsigned long int syssize = 0;
2868 unsigned long int dviitm = DVI$_DEVNAM;
2869 char csize[LNM$C_NAMLENGTH+1];
2873 unsigned long syiitm = SYI$_MAXBUF;
2875 * Get the SYSGEN parameter MAXBUF
2877 * If the logical 'PERL_MBX_SIZE' is defined
2878 * use the value of the logical instead of PERL_BUFSIZ, but
2879 * keep the size between 128 and MAXBUF.
2882 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2885 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2886 mbxbufsiz = atoi(csize);
2888 mbxbufsiz = PERL_BUFSIZ;
2890 if (mbxbufsiz < 128) mbxbufsiz = 128;
2891 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2893 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2895 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2896 _ckvmssts_noperl(sts);
2897 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2899 } /* end of create_mbx() */
2902 /*{{{ my_popen and my_pclose*/
2904 typedef struct _iosb IOSB;
2905 typedef struct _iosb* pIOSB;
2906 typedef struct _pipe Pipe;
2907 typedef struct _pipe* pPipe;
2908 typedef struct pipe_details Info;
2909 typedef struct pipe_details* pInfo;
2910 typedef struct _srqp RQE;
2911 typedef struct _srqp* pRQE;
2912 typedef struct _tochildbuf CBuf;
2913 typedef struct _tochildbuf* pCBuf;
2916 unsigned short status;
2917 unsigned short count;
2918 unsigned long dvispec;
2921 #pragma member_alignment save
2922 #pragma nomember_alignment quadword
2923 struct _srqp { /* VMS self-relative queue entry */
2924 unsigned long qptr[2];
2926 #pragma member_alignment restore
2927 static RQE RQE_ZERO = {0,0};
2929 struct _tochildbuf {
2932 unsigned short size;
2940 unsigned short chan_in;
2941 unsigned short chan_out;
2943 unsigned int bufsize;
2955 #if defined(PERL_IMPLICIT_CONTEXT)
2956 void *thx; /* Either a thread or an interpreter */
2957 /* pointer, depending on how we're built */
2965 PerlIO *fp; /* file pointer to pipe mailbox */
2966 int useFILE; /* using stdio, not perlio */
2967 int pid; /* PID of subprocess */
2968 int mode; /* == 'r' if pipe open for reading */
2969 int done; /* subprocess has completed */
2970 int waiting; /* waiting for completion/closure */
2971 int closing; /* my_pclose is closing this pipe */
2972 unsigned long completion; /* termination status of subprocess */
2973 pPipe in; /* pipe in to sub */
2974 pPipe out; /* pipe out of sub */
2975 pPipe err; /* pipe of sub's sys$error */
2976 int in_done; /* true when in pipe finished */
2979 unsigned short xchan; /* channel to debug xterm */
2980 unsigned short xchan_valid; /* channel is assigned */
2983 struct exit_control_block
2985 struct exit_control_block *flink;
2986 unsigned long int (*exit_routine)(void);
2987 unsigned long int arg_count;
2988 unsigned long int *status_address;
2989 unsigned long int exit_status;
2992 typedef struct _closed_pipes Xpipe;
2993 typedef struct _closed_pipes* pXpipe;
2995 struct _closed_pipes {
2996 int pid; /* PID of subprocess */
2997 unsigned long completion; /* termination status of subprocess */
2999 #define NKEEPCLOSED 50
3000 static Xpipe closed_list[NKEEPCLOSED];
3001 static int closed_index = 0;
3002 static int closed_num = 0;
3004 #define RETRY_DELAY "0 ::0.20"
3005 #define MAX_RETRY 50
3007 static int pipe_ef = 0; /* first call to safe_popen inits these*/
3008 static unsigned long mypid;
3009 static unsigned long delaytime[2];
3011 static pInfo open_pipes = NULL;
3012 static $DESCRIPTOR(nl_desc, "NL:");
3014 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
3018 static unsigned long int
3019 pipe_exit_routine(void)
3022 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
3023 int sts, did_stuff, j;
3026 * Flush any pending i/o, but since we are in process run-down, be
3027 * careful about referencing PerlIO structures that may already have
3028 * been deallocated. We may not even have an interpreter anymore.
3033 #if defined(PERL_IMPLICIT_CONTEXT)
3034 /* We need to use the Perl context of the thread that created */
3038 aTHX = info->err->thx;
3040 aTHX = info->out->thx;
3042 aTHX = info->in->thx;
3045 #if defined(USE_ITHREADS)
3049 && PL_perlio_fd_refcnt
3052 PerlIO_flush(info->fp);
3054 fflush((FILE *)info->fp);
3060 next we try sending an EOF...ignore if doesn't work, make sure we
3067 _ckvmssts_noperl(sys$setast(0));
3068 if (info->in && !info->in->shut_on_empty) {
3069 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3074 _ckvmssts_noperl(sys$setast(1));
3078 /* wait for EOF to have effect, up to ~ 30 sec [default] */
3080 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3085 _ckvmssts_noperl(sys$setast(0));
3086 if (info->waiting && info->done)
3088 nwait += info->waiting;
3089 _ckvmssts_noperl(sys$setast(1));
3099 _ckvmssts_noperl(sys$setast(0));
3100 if (!info->done) { /* Tap them gently on the shoulder . . .*/
3101 sts = sys$forcex(&info->pid,0,&abort);
3102 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3105 _ckvmssts_noperl(sys$setast(1));
3109 /* again, wait for effect */
3111 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3116 _ckvmssts_noperl(sys$setast(0));
3117 if (info->waiting && info->done)
3119 nwait += info->waiting;
3120 _ckvmssts_noperl(sys$setast(1));
3129 _ckvmssts_noperl(sys$setast(0));
3130 if (!info->done) { /* We tried to be nice . . . */
3131 sts = sys$delprc(&info->pid,0);
3132 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3133 info->done = 1; /* sys$delprc is as done as we're going to get. */
3135 _ckvmssts_noperl(sys$setast(1));
3141 #if defined(PERL_IMPLICIT_CONTEXT)
3142 /* We need to use the Perl context of the thread that created */
3145 if (open_pipes->err)
3146 aTHX = open_pipes->err->thx;
3147 else if (open_pipes->out)
3148 aTHX = open_pipes->out->thx;
3149 else if (open_pipes->in)
3150 aTHX = open_pipes->in->thx;
3152 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3153 else if (!(sts & 1)) retsts = sts;
3158 static struct exit_control_block pipe_exitblock =
3159 {(struct exit_control_block *) 0,
3160 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3162 static void pipe_mbxtofd_ast(pPipe p);
3163 static void pipe_tochild1_ast(pPipe p);
3164 static void pipe_tochild2_ast(pPipe p);
3167 popen_completion_ast(pInfo info)
3169 pInfo i = open_pipes;
3172 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3173 closed_list[closed_index].pid = info->pid;
3174 closed_list[closed_index].completion = info->completion;
3176 if (closed_index == NKEEPCLOSED)
3181 if (i == info) break;
3184 if (!i) return; /* unlinked, probably freed too */
3189 Writing to subprocess ...
3190 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3192 chan_out may be waiting for "done" flag, or hung waiting
3193 for i/o completion to child...cancel the i/o. This will
3194 put it into "snarf mode" (done but no EOF yet) that discards
3197 Output from subprocess (stdout, stderr) needs to be flushed and
3198 shut down. We try sending an EOF, but if the mbx is full the pipe
3199 routine should still catch the "shut_on_empty" flag, telling it to
3200 use immediate-style reads so that "mbx empty" -> EOF.
3204 if (info->in && !info->in_done) { /* only for mode=w */
3205 if (info->in->shut_on_empty && info->in->need_wake) {
3206 info->in->need_wake = FALSE;
3207 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3209 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3213 if (info->out && !info->out_done) { /* were we also piping output? */
3214 info->out->shut_on_empty = TRUE;
3215 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3216 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3217 _ckvmssts_noperl(iss);
3220 if (info->err && !info->err_done) { /* we were piping stderr */
3221 info->err->shut_on_empty = TRUE;
3222 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3223 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3224 _ckvmssts_noperl(iss);
3226 _ckvmssts_noperl(sys$setef(pipe_ef));
3230 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3231 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3232 static void pipe_infromchild_ast(pPipe p);
3235 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3236 inside an AST routine without worrying about reentrancy and which Perl
3237 memory allocator is being used.
3239 We read data and queue up the buffers, then spit them out one at a
3240 time to the output mailbox when the output mailbox is ready for one.
3243 #define INITIAL_TOCHILDQUEUE 2
3246 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3250 char mbx1[64], mbx2[64];
3251 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3252 DSC$K_CLASS_S, mbx1},
3253 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3254 DSC$K_CLASS_S, mbx2};
3255 unsigned int dviitm = DVI$_DEVBUFSIZ;
3259 _ckvmssts_noperl(lib$get_vm(&n, &p));
3261 create_mbx(&p->chan_in , &d_mbx1);
3262 create_mbx(&p->chan_out, &d_mbx2);
3263 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3266 p->shut_on_empty = FALSE;
3267 p->need_wake = FALSE;
3270 p->iosb.status = SS$_NORMAL;
3271 p->iosb2.status = SS$_NORMAL;
3277 #ifdef PERL_IMPLICIT_CONTEXT
3281 n = sizeof(CBuf) + p->bufsize;
3283 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3284 _ckvmssts_noperl(lib$get_vm(&n, &b));
3285 b->buf = (char *) b + sizeof(CBuf);
3286 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3289 pipe_tochild2_ast(p);
3290 pipe_tochild1_ast(p);
3296 /* reads the MBX Perl is writing, and queues */
3299 pipe_tochild1_ast(pPipe p)
3302 int iss = p->iosb.status;
3303 int eof = (iss == SS$_ENDOFFILE);
3305 #ifdef PERL_IMPLICIT_CONTEXT
3311 p->shut_on_empty = TRUE;
3313 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3315 _ckvmssts_noperl(iss);
3319 b->size = p->iosb.count;
3320 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3322 p->need_wake = FALSE;
3323 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3326 p->retry = 1; /* initial call */
3329 if (eof) { /* flush the free queue, return when done */
3330 int n = sizeof(CBuf) + p->bufsize;
3332 iss = lib$remqti(&p->free, &b);
3333 if (iss == LIB$_QUEWASEMP) return;
3334 _ckvmssts_noperl(iss);
3335 _ckvmssts_noperl(lib$free_vm(&n, &b));
3339 iss = lib$remqti(&p->free, &b);
3340 if (iss == LIB$_QUEWASEMP) {
3341 int n = sizeof(CBuf) + p->bufsize;
3342 _ckvmssts_noperl(lib$get_vm(&n, &b));
3343 b->buf = (char *) b + sizeof(CBuf);
3345 _ckvmssts_noperl(iss);
3349 iss = sys$qio(0,p->chan_in,
3350 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3352 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3353 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3354 _ckvmssts_noperl(iss);
3358 /* writes queued buffers to output, waits for each to complete before
3362 pipe_tochild2_ast(pPipe p)
3365 int iss = p->iosb2.status;
3366 int n = sizeof(CBuf) + p->bufsize;
3367 int done = (p->info && p->info->done) ||
3368 iss == SS$_CANCEL || iss == SS$_ABORT;
3369 #if defined(PERL_IMPLICIT_CONTEXT)
3374 if (p->type) { /* type=1 has old buffer, dispose */
3375 if (p->shut_on_empty) {
3376 _ckvmssts_noperl(lib$free_vm(&n, &b));
3378 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3383 iss = lib$remqti(&p->wait, &b);
3384 if (iss == LIB$_QUEWASEMP) {
3385 if (p->shut_on_empty) {
3387 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3388 *p->pipe_done = TRUE;
3389 _ckvmssts_noperl(sys$setef(pipe_ef));
3391 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3392 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3396 p->need_wake = TRUE;
3399 _ckvmssts_noperl(iss);
3406 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3407 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3409 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3410 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3419 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3422 char mbx1[64], mbx2[64];
3423 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3424 DSC$K_CLASS_S, mbx1},
3425 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3426 DSC$K_CLASS_S, mbx2};
3427 unsigned int dviitm = DVI$_DEVBUFSIZ;
3429 int n = sizeof(Pipe);
3430 _ckvmssts_noperl(lib$get_vm(&n, &p));
3431 create_mbx(&p->chan_in , &d_mbx1);
3432 create_mbx(&p->chan_out, &d_mbx2);
3434 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3435 n = p->bufsize * sizeof(char);
3436 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3437 p->shut_on_empty = FALSE;
3440 p->iosb.status = SS$_NORMAL;
3441 #if defined(PERL_IMPLICIT_CONTEXT)
3444 pipe_infromchild_ast(p);
3452 pipe_infromchild_ast(pPipe p)
3454 int iss = p->iosb.status;
3455 int eof = (iss == SS$_ENDOFFILE);
3456 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3457 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3458 #if defined(PERL_IMPLICIT_CONTEXT)
3462 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3463 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3468 input shutdown if EOF from self (done or shut_on_empty)
3469 output shutdown if closing flag set (my_pclose)
3470 send data/eof from child or eof from self
3471 otherwise, re-read (snarf of data from child)
3476 if (myeof && p->chan_in) { /* input shutdown */
3477 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3482 if (myeof || kideof) { /* pass EOF to parent */
3483 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3484 pipe_infromchild_ast, p,
3487 } else if (eof) { /* eat EOF --- fall through to read*/
3489 } else { /* transmit data */
3490 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3491 pipe_infromchild_ast,p,
3492 p->buf, p->iosb.count, 0, 0, 0, 0));
3498 /* everything shut? flag as done */
3500 if (!p->chan_in && !p->chan_out) {
3501 *p->pipe_done = TRUE;
3502 _ckvmssts_noperl(sys$setef(pipe_ef));
3506 /* write completed (or read, if snarfing from child)
3507 if still have input active,
3508 queue read...immediate mode if shut_on_empty so we get EOF if empty
3510 check if Perl reading, generate EOFs as needed
3516 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3517 pipe_infromchild_ast,p,
3518 p->buf, p->bufsize, 0, 0, 0, 0);
3519 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3520 _ckvmssts_noperl(iss);
3521 } else { /* send EOFs for extra reads */
3522 p->iosb.status = SS$_ENDOFFILE;
3523 p->iosb.dvispec = 0;
3524 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3526 pipe_infromchild_ast, p, 0, 0, 0, 0));
3532 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3536 unsigned long dviitm = DVI$_DEVBUFSIZ;
3538 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3539 DSC$K_CLASS_S, mbx};
3540 int n = sizeof(Pipe);
3542 /* things like terminals and mbx's don't need this filter */
3543 if (fd && fstat(fd,&s) == 0) {
3544 unsigned long devchar;
3546 unsigned short dev_len;
3547 struct dsc$descriptor_s d_dev;
3549 struct item_list_3 items[3];
3551 unsigned short dvi_iosb[4];
3553 cptr = getname(fd, out, 1);
3554 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3555 d_dev.dsc$a_pointer = out;
3556 d_dev.dsc$w_length = strlen(out);
3557 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3558 d_dev.dsc$b_class = DSC$K_CLASS_S;
3561 items[0].code = DVI$_DEVCHAR;
3562 items[0].bufadr = &devchar;
3563 items[0].retadr = NULL;
3565 items[1].code = DVI$_FULLDEVNAM;
3566 items[1].bufadr = device;
3567 items[1].retadr = &dev_len;
3571 status = sys$getdviw
3572 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3573 _ckvmssts_noperl(status);
3574 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3575 device[dev_len] = 0;
3577 if (!(devchar & DEV$M_DIR)) {
3578 strcpy(out, device);
3584 _ckvmssts_noperl(lib$get_vm(&n, &p));
3585 p->fd_out = dup(fd);
3586 create_mbx(&p->chan_in, &d_mbx);
3587 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3588 n = (p->bufsize+1) * sizeof(char);
3589 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3590 p->shut_on_empty = FALSE;
3595 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3596 pipe_mbxtofd_ast, p,
3597 p->buf, p->bufsize, 0, 0, 0, 0));
3603 pipe_mbxtofd_ast(pPipe p)
3605 int iss = p->iosb.status;
3606 int done = p->info->done;
3608 int eof = (iss == SS$_ENDOFFILE);
3609 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3610 int err = !(iss&1) && !eof;
3611 #if defined(PERL_IMPLICIT_CONTEXT)
3615 if (done && myeof) { /* end piping */
3617 sys$dassgn(p->chan_in);
3618 *p->pipe_done = TRUE;
3619 _ckvmssts_noperl(sys$setef(pipe_ef));
3623 if (!err && !eof) { /* good data to send to file */
3624 p->buf[p->iosb.count] = '\n';
3625 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3628 if (p->retry < MAX_RETRY) {
3629 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3635 _ckvmssts_noperl(iss);
3639 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3640 pipe_mbxtofd_ast, p,
3641 p->buf, p->bufsize, 0, 0, 0, 0);
3642 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3643 _ckvmssts_noperl(iss);
3647 typedef struct _pipeloc PLOC;
3648 typedef struct _pipeloc* pPLOC;
3652 char dir[NAM$C_MAXRSS+1];
3654 static pPLOC head_PLOC = 0;
3657 free_pipelocs(pTHX_ void *head)
3660 pPLOC *pHead = (pPLOC *)head;
3672 store_pipelocs(pTHX)
3680 char temp[NAM$C_MAXRSS+1];
3684 free_pipelocs(aTHX_ &head_PLOC);
3686 /* the . directory from @INC comes last */
3688 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3689 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3690 p->next = head_PLOC;
3692 strcpy(p->dir,"./");
3694 /* get the directory from $^X */
3696 unixdir = (char *)PerlMem_malloc(VMS_MAXRSS);
3697 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3699 #ifdef PERL_IMPLICIT_CONTEXT
3700 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3702 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3704 my_strlcpy(temp, PL_origargv[0], sizeof(temp));
3705 x = strrchr(temp,']');
3707 x = strrchr(temp,'>');
3709 /* It could be a UNIX path */
3710 x = strrchr(temp,'/');
3716 /* Got a bare name, so use default directory */
3721 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3722 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3723 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3724 p->next = head_PLOC;
3726 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3730 /* reverse order of @INC entries, skip "." since entered above */
3732 #ifdef PERL_IMPLICIT_CONTEXT
3735 if (PL_incgv) av = GvAVn(PL_incgv);
3737 for (i = 0; av && i <= AvFILL(av); i++) {
3738 dirsv = *av_fetch(av,i,TRUE);
3740 if (SvROK(dirsv)) continue;
3741 dir = SvPVx(dirsv,n_a);
3742 if (strEQ(dir,".")) continue;
3743 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3746 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3747 p->next = head_PLOC;
3749 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3752 /* most likely spot (ARCHLIB) put first in the list */
3755 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3756 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3757 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3758 p->next = head_PLOC;
3760 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3763 PerlMem_free(unixdir);
3766 static I32 Perl_cando_by_name_int(pTHX_ I32 bit, bool effective,
3767 const char *fname, int opts);
3768 #if !defined(PERL_IMPLICIT_CONTEXT)
3769 #define cando_by_name_int Perl_cando_by_name_int
3771 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3777 static int vmspipe_file_status = 0;
3778 static char vmspipe_file[NAM$C_MAXRSS+1];
3780 /* already found? Check and use ... need read+execute permission */
3782 if (vmspipe_file_status == 1) {
3783 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3784 && cando_by_name_int
3785 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3786 return vmspipe_file;
3788 vmspipe_file_status = 0;
3791 /* scan through stored @INC, $^X */
3793 if (vmspipe_file_status == 0) {
3794 char file[NAM$C_MAXRSS+1];
3795 pPLOC p = head_PLOC;
3800 dirlen = my_strlcpy(file, p->dir, sizeof(file));
3801 my_strlcat(file, "vmspipe.com", sizeof(file));
3804 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3805 if (!exp_res) continue;
3807 if (cando_by_name_int
3808 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3809 && cando_by_name_int
3810 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3811 vmspipe_file_status = 1;
3812 return vmspipe_file;
3815 vmspipe_file_status = -1; /* failed, use tempfiles */
3822 vmspipe_tempfile(pTHX)
3824 char file[NAM$C_MAXRSS+1];
3826 static int index = 0;
3830 /* create a tempfile */
3832 /* we can't go from W, shr=get to R, shr=get without
3833 an intermediate vulnerable state, so don't bother trying...
3835 and lib$spawn doesn't shr=put, so have to close the write
3837 So... match up the creation date/time and the FID to
3838 make sure we're dealing with the same file
3843 if (!DECC_FILENAME_UNIX_ONLY) {
3844 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3845 fp = fopen(file,"w");
3847 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3848 fp = fopen(file,"w");
3850 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3851 fp = fopen(file,"w");
3856 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3857 fp = fopen(file,"w");
3859 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3860 fp = fopen(file,"w");
3862 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3863 fp = fopen(file,"w");
3867 if (!fp) return 0; /* we're hosed */
3869 fprintf(fp,"$! 'f$verify(0)'\n");
3870 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3871 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3872 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3873 fprintf(fp,"$ perl_on = \"set noon\"\n");
3874 fprintf(fp,"$ perl_exit = \"exit\"\n");
3875 fprintf(fp,"$ perl_del = \"delete\"\n");
3876 fprintf(fp,"$ pif = \"if\"\n");
3877 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3878 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3879 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3880 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3881 fprintf(fp,"$! --- build command line to get max possible length\n");
3882 fprintf(fp,"$c=perl_popen_cmd0\n");
3883 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3884 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3885 fprintf(fp,"$x=perl_popen_cmd3\n");
3886 fprintf(fp,"$c=c+x\n");
3887 fprintf(fp,"$ perl_on\n");
3888 fprintf(fp,"$ 'c'\n");
3889 fprintf(fp,"$ perl_status = $STATUS\n");
3890 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3891 fprintf(fp,"$ perl_exit 'perl_status'\n");
3894 fgetname(fp, file, 1);
3895 fstat(fileno(fp), &s0.crtl_stat);
3898 if (DECC_FILENAME_UNIX_ONLY)
3899 int_tounixspec(file, file, NULL);
3900 fp = fopen(file,"r","shr=get");
3902 fstat(fileno(fp), &s1.crtl_stat);
3904 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3905 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3915 vms_is_syscommand_xterm(void)
3917 const static struct dsc$descriptor_s syscommand_dsc =
3918 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3920 const static struct dsc$descriptor_s decwdisplay_dsc =
3921 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3923 struct item_list_3 items[2];
3924 unsigned short dvi_iosb[4];
3925 unsigned long devchar;
3926 unsigned long devclass;
3929 /* Very simple check to guess if sys$command is a decterm? */
3930 /* First see if the DECW$DISPLAY: device exists */
3932 items[0].code = DVI$_DEVCHAR;
3933 items[0].bufadr = &devchar;
3934 items[0].retadr = NULL;
3938 status = sys$getdviw
3939 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3941 if ($VMS_STATUS_SUCCESS(status)) {
3942 status = dvi_iosb[0];
3945 if (!$VMS_STATUS_SUCCESS(status)) {
3946 SETERRNO(EVMSERR, status);
3950 /* If it does, then for now assume that we are on a workstation */
3951 /* Now verify that SYS$COMMAND is a terminal */
3952 /* for creating the debugger DECTerm */
3955 items[0].code = DVI$_DEVCLASS;
3956 items[0].bufadr = &devclass;
3957 items[0].retadr = NULL;
3961 status = sys$getdviw
3962 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3964 if ($VMS_STATUS_SUCCESS(status)) {
3965 status = dvi_iosb[0];
3968 if (!$VMS_STATUS_SUCCESS(status)) {
3969 SETERRNO(EVMSERR, status);
3973 if (devclass == DC$_TERM) {
3980 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3982 create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3987 char device_name[65];
3988 unsigned short device_name_len;
3989 struct dsc$descriptor_s customization_dsc;
3990 struct dsc$descriptor_s device_name_dsc;
3992 char customization[200];
3996 unsigned short p_chan;
3998 unsigned short iosb[4];
3999 const char * cust_str =
4000 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
4001 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
4002 DSC$K_CLASS_S, mbx1};
4004 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
4005 /*---------------------------------------*/
4006 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
4009 /* Make sure that this is from the Perl debugger */
4010 ret_char = strstr(cmd," xterm ");
4011 if (ret_char == NULL)
4013 cptr = ret_char + 7;
4014 ret_char = strstr(cmd,"tty");
4015 if (ret_char == NULL)
4017 ret_char = strstr(cmd,"sleep");
4018 if (ret_char == NULL)
4021 if (decw_term_port == 0) {
4022 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
4023 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
4024 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
4026 status = lib$find_image_symbol
4028 &decw_term_port_dsc,
4029 (void *)&decw_term_port,
4033 /* Try again with the other image name */
4034 if (!$VMS_STATUS_SUCCESS(status)) {
4036 status = lib$find_image_symbol
4038 &decw_term_port_dsc,
4039 (void *)&decw_term_port,
4048 /* No decw$term_port, give it up */
4049 if (!$VMS_STATUS_SUCCESS(status))
4052 /* Are we on a workstation? */
4053 /* to do: capture the rows / columns and pass their properties */
4054 ret_stat = vms_is_syscommand_xterm();
4058 /* Make the title: */
4059 ret_char = strstr(cptr,"-title");
4060 if (ret_char != NULL) {
4061 while ((*cptr != 0) && (*cptr != '\"')) {
4067 while ((*cptr != 0) && (*cptr != '\"')) {
4080 strcpy(title,"Perl Debug DECTerm");
4082 sprintf(customization, cust_str, title);
4084 customization_dsc.dsc$a_pointer = customization;
4085 customization_dsc.dsc$w_length = strlen(customization);
4086 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4087 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
4089 device_name_dsc.dsc$a_pointer = device_name;
4090 device_name_dsc.dsc$w_length = sizeof device_name -1;
4091 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4092 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
4094 device_name_len = 0;
4096 /* Try to create the window */
4097 status = (*decw_term_port)
4106 if (!$VMS_STATUS_SUCCESS(status)) {
4107 SETERRNO(EVMSERR, status);
4111 device_name[device_name_len] = '\0';
4113 /* Need to set this up to look like a pipe for cleanup */
4115 status = lib$get_vm(&n, &info);
4116 if (!$VMS_STATUS_SUCCESS(status)) {
4117 SETERRNO(ENOMEM, status);
4123 info->completion = 0;
4124 info->closing = FALSE;
4131 info->in_done = TRUE;
4132 info->out_done = TRUE;
4133 info->err_done = TRUE;
4135 /* Assign a channel on this so that it will persist, and not login */
4136 /* We stash this channel in the info structure for reference. */
4137 /* The created xterm self destructs when the last channel is removed */
4138 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4139 /* So leave this assigned. */
4140 device_name_dsc.dsc$w_length = device_name_len;
4141 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4142 if (!$VMS_STATUS_SUCCESS(status)) {
4143 SETERRNO(EVMSERR, status);
4146 info->xchan_valid = 1;
4148 /* Now create a mailbox to be read by the application */
4150 create_mbx(&p_chan, &d_mbx1);
4152 /* write the name of the created terminal to the mailbox */
4153 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4154 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4156 if (!$VMS_STATUS_SUCCESS(status)) {
4157 SETERRNO(EVMSERR, status);
4161 info->fp = PerlIO_open(mbx1, mode);
4163 /* Done with this channel */
4166 /* If any errors, then clean up */
4169 _ckvmssts_noperl(lib$free_vm(&n, &info));
4177 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4180 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4182 static int handler_set_up = FALSE;
4184 unsigned long int sts, flags = CLI$M_NOWAIT;
4185 /* The use of a GLOBAL table (as was done previously) rendered
4186 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4187 * environment. Hence we've switched to LOCAL symbol table.
4189 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4191 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4192 char *in, *out, *err, mbx[512];
4194 char tfilebuf[NAM$C_MAXRSS+1];
4196 char cmd_sym_name[20];
4197 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4198 DSC$K_CLASS_S, symbol};
4199 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4201 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4202 DSC$K_CLASS_S, cmd_sym_name};
4203 struct dsc$descriptor_s *vmscmd;
4204 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4205 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4206 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4208 /* Check here for Xterm create request. This means looking for
4209 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4210 * is possible to create an xterm.
4212 if (*in_mode == 'r') {
4215 #if defined(PERL_IMPLICIT_CONTEXT)
4216 /* Can not fork an xterm with a NULL context */
4217 /* This probably could never happen */
4221 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4222 if (xterm_fd != NULL)
4226 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4228 /* once-per-program initialization...
4229 note that the SETAST calls and the dual test of pipe_ef
4230 makes sure that only the FIRST thread through here does
4231 the initialization...all other threads wait until it's
4234 Yeah, uglier than a pthread call, it's got all the stuff inline
4235 rather than in a separate routine.
4239 _ckvmssts_noperl(sys$setast(0));
4241 unsigned long int pidcode = JPI$_PID;
4242 $DESCRIPTOR(d_delay, RETRY_DELAY);
4243 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4244 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4245 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4247 if (!handler_set_up) {
4248 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4249 handler_set_up = TRUE;
4251 _ckvmssts_noperl(sys$setast(1));
4254 /* see if we can find a VMSPIPE.COM */
4257 vmspipe = find_vmspipe(aTHX);
4259 vmspipedsc.dsc$w_length = my_strlcpy(tfilebuf+1, vmspipe, sizeof(tfilebuf)-1) + 1;
4260 } else { /* uh, oh...we're in tempfile hell */
4261 tpipe = vmspipe_tempfile(aTHX);
4262 if (!tpipe) { /* a fish popular in Boston */
4263 if (ckWARN(WARN_PIPE)) {
4264 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4268 fgetname(tpipe,tfilebuf+1,1);
4269 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4271 vmspipedsc.dsc$a_pointer = tfilebuf;
4273 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4276 case RMS$_FNF: case RMS$_DNF:
4277 set_errno(ENOENT); break;
4279 set_errno(ENOTDIR); break;
4281 set_errno(ENODEV); break;
4283 set_errno(EACCES); break;
4285 set_errno(EINVAL); break;
4286 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4287 set_errno(E2BIG); break;
4288 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4289 _ckvmssts_noperl(sts); /* fall through */
4290 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4293 set_vaxc_errno(sts);
4294 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4295 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4301 _ckvmssts_noperl(lib$get_vm(&n, &info));
4303 my_strlcpy(mode, in_mode, sizeof(mode));
4306 info->completion = 0;
4307 info->closing = FALSE;
4314 info->in_done = TRUE;
4315 info->out_done = TRUE;
4316 info->err_done = TRUE;
4318 info->xchan_valid = 0;
4320 in = (char *)PerlMem_malloc(VMS_MAXRSS);
4321 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4322 out = (char *)PerlMem_malloc(VMS_MAXRSS);
4323 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4324 err = (char *)PerlMem_malloc(VMS_MAXRSS);
4325 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4327 in[0] = out[0] = err[0] = '\0';
4329 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4333 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4338 if (*mode == 'r') { /* piping from subroutine */
4340 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4342 info->out->pipe_done = &info->out_done;
4343 info->out_done = FALSE;
4344 info->out->info = info;
4346 if (!info->useFILE) {
4347 info->fp = PerlIO_open(mbx, mode);
4349 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4350 vmssetuserlnm("SYS$INPUT", mbx);
4353 if (!info->fp && info->out) {
4354 sys$cancel(info->out->chan_out);
4356 while (!info->out_done) {
4358 _ckvmssts_noperl(sys$setast(0));
4359 done = info->out_done;
4360 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4361 _ckvmssts_noperl(sys$setast(1));
4362 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4365 if (info->out->buf) {
4366 n = info->out->bufsize * sizeof(char);
4367 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4370 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4372 _ckvmssts_noperl(lib$free_vm(&n, &info));
4377 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4379 info->err->pipe_done = &info->err_done;
4380 info->err_done = FALSE;
4381 info->err->info = info;
4384 } else if (*mode == 'w') { /* piping to subroutine */
4386 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4388 info->out->pipe_done = &info->out_done;
4389 info->out_done = FALSE;
4390 info->out->info = info;
4393 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4395 info->err->pipe_done = &info->err_done;
4396 info->err_done = FALSE;
4397 info->err->info = info;
4400 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4401 if (!info->useFILE) {
4402 info->fp = PerlIO_open(mbx, mode);
4404 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4405 vmssetuserlnm("SYS$OUTPUT", mbx);
4409 info->in->pipe_done = &info->in_done;
4410 info->in_done = FALSE;
4411 info->in->info = info;
4415 if (!info->fp && info->in) {
4417 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4418 0, 0, 0, 0, 0, 0, 0, 0));
4420 while (!info->in_done) {
4422 _ckvmssts_noperl(sys$setast(0));
4423 done = info->in_done;
4424 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4425 _ckvmssts_noperl(sys$setast(1));
4426 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4429 if (info->in->buf) {
4430 n = info->in->bufsize * sizeof(char);
4431 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4434 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4436 _ckvmssts_noperl(lib$free_vm(&n, &info));
4442 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4443 /* Let the child inherit standard input, unless it's a directory. */
4445 if (my_trnlnm("SYS$INPUT", in, 0)) {
4446 if (flex_stat(in, &st) != 0 || S_ISDIR(st.st_mode))
4450 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4452 info->out->pipe_done = &info->out_done;
4453 info->out_done = FALSE;
4454 info->out->info = info;
4457 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4459 info->err->pipe_done = &info->err_done;
4460 info->err_done = FALSE;
4461 info->err->info = info;
4465 d_symbol.dsc$w_length = my_strlcpy(symbol, in, sizeof(symbol));
4466 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4468 d_symbol.dsc$w_length = my_strlcpy(symbol, err, sizeof(symbol));
4469 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4471 d_symbol.dsc$w_length = my_strlcpy(symbol, out, sizeof(symbol));
4472 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4474 /* Done with the names for the pipes */
4479 p = vmscmd->dsc$a_pointer;
4480 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4481 if (*p == '$') p++; /* remove leading $ */
4482 while (*p == ' ' || *p == '\t') p++;
4484 for (j = 0; j < 4; j++) {
4485 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4486 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4488 d_symbol.dsc$w_length = my_strlcpy(symbol, p, sizeof(symbol));
4489 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4491 if (strlen(p) > MAX_DCL_SYMBOL) {
4492 p += MAX_DCL_SYMBOL;
4497 _ckvmssts_noperl(sys$setast(0));
4498 info->next=open_pipes; /* prepend to list */
4500 _ckvmssts_noperl(sys$setast(1));
4501 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4502 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4503 * have SYS$COMMAND if we need it.
4505 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4506 0, &info->pid, &info->completion,
4507 0, popen_completion_ast,info,0,0,0));
4509 /* if we were using a tempfile, close it now */
4511 if (tpipe) fclose(tpipe);
4513 /* once the subprocess is spawned, it has copied the symbols and
4514 we can get rid of ours */
4516 for (j = 0; j < 4; j++) {
4517 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4518 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4519 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4521 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4522 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4523 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4524 vms_execfree(vmscmd);
4526 #ifdef PERL_IMPLICIT_CONTEXT
4529 PL_forkprocess = info->pid;
4536 _ckvmssts_noperl(sys$setast(0));
4538 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4539 _ckvmssts_noperl(sys$setast(1));
4540 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4542 *psts = info->completion;
4543 /* Caller thinks it is open and tries to close it. */
4544 /* This causes some problems, as it changes the error status */
4545 /* my_pclose(info->fp); */
4547 /* If we did not have a file pointer open, then we have to */
4548 /* clean up here or eventually we will run out of something */
4550 if (info->fp == NULL) {
4551 my_pclose_pinfo(aTHX_ info);
4559 } /* end of safe_popen */
4562 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4564 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4568 TAINT_PROPER("popen");
4569 PERL_FLUSHALL_FOR_CHILD;
4570 return safe_popen(aTHX_ cmd,mode,&sts);
4576 /* Routine to close and cleanup a pipe info structure */
4579 my_pclose_pinfo(pTHX_ pInfo info) {
4581 unsigned long int retsts;
4585 /* If we were writing to a subprocess, insure that someone reading from
4586 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4587 * produce an EOF record in the mailbox.
4589 * well, at least sometimes it *does*, so we have to watch out for
4590 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4594 #if defined(USE_ITHREADS)
4598 && PL_perlio_fd_refcnt
4601 PerlIO_flush(info->fp);
4603 fflush((FILE *)info->fp);
4606 _ckvmssts(sys$setast(0));
4607 info->closing = TRUE;
4608 done = info->done && info->in_done && info->out_done && info->err_done;
4609 /* hanging on write to Perl's input? cancel it */
4610 if (info->mode == 'r' && info->out && !info->out_done) {
4611 if (info->out->chan_out) {
4612 _ckvmssts(sys$cancel(info->out->chan_out));
4613 if (!info->out->chan_in) { /* EOF generation, need AST */
4614 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4618 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4619 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4621 _ckvmssts(sys$setast(1));
4624 #if defined(USE_ITHREADS)
4628 && PL_perlio_fd_refcnt
4631 PerlIO_close(info->fp);
4633 fclose((FILE *)info->fp);
4636 we have to wait until subprocess completes, but ALSO wait until all
4637 the i/o completes...otherwise we'll be freeing the "info" structure
4638 that the i/o ASTs could still be using...
4642 _ckvmssts(sys$setast(0));
4643 done = info->done && info->in_done && info->out_done && info->err_done;
4644 if (!done) _ckvmssts(sys$clref(pipe_ef));
4645 _ckvmssts(sys$setast(1));
4646 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4648 retsts = info->completion;
4650 /* remove from list of open pipes */
4651 _ckvmssts(sys$setast(0));
4653 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4659 last->next = info->next;
4661 open_pipes = info->next;
4662 _ckvmssts(sys$setast(1));
4664 /* free buffers and structures */
4667 if (info->in->buf) {
4668 n = info->in->bufsize * sizeof(char);
4669 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4672 _ckvmssts(lib$free_vm(&n, &info->in));
4675 if (info->out->buf) {
4676 n = info->out->bufsize * sizeof(char);
4677 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4680 _ckvmssts(lib$free_vm(&n, &info->out));
4683 if (info->err->buf) {
4684 n = info->err->bufsize * sizeof(char);
4685 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4688 _ckvmssts(lib$free_vm(&n, &info->err));
4691 _ckvmssts(lib$free_vm(&n, &info));
4697 /*{{{ I32 my_pclose(PerlIO *fp)*/
4698 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4700 pInfo info, last = NULL;
4703 /* Fixme - need ast and mutex protection here */
4704 for (info = open_pipes; info != NULL; last = info, info = info->next)
4705 if (info->fp == fp) break;
4707 if (info == NULL) { /* no such pipe open */
4708 set_errno(ECHILD); /* quoth POSIX */
4709 set_vaxc_errno(SS$_NONEXPR);
4713 ret_status = my_pclose_pinfo(aTHX_ info);
4717 } /* end of my_pclose() */
4719 /* Roll our own prototype because we want this regardless of whether
4720 * _VMS_WAIT is defined.
4726 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4731 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4732 created with popen(); otherwise partially emulate waitpid() unless
4733 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4734 Also check processes not considered by the CRTL waitpid().
4736 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4738 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4745 if (statusp) *statusp = 0;
4747 for (info = open_pipes; info != NULL; info = info->next)
4748 if (info->pid == pid) break;
4750 if (info != NULL) { /* we know about this child */
4751 while (!info->done) {
4752 _ckvmssts(sys$setast(0));
4754 if (!done) _ckvmssts(sys$clref(pipe_ef));
4755 _ckvmssts(sys$setast(1));
4756 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4759 if (statusp) *statusp = info->completion;
4763 /* child that already terminated? */
4765 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4766 if (closed_list[j].pid == pid) {
4767 if (statusp) *statusp = closed_list[j].completion;
4772 /* fall through if this child is not one of our own pipe children */
4774 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4775 * in 7.2 did we get a version that fills in the VMS completion
4776 * status as Perl has always tried to do.
4779 sts = __vms_waitpid( pid, statusp, flags );
4781 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4784 /* If the real waitpid tells us the child does not exist, we
4785 * fall through here to implement waiting for a child that
4786 * was created by some means other than exec() (say, spawned
4787 * from DCL) or to wait for a process that is not a subprocess
4788 * of the current process.
4792 $DESCRIPTOR(intdsc,"0 00:00:01");
4793 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4794 unsigned long int pidcode = JPI$_PID, mypid;
4795 unsigned long int interval[2];
4796 unsigned int jpi_iosb[2];
4797 struct itmlst_3 jpilist[2] = {
4798 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4803 /* Sorry folks, we don't presently implement rooting around for
4804 the first child we can find, and we definitely don't want to
4805 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4811 /* Get the owner of the child so I can warn if it's not mine. If the
4812 * process doesn't exist or I don't have the privs to look at it,
4813 * I can go home early.
4815 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4816 if (sts & 1) sts = jpi_iosb[0];
4828 set_vaxc_errno(sts);
4832 if (ckWARN(WARN_EXEC)) {
4833 /* remind folks they are asking for non-standard waitpid behavior */
4834 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4835 if (ownerpid != mypid)
4836 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4837 "waitpid: process %x is not a child of process %x",
4841 /* simply check on it once a second until it's not there anymore. */
4843 _ckvmssts(sys$bintim(&intdsc,interval));
4844 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4845 _ckvmssts(sys$schdwk(0,0,interval,0));
4846 _ckvmssts(sys$hiber());
4848 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4853 } /* end of waitpid() */
4858 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4860 my_gconvert(double val, int ndig, int trail, char *buf)
4862 static char __gcvtbuf[DBL_DIG+1];
4865 loc = buf ? buf : __gcvtbuf;
4868 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4869 return gcvt(val,ndig,loc);
4872 loc[0] = '0'; loc[1] = '\0';
4879 #if !defined(NAML$C_MAXRSS)
4881 rms_free_search_context(struct FAB * fab)
4885 nam = fab->fab$l_nam;
4886 nam->nam$b_nop |= NAM$M_SYNCHK;
4887 nam->nam$l_rlf = NULL;
4889 return sys$parse(fab, NULL, NULL);
4892 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4893 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4894 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4895 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4896 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4897 #define rms_nam_esll(nam) nam.nam$b_esl
4898 #define rms_nam_esl(nam) nam.nam$b_esl
4899 #define rms_nam_name(nam) nam.nam$l_name
4900 #define rms_nam_namel(nam) nam.nam$l_name
4901 #define rms_nam_type(nam) nam.nam$l_type
4902 #define rms_nam_typel(nam) nam.nam$l_type
4903 #define rms_nam_ver(nam) nam.nam$l_ver
4904 #define rms_nam_verl(nam) nam.nam$l_ver
4905 #define rms_nam_rsll(nam) nam.nam$b_rsl
4906 #define rms_nam_rsl(nam) nam.nam$b_rsl
4907 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4908 #define rms_set_fna(fab, nam, name, size) \
4909 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4910 #define rms_get_fna(fab, nam) fab.fab$l_fna
4911 #define rms_set_dna(fab, nam, name, size) \
4912 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4913 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4914 #define rms_set_esa(nam, name, size) \
4915 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4916 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4917 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4918 #define rms_set_rsa(nam, name, size) \
4919 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4920 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4921 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4922 #define rms_nam_name_type_l_size(nam) \
4923 (nam.nam$b_name + nam.nam$b_type)
4926 rms_free_search_context(struct FAB * fab)
4930 nam = fab->fab$l_naml;
4931 nam->naml$b_nop |= NAM$M_SYNCHK;
4932 nam->naml$l_rlf = NULL;
4933 nam->naml$l_long_defname_size = 0;
4936 return sys$parse(fab, NULL, NULL);
4939 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4940 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4941 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4942 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4943 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4944 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4945 #define rms_nam_esl(nam) nam.naml$b_esl
4946 #define rms_nam_name(nam) nam.naml$l_name
4947 #define rms_nam_namel(nam) nam.naml$l_long_name
4948 #define rms_nam_type(nam) nam.naml$l_type
4949 #define rms_nam_typel(nam) nam.naml$l_long_type
4950 #define rms_nam_ver(nam) nam.naml$l_ver
4951 #define rms_nam_verl(nam) nam.naml$l_long_ver
4952 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4953 #define rms_nam_rsl(nam) nam.naml$b_rsl
4954 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4955 #define rms_set_fna(fab, nam, name, size) \
4956 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4957 nam.naml$l_long_filename_size = size; \
4958 nam.naml$l_long_filename = name;}
4959 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4960 #define rms_set_dna(fab, nam, name, size) \
4961 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4962 nam.naml$l_long_defname_size = size; \
4963 nam.naml$l_long_defname = name; }
4964 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4965 #define rms_set_esa(nam, name, size) \
4966 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4967 nam.naml$l_long_expand_alloc = size; \
4968 nam.naml$l_long_expand = name; }
4969 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4970 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4971 nam.naml$l_long_expand = l_name; \
4972 nam.naml$l_long_expand_alloc = l_size; }
4973 #define rms_set_rsa(nam, name, size) \
4974 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4975 nam.naml$l_long_result = name; \
4976 nam.naml$l_long_result_alloc = size; }
4977 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4978 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4979 nam.naml$l_long_result = l_name; \
4980 nam.naml$l_long_result_alloc = l_size; }
4981 #define rms_nam_name_type_l_size(nam) \
4982 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4987 * The CRTL for 8.3 and later can create symbolic links in any mode,
4988 * however in 8.3 the unlink/remove/delete routines will only properly handle
4989 * them if one of the PCP modes is active.
4992 rms_erase(const char * vmsname)
4995 struct FAB myfab = cc$rms_fab;
4996 rms_setup_nam(mynam);
4998 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4999 rms_bind_fab_nam(myfab, mynam);
5001 #ifdef NAML$M_OPEN_SPECIAL
5002 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5005 status = sys$erase(&myfab, 0, 0);
5012 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
5013 const struct dsc$descriptor_s * vms_dst_dsc,
5014 unsigned long flags)
5016 /* VMS and UNIX handle file permissions differently and the
5017 * the same ACL trick may be needed for renaming files,
5018 * especially if they are directories.
5021 /* todo: get kill_file and rename to share common code */
5022 /* I can not find online documentation for $change_acl
5023 * it appears to be replaced by $set_security some time ago */
5025 const unsigned int access_mode = 0;
5026 $DESCRIPTOR(obj_file_dsc,"FILE");
5029 unsigned long int jpicode = JPI$_UIC;
5030 int aclsts, fndsts, rnsts = -1;
5031 unsigned int ctx = 0;
5032 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5033 struct dsc$descriptor_s * clean_dsc;
5036 unsigned char myace$b_length;
5037 unsigned char myace$b_type;
5038 unsigned short int myace$w_flags;
5039 unsigned long int myace$l_access;
5040 unsigned long int myace$l_ident;
5041 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
5042 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
5044 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
5047 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
5048 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
5050 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
5051 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
5055 /* Expand the input spec using RMS, since we do not want to put
5056 * ACLs on the target of a symbolic link */
5057 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
5058 if (vmsname == NULL)
5061 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
5063 PERL_RMSEXPAND_M_SYMLINK);
5065 PerlMem_free(vmsname);
5069 /* So we get our own UIC to use as a rights identifier,
5070 * and the insert an ACE at the head of the ACL which allows us
5071 * to delete the file.
5073 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
5075 fildsc.dsc$w_length = strlen(vmsname);
5076 fildsc.dsc$a_pointer = vmsname;
5078 newace.myace$l_ident = oldace.myace$l_ident;
5081 /* Grab any existing ACEs with this identifier in case we fail */
5082 clean_dsc = &fildsc;
5083 aclsts = fndsts = sys$get_security(&obj_file_dsc,
5091 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
5092 /* Add the new ACE . . . */
5094 /* if the sys$get_security succeeded, then ctx is valid, and the
5095 * object/file descriptors will be ignored. But otherwise they
5098 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
5099 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5100 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5102 set_vaxc_errno(aclsts);
5103 PerlMem_free(vmsname);
5107 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5110 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5112 if ($VMS_STATUS_SUCCESS(rnsts)) {
5113 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5116 /* Put things back the way they were. */
5118 aclsts = sys$get_security(&obj_file_dsc,
5126 if ($VMS_STATUS_SUCCESS(aclsts)) {
5130 if (!$VMS_STATUS_SUCCESS(fndsts))
5131 sec_flags = OSS$M_RELCTX;
5133 /* Get rid of the new ACE */
5134 aclsts = sys$set_security(NULL, NULL, NULL,
5135 sec_flags, dellst, &ctx, &access_mode);
5137 /* If there was an old ACE, put it back */
5138 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5139 addlst[0].bufadr = &oldace;
5140 aclsts = sys$set_security(NULL, NULL, NULL,
5141 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5142 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5144 set_vaxc_errno(aclsts);
5150 /* Try to clear the lock on the ACL list */
5151 aclsts2 = sys$set_security(NULL, NULL, NULL,
5152 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5154 /* Rename errors are most important */
5155 if (!$VMS_STATUS_SUCCESS(rnsts))
5158 set_vaxc_errno(aclsts);
5163 if (aclsts != SS$_ACLEMPTY)
5170 PerlMem_free(vmsname);
5175 /*{{{int rename(const char *, const char * */
5176 /* Not exactly what X/Open says to do, but doing it absolutely right
5177 * and efficiently would require a lot more work. This should be close
5178 * enough to pass all but the most strict X/Open compliance test.
5181 Perl_rename(pTHX_ const char *src, const char * dst)
5190 /* Validate the source file */
5191 src_sts = flex_lstat(src, &src_st);
5194 /* No source file or other problem */
5197 if (src_st.st_devnam[0] == 0) {
5198 /* This may be possible so fail if it is seen. */
5203 dst_sts = flex_lstat(dst, &dst_st);
5206 if (dst_st.st_dev != src_st.st_dev) {
5207 /* Must be on the same device */
5212 /* VMS_INO_T_COMPARE is true if the inodes are different
5213 * to match the output of memcmp
5216 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5217 /* That was easy, the files are the same! */
5221 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5222 /* If source is a directory, so must be dest */
5230 if ((dst_sts == 0) &&
5231 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5233 /* We have issues here if vms_unlink_all_versions is set
5234 * If the destination exists, and is not a directory, then
5235 * we must delete in advance.
5237 * If the src is a directory, then we must always pre-delete
5240 * If we successfully delete the dst in advance, and the rename fails
5241 * X/Open requires that errno be EIO.
5245 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5247 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
5248 S_ISDIR(dst_st.st_mode));
5250 /* Need to delete all versions ? */
5251 if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5254 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
5255 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
5260 /* Make sure that we do not loop forever */
5272 /* We killed the destination, so only errno now is EIO */
5277 /* Originally the idea was to call the CRTL rename() and only
5278 * try the lib$rename_file if it failed.
5279 * It turns out that there are too many variants in what the
5280 * the CRTL rename might do, so only use lib$rename_file
5285 /* Is the source and dest both in VMS format */
5286 /* if the source is a directory, then need to fileify */
5287 /* and dest must be a directory or non-existent. */
5292 unsigned long flags;
5293 struct dsc$descriptor_s old_file_dsc;
5294 struct dsc$descriptor_s new_file_dsc;
5296 /* We need to modify the src and dst depending
5297 * on if one or more of them are directories.
5300 vms_dst = (char *)PerlMem_malloc(VMS_MAXRSS);
5301 if (vms_dst == NULL)
5302 _ckvmssts_noperl(SS$_INSFMEM);
5304 if (S_ISDIR(src_st.st_mode)) {
5306 char * vms_dir_file;
5308 vms_dir_file = (char *)PerlMem_malloc(VMS_MAXRSS);
5309 if (vms_dir_file == NULL)
5310 _ckvmssts_noperl(SS$_INSFMEM);
5312 /* If the dest is a directory, we must remove it */
5315 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
5317 PerlMem_free(vms_dst);
5325 /* The dest must be a VMS file specification */
5326 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5327 if (ret_str == NULL) {
5328 PerlMem_free(vms_dst);
5333 /* The source must be a file specification */
5334 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5335 if (ret_str == NULL) {
5336 PerlMem_free(vms_dst);
5337 PerlMem_free(vms_dir_file);
5341 PerlMem_free(vms_dst);
5342 vms_dst = vms_dir_file;
5345 /* File to file or file to new dir */
5347 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5348 /* VMS pathify a dir target */
5349 ret_str = int_tovmspath(dst, vms_dst, NULL);
5350 if (ret_str == NULL) {
5351 PerlMem_free(vms_dst);
5356 char * v_spec, * r_spec, * d_spec, * n_spec;
5357 char * e_spec, * vs_spec;
5358 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5360 /* fileify a target VMS file specification */
5361 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5362 if (ret_str == NULL) {
5363 PerlMem_free(vms_dst);
5368 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5369 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5370 &e_len, &vs_spec, &vs_len);
5373 /* Get rid of the version */
5377 /* Need to specify a '.' so that the extension */
5378 /* is not inherited */
5379 strcat(vms_dst,".");
5385 old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5386 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
5387 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5388 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5390 new_file_dsc.dsc$a_pointer = vms_dst;
5391 new_file_dsc.dsc$w_length = strlen(vms_dst);
5392 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5393 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5396 #if defined(NAML$C_MAXRSS)
5397 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5400 sts = lib$rename_file(&old_file_dsc,
5404 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5405 if (!$VMS_STATUS_SUCCESS(sts)) {
5407 /* We could have failed because VMS style permissions do not
5408 * permit renames that UNIX will allow. Just like the hack
5411 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5414 PerlMem_free(vms_dst);
5415 if (!$VMS_STATUS_SUCCESS(sts)) {
5422 if (vms_unlink_all_versions) {
5423 /* Now get rid of any previous versions of the source file that
5429 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5430 S_ISDIR(src_st.st_mode));
5431 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
5432 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5433 S_ISDIR(src_st.st_mode));
5438 /* Make sure that we do not loop forever */
5447 /* We deleted the destination, so must force the error to be EIO */
5448 if ((retval != 0) && (pre_delete != 0))
5456 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5457 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5458 * to expand file specification. Allows for a single default file
5459 * specification and a simple mask of options. If outbuf is non-NULL,
5460 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5461 * the resultant file specification is placed. If outbuf is NULL, the
5462 * resultant file specification is placed into a static buffer.
5463 * The third argument, if non-NULL, is taken to be a default file
5464 * specification string. The fourth argument is unused at present.
5465 * rmesexpand() returns the address of the resultant string if
5466 * successful, and NULL on error.
5468 * New functionality for previously unused opts value:
5469 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5470 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5471 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5472 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5474 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5478 (const char *filespec,
5480 const char *defspec,
5486 const char * in_spec;
5488 const char * def_spec;
5489 char * vmsfspec, *vmsdefspec;
5493 struct FAB myfab = cc$rms_fab;
5494 rms_setup_nam(mynam);
5496 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5499 /* temp hack until UTF8 is actually implemented */
5500 if (fs_utf8 != NULL)
5503 if (!filespec || !*filespec) {
5504 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5514 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5515 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5516 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5518 /* If this is a UNIX file spec, convert it to VMS */
5519 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5520 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5521 &e_len, &vs_spec, &vs_len);
5526 vmsfspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5527 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5528 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5529 if (ret_spec == NULL) {
5530 PerlMem_free(vmsfspec);
5533 in_spec = (const char *)vmsfspec;
5535 /* Unless we are forcing to VMS format, a UNIX input means
5536 * UNIX output, and that requires long names to be used
5538 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5539 #if defined(NAML$C_MAXRSS)
5540 opts |= PERL_RMSEXPAND_M_LONG;
5550 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5551 rms_bind_fab_nam(myfab, mynam);
5553 /* Process the default file specification if present */
5555 if (defspec && *defspec) {
5557 t_isunix = is_unix_filespec(defspec);
5559 vmsdefspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5560 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5561 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5563 if (ret_spec == NULL) {
5564 /* Clean up and bail */
5565 PerlMem_free(vmsdefspec);
5566 if (vmsfspec != NULL)
5567 PerlMem_free(vmsfspec);
5570 def_spec = (const char *)vmsdefspec;
5572 rms_set_dna(myfab, mynam,
5573 (char *)def_spec, strlen(def_spec)); /* cast ok */
5576 /* Now we need the expansion buffers */
5577 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
5578 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5579 #if defined(NAML$C_MAXRSS)
5580 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
5581 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5583 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5585 /* If a NAML block is used RMS always writes to the long and short
5586 * addresses unless you suppress the short name.
5588 #if defined(NAML$C_MAXRSS)
5589 outbufl = (char *)PerlMem_malloc(VMS_MAXRSS);
5590 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5592 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5594 #ifdef NAM$M_NO_SHORT_UPCASE
5595 if (DECC_EFS_CASE_PRESERVE)
5596 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5599 /* We may not want to follow symbolic links */
5600 #ifdef NAML$M_OPEN_SPECIAL
5601 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5602 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5605 /* First attempt to parse as an existing file */
5606 retsts = sys$parse(&myfab,0,0);
5607 if (!(retsts & STS$K_SUCCESS)) {
5609 /* Could not find the file, try as syntax only if error is not fatal */
5610 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5611 if (retsts == RMS$_DNF ||
5612 retsts == RMS$_DIR ||
5613 retsts == RMS$_DEV ||
5614 retsts == RMS$_PRV) {
5615 retsts = sys$parse(&myfab,0,0);
5616 if (retsts & STS$K_SUCCESS) goto int_expanded;
5619 /* Still could not parse the file specification */
5620 /*----------------------------------------------*/
5621 sts = rms_free_search_context(&myfab); /* Free search context */
5622 if (vmsdefspec != NULL)
5623 PerlMem_free(vmsdefspec);
5624 if (vmsfspec != NULL)
5625 PerlMem_free(vmsfspec);
5626 if (outbufl != NULL)
5627 PerlMem_free(outbufl);
5631 set_vaxc_errno(retsts);
5632 if (retsts == RMS$_PRV) set_errno(EACCES);
5633 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5634 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5635 else set_errno(EVMSERR);
5638 retsts = sys$search(&myfab,0,0);
5639 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5640 sts = rms_free_search_context(&myfab); /* Free search context */
5641 if (vmsdefspec != NULL)
5642 PerlMem_free(vmsdefspec);
5643 if (vmsfspec != NULL)
5644 PerlMem_free(vmsfspec);
5645 if (outbufl != NULL)
5646 PerlMem_free(outbufl);
5650 set_vaxc_errno(retsts);
5651 if (retsts == RMS$_PRV) set_errno(EACCES);
5652 else set_errno(EVMSERR);
5656 /* If the input filespec contained any lowercase characters,
5657 * downcase the result for compatibility with Unix-minded code. */
5659 if (!DECC_EFS_CASE_PRESERVE) {
5661 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5662 if (islower(*tbuf)) { haslower = 1; break; }
5665 /* Is a long or a short name expected */
5666 /*------------------------------------*/
5668 #if defined(NAML$C_MAXRSS)
5669 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5670 if (rms_nam_rsll(mynam)) {
5672 speclen = rms_nam_rsll(mynam);
5675 spec_buf = esal; /* Not esa */
5676 speclen = rms_nam_esll(mynam);
5681 if (rms_nam_rsl(mynam)) {
5683 speclen = rms_nam_rsl(mynam);
5686 spec_buf = esa; /* Not esal */
5687 speclen = rms_nam_esl(mynam);
5689 #if defined(NAML$C_MAXRSS)
5692 spec_buf[speclen] = '\0';
5694 /* Trim off null fields added by $PARSE
5695 * If type > 1 char, must have been specified in original or default spec
5696 * (not true for version; $SEARCH may have added version of existing file).
5698 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5699 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5700 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5701 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5704 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5705 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5707 if (trimver || trimtype) {
5708 if (defspec && *defspec) {
5709 char *defesal = NULL;
5710 char *defesa = NULL;
5711 defesa = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5712 if (defesa != NULL) {
5713 struct FAB deffab = cc$rms_fab;
5714 #if defined(NAML$C_MAXRSS)
5715 defesal = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5716 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5718 rms_setup_nam(defnam);
5720 rms_bind_fab_nam(deffab, defnam);
5724 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5726 /* RMS needs the esa/esal as a work area if wildcards are involved */
5727 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5729 rms_clear_nam_nop(defnam);
5730 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5731 #ifdef NAM$M_NO_SHORT_UPCASE
5732 if (DECC_EFS_CASE_PRESERVE)
5733 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5735 #ifdef NAML$M_OPEN_SPECIAL
5736 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5737 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5739 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5741 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5744 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5747 if (defesal != NULL)
5748 PerlMem_free(defesal);
5749 PerlMem_free(defesa);
5751 _ckvmssts_noperl(SS$_INSFMEM);
5755 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5756 if (*(rms_nam_verl(mynam)) != '\"')
5757 speclen = rms_nam_verl(mynam) - spec_buf;
5760 if (*(rms_nam_ver(mynam)) != '\"')
5761 speclen = rms_nam_ver(mynam) - spec_buf;
5765 /* If we didn't already trim version, copy down */
5766 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5767 if (speclen > rms_nam_verl(mynam) - spec_buf)
5769 (rms_nam_typel(mynam),
5770 rms_nam_verl(mynam),
5771 speclen - (rms_nam_verl(mynam) - spec_buf));
5772 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5775 if (speclen > rms_nam_ver(mynam) - spec_buf)
5777 (rms_nam_type(mynam),
5779 speclen - (rms_nam_ver(mynam) - spec_buf));
5780 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5785 /* Done with these copies of the input files */
5786 /*-------------------------------------------*/
5787 if (vmsfspec != NULL)
5788 PerlMem_free(vmsfspec);
5789 if (vmsdefspec != NULL)
5790 PerlMem_free(vmsdefspec);
5792 /* If we just had a directory spec on input, $PARSE "helpfully"
5793 * adds an empty name and type for us */
5794 #if defined(NAML$C_MAXRSS)
5795 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5796 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5797 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5798 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5799 speclen = rms_nam_namel(mynam) - spec_buf;
5804 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5805 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5806 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5807 speclen = rms_nam_name(mynam) - spec_buf;
5810 /* Posix format specifications must have matching quotes */
5811 if (speclen < (VMS_MAXRSS - 1)) {
5812 if (DECC_POSIX_COMPLIANT_PATHNAMES && (spec_buf[0] == '\"')) {
5813 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5814 spec_buf[speclen] = '\"';
5819 spec_buf[speclen] = '\0';
5820 if (haslower && !DECC_EFS_CASE_PRESERVE) __mystrtolower(spec_buf);
5822 /* Have we been working with an expanded, but not resultant, spec? */
5823 /* Also, convert back to Unix syntax if necessary. */
5827 #if defined(NAML$C_MAXRSS)
5828 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5829 rsl = rms_nam_rsll(mynam);
5833 rsl = rms_nam_rsl(mynam);
5836 /* rsl is not present, it means that spec_buf is either */
5837 /* esa or esal, and needs to be copied to outbuf */
5838 /* convert to Unix if desired */
5840 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5842 /* VMS file specs are not in UTF-8 */
5843 if (fs_utf8 != NULL)
5845 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5850 /* Now spec_buf is either outbuf or outbufl */
5851 /* We need the result into outbuf */
5853 /* If we need this in UNIX, then we need another buffer */
5854 /* to keep things in order */
5856 char * new_src = NULL;
5857 if (spec_buf == outbuf) {
5858 new_src = (char *)PerlMem_malloc(VMS_MAXRSS);
5859 my_strlcpy(new_src, spec_buf, VMS_MAXRSS);
5863 ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5865 PerlMem_free(new_src);
5868 /* VMS file specs are not in UTF-8 */
5869 if (fs_utf8 != NULL)
5872 /* Copy the buffer if needed */
5873 if (outbuf != spec_buf)
5874 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5880 /* Need to clean up the search context */
5881 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5882 sts = rms_free_search_context(&myfab); /* Free search context */
5884 /* Clean up the extra buffers */
5888 if (outbufl != NULL)
5889 PerlMem_free(outbufl);
5891 /* Return the result */
5895 /* Common simple case - Expand an already VMS spec */
5897 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5898 opts |= PERL_RMSEXPAND_M_VMS_IN;
5899 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5902 /* Common simple case - Expand to a VMS spec */
5904 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5905 opts |= PERL_RMSEXPAND_M_VMS;
5906 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5910 /* Entry point used by perl routines */
5913 (pTHX_ const char *filespec,
5916 const char *defspec,
5921 static char __rmsexpand_retbuf[VMS_MAXRSS];
5922 char * expanded, *ret_spec, *ret_buf;
5926 if (ret_buf == NULL) {
5928 Newx(expanded, VMS_MAXRSS, char);
5929 if (expanded == NULL)
5930 _ckvmssts(SS$_INSFMEM);
5933 ret_buf = __rmsexpand_retbuf;
5938 ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5939 opts, fs_utf8, dfs_utf8);
5941 if (ret_spec == NULL) {
5942 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5950 /* External entry points */
5952 Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5954 return do_rmsexpand(spec, buf, 0, def, opt, NULL, NULL);
5958 Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5960 return do_rmsexpand(spec, buf, 1, def, opt, NULL, NULL);
5964 Perl_rmsexpand_utf8(pTHX_ const char *spec, char *buf, const char *def,
5965 unsigned opt, int * fs_utf8, int * dfs_utf8)
5967 return do_rmsexpand(spec, buf, 0, def, opt, fs_utf8, dfs_utf8);
5971 Perl_rmsexpand_utf8_ts(pTHX_ const char *spec, char *buf, const char *def,
5972 unsigned opt, int * fs_utf8, int * dfs_utf8)
5974 return do_rmsexpand(spec, buf, 1, def, opt, fs_utf8, dfs_utf8);
5979 ** The following routines are provided to make life easier when
5980 ** converting among VMS-style and Unix-style directory specifications.
5981 ** All will take input specifications in either VMS or Unix syntax. On
5982 ** failure, all return NULL. If successful, the routines listed below
5983 ** return a pointer to a buffer containing the appropriately
5984 ** reformatted spec (and, therefore, subsequent calls to that routine
5985 ** will clobber the result), while the routines of the same names with
5986 ** a _ts suffix appended will return a pointer to a mallocd string
5987 ** containing the appropriately reformatted spec.
5988 ** In all cases, only explicit syntax is altered; no check is made that
5989 ** the resulting string is valid or that the directory in question
5992 ** fileify_dirspec() - convert a directory spec into the name of the
5993 ** directory file (i.e. what you can stat() to see if it's a dir).
5994 ** The style (VMS or Unix) of the result is the same as the style
5995 ** of the parameter passed in.
5996 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5997 ** what you prepend to a filename to indicate what directory it's in).
5998 ** The style (VMS or Unix) of the result is the same as the style
5999 ** of the parameter passed in.
6000 ** tounixpath() - convert a directory spec into a Unix-style path.
6001 ** tovmspath() - convert a directory spec into a VMS-style path.
6002 ** tounixspec() - convert any file spec into a Unix-style file spec.
6003 ** tovmsspec() - convert any file spec into a VMS-style spec.
6004 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
6006 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
6007 ** Permission is given to distribute this code as part of the Perl
6008 ** standard distribution under the terms of the GNU General Public
6009 ** License or the Perl Artistic License. Copies of each may be
6010 ** found in the Perl standard distribution.
6013 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6015 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
6017 unsigned long int dirlen, retlen, hasfilename = 0;
6018 char *cp1, *cp2, *lastdir;
6019 char *trndir, *vmsdir;
6020 unsigned short int trnlnm_iter_count;
6022 if (utf8_fl != NULL)
6025 if (!dir || !*dir) {
6026 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6028 dirlen = strlen(dir);
6029 while (dirlen && dir[dirlen-1] == '/') --dirlen;
6030 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
6031 if (!DECC_POSIX_COMPLIANT_PATHNAMES && DECC_DISABLE_POSIX_ROOT) {
6038 if (dirlen > (VMS_MAXRSS - 1)) {
6039 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
6042 trndir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
6043 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6044 if (!strpbrk(dir+1,"/]>:") &&
6045 (!DECC_POSIX_COMPLIANT_PATHNAMES && DECC_DISABLE_POSIX_ROOT)) {
6046 strcpy(trndir,*dir == '/' ? dir + 1: dir);
6047 trnlnm_iter_count = 0;
6048 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
6049 trnlnm_iter_count++;
6050 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6052 dirlen = strlen(trndir);
6055 memcpy(trndir, dir, dirlen);
6056 trndir[dirlen] = '\0';
6059 /* At this point we are done with *dir and use *trndir which is a
6060 * copy that can be modified. *dir must not be modified.
6063 /* If we were handed a rooted logical name or spec, treat it like a
6064 * simple directory, so that
6065 * $ Define myroot dev:[dir.]
6066 * ... do_fileify_dirspec("myroot",buf,1) ...
6067 * does something useful.
6069 if (dirlen >= 2 && strEQ(trndir+dirlen-2,".]")) {
6070 trndir[--dirlen] = '\0';
6071 trndir[dirlen-1] = ']';
6073 if (dirlen >= 2 && strEQ(trndir+dirlen-2,".>")) {
6074 trndir[--dirlen] = '\0';
6075 trndir[dirlen-1] = '>';
6078 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
6079 /* If we've got an explicit filename, we can just shuffle the string. */
6080 if (*(cp1+1)) hasfilename = 1;
6081 /* Similarly, we can just back up a level if we've got multiple levels
6082 of explicit directories in a VMS spec which ends with directories. */
6084 for (cp2 = cp1; cp2 > trndir; cp2--) {
6086 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
6087 /* fix-me, can not scan EFS file specs backward like this */
6088 *cp2 = *cp1; *cp1 = '\0';
6093 if (*cp2 == '[' || *cp2 == '<') break;
6098 vmsdir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
6099 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6100 cp1 = strpbrk(trndir,"]:>");
6101 if (cp1 && *(cp1+1) == ':') /* DECNet node spec with :: */
6102 cp1 = strpbrk(cp1+2,"]:>");
6104 if (hasfilename || !cp1) { /* filename present or not VMS */
6106 if (trndir[0] == '.') {
6107 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
6108 PerlMem_free(trndir);
6109 PerlMem_free(vmsdir);
6110 return int_fileify_dirspec("[]", buf, NULL);
6112 else if (trndir[1] == '.' &&
6113 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
6114 PerlMem_free(trndir);
6115 PerlMem_free(vmsdir);
6116 return int_fileify_dirspec("[-]", buf, NULL);
6119 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
6120 dirlen -= 1; /* to last element */
6121 lastdir = strrchr(trndir,'/');
6123 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
6124 /* If we have "/." or "/..", VMSify it and let the VMS code
6125 * below expand it, rather than repeating the code to handle
6126 * relative components of a filespec here */
6128 if (*(cp1+2) == '.') cp1++;
6129 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6131 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6132 PerlMem_free(trndir);
6133 PerlMem_free(vmsdir);
6136 if (strchr(vmsdir,'/') != NULL) {
6137 /* If int_tovmsspec() returned it, it must have VMS syntax
6138 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6139 * the time to check this here only so we avoid a recursion
6140 * loop; otherwise, gigo.
6142 PerlMem_free(trndir);
6143 PerlMem_free(vmsdir);
6144 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6147 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6148 PerlMem_free(trndir);
6149 PerlMem_free(vmsdir);
6152 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6153 PerlMem_free(trndir);
6154 PerlMem_free(vmsdir);
6158 } while ((cp1 = strstr(cp1,"/.")) != NULL);
6159 lastdir = strrchr(trndir,'/');
6161 else if (dirlen >= 7 && strEQ(&trndir[dirlen-7],"/000000")) {
6163 /* Ditto for specs that end in an MFD -- let the VMS code
6164 * figure out whether it's a real device or a rooted logical. */
6166 /* This should not happen any more. Allowing the fake /000000
6167 * in a UNIX pathname causes all sorts of problems when trying
6168 * to run in UNIX emulation. So the VMS to UNIX conversions
6169 * now remove the fake /000000 directories.
6172 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6173 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6174 PerlMem_free(trndir);
6175 PerlMem_free(vmsdir);
6178 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6179 PerlMem_free(trndir);
6180 PerlMem_free(vmsdir);
6183 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6184 PerlMem_free(trndir);
6185 PerlMem_free(vmsdir);
6190 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6191 !(lastdir = cp1 = strrchr(trndir,']')) &&
6192 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6194 cp2 = strrchr(cp1,'.');
6196 int e_len, vs_len = 0;
6199 cp3 = strchr(cp2,';');
6200 e_len = strlen(cp2);
6202 vs_len = strlen(cp3);
6203 e_len = e_len - vs_len;
6205 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6207 if (!DECC_EFS_CHARSET) {
6208 /* If this is not EFS, then not a directory */
6209 PerlMem_free(trndir);
6210 PerlMem_free(vmsdir);
6212 set_vaxc_errno(RMS$_DIR);
6216 /* Ok, here we have an issue, technically if a .dir shows */
6217 /* from inside a directory, then we should treat it as */
6218 /* xxx^.dir.dir. But we do not have that context at this */
6219 /* point unless this is totally restructured, so we remove */
6220 /* The .dir for now, and fix this better later */
6221 dirlen = cp2 - trndir;
6223 if (DECC_EFS_CHARSET && !strchr(trndir,'/')) {
6224 /* Dots are allowed in dir names, so escape them if input not in Unix syntax. */
6225 char *cp4 = is_dir ? (cp2 - 1) : cp2;
6227 for (; cp4 > cp1; cp4--) {
6229 if ((cp4 - 1 > trndir) && (*(cp4 - 1) != '^')) {
6230 memmove(cp4 + 1, cp4, trndir + dirlen - cp4 + 1);
6241 retlen = dirlen + 6;
6242 memcpy(buf, trndir, dirlen);
6245 /* We've picked up everything up to the directory file name.
6246 Now just add the type and version, and we're set. */
6247 if ((!DECC_EFS_CASE_PRESERVE) && vms_process_case_tolerant)
6251 if (!DECC_FILENAME_UNIX_NO_VERSION)
6253 PerlMem_free(trndir);
6254 PerlMem_free(vmsdir);
6257 else { /* VMS-style directory spec */
6259 char *esa, *esal, term, *cp;
6262 unsigned long int cmplen, haslower = 0;
6263 struct FAB dirfab = cc$rms_fab;
6264 rms_setup_nam(savnam);
6265 rms_setup_nam(dirnam);
6267 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
6268 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6270 #if defined(NAML$C_MAXRSS)
6271 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
6272 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6274 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6275 rms_bind_fab_nam(dirfab, dirnam);
6276 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6277 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6278 #ifdef NAM$M_NO_SHORT_UPCASE
6279 if (DECC_EFS_CASE_PRESERVE)
6280 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6283 for (cp = trndir; *cp; cp++)
6284 if (islower(*cp)) { haslower = 1; break; }
6285 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6286 if ((dirfab.fab$l_sts == RMS$_DIR) ||
6287 (dirfab.fab$l_sts == RMS$_DNF) ||
6288 (dirfab.fab$l_sts == RMS$_PRV)) {
6289 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6290 sts = sys$parse(&dirfab);
6296 PerlMem_free(trndir);
6297 PerlMem_free(vmsdir);
6299 set_vaxc_errno(dirfab.fab$l_sts);
6305 /* Does the file really exist? */
6306 if (sys$search(&dirfab)& STS$K_SUCCESS) {
6307 /* Yes; fake the fnb bits so we'll check type below */
6308 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6310 else { /* No; just work with potential name */
6311 if (dirfab.fab$l_sts == RMS$_FNF
6312 || dirfab.fab$l_sts == RMS$_DNF
6313 || dirfab.fab$l_sts == RMS$_FND)
6317 fab_sts = dirfab.fab$l_sts;
6318 sts = rms_free_search_context(&dirfab);
6322 PerlMem_free(trndir);
6323 PerlMem_free(vmsdir);
6324 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
6330 /* Make sure we are using the right buffer */
6331 #if defined(NAML$C_MAXRSS)
6334 my_esa_len = rms_nam_esll(dirnam);
6338 my_esa_len = rms_nam_esl(dirnam);
6339 #if defined(NAML$C_MAXRSS)
6342 my_esa[my_esa_len] = '\0';
6343 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6344 cp1 = strchr(my_esa,']');
6345 if (!cp1) cp1 = strchr(my_esa,'>');
6346 if (cp1) { /* Should always be true */
6347 my_esa_len -= cp1 - my_esa - 1;
6348 memmove(my_esa, cp1 + 1, my_esa_len);
6351 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6352 /* Yep; check version while we're at it, if it's there. */
6353 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6354 if (strnNE(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6355 /* Something other than .DIR[;1]. Bzzt. */
6356 sts = rms_free_search_context(&dirfab);
6360 PerlMem_free(trndir);
6361 PerlMem_free(vmsdir);
6363 set_vaxc_errno(RMS$_DIR);
6368 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6369 /* They provided at least the name; we added the type, if necessary, */
6370 my_strlcpy(buf, my_esa, VMS_MAXRSS);
6371 sts = rms_free_search_context(&dirfab);
6372 PerlMem_free(trndir);
6376 PerlMem_free(vmsdir);
6379 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6380 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6384 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6385 if (cp1 == NULL) { /* should never happen */
6386 sts = rms_free_search_context(&dirfab);
6387 PerlMem_free(trndir);
6391 PerlMem_free(vmsdir);
6396 retlen = strlen(my_esa);
6397 cp1 = strrchr(my_esa,'.');
6398 /* ODS-5 directory specifications can have extra "." in them. */
6399 /* Fix-me, can not scan EFS file specifications backwards */
6400 while (cp1 != NULL) {
6401 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6405 while ((cp1 > my_esa) && (*cp1 != '.'))
6412 if ((cp1) != NULL) {
6413 /* There's more than one directory in the path. Just roll back. */
6415 my_strlcpy(buf, my_esa, VMS_MAXRSS);
6418 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6419 /* Go back and expand rooted logical name */
6420 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6421 #ifdef NAM$M_NO_SHORT_UPCASE
6422 if (DECC_EFS_CASE_PRESERVE)
6423 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6425 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6426 sts = rms_free_search_context(&dirfab);
6430 PerlMem_free(trndir);
6431 PerlMem_free(vmsdir);
6433 set_vaxc_errno(dirfab.fab$l_sts);
6437 /* This changes the length of the string of course */
6439 my_esa_len = rms_nam_esll(dirnam);
6441 my_esa_len = rms_nam_esl(dirnam);
6444 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6445 cp1 = strstr(my_esa,"][");
6446 if (!cp1) cp1 = strstr(my_esa,"]<");
6447 dirlen = cp1 - my_esa;
6448 memcpy(buf, my_esa, dirlen);
6449 if (strBEGINs(cp1+2,"000000]")) {
6450 buf[dirlen-1] = '\0';
6451 /* fix-me Not full ODS-5, just extra dots in directories for now */
6452 cp1 = buf + dirlen - 1;
6458 if (*(cp1-1) != '^')
6463 if (*cp1 == '.') *cp1 = ']';
6465 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6466 memmove(cp1+1,"000000]",7);
6470 memmove(buf+dirlen, cp1+2, retlen-dirlen);
6472 /* Convert last '.' to ']' */
6474 while (*cp != '[') {
6477 /* Do not trip on extra dots in ODS-5 directories */
6478 if ((cp1 == buf) || (*(cp1-1) != '^'))
6482 if (*cp1 == '.') *cp1 = ']';
6484 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6485 memmove(cp1+1,"000000]",7);
6489 else { /* This is a top-level dir. Add the MFD to the path. */
6490 cp1 = strrchr(my_esa, ':');
6492 memmove(buf, my_esa, cp1 - my_esa + 1);
6493 memmove(buf + (cp1 - my_esa) + 1, "[000000]", 8);
6494 memmove(buf + (cp1 - my_esa) + 9, cp1 + 2, retlen - (cp1 - my_esa + 2));
6495 buf[retlen + 7] = '\0'; /* We've inserted '000000]' */
6498 sts = rms_free_search_context(&dirfab);
6499 /* We've set up the string up through the filename. Add the
6500 type and version, and we're done. */
6501 strcat(buf,".DIR;1");
6503 /* $PARSE may have upcased filespec, so convert output to lower
6504 * case if input contained any lowercase characters. */
6505 if (haslower && !DECC_EFS_CASE_PRESERVE) __mystrtolower(buf);
6506 PerlMem_free(trndir);
6510 PerlMem_free(vmsdir);
6513 } /* end of int_fileify_dirspec() */
6516 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6518 mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6520 static char __fileify_retbuf[VMS_MAXRSS];
6521 char * fileified, *ret_spec, *ret_buf;
6525 if (ret_buf == NULL) {
6527 Newx(fileified, VMS_MAXRSS, char);
6528 if (fileified == NULL)
6529 _ckvmssts(SS$_INSFMEM);
6530 ret_buf = fileified;
6532 ret_buf = __fileify_retbuf;
6536 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6538 if (ret_spec == NULL) {
6539 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6541 Safefree(fileified);
6545 } /* end of do_fileify_dirspec() */
6548 /* External entry points */
6550 Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6552 return do_fileify_dirspec(dir, buf, 0, NULL);
6556 Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6558 return do_fileify_dirspec(dir, buf, 1, NULL);
6562 Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6564 return do_fileify_dirspec(dir, buf, 0, utf8_fl);
6568 Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6570 return do_fileify_dirspec(dir, buf, 1, utf8_fl);
6574 int_pathify_dirspec_simple(const char * dir, char * buf,
6575 char * v_spec, int v_len, char * r_spec, int r_len,
6576 char * d_spec, int d_len, char * n_spec, int n_len,
6577 char * e_spec, int e_len, char * vs_spec, int vs_len)
6580 /* VMS specification - Try to do this the simple way */
6581 if ((v_len + r_len > 0) || (d_len > 0)) {
6584 /* No name or extension component, already a directory */
6585 if ((n_len + e_len + vs_len) == 0) {
6590 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6591 /* This results from catfile() being used instead of catdir() */
6592 /* So even though it should not work, we need to allow it */
6594 /* If this is .DIR;1 then do a simple conversion */
6595 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6596 if (is_dir || (e_len == 0) && (d_len > 0)) {
6598 len = v_len + r_len + d_len - 1;
6599 char dclose = d_spec[d_len - 1];
6600 memcpy(buf, dir, len);
6603 memcpy(&buf[len], n_spec, n_len);
6606 buf[len + 1] = '\0';
6611 else if (d_len > 0) {
6612 /* In the olden days, a directory needed to have a .DIR */
6613 /* extension to be a valid directory, but now it could */
6614 /* be a symbolic link */
6616 len = v_len + r_len + d_len - 1;
6617 char dclose = d_spec[d_len - 1];
6618 memcpy(buf, dir, len);
6621 memcpy(&buf[len], n_spec, n_len);
6624 if (DECC_EFS_CHARSET) {
6626 && (toUPPER_A(e_spec[1]) == 'D')
6627 && (toUPPER_A(e_spec[2]) == 'I')
6628 && (toUPPER_A(e_spec[3]) == 'R')) {
6630 /* Corner case: directory spec with invalid version.
6631 * Valid would have followed is_dir path above.
6633 SETERRNO(ENOTDIR, RMS$_DIR);
6639 memcpy(&buf[len], e_spec, e_len);
6644 SETERRNO(ENOTDIR, RMS$_DIR);
6649 buf[len + 1] = '\0';
6654 set_vaxc_errno(RMS$_DIR);
6660 set_vaxc_errno(RMS$_DIR);
6666 /* Internal routine to make sure or convert a directory to be in a */
6667 /* path specification. No utf8 flag because it is not changed or used */
6669 int_pathify_dirspec(const char *dir, char *buf)
6671 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6672 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6673 char * exp_spec, *ret_spec;
6675 unsigned short int trnlnm_iter_count;
6679 if (vms_debug_fileify) {
6681 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6683 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6686 /* We may need to lower case the result if we translated */
6687 /* a logical name or got the current working directory */
6690 if (!dir || !*dir) {
6692 set_vaxc_errno(SS$_BADPARAM);
6696 trndir = (char *)PerlMem_malloc(VMS_MAXRSS);
6698 _ckvmssts_noperl(SS$_INSFMEM);
6700 /* If no directory specified use the current default */
6702 my_strlcpy(trndir, dir, VMS_MAXRSS);
6704 getcwd(trndir, VMS_MAXRSS - 1);
6708 /* now deal with bare names that could be logical names */
6709 trnlnm_iter_count = 0;
6710 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6711 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6712 trnlnm_iter_count++;
6714 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6716 trnlen = strlen(trndir);
6718 /* Trap simple rooted lnms, and return lnm:[000000] */
6719 if (strEQ(trndir+trnlen-2,".]")) {
6720 my_strlcpy(buf, dir, VMS_MAXRSS);
6721 strcat(buf, ":[000000]");
6722 PerlMem_free(trndir);
6724 if (vms_debug_fileify) {
6725 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6731 /* At this point we do not work with *dir, but the copy in *trndir */
6733 if (need_to_lower && !DECC_EFS_CASE_PRESERVE) {
6734 /* Legacy mode, lower case the returned value */
6735 __mystrtolower(trndir);
6739 /* Some special cases, '..', '.' */
6741 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6742 /* Force UNIX filespec */
6746 /* Is this Unix or VMS format? */
6747 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6748 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6749 &e_len, &vs_spec, &vs_len);
6752 /* Just a filename? */
6753 if ((v_len + r_len + d_len) == 0) {
6755 /* Now we have a problem, this could be Unix or VMS */
6756 /* We have to guess. .DIR usually means VMS */
6758 /* In UNIX report mode, the .DIR extension is removed */
6759 /* if one shows up, it is for a non-directory or a directory */
6760 /* in EFS charset mode */
6762 /* So if we are in Unix report mode, assume that this */
6763 /* is a relative Unix directory specification */
6766 if (!DECC_FILENAME_UNIX_REPORT && DECC_EFS_CHARSET) {
6768 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6771 /* Traditional mode, assume .DIR is directory */
6774 memcpy(&buf[2], n_spec, n_len);
6775 buf[n_len + 2] = ']';
6776 buf[n_len + 3] = '\0';
6777 PerlMem_free(trndir);
6778 if (vms_debug_fileify) {
6780 "int_pathify_dirspec: buf = %s\n",
6790 ret_spec = int_pathify_dirspec_simple(trndir, buf,
6791 v_spec, v_len, r_spec, r_len,
6792 d_spec, d_len, n_spec, n_len,
6793 e_spec, e_len, vs_spec, vs_len);
6795 if (ret_spec != NULL) {
6796 PerlMem_free(trndir);
6797 if (vms_debug_fileify) {
6799 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6804 /* Simple way did not work, which means that a logical name */
6805 /* was present for the directory specification. */
6806 /* Need to use an rmsexpand variant to decode it completely */
6807 exp_spec = (char *)PerlMem_malloc(VMS_MAXRSS);
6808 if (exp_spec == NULL)
6809 _ckvmssts_noperl(SS$_INSFMEM);
6811 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6812 if (ret_spec != NULL) {
6813 sts = vms_split_path(exp_spec, &v_spec, &v_len,
6814 &r_spec, &r_len, &d_spec, &d_len,
6815 &n_spec, &n_len, &e_spec,
6816 &e_len, &vs_spec, &vs_len);
6818 ret_spec = int_pathify_dirspec_simple(
6819 exp_spec, buf, v_spec, v_len, r_spec, r_len,
6820 d_spec, d_len, n_spec, n_len,
6821 e_spec, e_len, vs_spec, vs_len);
6823 if ((ret_spec != NULL) && (!DECC_EFS_CASE_PRESERVE)) {
6824 /* Legacy mode, lower case the returned value */
6825 __mystrtolower(ret_spec);
6828 set_vaxc_errno(RMS$_DIR);
6833 PerlMem_free(exp_spec);
6834 PerlMem_free(trndir);
6835 if (vms_debug_fileify) {
6836 if (ret_spec == NULL)
6837 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6840 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6845 /* Unix specification, Could be trivial conversion, */
6846 /* but have to deal with trailing '.dir' or extra '.' */
6851 STRLEN dir_len = strlen(trndir);
6853 lastslash = strrchr(trndir, '/');
6854 if (lastslash == NULL)
6861 /* '..' or '.' are valid directory components */
6863 if (lastslash[0] == '.') {
6864 if (lastslash[1] == '\0') {
6866 } else if (lastslash[1] == '.') {
6867 if (lastslash[2] == '\0') {
6870 /* And finally allow '...' */
6871 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6879 lastdot = strrchr(lastslash, '.');
6881 if (lastdot != NULL) {
6883 /* '.dir' is discarded, and any other '.' is invalid */
6884 e_len = strlen(lastdot);
6886 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6889 dir_len = dir_len - 4;
6893 my_strlcpy(buf, trndir, VMS_MAXRSS);
6894 if (buf[dir_len - 1] != '/') {
6896 buf[dir_len + 1] = '\0';
6899 /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6900 if (!DECC_EFS_CHARSET) {
6903 if (str[0] == '.') {
6906 while ((dots[cnt] == '.') && (cnt < 3))
6909 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6915 for (; *str; ++str) {
6916 while (*str == '/') {
6922 /* Have to skip up to three dots which could be */
6923 /* directories, 3 dots being a VMS extension for Perl */
6926 while ((dots[cnt] == '.') && (cnt < 3)) {
6929 if (dots[cnt] == '\0')
6931 if ((cnt > 1) && (dots[cnt] != '/')) {
6937 /* too many dots? */
6938 if ((cnt == 0) || (cnt > 3)) {
6942 if (!dir_start && (*str == '.')) {
6947 PerlMem_free(trndir);
6949 if (vms_debug_fileify) {
6950 if (ret_spec == NULL)
6951 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6954 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6960 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6962 mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6964 static char __pathify_retbuf[VMS_MAXRSS];
6965 char * pathified, *ret_spec, *ret_buf;
6969 if (ret_buf == NULL) {
6971 Newx(pathified, VMS_MAXRSS, char);
6972 if (pathified == NULL)
6973 _ckvmssts(SS$_INSFMEM);
6974 ret_buf = pathified;
6976 ret_buf = __pathify_retbuf;
6980 ret_spec = int_pathify_dirspec(dir, ret_buf);
6982 if (ret_spec == NULL) {
6983 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6985 Safefree(pathified);
6990 } /* end of do_pathify_dirspec() */
6993 /* External entry points */
6995 Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6997 return do_pathify_dirspec(dir, buf, 0, NULL);
7001 Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
7003 return do_pathify_dirspec(dir, buf, 1, NULL);
7007 Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
7009 return do_pathify_dirspec(dir, buf, 0, utf8_fl);
7013 Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
7015 return do_pathify_dirspec(dir, buf, 1, utf8_fl);
7018 /* Internal tounixspec routine that does not use a thread context */
7019 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
7021 int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
7023 char *dirend, *cp1, *cp3, *tmp;
7026 unsigned short int trnlnm_iter_count;
7027 int cmp_rslt, outchars_added;
7028 if (utf8_fl != NULL)
7031 if (vms_debug_fileify) {
7033 fprintf(stderr, "int_tounixspec: spec = NULL\n");
7035 fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
7041 set_vaxc_errno(SS$_BADPARAM);
7044 if (strlen(spec) > (VMS_MAXRSS-1)) {
7046 set_vaxc_errno(SS$_BUFFEROVF);
7050 /* New VMS specific format needs translation
7051 * glob passes filenames with trailing '\n' and expects this preserved.
7053 if (DECC_POSIX_COMPLIANT_PATHNAMES) {
7054 if (! strBEGINs(spec, "\"^UP^")) {
7060 tunix = (char *)PerlMem_malloc(VMS_MAXRSS);
7061 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7062 tunix_len = my_strlcpy(tunix, spec, VMS_MAXRSS);
7064 if (tunix[tunix_len - 1] == '\n') {
7065 tunix[tunix_len - 1] = '\"';
7066 tunix[tunix_len] = '\0';
7070 uspec = decc$translate_vms(tunix);
7071 PerlMem_free(tunix);
7072 if ((int)uspec > 0) {
7073 my_strlcpy(rslt, uspec, VMS_MAXRSS);
7078 /* If we can not translate it, makemaker wants as-is */
7079 my_strlcpy(rslt, spec, VMS_MAXRSS);
7086 cmp_rslt = 0; /* Presume VMS */
7087 cp1 = strchr(spec, '/');
7091 /* Look for EFS ^/ */
7092 if (DECC_EFS_CHARSET) {
7093 while (cp1 != NULL) {
7096 /* Found illegal VMS, assume UNIX */
7101 cp1 = strchr(cp1, '/');
7105 /* Look for "." and ".." */
7106 if (DECC_FILENAME_UNIX_REPORT) {
7107 if (spec[0] == '.') {
7108 if ((spec[1] == '\0') || (spec[1] == '\n')) {
7112 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
7122 /* This is already UNIX or at least nothing VMS understands,
7123 * so all we can reasonably do is unescape extended chars.
7127 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7128 cp1 += outchars_added;
7131 if (vms_debug_fileify) {
7132 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7137 dirend = strrchr(spec,']');
7138 if (dirend == NULL) dirend = strrchr(spec,'>');
7139 if (dirend == NULL) dirend = strchr(spec,':');
7140 if (dirend == NULL) {
7142 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7143 cp1 += outchars_added;
7146 if (vms_debug_fileify) {
7147 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7152 /* Special case 1 - sys$posix_root = / */
7153 if (!DECC_DISABLE_POSIX_ROOT) {
7154 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7161 /* Special case 2 - Convert NLA0: to /dev/null */
7162 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7163 if (cmp_rslt == 0) {
7164 strcpy(rslt, "/dev/null");
7167 if (spec[6] != '\0') {
7174 /* Also handle special case "SYS$SCRATCH:" */
7175 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7176 tmp = (char *)PerlMem_malloc(VMS_MAXRSS);
7177 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7178 if (cmp_rslt == 0) {
7181 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7183 strcpy(rslt, "/tmp");
7186 if (spec[12] != '\0') {
7194 if (*cp2 != '[' && *cp2 != '<') {
7197 else { /* the VMS spec begins with directories */
7199 if (*cp2 == ']' || *cp2 == '>') {
7203 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7204 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7206 if (vms_debug_fileify) {
7207 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7211 trnlnm_iter_count = 0;
7214 while (*cp3 != ':' && *cp3) cp3++;
7216 if (strchr(cp3,']') != NULL) break;
7217 trnlnm_iter_count++;
7218 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7219 } while (vmstrnenv(tmp,tmp,0,fildev,0));
7224 *(cp1++) = *(cp3++);
7225 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7227 set_errno(ENAMETOOLONG);
7228 set_vaxc_errno(SS$_BUFFEROVF);
7229 if (vms_debug_fileify) {
7230 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7232 return NULL; /* No room */
7237 if ((*cp2 == '^')) {
7238 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7239 cp1 += outchars_added;
7241 else if ( *cp2 == '.') {
7242 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7243 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7250 for (; cp2 <= dirend; cp2++) {
7251 if ((*cp2 == '^')) {
7252 /* EFS file escape -- unescape it. */
7253 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added) - 1;
7254 cp1 += outchars_added;
7256 else if (*cp2 == ':') {
7258 if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7260 else if (*cp2 == ']' || *cp2 == '>') {
7261 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7263 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7265 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7266 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7267 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7268 if (memEQs(cp2,7,"[000000") && (*(cp2+7) == ']' ||
7269 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7271 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7272 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7276 else if (*cp2 == '-') {
7277 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7278 while (*cp2 == '-') {
7280 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7282 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7283 /* filespecs like */
7284 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
7285 if (vms_debug_fileify) {
7286 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7291 else *(cp1++) = *cp2;
7293 else *(cp1++) = *cp2;
7295 /* Translate the rest of the filename. */
7299 /* Fixme - for compatibility with the CRTL we should be removing */
7300 /* spaces from the file specifications, but this may show that */
7301 /* some tests that were appearing to pass are not really passing */
7307 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7308 cp1 += outchars_added;
7311 if (DECC_FILENAME_UNIX_NO_VERSION) {
7312 /* Easy, drop the version */
7317 /* Punt - passing the version as a dot will probably */
7318 /* break perl in weird ways, but so did passing */
7319 /* through the ; as a version. Follow the CRTL and */
7320 /* hope for the best. */
7327 /* We will need to fix this properly later */
7328 /* As Perl may be installed on an ODS-5 volume, but not */
7329 /* have the EFS_CHARSET enabled, it still may encounter */
7330 /* filenames with extra dots in them, and a precedent got */
7331 /* set which allowed them to work, that we will uphold here */
7332 /* If extra dots are present in a name and no ^ is on them */
7333 /* VMS assumes that the first one is the extension delimiter */
7334 /* the rest have an implied ^. */
7336 /* this is also a conflict as the . is also a version */
7337 /* delimiter in VMS, */
7339 *(cp1++) = *(cp2++);
7343 /* This is an extension */
7344 if (DECC_READDIR_DROPDOTNOTYPE) {
7346 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7347 /* Drop the dot for the extension */
7355 *(cp1++) = *(cp2++);
7360 /* This still leaves /000000/ when working with a
7361 * VMS device root or concealed root.
7367 ulen = strlen(rslt);
7369 /* Get rid of "000000/ in rooted filespecs */
7371 zeros = strstr(rslt, "/000000/");
7372 if (zeros != NULL) {
7374 mlen = ulen - (zeros - rslt) - 7;
7375 memmove(zeros, &zeros[7], mlen);
7382 if (vms_debug_fileify) {
7383 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7387 } /* end of int_tounixspec() */
7390 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7392 mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7394 static char __tounixspec_retbuf[VMS_MAXRSS];
7395 char * unixspec, *ret_spec, *ret_buf;
7399 if (ret_buf == NULL) {
7401 Newx(unixspec, VMS_MAXRSS, char);
7402 if (unixspec == NULL)
7403 _ckvmssts(SS$_INSFMEM);
7406 ret_buf = __tounixspec_retbuf;
7410 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7412 if (ret_spec == NULL) {
7413 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7420 } /* end of do_tounixspec() */
7422 /* External entry points */
7424 Perl_tounixspec(pTHX_ const char *spec, char *buf)
7426 return do_tounixspec(spec, buf, 0, NULL);
7430 Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7432 return do_tounixspec(spec,buf,1, NULL);
7436 Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7438 return do_tounixspec(spec,buf,0, utf8_fl);
7442 Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7444 return do_tounixspec(spec,buf,1, utf8_fl);
7448 This procedure is used to identify if a path is based in either
7449 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7450 it returns the OpenVMS format directory for it.
7452 It is expecting specifications of only '/' or '/xxxx/'
7454 If a posix root does not exist, or 'xxxx' is not a directory
7455 in the posix root, it returns a failure.
7457 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7459 It is used only internally by posix_to_vmsspec_hardway().
7463 posix_root_to_vms(char *vmspath, int vmspath_len,
7464 const char *unixpath, const int * utf8_fl)
7467 struct FAB myfab = cc$rms_fab;
7468 rms_setup_nam(mynam);
7469 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7470 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7471 char * esa, * esal, * rsa, * rsal;
7477 unixlen = strlen(unixpath);
7482 #if __CRTL_VER >= 80200000
7483 /* If not a posix spec already, convert it */
7484 if (DECC_POSIX_COMPLIANT_PATHNAMES) {
7485 if (! strBEGINs(unixpath,"\"^UP^")) {
7486 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7489 /* This is already a VMS specification, no conversion */
7491 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7500 /* Check to see if this is under the POSIX root */
7501 if (DECC_DISABLE_POSIX_ROOT) {
7505 /* Skip leading / */
7506 if (unixpath[0] == '/') {
7512 strcpy(vmspath,"SYS$POSIX_ROOT:");
7514 /* If this is only the / , or blank, then... */
7515 if (unixpath[0] == '\0') {
7516 /* by definition, this is the answer */
7520 /* Need to look up a directory */
7524 /* Copy and add '^' escape characters as needed */
7527 while (unixpath[i] != 0) {
7530 j += copy_expand_unix_filename_escape
7531 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7535 path_len = strlen(vmspath);
7536 if (vmspath[path_len - 1] == '/')
7538 vmspath[path_len] = ']';
7540 vmspath[path_len] = '\0';
7543 vmspath[vmspath_len] = 0;
7544 if (unixpath[unixlen - 1] == '/')
7546 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
7547 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7548 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7549 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7550 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
7551 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7552 rsa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7553 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7554 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7555 rms_bind_fab_nam(myfab, mynam);
7556 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7557 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7558 if (DECC_EFS_CASE_PRESERVE)
7559 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7560 #ifdef NAML$M_OPEN_SPECIAL
7561 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7564 /* Set up the remaining naml fields */
7565 sts = sys$parse(&myfab);
7567 /* It failed! Try again as a UNIX filespec */
7576 /* get the Device ID and the FID */
7577 sts = sys$search(&myfab);
7579 /* These are no longer needed */
7584 /* on any failure, returned the POSIX ^UP^ filespec */
7589 specdsc.dsc$a_pointer = vmspath;
7590 specdsc.dsc$w_length = vmspath_len;
7592 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7593 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7594 sts = lib$fid_to_name
7595 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7597 /* on any failure, returned the POSIX ^UP^ filespec */
7599 /* This can happen if user does not have permission to read directories */
7600 if (! strBEGINs(unixpath,"\"^UP^"))
7601 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7603 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7606 vmspath[specdsc.dsc$w_length] = 0;
7608 /* Are we expecting a directory? */
7609 if (dir_flag != 0) {
7615 i = specdsc.dsc$w_length - 1;
7619 /* Version must be '1' */
7620 if (vmspath[i--] != '1')
7622 /* Version delimiter is one of ".;" */
7623 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7626 if (vmspath[i--] != 'R')
7628 if (vmspath[i--] != 'I')
7630 if (vmspath[i--] != 'D')
7632 if (vmspath[i--] != '.')
7634 eptr = &vmspath[i+1];
7636 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7637 if (vmspath[i-1] != '^') {
7645 /* Get rid of 6 imaginary zero directory filename */
7646 vmspath[i+1] = '\0';
7650 if (vmspath[i] == '0')
7664 /* /dev/mumble needs to be handled special.
7665 /dev/null becomes NLA0:, And there is the potential for other stuff
7666 like /dev/tty which may need to be mapped to something.
7670 slash_dev_special_to_vms(const char *unixptr, char *vmspath, int vmspath_len)
7676 nextslash = strchr(unixptr, '/');
7677 len = strlen(unixptr);
7678 if (nextslash != NULL)
7679 len = nextslash - unixptr;
7680 if (strEQ(unixptr, "null")) {
7681 if (vmspath_len >= 6) {
7682 strcpy(vmspath, "_NLA0:");
7690 /* The built in routines do not understand perl's special needs, so
7691 doing a manual conversion from UNIX to VMS
7693 If the utf8_fl is not null and points to a non-zero value, then
7694 treat 8 bit characters as UTF-8.
7696 The sequence starting with '$(' and ending with ')' will be passed
7697 through with out interpretation instead of being escaped.
7701 posix_to_vmsspec_hardway(char *vmspath, int vmspath_len, const char *unixpath,
7702 int dir_flag, int * utf8_fl)
7706 const char *unixptr;
7707 const char *unixend;
7709 const char *lastslash;
7710 const char *lastdot;
7716 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7717 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7719 if (utf8_fl != NULL)
7725 /* Ignore leading "/" characters */
7726 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7729 unixlen = strlen(unixptr);
7731 /* Do nothing with blank paths */
7738 /* This could have a "^UP^ on the front */
7739 if (strBEGINs(unixptr,"\"^UP^")) {
7745 lastslash = strrchr(unixptr,'/');
7746 lastdot = strrchr(unixptr,'.');
7747 unixend = strrchr(unixptr,'\"');
7748 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7749 unixend = unixptr + unixlen;
7752 /* last dot is last dot or past end of string */
7753 if (lastdot == NULL)
7754 lastdot = unixptr + unixlen;
7756 /* if no directories, set last slash to beginning of string */
7757 if (lastslash == NULL) {
7758 lastslash = unixptr;
7761 /* Watch out for trailing "." after last slash, still a directory */
7762 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7763 lastslash = unixptr + unixlen;
7766 /* Watch out for trailing ".." after last slash, still a directory */
7767 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7768 lastslash = unixptr + unixlen;
7771 /* dots in directories are aways escaped */
7772 if (lastdot < lastslash)
7773 lastdot = unixptr + unixlen;
7776 /* if (unixptr < lastslash) then we are in a directory */
7783 /* Start with the UNIX path */
7784 if (*unixptr != '/') {
7785 /* relative paths */
7787 /* If allowing logical names on relative pathnames, then handle here */
7788 if ((unixptr[0] != '.') && !DECC_DISABLE_TO_VMS_LOGNAME_TRANSLATION &&
7789 !DECC_POSIX_COMPLIANT_PATHNAMES) {
7795 /* Find the next slash */
7796 nextslash = strchr(unixptr,'/');
7798 esa = (char *)PerlMem_malloc(vmspath_len);
7799 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7801 trn = (char *)PerlMem_malloc(VMS_MAXRSS);
7802 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7804 if (nextslash != NULL) {
7806 seg_len = nextslash - unixptr;
7807 memcpy(esa, unixptr, seg_len);
7811 seg_len = my_strlcpy(esa, unixptr, sizeof(esa));
7813 /* trnlnm(section) */
7814 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7817 /* Now fix up the directory */
7819 /* Split up the path to find the components */
7820 sts = vms_split_path
7837 /* A logical name must be a directory or the full
7838 specification. It is only a full specification if
7839 it is the only component */
7840 if ((unixptr[seg_len] == '\0') ||
7841 (unixptr[seg_len+1] == '\0')) {
7843 /* Is a directory being required? */
7844 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7845 /* Not a logical name */
7850 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7851 /* This must be a directory */
7852 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7853 vmslen = my_strlcpy(vmsptr, esa, vmspath_len - 1);
7854 vmsptr[vmslen] = ':';
7856 vmsptr[vmslen] = '\0';
7864 /* must be dev/directory - ignore version */
7865 if ((n_len + e_len) != 0)
7868 /* transfer the volume */
7869 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7870 memcpy(vmsptr, v_spec, v_len);
7876 /* unroot the rooted directory */
7877 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7879 r_spec[r_len - 1] = ']';
7881 /* This should not be there, but nothing is perfect */
7883 if (strEQ(&r_spec[1], "000000.")) {
7892 memcpy(vmsptr, r_spec, r_len);
7898 /* Bring over the directory. */
7900 ((d_len + vmslen) < vmspath_len)) {
7902 d_spec[d_len - 1] = ']';
7904 if (strEQ(&d_spec[1], "000000.")) {
7914 /* Remove the redundant root */
7922 memcpy(vmsptr, d_spec, d_len);
7936 if (lastslash > unixptr) {
7939 /* skip leading ./ */
7941 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7947 /* Are we still in a directory? */
7948 if (unixptr <= lastslash) {
7953 /* if not backing up, then it is relative forward. */
7954 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7955 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7963 /* Perl wants an empty directory here to tell the difference
7964 * between a DCL command and a filename
7973 /* Handle two special files . and .. */
7974 if (unixptr[0] == '.') {
7975 if (&unixptr[1] == unixend) {
7982 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7993 else { /* Absolute PATH handling */
7997 /* Need to find out where root is */
7999 /* In theory, this procedure should never get an absolute POSIX pathname
8000 * that can not be found on the POSIX root.
8001 * In practice, that can not be relied on, and things will show up
8002 * here that are a VMS device name or concealed logical name instead.
8003 * So to make things work, this procedure must be tolerant.
8005 esa = (char *)PerlMem_malloc(vmspath_len);
8006 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8009 nextslash = strchr(&unixptr[1],'/');
8011 if (nextslash != NULL) {
8012 seg_len = nextslash - &unixptr[1];
8013 my_strlcpy(vmspath, unixptr, seg_len + 2);
8014 if (memEQs(vmspath, seg_len, "dev")) {
8015 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
8016 if (sts == SS$_NORMAL)
8019 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
8022 if ($VMS_STATUS_SUCCESS(sts)) {
8023 /* This is verified to be a real path */
8025 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
8026 if ($VMS_STATUS_SUCCESS(sts)) {
8027 vmslen = my_strlcpy(vmspath, esa, vmspath_len + 1);
8028 vmsptr = vmspath + vmslen;
8030 if (unixptr < lastslash) {
8038 if (strEQ(rptr,"000000.")) {
8042 } /* removing 6 zeros */
8043 } /* vmslen < 7, no 6 zeros possible */
8044 } /* Not in a directory */
8045 } /* Posix root found */
8047 /* No posix root, fall back to default directory */
8048 strcpy(vmspath, "SYS$DISK:[");
8049 vmsptr = &vmspath[10];
8051 if (unixptr > lastslash) {
8060 } /* end of verified real path handling */
8065 /* Ok, we have a device or a concealed root that is not in POSIX
8066 * or we have garbage. Make the best of it.
8069 /* Posix to VMS destroyed this, so copy it again */
8070 my_strlcpy(vmspath, &unixptr[1], seg_len + 1);
8071 vmslen = strlen(vmspath); /* We know we're truncating. */
8072 vmsptr = &vmsptr[vmslen];
8075 /* Now do we need to add the fake 6 zero directory to it? */
8077 if ((*lastslash == '/') && (nextslash < lastslash)) {
8078 /* No there is another directory */
8084 /* now we have foo:bar or foo:[000000]bar to decide from */
8085 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
8087 if (!islnm && !DECC_POSIX_COMPLIANT_PATHNAMES) {
8088 if (strEQ(vmspath, "bin")) {
8089 /* bin => SYS$SYSTEM: */
8090 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
8093 /* tmp => SYS$SCRATCH: */
8094 if (strEQ(vmspath, "tmp")) {
8095 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
8100 trnend = islnm ? islnm - 1 : 0;
8102 /* if this was a logical name, ']' or '>' must be present */
8103 /* if not a logical name, then assume a device and hope. */
8104 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
8106 /* if log name and trailing '.' then rooted - treat as device */
8107 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
8109 /* Fix me, if not a logical name, a device lookup should be
8110 * done to see if the device is file structured. If the device
8111 * is not file structured, the 6 zeros should not be put on.
8113 * As it is, perl is occasionally looking for dev:[000000]tty.
8114 * which looks a little strange.
8116 * Not that easy to detect as "/dev" may be file structured with
8117 * special device files.
8120 if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
8121 (&nextslash[1] == unixend)) {
8122 /* No real directory present */
8127 /* Put the device delimiter on */
8130 unixptr = nextslash;
8133 /* Start directory if needed */
8134 if (!islnm || add_6zero) {
8140 /* add fake 000000] if needed */
8153 } /* non-POSIX translation */
8155 } /* End of relative/absolute path handling */
8157 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
8164 if (dir_start != 0) {
8166 /* First characters in a directory are handled special */
8167 while ((*unixptr == '/') ||
8168 ((*unixptr == '.') &&
8169 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8170 (&unixptr[1]==unixend)))) {
8175 /* Skip redundant / in specification */
8176 while ((*unixptr == '/') && (dir_start != 0)) {
8179 if (unixptr == lastslash)
8182 if (unixptr == lastslash)
8185 /* Skip redundant ./ characters */
8186 while ((*unixptr == '.') &&
8187 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8190 if (unixptr == lastslash)
8192 if (*unixptr == '/')
8195 if (unixptr == lastslash)
8198 /* Skip redundant ../ characters */
8199 while ((*unixptr == '.') && (unixptr[1] == '.') &&
8200 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8201 /* Set the backing up flag */
8207 unixptr++; /* first . */
8208 unixptr++; /* second . */
8209 if (unixptr == lastslash)
8211 if (*unixptr == '/') /* The slash */
8214 if (unixptr == lastslash)
8217 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8218 /* Not needed when VMS is pretending to be UNIX. */
8220 /* Is this loop stuck because of too many dots? */
8221 if (loop_flag == 0) {
8222 /* Exit the loop and pass the rest through */
8227 /* Are we done with directories yet? */
8228 if (unixptr >= lastslash) {
8230 /* Watch out for trailing dots */
8239 if (*unixptr == '/')
8243 /* Have we stopped backing up? */
8248 /* dir_start continues to be = 1 */
8250 if (*unixptr == '-') {
8252 *vmsptr++ = *unixptr++;
8256 /* Now are we done with directories yet? */
8257 if (unixptr >= lastslash) {
8259 /* Watch out for trailing dots */
8275 if (unixptr >= unixend)
8278 /* Normal characters - More EFS work probably needed */
8284 /* remove multiple / */
8285 while (unixptr[1] == '/') {
8288 if (unixptr == lastslash) {
8289 /* Watch out for trailing dots */
8301 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8302 /* Not needed when VMS is pretending to be UNIX. */
8306 if (unixptr != unixend)
8311 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8312 (&unixptr[1] == unixend)) {
8318 /* trailing dot ==> '^..' on VMS */
8319 if (unixptr == unixend) {
8327 *vmsptr++ = *unixptr++;
8331 if (quoted && (&unixptr[1] == unixend)) {
8335 in_cnt = copy_expand_unix_filename_escape
8336 (vmsptr, unixptr, &out_cnt, utf8_fl);
8345 in_cnt = copy_expand_unix_filename_escape
8346 (vmsptr, unixptr, &out_cnt, utf8_fl);
8353 /* Make sure directory is closed */
8354 if (unixptr == lastslash) {
8356 vmsptr2 = vmsptr - 1;
8358 if (*vmsptr2 != ']') {
8361 /* directories do not end in a dot bracket */
8362 if (*vmsptr2 == '.') {
8366 if (*vmsptr2 != '^') {
8367 vmsptr--; /* back up over the dot */
8375 /* Add a trailing dot if a file with no extension */
8376 vmsptr2 = vmsptr - 1;
8378 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8379 (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
8389 /* A convenience macro for copying dots in filenames and escaping
8390 * them when they haven't already been escaped, with guards to
8391 * avoid checking before the start of the buffer or advancing
8392 * beyond the end of it (allowing room for the NUL terminator).
8394 #define VMSEFS_DOT_WITH_ESCAPE(vmsefsdot,vmsefsbuf,vmsefsbufsiz) STMT_START { \
8395 if ( ((vmsefsdot) > (vmsefsbuf) && *((vmsefsdot) - 1) != '^' \
8396 || ((vmsefsdot) == (vmsefsbuf))) \
8397 && (vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 3 \
8399 *((vmsefsdot)++) = '^'; \
8401 if ((vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 2) \
8402 *((vmsefsdot)++) = '.'; \
8405 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8407 int_tovmsspec(const char *path, char *rslt, int dir_flag, int * utf8_flag)
8413 unsigned long int infront = 0, hasdir = 1;
8416 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8417 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8419 if (vms_debug_fileify) {
8421 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8423 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8427 /* If we fail, we should be setting errno */
8429 set_vaxc_errno(SS$_BADPARAM);
8432 rslt_len = VMS_MAXRSS-1;
8434 /* '.' and '..' are "[]" and "[-]" for a quick check */
8435 if (path[0] == '.') {
8436 if (path[1] == '\0') {
8438 if (utf8_flag != NULL)
8443 if (path[1] == '.' && path[2] == '\0') {
8445 if (utf8_flag != NULL)
8452 /* Posix specifications are now a native VMS format */
8453 /*--------------------------------------------------*/
8454 #if __CRTL_VER >= 80200000
8455 if (DECC_POSIX_COMPLIANT_PATHNAMES) {
8456 if (strBEGINs(path,"\"^UP^")) {
8457 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8463 /* This is really the only way to see if this is already in VMS format */
8464 sts = vms_split_path
8479 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8480 replacement, because the above parse just took care of most of
8481 what is needed to do vmspath when the specification is already
8484 And if it is not already, it is easier to do the conversion as
8485 part of this routine than to call this routine and then work on
8489 /* If VMS punctuation was found, it is already VMS format */
8490 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8491 if (utf8_flag != NULL)
8493 my_strlcpy(rslt, path, VMS_MAXRSS);
8494 if (vms_debug_fileify) {
8495 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8499 /* Now, what to do with trailing "." cases where there is no
8500 extension? If this is a UNIX specification, and EFS characters
8501 are enabled, then the trailing "." should be converted to a "^.".
8502 But if this was already a VMS specification, then it should be
8505 So in the case of ambiguity, leave the specification alone.
8509 /* If there is a possibility of UTF8, then if any UTF8 characters
8510 are present, then they must be converted to VTF-7
8512 if (utf8_flag != NULL)
8514 my_strlcpy(rslt, path, VMS_MAXRSS);
8515 if (vms_debug_fileify) {
8516 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8521 dirend = strrchr(path,'/');
8523 if (dirend == NULL) {
8524 /* If we get here with no Unix directory delimiters, then this is an
8525 * ambiguous file specification, such as a Unix glob specification, a
8526 * shell or make macro, or a filespec that would be valid except for
8527 * unescaped extended characters. The safest thing if it's a macro
8528 * is to pass it through as-is.
8530 if (strstr(path, "$(")) {
8531 my_strlcpy(rslt, path, VMS_MAXRSS);
8532 if (vms_debug_fileify) {
8533 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8539 else if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
8540 if (!*(dirend+2)) dirend +=2;
8541 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8542 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8547 lastdot = strrchr(cp2,'.');
8553 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8555 if (DECC_DISABLE_POSIX_ROOT) {
8556 strcpy(rslt,"sys$disk:[000000]");
8559 strcpy(rslt,"sys$posix_root:[000000]");
8561 if (utf8_flag != NULL)
8563 if (vms_debug_fileify) {
8564 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8568 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8570 trndev = (char *)PerlMem_malloc(VMS_MAXRSS);
8571 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8572 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8574 /* DECC special handling */
8576 if (strEQ(rslt,"bin")) {
8577 strcpy(rslt,"sys$system");
8580 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8582 else if (strEQ(rslt,"tmp")) {
8583 strcpy(rslt,"sys$scratch");
8586 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8588 else if (!DECC_DISABLE_POSIX_ROOT) {
8589 strcpy(rslt, "sys$posix_root");
8593 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8594 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8596 else if (strEQ(rslt,"dev")) {
8597 if (strBEGINs(cp2,"/null")) {
8598 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8599 strcpy(rslt,"NLA0");
8603 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8609 trnend = islnm ? strlen(trndev) - 1 : 0;
8610 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8611 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8612 /* If the first element of the path is a logical name, determine
8613 * whether it has to be translated so we can add more directories. */
8614 if (!islnm || rooted) {
8617 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8621 if (cp2 != dirend) {
8622 my_strlcpy(rslt, trndev, VMS_MAXRSS);
8623 cp1 = rslt + trnend;
8630 if (DECC_DISABLE_POSIX_ROOT) {
8636 PerlMem_free(trndev);
8641 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8642 cp2 += 2; /* skip over "./" - it's redundant */
8643 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8645 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8646 *(cp1++) = '-'; /* "../" --> "-" */
8649 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8650 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8651 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8652 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8655 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8656 /* Escape the extra dots in EFS file specifications */
8659 if (cp2 > dirend) cp2 = dirend;
8661 else *(cp1++) = '.';
8663 for (; cp2 < dirend; cp2++) {
8665 if (*(cp2-1) == '/') continue;
8666 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.';
8669 else if (!infront && *cp2 == '.') {
8670 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8671 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
8672 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8673 if (cp1 > rslt && (*(cp1-1) == '-' || *(cp1-1) == '[')) *(cp1++) = '-'; /* handle "../" */
8674 else if (cp1 > rslt + 1 && *(cp1-2) == '[') *(cp1-1) = '-';
8679 if (cp2 == dirend) break;
8681 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8682 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8683 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8684 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8686 *(cp1++) = '.'; /* Simulate trailing '/' */
8687 cp2 += 2; /* for loop will incr this to == dirend */
8689 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8692 if (DECC_EFS_CHARSET == 0) {
8693 if (cp1 > rslt && *(cp1-1) == '^')
8694 cp1--; /* remove the escape, if any */
8695 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8698 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8703 if (!infront && cp1 > rslt && *(cp1-1) == '-') *(cp1++) = '.';
8705 if (DECC_EFS_CHARSET == 0) {
8706 if (cp1 > rslt && *(cp1-1) == '^')
8707 cp1--; /* remove the escape, if any */
8711 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8716 cp2 += copy_expand_unix_filename_escape(cp1, cp2, &out_cnt, utf8_flag);
8717 cp2--; /* we're in a loop that will increment this */
8723 if (cp1 > rslt && *(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8724 if (hasdir) *(cp1++) = ']';
8725 if (*cp2 && *cp2 == '/') cp2++; /* check in case we ended with trailing '/' */
8732 if (DECC_EFS_CHARSET == 0)
8739 if (cp2 >= path && (cp2 == path || *(cp2-1) != '^')) /* not previously escaped */
8745 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8746 DECC_READDIR_DROPDOTNOTYPE) {
8747 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8750 /* trailing dot ==> '^..' on VMS */
8757 *(cp1++) = *(cp2++);
8762 /* This could be a macro to be passed through */
8763 *(cp1++) = *(cp2++);
8765 const char * save_cp2;
8769 /* paranoid check */
8775 *(cp1++) = *(cp2++);
8776 if (isALPHA_L1(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8777 *(cp1++) = *(cp2++);
8778 while (isALPHA_L1(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8779 *(cp1++) = *(cp2++);
8782 *(cp1++) = *(cp2++);
8786 if (is_macro == 0) {
8787 /* Not really a macro - never mind */
8799 /* Don't escape again if following character is
8800 * already something we escape.
8802 if (strchr("\"`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8803 *(cp1++) = *(cp2++);
8806 /* But otherwise fall through and escape it. */
8823 if (cp2 >= path && *(cp2-1) != '^') /* not previously escaped */
8825 *(cp1++) = *(cp2++);
8828 /* If it doesn't look like the beginning of a version number,
8829 * or we've been promised there are no version numbers, then
8832 if (DECC_FILENAME_UNIX_NO_VERSION) {
8836 size_t all_nums = strspn(cp2+1, "0123456789");
8837 if (all_nums > 5 || *(cp2 + all_nums + 1) != '\0')
8840 *(cp1++) = *(cp2++);
8843 *(cp1++) = *(cp2++);
8846 if ((no_type_seen == 1) && DECC_READDIR_DROPDOTNOTYPE) {
8850 /* Fix me for "^]", but that requires making sure that you do
8851 * not back up past the start of the filename
8853 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8858 if (utf8_flag != NULL)
8860 if (vms_debug_fileify) {
8861 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8865 } /* end of int_tovmsspec() */
8868 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8870 mp_do_tovmsspec(pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag)
8872 static char __tovmsspec_retbuf[VMS_MAXRSS];
8873 char * vmsspec, *ret_spec, *ret_buf;
8877 if (ret_buf == NULL) {
8879 Newx(vmsspec, VMS_MAXRSS, char);
8880 if (vmsspec == NULL)
8881 _ckvmssts(SS$_INSFMEM);
8884 ret_buf = __tovmsspec_retbuf;
8888 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8890 if (ret_spec == NULL) {
8891 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8898 } /* end of mp_do_tovmsspec() */
8900 /* External entry points */
8902 Perl_tovmsspec(pTHX_ const char *path, char *buf)
8904 return do_tovmsspec(path, buf, 0, NULL);
8908 Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8910 return do_tovmsspec(path, buf, 1, NULL);
8914 Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8916 return do_tovmsspec(path, buf, 0, utf8_fl);
8920 Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8922 return do_tovmsspec(path, buf, 1, utf8_fl);
8925 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
8926 /* Internal routine for use with out an explicit context present */
8928 int_tovmspath(const char *path, char *buf, int * utf8_fl)
8930 char * ret_spec, *pathified;
8935 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8936 if (pathified == NULL)
8937 _ckvmssts_noperl(SS$_INSFMEM);
8939 ret_spec = int_pathify_dirspec(path, pathified);
8941 if (ret_spec == NULL) {
8942 PerlMem_free(pathified);
8946 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
8948 PerlMem_free(pathified);
8953 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8955 mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl)
8957 static char __tovmspath_retbuf[VMS_MAXRSS];
8959 char *pathified, *vmsified, *cp;
8961 if (path == NULL) return NULL;
8962 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8963 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8964 if (int_pathify_dirspec(path, pathified) == NULL) {
8965 PerlMem_free(pathified);
8971 Newx(vmsified, VMS_MAXRSS, char);
8972 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8973 PerlMem_free(pathified);
8974 if (vmsified) Safefree(vmsified);
8977 PerlMem_free(pathified);
8982 vmslen = strlen(vmsified);
8983 Newx(cp,vmslen+1,char);
8984 memcpy(cp,vmsified,vmslen);
8990 my_strlcpy(__tovmspath_retbuf, vmsified, sizeof(__tovmspath_retbuf));
8992 return __tovmspath_retbuf;
8995 } /* end of do_tovmspath() */
8997 /* External entry points */
8999 Perl_tovmspath(pTHX_ const char *path, char *buf)
9001 return do_tovmspath(path, buf, 0, NULL);
9005 Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
9007 return do_tovmspath(path, buf, 1, NULL);
9011 Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
9013 return do_tovmspath(path, buf, 0, utf8_fl);
9017 Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
9019 return do_tovmspath(path, buf, 1, utf8_fl);
9023 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
9025 mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl)
9027 static char __tounixpath_retbuf[VMS_MAXRSS];
9029 char *pathified, *unixified, *cp;
9031 if (path == NULL) return NULL;
9032 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
9033 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
9034 if (int_pathify_dirspec(path, pathified) == NULL) {
9035 PerlMem_free(pathified);
9041 Newx(unixified, VMS_MAXRSS, char);
9043 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
9044 PerlMem_free(pathified);
9045 if (unixified) Safefree(unixified);
9048 PerlMem_free(pathified);
9053 unixlen = strlen(unixified);
9054 Newx(cp,unixlen+1,char);
9055 memcpy(cp,unixified,unixlen);
9057 Safefree(unixified);
9061 my_strlcpy(__tounixpath_retbuf, unixified, sizeof(__tounixpath_retbuf));
9062 Safefree(unixified);
9063 return __tounixpath_retbuf;
9066 } /* end of do_tounixpath() */
9068 /* External entry points */
9070 Perl_tounixpath(pTHX_ const char *path, char *buf)
9072 return do_tounixpath(path, buf, 0, NULL);
9076 Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
9078 return do_tounixpath(path, buf, 1, NULL);
9082 Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9084 return do_tounixpath(path, buf, 0, utf8_fl);
9088 Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9090 return do_tounixpath(path, buf, 1, utf8_fl);
9094 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
9096 *****************************************************************************
9098 * Copyright (C) 1989-1994, 2007 by *
9099 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
9101 * Permission is hereby granted for the reproduction of this software *
9102 * on condition that this copyright notice is included in source *
9103 * distributions of the software. The code may be modified and *
9104 * distributed under the same terms as Perl itself. *
9106 * 27-Aug-1994 Modified for inclusion in perl5 *
9107 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
9108 *****************************************************************************
9112 * getredirection() is intended to aid in porting C programs
9113 * to VMS (Vax-11 C). The native VMS environment does not support
9114 * '>' and '<' I/O redirection, or command line wild card expansion,
9115 * or a command line pipe mechanism using the '|' AND background
9116 * command execution '&'. All of these capabilities are provided to any
9117 * C program which calls this procedure as the first thing in the
9119 * The piping mechanism will probably work with almost any 'filter' type
9120 * of program. With suitable modification, it may useful for other
9121 * portability problems as well.
9123 * Author: Mark Pizzolato (mark AT infocomm DOT com)
9127 struct list_item *next;
9131 static void add_item(struct list_item **head,
9132 struct list_item **tail,
9136 static void mp_expand_wild_cards(pTHX_ char *item,
9137 struct list_item **head,
9138 struct list_item **tail,
9141 static int background_process(pTHX_ int argc, char **argv);
9143 static void pipe_and_fork(pTHX_ char **cmargv);
9145 /*{{{ void getredirection(int *ac, char ***av)*/
9147 mp_getredirection(pTHX_ int *ac, char ***av)
9149 * Process vms redirection arg's. Exit if any error is seen.
9150 * If getredirection() processes an argument, it is erased
9151 * from the vector. getredirection() returns a new argc and argv value.
9152 * In the event that a background command is requested (by a trailing "&"),
9153 * this routine creates a background subprocess, and simply exits the program.
9155 * Warning: do not try to simplify the code for vms. The code
9156 * presupposes that getredirection() is called before any data is
9157 * read from stdin or written to stdout.
9159 * Normal usage is as follows:
9165 * getredirection(&argc, &argv);
9169 int argc = *ac; /* Argument Count */
9170 char **argv = *av; /* Argument Vector */
9171 char *ap; /* Argument pointer */
9172 int j; /* argv[] index */
9173 int item_count = 0; /* Count of Items in List */
9174 struct list_item *list_head = 0; /* First Item in List */
9175 struct list_item *list_tail; /* Last Item in List */
9176 char *in = NULL; /* Input File Name */
9177 char *out = NULL; /* Output File Name */
9178 char *outmode = "w"; /* Mode to Open Output File */
9179 char *err = NULL; /* Error File Name */
9180 char *errmode = "w"; /* Mode to Open Error File */
9181 int cmargc = 0; /* Piped Command Arg Count */
9182 char **cmargv = NULL;/* Piped Command Arg Vector */
9185 * First handle the case where the last thing on the line ends with
9186 * a '&'. This indicates the desire for the command to be run in a
9187 * subprocess, so we satisfy that desire.
9191 exit(background_process(aTHX_ --argc, argv));
9192 if (*ap && '&' == ap[strlen(ap)-1])
9194 ap[strlen(ap)-1] = '\0';
9195 exit(background_process(aTHX_ argc, argv));
9198 * Now we handle the general redirection cases that involve '>', '>>',
9199 * '<', and pipes '|'.
9201 for (j = 0; j < argc; ++j)
9203 if (strEQ(argv[j], "<"))
9207 fprintf(stderr,"No input file after < on command line");
9208 exit(LIB$_WRONUMARG);
9213 if ('<' == *(ap = argv[j]))
9222 fprintf(stderr,"No output file after > on command line");
9223 exit(LIB$_WRONUMARG);
9242 fprintf(stderr,"No output file after > or >> on command line");
9243 exit(LIB$_WRONUMARG);
9247 if (('2' == *ap) && ('>' == ap[1]))
9264 fprintf(stderr,"No output file after 2> or 2>> on command line");
9265 exit(LIB$_WRONUMARG);
9269 if (strEQ(argv[j], "|"))
9273 fprintf(stderr,"No command into which to pipe on command line");
9274 exit(LIB$_WRONUMARG);
9276 cmargc = argc-(j+1);
9277 cmargv = &argv[j+1];
9281 if ('|' == *(ap = argv[j]))
9289 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9292 * Allocate and fill in the new argument vector, Some Unix's terminate
9293 * the list with an extra null pointer.
9295 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9296 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9298 for (j = 0; j < item_count; ++j, list_head = list_head->next)
9299 argv[j] = list_head->value;
9305 fprintf(stderr,"'|' and '>' may not both be specified on command line");
9306 exit(LIB$_INVARGORD);
9308 pipe_and_fork(aTHX_ cmargv);
9311 /* Check for input from a pipe (mailbox) */
9313 if (in == NULL && 1 == isapipe(0))
9315 char mbxname[L_tmpnam];
9317 long int dvi_item = DVI$_DEVBUFSIZ;
9318 $DESCRIPTOR(mbxnam, "");
9319 $DESCRIPTOR(mbxdevnam, "");
9321 /* Input from a pipe, reopen it in binary mode to disable */
9322 /* carriage control processing. */
9324 fgetname(stdin, mbxname, 1);
9325 mbxnam.dsc$a_pointer = mbxname;
9326 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9327 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9328 mbxdevnam.dsc$a_pointer = mbxname;
9329 mbxdevnam.dsc$w_length = sizeof(mbxname);
9330 dvi_item = DVI$_DEVNAM;
9331 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9332 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9335 freopen(mbxname, "rb", stdin);
9338 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9342 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9344 fprintf(stderr,"Can't open input file %s as stdin",in);
9347 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9349 fprintf(stderr,"Can't open output file %s as stdout",out);
9352 if (out != NULL) vmssetuserlnm("SYS$OUTPUT", out);
9355 if (strEQ(err, "&1")) {
9356 dup2(fileno(stdout), fileno(stderr));
9357 vmssetuserlnm("SYS$ERROR", "SYS$OUTPUT");
9360 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9362 fprintf(stderr,"Can't open error file %s as stderr",err);
9366 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9370 vmssetuserlnm("SYS$ERROR", err);
9373 #ifdef ARGPROC_DEBUG
9374 PerlIO_printf(Perl_debug_log, "Arglist:\n");
9375 for (j = 0; j < *ac; ++j)
9376 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9378 /* Clear errors we may have hit expanding wildcards, so they don't
9379 show up in Perl's $! later */
9380 set_errno(0); set_vaxc_errno(1);
9381 } /* end of getredirection() */
9385 add_item(struct list_item **head, struct list_item **tail, char *value, int *count)
9389 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9390 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9394 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9395 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9396 *tail = (*tail)->next;
9398 (*tail)->value = value;
9403 mp_expand_wild_cards(pTHX_ char *item, struct list_item **head,
9404 struct list_item **tail, int *count)
9407 unsigned long int context = 0;
9415 $DESCRIPTOR(filespec, "");
9416 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9417 $DESCRIPTOR(resultspec, "");
9418 unsigned long int lff_flags = 0;
9422 #ifdef VMS_LONGNAME_SUPPORT
9423 lff_flags = LIB$M_FIL_LONG_NAMES;
9426 for (cp = item; *cp; cp++) {
9427 if (*cp == '*' || *cp == '%' || isSPACE_L1(*cp)) break;
9428 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9430 if (!*cp || isSPACE_L1(*cp))
9432 add_item(head, tail, item, count);
9437 /* "double quoted" wild card expressions pass as is */
9438 /* From DCL that means using e.g.: */
9439 /* perl program """perl.*""" */
9440 item_len = strlen(item);
9441 if ( '"' == *item && '"' == item[item_len-1] )
9444 item[item_len-2] = '\0';
9445 add_item(head, tail, item, count);
9449 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9450 resultspec.dsc$b_class = DSC$K_CLASS_D;
9451 resultspec.dsc$a_pointer = NULL;
9452 vmsspec = (char *)PerlMem_malloc(VMS_MAXRSS);
9453 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9454 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9455 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9456 if (!isunix || !filespec.dsc$a_pointer)
9457 filespec.dsc$a_pointer = item;
9458 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9460 * Only return version specs, if the caller specified a version
9462 had_version = strchr(item, ';');
9464 * Only return device and directory specs, if the caller specified either.
9466 had_device = strchr(item, ':');
9467 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9469 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9470 (&filespec, &resultspec, &context,
9471 &defaultspec, 0, &rms_sts, &lff_flags)))
9476 string = (char *)PerlMem_malloc(resultspec.dsc$w_length+1);
9477 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9478 my_strlcpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length+1);
9479 if (NULL == had_version)
9480 *(strrchr(string, ';')) = '\0';
9481 if ((!had_directory) && (had_device == NULL))
9483 if (NULL == (devdir = strrchr(string, ']')))
9484 devdir = strrchr(string, '>');
9485 my_strlcpy(string, devdir + 1, resultspec.dsc$w_length+1);
9488 * Be consistent with what the C RTL has already done to the rest of
9489 * the argv items and lowercase all of these names.
9491 if (!DECC_EFS_CASE_PRESERVE) {
9492 for (c = string; *c; ++c)
9494 *c = toLOWER_L1(*c);
9496 if (isunix) trim_unixpath(string,item,1);
9497 add_item(head, tail, string, count);
9500 PerlMem_free(vmsspec);
9501 if (sts != RMS$_NMF)
9503 set_vaxc_errno(sts);
9506 case RMS$_FNF: case RMS$_DNF:
9507 set_errno(ENOENT); break;
9509 set_errno(ENOTDIR); break;
9511 set_errno(ENODEV); break;
9512 case RMS$_FNM: case RMS$_SYN:
9513 set_errno(EINVAL); break;
9515 set_errno(EACCES); break;
9517 _ckvmssts_noperl(sts);
9521 add_item(head, tail, item, count);
9522 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9523 _ckvmssts_noperl(lib$find_file_end(&context));
9528 pipe_and_fork(pTHX_ char **cmargv)
9531 struct dsc$descriptor_s *vmscmd;
9532 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9533 int sts, j, l, ismcr, quote, tquote = 0;
9535 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
9536 vms_execfree(vmscmd);
9541 ismcr = q && toUPPER_A(*q) == 'M' && toUPPER_A(*(q+1)) == 'C'
9542 && toUPPER_A(*(q+2)) == 'R' && !*(q+3);
9544 while (q && l < MAX_DCL_LINE_LENGTH) {
9546 if (j > 0 && quote) {
9552 if (ismcr && j > 1) quote = 1;
9553 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9556 if (quote || tquote) {
9562 if ((quote||tquote) && *q == '"') {
9572 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9574 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9579 background_process(pTHX_ int argc, char **argv)
9581 char command[MAX_DCL_SYMBOL + 1] = "$";
9582 $DESCRIPTOR(value, "");
9583 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9584 static $DESCRIPTOR(null, "NLA0:");
9585 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9587 $DESCRIPTOR(pidstr, "");
9589 unsigned long int flags = 17, one = 1, retsts;
9592 len = my_strlcat(command, argv[0], sizeof(command));
9593 while (--argc && (len < MAX_DCL_SYMBOL))
9595 my_strlcat(command, " \"", sizeof(command));
9596 my_strlcat(command, *(++argv), sizeof(command));
9597 len = my_strlcat(command, "\"", sizeof(command));
9599 value.dsc$a_pointer = command;
9600 value.dsc$w_length = strlen(value.dsc$a_pointer);
9601 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9602 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9603 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9604 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9607 _ckvmssts_noperl(retsts);
9609 #ifdef ARGPROC_DEBUG
9610 PerlIO_printf(Perl_debug_log, "%s\n", command);
9612 sprintf(pidstring, "%08X", pid);
9613 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9614 pidstr.dsc$a_pointer = pidstring;
9615 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9616 lib$set_symbol(&pidsymbol, &pidstr);
9620 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9623 /* OS-specific initialization at image activation (not thread startup) */
9624 /* Older VAXC header files lack these constants */
9625 #ifndef JPI$_RIGHTS_SIZE
9626 # define JPI$_RIGHTS_SIZE 817
9628 #ifndef KGB$M_SUBSYSTEM
9629 # define KGB$M_SUBSYSTEM 0x8
9632 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9634 /*{{{void vms_image_init(int *, char ***)*/
9636 vms_image_init(int *argcp, char ***argvp)
9639 char eqv[LNM$C_NAMLENGTH+1] = "";
9640 unsigned int len, tabct = 8, tabidx = 0;
9641 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9642 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9643 unsigned short int dummy, rlen;
9644 struct dsc$descriptor_s **tabvec;
9645 #if defined(PERL_IMPLICIT_CONTEXT)
9648 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9649 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9650 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9653 #ifdef KILL_BY_SIGPRC
9654 Perl_csighandler_init();
9657 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9658 _ckvmssts_noperl(iosb[0]);
9659 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9660 if (iprv[i]) { /* Running image installed with privs? */
9661 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
9666 /* Rights identifiers might trigger tainting as well. */
9667 if (!will_taint && (rlen || rsz)) {
9668 while (rlen < rsz) {
9669 /* We didn't get all the identifiers on the first pass. Allocate a
9670 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9671 * were needed to hold all identifiers at time of last call; we'll
9672 * allocate that many unsigned long ints), and go back and get 'em.
9673 * If it gave us less than it wanted to despite ample buffer space,
9674 * something's broken. Is your system missing a system identifier?
9676 if (rsz <= jpilist[1].buflen) {
9677 /* Perl_croak accvios when used this early in startup. */
9678 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9679 rsz, (unsigned long) jpilist[1].buflen,
9680 "Check your rights database for corruption.\n");
9683 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9684 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9685 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9686 jpilist[1].buflen = rsz * sizeof(unsigned long int);
9687 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9688 _ckvmssts_noperl(iosb[0]);
9690 mask = (unsigned long int *)jpilist[1].bufadr;
9691 /* Check attribute flags for each identifier (2nd longword); protected
9692 * subsystem identifiers trigger tainting.
9694 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9695 if (mask[i] & KGB$M_SUBSYSTEM) {
9700 if (mask != rlst) PerlMem_free(mask);
9703 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9704 * logical, some versions of the CRTL will add a phanthom /000000/
9705 * directory. This needs to be removed.
9707 if (DECC_FILENAME_UNIX_REPORT) {
9710 ulen = strlen(argvp[0][0]);
9712 zeros = strstr(argvp[0][0], "/000000/");
9713 if (zeros != NULL) {
9715 mlen = ulen - (zeros - argvp[0][0]) - 7;
9716 memmove(zeros, &zeros[7], mlen);
9718 argvp[0][0][ulen] = '\0';
9721 /* It also may have a trailing dot that needs to be removed otherwise
9722 * it will be converted to VMS mode incorrectly.
9725 if ((argvp[0][0][ulen] == '.') && (DECC_READDIR_DROPDOTNOTYPE))
9726 argvp[0][0][ulen] = '\0';
9729 /* We need to use this hack to tell Perl it should run with tainting,
9730 * since its tainting flag may be part of the PL_curinterp struct, which
9731 * hasn't been allocated when vms_image_init() is called.
9734 char **newargv, **oldargv;
9736 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9737 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9738 newargv[0] = oldargv[0];
9739 newargv[1] = (char *)PerlMem_malloc(3 * sizeof(char));
9740 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9741 strcpy(newargv[1], "-T");
9742 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9744 newargv[*argcp] = NULL;
9745 /* We orphan the old argv, since we don't know where it's come from,
9746 * so we don't know how to free it.
9750 else { /* Did user explicitly request tainting? */
9752 char *cp, **av = *argvp;
9753 for (i = 1; i < *argcp; i++) {
9754 if (*av[i] != '-') break;
9755 for (cp = av[i]+1; *cp; cp++) {
9756 if (*cp == 'T') { will_taint = 1; break; }
9757 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9758 strchr("DFIiMmx",*cp)) break;
9760 if (will_taint) break;
9765 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9768 tabvec = (struct dsc$descriptor_s **)
9769 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9770 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9772 else if (tabidx >= tabct) {
9774 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9775 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9777 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9778 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9779 tabvec[tabidx]->dsc$w_length = len;
9780 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9781 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_S;
9782 tabvec[tabidx]->dsc$a_pointer = (char *)PerlMem_malloc(len + 1);
9783 if (tabvec[tabidx]->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9784 my_strlcpy(tabvec[tabidx]->dsc$a_pointer, eqv, len + 1);
9786 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9788 getredirection(argcp,argvp);
9789 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9791 # include <reentrancy.h>
9792 decc$set_reentrancy(C$C_MULTITHREAD);
9801 * Trim Unix-style prefix off filespec, so it looks like what a shell
9802 * glob expansion would return (i.e. from specified prefix on, not
9803 * full path). Note that returned filespec is Unix-style, regardless
9804 * of whether input filespec was VMS-style or Unix-style.
9806 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9807 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9808 * vector of options; at present, only bit 0 is used, and if set tells
9809 * trim unixpath to try the current default directory as a prefix when
9810 * presented with a possibly ambiguous ... wildcard.
9812 * Returns !=0 on success, with trimmed filespec replacing contents of
9813 * fspec, and 0 on failure, with contents of fpsec unchanged.
9815 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9817 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9819 char *unixified, *unixwild, *tplate, *base, *end, *cp1, *cp2;
9820 int tmplen, reslen = 0, dirs = 0;
9822 if (!wildspec || !fspec) return 0;
9824 unixwild = (char *)PerlMem_malloc(VMS_MAXRSS);
9825 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9827 if (strpbrk(wildspec,"]>:") != NULL) {
9828 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
9829 PerlMem_free(unixwild);
9834 my_strlcpy(unixwild, wildspec, VMS_MAXRSS);
9836 unixified = (char *)PerlMem_malloc(VMS_MAXRSS);
9837 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9838 if (strpbrk(fspec,"]>:") != NULL) {
9839 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
9840 PerlMem_free(unixwild);
9841 PerlMem_free(unixified);
9844 else base = unixified;
9845 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9846 * check to see that final result fits into (isn't longer than) fspec */
9847 reslen = strlen(fspec);
9851 /* No prefix or absolute path on wildcard, so nothing to remove */
9852 if (!*tplate || *tplate == '/') {
9853 PerlMem_free(unixwild);
9854 if (base == fspec) {
9855 PerlMem_free(unixified);
9858 tmplen = strlen(unixified);
9859 if (tmplen > reslen) {
9860 PerlMem_free(unixified);
9861 return 0; /* not enough space */
9863 /* Copy unixified resultant, including trailing NUL */
9864 memmove(fspec,unixified,tmplen+1);
9865 PerlMem_free(unixified);
9869 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9870 if ((cp1 = strstr(tplate,".../")) == NULL) { /* No ...; just count elts */
9871 for (cp1 = tplate; *cp1; cp1++) if (*cp1 == '/') dirs++;
9872 for (cp1 = end ;cp1 >= base; cp1--)
9873 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9875 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9876 PerlMem_free(unixified);
9877 PerlMem_free(unixwild);
9882 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9883 int ells = 1, totells, segdirs, match;
9884 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9885 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9887 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9889 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9890 tpl = (char *)PerlMem_malloc(VMS_MAXRSS);
9891 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9892 if (ellipsis == tplate && opts & 1) {
9893 /* Template begins with an ellipsis. Since we can't tell how many
9894 * directory names at the front of the resultant to keep for an
9895 * arbitrary starting point, we arbitrarily choose the current
9896 * default directory as a starting point. If it's there as a prefix,
9897 * clip it off. If not, fall through and act as if the leading
9898 * ellipsis weren't there (i.e. return shortest possible path that
9899 * could match template).
9901 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9903 PerlMem_free(unixified);
9904 PerlMem_free(unixwild);
9907 if (!DECC_EFS_CASE_PRESERVE) {
9908 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9909 if (toLOWER_L1(*cp1) != toLOWER_L1(*cp2)) break;
9911 segdirs = dirs - totells; /* Min # of dirs we must have left */
9912 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9913 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9914 memmove(fspec,cp2+1,end - cp2);
9916 PerlMem_free(unixified);
9917 PerlMem_free(unixwild);
9921 /* First off, back up over constant elements at end of path */
9923 for (front = end ; front >= base; front--)
9924 if (*front == '/' && !dirs--) { front++; break; }
9926 lcres = (char *)PerlMem_malloc(VMS_MAXRSS);
9927 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9928 for (cp1=tplate,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9930 if (!DECC_EFS_CASE_PRESERVE) {
9931 *cp2 = toLOWER_L1(*cp1); /* Make lc copy for match */
9939 PerlMem_free(unixified);
9940 PerlMem_free(unixwild);
9941 PerlMem_free(lcres);
9942 return 0; /* Path too long. */
9945 *cp2 = '\0'; /* Pick up with memcpy later */
9946 lcfront = lcres + (front - base);
9947 /* Now skip over each ellipsis and try to match the path in front of it. */
9949 for (cp1 = ellipsis - 2; cp1 >= tplate; cp1--)
9950 if (*(cp1) == '.' && *(cp1+1) == '.' &&
9951 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
9952 if (cp1 < tplate) break; /* template started with an ellipsis */
9953 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9954 ellipsis = cp1; continue;
9956 wilddsc.dsc$a_pointer = tpl;
9957 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9959 for (segdirs = 0, cp2 = tpl;
9960 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
9962 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
9964 if (!DECC_EFS_CASE_PRESERVE) {
9965 *cp2 = toLOWER_L1(*cp1); /* else lowercase for match */
9968 *cp2 = *cp1; /* else preserve case for match */
9971 if (*cp2 == '/') segdirs++;
9973 if (cp1 != ellipsis - 1) {
9975 PerlMem_free(unixified);
9976 PerlMem_free(unixwild);
9977 PerlMem_free(lcres);
9978 return 0; /* Path too long */
9980 /* Back up at least as many dirs as in template before matching */
9981 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
9982 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
9983 for (match = 0; cp1 > lcres;) {
9984 resdsc.dsc$a_pointer = cp1;
9985 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
9987 if (match == 1) lcfront = cp1;
9989 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
9993 PerlMem_free(unixified);
9994 PerlMem_free(unixwild);
9995 PerlMem_free(lcres);
9996 return 0; /* Can't find prefix ??? */
9998 if (match > 1 && opts & 1) {
9999 /* This ... wildcard could cover more than one set of dirs (i.e.
10000 * a set of similar dir names is repeated). If the template
10001 * contains more than 1 ..., upstream elements could resolve the
10002 * ambiguity, but it's not worth a full backtracking setup here.
10003 * As a quick heuristic, clip off the current default directory
10004 * if it's present to find the trimmed spec, else use the
10005 * shortest string that this ... could cover.
10007 char def[NAM$C_MAXRSS+1], *st;
10009 if (getcwd(def, sizeof def,0) == NULL) {
10010 PerlMem_free(unixified);
10011 PerlMem_free(unixwild);
10012 PerlMem_free(lcres);
10016 if (!DECC_EFS_CASE_PRESERVE) {
10017 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10018 if (toLOWER_L1(*cp1) != toLOWER_L1(*cp2)) break;
10020 segdirs = dirs - totells; /* Min # of dirs we must have left */
10021 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
10022 if (*cp1 == '\0' && *cp2 == '/') {
10023 memmove(fspec,cp2+1,end - cp2);
10025 PerlMem_free(unixified);
10026 PerlMem_free(unixwild);
10027 PerlMem_free(lcres);
10030 /* Nope -- stick with lcfront from above and keep going. */
10033 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
10035 PerlMem_free(unixified);
10036 PerlMem_free(unixwild);
10037 PerlMem_free(lcres);
10041 } /* end of trim_unixpath() */
10046 * VMS readdir() routines.
10047 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
10049 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
10050 * Minor modifications to original routines.
10053 /* readdir may have been redefined by reentr.h, so make sure we get
10054 * the local version for what we do here.
10059 #if !defined(PERL_IMPLICIT_CONTEXT)
10060 # define readdir Perl_readdir
10062 # define readdir(a) Perl_readdir(aTHX_ a)
10065 /* Number of elements in vms_versions array */
10066 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
10069 * Open a directory, return a handle for later use.
10071 /*{{{ DIR *opendir(char*name) */
10073 Perl_opendir(pTHX_ const char *name)
10079 Newx(dir, VMS_MAXRSS, char);
10080 if (int_tovmspath(name, dir, NULL) == NULL) {
10084 /* Check access before stat; otherwise stat does not
10085 * accurately report whether it's a directory.
10087 if (!strstr(dir, "::") /* sys$check_access doesn't do remotes */
10088 && !cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
10089 /* cando_by_name has already set errno */
10093 if (flex_stat(dir,&sb) == -1) return NULL;
10094 if (!S_ISDIR(sb.st_mode)) {
10096 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
10099 /* Get memory for the handle, and the pattern. */
10101 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
10103 /* Fill in the fields; mainly playing with the descriptor. */
10104 sprintf(dd->pattern, "%s*.*",dir);
10109 /* By saying we want the result of readdir() in unix format, we are really
10110 * saying we want all the escapes removed, translating characters that
10111 * must be escaped in a VMS-format name to their unescaped form, which is
10112 * presumably allowed in a Unix-format name.
10114 dd->flags = DECC_FILENAME_UNIX_REPORT ? PERL_VMSDIR_M_UNIXSPECS : 0;
10115 dd->pat.dsc$a_pointer = dd->pattern;
10116 dd->pat.dsc$w_length = strlen(dd->pattern);
10117 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10118 dd->pat.dsc$b_class = DSC$K_CLASS_S;
10119 #if defined(USE_ITHREADS)
10120 Newx(dd->mutex,1,perl_mutex);
10121 MUTEX_INIT( (perl_mutex *) dd->mutex );
10127 } /* end of opendir() */
10131 * Set the flag to indicate we want versions or not.
10133 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10135 vmsreaddirversions(DIR *dd, int flag)
10138 dd->flags |= PERL_VMSDIR_M_VERSIONS;
10140 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10145 * Free up an opened directory.
10147 /*{{{ void closedir(DIR *dd)*/
10149 Perl_closedir(DIR *dd)
10153 sts = lib$find_file_end(&dd->context);
10154 Safefree(dd->pattern);
10155 #if defined(USE_ITHREADS)
10156 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10157 Safefree(dd->mutex);
10164 * Collect all the version numbers for the current file.
10167 collectversions(pTHX_ DIR *dd)
10169 struct dsc$descriptor_s pat;
10170 struct dsc$descriptor_s res;
10172 char *p, *text, *buff;
10174 unsigned long context, tmpsts;
10176 /* Convenient shorthand. */
10179 /* Add the version wildcard, ignoring the "*.*" put on before */
10180 i = strlen(dd->pattern);
10181 Newx(text,i + e->d_namlen + 3,char);
10182 my_strlcpy(text, dd->pattern, i + 1);
10183 sprintf(&text[i - 3], "%s;*", e->d_name);
10185 /* Set up the pattern descriptor. */
10186 pat.dsc$a_pointer = text;
10187 pat.dsc$w_length = i + e->d_namlen - 1;
10188 pat.dsc$b_dtype = DSC$K_DTYPE_T;
10189 pat.dsc$b_class = DSC$K_CLASS_S;
10191 /* Set up result descriptor. */
10192 Newx(buff, VMS_MAXRSS, char);
10193 res.dsc$a_pointer = buff;
10194 res.dsc$w_length = VMS_MAXRSS - 1;
10195 res.dsc$b_dtype = DSC$K_DTYPE_T;
10196 res.dsc$b_class = DSC$K_CLASS_S;
10198 /* Read files, collecting versions. */
10199 for (context = 0, e->vms_verscount = 0;
10200 e->vms_verscount < VERSIZE(e);
10201 e->vms_verscount++) {
10202 unsigned long rsts;
10203 unsigned long flags = 0;
10205 #ifdef VMS_LONGNAME_SUPPORT
10206 flags = LIB$M_FIL_LONG_NAMES;
10208 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10209 if (tmpsts == RMS$_NMF || context == 0) break;
10211 buff[VMS_MAXRSS - 1] = '\0';
10212 if ((p = strchr(buff, ';')))
10213 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10215 e->vms_versions[e->vms_verscount] = -1;
10218 _ckvmssts(lib$find_file_end(&context));
10222 } /* end of collectversions() */
10225 * Read the next entry from the directory.
10227 /*{{{ struct dirent *readdir(DIR *dd)*/
10229 Perl_readdir(pTHX_ DIR *dd)
10231 struct dsc$descriptor_s res;
10233 unsigned long int tmpsts;
10234 unsigned long rsts;
10235 unsigned long flags = 0;
10236 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10237 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10239 /* Set up result descriptor, and get next file. */
10240 Newx(buff, VMS_MAXRSS, char);
10241 res.dsc$a_pointer = buff;
10242 res.dsc$w_length = VMS_MAXRSS - 1;
10243 res.dsc$b_dtype = DSC$K_DTYPE_T;
10244 res.dsc$b_class = DSC$K_CLASS_S;
10246 #ifdef VMS_LONGNAME_SUPPORT
10247 flags = LIB$M_FIL_LONG_NAMES;
10250 tmpsts = lib$find_file
10251 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10252 if (dd->context == 0)
10253 tmpsts = RMS$_NMF; /* None left. (should be set, but make sure) */
10255 if (!(tmpsts & 1)) {
10258 break; /* no more files considered success */
10260 SETERRNO(EACCES, tmpsts); break;
10262 SETERRNO(ENODEV, tmpsts); break;
10264 SETERRNO(ENOTDIR, tmpsts); break;
10265 case RMS$_FNF: case RMS$_DNF:
10266 SETERRNO(ENOENT, tmpsts); break;
10268 SETERRNO(EVMSERR, tmpsts);
10274 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10275 buff[res.dsc$w_length] = '\0';
10276 p = buff + res.dsc$w_length;
10277 while (--p >= buff) if (!isSPACE_L1(*p)) break;
10279 if (!DECC_EFS_CASE_PRESERVE) {
10280 for (p = buff; *p; p++) *p = toLOWER_L1(*p);
10283 /* Skip any directory component and just copy the name. */
10284 sts = vms_split_path
10299 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10301 /* In Unix report mode, remove the ".dir;1" from the name */
10302 /* if it is a real directory. */
10303 if (DECC_FILENAME_UNIX_REPORT && DECC_EFS_CHARSET) {
10304 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10308 ret_sts = flex_lstat(buff, &statbuf);
10309 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10316 /* Drop NULL extensions on UNIX file specification */
10317 if ((e_len == 1) && DECC_READDIR_DROPDOTNOTYPE) {
10323 memcpy(dd->entry.d_name, n_spec, n_len + e_len);
10324 dd->entry.d_name[n_len + e_len] = '\0';
10325 dd->entry.d_namlen = n_len + e_len;
10327 /* Convert the filename to UNIX format if needed */
10328 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10330 /* Translate the encoded characters. */
10331 /* Fixme: Unicode handling could result in embedded 0 characters */
10332 if (strchr(dd->entry.d_name, '^') != NULL) {
10333 char new_name[256];
10335 p = dd->entry.d_name;
10338 int inchars_read, outchars_added;
10339 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10341 q += outchars_added;
10343 /* if outchars_added > 1, then this is a wide file specification */
10344 /* Wide file specifications need to be passed in Perl */
10345 /* counted strings apparently with a Unicode flag */
10348 dd->entry.d_namlen = my_strlcpy(dd->entry.d_name, new_name, sizeof(dd->entry.d_name));
10352 dd->entry.vms_verscount = 0;
10353 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10357 } /* end of readdir() */
10361 * Read the next entry from the directory -- thread-safe version.
10363 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10365 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10369 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10371 entry = readdir(dd);
10373 retval = ( *result == NULL ? errno : 0 );
10375 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10379 } /* end of readdir_r() */
10383 * Return something that can be used in a seekdir later.
10385 /*{{{ long telldir(DIR *dd)*/
10387 Perl_telldir(DIR *dd)
10394 * Return to a spot where we used to be. Brute force.
10396 /*{{{ void seekdir(DIR *dd,long count)*/
10398 Perl_seekdir(pTHX_ DIR *dd, long count)
10402 /* If we haven't done anything yet... */
10403 if (dd->count == 0)
10406 /* Remember some state, and clear it. */
10407 old_flags = dd->flags;
10408 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10409 _ckvmssts(lib$find_file_end(&dd->context));
10412 /* The increment is in readdir(). */
10413 for (dd->count = 0; dd->count < count; )
10416 dd->flags = old_flags;
10418 } /* end of seekdir() */
10421 /* VMS subprocess management
10423 * my_vfork() - just a vfork(), after setting a flag to record that
10424 * the current script is trying a Unix-style fork/exec.
10426 * vms_do_aexec() and vms_do_exec() are called in response to the
10427 * perl 'exec' function. If this follows a vfork call, then they
10428 * call out the regular perl routines in doio.c which do an
10429 * execvp (for those who really want to try this under VMS).
10430 * Otherwise, they do exactly what the perl docs say exec should
10431 * do - terminate the current script and invoke a new command
10432 * (See below for notes on command syntax.)
10434 * do_aspawn() and do_spawn() implement the VMS side of the perl
10435 * 'system' function.
10437 * Note on command arguments to perl 'exec' and 'system': When handled
10438 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10439 * are concatenated to form a DCL command string. If the first non-numeric
10440 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10441 * the command string is handed off to DCL directly. Otherwise,
10442 * the first token of the command is taken as the filespec of an image
10443 * to run. The filespec is expanded using a default type of '.EXE' and
10444 * the process defaults for device, directory, etc., and if found, the resultant
10445 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10446 * the command string as parameters. This is perhaps a bit complicated,
10447 * but I hope it will form a happy medium between what VMS folks expect
10448 * from lib$spawn and what Unix folks expect from exec.
10451 static int vfork_called;
10453 /*{{{int my_vfork(void)*/
10464 vms_execfree(struct dsc$descriptor_s *vmscmd)
10467 if (vmscmd->dsc$a_pointer) {
10468 PerlMem_free(vmscmd->dsc$a_pointer);
10470 PerlMem_free(vmscmd);
10475 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10477 char *junk, *tmps = NULL, *cmd;
10485 tmps = SvPV(really,rlen);
10487 cmdlen += rlen + 1;
10492 for (idx++; idx <= sp; idx++) {
10494 junk = SvPVx(*idx,rlen);
10495 cmdlen += rlen ? rlen + 1 : 0;
10498 Newx(cmd, cmdlen+1, char);
10501 if (tmps && *tmps) {
10502 my_strlcpy(cmd, tmps, cmdlen + 1);
10506 while (++mark <= sp) {
10508 char *s = SvPVx(*mark,n_a);
10510 if (*cmd) my_strlcat(cmd, " ", cmdlen+1);
10511 my_strlcat(cmd, s, cmdlen+1);
10516 } /* end of setup_argstr() */
10519 static unsigned long int
10520 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10521 struct dsc$descriptor_s **pvmscmd)
10525 char image_name[NAM$C_MAXRSS+1];
10526 char image_argv[NAM$C_MAXRSS+1];
10527 $DESCRIPTOR(defdsc,".EXE");
10528 $DESCRIPTOR(defdsc2,".");
10529 struct dsc$descriptor_s resdsc;
10530 struct dsc$descriptor_s *vmscmd;
10531 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10532 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10533 char *s, *rest, *cp, *wordbreak;
10538 vmscmd = (struct dsc$descriptor_s *)PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10539 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10541 /* vmsspec is a DCL command buffer, not just a filename */
10542 vmsspec = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10543 if (vmsspec == NULL)
10544 _ckvmssts_noperl(SS$_INSFMEM);
10546 resspec = (char *)PerlMem_malloc(VMS_MAXRSS);
10547 if (resspec == NULL)
10548 _ckvmssts_noperl(SS$_INSFMEM);
10550 /* Make a copy for modification */
10551 cmdlen = strlen(incmd);
10552 cmd = (char *)PerlMem_malloc(cmdlen+1);
10553 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10554 my_strlcpy(cmd, incmd, cmdlen + 1);
10558 resdsc.dsc$a_pointer = resspec;
10559 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10560 resdsc.dsc$b_class = DSC$K_CLASS_S;
10561 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10563 vmscmd->dsc$a_pointer = NULL;
10564 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10565 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10566 vmscmd->dsc$w_length = 0;
10567 if (pvmscmd) *pvmscmd = vmscmd;
10569 if (suggest_quote) *suggest_quote = 0;
10571 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10573 PerlMem_free(vmsspec);
10574 PerlMem_free(resspec);
10575 return CLI$_BUFOVF; /* continuation lines currently unsupported */
10580 while (*s && isSPACE_L1(*s)) s++;
10582 if (*s == '@' || *s == '$') {
10583 vmsspec[0] = *s; rest = s + 1;
10584 for (cp = &vmsspec[1]; *rest && isSPACE_L1(*rest); rest++,cp++) *cp = *rest;
10586 else { cp = vmsspec; rest = s; }
10588 /* If the first word is quoted, then we need to unquote it and
10589 * escape spaces within it. We'll expand into the resspec buffer,
10590 * then copy back into the cmd buffer, expanding the latter if
10593 if (*rest == '"') {
10598 int soff = s - cmd;
10600 for (cp2 = resspec;
10601 *rest && cp2 - resspec < (VMS_MAXRSS - 1);
10604 if (*rest == ' ') { /* Escape ' ' to '^_'. */
10610 else if (*rest == '"') {
10612 if (in_quote) { /* Must be closing quote. */
10625 /* Expand the command buffer if necessary. */
10626 if (clen > cmdlen) {
10627 cmd = (char *)PerlMem_realloc(cmd, clen);
10629 _ckvmssts_noperl(SS$_INSFMEM);
10630 /* Where we are may have changed, so recompute offsets */
10631 r = cmd + (r - s - soff);
10632 rest = cmd + (rest - s - soff);
10636 /* Shift the non-verb portion of the command (if any) up or
10637 * down as necessary.
10640 memmove(rest + clen - cmdlen, rest, s - soff + cmdlen - rest);
10642 /* Copy the unquoted and escaped command verb into place. */
10643 memcpy(r, resspec, cp2 - resspec);
10646 rest = r; /* Rewind for subsequent operations. */
10649 if (*rest == '.' || *rest == '/') {
10651 for (cp2 = resspec;
10652 *rest && !isSPACE_L1(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10653 rest++, cp2++) *cp2 = *rest;
10655 if (int_tovmsspec(resspec, cp, 0, NULL)) {
10658 /* When a UNIX spec with no file type is translated to VMS, */
10659 /* A trailing '.' is appended under ODS-5 rules. */
10660 /* Here we do not want that trailing "." as it prevents */
10661 /* Looking for a implied ".exe" type. */
10662 if (DECC_EFS_CHARSET) {
10664 i = strlen(vmsspec);
10665 if (vmsspec[i-1] == '.') {
10666 vmsspec[i-1] = '\0';
10671 for (cp2 = vmsspec + strlen(vmsspec);
10672 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10673 rest++, cp2++) *cp2 = *rest;
10678 /* Intuit whether verb (first word of cmd) is a DCL command:
10679 * - if first nonspace char is '@', it's a DCL indirection
10681 * - if verb contains a filespec separator, it's not a DCL command
10682 * - if it doesn't, caller tells us whether to default to a DCL
10683 * command, or to a local image unless told it's DCL (by leading '$')
10687 if (suggest_quote) *suggest_quote = 1;
10689 char *filespec = strpbrk(s,":<[.;");
10690 rest = wordbreak = strpbrk(s," \"\t/");
10691 if (!wordbreak) wordbreak = s + strlen(s);
10692 if (*s == '$') check_img = 0;
10693 if (filespec && (filespec < wordbreak)) isdcl = 0;
10694 else isdcl = !check_img;
10699 imgdsc.dsc$a_pointer = s;
10700 imgdsc.dsc$w_length = wordbreak - s;
10701 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10703 _ckvmssts_noperl(lib$find_file_end(&cxt));
10704 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10705 if (!(retsts & 1) && *s == '$') {
10706 _ckvmssts_noperl(lib$find_file_end(&cxt));
10707 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10708 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10710 _ckvmssts_noperl(lib$find_file_end(&cxt));
10711 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10715 _ckvmssts_noperl(lib$find_file_end(&cxt));
10720 while (*s && !isSPACE_L1(*s)) s++;
10723 /* check that it's really not DCL with no file extension */
10724 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10726 char b[256] = {0,0,0,0};
10727 read(fileno(fp), b, 256);
10728 isdcl = isPRINT_L1(b[0]) && isPRINT_L1(b[1]) && isPRINT_L1(b[2]) && isPRINT_L1(b[3]);
10732 /* Check for script */
10734 if ((b[0] == '#') && (b[1] == '!'))
10736 #ifdef ALTERNATE_SHEBANG
10738 if (strEQ(b, ALTERNATE_SHEBANG)) {
10740 perlstr = strstr("perl",b);
10741 if (perlstr == NULL)
10744 shebang_len = strlen(ALTERNATE_SHEBANG);
10751 if (shebang_len > 0) {
10754 char tmpspec[NAM$C_MAXRSS + 1];
10757 /* Image is following after white space */
10758 /*--------------------------------------*/
10759 while (isPRINT_L1(b[i]) && isSPACE_L1(b[i]))
10763 while (isPRINT_L1(b[i]) && !isSPACE_L1(b[i])) {
10764 tmpspec[j++] = b[i++];
10765 if (j >= NAM$C_MAXRSS)
10770 /* There may be some default parameters to the image */
10771 /*---------------------------------------------------*/
10773 while (isPRINT_L1(b[i])) {
10774 image_argv[j++] = b[i++];
10775 if (j >= NAM$C_MAXRSS)
10778 while ((j > 0) && !isPRINT_L1(image_argv[j-1]))
10782 /* It will need to be converted to VMS format and validated */
10783 if (tmpspec[0] != '\0') {
10786 /* Try to find the exact program requested to be run */
10787 /*---------------------------------------------------*/
10788 iname = int_rmsexpand
10789 (tmpspec, image_name, ".exe",
10790 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10791 if (iname != NULL) {
10792 if (cando_by_name_int
10793 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10794 /* MCR prefix needed */
10798 /* Try again with a null type */
10799 /*----------------------------*/
10800 iname = int_rmsexpand
10801 (tmpspec, image_name, ".",
10802 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10803 if (iname != NULL) {
10804 if (cando_by_name_int
10805 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10806 /* MCR prefix needed */
10812 /* Did we find the image to run the script? */
10813 /*------------------------------------------*/
10817 /* Assume DCL or foreign command exists */
10818 /*--------------------------------------*/
10819 tchr = strrchr(tmpspec, '/');
10820 if (tchr != NULL) {
10826 my_strlcpy(image_name, tchr, sizeof(image_name));
10834 if (check_img && isdcl) {
10836 PerlMem_free(resspec);
10837 PerlMem_free(vmsspec);
10841 if (cando_by_name(S_IXUSR,0,resspec)) {
10842 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10843 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10845 my_strlcpy(vmscmd->dsc$a_pointer,"$ MCR ", MAX_DCL_LINE_LENGTH);
10846 if (image_name[0] != 0) {
10847 my_strlcat(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10848 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10850 } else if (image_name[0] != 0) {
10851 my_strlcpy(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10852 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10854 my_strlcpy(vmscmd->dsc$a_pointer, "@", MAX_DCL_LINE_LENGTH);
10856 if (suggest_quote) *suggest_quote = 1;
10858 /* If there is an image name, use original command */
10859 if (image_name[0] == 0)
10860 my_strlcat(vmscmd->dsc$a_pointer, resspec, MAX_DCL_LINE_LENGTH);
10863 while (*rest && isSPACE_L1(*rest)) rest++;
10866 if (image_argv[0] != 0) {
10867 my_strlcat(vmscmd->dsc$a_pointer, image_argv, MAX_DCL_LINE_LENGTH);
10868 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10874 rest_len = strlen(rest);
10875 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10876 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10877 my_strlcat(vmscmd->dsc$a_pointer, rest, MAX_DCL_LINE_LENGTH);
10879 retsts = CLI$_BUFOVF;
10881 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10883 PerlMem_free(vmsspec);
10884 PerlMem_free(resspec);
10885 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10891 /* It's either a DCL command or we couldn't find a suitable image */
10892 vmscmd->dsc$w_length = strlen(cmd);
10894 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(vmscmd->dsc$w_length + 1);
10895 my_strlcpy(vmscmd->dsc$a_pointer, cmd, vmscmd->dsc$w_length + 1);
10898 PerlMem_free(resspec);
10899 PerlMem_free(vmsspec);
10901 /* check if it's a symbol (for quoting purposes) */
10902 if (suggest_quote && !*suggest_quote) {
10904 char equiv[LNM$C_NAMLENGTH];
10905 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10906 eqvdsc.dsc$a_pointer = equiv;
10908 iss = lib$get_symbol(vmscmd,&eqvdsc);
10909 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10911 if (!(retsts & 1)) {
10912 /* just hand off status values likely to be due to user error */
10913 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10914 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10915 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10916 else { _ckvmssts_noperl(retsts); }
10919 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10921 } /* end of setup_cmddsc() */
10924 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10926 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10931 if (vfork_called) { /* this follows a vfork - act Unixish */
10933 if (vfork_called < 0) {
10934 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10937 else return do_aexec(really,mark,sp);
10939 /* no vfork - act VMSish */
10942 cmd = setup_argstr(aTHX_ really,mark,sp);
10943 exec_sts = vms_do_exec(cmd);
10948 SETERRNO(ENOENT, RMS_FNF);
10950 } /* end of vms_do_aexec() */
10953 /* {{{bool vms_do_exec(char *cmd) */
10955 Perl_vms_do_exec(pTHX_ const char *cmd)
10957 struct dsc$descriptor_s *vmscmd;
10959 if (vfork_called) { /* this follows a vfork - act Unixish */
10961 if (vfork_called < 0) {
10962 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10965 else return do_exec(cmd);
10968 { /* no vfork - act VMSish */
10969 unsigned long int retsts;
10972 TAINT_PROPER("exec");
10973 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10974 retsts = lib$do_command(vmscmd);
10977 case RMS$_FNF: case RMS$_DNF:
10978 set_errno(ENOENT); break;
10980 set_errno(ENOTDIR); break;
10982 set_errno(ENODEV); break;
10984 set_errno(EACCES); break;
10986 set_errno(EINVAL); break;
10987 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10988 set_errno(E2BIG); break;
10989 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10990 _ckvmssts_noperl(retsts); /* fall through */
10991 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10992 set_errno(EVMSERR);
10994 set_vaxc_errno(retsts);
10995 if (ckWARN(WARN_EXEC)) {
10996 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
10997 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
10999 vms_execfree(vmscmd);
11004 } /* end of vms_do_exec() */
11007 int do_spawn2(pTHX_ const char *, int);
11010 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
11012 unsigned long int sts;
11018 /* We'll copy the (undocumented?) Win32 behavior and allow a
11019 * numeric first argument. But the only value we'll support
11020 * through do_aspawn is a value of 1, which means spawn without
11021 * waiting for completion -- other values are ignored.
11023 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
11025 flags = SvIVx(*mark);
11028 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
11029 flags = CLI$M_NOWAIT;
11034 cmd = setup_argstr(aTHX_ really, mark, sp);
11035 sts = do_spawn2(aTHX_ cmd, flags);
11037 /* pp_sys will clean up cmd */
11041 } /* end of do_aspawn() */
11045 /* {{{int do_spawn(char* cmd) */
11047 Perl_do_spawn(pTHX_ char* cmd)
11049 PERL_ARGS_ASSERT_DO_SPAWN;
11051 return do_spawn2(aTHX_ cmd, 0);
11055 /* {{{int do_spawn_nowait(char* cmd) */
11057 Perl_do_spawn_nowait(pTHX_ char* cmd)
11059 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
11061 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
11065 /* {{{int do_spawn2(char *cmd) */
11067 do_spawn2(pTHX_ const char *cmd, int flags)
11069 unsigned long int sts, substs;
11072 TAINT_PROPER("spawn");
11073 if (!cmd || !*cmd) {
11074 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
11077 case RMS$_FNF: case RMS$_DNF:
11078 set_errno(ENOENT); break;
11080 set_errno(ENOTDIR); break;
11082 set_errno(ENODEV); break;
11084 set_errno(EACCES); break;
11086 set_errno(EINVAL); break;
11087 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11088 set_errno(E2BIG); break;
11089 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11090 _ckvmssts_noperl(sts); /* fall through */
11091 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11092 set_errno(EVMSERR);
11094 set_vaxc_errno(sts);
11095 if (ckWARN(WARN_EXEC)) {
11096 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
11105 if (flags & CLI$M_NOWAIT)
11108 strcpy(mode, "nW");
11110 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
11113 /* sts will be the pid in the nowait case, so leave a
11114 * hint saying not to do any bit shifting to it.
11116 if (flags & CLI$M_NOWAIT)
11117 PL_statusvalue = -1;
11120 } /* end of do_spawn2() */
11124 static unsigned int *sockflags, sockflagsize;
11127 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11128 * routines found in some versions of the CRTL can't deal with sockets.
11129 * We don't shim the other file open routines since a socket isn't
11130 * likely to be opened by a name.
11132 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11134 my_fdopen(int fd, const char *mode)
11136 FILE *fp = fdopen(fd, mode);
11139 unsigned int fdoff = fd / sizeof(unsigned int);
11140 Stat_t sbuf; /* native stat; we don't need flex_stat */
11141 if (!sockflagsize || fdoff > sockflagsize) {
11142 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
11143 else Newx (sockflags,fdoff+2,unsigned int);
11144 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11145 sockflagsize = fdoff + 2;
11147 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11148 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11157 * Clear the corresponding bit when the (possibly) socket stream is closed.
11158 * There still a small hole: we miss an implicit close which might occur
11159 * via freopen(). >> Todo
11161 /*{{{ int my_fclose(FILE *fp)*/
11163 my_fclose(FILE *fp) {
11165 unsigned int fd = fileno(fp);
11166 unsigned int fdoff = fd / sizeof(unsigned int);
11168 if (sockflagsize && fdoff < sockflagsize)
11169 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11177 * A simple fwrite replacement which outputs itmsz*nitm chars without
11178 * introducing record boundaries every itmsz chars.
11179 * We are using fputs, which depends on a terminating null. We may
11180 * well be writing binary data, so we need to accommodate not only
11181 * data with nulls sprinkled in the middle but also data with no null
11184 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11186 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11188 char *cp, *end, *cpd;
11190 unsigned int fd = fileno(dest);
11191 unsigned int fdoff = fd / sizeof(unsigned int);
11193 int bufsize = itmsz * nitm + 1;
11195 if (fdoff < sockflagsize &&
11196 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11197 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11201 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11202 memcpy( data, src, itmsz*nitm );
11203 data[itmsz*nitm] = '\0';
11205 end = data + itmsz * nitm;
11206 retval = (int) nitm; /* on success return # items written */
11209 while (cpd <= end) {
11210 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11211 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11213 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11217 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11220 } /* end of my_fwrite() */
11223 /*{{{ int my_flush(FILE *fp)*/
11225 Perl_my_flush(pTHX_ FILE *fp)
11228 if ((res = fflush(fp)) == 0 && fp) {
11229 #ifdef VMS_DO_SOCKETS
11231 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11233 res = fsync(fileno(fp));
11236 * If the flush succeeded but set end-of-file, we need to clear
11237 * the error because our caller may check ferror(). BTW, this
11238 * probably means we just flushed an empty file.
11240 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11246 /* fgetname() is not returning the correct file specifications when
11247 * decc_filename_unix_report mode is active. So we have to have it
11248 * aways return filenames in VMS mode and convert it ourselves.
11251 /*{{{ char * my_fgetname(FILE *fp, buf)*/
11253 Perl_my_fgetname(FILE *fp, char * buf) {
11257 retname = fgetname(fp, buf, 1);
11259 /* If we are in VMS mode, then we are done */
11260 if (!DECC_FILENAME_UNIX_REPORT || (retname == NULL)) {
11264 /* Convert this to Unix format */
11265 vms_name = (char *)PerlMem_malloc(VMS_MAXRSS);
11266 my_strlcpy(vms_name, retname, VMS_MAXRSS);
11267 retname = int_tounixspec(vms_name, buf, NULL);
11268 PerlMem_free(vms_name);
11275 * Here are replacements for the following Unix routines in the VMS environment:
11276 * getpwuid Get information for a particular UIC or UID
11277 * getpwnam Get information for a named user
11278 * getpwent Get information for each user in the rights database
11279 * setpwent Reset search to the start of the rights database
11280 * endpwent Finish searching for users in the rights database
11282 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11283 * (defined in pwd.h), which contains the following fields:-
11285 * char *pw_name; Username (in lower case)
11286 * char *pw_passwd; Hashed password
11287 * unsigned int pw_uid; UIC
11288 * unsigned int pw_gid; UIC group number
11289 * char *pw_unixdir; Default device/directory (VMS-style)
11290 * char *pw_gecos; Owner name
11291 * char *pw_dir; Default device/directory (Unix-style)
11292 * char *pw_shell; Default CLI name (eg. DCL)
11294 * If the specified user does not exist, getpwuid and getpwnam return NULL.
11296 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11297 * not the UIC member number (eg. what's returned by getuid()),
11298 * getpwuid() can accept either as input (if uid is specified, the caller's
11299 * UIC group is used), though it won't recognise gid=0.
11301 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11302 * information about other users in your group or in other groups, respectively.
11303 * If the required privilege is not available, then these routines fill only
11304 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11307 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11310 /* sizes of various UAF record fields */
11311 #define UAI$S_USERNAME 12
11312 #define UAI$S_IDENT 31
11313 #define UAI$S_OWNER 31
11314 #define UAI$S_DEFDEV 31
11315 #define UAI$S_DEFDIR 63
11316 #define UAI$S_DEFCLI 31
11317 #define UAI$S_PWD 8
11319 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
11320 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11321 (uic).uic$v_group != UIC$K_WILD_GROUP)
11323 static char __empty[]= "";
11324 static struct passwd __passwd_empty=
11325 {(char *) __empty, (char *) __empty, 0, 0,
11326 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11327 static int contxt= 0;
11328 static struct passwd __pwdcache;
11329 static char __pw_namecache[UAI$S_IDENT+1];
11332 * This routine does most of the work extracting the user information.
11335 fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11338 unsigned char length;
11339 char pw_gecos[UAI$S_OWNER+1];
11341 static union uicdef uic;
11343 unsigned char length;
11344 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11347 unsigned char length;
11348 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11351 unsigned char length;
11352 char pw_shell[UAI$S_DEFCLI+1];
11354 static char pw_passwd[UAI$S_PWD+1];
11356 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11357 struct dsc$descriptor_s name_desc;
11358 unsigned long int sts;
11360 static struct itmlst_3 itmlst[]= {
11361 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
11362 {sizeof(uic), UAI$_UIC, &uic, &luic},
11363 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
11364 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
11365 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
11366 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
11367 {0, 0, NULL, NULL}};
11369 name_desc.dsc$w_length= strlen(name);
11370 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11371 name_desc.dsc$b_class= DSC$K_CLASS_S;
11372 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11374 /* Note that sys$getuai returns many fields as counted strings. */
11375 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11376 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11377 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11379 else { _ckvmssts(sts); }
11380 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
11382 if ((int) owner.length < lowner) lowner= (int) owner.length;
11383 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11384 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11385 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11386 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11387 owner.pw_gecos[lowner]= '\0';
11388 defdev.pw_dir[ldefdev+ldefdir]= '\0';
11389 defcli.pw_shell[ldefcli]= '\0';
11390 if (valid_uic(uic)) {
11391 pwd->pw_uid= uic.uic$l_uic;
11392 pwd->pw_gid= uic.uic$v_group;
11395 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11396 pwd->pw_passwd= pw_passwd;
11397 pwd->pw_gecos= owner.pw_gecos;
11398 pwd->pw_dir= defdev.pw_dir;
11399 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11400 pwd->pw_shell= defcli.pw_shell;
11401 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11403 ldir= strlen(pwd->pw_unixdir) - 1;
11404 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11407 my_strlcpy(pwd->pw_unixdir, pwd->pw_dir, sizeof(pwd->pw_unixdir));
11408 if (!DECC_EFS_CASE_PRESERVE)
11409 __mystrtolower(pwd->pw_unixdir);
11414 * Get information for a named user.
11416 /*{{{struct passwd *getpwnam(char *name)*/
11418 Perl_my_getpwnam(pTHX_ const char *name)
11420 struct dsc$descriptor_s name_desc;
11422 unsigned long int sts;
11424 __pwdcache = __passwd_empty;
11425 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11426 /* We still may be able to determine pw_uid and pw_gid */
11427 name_desc.dsc$w_length= strlen(name);
11428 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11429 name_desc.dsc$b_class= DSC$K_CLASS_S;
11430 name_desc.dsc$a_pointer= (char *) name;
11431 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11432 __pwdcache.pw_uid= uic.uic$l_uic;
11433 __pwdcache.pw_gid= uic.uic$v_group;
11436 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11437 set_vaxc_errno(sts);
11438 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11441 else { _ckvmssts(sts); }
11444 my_strlcpy(__pw_namecache, name, sizeof(__pw_namecache));
11445 __pwdcache.pw_name= __pw_namecache;
11446 return &__pwdcache;
11447 } /* end of my_getpwnam() */
11451 * Get information for a particular UIC or UID.
11452 * Called by my_getpwent with uid=-1 to list all users.
11454 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11456 Perl_my_getpwuid(pTHX_ Uid_t uid)
11458 const $DESCRIPTOR(name_desc,__pw_namecache);
11459 unsigned short lname;
11461 unsigned long int status;
11463 if (uid == (unsigned int) -1) {
11465 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11466 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11467 set_vaxc_errno(status);
11468 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11472 else { _ckvmssts(status); }
11473 } while (!valid_uic (uic));
11476 uic.uic$l_uic= uid;
11477 if (!uic.uic$v_group)
11478 uic.uic$v_group= PerlProc_getgid();
11479 if (valid_uic(uic))
11480 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11481 else status = SS$_IVIDENT;
11482 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11483 status == RMS$_PRV) {
11484 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11487 else { _ckvmssts(status); }
11489 __pw_namecache[lname]= '\0';
11490 __mystrtolower(__pw_namecache);
11492 __pwdcache = __passwd_empty;
11493 __pwdcache.pw_name = __pw_namecache;
11495 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11496 The identifier's value is usually the UIC, but it doesn't have to be,
11497 so if we can, we let fillpasswd update this. */
11498 __pwdcache.pw_uid = uic.uic$l_uic;
11499 __pwdcache.pw_gid = uic.uic$v_group;
11501 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11502 return &__pwdcache;
11504 } /* end of my_getpwuid() */
11508 * Get information for next user.
11510 /*{{{struct passwd *my_getpwent()*/
11512 Perl_my_getpwent(pTHX)
11514 return (my_getpwuid((unsigned int) -1));
11519 * Finish searching rights database for users.
11521 /*{{{void my_endpwent()*/
11523 Perl_my_endpwent(pTHX)
11526 _ckvmssts(sys$finish_rdb(&contxt));
11532 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11533 * my_utime(), and flex_stat(), all of which operate on UTC unless
11534 * VMSISH_TIMES is true.
11536 /* method used to handle UTC conversions:
11537 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
11539 static int gmtime_emulation_type;
11540 /* number of secs to add to UTC POSIX-style time to get local time */
11541 static long int utc_offset_secs;
11543 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11544 * in vmsish.h. #undef them here so we can call the CRTL routines
11552 static time_t toutc_dst(time_t loc) {
11555 if ((rsltmp = localtime(&loc)) == NULL) return -1u;
11556 loc -= utc_offset_secs;
11557 if (rsltmp->tm_isdst) loc -= 3600;
11560 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11561 ((gmtime_emulation_type || my_time(NULL)), \
11562 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11563 ((secs) - utc_offset_secs))))
11565 static time_t toloc_dst(time_t utc) {
11568 utc += utc_offset_secs;
11569 if ((rsltmp = localtime(&utc)) == NULL) return -1u;
11570 if (rsltmp->tm_isdst) utc += 3600;
11573 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11574 ((gmtime_emulation_type || my_time(NULL)), \
11575 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11576 ((secs) + utc_offset_secs))))
11578 /* my_time(), my_localtime(), my_gmtime()
11579 * By default traffic in UTC time values, using CRTL gmtime() or
11580 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11581 * Note: We need to use these functions even when the CRTL has working
11582 * UTC support, since they also handle C<use vmsish qw(times);>
11584 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
11585 * Modified by Charles Bailey <bailey@newman.upenn.edu>
11588 /*{{{time_t my_time(time_t *timep)*/
11590 Perl_my_time(pTHX_ time_t *timep)
11595 if (gmtime_emulation_type == 0) {
11596 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
11597 /* results of calls to gmtime() and localtime() */
11598 /* for same &base */
11600 gmtime_emulation_type++;
11601 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11602 char off[LNM$C_NAMLENGTH+1];;
11604 gmtime_emulation_type++;
11605 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11606 gmtime_emulation_type++;
11607 utc_offset_secs = 0;
11608 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11610 else { utc_offset_secs = atol(off); }
11612 else { /* We've got a working gmtime() */
11613 struct tm gmt, local;
11616 tm_p = localtime(&base);
11618 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
11619 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11620 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
11621 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
11626 # ifdef VMSISH_TIME
11627 if (VMSISH_TIME) when = _toloc(when);
11629 if (timep != NULL) *timep = when;
11632 } /* end of my_time() */
11636 /*{{{struct tm *my_gmtime(const time_t *timep)*/
11638 Perl_my_gmtime(pTHX_ const time_t *timep)
11643 if (timep == NULL) {
11644 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11647 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11650 # ifdef VMSISH_TIME
11651 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11653 return gmtime(&when);
11654 } /* end of my_gmtime() */
11658 /*{{{struct tm *my_localtime(const time_t *timep)*/
11660 Perl_my_localtime(pTHX_ const time_t *timep)
11664 if (timep == NULL) {
11665 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11668 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11669 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
11672 # ifdef VMSISH_TIME
11673 if (VMSISH_TIME) when = _toutc(when);
11675 /* CRTL localtime() wants UTC as input, does tz correction itself */
11676 return localtime(&when);
11677 } /* end of my_localtime() */
11680 /* Reset definitions for later calls */
11681 #define gmtime(t) my_gmtime(t)
11682 #define localtime(t) my_localtime(t)
11683 #define time(t) my_time(t)
11686 /* my_utime - update modification/access time of a file
11688 * Only the UTC translation is home-grown. The rest is handled by the
11689 * CRTL utime(), which will take into account the relevant feature
11690 * logicals and ODS-5 volume characteristics for true access times.
11694 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11695 * to VMS epoch (01-JAN-1858 00:00:00.00)
11696 * in 100 ns intervals.
11698 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11700 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11702 Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
11704 struct utimbuf utc_utimes, *utc_utimesp;
11706 if (utimes != NULL) {
11707 utc_utimes.actime = utimes->actime;
11708 utc_utimes.modtime = utimes->modtime;
11709 # ifdef VMSISH_TIME
11710 /* If input was local; convert to UTC for sys svc */
11712 utc_utimes.actime = _toutc(utimes->actime);
11713 utc_utimes.modtime = _toutc(utimes->modtime);
11716 utc_utimesp = &utc_utimes;
11719 utc_utimesp = NULL;
11722 return utime(file, utc_utimesp);
11724 } /* end of my_utime() */
11728 * flex_stat, flex_lstat, flex_fstat
11729 * basic stat, but gets it right when asked to stat
11730 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
11733 #ifndef _USE_STD_STAT
11734 /* encode_dev packs a VMS device name string into an integer to allow
11735 * simple comparisons. This can be used, for example, to check whether two
11736 * files are located on the same device, by comparing their encoded device
11737 * names. Even a string comparison would not do, because stat() reuses the
11738 * device name buffer for each call; so without encode_dev, it would be
11739 * necessary to save the buffer and use strcmp (this would mean a number of
11740 * changes to the standard Perl code, to say nothing of what a Perl script
11741 * would have to do.
11743 * The device lock id, if it exists, should be unique (unless perhaps compared
11744 * with lock ids transferred from other nodes). We have a lock id if the disk is
11745 * mounted cluster-wide, which is when we tend to get long (host-qualified)
11746 * device names. Thus we use the lock id in preference, and only if that isn't
11747 * available, do we try to pack the device name into an integer (flagged by
11748 * the sign bit (LOCKID_MASK) being set).
11750 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
11751 * name and its encoded form, but it seems very unlikely that we will find
11752 * two files on different disks that share the same encoded device names,
11753 * and even more remote that they will share the same file id (if the test
11754 * is to check for the same file).
11756 * A better method might be to use sys$device_scan on the first call, and to
11757 * search for the device, returning an index into the cached array.
11758 * The number returned would be more intelligible.
11759 * This is probably not worth it, and anyway would take quite a bit longer
11760 * on the first call.
11762 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
11764 encode_dev (pTHX_ const char *dev)
11767 unsigned long int f;
11772 if (!dev || !dev[0]) return 0;
11776 struct dsc$descriptor_s dev_desc;
11777 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
11779 /* For cluster-mounted disks, the disk lock identifier is unique, so we
11780 can try that first. */
11781 dev_desc.dsc$w_length = strlen (dev);
11782 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
11783 dev_desc.dsc$b_class = DSC$K_CLASS_S;
11784 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
11785 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
11786 if (!$VMS_STATUS_SUCCESS(status)) {
11788 case SS$_NOSUCHDEV:
11789 SETERRNO(ENODEV, status);
11795 if (lockid) return (lockid & ~LOCKID_MASK);
11799 /* Otherwise we try to encode the device name */
11803 for (q = dev + strlen(dev); q--; q >= dev) {
11808 else if (isALPHA_A(toUPPER_A(*q)))
11809 c= toupper (*q) - 'A' + (char)10;
11811 continue; /* Skip '$'s */
11813 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
11815 enc += f * (unsigned long int) c;
11817 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
11819 } /* end of encode_dev() */
11820 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11821 device_no = encode_dev(aTHX_ devname)
11823 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11824 device_no = new_dev_no
11828 is_null_device(const char *name)
11830 if (decc_bug_devnull != 0) {
11831 if (strBEGINs(name, "/dev/null"))
11834 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
11835 The underscore prefix, controller letter, and unit number are
11836 independently optional; for our purposes, the colon punctuation
11837 is not. The colon can be trailed by optional directory and/or
11838 filename, but two consecutive colons indicates a nodename rather
11839 than a device. [pr] */
11840 if (*name == '_') ++name;
11841 if (toLOWER_L1(*name++) != 'n') return 0;
11842 if (toLOWER_L1(*name++) != 'l') return 0;
11843 if (toLOWER_L1(*name) == 'a') ++name;
11844 if (*name == '0') ++name;
11845 return (*name++ == ':') && (*name != ':');
11849 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
11851 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
11854 Perl_cando_by_name_int(pTHX_ I32 bit, bool effective, const char *fname, int opts)
11856 char usrname[L_cuserid];
11857 struct dsc$descriptor_s usrdsc =
11858 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
11859 char *vmsname = NULL, *fileified = NULL;
11860 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
11861 unsigned short int retlen, trnlnm_iter_count;
11862 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11863 union prvdef curprv;
11864 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
11865 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
11866 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
11867 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
11868 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
11870 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
11872 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11874 static int profile_context = -1;
11876 if (!fname || !*fname) return FALSE;
11878 /* Make sure we expand logical names, since sys$check_access doesn't */
11879 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
11880 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11881 if (!strpbrk(fname,"/]>:")) {
11882 my_strlcpy(fileified, fname, VMS_MAXRSS);
11883 trnlnm_iter_count = 0;
11884 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
11885 trnlnm_iter_count++;
11886 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
11891 vmsname = (char *)PerlMem_malloc(VMS_MAXRSS);
11892 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11893 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
11894 /* Don't know if already in VMS format, so make sure */
11895 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
11896 PerlMem_free(fileified);
11897 PerlMem_free(vmsname);
11902 my_strlcpy(vmsname, fname, VMS_MAXRSS);
11905 /* sys$check_access needs a file spec, not a directory spec.
11906 * flex_stat now will handle a null thread context during startup.
11909 retlen = namdsc.dsc$w_length = strlen(vmsname);
11910 if (vmsname[retlen-1] == ']'
11911 || vmsname[retlen-1] == '>'
11912 || vmsname[retlen-1] == ':'
11913 || (!flex_stat_int(vmsname, &st, 1) &&
11914 S_ISDIR(st.st_mode))) {
11916 if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
11917 PerlMem_free(fileified);
11918 PerlMem_free(vmsname);
11927 retlen = namdsc.dsc$w_length = strlen(fname);
11928 namdsc.dsc$a_pointer = (char *)fname;
11931 case S_IXUSR: case S_IXGRP: case S_IXOTH:
11932 access = ARM$M_EXECUTE;
11933 flags = CHP$M_READ;
11935 case S_IRUSR: case S_IRGRP: case S_IROTH:
11936 access = ARM$M_READ;
11937 flags = CHP$M_READ | CHP$M_USEREADALL;
11939 case S_IWUSR: case S_IWGRP: case S_IWOTH:
11940 access = ARM$M_WRITE;
11941 flags = CHP$M_READ | CHP$M_WRITE;
11943 case S_IDUSR: case S_IDGRP: case S_IDOTH:
11944 access = ARM$M_DELETE;
11945 flags = CHP$M_READ | CHP$M_WRITE;
11948 if (fileified != NULL)
11949 PerlMem_free(fileified);
11950 if (vmsname != NULL)
11951 PerlMem_free(vmsname);
11955 /* Before we call $check_access, create a user profile with the current
11956 * process privs since otherwise it just uses the default privs from the
11957 * UAF and might give false positives or negatives. This only works on
11958 * VMS versions v6.0 and later since that's when sys$create_user_profile
11959 * became available.
11962 /* get current process privs and username */
11963 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
11964 _ckvmssts_noperl(iosb[0]);
11966 /* find out the space required for the profile */
11967 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
11968 &usrprodsc.dsc$w_length,&profile_context));
11970 /* allocate space for the profile and get it filled in */
11971 usrprodsc.dsc$a_pointer = (char *)PerlMem_malloc(usrprodsc.dsc$w_length);
11972 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11973 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
11974 &usrprodsc.dsc$w_length,&profile_context));
11976 /* use the profile to check access to the file; free profile & analyze results */
11977 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
11978 PerlMem_free(usrprodsc.dsc$a_pointer);
11979 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
11981 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
11982 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
11983 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
11984 set_vaxc_errno(retsts);
11985 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11986 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
11987 else set_errno(ENOENT);
11988 if (fileified != NULL)
11989 PerlMem_free(fileified);
11990 if (vmsname != NULL)
11991 PerlMem_free(vmsname);
11994 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
11995 if (fileified != NULL)
11996 PerlMem_free(fileified);
11997 if (vmsname != NULL)
11998 PerlMem_free(vmsname);
12001 _ckvmssts_noperl(retsts);
12003 if (fileified != NULL)
12004 PerlMem_free(fileified);
12005 if (vmsname != NULL)
12006 PerlMem_free(vmsname);
12007 return FALSE; /* Should never get here */
12011 /* Do the permissions in *statbufp allow some operation? */
12012 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12013 * subset of the applicable information.
12016 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12018 return cando_by_name_int
12019 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12020 } /* end of cando() */
12024 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12026 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12028 return cando_by_name_int(bit, effective, fname, 0);
12030 } /* end of cando_by_name() */
12034 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12036 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12038 dSAVE_ERRNO; /* fstat may set this even on success */
12039 if (!fstat(fd, &statbufp->crtl_stat)) {
12041 char *vms_filename;
12042 vms_filename = (char *)PerlMem_malloc(VMS_MAXRSS);
12043 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12045 /* Save name for cando by name in VMS format */
12046 cptr = getname(fd, vms_filename, 1);
12048 /* This should not happen, but just in case */
12049 if (cptr == NULL) {
12050 statbufp->st_devnam[0] = 0;
12053 /* Make sure that the saved name fits in 255 characters */
12054 cptr = int_rmsexpand_vms
12056 statbufp->st_devnam,
12059 statbufp->st_devnam[0] = 0;
12061 PerlMem_free(vms_filename);
12063 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12065 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12067 # ifdef VMSISH_TIME
12069 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12070 statbufp->st_atime = _toloc(statbufp->st_atime);
12071 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12079 } /* end of flex_fstat() */
12083 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12085 char *temp_fspec = NULL;
12086 char *fileified = NULL;
12087 const char *save_spec;
12091 char already_fileified = 0;
12099 if (decc_bug_devnull != 0) {
12100 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12101 memset(statbufp,0,sizeof *statbufp);
12102 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12103 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12104 statbufp->st_uid = 0x00010001;
12105 statbufp->st_gid = 0x0001;
12106 time((time_t *)&statbufp->st_mtime);
12107 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12114 #if __CRTL_VER >= 80200000
12116 * If we are in POSIX filespec mode, accept the filename as is.
12118 if (!DECC_POSIX_COMPLIANT_PATHNAMES) {
12121 /* Try for a simple stat first. If fspec contains a filename without
12122 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12123 * and sea:[wine.dark]water. exist, the CRTL prefers the directory here.
12124 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12125 * not sea:[wine.dark]., if the latter exists. If the intended target is
12126 * the file with null type, specify this by calling flex_stat() with
12127 * a '.' at the end of fspec.
12130 if (lstat_flag == 0)
12131 retval = stat(fspec, &statbufp->crtl_stat);
12133 retval = lstat(fspec, &statbufp->crtl_stat);
12139 /* In the odd case where we have write but not read access
12140 * to a directory, stat('foo.DIR') works but stat('foo') doesn't.
12142 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12143 if (fileified == NULL)
12144 _ckvmssts_noperl(SS$_INSFMEM);
12146 ret_spec = int_fileify_dirspec(fspec, fileified, NULL);
12147 if (ret_spec != NULL) {
12148 if (lstat_flag == 0)
12149 retval = stat(fileified, &statbufp->crtl_stat);
12151 retval = lstat(fileified, &statbufp->crtl_stat);
12152 save_spec = fileified;
12153 already_fileified = 1;
12157 if (retval && vms_bug_stat_filename) {
12159 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
12160 if (temp_fspec == NULL)
12161 _ckvmssts_noperl(SS$_INSFMEM);
12163 /* We should try again as a vmsified file specification. */
12165 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12166 if (ret_spec != NULL) {
12167 if (lstat_flag == 0)
12168 retval = stat(temp_fspec, &statbufp->crtl_stat);
12170 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12171 save_spec = temp_fspec;
12176 /* Last chance - allow multiple dots without EFS CHARSET */
12177 /* The CRTL stat() falls down hard on multi-dot filenames in unix
12178 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12179 * enable it if it isn't already.
12181 if (!DECC_EFS_CHARSET && (efs_charset_index > 0))
12182 decc$feature_set_value(efs_charset_index, 1, 1);
12183 if (lstat_flag == 0)
12184 retval = stat(fspec, &statbufp->crtl_stat);
12186 retval = lstat(fspec, &statbufp->crtl_stat);
12188 if (!DECC_EFS_CHARSET && (efs_charset_index > 0)) {
12189 decc$feature_set_value(efs_charset_index, 1, 0);
12194 #if __CRTL_VER >= 80200000
12196 if (lstat_flag == 0)
12197 retval = stat(temp_fspec, &statbufp->crtl_stat);
12199 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12200 save_spec = temp_fspec;
12204 /* As you were... */
12205 if (!DECC_EFS_CHARSET)
12206 decc$feature_set_value(efs_charset_index,1,0);
12210 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12212 /* If this is an lstat, do not follow the link */
12214 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12216 /* If we used the efs_hack above, we must also use it here for */
12217 /* perl_cando to work */
12218 if (efs_hack && (efs_charset_index > 0)) {
12219 decc$feature_set_value(efs_charset_index, 1, 1);
12222 /* If we've got a directory, save a fileified, expanded version of it
12223 * in st_devnam. If not a directory, just an expanded version.
12225 if (S_ISDIR(statbufp->st_mode) && !already_fileified) {
12226 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12227 if (fileified == NULL)
12228 _ckvmssts_noperl(SS$_INSFMEM);
12230 cptr = do_fileify_dirspec(save_spec, fileified, 0, NULL);
12232 save_spec = fileified;
12235 cptr = int_rmsexpand(save_spec,
12236 statbufp->st_devnam,
12242 if (efs_hack && (efs_charset_index > 0)) {
12243 decc$feature_set_value(efs_charset_index, 1, 0);
12246 /* Fix me: If this is NULL then stat found a file, and we could */
12247 /* not convert the specification to VMS - Should never happen */
12249 statbufp->st_devnam[0] = 0;
12251 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12253 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12254 # ifdef VMSISH_TIME
12256 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12257 statbufp->st_atime = _toloc(statbufp->st_atime);
12258 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12262 /* If we were successful, leave errno where we found it */
12263 if (retval == 0) RESTORE_ERRNO;
12265 PerlMem_free(temp_fspec);
12267 PerlMem_free(fileified);
12270 } /* end of flex_stat_int() */
12273 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12275 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12277 return flex_stat_int(fspec, statbufp, 0);
12281 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12283 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12285 return flex_stat_int(fspec, statbufp, 1);
12290 /* rmscopy - copy a file using VMS RMS routines
12292 * Copies contents and attributes of spec_in to spec_out, except owner
12293 * and protection information. Name and type of spec_in are used as
12294 * defaults for spec_out. The third parameter specifies whether rmscopy()
12295 * should try to propagate timestamps from the input file to the output file.
12296 * If it is less than 0, no timestamps are preserved. If it is 0, then
12297 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12298 * propagated to the output file at creation iff the output file specification
12299 * did not contain an explicit name or type, and the revision date is always
12300 * updated at the end of the copy operation. If it is greater than 0, then
12301 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12302 * other than the revision date should be propagated, and bit 1 indicates
12303 * that the revision date should be propagated.
12305 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12307 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12308 * Incorporates, with permission, some code from EZCOPY by Tim Adye
12309 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12310 * as part of the Perl standard distribution under the terms of the
12311 * GNU General Public License or the Perl Artistic License. Copies
12312 * of each may be found in the Perl standard distribution.
12314 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12316 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12318 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12319 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12320 unsigned long int sts;
12322 struct FAB fab_in, fab_out;
12323 struct RAB rab_in, rab_out;
12324 rms_setup_nam(nam);
12325 rms_setup_nam(nam_out);
12326 struct XABDAT xabdat;
12327 struct XABFHC xabfhc;
12328 struct XABRDT xabrdt;
12329 struct XABSUM xabsum;
12331 vmsin = (char *)PerlMem_malloc(VMS_MAXRSS);
12332 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12333 vmsout = (char *)PerlMem_malloc(VMS_MAXRSS);
12334 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12335 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12336 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
12337 PerlMem_free(vmsin);
12338 PerlMem_free(vmsout);
12339 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12343 esa = (char *)PerlMem_malloc(VMS_MAXRSS);
12344 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12346 #if defined(NAML$C_MAXRSS)
12347 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
12348 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12350 fab_in = cc$rms_fab;
12351 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12352 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12353 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12354 fab_in.fab$l_fop = FAB$M_SQO;
12355 rms_bind_fab_nam(fab_in, nam);
12356 fab_in.fab$l_xab = (void *) &xabdat;
12358 rsa = (char *)PerlMem_malloc(VMS_MAXRSS);
12359 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12361 #if defined(NAML$C_MAXRSS)
12362 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
12363 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12365 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12366 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12367 rms_nam_esl(nam) = 0;
12368 rms_nam_rsl(nam) = 0;
12369 rms_nam_esll(nam) = 0;
12370 rms_nam_rsll(nam) = 0;
12371 #ifdef NAM$M_NO_SHORT_UPCASE
12372 if (DECC_EFS_CASE_PRESERVE)
12373 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
12376 xabdat = cc$rms_xabdat; /* To get creation date */
12377 xabdat.xab$l_nxt = (void *) &xabfhc;
12379 xabfhc = cc$rms_xabfhc; /* To get record length */
12380 xabfhc.xab$l_nxt = (void *) &xabsum;
12382 xabsum = cc$rms_xabsum; /* To get key and area information */
12384 if (!((sts = sys$open(&fab_in)) & 1)) {
12385 PerlMem_free(vmsin);
12386 PerlMem_free(vmsout);
12389 PerlMem_free(esal);
12392 PerlMem_free(rsal);
12393 set_vaxc_errno(sts);
12395 case RMS$_FNF: case RMS$_DNF:
12396 set_errno(ENOENT); break;
12398 set_errno(ENOTDIR); break;
12400 set_errno(ENODEV); break;
12402 set_errno(EINVAL); break;
12404 set_errno(EACCES); break;
12406 set_errno(EVMSERR);
12413 fab_out.fab$w_ifi = 0;
12414 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12415 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12416 fab_out.fab$l_fop = FAB$M_SQO;
12417 rms_bind_fab_nam(fab_out, nam_out);
12418 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12419 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12420 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
12421 esa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12422 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12423 rsa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12424 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12427 #if defined(NAML$C_MAXRSS)
12428 esal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12429 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12430 rsal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12431 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12433 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
12434 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
12436 if (preserve_dates == 0) { /* Act like DCL COPY */
12437 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
12438 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
12439 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
12440 PerlMem_free(vmsin);
12441 PerlMem_free(vmsout);
12444 PerlMem_free(esal);
12447 PerlMem_free(rsal);
12448 PerlMem_free(esa_out);
12449 if (esal_out != NULL)
12450 PerlMem_free(esal_out);
12451 PerlMem_free(rsa_out);
12452 if (rsal_out != NULL)
12453 PerlMem_free(rsal_out);
12454 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12455 set_vaxc_errno(sts);
12458 fab_out.fab$l_xab = (void *) &xabdat;
12459 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12460 preserve_dates = 1;
12462 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
12463 preserve_dates =0; /* bitmask from this point forward */
12465 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
12466 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
12467 PerlMem_free(vmsin);
12468 PerlMem_free(vmsout);
12471 PerlMem_free(esal);
12474 PerlMem_free(rsal);
12475 PerlMem_free(esa_out);
12476 if (esal_out != NULL)
12477 PerlMem_free(esal_out);
12478 PerlMem_free(rsa_out);
12479 if (rsal_out != NULL)
12480 PerlMem_free(rsal_out);
12481 set_vaxc_errno(sts);
12484 set_errno(ENOENT); break;
12486 set_errno(ENOTDIR); break;
12488 set_errno(ENODEV); break;
12490 set_errno(EINVAL); break;
12492 set_errno(EACCES); break;
12494 set_errno(EVMSERR);
12498 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
12499 if (preserve_dates & 2) {
12500 /* sys$close() will process xabrdt, not xabdat */
12501 xabrdt = cc$rms_xabrdt;
12502 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12503 fab_out.fab$l_xab = (void *) &xabrdt;
12506 ubf = (char *)PerlMem_malloc(32256);
12507 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12508 rab_in = cc$rms_rab;
12509 rab_in.rab$l_fab = &fab_in;
12510 rab_in.rab$l_rop = RAB$M_BIO;
12511 rab_in.rab$l_ubf = ubf;
12512 rab_in.rab$w_usz = 32256;
12513 if (!((sts = sys$connect(&rab_in)) & 1)) {
12514 sys$close(&fab_in); sys$close(&fab_out);
12515 PerlMem_free(vmsin);
12516 PerlMem_free(vmsout);
12520 PerlMem_free(esal);
12523 PerlMem_free(rsal);
12524 PerlMem_free(esa_out);
12525 if (esal_out != NULL)
12526 PerlMem_free(esal_out);
12527 PerlMem_free(rsa_out);
12528 if (rsal_out != NULL)
12529 PerlMem_free(rsal_out);
12530 set_errno(EVMSERR); set_vaxc_errno(sts);
12534 rab_out = cc$rms_rab;
12535 rab_out.rab$l_fab = &fab_out;
12536 rab_out.rab$l_rbf = ubf;
12537 if (!((sts = sys$connect(&rab_out)) & 1)) {
12538 sys$close(&fab_in); sys$close(&fab_out);
12539 PerlMem_free(vmsin);
12540 PerlMem_free(vmsout);
12544 PerlMem_free(esal);
12547 PerlMem_free(rsal);
12548 PerlMem_free(esa_out);
12549 if (esal_out != NULL)
12550 PerlMem_free(esal_out);
12551 PerlMem_free(rsa_out);
12552 if (rsal_out != NULL)
12553 PerlMem_free(rsal_out);
12554 set_errno(EVMSERR); set_vaxc_errno(sts);
12558 while ((sts = sys$read(&rab_in))) { /* always true */
12559 if (sts == RMS$_EOF) break;
12560 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12561 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
12562 sys$close(&fab_in); sys$close(&fab_out);
12563 PerlMem_free(vmsin);
12564 PerlMem_free(vmsout);
12568 PerlMem_free(esal);
12571 PerlMem_free(rsal);
12572 PerlMem_free(esa_out);
12573 if (esal_out != NULL)
12574 PerlMem_free(esal_out);
12575 PerlMem_free(rsa_out);
12576 if (rsal_out != NULL)
12577 PerlMem_free(rsal_out);
12578 set_errno(EVMSERR); set_vaxc_errno(sts);
12584 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
12585 sys$close(&fab_in); sys$close(&fab_out);
12586 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
12588 PerlMem_free(vmsin);
12589 PerlMem_free(vmsout);
12593 PerlMem_free(esal);
12596 PerlMem_free(rsal);
12597 PerlMem_free(esa_out);
12598 if (esal_out != NULL)
12599 PerlMem_free(esal_out);
12600 PerlMem_free(rsa_out);
12601 if (rsal_out != NULL)
12602 PerlMem_free(rsal_out);
12605 set_errno(EVMSERR); set_vaxc_errno(sts);
12611 } /* end of rmscopy() */
12615 /*** The following glue provides 'hooks' to make some of the routines
12616 * from this file available from Perl. These routines are sufficiently
12617 * basic, and are required sufficiently early in the build process,
12618 * that's it's nice to have them available to miniperl as well as the
12619 * full Perl, so they're set up here instead of in an extension. The
12620 * Perl code which handles importation of these names into a given
12621 * package lives in [.VMS]Filespec.pm in @INC.
12625 rmsexpand_fromperl(pTHX_ CV *cv)
12628 char *fspec, *defspec = NULL, *rslt;
12630 int fs_utf8, dfs_utf8;
12634 if (!items || items > 2)
12635 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
12636 fspec = SvPV(ST(0),n_a);
12637 fs_utf8 = SvUTF8(ST(0));
12638 if (!fspec || !*fspec) XSRETURN_UNDEF;
12640 defspec = SvPV(ST(1),n_a);
12641 dfs_utf8 = SvUTF8(ST(1));
12643 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
12644 ST(0) = sv_newmortal();
12645 if (rslt != NULL) {
12646 sv_usepvn(ST(0),rslt,strlen(rslt));
12655 vmsify_fromperl(pTHX_ CV *cv)
12662 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
12663 utf8_fl = SvUTF8(ST(0));
12664 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12665 ST(0) = sv_newmortal();
12666 if (vmsified != NULL) {
12667 sv_usepvn(ST(0),vmsified,strlen(vmsified));
12676 unixify_fromperl(pTHX_ CV *cv)
12683 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
12684 utf8_fl = SvUTF8(ST(0));
12685 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12686 ST(0) = sv_newmortal();
12687 if (unixified != NULL) {
12688 sv_usepvn(ST(0),unixified,strlen(unixified));
12697 fileify_fromperl(pTHX_ CV *cv)
12704 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
12705 utf8_fl = SvUTF8(ST(0));
12706 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12707 ST(0) = sv_newmortal();
12708 if (fileified != NULL) {
12709 sv_usepvn(ST(0),fileified,strlen(fileified));
12718 pathify_fromperl(pTHX_ CV *cv)
12725 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
12726 utf8_fl = SvUTF8(ST(0));
12727 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12728 ST(0) = sv_newmortal();
12729 if (pathified != NULL) {
12730 sv_usepvn(ST(0),pathified,strlen(pathified));
12739 vmspath_fromperl(pTHX_ CV *cv)
12746 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
12747 utf8_fl = SvUTF8(ST(0));
12748 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12749 ST(0) = sv_newmortal();
12750 if (vmspath != NULL) {
12751 sv_usepvn(ST(0),vmspath,strlen(vmspath));
12760 unixpath_fromperl(pTHX_ CV *cv)
12767 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
12768 utf8_fl = SvUTF8(ST(0));
12769 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12770 ST(0) = sv_newmortal();
12771 if (unixpath != NULL) {
12772 sv_usepvn(ST(0),unixpath,strlen(unixpath));
12781 candelete_fromperl(pTHX_ CV *cv)
12789 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
12791 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12792 Newx(fspec, VMS_MAXRSS, char);
12793 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
12794 if (isGV_with_GP(mysv)) {
12795 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
12796 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12804 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
12805 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12812 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
12818 rmscopy_fromperl(pTHX_ CV *cv)
12821 char *inspec, *outspec, *inp, *outp;
12827 if (items < 2 || items > 3)
12828 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
12830 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12831 Newx(inspec, VMS_MAXRSS, char);
12832 if (isGV_with_GP(mysv)) {
12833 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
12834 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12835 ST(0) = sv_2mortal(newSViv(0));
12842 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
12843 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12844 ST(0) = sv_2mortal(newSViv(0));
12849 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
12850 Newx(outspec, VMS_MAXRSS, char);
12851 if (isGV_with_GP(mysv)) {
12852 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
12853 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12854 ST(0) = sv_2mortal(newSViv(0));
12862 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
12863 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12864 ST(0) = sv_2mortal(newSViv(0));
12870 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
12872 ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag)));
12878 /* The mod2fname is limited to shorter filenames by design, so it should
12879 * not be modified to support longer EFS pathnames
12882 mod2fname(pTHX_ CV *cv)
12885 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
12886 workbuff[NAM$C_MAXRSS*1 + 1];
12887 SSize_t counter, num_entries;
12888 /* ODS-5 ups this, but we want to be consistent, so... */
12889 int max_name_len = 39;
12890 AV *in_array = (AV *)SvRV(ST(0));
12892 num_entries = av_tindex(in_array);
12894 /* All the names start with PL_. */
12895 strcpy(ultimate_name, "PL_");
12897 /* Clean up our working buffer */
12898 Zero(work_name, sizeof(work_name), char);
12900 /* Run through the entries and build up a working name */
12901 for(counter = 0; counter <= num_entries; counter++) {
12902 /* If it's not the first name then tack on a __ */
12904 my_strlcat(work_name, "__", sizeof(work_name));
12906 my_strlcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)), sizeof(work_name));
12909 /* Check to see if we actually have to bother...*/
12910 if (strlen(work_name) + 3 <= max_name_len) {
12911 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
12913 /* It's too darned big, so we need to go strip. We use the same */
12914 /* algorithm as xsubpp does. First, strip out doubled __ */
12915 char *source, *dest, last;
12918 for (source = work_name; *source; source++) {
12919 if (last == *source && last == '_') {
12925 /* Go put it back */
12926 my_strlcpy(work_name, workbuff, sizeof(work_name));
12927 /* Is it still too big? */
12928 if (strlen(work_name) + 3 > max_name_len) {
12929 /* Strip duplicate letters */
12932 for (source = work_name; *source; source++) {
12933 if (last == toUPPER_A(*source)) {
12937 last = toUPPER_A(*source);
12939 my_strlcpy(work_name, workbuff, sizeof(work_name));
12942 /* Is it *still* too big? */
12943 if (strlen(work_name) + 3 > max_name_len) {
12944 /* Too bad, we truncate */
12945 work_name[max_name_len - 2] = 0;
12947 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
12950 /* Okay, return it */
12951 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
12956 hushexit_fromperl(pTHX_ CV *cv)
12961 VMSISH_HUSHED = SvTRUE(ST(0));
12963 ST(0) = boolSV(VMSISH_HUSHED);
12969 Perl_vms_start_glob(pTHX_ SV *tmpglob, IO *io)
12972 struct vs_str_st *rslt;
12976 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
12979 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12980 struct dsc$descriptor_vs rsdsc;
12981 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
12982 unsigned long hasver = 0, isunix = 0;
12983 unsigned long int lff_flags = 0;
12985 int vms_old_glob = 1;
12987 if (!SvOK(tmpglob)) {
12988 SETERRNO(ENOENT,RMS$_FNF);
12992 vms_old_glob = !DECC_FILENAME_UNIX_REPORT;
12994 #ifdef VMS_LONGNAME_SUPPORT
12995 lff_flags = LIB$M_FIL_LONG_NAMES;
12997 /* The Newx macro will not allow me to assign a smaller array
12998 * to the rslt pointer, so we will assign it to the begin char pointer
12999 * and then copy the value into the rslt pointer.
13001 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13002 rslt = (struct vs_str_st *)begin;
13004 rstr = &rslt->str[0];
13005 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13006 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13007 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13008 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13010 Newx(vmsspec, VMS_MAXRSS, char);
13012 /* We could find out if there's an explicit dev/dir or version
13013 by peeking into lib$find_file's internal context at
13014 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13015 but that's unsupported, so I don't want to do it now and
13016 have it bite someone in the future. */
13017 /* Fix-me: vms_split_path() is the only way to do this, the
13018 existing method will fail with many legal EFS or UNIX specifications
13021 cp = SvPV(tmpglob,i);
13024 if (cp[i] == ';') hasver = 1;
13025 if (cp[i] == '.') {
13026 if (sts) hasver = 1;
13029 if (cp[i] == '/') {
13030 hasdir = isunix = 1;
13033 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13039 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13040 if ((hasdir == 0) && DECC_FILENAME_UNIX_REPORT) {
13044 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13045 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13046 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13052 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13053 if (!stat_sts && S_ISDIR(st.st_mode)) {
13055 const char * fname;
13058 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13059 /* path delimiter of ':>]', if so, then the old behavior has */
13060 /* obviously been specifically requested */
13062 fname = SvPVX_const(tmpglob);
13063 fname_len = strlen(fname);
13064 vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13065 if (vms_old_glob || (vms_dir != NULL)) {
13066 wilddsc.dsc$a_pointer = tovmspath_utf8(
13067 SvPVX(tmpglob),vmsspec,NULL);
13068 ok = (wilddsc.dsc$a_pointer != NULL);
13069 /* maybe passed 'foo' rather than '[.foo]', thus not
13073 /* Operate just on the directory, the special stat/fstat for */
13074 /* leaves the fileified specification in the st_devnam */
13076 wilddsc.dsc$a_pointer = st.st_devnam;
13081 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13082 ok = (wilddsc.dsc$a_pointer != NULL);
13085 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13087 /* If not extended character set, replace ? with % */
13088 /* With extended character set, ? is a wildcard single character */
13089 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13092 if (!DECC_EFS_CHARSET)
13094 } else if (*cp == '%') {
13096 } else if (*cp == '*') {
13102 wv_sts = vms_split_path(
13103 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13104 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13105 &wvs_spec, &wvs_len);
13114 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13115 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13116 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13120 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13121 &dfltdsc,NULL,&rms_sts,&lff_flags);
13122 if (!$VMS_STATUS_SUCCESS(sts))
13125 /* with varying string, 1st word of buffer contains result length */
13126 rstr[rslt->length] = '\0';
13128 /* Find where all the components are */
13129 v_sts = vms_split_path
13144 /* If no version on input, truncate the version on output */
13145 if (!hasver && (vs_len > 0)) {
13152 /* In Unix report mode, remove the ".dir;1" from the name */
13153 /* if it is a real directory */
13154 if (DECC_FILENAME_UNIX_REPORT && DECC_EFS_CHARSET) {
13155 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13159 ret_sts = flex_lstat(rstr, &statbuf);
13160 if ((ret_sts == 0) &&
13161 S_ISDIR(statbuf.st_mode)) {
13168 /* No version & a null extension on UNIX handling */
13169 if ((e_len == 1) && DECC_READDIR_DROPDOTNOTYPE) {
13175 if (!DECC_EFS_CASE_PRESERVE) {
13176 for (cp = rstr; *cp; cp++) *cp = toLOWER_L1(*cp);
13179 /* Find File treats a Null extension as return all extensions */
13180 /* This is contrary to Perl expectations */
13182 if (wildstar || wildquery || vms_old_glob) {
13183 /* really need to see if the returned file name matched */
13184 /* but for now will assume that it matches */
13187 /* Exact Match requested */
13188 /* How are directories handled? - like a file */
13189 if ((e_len == we_len) && (n_len == wn_len)) {
13193 t1 = strncmp(e_spec, we_spec, e_len);
13197 t1 = strncmp(n_spec, we_spec, n_len);
13208 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13212 /* Start with the name */
13215 strcat(begin,"\n");
13216 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13219 if (cxt) (void)lib$find_file_end(&cxt);
13222 /* Be POSIXish: return the input pattern when no matches */
13223 my_strlcpy(rstr, SvPVX(tmpglob), VMS_MAXRSS);
13225 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13228 if (ok && sts != RMS$_NMF &&
13229 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13232 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13234 PerlIO_close(tmpfp);
13238 PerlIO_rewind(tmpfp);
13239 IoTYPE(io) = IoTYPE_RDONLY;
13240 IoIFP(io) = fp = tmpfp;
13241 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13251 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13255 unixrealpath_fromperl(pTHX_ CV *cv)
13258 char *fspec, *rslt_spec, *rslt;
13261 if (!items || items != 1)
13262 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13264 fspec = SvPV(ST(0),n_a);
13265 if (!fspec || !*fspec) XSRETURN_UNDEF;
13267 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13268 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13270 ST(0) = sv_newmortal();
13272 sv_usepvn(ST(0),rslt,strlen(rslt));
13274 Safefree(rslt_spec);
13279 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13283 vmsrealpath_fromperl(pTHX_ CV *cv)
13286 char *fspec, *rslt_spec, *rslt;
13289 if (!items || items != 1)
13290 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13292 fspec = SvPV(ST(0),n_a);
13293 if (!fspec || !*fspec) XSRETURN_UNDEF;
13295 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13296 rslt = do_vms_realname(fspec, rslt_spec, NULL);
13298 ST(0) = sv_newmortal();
13300 sv_usepvn(ST(0),rslt,strlen(rslt));
13302 Safefree(rslt_spec);
13308 * A thin wrapper around decc$symlink to make sure we follow the
13309 * standard and do not create a symlink with a zero-length name,
13310 * and convert the target to Unix format, as the CRTL can't handle
13311 * targets in VMS format.
13313 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13315 Perl_my_symlink(pTHX_ const char *contents, const char *link_name)
13320 if (!link_name || !*link_name) {
13321 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13325 utarget = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
13326 /* An untranslatable filename should be passed through. */
13327 (void) int_tounixspec(contents, utarget, NULL);
13328 sts = symlink(utarget, link_name);
13329 PerlMem_free(utarget);
13334 #endif /* HAS_SYMLINK */
13336 int do_vms_case_tolerant(void);
13339 case_tolerant_process_fromperl(pTHX_ CV *cv)
13342 ST(0) = boolSV(do_vms_case_tolerant());
13346 #ifdef USE_ITHREADS
13349 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
13350 struct interp_intern *dst)
13352 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13354 memcpy(dst,src,sizeof(struct interp_intern));
13360 Perl_sys_intern_clear(pTHX)
13365 Perl_sys_intern_init(pTHX)
13367 unsigned int ix = RAND_MAX;
13372 MY_POSIX_EXIT = vms_posix_exit;
13375 MY_INV_RAND_MAX = 1./x;
13379 init_os_extras(void)
13382 char* file = __FILE__;
13383 if (DECC_DISABLE_TO_VMS_LOGNAME_TRANSLATION) {
13384 no_translate_barewords = TRUE;
13386 no_translate_barewords = FALSE;
13389 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
13390 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
13391 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
13392 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
13393 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
13394 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
13395 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
13396 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
13397 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
13398 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
13399 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
13400 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
13401 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
13402 newXSproto("VMS::Filespec::case_tolerant_process",
13403 case_tolerant_process_fromperl,file,"");
13405 store_pipelocs(aTHX); /* will redo any earlier attempts */
13410 #if __CRTL_VER == 80200000
13411 /* This missed getting in to the DECC SDK for 8.2 */
13412 char *realpath(const char *file_name, char * resolved_name, ...);
13415 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
13416 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
13417 * The perl fallback routine to provide realpath() is not as efficient
13425 /* Hack, use old stat() as fastest way of getting ino_t and device */
13426 int decc$stat(const char *name, void * statbuf);
13427 #if __CRTL_VER >= 80200000
13428 int decc$lstat(const char *name, void * statbuf);
13430 #define decc$lstat decc$stat
13438 /* Realpath is fragile. In 8.3 it does not work if the feature
13439 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
13440 * links are implemented in RMS, not the CRTL. It also can fail if the
13441 * user does not have read/execute access to some of the directories.
13442 * So in order for Do What I Mean mode to work, if realpath() fails,
13443 * fall back to looking up the filename by the device name and FID.
13446 int vms_fid_to_name(char * outname, int outlen,
13447 const char * name, int lstat_flag, mode_t * mode)
13449 #pragma message save
13450 #pragma message disable MISALGNDSTRCT
13451 #pragma message disable MISALGNDMEM
13452 #pragma member_alignment save
13453 #pragma nomember_alignment
13456 unsigned short st_ino[3];
13457 unsigned short old_st_mode;
13458 unsigned long padl[30]; /* plenty of room */
13460 #pragma message restore
13461 #pragma member_alignment restore
13464 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13465 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13470 /* Need to follow the mostly the same rules as flex_stat_int, or we may get
13471 * unexpected answers
13474 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
13475 if (fileified == NULL)
13476 _ckvmssts_noperl(SS$_INSFMEM);
13478 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
13479 if (temp_fspec == NULL)
13480 _ckvmssts_noperl(SS$_INSFMEM);
13483 /* First need to try as a directory */
13484 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13485 if (ret_spec != NULL) {
13486 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
13487 if (ret_spec != NULL) {
13488 if (lstat_flag == 0)
13489 sts = decc$stat(fileified, &statbuf);
13491 sts = decc$lstat(fileified, &statbuf);
13495 /* Then as a VMS file spec */
13497 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
13498 if (ret_spec != NULL) {
13499 if (lstat_flag == 0) {
13500 sts = decc$stat(temp_fspec, &statbuf);
13502 sts = decc$lstat(temp_fspec, &statbuf);
13508 /* Next try - allow multiple dots with out EFS CHARSET */
13509 /* The CRTL stat() falls down hard on multi-dot filenames in unix
13510 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
13511 * enable it if it isn't already.
13513 if (!DECC_EFS_CHARSET && (efs_charset_index > 0))
13514 decc$feature_set_value(efs_charset_index, 1, 1);
13515 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13516 if (lstat_flag == 0) {
13517 sts = decc$stat(name, &statbuf);
13519 sts = decc$lstat(name, &statbuf);
13521 if (!DECC_EFS_CHARSET && (efs_charset_index > 0))
13522 decc$feature_set_value(efs_charset_index, 1, 0);
13526 /* and then because the Perl Unix to VMS conversion is not perfect */
13527 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
13528 /* characters from filenames so we need to try it as-is */
13530 if (lstat_flag == 0) {
13531 sts = decc$stat(name, &statbuf);
13533 sts = decc$lstat(name, &statbuf);
13540 dvidsc.dsc$a_pointer=statbuf.st_dev;
13541 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
13543 specdsc.dsc$a_pointer = outname;
13544 specdsc.dsc$w_length = outlen-1;
13546 vms_sts = lib$fid_to_name
13547 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
13548 if ($VMS_STATUS_SUCCESS(vms_sts)) {
13549 outname[specdsc.dsc$w_length] = 0;
13551 /* Return the mode */
13553 *mode = statbuf.old_st_mode;
13557 PerlMem_free(temp_fspec);
13558 PerlMem_free(fileified);
13565 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
13568 char * rslt = NULL;
13571 if (DECC_POSIX_COMPLIANT_PATHNAMES) {
13572 /* realpath currently only works if posix compliant pathnames are
13573 * enabled. It may start working when they are not, but in that
13574 * case we still want the fallback behavior for backwards compatibility
13576 rslt = realpath(filespec, outbuf);
13580 if (rslt == NULL) {
13582 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13583 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13586 /* Fall back to fid_to_name */
13588 Newx(vms_spec, VMS_MAXRSS + 1, char);
13590 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
13594 /* Now need to trim the version off */
13595 sts = vms_split_path
13615 /* Trim off the version */
13616 int file_len = v_len + r_len + d_len + n_len + e_len;
13617 vms_spec[file_len] = 0;
13619 /* Trim off the .DIR if this is a directory */
13620 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13621 if (S_ISDIR(my_mode)) {
13627 /* Drop NULL extensions on UNIX file specification */
13628 if ((e_len == 1) && DECC_READDIR_DROPDOTNOTYPE) {
13633 /* The result is expected to be in UNIX format */
13634 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
13636 /* Downcase if input had any lower case letters and
13637 * case preservation is not in effect.
13639 if (!DECC_EFS_CASE_PRESERVE) {
13640 for (cp = filespec; *cp; cp++)
13641 if (islower(*cp)) { haslower = 1; break; }
13643 if (haslower) __mystrtolower(rslt);
13648 /* Now for some hacks to deal with backwards and forward */
13649 /* compatibility */
13650 if (!DECC_EFS_CHARSET) {
13652 /* 1. ODS-2 mode wants to do a syntax only translation */
13653 rslt = int_rmsexpand(filespec, outbuf,
13654 NULL, 0, NULL, utf8_fl);
13657 if (DECC_FILENAME_UNIX_REPORT) {
13659 char * vms_dir_name;
13662 /* 2. ODS-5 / UNIX report mode should return a failure */
13663 /* if the parent directory also does not exist */
13664 /* Otherwise, get the real path for the parent */
13665 /* and add the child to it. */
13667 /* basename / dirname only available for VMS 7.0+ */
13668 /* So we may need to implement them as common routines */
13670 Newx(dir_name, VMS_MAXRSS + 1, char);
13671 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
13672 dir_name[0] = '\0';
13675 /* First try a VMS parse */
13676 sts = vms_split_path
13694 int dir_len = v_len + r_len + d_len + n_len;
13696 memcpy(dir_name, filespec, dir_len);
13697 dir_name[dir_len] = '\0';
13698 file_name = (char *)&filespec[dir_len + 1];
13701 /* This must be UNIX */
13704 tchar = strrchr(filespec, '/');
13706 if (tchar != NULL) {
13707 int dir_len = tchar - filespec;
13708 memcpy(dir_name, filespec, dir_len);
13709 dir_name[dir_len] = '\0';
13710 file_name = (char *) &filespec[dir_len + 1];
13714 /* Dir name is defaulted */
13715 if (dir_name[0] == 0) {
13717 dir_name[1] = '\0';
13720 /* Need realpath for the directory */
13721 sts = vms_fid_to_name(vms_dir_name,
13723 dir_name, 0, NULL);
13726 /* Now need to pathify it. */
13727 char *tdir = int_pathify_dirspec(vms_dir_name,
13730 /* And now add the original filespec to it */
13731 if (file_name != NULL) {
13732 my_strlcat(outbuf, file_name, VMS_MAXRSS);
13736 Safefree(vms_dir_name);
13737 Safefree(dir_name);
13741 Safefree(vms_spec);
13747 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
13750 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13751 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13753 /* Fall back to fid_to_name */
13755 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
13762 /* Now need to trim the version off */
13763 sts = vms_split_path
13783 /* Trim off the version */
13784 int file_len = v_len + r_len + d_len + n_len + e_len;
13785 outbuf[file_len] = 0;
13787 /* Downcase if input had any lower case letters and
13788 * case preservation is not in effect.
13790 if (!DECC_EFS_CASE_PRESERVE) {
13791 for (cp = filespec; *cp; cp++)
13792 if (islower(*cp)) { haslower = 1; break; }
13794 if (haslower) __mystrtolower(outbuf);
13803 /* External entry points */
13805 Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13807 return do_vms_realpath(filespec, outbuf, utf8_fl);
13811 Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13813 return do_vms_realname(filespec, outbuf, utf8_fl);
13816 /* case_tolerant */
13818 /*{{{int do_vms_case_tolerant(void)*/
13819 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
13820 * controlled by a process setting.
13823 do_vms_case_tolerant(void)
13825 return vms_process_case_tolerant;
13828 /* External entry points */
13830 Perl_vms_case_tolerant(void)
13832 return do_vms_case_tolerant();
13835 /* Start of DECC RTL Feature handling */
13838 set_feature_default(const char *name, int value)
13844 /* If the feature has been explicitly disabled in the environment,
13845 * then don't enable it here.
13848 status = simple_trnlnm(name, val_str, sizeof(val_str));
13850 val_str[0] = toUPPER_A(val_str[0]);
13851 if (val_str[0] == 'D' || val_str[0] == '0' || val_str[0] == 'F')
13856 index = decc$feature_get_index(name);
13858 status = decc$feature_set_value(index, 1, value);
13859 if (index == -1 || (status == -1)) {
13863 status = decc$feature_get_value(index, 1);
13864 if (status != value) {
13868 /* Various things may check for an environment setting
13869 * rather than the feature directly, so set that too.
13871 vmssetuserlnm(name, value ? "ENABLE" : "DISABLE");
13877 /* C RTL Feature settings */
13879 #if defined(__DECC) || defined(__DECCXX)
13886 vmsperl_set_features(void)
13888 int status, initial;
13890 char val_str[LNM$C_NAMLENGTH+1];
13891 #if defined(JPI$_CASE_LOOKUP_PERM)
13892 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
13893 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
13894 unsigned long case_perm;
13895 unsigned long case_image;
13898 /* Allow an exception to bring Perl into the VMS debugger */
13899 vms_debug_on_exception = 0;
13900 status = simple_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
13902 val_str[0] = toUPPER_A(val_str[0]);
13903 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13904 vms_debug_on_exception = 1;
13906 vms_debug_on_exception = 0;
13909 /* Debug unix/vms file translation routines */
13910 vms_debug_fileify = 0;
13911 status = simple_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
13913 val_str[0] = toUPPER_A(val_str[0]);
13914 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13915 vms_debug_fileify = 1;
13917 vms_debug_fileify = 0;
13921 /* Historically PERL has been doing vmsify / stat differently than */
13922 /* the CRTL. In particular, under some conditions the CRTL will */
13923 /* remove some illegal characters like spaces from filenames */
13924 /* resulting in some differences. The stat()/lstat() wrapper has */
13925 /* been reporting such file names as invalid and fails to stat them */
13926 /* fixing this bug so that stat()/lstat() accept these like the */
13927 /* CRTL does will result in several tests failing. */
13928 /* This should really be fixed, but for now, set up a feature to */
13929 /* enable it so that the impact can be studied. */
13930 vms_bug_stat_filename = 0;
13931 status = simple_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
13933 val_str[0] = toUPPER_A(val_str[0]);
13934 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13935 vms_bug_stat_filename = 1;
13937 vms_bug_stat_filename = 0;
13941 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
13942 vms_vtf7_filenames = 0;
13943 status = simple_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
13945 val_str[0] = toUPPER_A(val_str[0]);
13946 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13947 vms_vtf7_filenames = 1;
13949 vms_vtf7_filenames = 0;
13952 /* unlink all versions on unlink() or rename() */
13953 vms_unlink_all_versions = 0;
13954 status = simple_trnlnm("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
13956 val_str[0] = toUPPER_A(val_str[0]);
13957 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13958 vms_unlink_all_versions = 1;
13960 vms_unlink_all_versions = 0;
13963 /* The path separator in PERL5LIB is '|' unless running under a Unix shell. */
13964 PL_perllib_sep = '|';
13966 /* Detect running under GNV Bash or other UNIX like shell */
13967 gnv_unix_shell = 0;
13968 status = simple_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
13970 gnv_unix_shell = 1;
13971 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
13972 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
13973 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
13974 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
13975 vms_unlink_all_versions = 1;
13976 vms_posix_exit = 1;
13977 /* Reverse default ordering of PERL_ENV_TABLES. */
13978 defenv[0] = &crtlenvdsc;
13979 defenv[1] = &fildevdsc;
13980 PL_perllib_sep = ':';
13982 /* Some reasonable defaults that are not CRTL defaults */
13983 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
13984 set_feature_default("DECC$ARGV_PARSE_STYLE", 1); /* Requires extended parse. */
13985 set_feature_default("DECC$EFS_CHARSET", 1);
13987 /* If POSIX root doesn't exist or nothing has set it explicitly, we disable it,
13988 * which confusingly means enabling the feature. For some reason only the default
13989 * -- not current -- value can be set, so we cannot use the confusingly-named
13990 * set_feature_default function, which sets the current value.
13992 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
13993 disable_posix_root_index = s;
13995 status = simple_trnlnm("SYS$POSIX_ROOT", val_str, LNM$C_NAMLENGTH);
13996 initial = decc$feature_get_value(disable_posix_root_index, __FEATURE_MODE_INIT_STATE);
13997 if (!status || !initial) {
13998 decc$feature_set_value(disable_posix_root_index, 0, 1);
14001 /* hacks to see if known bugs are still present for testing */
14003 /* PCP mode requires creating /dev/null special device file */
14004 decc_bug_devnull = 0;
14005 status = simple_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14007 val_str[0] = toUPPER_A(val_str[0]);
14008 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14009 decc_bug_devnull = 1;
14011 decc_bug_devnull = 0;
14014 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14015 disable_to_vms_logname_translation_index = s;
14017 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14018 efs_case_preserve_index = s;
14020 s = decc$feature_get_index("DECC$EFS_CHARSET");
14021 efs_charset_index = s;
14023 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14024 filename_unix_report_index = s;
14026 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14027 filename_unix_only_index = s;
14029 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14030 filename_unix_no_version_index = s;
14032 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14033 readdir_dropdotnotype_index = s;
14035 #if __CRTL_VER >= 80200000
14036 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14037 posix_compliant_pathnames_index = s;
14040 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND)
14042 /* Report true case tolerance */
14043 /*----------------------------*/
14044 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14045 if (!$VMS_STATUS_SUCCESS(status))
14046 case_perm = PPROP$K_CASE_BLIND;
14047 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14048 if (!$VMS_STATUS_SUCCESS(status))
14049 case_image = PPROP$K_CASE_BLIND;
14050 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14051 (case_image == PPROP$K_CASE_SENSITIVE))
14052 vms_process_case_tolerant = 0;
14056 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
14057 /* for strict backward compatibility */
14058 status = simple_trnlnm("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14060 val_str[0] = toUPPER_A(val_str[0]);
14061 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14062 vms_posix_exit = 1;
14064 vms_posix_exit = 0;
14068 /* Use 32-bit pointers because that's what the image activator
14069 * assumes for the LIB$INITIALZE psect.
14071 #if __INITIAL_POINTER_SIZE
14072 #pragma pointer_size save
14073 #pragma pointer_size 32
14076 /* Create a reference to the LIB$INITIALIZE function. */
14077 extern void LIB$INITIALIZE(void);
14078 extern void (*vmsperl_unused_global_1)(void) = LIB$INITIALIZE;
14080 /* Create an array of pointers to the init functions in the special
14081 * LIB$INITIALIZE section. In our case, the array only has one entry.
14083 #pragma extern_model save
14084 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic,gbl,nowrt,noshr,long
14085 extern void (* const vmsperl_unused_global_2[])() =
14087 vmsperl_set_features,
14089 #pragma extern_model restore
14091 #if __INITIAL_POINTER_SIZE
14092 #pragma pointer_size restore
14099 #endif /* defined(__DECC) || defined(__DECCXX) */