3 * VMS-specific routines for perl5
5 * Copyright (C) 1993-2013 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_]
26 #if __CRTL_VER < 70300000
27 /* needed for home-rolled utime() */
33 #include <climsgdef.h>
43 #include <libclidef.h>
45 #include <lib$routines.h>
48 #if __CRTL_VER >= 70301000 && !defined(__VAX)
58 #include <str$routines.h>
64 #define NO_EFN EFN$C_ENF
66 #if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
67 int decc$feature_get_index(const char *name);
68 char* decc$feature_get_name(int index);
69 int decc$feature_get_value(int index, int mode);
70 int decc$feature_set_value(int index, int mode, int value);
75 #pragma member_alignment save
76 #pragma nomember_alignment longword
81 unsigned short * retadr;
83 #pragma member_alignment restore
85 /* Older versions of ssdef.h don't have these */
86 #ifndef SS$_INVFILFOROP
87 # define SS$_INVFILFOROP 3930
89 #ifndef SS$_NOSUCHOBJECT
90 # define SS$_NOSUCHOBJECT 2696
93 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
94 #define PERLIO_NOT_STDIO 0
96 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
97 * code below needs to get to the underlying CRTL routines. */
98 #define DONT_MASK_RTL_CALLS
102 /* Anticipating future expansion in lexical warnings . . . */
103 #ifndef WARN_INTERNAL
104 # define WARN_INTERNAL WARN_MISC
107 #ifdef VMS_LONGNAME_SUPPORT
108 #include <libfildef.h>
111 #if !defined(__VAX) && __CRTL_VER >= 80200000
119 #define lstat(_x, _y) stat(_x, _y)
122 /* Routine to create a decterm for use with the Perl debugger */
123 /* No headers, this information was found in the Programming Concepts Manual */
125 static int (*decw_term_port)
126 (const struct dsc$descriptor_s * display,
127 const struct dsc$descriptor_s * setup_file,
128 const struct dsc$descriptor_s * customization,
129 struct dsc$descriptor_s * result_device_name,
130 unsigned short * result_device_name_length,
133 void * char_change_buffer) = 0;
135 /* gcc's header files don't #define direct access macros
136 * corresponding to VAXC's variant structs */
138 # define uic$v_format uic$r_uic_form.uic$v_format
139 # define uic$v_group uic$r_uic_form.uic$v_group
140 # define uic$v_member uic$r_uic_form.uic$v_member
141 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
142 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
143 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
144 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
147 #if defined(NEED_AN_H_ERRNO)
151 #if defined(__DECC) || defined(__DECCXX)
152 #pragma member_alignment save
153 #pragma nomember_alignment longword
155 #pragma message disable misalgndmem
158 unsigned short int buflen;
159 unsigned short int itmcode;
161 unsigned short int *retlen;
164 struct filescan_itmlst_2 {
165 unsigned short length;
166 unsigned short itmcode;
171 unsigned short length;
172 char str[VMS_MAXRSS];
173 unsigned short pad; /* for longword struct alignment */
176 #if defined(__DECC) || defined(__DECCXX)
177 #pragma message restore
178 #pragma member_alignment restore
181 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
182 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
183 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
184 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
185 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
186 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
187 #define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c)
188 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
189 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
190 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
191 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
192 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
194 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
195 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
196 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
197 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
199 static char * int_rmsexpand_vms(
200 const char * filespec, char * outbuf, unsigned opts);
201 static char * int_rmsexpand_tovms(
202 const char * filespec, char * outbuf, unsigned opts);
203 static char *int_tovmsspec
204 (const char *path, char *buf, int dir_flag, int * utf8_flag);
205 static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl);
206 static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
207 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl);
209 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
210 #define PERL_LNM_MAX_ALLOWED_INDEX 127
212 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
213 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
216 #define PERL_LNM_MAX_ITER 10
218 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
219 #if __CRTL_VER >= 70302000 && !defined(__VAX)
220 #define MAX_DCL_SYMBOL (8192)
221 #define MAX_DCL_LINE_LENGTH (4096 - 4)
223 #define MAX_DCL_SYMBOL (1024)
224 #define MAX_DCL_LINE_LENGTH (1024 - 4)
227 static char *__mystrtolower(char *str)
229 if (str) for (; *str; ++str) *str= tolower(*str);
233 static struct dsc$descriptor_s fildevdsc =
234 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
235 static struct dsc$descriptor_s crtlenvdsc =
236 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
237 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
238 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
239 static struct dsc$descriptor_s **env_tables = defenv;
240 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
242 /* True if we shouldn't treat barewords as logicals during directory */
244 static int no_translate_barewords;
246 /* DECC Features that may need to affect how Perl interprets
247 * displays filename information
249 static int decc_disable_to_vms_logname_translation = 1;
250 static int decc_disable_posix_root = 1;
251 int decc_efs_case_preserve = 0;
252 static int decc_efs_charset = 0;
253 static int decc_efs_charset_index = -1;
254 static int decc_filename_unix_no_version = 0;
255 static int decc_filename_unix_only = 0;
256 int decc_filename_unix_report = 0;
257 int decc_posix_compliant_pathnames = 0;
258 int decc_readdir_dropdotnotype = 0;
259 static int vms_process_case_tolerant = 1;
260 int vms_vtf7_filenames = 0;
261 int gnv_unix_shell = 0;
262 static int vms_unlink_all_versions = 0;
263 static int vms_posix_exit = 0;
265 /* bug workarounds if needed */
266 int decc_bug_devnull = 1;
267 int vms_bug_stat_filename = 0;
269 static int vms_debug_on_exception = 0;
270 static int vms_debug_fileify = 0;
272 /* Simple logical name translation */
273 static int simple_trnlnm
274 (const char * logname,
278 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
279 const unsigned long attr = LNM$M_CASE_BLIND;
280 struct dsc$descriptor_s name_dsc;
282 unsigned short result;
283 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
286 name_dsc.dsc$w_length = strlen(logname);
287 name_dsc.dsc$a_pointer = (char *)logname;
288 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
289 name_dsc.dsc$b_class = DSC$K_CLASS_S;
291 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
293 if ($VMS_STATUS_SUCCESS(status)) {
295 /* Null terminate and return the string */
296 /*--------------------------------------*/
305 /* Is this a UNIX file specification?
306 * No longer a simple check with EFS file specs
307 * For now, not a full check, but need to
308 * handle POSIX ^UP^ specifications
309 * Fixing to handle ^/ cases would require
310 * changes to many other conversion routines.
313 static int is_unix_filespec(const char *path)
319 if (strncmp(path,"\"^UP^",5) != 0) {
320 pch1 = strchr(path, '/');
325 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
326 if (decc_filename_unix_report || decc_filename_unix_only) {
327 if (strcmp(path,".") == 0)
335 /* This routine converts a UCS-2 character to be VTF-7 encoded.
338 static void ucs2_to_vtf7
340 unsigned long ucs2_char,
343 unsigned char * ucs_ptr;
346 ucs_ptr = (unsigned char *)&ucs2_char;
350 hex = (ucs_ptr[1] >> 4) & 0xf;
352 outspec[2] = hex + '0';
354 outspec[2] = (hex - 9) + 'A';
355 hex = ucs_ptr[1] & 0xF;
357 outspec[3] = hex + '0';
359 outspec[3] = (hex - 9) + 'A';
361 hex = (ucs_ptr[0] >> 4) & 0xf;
363 outspec[4] = hex + '0';
365 outspec[4] = (hex - 9) + 'A';
366 hex = ucs_ptr[1] & 0xF;
368 outspec[5] = hex + '0';
370 outspec[5] = (hex - 9) + 'A';
376 /* This handles the conversion of a UNIX extended character set to a ^
377 * escaped VMS character.
378 * in a UNIX file specification.
380 * The output count variable contains the number of characters added
381 * to the output string.
383 * The return value is the number of characters read from the input string
385 static int copy_expand_unix_filename_escape
386 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
393 utf8_flag = *utf8_fl;
397 if (*inspec >= 0x80) {
398 if (utf8_fl && vms_vtf7_filenames) {
399 unsigned long ucs_char;
403 if ((*inspec & 0xE0) == 0xC0) {
405 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
406 if (ucs_char >= 0x80) {
407 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
410 } else if ((*inspec & 0xF0) == 0xE0) {
412 ucs_char = ((inspec[0] & 0xF) << 12) +
413 ((inspec[1] & 0x3f) << 6) +
415 if (ucs_char >= 0x800) {
416 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
420 #if 0 /* I do not see longer sequences supported by OpenVMS */
421 /* Maybe some one can fix this later */
422 } else if ((*inspec & 0xF8) == 0xF0) {
425 } else if ((*inspec & 0xFC) == 0xF8) {
428 } else if ((*inspec & 0xFE) == 0xFC) {
435 /* High bit set, but not a Unicode character! */
437 /* Non printing DECMCS or ISO Latin-1 character? */
438 if ((unsigned char)*inspec <= 0x9F) {
442 hex = (*inspec >> 4) & 0xF;
444 outspec[1] = hex + '0';
446 outspec[1] = (hex - 9) + 'A';
450 outspec[2] = hex + '0';
452 outspec[2] = (hex - 9) + 'A';
456 } else if ((unsigned char)*inspec == 0xA0) {
462 } else if ((unsigned char)*inspec == 0xFF) {
474 /* Is this a macro that needs to be passed through?
475 * Macros start with $( and an alpha character, followed
476 * by a string of alpha numeric characters ending with a )
477 * If this does not match, then encode it as ODS-5.
479 if ((inspec[0] == '$') && (inspec[1] == '(')) {
482 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
484 outspec[0] = inspec[0];
485 outspec[1] = inspec[1];
486 outspec[2] = inspec[2];
488 while(isalnum(inspec[tcnt]) ||
489 (inspec[2] == '.') || (inspec[2] == '_')) {
490 outspec[tcnt] = inspec[tcnt];
493 if (inspec[tcnt] == ')') {
494 outspec[tcnt] = inspec[tcnt];
511 if (decc_efs_charset == 0)
538 /* Don't escape again if following character is
539 * already something we escape.
541 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
547 /* But otherwise fall through and escape it. */
549 /* Assume that this is to be escaped */
551 outspec[1] = *inspec;
555 case ' ': /* space */
556 /* Assume that this is to be escaped */
572 /* This handles the expansion of a '^' prefix to the proper character
573 * in a UNIX file specification.
575 * The output count variable contains the number of characters added
576 * to the output string.
578 * The return value is the number of characters read from the input
581 static int copy_expand_vms_filename_escape
582 (char *outspec, const char *inspec, int *output_cnt)
589 if (*inspec == '^') {
592 /* Spaces and non-trailing dots should just be passed through,
593 * but eat the escape character.
600 case '_': /* space */
606 /* Hmm. Better leave the escape escaped. */
612 case 'U': /* Unicode - FIX-ME this is wrong. */
615 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
618 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
619 outspec[0] = c1 & 0xff;
620 outspec[1] = c2 & 0xff;
627 /* Error - do best we can to continue */
637 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
641 scnt = sscanf(inspec, "%2x", &c1);
642 outspec[0] = c1 & 0xff;
663 /* vms_split_path - Verify that the input file specification is a
664 * VMS format file specification, and provide pointers to the components of
665 * it. With EFS format filenames, this is virtually the only way to
666 * parse a VMS path specification into components.
668 * If the sum of the components do not add up to the length of the
669 * string, then the passed file specification is probably a UNIX style
672 static int vms_split_path
687 struct dsc$descriptor path_desc;
691 struct filescan_itmlst_2 item_list[9];
692 const int filespec = 0;
693 const int nodespec = 1;
694 const int devspec = 2;
695 const int rootspec = 3;
696 const int dirspec = 4;
697 const int namespec = 5;
698 const int typespec = 6;
699 const int verspec = 7;
701 /* Assume the worst for an easy exit */
715 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
716 path_desc.dsc$w_length = strlen(path);
717 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
718 path_desc.dsc$b_class = DSC$K_CLASS_S;
720 /* Get the total length, if it is shorter than the string passed
721 * then this was probably not a VMS formatted file specification
723 item_list[filespec].itmcode = FSCN$_FILESPEC;
724 item_list[filespec].length = 0;
725 item_list[filespec].component = NULL;
727 /* If the node is present, then it gets considered as part of the
728 * volume name to hopefully make things simple.
730 item_list[nodespec].itmcode = FSCN$_NODE;
731 item_list[nodespec].length = 0;
732 item_list[nodespec].component = NULL;
734 item_list[devspec].itmcode = FSCN$_DEVICE;
735 item_list[devspec].length = 0;
736 item_list[devspec].component = NULL;
738 /* root is a special case, adding it to either the directory or
739 * the device components will probably complicate things for the
740 * callers of this routine, so leave it separate.
742 item_list[rootspec].itmcode = FSCN$_ROOT;
743 item_list[rootspec].length = 0;
744 item_list[rootspec].component = NULL;
746 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
747 item_list[dirspec].length = 0;
748 item_list[dirspec].component = NULL;
750 item_list[namespec].itmcode = FSCN$_NAME;
751 item_list[namespec].length = 0;
752 item_list[namespec].component = NULL;
754 item_list[typespec].itmcode = FSCN$_TYPE;
755 item_list[typespec].length = 0;
756 item_list[typespec].component = NULL;
758 item_list[verspec].itmcode = FSCN$_VERSION;
759 item_list[verspec].length = 0;
760 item_list[verspec].component = NULL;
762 item_list[8].itmcode = 0;
763 item_list[8].length = 0;
764 item_list[8].component = NULL;
766 status = sys$filescan
767 ((const struct dsc$descriptor_s *)&path_desc, item_list,
769 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
771 /* If we parsed it successfully these two lengths should be the same */
772 if (path_desc.dsc$w_length != item_list[filespec].length)
775 /* If we got here, then it is a VMS file specification */
778 /* set the volume name */
779 if (item_list[nodespec].length > 0) {
780 *volume = item_list[nodespec].component;
781 *vol_len = item_list[nodespec].length + item_list[devspec].length;
784 *volume = item_list[devspec].component;
785 *vol_len = item_list[devspec].length;
788 *root = item_list[rootspec].component;
789 *root_len = item_list[rootspec].length;
791 *dir = item_list[dirspec].component;
792 *dir_len = item_list[dirspec].length;
794 /* Now fun with versions and EFS file specifications
795 * The parser can not tell the difference when a "." is a version
796 * delimiter or a part of the file specification.
798 if ((decc_efs_charset) &&
799 (item_list[verspec].length > 0) &&
800 (item_list[verspec].component[0] == '.')) {
801 *name = item_list[namespec].component;
802 *name_len = item_list[namespec].length + item_list[typespec].length;
803 *ext = item_list[verspec].component;
804 *ext_len = item_list[verspec].length;
809 *name = item_list[namespec].component;
810 *name_len = item_list[namespec].length;
811 *ext = item_list[typespec].component;
812 *ext_len = item_list[typespec].length;
813 *version = item_list[verspec].component;
814 *ver_len = item_list[verspec].length;
819 /* Routine to determine if the file specification ends with .dir */
820 static int is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len) {
822 /* e_len must be 4, and version must be <= 2 characters */
823 if (e_len != 4 || vs_len > 2)
826 /* If a version number is present, it needs to be one */
827 if ((vs_len == 2) && (vs_spec[1] != '1'))
830 /* Look for the DIR on the extension */
831 if (vms_process_case_tolerant) {
832 if ((toupper(e_spec[1]) == 'D') &&
833 (toupper(e_spec[2]) == 'I') &&
834 (toupper(e_spec[3]) == 'R')) {
838 /* Directory extensions are supposed to be in upper case only */
839 /* I would not be surprised if this rule can not be enforced */
840 /* if and when someone fully debugs the case sensitive mode */
841 if ((e_spec[1] == 'D') &&
842 (e_spec[2] == 'I') &&
843 (e_spec[3] == 'R')) {
852 * Routine to retrieve the maximum equivalence index for an input
853 * logical name. Some calls to this routine have no knowledge if
854 * the variable is a logical or not. So on error we return a max
857 /*{{{int my_maxidx(const char *lnm) */
859 my_maxidx(const char *lnm)
863 int attr = LNM$M_CASE_BLIND;
864 struct dsc$descriptor lnmdsc;
865 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
868 lnmdsc.dsc$w_length = strlen(lnm);
869 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
870 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
871 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
873 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
874 if ((status & 1) == 0)
881 /* Routine to remove the 2-byte prefix from the translation of a
882 * process-permanent file (PPF).
884 static inline unsigned short int
885 S_remove_ppf_prefix(const char *lnm, char *eqv, unsigned short int eqvlen)
887 if (*((int *)lnm) == *((int *)"SYS$") &&
888 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
889 ( (lnm[4] == 'O' && !strcmp(lnm,"SYS$OUTPUT")) ||
890 (lnm[4] == 'I' && !strcmp(lnm,"SYS$INPUT")) ||
891 (lnm[4] == 'E' && !strcmp(lnm,"SYS$ERROR")) ||
892 (lnm[4] == 'C' && !strcmp(lnm,"SYS$COMMAND")) ) ) {
894 memmove(eqv, eqv+4, eqvlen-4);
900 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
902 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
903 struct dsc$descriptor_s **tabvec, unsigned long int flags)
906 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
907 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
908 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
910 unsigned char acmode;
911 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
912 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
913 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
914 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
916 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
917 #if defined(PERL_IMPLICIT_CONTEXT)
920 aTHX = PERL_GET_INTERP;
926 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
927 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
929 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
930 *cp2 = _toupper(*cp1);
931 if (cp1 - lnm > LNM$C_NAMLENGTH) {
932 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
936 lnmdsc.dsc$w_length = cp1 - lnm;
937 lnmdsc.dsc$a_pointer = uplnm;
938 uplnm[lnmdsc.dsc$w_length] = '\0';
939 secure = flags & PERL__TRNENV_SECURE;
940 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
941 if (!tabvec || !*tabvec) tabvec = env_tables;
943 for (curtab = 0; tabvec[curtab]; curtab++) {
944 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
945 if (!ivenv && !secure) {
950 #if defined(PERL_IMPLICIT_CONTEXT)
953 "Can't read CRTL environ\n");
956 Perl_warn(aTHX_ "Can't read CRTL environ\n");
959 retsts = SS$_NOLOGNAM;
960 for (i = 0; environ[i]; i++) {
961 if ((eq = strchr(environ[i],'=')) &&
962 lnmdsc.dsc$w_length == (eq - environ[i]) &&
963 !strncmp(environ[i],uplnm,eq - environ[i])) {
965 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
966 if (!eqvlen) continue;
971 if (retsts != SS$_NOLOGNAM) break;
974 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
975 !str$case_blind_compare(&tmpdsc,&clisym)) {
976 if (!ivsym && !secure) {
977 unsigned short int deflen = LNM$C_NAMLENGTH;
978 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
979 /* dynamic dsc to accommodate possible long value */
980 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
981 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
983 if (eqvlen > MAX_DCL_SYMBOL) {
984 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
985 eqvlen = MAX_DCL_SYMBOL;
986 /* Special hack--we might be called before the interpreter's */
987 /* fully initialized, in which case either thr or PL_curcop */
988 /* might be bogus. We have to check, since ckWARN needs them */
989 /* both to be valid if running threaded */
990 #if defined(PERL_IMPLICIT_CONTEXT)
993 "Value of CLI symbol \"%s\" too long",lnm);
996 if (ckWARN(WARN_MISC)) {
997 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
1000 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
1002 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
1003 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1004 if (retsts == LIB$_NOSUCHSYM) continue;
1009 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
1010 midx = my_maxidx(lnm);
1011 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1012 lnmlst[1].bufadr = cp2;
1014 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1015 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1016 if (retsts == SS$_NOLOGNAM) break;
1017 eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen);
1021 if ((retsts == SS$_IVLOGNAM) ||
1022 (retsts == SS$_NOLOGNAM)) { continue; }
1023 eqvlen = strlen(eqv);
1026 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1027 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1028 if (retsts == SS$_NOLOGNAM) continue;
1029 eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen);
1035 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1036 else if (retsts == LIB$_NOSUCHSYM ||
1037 retsts == SS$_NOLOGNAM) {
1038 /* Unsuccessful lookup is normal -- no need to set errno */
1041 else if (retsts == LIB$_INVSYMNAM ||
1042 retsts == SS$_IVLOGNAM ||
1043 retsts == SS$_IVLOGTAB) {
1044 set_errno(EINVAL); set_vaxc_errno(retsts);
1046 else _ckvmssts_noperl(retsts);
1048 } /* end of vmstrnenv */
1051 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1052 /* Define as a function so we can access statics. */
1053 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1057 #if defined(PERL_IMPLICIT_CONTEXT)
1060 #ifdef SECURE_INTERNAL_GETENV
1061 flags = (PL_curinterp ? TAINTING_get : will_taint) ?
1062 PERL__TRNENV_SECURE : 0;
1065 return vmstrnenv(lnm, eqv, idx, fildev, flags);
1070 * Note: Uses Perl temp to store result so char * can be returned to
1071 * caller; this pointer will be invalidated at next Perl statement
1073 * We define this as a function rather than a macro in terms of my_getenv_len()
1074 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1077 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1079 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1082 static char *__my_getenv_eqv = NULL;
1083 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1084 unsigned long int idx = 0;
1085 int success, secure;
1089 midx = my_maxidx(lnm) + 1;
1091 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1092 /* Set up a temporary buffer for the return value; Perl will
1093 * clean it up at the next statement transition */
1094 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1095 if (!tmpsv) return NULL;
1099 /* Assume no interpreter ==> single thread */
1100 if (__my_getenv_eqv != NULL) {
1101 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1104 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1106 eqv = __my_getenv_eqv;
1109 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1110 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1112 getcwd(eqv,LNM$C_NAMLENGTH);
1116 /* Get rid of "000000/ in rooted filespecs */
1119 zeros = strstr(eqv, "/000000/");
1120 if (zeros != NULL) {
1122 mlen = len - (zeros - eqv) - 7;
1123 memmove(zeros, &zeros[7], mlen);
1131 /* Impose security constraints only if tainting */
1133 /* Impose security constraints only if tainting */
1134 secure = PL_curinterp ? TAINTING_get : will_taint;
1141 #ifdef SECURE_INTERNAL_GETENV
1142 secure ? PERL__TRNENV_SECURE : 0
1148 /* For the getenv interface we combine all the equivalence names
1149 * of a search list logical into one value to acquire a maximum
1150 * value length of 255*128 (assuming %ENV is using logicals).
1152 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1154 /* If the name contains a semicolon-delimited index, parse it
1155 * off and make sure we only retrieve the equivalence name for
1157 if ((cp2 = strchr(lnm,';')) != NULL) {
1158 my_strlcpy(uplnm, lnm, cp2 - lnm + 1);
1159 idx = strtoul(cp2+1,NULL,0);
1161 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1164 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1166 return success ? eqv : NULL;
1169 } /* end of my_getenv() */
1173 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1175 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1179 unsigned long idx = 0;
1181 static char *__my_getenv_len_eqv = NULL;
1185 midx = my_maxidx(lnm) + 1;
1187 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1188 /* Set up a temporary buffer for the return value; Perl will
1189 * clean it up at the next statement transition */
1190 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1191 if (!tmpsv) return NULL;
1195 /* Assume no interpreter ==> single thread */
1196 if (__my_getenv_len_eqv != NULL) {
1197 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1200 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1202 buf = __my_getenv_len_eqv;
1205 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1206 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1209 getcwd(buf,LNM$C_NAMLENGTH);
1212 /* Get rid of "000000/ in rooted filespecs */
1214 zeros = strstr(buf, "/000000/");
1215 if (zeros != NULL) {
1217 mlen = *len - (zeros - buf) - 7;
1218 memmove(zeros, &zeros[7], mlen);
1227 /* Impose security constraints only if tainting */
1228 secure = PL_curinterp ? TAINTING_get : will_taint;
1235 #ifdef SECURE_INTERNAL_GETENV
1236 secure ? PERL__TRNENV_SECURE : 0
1242 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1244 if ((cp2 = strchr(lnm,';')) != NULL) {
1245 my_strlcpy(buf, lnm, cp2 - lnm + 1);
1246 idx = strtoul(cp2+1,NULL,0);
1248 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1251 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1253 /* Get rid of "000000/ in rooted filespecs */
1256 zeros = strstr(buf, "/000000/");
1257 if (zeros != NULL) {
1259 mlen = *len - (zeros - buf) - 7;
1260 memmove(zeros, &zeros[7], mlen);
1266 return *len ? buf : NULL;
1269 } /* end of my_getenv_len() */
1272 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1274 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1276 /*{{{ void prime_env_iter() */
1278 prime_env_iter(void)
1279 /* Fill the %ENV associative array with all logical names we can
1280 * find, in preparation for iterating over it.
1283 static int primed = 0;
1284 HV *seenhv = NULL, *envhv;
1286 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1287 unsigned short int chan;
1288 #ifndef CLI$M_TRUSTED
1289 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1291 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1292 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0;
1294 bool have_sym = FALSE, have_lnm = FALSE;
1295 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1296 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1297 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1298 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1299 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1300 #if defined(PERL_IMPLICIT_CONTEXT)
1303 #if defined(USE_ITHREADS)
1304 static perl_mutex primenv_mutex;
1305 MUTEX_INIT(&primenv_mutex);
1308 #if defined(PERL_IMPLICIT_CONTEXT)
1309 /* We jump through these hoops because we can be called at */
1310 /* platform-specific initialization time, which is before anything is */
1311 /* set up--we can't even do a plain dTHX since that relies on the */
1312 /* interpreter structure to be initialized */
1314 aTHX = PERL_GET_INTERP;
1316 /* we never get here because the NULL pointer will cause the */
1317 /* several of the routines called by this routine to access violate */
1319 /* This routine is only called by hv.c/hv_iterinit which has a */
1320 /* context, so the real fix may be to pass it through instead of */
1321 /* the hoops above */
1326 if (primed || !PL_envgv) return;
1327 MUTEX_LOCK(&primenv_mutex);
1328 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1329 envhv = GvHVn(PL_envgv);
1330 /* Perform a dummy fetch as an lval to insure that the hash table is
1331 * set up. Otherwise, the hv_store() will turn into a nullop. */
1332 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1334 for (i = 0; env_tables[i]; i++) {
1335 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1336 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1337 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1339 if (have_sym || have_lnm) {
1340 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1341 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1342 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1343 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1346 for (i--; i >= 0; i--) {
1347 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1350 for (j = 0; environ[j]; j++) {
1351 if (!(start = strchr(environ[j],'='))) {
1352 if (ckWARN(WARN_INTERNAL))
1353 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1357 sv = newSVpv(start,0);
1359 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1364 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1365 !str$case_blind_compare(&tmpdsc,&clisym)) {
1366 my_strlcpy(cmd, "Show Symbol/Global *", sizeof(cmd));
1367 cmddsc.dsc$w_length = 20;
1368 if (env_tables[i]->dsc$w_length == 12 &&
1369 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1370 !str$case_blind_compare(&tmpdsc,&local)) my_strlcpy(cmd+12, "Local *", sizeof(cmd)-12);
1371 flags = defflags | CLI$M_NOLOGNAM;
1374 my_strlcpy(cmd, "Show Logical *", sizeof(cmd));
1375 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1376 my_strlcat(cmd," /Table=", sizeof(cmd));
1377 cmddsc.dsc$w_length = my_strlcat(cmd, env_tables[i]->dsc$a_pointer, sizeof(cmd));
1379 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1380 flags = defflags | CLI$M_NOCLISYM;
1383 /* Create a new subprocess to execute each command, to exclude the
1384 * remote possibility that someone could subvert a mbx or file used
1385 * to write multiple commands to a single subprocess.
1388 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1389 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1390 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1391 defflags &= ~CLI$M_TRUSTED;
1392 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1394 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1395 if (seenhv) SvREFCNT_dec(seenhv);
1398 char *cp1, *cp2, *key;
1399 unsigned long int sts, iosb[2], retlen, keylen;
1402 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1403 if (sts & 1) sts = iosb[0] & 0xffff;
1404 if (sts == SS$_ENDOFFILE) {
1406 while (substs == 0) { sys$hiber(); wakect++;}
1407 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1412 retlen = iosb[0] >> 16;
1413 if (!retlen) continue; /* blank line */
1415 if (iosb[1] != subpid) {
1417 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1421 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1422 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1424 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1425 if (*cp1 == '(' || /* Logical name table name */
1426 *cp1 == '=' /* Next eqv of searchlist */) continue;
1427 if (*cp1 == '"') cp1++;
1428 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1429 key = cp1; keylen = cp2 - cp1;
1430 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1431 while (*cp2 && *cp2 != '=') cp2++;
1432 while (*cp2 && *cp2 == '=') cp2++;
1433 while (*cp2 && *cp2 == ' ') cp2++;
1434 if (*cp2 == '"') { /* String translation; may embed "" */
1435 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1436 cp2++; cp1--; /* Skip "" surrounding translation */
1438 else { /* Numeric translation */
1439 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1440 cp1--; /* stop on last non-space char */
1442 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1443 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1446 PERL_HASH(hash,key,keylen);
1448 if (cp1 == cp2 && *cp2 == '.') {
1449 /* A single dot usually means an unprintable character, such as a null
1450 * to indicate a zero-length value. Get the actual value to make sure.
1452 char lnm[LNM$C_NAMLENGTH+1];
1453 char eqv[MAX_DCL_SYMBOL+1];
1455 strncpy(lnm, key, keylen);
1456 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1457 sv = newSVpvn(eqv, strlen(eqv));
1460 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1464 hv_store(envhv,key,keylen,sv,hash);
1465 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1467 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1468 /* get the PPFs for this process, not the subprocess */
1469 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1470 char eqv[LNM$C_NAMLENGTH+1];
1472 for (i = 0; ppfs[i]; i++) {
1473 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1474 sv = newSVpv(eqv,trnlen);
1476 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1481 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1482 if (buf) Safefree(buf);
1483 if (seenhv) SvREFCNT_dec(seenhv);
1484 MUTEX_UNLOCK(&primenv_mutex);
1487 } /* end of prime_env_iter */
1491 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1492 /* Define or delete an element in the same "environment" as
1493 * vmstrnenv(). If an element is to be deleted, it's removed from
1494 * the first place it's found. If it's to be set, it's set in the
1495 * place designated by the first element of the table vector.
1496 * Like setenv() returns 0 for success, non-zero on error.
1499 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1502 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1503 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1505 unsigned long int retsts, usermode = PSL$C_USER;
1506 struct itmlst_3 *ile, *ilist;
1507 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1508 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1509 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1510 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1511 $DESCRIPTOR(local,"_LOCAL");
1514 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1515 return SS$_IVLOGNAM;
1518 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1519 *cp2 = _toupper(*cp1);
1520 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1521 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1522 return SS$_IVLOGNAM;
1525 lnmdsc.dsc$w_length = cp1 - lnm;
1526 if (!tabvec || !*tabvec) tabvec = env_tables;
1528 if (!eqv) { /* we're deleting n element */
1529 for (curtab = 0; tabvec[curtab]; curtab++) {
1530 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1532 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1533 if ((cp1 = strchr(environ[i],'=')) &&
1534 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1535 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1537 return setenv(lnm,"",1) ? vaxc$errno : 0;
1540 ivenv = 1; retsts = SS$_NOLOGNAM;
1542 if (ckWARN(WARN_INTERNAL))
1543 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1544 ivenv = 1; retsts = SS$_NOSUCHPGM;
1550 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1551 !str$case_blind_compare(&tmpdsc,&clisym)) {
1552 unsigned int symtype;
1553 if (tabvec[curtab]->dsc$w_length == 12 &&
1554 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1555 !str$case_blind_compare(&tmpdsc,&local))
1556 symtype = LIB$K_CLI_LOCAL_SYM;
1557 else symtype = LIB$K_CLI_GLOBAL_SYM;
1558 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1559 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1560 if (retsts == LIB$_NOSUCHSYM) continue;
1564 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1565 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1566 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1567 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1568 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1572 else { /* we're defining a value */
1573 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1575 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1577 if (ckWARN(WARN_INTERNAL))
1578 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1579 retsts = SS$_NOSUCHPGM;
1583 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1584 eqvdsc.dsc$w_length = strlen(eqv);
1585 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1586 !str$case_blind_compare(&tmpdsc,&clisym)) {
1587 unsigned int symtype;
1588 if (tabvec[0]->dsc$w_length == 12 &&
1589 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1590 !str$case_blind_compare(&tmpdsc,&local))
1591 symtype = LIB$K_CLI_LOCAL_SYM;
1592 else symtype = LIB$K_CLI_GLOBAL_SYM;
1593 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1596 if (!*eqv) eqvdsc.dsc$w_length = 1;
1597 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1599 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1600 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1601 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1602 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1603 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1604 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1607 Newx(ilist,nseg+1,struct itmlst_3);
1610 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1613 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1615 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1616 ile->itmcode = LNM$_STRING;
1618 if ((j+1) == nseg) {
1619 ile->buflen = strlen(c);
1620 /* in case we are truncating one that's too long */
1621 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1624 ile->buflen = LNM$C_NAMLENGTH;
1628 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1632 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1637 if (!(retsts & 1)) {
1639 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1640 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1641 set_errno(EVMSERR); break;
1642 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1643 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1644 set_errno(EINVAL); break;
1646 set_errno(EACCES); break;
1651 set_vaxc_errno(retsts);
1652 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1655 /* We reset error values on success because Perl does an hv_fetch()
1656 * before each hv_store(), and if the thing we're setting didn't
1657 * previously exist, we've got a leftover error message. (Of course,
1658 * this fails in the face of
1659 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1660 * in that the error reported in $! isn't spurious,
1661 * but it's right more often than not.)
1663 set_errno(0); set_vaxc_errno(retsts);
1667 } /* end of vmssetenv() */
1670 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1671 /* This has to be a function since there's a prototype for it in proto.h */
1673 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1676 int len = strlen(lnm);
1680 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1681 if (!strcmp(uplnm,"DEFAULT")) {
1682 if (eqv && *eqv) my_chdir(eqv);
1687 (void) vmssetenv(lnm,eqv,NULL);
1691 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1693 * sets a user-mode logical in the process logical name table
1694 * used for redirection of sys$error
1697 Perl_vmssetuserlnm(const char *name, const char *eqv)
1699 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1700 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1701 unsigned long int iss, attr = LNM$M_CONFINE;
1702 unsigned char acmode = PSL$C_USER;
1703 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1705 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1706 d_name.dsc$w_length = strlen(name);
1708 lnmlst[0].buflen = strlen(eqv);
1709 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1711 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1712 if (!(iss&1)) lib$signal(iss);
1717 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1718 /* my_crypt - VMS password hashing
1719 * my_crypt() provides an interface compatible with the Unix crypt()
1720 * C library function, and uses sys$hash_password() to perform VMS
1721 * password hashing. The quadword hashed password value is returned
1722 * as a NUL-terminated 8 character string. my_crypt() does not change
1723 * the case of its string arguments; in order to match the behavior
1724 * of LOGINOUT et al., alphabetic characters in both arguments must
1725 * be upcased by the caller.
1727 * - fix me to call ACM services when available
1730 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1732 # ifndef UAI$C_PREFERRED_ALGORITHM
1733 # define UAI$C_PREFERRED_ALGORITHM 127
1735 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1736 unsigned short int salt = 0;
1737 unsigned long int sts;
1739 unsigned short int dsc$w_length;
1740 unsigned char dsc$b_type;
1741 unsigned char dsc$b_class;
1742 const char * dsc$a_pointer;
1743 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1744 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1745 struct itmlst_3 uailst[3] = {
1746 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1747 { sizeof salt, UAI$_SALT, &salt, 0},
1748 { 0, 0, NULL, NULL}};
1749 static char hash[9];
1751 usrdsc.dsc$w_length = strlen(usrname);
1752 usrdsc.dsc$a_pointer = usrname;
1753 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1755 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1759 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1764 set_vaxc_errno(sts);
1765 if (sts != RMS$_RNF) return NULL;
1768 txtdsc.dsc$w_length = strlen(textpasswd);
1769 txtdsc.dsc$a_pointer = textpasswd;
1770 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1771 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1774 return (char *) hash;
1776 } /* end of my_crypt() */
1780 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1781 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1782 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1784 /* fixup barenames that are directories for internal use.
1785 * There have been problems with the consistent handling of UNIX
1786 * style directory names when routines are presented with a name that
1787 * has no directory delimiters at all. So this routine will eventually
1790 static char * fixup_bare_dirnames(const char * name)
1792 if (decc_disable_to_vms_logname_translation) {
1798 /* 8.3, remove() is now broken on symbolic links */
1799 static int rms_erase(const char * vmsname);
1803 * A little hack to get around a bug in some implementation of remove()
1804 * that do not know how to delete a directory
1806 * Delete any file to which user has control access, regardless of whether
1807 * delete access is explicitly allowed.
1808 * Limitations: User must have write access to parent directory.
1809 * Does not block signals or ASTs; if interrupted in midstream
1810 * may leave file with an altered ACL.
1813 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1815 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1819 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1820 unsigned long int cxt = 0, aclsts, fndsts;
1822 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1824 unsigned char myace$b_length;
1825 unsigned char myace$b_type;
1826 unsigned short int myace$w_flags;
1827 unsigned long int myace$l_access;
1828 unsigned long int myace$l_ident;
1829 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1830 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1831 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1833 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1834 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1835 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1836 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1837 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1838 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1840 /* Expand the input spec using RMS, since the CRTL remove() and
1841 * system services won't do this by themselves, so we may miss
1842 * a file "hiding" behind a logical name or search list. */
1843 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
1844 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1846 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
1848 PerlMem_free(vmsname);
1852 /* Erase the file */
1853 rmsts = rms_erase(vmsname);
1855 /* Did it succeed */
1856 if ($VMS_STATUS_SUCCESS(rmsts)) {
1857 PerlMem_free(vmsname);
1861 /* If not, can changing protections help? */
1862 if (rmsts != RMS$_PRV) {
1863 set_vaxc_errno(rmsts);
1864 PerlMem_free(vmsname);
1868 /* No, so we get our own UIC to use as a rights identifier,
1869 * and the insert an ACE at the head of the ACL which allows us
1870 * to delete the file.
1872 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1873 fildsc.dsc$w_length = strlen(vmsname);
1874 fildsc.dsc$a_pointer = vmsname;
1876 newace.myace$l_ident = oldace.myace$l_ident;
1878 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1880 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1881 set_errno(ENOENT); break;
1883 set_errno(ENOTDIR); break;
1885 set_errno(ENODEV); break;
1886 case RMS$_SYN: case SS$_INVFILFOROP:
1887 set_errno(EINVAL); break;
1889 set_errno(EACCES); break;
1891 _ckvmssts_noperl(aclsts);
1893 set_vaxc_errno(aclsts);
1894 PerlMem_free(vmsname);
1897 /* Grab any existing ACEs with this identifier in case we fail */
1898 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1899 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1900 || fndsts == SS$_NOMOREACE ) {
1901 /* Add the new ACE . . . */
1902 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1905 rmsts = rms_erase(vmsname);
1906 if ($VMS_STATUS_SUCCESS(rmsts)) {
1911 /* We blew it - dir with files in it, no write priv for
1912 * parent directory, etc. Put things back the way they were. */
1913 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1916 addlst[0].bufadr = &oldace;
1917 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1924 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1925 /* We just deleted it, so of course it's not there. Some versions of
1926 * VMS seem to return success on the unlock operation anyhow (after all
1927 * the unlock is successful), but others don't.
1929 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1930 if (aclsts & 1) aclsts = fndsts;
1931 if (!(aclsts & 1)) {
1933 set_vaxc_errno(aclsts);
1936 PerlMem_free(vmsname);
1939 } /* end of kill_file() */
1943 /*{{{int do_rmdir(char *name)*/
1945 Perl_do_rmdir(pTHX_ const char *name)
1951 /* lstat returns a VMS fileified specification of the name */
1952 /* that is looked up, and also lets verifies that this is a directory */
1954 retval = flex_lstat(name, &st);
1958 /* Due to a historical feature, flex_stat/lstat can not see some */
1959 /* Unix format file names that the rest of the CRTL can see */
1960 /* Fixing that feature will cause some perl tests to fail */
1961 /* So try this one more time. */
1963 retval = lstat(name, &st.crtl_stat);
1967 /* force it to a file spec for the kill file to work. */
1968 ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
1969 if (ret_spec == NULL) {
1975 if (!S_ISDIR(st.st_mode)) {
1980 dirfile = st.st_devnam;
1982 /* It may be possible for flex_stat to find a file and vmsify() to */
1983 /* fail with ODS-2 specifications. mp_do_kill_file can not deal */
1984 /* with that case, so fail it */
1985 if (dirfile[0] == 0) {
1990 retval = mp_do_kill_file(aTHX_ dirfile, 1);
1995 } /* end of do_rmdir */
1999 * Delete any file to which user has control access, regardless of whether
2000 * delete access is explicitly allowed.
2001 * Limitations: User must have write access to parent directory.
2002 * Does not block signals or ASTs; if interrupted in midstream
2003 * may leave file with an altered ACL.
2006 /*{{{int kill_file(char *name)*/
2008 Perl_kill_file(pTHX_ const char *name)
2014 /* Convert the filename to VMS format and see if it is a directory */
2015 /* flex_lstat returns a vmsified file specification */
2016 rmsts = flex_lstat(name, &st);
2019 /* Due to a historical feature, flex_stat/lstat can not see some */
2020 /* Unix format file names that the rest of the CRTL can see when */
2021 /* ODS-2 file specifications are in use. */
2022 /* Fixing that feature will cause some perl tests to fail */
2023 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2025 vmsfile = (char *) name; /* cast ok */
2028 vmsfile = st.st_devnam;
2029 if (vmsfile[0] == 0) {
2030 /* It may be possible for flex_stat to find a file and vmsify() */
2031 /* to fail with ODS-2 specifications. mp_do_kill_file can not */
2032 /* deal with that case, so fail it */
2038 /* Remove() is allowed to delete directories, according to the X/Open
2040 * This may need special handling to work with the ACL hacks.
2042 if (S_ISDIR(st.st_mode)) {
2043 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2047 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2049 /* Need to delete all versions ? */
2050 if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2053 /* Just use lstat() here as do not need st_dev */
2054 /* and we know that the file is in VMS format or that */
2055 /* because of a historical bug, flex_stat can not see the file */
2056 while (lstat(vmsfile, (stat_t *)&st) == 0) {
2057 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2062 /* Make sure that we do not loop forever */
2073 } /* end of kill_file() */
2077 /*{{{int my_mkdir(char *,Mode_t)*/
2079 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2081 STRLEN dirlen = strlen(dir);
2083 /* zero length string sometimes gives ACCVIO */
2084 if (dirlen == 0) return -1;
2086 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2087 * null file name/type. However, it's commonplace under Unix,
2088 * so we'll allow it for a gain in portability.
2090 if (dir[dirlen-1] == '/') {
2091 char *newdir = savepvn(dir,dirlen-1);
2092 int ret = mkdir(newdir,mode);
2096 else return mkdir(dir,mode);
2097 } /* end of my_mkdir */
2100 /*{{{int my_chdir(char *)*/
2102 Perl_my_chdir(pTHX_ const char *dir)
2104 STRLEN dirlen = strlen(dir);
2105 const char *dir1 = dir;
2107 /* zero length string sometimes gives ACCVIO */
2109 SETERRNO(EINVAL, SS$_BADPARAM);
2113 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2114 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2115 * so that existing scripts do not need to be changed.
2117 while ((dirlen > 0) && (*dir1 == ' ')) {
2122 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2124 * null file name/type. However, it's commonplace under Unix,
2125 * so we'll allow it for a gain in portability.
2127 * '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
2129 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2132 newdir = (char *)PerlMem_malloc(dirlen);
2134 _ckvmssts_noperl(SS$_INSFMEM);
2135 memcpy(newdir, dir1, dirlen-1);
2136 newdir[dirlen-1] = '\0';
2137 ret = chdir(newdir);
2138 PerlMem_free(newdir);
2141 else return chdir(dir1);
2142 } /* end of my_chdir */
2146 /*{{{int my_chmod(char *, mode_t)*/
2148 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2153 STRLEN speclen = strlen(file_spec);
2155 /* zero length string sometimes gives ACCVIO */
2156 if (speclen == 0) return -1;
2158 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2159 * that implies null file name/type. However, it's commonplace under Unix,
2160 * so we'll allow it for a gain in portability.
2162 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2163 * in VMS file.dir notation.
2165 changefile = (char *) file_spec; /* cast ok */
2166 ret = flex_lstat(file_spec, &st);
2169 /* Due to a historical feature, flex_stat/lstat can not see some */
2170 /* Unix format file names that the rest of the CRTL can see when */
2171 /* ODS-2 file specifications are in use. */
2172 /* Fixing that feature will cause some perl tests to fail */
2173 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2177 /* It may be possible to get here with nothing in st_devname */
2178 /* chmod still may work though */
2179 if (st.st_devnam[0] != 0) {
2180 changefile = st.st_devnam;
2183 ret = chmod(changefile, mode);
2185 } /* end of my_chmod */
2189 /*{{{FILE *my_tmpfile()*/
2196 if ((fp = tmpfile())) return fp;
2198 cp = (char *)PerlMem_malloc(L_tmpnam+24);
2199 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2201 if (decc_filename_unix_only == 0)
2202 strcpy(cp,"Sys$Scratch:");
2205 tmpnam(cp+strlen(cp));
2206 strcat(cp,".Perltmp");
2207 fp = fopen(cp,"w+","fop=dlt");
2215 * The C RTL's sigaction fails to check for invalid signal numbers so we
2216 * help it out a bit. The docs are correct, but the actual routine doesn't
2217 * do what the docs say it will.
2219 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2221 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2222 struct sigaction* oact)
2224 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2225 SETERRNO(EINVAL, SS$_INVARG);
2228 return sigaction(sig, act, oact);
2232 #ifdef KILL_BY_SIGPRC
2233 #include <errnodef.h>
2235 /* We implement our own kill() using the undocumented system service
2236 sys$sigprc for one of two reasons:
2238 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2239 target process to do a sys$exit, which usually can't be handled
2240 gracefully...certainly not by Perl and the %SIG{} mechanism.
2242 2.) If the kill() in the CRTL can't be called from a signal
2243 handler without disappearing into the ether, i.e., the signal
2244 it purportedly sends is never trapped. Still true as of VMS 7.3.
2246 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2247 in the target process rather than calling sys$exit.
2249 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2250 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2251 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2252 with condition codes C$_SIG0+nsig*8, catching the exception on the
2253 target process and resignaling with appropriate arguments.
2255 But we don't have that VMS 7.0+ exception handler, so if you
2256 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2258 Also note that SIGTERM is listed in the docs as being "unimplemented",
2259 yet always seems to be signaled with a VMS condition code of 4 (and
2260 correctly handled for that code). So we hardwire it in.
2262 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2263 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2264 than signalling with an unrecognized (and unhandled by CRTL) code.
2267 #define _MY_SIG_MAX 28
2270 Perl_sig_to_vmscondition_int(int sig)
2272 static unsigned int sig_code[_MY_SIG_MAX+1] =
2275 SS$_HANGUP, /* 1 SIGHUP */
2276 SS$_CONTROLC, /* 2 SIGINT */
2277 SS$_CONTROLY, /* 3 SIGQUIT */
2278 SS$_RADRMOD, /* 4 SIGILL */
2279 SS$_BREAK, /* 5 SIGTRAP */
2280 SS$_OPCCUS, /* 6 SIGABRT */
2281 SS$_COMPAT, /* 7 SIGEMT */
2283 SS$_FLTOVF, /* 8 SIGFPE VAX */
2285 SS$_HPARITH, /* 8 SIGFPE AXP */
2287 SS$_ABORT, /* 9 SIGKILL */
2288 SS$_ACCVIO, /* 10 SIGBUS */
2289 SS$_ACCVIO, /* 11 SIGSEGV */
2290 SS$_BADPARAM, /* 12 SIGSYS */
2291 SS$_NOMBX, /* 13 SIGPIPE */
2292 SS$_ASTFLT, /* 14 SIGALRM */
2309 static int initted = 0;
2312 sig_code[16] = C$_SIGUSR1;
2313 sig_code[17] = C$_SIGUSR2;
2314 sig_code[20] = C$_SIGCHLD;
2315 #if __CRTL_VER >= 70300000
2316 sig_code[28] = C$_SIGWINCH;
2320 if (sig < _SIG_MIN) return 0;
2321 if (sig > _MY_SIG_MAX) return 0;
2322 return sig_code[sig];
2326 Perl_sig_to_vmscondition(int sig)
2329 if (vms_debug_on_exception != 0)
2330 lib$signal(SS$_DEBUG);
2332 return Perl_sig_to_vmscondition_int(sig);
2336 #define sys$sigprc SYS$SIGPRC
2340 int sys$sigprc(unsigned int *pidadr,
2341 struct dsc$descriptor_s *prcname,
2348 Perl_my_kill(int pid, int sig)
2353 /* sig 0 means validate the PID */
2354 /*------------------------------*/
2356 const unsigned long int jpicode = JPI$_PID;
2359 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2360 if ($VMS_STATUS_SUCCESS(status))
2363 case SS$_NOSUCHNODE:
2364 case SS$_UNREACHABLE:
2378 code = Perl_sig_to_vmscondition_int(sig);
2381 SETERRNO(EINVAL, SS$_BADPARAM);
2385 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2386 * signals are to be sent to multiple processes.
2387 * pid = 0 - all processes in group except ones that the system exempts
2388 * pid = -1 - all processes except ones that the system exempts
2389 * pid = -n - all processes in group (abs(n)) except ...
2390 * For now, just report as not supported.
2394 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2398 iss = sys$sigprc((unsigned int *)&pid,0,code);
2399 if (iss&1) return 0;
2403 set_errno(EPERM); break;
2405 case SS$_NOSUCHNODE:
2406 case SS$_UNREACHABLE:
2407 set_errno(ESRCH); break;
2409 set_errno(ENOMEM); break;
2411 _ckvmssts_noperl(iss);
2414 set_vaxc_errno(iss);
2420 /* Routine to convert a VMS status code to a UNIX status code.
2421 ** More tricky than it appears because of conflicting conventions with
2424 ** VMS status codes are a bit mask, with the least significant bit set for
2427 ** Special UNIX status of EVMSERR indicates that no translation is currently
2428 ** available, and programs should check the VMS status code.
2430 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2434 #ifndef C_FACILITY_NO
2435 #define C_FACILITY_NO 0x350000
2438 #define DCL_IVVERB 0x38090
2441 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2449 /* Assume the best or the worst */
2450 if (vms_status & STS$M_SUCCESS)
2453 unix_status = EVMSERR;
2455 msg_status = vms_status & ~STS$M_CONTROL;
2457 facility = vms_status & STS$M_FAC_NO;
2458 fac_sp = vms_status & STS$M_FAC_SP;
2459 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2461 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2467 unix_status = EFAULT;
2469 case SS$_DEVOFFLINE:
2470 unix_status = EBUSY;
2473 unix_status = ENOTCONN;
2481 case SS$_INVFILFOROP:
2485 unix_status = EINVAL;
2487 case SS$_UNSUPPORTED:
2488 unix_status = ENOTSUP;
2493 unix_status = EACCES;
2495 case SS$_DEVICEFULL:
2496 unix_status = ENOSPC;
2499 unix_status = ENODEV;
2501 case SS$_NOSUCHFILE:
2502 case SS$_NOSUCHOBJECT:
2503 unix_status = ENOENT;
2505 case SS$_ABORT: /* Fatal case */
2506 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2507 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2508 unix_status = EINTR;
2511 unix_status = E2BIG;
2514 unix_status = ENOMEM;
2517 unix_status = EPERM;
2519 case SS$_NOSUCHNODE:
2520 case SS$_UNREACHABLE:
2521 unix_status = ESRCH;
2524 unix_status = ECHILD;
2527 if ((facility == 0) && (msg_no < 8)) {
2528 /* These are not real VMS status codes so assume that they are
2529 ** already UNIX status codes
2531 unix_status = msg_no;
2537 /* Translate a POSIX exit code to a UNIX exit code */
2538 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2539 unix_status = (msg_no & 0x07F8) >> 3;
2543 /* Documented traditional behavior for handling VMS child exits */
2544 /*--------------------------------------------------------------*/
2545 if (child_flag != 0) {
2547 /* Success / Informational return 0 */
2548 /*----------------------------------*/
2549 if (msg_no & STS$K_SUCCESS)
2552 /* Warning returns 1 */
2553 /*-------------------*/
2554 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2557 /* Everything else pass through the severity bits */
2558 /*------------------------------------------------*/
2559 return (msg_no & STS$M_SEVERITY);
2562 /* Normal VMS status to ERRNO mapping attempt */
2563 /*--------------------------------------------*/
2564 switch(msg_status) {
2565 /* case RMS$_EOF: */ /* End of File */
2566 case RMS$_FNF: /* File Not Found */
2567 case RMS$_DNF: /* Dir Not Found */
2568 unix_status = ENOENT;
2570 case RMS$_RNF: /* Record Not Found */
2571 unix_status = ESRCH;
2574 unix_status = ENOTDIR;
2577 unix_status = ENODEV;
2582 unix_status = EBADF;
2585 unix_status = EEXIST;
2589 case LIB$_INVSTRDES:
2591 case LIB$_NOSUCHSYM:
2592 case LIB$_INVSYMNAM:
2594 unix_status = EINVAL;
2600 unix_status = E2BIG;
2602 case RMS$_PRV: /* No privilege */
2603 case RMS$_ACC: /* ACP file access failed */
2604 case RMS$_WLK: /* Device write locked */
2605 unix_status = EACCES;
2607 case RMS$_MKD: /* Failed to mark for delete */
2608 unix_status = EPERM;
2610 /* case RMS$_NMF: */ /* No more files */
2618 /* Try to guess at what VMS error status should go with a UNIX errno
2619 * value. This is hard to do as there could be many possible VMS
2620 * error statuses that caused the errno value to be set.
2623 int Perl_unix_status_to_vms(int unix_status)
2625 int test_unix_status;
2627 /* Trivial cases first */
2628 /*---------------------*/
2629 if (unix_status == EVMSERR)
2632 /* Is vaxc$errno sane? */
2633 /*---------------------*/
2634 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2635 if (test_unix_status == unix_status)
2638 /* If way out of range, must be VMS code already */
2639 /*-----------------------------------------------*/
2640 if (unix_status > EVMSERR)
2643 /* If out of range, punt */
2644 /*-----------------------*/
2645 if (unix_status > __ERRNO_MAX)
2649 /* Ok, now we have to do it the hard way. */
2650 /*----------------------------------------*/
2651 switch(unix_status) {
2652 case 0: return SS$_NORMAL;
2653 case EPERM: return SS$_NOPRIV;
2654 case ENOENT: return SS$_NOSUCHOBJECT;
2655 case ESRCH: return SS$_UNREACHABLE;
2656 case EINTR: return SS$_ABORT;
2659 case E2BIG: return SS$_BUFFEROVF;
2661 case EBADF: return RMS$_IFI;
2662 case ECHILD: return SS$_NONEXPR;
2664 case ENOMEM: return SS$_INSFMEM;
2665 case EACCES: return SS$_FILACCERR;
2666 case EFAULT: return SS$_ACCVIO;
2668 case EBUSY: return SS$_DEVOFFLINE;
2669 case EEXIST: return RMS$_FEX;
2671 case ENODEV: return SS$_NOSUCHDEV;
2672 case ENOTDIR: return RMS$_DIR;
2674 case EINVAL: return SS$_INVARG;
2680 case ENOSPC: return SS$_DEVICEFULL;
2681 case ESPIPE: return LIB$_INVARG;
2686 case ERANGE: return LIB$_INVARG;
2687 /* case EWOULDBLOCK */
2688 /* case EINPROGRESS */
2691 /* case EDESTADDRREQ */
2693 /* case EPROTOTYPE */
2694 /* case ENOPROTOOPT */
2695 /* case EPROTONOSUPPORT */
2696 /* case ESOCKTNOSUPPORT */
2697 /* case EOPNOTSUPP */
2698 /* case EPFNOSUPPORT */
2699 /* case EAFNOSUPPORT */
2700 /* case EADDRINUSE */
2701 /* case EADDRNOTAVAIL */
2703 /* case ENETUNREACH */
2704 /* case ENETRESET */
2705 /* case ECONNABORTED */
2706 /* case ECONNRESET */
2709 case ENOTCONN: return SS$_CLEARED;
2710 /* case ESHUTDOWN */
2711 /* case ETOOMANYREFS */
2712 /* case ETIMEDOUT */
2713 /* case ECONNREFUSED */
2715 /* case ENAMETOOLONG */
2716 /* case EHOSTDOWN */
2717 /* case EHOSTUNREACH */
2718 /* case ENOTEMPTY */
2730 /* case ECANCELED */
2734 return SS$_UNSUPPORTED;
2740 /* case EABANDONED */
2742 return SS$_ABORT; /* punt */
2747 /* default piping mailbox size */
2749 # define PERL_BUFSIZ 512
2751 # define PERL_BUFSIZ 8192
2756 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2758 unsigned long int mbxbufsiz;
2759 static unsigned long int syssize = 0;
2760 unsigned long int dviitm = DVI$_DEVNAM;
2761 char csize[LNM$C_NAMLENGTH+1];
2765 unsigned long syiitm = SYI$_MAXBUF;
2767 * Get the SYSGEN parameter MAXBUF
2769 * If the logical 'PERL_MBX_SIZE' is defined
2770 * use the value of the logical instead of PERL_BUFSIZ, but
2771 * keep the size between 128 and MAXBUF.
2774 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2777 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2778 mbxbufsiz = atoi(csize);
2780 mbxbufsiz = PERL_BUFSIZ;
2782 if (mbxbufsiz < 128) mbxbufsiz = 128;
2783 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2785 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2787 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2788 _ckvmssts_noperl(sts);
2789 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2791 } /* end of create_mbx() */
2794 /*{{{ my_popen and my_pclose*/
2796 typedef struct _iosb IOSB;
2797 typedef struct _iosb* pIOSB;
2798 typedef struct _pipe Pipe;
2799 typedef struct _pipe* pPipe;
2800 typedef struct pipe_details Info;
2801 typedef struct pipe_details* pInfo;
2802 typedef struct _srqp RQE;
2803 typedef struct _srqp* pRQE;
2804 typedef struct _tochildbuf CBuf;
2805 typedef struct _tochildbuf* pCBuf;
2808 unsigned short status;
2809 unsigned short count;
2810 unsigned long dvispec;
2813 #pragma member_alignment save
2814 #pragma nomember_alignment quadword
2815 struct _srqp { /* VMS self-relative queue entry */
2816 unsigned long qptr[2];
2818 #pragma member_alignment restore
2819 static RQE RQE_ZERO = {0,0};
2821 struct _tochildbuf {
2824 unsigned short size;
2832 unsigned short chan_in;
2833 unsigned short chan_out;
2835 unsigned int bufsize;
2847 #if defined(PERL_IMPLICIT_CONTEXT)
2848 void *thx; /* Either a thread or an interpreter */
2849 /* pointer, depending on how we're built */
2857 PerlIO *fp; /* file pointer to pipe mailbox */
2858 int useFILE; /* using stdio, not perlio */
2859 int pid; /* PID of subprocess */
2860 int mode; /* == 'r' if pipe open for reading */
2861 int done; /* subprocess has completed */
2862 int waiting; /* waiting for completion/closure */
2863 int closing; /* my_pclose is closing this pipe */
2864 unsigned long completion; /* termination status of subprocess */
2865 pPipe in; /* pipe in to sub */
2866 pPipe out; /* pipe out of sub */
2867 pPipe err; /* pipe of sub's sys$error */
2868 int in_done; /* true when in pipe finished */
2871 unsigned short xchan; /* channel to debug xterm */
2872 unsigned short xchan_valid; /* channel is assigned */
2875 struct exit_control_block
2877 struct exit_control_block *flink;
2878 unsigned long int (*exit_routine)(void);
2879 unsigned long int arg_count;
2880 unsigned long int *status_address;
2881 unsigned long int exit_status;
2884 typedef struct _closed_pipes Xpipe;
2885 typedef struct _closed_pipes* pXpipe;
2887 struct _closed_pipes {
2888 int pid; /* PID of subprocess */
2889 unsigned long completion; /* termination status of subprocess */
2891 #define NKEEPCLOSED 50
2892 static Xpipe closed_list[NKEEPCLOSED];
2893 static int closed_index = 0;
2894 static int closed_num = 0;
2896 #define RETRY_DELAY "0 ::0.20"
2897 #define MAX_RETRY 50
2899 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2900 static unsigned long mypid;
2901 static unsigned long delaytime[2];
2903 static pInfo open_pipes = NULL;
2904 static $DESCRIPTOR(nl_desc, "NL:");
2906 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2910 static unsigned long int
2911 pipe_exit_routine(void)
2914 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2915 int sts, did_stuff, j;
2918 * Flush any pending i/o, but since we are in process run-down, be
2919 * careful about referencing PerlIO structures that may already have
2920 * been deallocated. We may not even have an interpreter anymore.
2925 #if defined(PERL_IMPLICIT_CONTEXT)
2926 /* We need to use the Perl context of the thread that created */
2930 aTHX = info->err->thx;
2932 aTHX = info->out->thx;
2934 aTHX = info->in->thx;
2937 #if defined(USE_ITHREADS)
2941 && PL_perlio_fd_refcnt
2944 PerlIO_flush(info->fp);
2946 fflush((FILE *)info->fp);
2952 next we try sending an EOF...ignore if doesn't work, make sure we
2959 _ckvmssts_noperl(sys$setast(0));
2960 if (info->in && !info->in->shut_on_empty) {
2961 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2966 _ckvmssts_noperl(sys$setast(1));
2970 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2972 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2977 _ckvmssts_noperl(sys$setast(0));
2978 if (info->waiting && info->done)
2980 nwait += info->waiting;
2981 _ckvmssts_noperl(sys$setast(1));
2991 _ckvmssts_noperl(sys$setast(0));
2992 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2993 sts = sys$forcex(&info->pid,0,&abort);
2994 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2997 _ckvmssts_noperl(sys$setast(1));
3001 /* again, wait for effect */
3003 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3008 _ckvmssts_noperl(sys$setast(0));
3009 if (info->waiting && info->done)
3011 nwait += info->waiting;
3012 _ckvmssts_noperl(sys$setast(1));
3021 _ckvmssts_noperl(sys$setast(0));
3022 if (!info->done) { /* We tried to be nice . . . */
3023 sts = sys$delprc(&info->pid,0);
3024 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3025 info->done = 1; /* sys$delprc is as done as we're going to get. */
3027 _ckvmssts_noperl(sys$setast(1));
3033 #if defined(PERL_IMPLICIT_CONTEXT)
3034 /* We need to use the Perl context of the thread that created */
3037 if (open_pipes->err)
3038 aTHX = open_pipes->err->thx;
3039 else if (open_pipes->out)
3040 aTHX = open_pipes->out->thx;
3041 else if (open_pipes->in)
3042 aTHX = open_pipes->in->thx;
3044 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3045 else if (!(sts & 1)) retsts = sts;
3050 static struct exit_control_block pipe_exitblock =
3051 {(struct exit_control_block *) 0,
3052 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3054 static void pipe_mbxtofd_ast(pPipe p);
3055 static void pipe_tochild1_ast(pPipe p);
3056 static void pipe_tochild2_ast(pPipe p);
3059 popen_completion_ast(pInfo info)
3061 pInfo i = open_pipes;
3064 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3065 closed_list[closed_index].pid = info->pid;
3066 closed_list[closed_index].completion = info->completion;
3068 if (closed_index == NKEEPCLOSED)
3073 if (i == info) break;
3076 if (!i) return; /* unlinked, probably freed too */
3081 Writing to subprocess ...
3082 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3084 chan_out may be waiting for "done" flag, or hung waiting
3085 for i/o completion to child...cancel the i/o. This will
3086 put it into "snarf mode" (done but no EOF yet) that discards
3089 Output from subprocess (stdout, stderr) needs to be flushed and
3090 shut down. We try sending an EOF, but if the mbx is full the pipe
3091 routine should still catch the "shut_on_empty" flag, telling it to
3092 use immediate-style reads so that "mbx empty" -> EOF.
3096 if (info->in && !info->in_done) { /* only for mode=w */
3097 if (info->in->shut_on_empty && info->in->need_wake) {
3098 info->in->need_wake = FALSE;
3099 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3101 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3105 if (info->out && !info->out_done) { /* were we also piping output? */
3106 info->out->shut_on_empty = TRUE;
3107 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3108 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3109 _ckvmssts_noperl(iss);
3112 if (info->err && !info->err_done) { /* we were piping stderr */
3113 info->err->shut_on_empty = TRUE;
3114 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3115 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3116 _ckvmssts_noperl(iss);
3118 _ckvmssts_noperl(sys$setef(pipe_ef));
3122 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3123 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3124 static void pipe_infromchild_ast(pPipe p);
3127 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3128 inside an AST routine without worrying about reentrancy and which Perl
3129 memory allocator is being used.
3131 We read data and queue up the buffers, then spit them out one at a
3132 time to the output mailbox when the output mailbox is ready for one.
3135 #define INITIAL_TOCHILDQUEUE 2
3138 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3142 char mbx1[64], mbx2[64];
3143 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3144 DSC$K_CLASS_S, mbx1},
3145 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3146 DSC$K_CLASS_S, mbx2};
3147 unsigned int dviitm = DVI$_DEVBUFSIZ;
3151 _ckvmssts_noperl(lib$get_vm(&n, &p));
3153 create_mbx(&p->chan_in , &d_mbx1);
3154 create_mbx(&p->chan_out, &d_mbx2);
3155 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3158 p->shut_on_empty = FALSE;
3159 p->need_wake = FALSE;
3162 p->iosb.status = SS$_NORMAL;
3163 p->iosb2.status = SS$_NORMAL;
3169 #ifdef PERL_IMPLICIT_CONTEXT
3173 n = sizeof(CBuf) + p->bufsize;
3175 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3176 _ckvmssts_noperl(lib$get_vm(&n, &b));
3177 b->buf = (char *) b + sizeof(CBuf);
3178 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3181 pipe_tochild2_ast(p);
3182 pipe_tochild1_ast(p);
3188 /* reads the MBX Perl is writing, and queues */
3191 pipe_tochild1_ast(pPipe p)
3194 int iss = p->iosb.status;
3195 int eof = (iss == SS$_ENDOFFILE);
3197 #ifdef PERL_IMPLICIT_CONTEXT
3203 p->shut_on_empty = TRUE;
3205 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3207 _ckvmssts_noperl(iss);
3211 b->size = p->iosb.count;
3212 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3214 p->need_wake = FALSE;
3215 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3218 p->retry = 1; /* initial call */
3221 if (eof) { /* flush the free queue, return when done */
3222 int n = sizeof(CBuf) + p->bufsize;
3224 iss = lib$remqti(&p->free, &b);
3225 if (iss == LIB$_QUEWASEMP) return;
3226 _ckvmssts_noperl(iss);
3227 _ckvmssts_noperl(lib$free_vm(&n, &b));
3231 iss = lib$remqti(&p->free, &b);
3232 if (iss == LIB$_QUEWASEMP) {
3233 int n = sizeof(CBuf) + p->bufsize;
3234 _ckvmssts_noperl(lib$get_vm(&n, &b));
3235 b->buf = (char *) b + sizeof(CBuf);
3237 _ckvmssts_noperl(iss);
3241 iss = sys$qio(0,p->chan_in,
3242 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3244 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3245 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3246 _ckvmssts_noperl(iss);
3250 /* writes queued buffers to output, waits for each to complete before
3254 pipe_tochild2_ast(pPipe p)
3257 int iss = p->iosb2.status;
3258 int n = sizeof(CBuf) + p->bufsize;
3259 int done = (p->info && p->info->done) ||
3260 iss == SS$_CANCEL || iss == SS$_ABORT;
3261 #if defined(PERL_IMPLICIT_CONTEXT)
3266 if (p->type) { /* type=1 has old buffer, dispose */
3267 if (p->shut_on_empty) {
3268 _ckvmssts_noperl(lib$free_vm(&n, &b));
3270 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3275 iss = lib$remqti(&p->wait, &b);
3276 if (iss == LIB$_QUEWASEMP) {
3277 if (p->shut_on_empty) {
3279 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3280 *p->pipe_done = TRUE;
3281 _ckvmssts_noperl(sys$setef(pipe_ef));
3283 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3284 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3288 p->need_wake = TRUE;
3291 _ckvmssts_noperl(iss);
3298 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3299 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3301 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3302 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3311 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3314 char mbx1[64], mbx2[64];
3315 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3316 DSC$K_CLASS_S, mbx1},
3317 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3318 DSC$K_CLASS_S, mbx2};
3319 unsigned int dviitm = DVI$_DEVBUFSIZ;
3321 int n = sizeof(Pipe);
3322 _ckvmssts_noperl(lib$get_vm(&n, &p));
3323 create_mbx(&p->chan_in , &d_mbx1);
3324 create_mbx(&p->chan_out, &d_mbx2);
3326 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3327 n = p->bufsize * sizeof(char);
3328 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3329 p->shut_on_empty = FALSE;
3332 p->iosb.status = SS$_NORMAL;
3333 #if defined(PERL_IMPLICIT_CONTEXT)
3336 pipe_infromchild_ast(p);
3344 pipe_infromchild_ast(pPipe p)
3346 int iss = p->iosb.status;
3347 int eof = (iss == SS$_ENDOFFILE);
3348 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3349 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3350 #if defined(PERL_IMPLICIT_CONTEXT)
3354 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3355 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3360 input shutdown if EOF from self (done or shut_on_empty)
3361 output shutdown if closing flag set (my_pclose)
3362 send data/eof from child or eof from self
3363 otherwise, re-read (snarf of data from child)
3368 if (myeof && p->chan_in) { /* input shutdown */
3369 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3374 if (myeof || kideof) { /* pass EOF to parent */
3375 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3376 pipe_infromchild_ast, p,
3379 } else if (eof) { /* eat EOF --- fall through to read*/
3381 } else { /* transmit data */
3382 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3383 pipe_infromchild_ast,p,
3384 p->buf, p->iosb.count, 0, 0, 0, 0));
3390 /* everything shut? flag as done */
3392 if (!p->chan_in && !p->chan_out) {
3393 *p->pipe_done = TRUE;
3394 _ckvmssts_noperl(sys$setef(pipe_ef));
3398 /* write completed (or read, if snarfing from child)
3399 if still have input active,
3400 queue read...immediate mode if shut_on_empty so we get EOF if empty
3402 check if Perl reading, generate EOFs as needed
3408 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3409 pipe_infromchild_ast,p,
3410 p->buf, p->bufsize, 0, 0, 0, 0);
3411 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3412 _ckvmssts_noperl(iss);
3413 } else { /* send EOFs for extra reads */
3414 p->iosb.status = SS$_ENDOFFILE;
3415 p->iosb.dvispec = 0;
3416 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3418 pipe_infromchild_ast, p, 0, 0, 0, 0));
3424 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3428 unsigned long dviitm = DVI$_DEVBUFSIZ;
3430 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3431 DSC$K_CLASS_S, mbx};
3432 int n = sizeof(Pipe);
3434 /* things like terminals and mbx's don't need this filter */
3435 if (fd && fstat(fd,&s) == 0) {
3436 unsigned long devchar;
3438 unsigned short dev_len;
3439 struct dsc$descriptor_s d_dev;
3441 struct item_list_3 items[3];
3443 unsigned short dvi_iosb[4];
3445 cptr = getname(fd, out, 1);
3446 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3447 d_dev.dsc$a_pointer = out;
3448 d_dev.dsc$w_length = strlen(out);
3449 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3450 d_dev.dsc$b_class = DSC$K_CLASS_S;
3453 items[0].code = DVI$_DEVCHAR;
3454 items[0].bufadr = &devchar;
3455 items[0].retadr = NULL;
3457 items[1].code = DVI$_FULLDEVNAM;
3458 items[1].bufadr = device;
3459 items[1].retadr = &dev_len;
3463 status = sys$getdviw
3464 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3465 _ckvmssts_noperl(status);
3466 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3467 device[dev_len] = 0;
3469 if (!(devchar & DEV$M_DIR)) {
3470 strcpy(out, device);
3476 _ckvmssts_noperl(lib$get_vm(&n, &p));
3477 p->fd_out = dup(fd);
3478 create_mbx(&p->chan_in, &d_mbx);
3479 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3480 n = (p->bufsize+1) * sizeof(char);
3481 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3482 p->shut_on_empty = FALSE;
3487 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3488 pipe_mbxtofd_ast, p,
3489 p->buf, p->bufsize, 0, 0, 0, 0));
3495 pipe_mbxtofd_ast(pPipe p)
3497 int iss = p->iosb.status;
3498 int done = p->info->done;
3500 int eof = (iss == SS$_ENDOFFILE);
3501 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3502 int err = !(iss&1) && !eof;
3503 #if defined(PERL_IMPLICIT_CONTEXT)
3507 if (done && myeof) { /* end piping */
3509 sys$dassgn(p->chan_in);
3510 *p->pipe_done = TRUE;
3511 _ckvmssts_noperl(sys$setef(pipe_ef));
3515 if (!err && !eof) { /* good data to send to file */
3516 p->buf[p->iosb.count] = '\n';
3517 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3520 if (p->retry < MAX_RETRY) {
3521 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3527 _ckvmssts_noperl(iss);
3531 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3532 pipe_mbxtofd_ast, p,
3533 p->buf, p->bufsize, 0, 0, 0, 0);
3534 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3535 _ckvmssts_noperl(iss);
3539 typedef struct _pipeloc PLOC;
3540 typedef struct _pipeloc* pPLOC;
3544 char dir[NAM$C_MAXRSS+1];
3546 static pPLOC head_PLOC = 0;
3549 free_pipelocs(pTHX_ void *head)
3552 pPLOC *pHead = (pPLOC *)head;
3564 store_pipelocs(pTHX)
3572 char temp[NAM$C_MAXRSS+1];
3576 free_pipelocs(aTHX_ &head_PLOC);
3578 /* the . directory from @INC comes last */
3580 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3581 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3582 p->next = head_PLOC;
3584 strcpy(p->dir,"./");
3586 /* get the directory from $^X */
3588 unixdir = (char *)PerlMem_malloc(VMS_MAXRSS);
3589 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3591 #ifdef PERL_IMPLICIT_CONTEXT
3592 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3594 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3596 my_strlcpy(temp, PL_origargv[0], sizeof(temp));
3597 x = strrchr(temp,']');
3599 x = strrchr(temp,'>');
3601 /* It could be a UNIX path */
3602 x = strrchr(temp,'/');
3608 /* Got a bare name, so use default directory */
3613 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3614 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3615 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3616 p->next = head_PLOC;
3618 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3622 /* reverse order of @INC entries, skip "." since entered above */
3624 #ifdef PERL_IMPLICIT_CONTEXT
3627 if (PL_incgv) av = GvAVn(PL_incgv);
3629 for (i = 0; av && i <= AvFILL(av); i++) {
3630 dirsv = *av_fetch(av,i,TRUE);
3632 if (SvROK(dirsv)) continue;
3633 dir = SvPVx(dirsv,n_a);
3634 if (strcmp(dir,".") == 0) continue;
3635 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3638 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3639 p->next = head_PLOC;
3641 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3644 /* most likely spot (ARCHLIB) put first in the list */
3647 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3648 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3649 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3650 p->next = head_PLOC;
3652 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3655 PerlMem_free(unixdir);
3659 Perl_cando_by_name_int
3660 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3661 #if !defined(PERL_IMPLICIT_CONTEXT)
3662 #define cando_by_name_int Perl_cando_by_name_int
3664 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3670 static int vmspipe_file_status = 0;
3671 static char vmspipe_file[NAM$C_MAXRSS+1];
3673 /* already found? Check and use ... need read+execute permission */
3675 if (vmspipe_file_status == 1) {
3676 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3677 && cando_by_name_int
3678 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3679 return vmspipe_file;
3681 vmspipe_file_status = 0;
3684 /* scan through stored @INC, $^X */
3686 if (vmspipe_file_status == 0) {
3687 char file[NAM$C_MAXRSS+1];
3688 pPLOC p = head_PLOC;
3693 dirlen = my_strlcpy(file, p->dir, sizeof(file));
3694 my_strlcat(file, "vmspipe.com", sizeof(file));
3697 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3698 if (!exp_res) continue;
3700 if (cando_by_name_int
3701 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3702 && cando_by_name_int
3703 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3704 vmspipe_file_status = 1;
3705 return vmspipe_file;
3708 vmspipe_file_status = -1; /* failed, use tempfiles */
3715 vmspipe_tempfile(pTHX)
3717 char file[NAM$C_MAXRSS+1];
3719 static int index = 0;
3723 /* create a tempfile */
3725 /* we can't go from W, shr=get to R, shr=get without
3726 an intermediate vulnerable state, so don't bother trying...
3728 and lib$spawn doesn't shr=put, so have to close the write
3730 So... match up the creation date/time and the FID to
3731 make sure we're dealing with the same file
3736 if (!decc_filename_unix_only) {
3737 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3738 fp = fopen(file,"w");
3740 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3741 fp = fopen(file,"w");
3743 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3744 fp = fopen(file,"w");
3749 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3750 fp = fopen(file,"w");
3752 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3753 fp = fopen(file,"w");
3755 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3756 fp = fopen(file,"w");
3760 if (!fp) return 0; /* we're hosed */
3762 fprintf(fp,"$! 'f$verify(0)'\n");
3763 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3764 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3765 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3766 fprintf(fp,"$ perl_on = \"set noon\"\n");
3767 fprintf(fp,"$ perl_exit = \"exit\"\n");
3768 fprintf(fp,"$ perl_del = \"delete\"\n");
3769 fprintf(fp,"$ pif = \"if\"\n");
3770 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3771 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3772 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3773 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3774 fprintf(fp,"$! --- build command line to get max possible length\n");
3775 fprintf(fp,"$c=perl_popen_cmd0\n");
3776 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3777 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3778 fprintf(fp,"$x=perl_popen_cmd3\n");
3779 fprintf(fp,"$c=c+x\n");
3780 fprintf(fp,"$ perl_on\n");
3781 fprintf(fp,"$ 'c'\n");
3782 fprintf(fp,"$ perl_status = $STATUS\n");
3783 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3784 fprintf(fp,"$ perl_exit 'perl_status'\n");
3787 fgetname(fp, file, 1);
3788 fstat(fileno(fp), &s0.crtl_stat);
3791 if (decc_filename_unix_only)
3792 int_tounixspec(file, file, NULL);
3793 fp = fopen(file,"r","shr=get");
3795 fstat(fileno(fp), &s1.crtl_stat);
3797 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3798 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3807 static int vms_is_syscommand_xterm(void)
3809 const static struct dsc$descriptor_s syscommand_dsc =
3810 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3812 const static struct dsc$descriptor_s decwdisplay_dsc =
3813 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3815 struct item_list_3 items[2];
3816 unsigned short dvi_iosb[4];
3817 unsigned long devchar;
3818 unsigned long devclass;
3821 /* Very simple check to guess if sys$command is a decterm? */
3822 /* First see if the DECW$DISPLAY: device exists */
3824 items[0].code = DVI$_DEVCHAR;
3825 items[0].bufadr = &devchar;
3826 items[0].retadr = NULL;
3830 status = sys$getdviw
3831 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3833 if ($VMS_STATUS_SUCCESS(status)) {
3834 status = dvi_iosb[0];
3837 if (!$VMS_STATUS_SUCCESS(status)) {
3838 SETERRNO(EVMSERR, status);
3842 /* If it does, then for now assume that we are on a workstation */
3843 /* Now verify that SYS$COMMAND is a terminal */
3844 /* for creating the debugger DECTerm */
3847 items[0].code = DVI$_DEVCLASS;
3848 items[0].bufadr = &devclass;
3849 items[0].retadr = NULL;
3853 status = sys$getdviw
3854 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3856 if ($VMS_STATUS_SUCCESS(status)) {
3857 status = dvi_iosb[0];
3860 if (!$VMS_STATUS_SUCCESS(status)) {
3861 SETERRNO(EVMSERR, status);
3865 if (devclass == DC$_TERM) {
3872 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3873 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3878 char device_name[65];
3879 unsigned short device_name_len;
3880 struct dsc$descriptor_s customization_dsc;
3881 struct dsc$descriptor_s device_name_dsc;
3883 char customization[200];
3887 unsigned short p_chan;
3889 unsigned short iosb[4];
3890 const char * cust_str =
3891 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3892 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3893 DSC$K_CLASS_S, mbx1};
3895 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
3896 /*---------------------------------------*/
3897 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
3900 /* Make sure that this is from the Perl debugger */
3901 ret_char = strstr(cmd," xterm ");
3902 if (ret_char == NULL)
3904 cptr = ret_char + 7;
3905 ret_char = strstr(cmd,"tty");
3906 if (ret_char == NULL)
3908 ret_char = strstr(cmd,"sleep");
3909 if (ret_char == NULL)
3912 if (decw_term_port == 0) {
3913 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
3914 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
3915 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
3917 status = lib$find_image_symbol
3919 &decw_term_port_dsc,
3920 (void *)&decw_term_port,
3924 /* Try again with the other image name */
3925 if (!$VMS_STATUS_SUCCESS(status)) {
3927 status = lib$find_image_symbol
3929 &decw_term_port_dsc,
3930 (void *)&decw_term_port,
3939 /* No decw$term_port, give it up */
3940 if (!$VMS_STATUS_SUCCESS(status))
3943 /* Are we on a workstation? */
3944 /* to do: capture the rows / columns and pass their properties */
3945 ret_stat = vms_is_syscommand_xterm();
3949 /* Make the title: */
3950 ret_char = strstr(cptr,"-title");
3951 if (ret_char != NULL) {
3952 while ((*cptr != 0) && (*cptr != '\"')) {
3958 while ((*cptr != 0) && (*cptr != '\"')) {
3971 strcpy(title,"Perl Debug DECTerm");
3973 sprintf(customization, cust_str, title);
3975 customization_dsc.dsc$a_pointer = customization;
3976 customization_dsc.dsc$w_length = strlen(customization);
3977 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3978 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
3980 device_name_dsc.dsc$a_pointer = device_name;
3981 device_name_dsc.dsc$w_length = sizeof device_name -1;
3982 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3983 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
3985 device_name_len = 0;
3987 /* Try to create the window */
3988 status = (*decw_term_port)
3997 if (!$VMS_STATUS_SUCCESS(status)) {
3998 SETERRNO(EVMSERR, status);
4002 device_name[device_name_len] = '\0';
4004 /* Need to set this up to look like a pipe for cleanup */
4006 status = lib$get_vm(&n, &info);
4007 if (!$VMS_STATUS_SUCCESS(status)) {
4008 SETERRNO(ENOMEM, status);
4014 info->completion = 0;
4015 info->closing = FALSE;
4022 info->in_done = TRUE;
4023 info->out_done = TRUE;
4024 info->err_done = TRUE;
4026 /* Assign a channel on this so that it will persist, and not login */
4027 /* We stash this channel in the info structure for reference. */
4028 /* The created xterm self destructs when the last channel is removed */
4029 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4030 /* So leave this assigned. */
4031 device_name_dsc.dsc$w_length = device_name_len;
4032 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4033 if (!$VMS_STATUS_SUCCESS(status)) {
4034 SETERRNO(EVMSERR, status);
4037 info->xchan_valid = 1;
4039 /* Now create a mailbox to be read by the application */
4041 create_mbx(&p_chan, &d_mbx1);
4043 /* write the name of the created terminal to the mailbox */
4044 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4045 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4047 if (!$VMS_STATUS_SUCCESS(status)) {
4048 SETERRNO(EVMSERR, status);
4052 info->fp = PerlIO_open(mbx1, mode);
4054 /* Done with this channel */
4057 /* If any errors, then clean up */
4060 _ckvmssts_noperl(lib$free_vm(&n, &info));
4068 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4071 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4073 static int handler_set_up = FALSE;
4075 unsigned long int sts, flags = CLI$M_NOWAIT;
4076 /* The use of a GLOBAL table (as was done previously) rendered
4077 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4078 * environment. Hence we've switched to LOCAL symbol table.
4080 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4082 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4083 char *in, *out, *err, mbx[512];
4085 char tfilebuf[NAM$C_MAXRSS+1];
4087 char cmd_sym_name[20];
4088 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4089 DSC$K_CLASS_S, symbol};
4090 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4092 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4093 DSC$K_CLASS_S, cmd_sym_name};
4094 struct dsc$descriptor_s *vmscmd;
4095 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4096 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4097 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4099 /* Check here for Xterm create request. This means looking for
4100 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4101 * is possible to create an xterm.
4103 if (*in_mode == 'r') {
4106 #if defined(PERL_IMPLICIT_CONTEXT)
4107 /* Can not fork an xterm with a NULL context */
4108 /* This probably could never happen */
4112 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4113 if (xterm_fd != NULL)
4117 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4119 /* once-per-program initialization...
4120 note that the SETAST calls and the dual test of pipe_ef
4121 makes sure that only the FIRST thread through here does
4122 the initialization...all other threads wait until it's
4125 Yeah, uglier than a pthread call, it's got all the stuff inline
4126 rather than in a separate routine.
4130 _ckvmssts_noperl(sys$setast(0));
4132 unsigned long int pidcode = JPI$_PID;
4133 $DESCRIPTOR(d_delay, RETRY_DELAY);
4134 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4135 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4136 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4138 if (!handler_set_up) {
4139 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4140 handler_set_up = TRUE;
4142 _ckvmssts_noperl(sys$setast(1));
4145 /* see if we can find a VMSPIPE.COM */
4148 vmspipe = find_vmspipe(aTHX);
4150 vmspipedsc.dsc$w_length = my_strlcpy(tfilebuf+1, vmspipe, sizeof(tfilebuf)-1) + 1;
4151 } else { /* uh, oh...we're in tempfile hell */
4152 tpipe = vmspipe_tempfile(aTHX);
4153 if (!tpipe) { /* a fish popular in Boston */
4154 if (ckWARN(WARN_PIPE)) {
4155 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4159 fgetname(tpipe,tfilebuf+1,1);
4160 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4162 vmspipedsc.dsc$a_pointer = tfilebuf;
4164 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4167 case RMS$_FNF: case RMS$_DNF:
4168 set_errno(ENOENT); break;
4170 set_errno(ENOTDIR); break;
4172 set_errno(ENODEV); break;
4174 set_errno(EACCES); break;
4176 set_errno(EINVAL); break;
4177 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4178 set_errno(E2BIG); break;
4179 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4180 _ckvmssts_noperl(sts); /* fall through */
4181 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4184 set_vaxc_errno(sts);
4185 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4186 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4192 _ckvmssts_noperl(lib$get_vm(&n, &info));
4194 my_strlcpy(mode, in_mode, sizeof(mode));
4197 info->completion = 0;
4198 info->closing = FALSE;
4205 info->in_done = TRUE;
4206 info->out_done = TRUE;
4207 info->err_done = TRUE;
4209 info->xchan_valid = 0;
4211 in = (char *)PerlMem_malloc(VMS_MAXRSS);
4212 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4213 out = (char *)PerlMem_malloc(VMS_MAXRSS);
4214 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4215 err = (char *)PerlMem_malloc(VMS_MAXRSS);
4216 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4218 in[0] = out[0] = err[0] = '\0';
4220 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4224 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4229 if (*mode == 'r') { /* piping from subroutine */
4231 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4233 info->out->pipe_done = &info->out_done;
4234 info->out_done = FALSE;
4235 info->out->info = info;
4237 if (!info->useFILE) {
4238 info->fp = PerlIO_open(mbx, mode);
4240 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4241 vmssetuserlnm("SYS$INPUT", mbx);
4244 if (!info->fp && info->out) {
4245 sys$cancel(info->out->chan_out);
4247 while (!info->out_done) {
4249 _ckvmssts_noperl(sys$setast(0));
4250 done = info->out_done;
4251 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4252 _ckvmssts_noperl(sys$setast(1));
4253 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4256 if (info->out->buf) {
4257 n = info->out->bufsize * sizeof(char);
4258 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4261 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4263 _ckvmssts_noperl(lib$free_vm(&n, &info));
4268 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4270 info->err->pipe_done = &info->err_done;
4271 info->err_done = FALSE;
4272 info->err->info = info;
4275 } else if (*mode == 'w') { /* piping to subroutine */
4277 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4279 info->out->pipe_done = &info->out_done;
4280 info->out_done = FALSE;
4281 info->out->info = info;
4284 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4286 info->err->pipe_done = &info->err_done;
4287 info->err_done = FALSE;
4288 info->err->info = info;
4291 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4292 if (!info->useFILE) {
4293 info->fp = PerlIO_open(mbx, mode);
4295 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4296 vmssetuserlnm("SYS$OUTPUT", mbx);
4300 info->in->pipe_done = &info->in_done;
4301 info->in_done = FALSE;
4302 info->in->info = info;
4306 if (!info->fp && info->in) {
4308 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4309 0, 0, 0, 0, 0, 0, 0, 0));
4311 while (!info->in_done) {
4313 _ckvmssts_noperl(sys$setast(0));
4314 done = info->in_done;
4315 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4316 _ckvmssts_noperl(sys$setast(1));
4317 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4320 if (info->in->buf) {
4321 n = info->in->bufsize * sizeof(char);
4322 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4325 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4327 _ckvmssts_noperl(lib$free_vm(&n, &info));
4333 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4334 /* Let the child inherit standard input, unless it's a directory. */
4336 if (my_trnlnm("SYS$INPUT", in, 0)) {
4337 if (flex_stat(in, &st) != 0 || S_ISDIR(st.st_mode))
4341 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4343 info->out->pipe_done = &info->out_done;
4344 info->out_done = FALSE;
4345 info->out->info = info;
4348 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4350 info->err->pipe_done = &info->err_done;
4351 info->err_done = FALSE;
4352 info->err->info = info;
4356 d_symbol.dsc$w_length = my_strlcpy(symbol, in, sizeof(symbol));
4357 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4359 d_symbol.dsc$w_length = my_strlcpy(symbol, err, sizeof(symbol));
4360 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4362 d_symbol.dsc$w_length = my_strlcpy(symbol, out, sizeof(symbol));
4363 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4365 /* Done with the names for the pipes */
4370 p = vmscmd->dsc$a_pointer;
4371 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4372 if (*p == '$') p++; /* remove leading $ */
4373 while (*p == ' ' || *p == '\t') p++;
4375 for (j = 0; j < 4; j++) {
4376 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4377 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4379 d_symbol.dsc$w_length = my_strlcpy(symbol, p, sizeof(symbol));
4380 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4382 if (strlen(p) > MAX_DCL_SYMBOL) {
4383 p += MAX_DCL_SYMBOL;
4388 _ckvmssts_noperl(sys$setast(0));
4389 info->next=open_pipes; /* prepend to list */
4391 _ckvmssts_noperl(sys$setast(1));
4392 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4393 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4394 * have SYS$COMMAND if we need it.
4396 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4397 0, &info->pid, &info->completion,
4398 0, popen_completion_ast,info,0,0,0));
4400 /* if we were using a tempfile, close it now */
4402 if (tpipe) fclose(tpipe);
4404 /* once the subprocess is spawned, it has copied the symbols and
4405 we can get rid of ours */
4407 for (j = 0; j < 4; j++) {
4408 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4409 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4410 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4412 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4413 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4414 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4415 vms_execfree(vmscmd);
4417 #ifdef PERL_IMPLICIT_CONTEXT
4420 PL_forkprocess = info->pid;
4427 _ckvmssts_noperl(sys$setast(0));
4429 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4430 _ckvmssts_noperl(sys$setast(1));
4431 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4433 *psts = info->completion;
4434 /* Caller thinks it is open and tries to close it. */
4435 /* This causes some problems, as it changes the error status */
4436 /* my_pclose(info->fp); */
4438 /* If we did not have a file pointer open, then we have to */
4439 /* clean up here or eventually we will run out of something */
4441 if (info->fp == NULL) {
4442 my_pclose_pinfo(aTHX_ info);
4450 } /* end of safe_popen */
4453 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4455 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4459 TAINT_PROPER("popen");
4460 PERL_FLUSHALL_FOR_CHILD;
4461 return safe_popen(aTHX_ cmd,mode,&sts);
4467 /* Routine to close and cleanup a pipe info structure */
4469 static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4471 unsigned long int retsts;
4475 /* If we were writing to a subprocess, insure that someone reading from
4476 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4477 * produce an EOF record in the mailbox.
4479 * well, at least sometimes it *does*, so we have to watch out for
4480 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4484 #if defined(USE_ITHREADS)
4488 && PL_perlio_fd_refcnt
4491 PerlIO_flush(info->fp);
4493 fflush((FILE *)info->fp);
4496 _ckvmssts(sys$setast(0));
4497 info->closing = TRUE;
4498 done = info->done && info->in_done && info->out_done && info->err_done;
4499 /* hanging on write to Perl's input? cancel it */
4500 if (info->mode == 'r' && info->out && !info->out_done) {
4501 if (info->out->chan_out) {
4502 _ckvmssts(sys$cancel(info->out->chan_out));
4503 if (!info->out->chan_in) { /* EOF generation, need AST */
4504 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4508 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4509 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4511 _ckvmssts(sys$setast(1));
4514 #if defined(USE_ITHREADS)
4518 && PL_perlio_fd_refcnt
4521 PerlIO_close(info->fp);
4523 fclose((FILE *)info->fp);
4526 we have to wait until subprocess completes, but ALSO wait until all
4527 the i/o completes...otherwise we'll be freeing the "info" structure
4528 that the i/o ASTs could still be using...
4532 _ckvmssts(sys$setast(0));
4533 done = info->done && info->in_done && info->out_done && info->err_done;
4534 if (!done) _ckvmssts(sys$clref(pipe_ef));
4535 _ckvmssts(sys$setast(1));
4536 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4538 retsts = info->completion;
4540 /* remove from list of open pipes */
4541 _ckvmssts(sys$setast(0));
4543 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4549 last->next = info->next;
4551 open_pipes = info->next;
4552 _ckvmssts(sys$setast(1));
4554 /* free buffers and structures */
4557 if (info->in->buf) {
4558 n = info->in->bufsize * sizeof(char);
4559 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4562 _ckvmssts(lib$free_vm(&n, &info->in));
4565 if (info->out->buf) {
4566 n = info->out->bufsize * sizeof(char);
4567 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4570 _ckvmssts(lib$free_vm(&n, &info->out));
4573 if (info->err->buf) {
4574 n = info->err->bufsize * sizeof(char);
4575 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4578 _ckvmssts(lib$free_vm(&n, &info->err));
4581 _ckvmssts(lib$free_vm(&n, &info));
4587 /*{{{ I32 my_pclose(PerlIO *fp)*/
4588 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4590 pInfo info, last = NULL;
4593 /* Fixme - need ast and mutex protection here */
4594 for (info = open_pipes; info != NULL; last = info, info = info->next)
4595 if (info->fp == fp) break;
4597 if (info == NULL) { /* no such pipe open */
4598 set_errno(ECHILD); /* quoth POSIX */
4599 set_vaxc_errno(SS$_NONEXPR);
4603 ret_status = my_pclose_pinfo(aTHX_ info);
4607 } /* end of my_pclose() */
4609 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4610 /* Roll our own prototype because we want this regardless of whether
4611 * _VMS_WAIT is defined.
4617 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4623 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4624 created with popen(); otherwise partially emulate waitpid() unless
4625 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4626 Also check processes not considered by the CRTL waitpid().
4628 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4630 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4637 if (statusp) *statusp = 0;
4639 for (info = open_pipes; info != NULL; info = info->next)
4640 if (info->pid == pid) break;
4642 if (info != NULL) { /* we know about this child */
4643 while (!info->done) {
4644 _ckvmssts(sys$setast(0));
4646 if (!done) _ckvmssts(sys$clref(pipe_ef));
4647 _ckvmssts(sys$setast(1));
4648 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4651 if (statusp) *statusp = info->completion;
4655 /* child that already terminated? */
4657 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4658 if (closed_list[j].pid == pid) {
4659 if (statusp) *statusp = closed_list[j].completion;
4664 /* fall through if this child is not one of our own pipe children */
4666 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4668 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4669 * in 7.2 did we get a version that fills in the VMS completion
4670 * status as Perl has always tried to do.
4673 sts = __vms_waitpid( pid, statusp, flags );
4675 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4678 /* If the real waitpid tells us the child does not exist, we
4679 * fall through here to implement waiting for a child that
4680 * was created by some means other than exec() (say, spawned
4681 * from DCL) or to wait for a process that is not a subprocess
4682 * of the current process.
4685 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4688 $DESCRIPTOR(intdsc,"0 00:00:01");
4689 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4690 unsigned long int pidcode = JPI$_PID, mypid;
4691 unsigned long int interval[2];
4692 unsigned int jpi_iosb[2];
4693 struct itmlst_3 jpilist[2] = {
4694 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4699 /* Sorry folks, we don't presently implement rooting around for
4700 the first child we can find, and we definitely don't want to
4701 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4707 /* Get the owner of the child so I can warn if it's not mine. If the
4708 * process doesn't exist or I don't have the privs to look at it,
4709 * I can go home early.
4711 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4712 if (sts & 1) sts = jpi_iosb[0];
4724 set_vaxc_errno(sts);
4728 if (ckWARN(WARN_EXEC)) {
4729 /* remind folks they are asking for non-standard waitpid behavior */
4730 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4731 if (ownerpid != mypid)
4732 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4733 "waitpid: process %x is not a child of process %x",
4737 /* simply check on it once a second until it's not there anymore. */
4739 _ckvmssts(sys$bintim(&intdsc,interval));
4740 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4741 _ckvmssts(sys$schdwk(0,0,interval,0));
4742 _ckvmssts(sys$hiber());
4744 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4749 } /* end of waitpid() */
4754 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4756 my_gconvert(double val, int ndig, int trail, char *buf)
4758 static char __gcvtbuf[DBL_DIG+1];
4761 loc = buf ? buf : __gcvtbuf;
4764 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4765 return gcvt(val,ndig,loc);
4768 loc[0] = '0'; loc[1] = '\0';
4775 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4776 static int rms_free_search_context(struct FAB * fab)
4780 nam = fab->fab$l_nam;
4781 nam->nam$b_nop |= NAM$M_SYNCHK;
4782 nam->nam$l_rlf = NULL;
4784 return sys$parse(fab, NULL, NULL);
4787 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4788 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4789 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4790 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4791 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4792 #define rms_nam_esll(nam) nam.nam$b_esl
4793 #define rms_nam_esl(nam) nam.nam$b_esl
4794 #define rms_nam_name(nam) nam.nam$l_name
4795 #define rms_nam_namel(nam) nam.nam$l_name
4796 #define rms_nam_type(nam) nam.nam$l_type
4797 #define rms_nam_typel(nam) nam.nam$l_type
4798 #define rms_nam_ver(nam) nam.nam$l_ver
4799 #define rms_nam_verl(nam) nam.nam$l_ver
4800 #define rms_nam_rsll(nam) nam.nam$b_rsl
4801 #define rms_nam_rsl(nam) nam.nam$b_rsl
4802 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4803 #define rms_set_fna(fab, nam, name, size) \
4804 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4805 #define rms_get_fna(fab, nam) fab.fab$l_fna
4806 #define rms_set_dna(fab, nam, name, size) \
4807 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4808 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4809 #define rms_set_esa(nam, name, size) \
4810 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4811 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4812 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4813 #define rms_set_rsa(nam, name, size) \
4814 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4815 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4816 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4817 #define rms_nam_name_type_l_size(nam) \
4818 (nam.nam$b_name + nam.nam$b_type)
4820 static int rms_free_search_context(struct FAB * fab)
4824 nam = fab->fab$l_naml;
4825 nam->naml$b_nop |= NAM$M_SYNCHK;
4826 nam->naml$l_rlf = NULL;
4827 nam->naml$l_long_defname_size = 0;
4830 return sys$parse(fab, NULL, NULL);
4833 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4834 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4835 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4836 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4837 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4838 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4839 #define rms_nam_esl(nam) nam.naml$b_esl
4840 #define rms_nam_name(nam) nam.naml$l_name
4841 #define rms_nam_namel(nam) nam.naml$l_long_name
4842 #define rms_nam_type(nam) nam.naml$l_type
4843 #define rms_nam_typel(nam) nam.naml$l_long_type
4844 #define rms_nam_ver(nam) nam.naml$l_ver
4845 #define rms_nam_verl(nam) nam.naml$l_long_ver
4846 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4847 #define rms_nam_rsl(nam) nam.naml$b_rsl
4848 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4849 #define rms_set_fna(fab, nam, name, size) \
4850 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4851 nam.naml$l_long_filename_size = size; \
4852 nam.naml$l_long_filename = name;}
4853 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4854 #define rms_set_dna(fab, nam, name, size) \
4855 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4856 nam.naml$l_long_defname_size = size; \
4857 nam.naml$l_long_defname = name; }
4858 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4859 #define rms_set_esa(nam, name, size) \
4860 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4861 nam.naml$l_long_expand_alloc = size; \
4862 nam.naml$l_long_expand = name; }
4863 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4864 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4865 nam.naml$l_long_expand = l_name; \
4866 nam.naml$l_long_expand_alloc = l_size; }
4867 #define rms_set_rsa(nam, name, size) \
4868 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4869 nam.naml$l_long_result = name; \
4870 nam.naml$l_long_result_alloc = size; }
4871 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4872 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4873 nam.naml$l_long_result = l_name; \
4874 nam.naml$l_long_result_alloc = l_size; }
4875 #define rms_nam_name_type_l_size(nam) \
4876 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4881 * The CRTL for 8.3 and later can create symbolic links in any mode,
4882 * however in 8.3 the unlink/remove/delete routines will only properly handle
4883 * them if one of the PCP modes is active.
4885 static int rms_erase(const char * vmsname)
4888 struct FAB myfab = cc$rms_fab;
4889 rms_setup_nam(mynam);
4891 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4892 rms_bind_fab_nam(myfab, mynam);
4894 #ifdef NAML$M_OPEN_SPECIAL
4895 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
4898 status = sys$erase(&myfab, 0, 0);
4905 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
4906 const struct dsc$descriptor_s * vms_dst_dsc,
4907 unsigned long flags)
4909 /* VMS and UNIX handle file permissions differently and the
4910 * the same ACL trick may be needed for renaming files,
4911 * especially if they are directories.
4914 /* todo: get kill_file and rename to share common code */
4915 /* I can not find online documentation for $change_acl
4916 * it appears to be replaced by $set_security some time ago */
4918 const unsigned int access_mode = 0;
4919 $DESCRIPTOR(obj_file_dsc,"FILE");
4922 unsigned long int jpicode = JPI$_UIC;
4923 int aclsts, fndsts, rnsts = -1;
4924 unsigned int ctx = 0;
4925 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4926 struct dsc$descriptor_s * clean_dsc;
4929 unsigned char myace$b_length;
4930 unsigned char myace$b_type;
4931 unsigned short int myace$w_flags;
4932 unsigned long int myace$l_access;
4933 unsigned long int myace$l_ident;
4934 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
4935 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
4937 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
4940 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
4941 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
4943 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
4944 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
4948 /* Expand the input spec using RMS, since we do not want to put
4949 * ACLs on the target of a symbolic link */
4950 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
4951 if (vmsname == NULL)
4954 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
4956 PERL_RMSEXPAND_M_SYMLINK);
4958 PerlMem_free(vmsname);
4962 /* So we get our own UIC to use as a rights identifier,
4963 * and the insert an ACE at the head of the ACL which allows us
4964 * to delete the file.
4966 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
4968 fildsc.dsc$w_length = strlen(vmsname);
4969 fildsc.dsc$a_pointer = vmsname;
4971 newace.myace$l_ident = oldace.myace$l_ident;
4974 /* Grab any existing ACEs with this identifier in case we fail */
4975 clean_dsc = &fildsc;
4976 aclsts = fndsts = sys$get_security(&obj_file_dsc,
4984 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
4985 /* Add the new ACE . . . */
4987 /* if the sys$get_security succeeded, then ctx is valid, and the
4988 * object/file descriptors will be ignored. But otherwise they
4991 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
4992 OSS$M_RELCTX, addlst, &ctx, &access_mode);
4993 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
4995 set_vaxc_errno(aclsts);
4996 PerlMem_free(vmsname);
5000 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5003 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5005 if ($VMS_STATUS_SUCCESS(rnsts)) {
5006 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5009 /* Put things back the way they were. */
5011 aclsts = sys$get_security(&obj_file_dsc,
5019 if ($VMS_STATUS_SUCCESS(aclsts)) {
5023 if (!$VMS_STATUS_SUCCESS(fndsts))
5024 sec_flags = OSS$M_RELCTX;
5026 /* Get rid of the new ACE */
5027 aclsts = sys$set_security(NULL, NULL, NULL,
5028 sec_flags, dellst, &ctx, &access_mode);
5030 /* If there was an old ACE, put it back */
5031 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5032 addlst[0].bufadr = &oldace;
5033 aclsts = sys$set_security(NULL, NULL, NULL,
5034 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5035 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5037 set_vaxc_errno(aclsts);
5043 /* Try to clear the lock on the ACL list */
5044 aclsts2 = sys$set_security(NULL, NULL, NULL,
5045 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5047 /* Rename errors are most important */
5048 if (!$VMS_STATUS_SUCCESS(rnsts))
5051 set_vaxc_errno(aclsts);
5056 if (aclsts != SS$_ACLEMPTY)
5063 PerlMem_free(vmsname);
5068 /*{{{int rename(const char *, const char * */
5069 /* Not exactly what X/Open says to do, but doing it absolutely right
5070 * and efficiently would require a lot more work. This should be close
5071 * enough to pass all but the most strict X/Open compliance test.
5074 Perl_rename(pTHX_ const char *src, const char * dst)
5083 /* Validate the source file */
5084 src_sts = flex_lstat(src, &src_st);
5087 /* No source file or other problem */
5090 if (src_st.st_devnam[0] == 0) {
5091 /* This may be possible so fail if it is seen. */
5096 dst_sts = flex_lstat(dst, &dst_st);
5099 if (dst_st.st_dev != src_st.st_dev) {
5100 /* Must be on the same device */
5105 /* VMS_INO_T_COMPARE is true if the inodes are different
5106 * to match the output of memcmp
5109 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5110 /* That was easy, the files are the same! */
5114 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5115 /* If source is a directory, so must be dest */
5123 if ((dst_sts == 0) &&
5124 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5126 /* We have issues here if vms_unlink_all_versions is set
5127 * If the destination exists, and is not a directory, then
5128 * we must delete in advance.
5130 * If the src is a directory, then we must always pre-delete
5133 * If we successfully delete the dst in advance, and the rename fails
5134 * X/Open requires that errno be EIO.
5138 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5140 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
5141 S_ISDIR(dst_st.st_mode));
5143 /* Need to delete all versions ? */
5144 if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5147 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
5148 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
5153 /* Make sure that we do not loop forever */
5165 /* We killed the destination, so only errno now is EIO */
5170 /* Originally the idea was to call the CRTL rename() and only
5171 * try the lib$rename_file if it failed.
5172 * It turns out that there are too many variants in what the
5173 * the CRTL rename might do, so only use lib$rename_file
5178 /* Is the source and dest both in VMS format */
5179 /* if the source is a directory, then need to fileify */
5180 /* and dest must be a directory or non-existent. */
5185 unsigned long flags;
5186 struct dsc$descriptor_s old_file_dsc;
5187 struct dsc$descriptor_s new_file_dsc;
5189 /* We need to modify the src and dst depending
5190 * on if one or more of them are directories.
5193 vms_dst = (char *)PerlMem_malloc(VMS_MAXRSS);
5194 if (vms_dst == NULL)
5195 _ckvmssts_noperl(SS$_INSFMEM);
5197 if (S_ISDIR(src_st.st_mode)) {
5199 char * vms_dir_file;
5201 vms_dir_file = (char *)PerlMem_malloc(VMS_MAXRSS);
5202 if (vms_dir_file == NULL)
5203 _ckvmssts_noperl(SS$_INSFMEM);
5205 /* If the dest is a directory, we must remove it */
5208 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
5210 PerlMem_free(vms_dst);
5218 /* The dest must be a VMS file specification */
5219 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5220 if (ret_str == NULL) {
5221 PerlMem_free(vms_dst);
5226 /* The source must be a file specification */
5227 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5228 if (ret_str == NULL) {
5229 PerlMem_free(vms_dst);
5230 PerlMem_free(vms_dir_file);
5234 PerlMem_free(vms_dst);
5235 vms_dst = vms_dir_file;
5238 /* File to file or file to new dir */
5240 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5241 /* VMS pathify a dir target */
5242 ret_str = int_tovmspath(dst, vms_dst, NULL);
5243 if (ret_str == NULL) {
5244 PerlMem_free(vms_dst);
5249 char * v_spec, * r_spec, * d_spec, * n_spec;
5250 char * e_spec, * vs_spec;
5251 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5253 /* fileify a target VMS file specification */
5254 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5255 if (ret_str == NULL) {
5256 PerlMem_free(vms_dst);
5261 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5262 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5263 &e_len, &vs_spec, &vs_len);
5266 /* Get rid of the version */
5270 /* Need to specify a '.' so that the extension */
5271 /* is not inherited */
5272 strcat(vms_dst,".");
5278 old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5279 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
5280 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5281 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5283 new_file_dsc.dsc$a_pointer = vms_dst;
5284 new_file_dsc.dsc$w_length = strlen(vms_dst);
5285 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5286 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5289 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5290 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5293 sts = lib$rename_file(&old_file_dsc,
5297 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5298 if (!$VMS_STATUS_SUCCESS(sts)) {
5300 /* We could have failed because VMS style permissions do not
5301 * permit renames that UNIX will allow. Just like the hack
5304 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5307 PerlMem_free(vms_dst);
5308 if (!$VMS_STATUS_SUCCESS(sts)) {
5315 if (vms_unlink_all_versions) {
5316 /* Now get rid of any previous versions of the source file that
5322 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5323 S_ISDIR(src_st.st_mode));
5324 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
5325 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5326 S_ISDIR(src_st.st_mode));
5331 /* Make sure that we do not loop forever */
5340 /* We deleted the destination, so must force the error to be EIO */
5341 if ((retval != 0) && (pre_delete != 0))
5349 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5350 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5351 * to expand file specification. Allows for a single default file
5352 * specification and a simple mask of options. If outbuf is non-NULL,
5353 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5354 * the resultant file specification is placed. If outbuf is NULL, the
5355 * resultant file specification is placed into a static buffer.
5356 * The third argument, if non-NULL, is taken to be a default file
5357 * specification string. The fourth argument is unused at present.
5358 * rmesexpand() returns the address of the resultant string if
5359 * successful, and NULL on error.
5361 * New functionality for previously unused opts value:
5362 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5363 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5364 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5365 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5367 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5371 (const char *filespec,
5373 const char *defspec,
5379 const char * in_spec;
5381 const char * def_spec;
5382 char * vmsfspec, *vmsdefspec;
5386 struct FAB myfab = cc$rms_fab;
5387 rms_setup_nam(mynam);
5389 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5392 /* temp hack until UTF8 is actually implemented */
5393 if (fs_utf8 != NULL)
5396 if (!filespec || !*filespec) {
5397 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5407 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5408 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5409 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5411 /* If this is a UNIX file spec, convert it to VMS */
5412 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5413 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5414 &e_len, &vs_spec, &vs_len);
5419 vmsfspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5420 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5421 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5422 if (ret_spec == NULL) {
5423 PerlMem_free(vmsfspec);
5426 in_spec = (const char *)vmsfspec;
5428 /* Unless we are forcing to VMS format, a UNIX input means
5429 * UNIX output, and that requires long names to be used
5431 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5432 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5433 opts |= PERL_RMSEXPAND_M_LONG;
5443 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5444 rms_bind_fab_nam(myfab, mynam);
5446 /* Process the default file specification if present */
5448 if (defspec && *defspec) {
5450 t_isunix = is_unix_filespec(defspec);
5452 vmsdefspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5453 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5454 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5456 if (ret_spec == NULL) {
5457 /* Clean up and bail */
5458 PerlMem_free(vmsdefspec);
5459 if (vmsfspec != NULL)
5460 PerlMem_free(vmsfspec);
5463 def_spec = (const char *)vmsdefspec;
5465 rms_set_dna(myfab, mynam,
5466 (char *)def_spec, strlen(def_spec)); /* cast ok */
5469 /* Now we need the expansion buffers */
5470 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
5471 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5472 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5473 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
5474 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5476 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5478 /* If a NAML block is used RMS always writes to the long and short
5479 * addresses unless you suppress the short name.
5481 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5482 outbufl = (char *)PerlMem_malloc(VMS_MAXRSS);
5483 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5485 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5487 #ifdef NAM$M_NO_SHORT_UPCASE
5488 if (decc_efs_case_preserve)
5489 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5492 /* We may not want to follow symbolic links */
5493 #ifdef NAML$M_OPEN_SPECIAL
5494 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5495 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5498 /* First attempt to parse as an existing file */
5499 retsts = sys$parse(&myfab,0,0);
5500 if (!(retsts & STS$K_SUCCESS)) {
5502 /* Could not find the file, try as syntax only if error is not fatal */
5503 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5504 if (retsts == RMS$_DNF ||
5505 retsts == RMS$_DIR ||
5506 retsts == RMS$_DEV ||
5507 retsts == RMS$_PRV) {
5508 retsts = sys$parse(&myfab,0,0);
5509 if (retsts & STS$K_SUCCESS) goto int_expanded;
5512 /* Still could not parse the file specification */
5513 /*----------------------------------------------*/
5514 sts = rms_free_search_context(&myfab); /* Free search context */
5515 if (vmsdefspec != NULL)
5516 PerlMem_free(vmsdefspec);
5517 if (vmsfspec != NULL)
5518 PerlMem_free(vmsfspec);
5519 if (outbufl != NULL)
5520 PerlMem_free(outbufl);
5524 set_vaxc_errno(retsts);
5525 if (retsts == RMS$_PRV) set_errno(EACCES);
5526 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5527 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5528 else set_errno(EVMSERR);
5531 retsts = sys$search(&myfab,0,0);
5532 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5533 sts = rms_free_search_context(&myfab); /* Free search context */
5534 if (vmsdefspec != NULL)
5535 PerlMem_free(vmsdefspec);
5536 if (vmsfspec != NULL)
5537 PerlMem_free(vmsfspec);
5538 if (outbufl != NULL)
5539 PerlMem_free(outbufl);
5543 set_vaxc_errno(retsts);
5544 if (retsts == RMS$_PRV) set_errno(EACCES);
5545 else set_errno(EVMSERR);
5549 /* If the input filespec contained any lowercase characters,
5550 * downcase the result for compatibility with Unix-minded code. */
5552 if (!decc_efs_case_preserve) {
5554 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5555 if (islower(*tbuf)) { haslower = 1; break; }
5558 /* Is a long or a short name expected */
5559 /*------------------------------------*/
5561 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5562 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5563 if (rms_nam_rsll(mynam)) {
5565 speclen = rms_nam_rsll(mynam);
5568 spec_buf = esal; /* Not esa */
5569 speclen = rms_nam_esll(mynam);
5574 if (rms_nam_rsl(mynam)) {
5576 speclen = rms_nam_rsl(mynam);
5579 spec_buf = esa; /* Not esal */
5580 speclen = rms_nam_esl(mynam);
5582 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5585 spec_buf[speclen] = '\0';
5587 /* Trim off null fields added by $PARSE
5588 * If type > 1 char, must have been specified in original or default spec
5589 * (not true for version; $SEARCH may have added version of existing file).
5591 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5592 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5593 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5594 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5597 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5598 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5600 if (trimver || trimtype) {
5601 if (defspec && *defspec) {
5602 char *defesal = NULL;
5603 char *defesa = NULL;
5604 defesa = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5605 if (defesa != NULL) {
5606 struct FAB deffab = cc$rms_fab;
5607 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5608 defesal = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5609 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5611 rms_setup_nam(defnam);
5613 rms_bind_fab_nam(deffab, defnam);
5617 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5619 /* RMS needs the esa/esal as a work area if wildcards are involved */
5620 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5622 rms_clear_nam_nop(defnam);
5623 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5624 #ifdef NAM$M_NO_SHORT_UPCASE
5625 if (decc_efs_case_preserve)
5626 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5628 #ifdef NAML$M_OPEN_SPECIAL
5629 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5630 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5632 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5634 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5637 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5640 if (defesal != NULL)
5641 PerlMem_free(defesal);
5642 PerlMem_free(defesa);
5644 _ckvmssts_noperl(SS$_INSFMEM);
5648 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5649 if (*(rms_nam_verl(mynam)) != '\"')
5650 speclen = rms_nam_verl(mynam) - spec_buf;
5653 if (*(rms_nam_ver(mynam)) != '\"')
5654 speclen = rms_nam_ver(mynam) - spec_buf;
5658 /* If we didn't already trim version, copy down */
5659 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5660 if (speclen > rms_nam_verl(mynam) - spec_buf)
5662 (rms_nam_typel(mynam),
5663 rms_nam_verl(mynam),
5664 speclen - (rms_nam_verl(mynam) - spec_buf));
5665 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5668 if (speclen > rms_nam_ver(mynam) - spec_buf)
5670 (rms_nam_type(mynam),
5672 speclen - (rms_nam_ver(mynam) - spec_buf));
5673 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5678 /* Done with these copies of the input files */
5679 /*-------------------------------------------*/
5680 if (vmsfspec != NULL)
5681 PerlMem_free(vmsfspec);
5682 if (vmsdefspec != NULL)
5683 PerlMem_free(vmsdefspec);
5685 /* If we just had a directory spec on input, $PARSE "helpfully"
5686 * adds an empty name and type for us */
5687 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5688 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5689 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5690 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5691 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5692 speclen = rms_nam_namel(mynam) - spec_buf;
5697 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5698 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5699 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5700 speclen = rms_nam_name(mynam) - spec_buf;
5703 /* Posix format specifications must have matching quotes */
5704 if (speclen < (VMS_MAXRSS - 1)) {
5705 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5706 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5707 spec_buf[speclen] = '\"';
5712 spec_buf[speclen] = '\0';
5713 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5715 /* Have we been working with an expanded, but not resultant, spec? */
5716 /* Also, convert back to Unix syntax if necessary. */
5720 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5721 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5722 rsl = rms_nam_rsll(mynam);
5726 rsl = rms_nam_rsl(mynam);
5729 /* rsl is not present, it means that spec_buf is either */
5730 /* esa or esal, and needs to be copied to outbuf */
5731 /* convert to Unix if desired */
5733 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5735 /* VMS file specs are not in UTF-8 */
5736 if (fs_utf8 != NULL)
5738 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5743 /* Now spec_buf is either outbuf or outbufl */
5744 /* We need the result into outbuf */
5746 /* If we need this in UNIX, then we need another buffer */
5747 /* to keep things in order */
5749 char * new_src = NULL;
5750 if (spec_buf == outbuf) {
5751 new_src = (char *)PerlMem_malloc(VMS_MAXRSS);
5752 my_strlcpy(new_src, spec_buf, VMS_MAXRSS);
5756 ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5758 PerlMem_free(new_src);
5761 /* VMS file specs are not in UTF-8 */
5762 if (fs_utf8 != NULL)
5765 /* Copy the buffer if needed */
5766 if (outbuf != spec_buf)
5767 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5773 /* Need to clean up the search context */
5774 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5775 sts = rms_free_search_context(&myfab); /* Free search context */
5777 /* Clean up the extra buffers */
5781 if (outbufl != NULL)
5782 PerlMem_free(outbufl);
5784 /* Return the result */
5788 /* Common simple case - Expand an already VMS spec */
5790 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5791 opts |= PERL_RMSEXPAND_M_VMS_IN;
5792 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5795 /* Common simple case - Expand to a VMS spec */
5797 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5798 opts |= PERL_RMSEXPAND_M_VMS;
5799 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5803 /* Entry point used by perl routines */
5806 (pTHX_ const char *filespec,
5809 const char *defspec,
5814 static char __rmsexpand_retbuf[VMS_MAXRSS];
5815 char * expanded, *ret_spec, *ret_buf;
5819 if (ret_buf == NULL) {
5821 Newx(expanded, VMS_MAXRSS, char);
5822 if (expanded == NULL)
5823 _ckvmssts(SS$_INSFMEM);
5826 ret_buf = __rmsexpand_retbuf;
5831 ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5832 opts, fs_utf8, dfs_utf8);
5834 if (ret_spec == NULL) {
5835 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5843 /* External entry points */
5844 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5845 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5846 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5847 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5848 char *Perl_rmsexpand_utf8
5849 (pTHX_ const char *spec, char *buf, const char *def,
5850 unsigned opt, int * fs_utf8, int * dfs_utf8)
5851 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5852 char *Perl_rmsexpand_utf8_ts
5853 (pTHX_ const char *spec, char *buf, const char *def,
5854 unsigned opt, int * fs_utf8, int * dfs_utf8)
5855 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
5859 ** The following routines are provided to make life easier when
5860 ** converting among VMS-style and Unix-style directory specifications.
5861 ** All will take input specifications in either VMS or Unix syntax. On
5862 ** failure, all return NULL. If successful, the routines listed below
5863 ** return a pointer to a buffer containing the appropriately
5864 ** reformatted spec (and, therefore, subsequent calls to that routine
5865 ** will clobber the result), while the routines of the same names with
5866 ** a _ts suffix appended will return a pointer to a mallocd string
5867 ** containing the appropriately reformatted spec.
5868 ** In all cases, only explicit syntax is altered; no check is made that
5869 ** the resulting string is valid or that the directory in question
5872 ** fileify_dirspec() - convert a directory spec into the name of the
5873 ** directory file (i.e. what you can stat() to see if it's a dir).
5874 ** The style (VMS or Unix) of the result is the same as the style
5875 ** of the parameter passed in.
5876 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5877 ** what you prepend to a filename to indicate what directory it's in).
5878 ** The style (VMS or Unix) of the result is the same as the style
5879 ** of the parameter passed in.
5880 ** tounixpath() - convert a directory spec into a Unix-style path.
5881 ** tovmspath() - convert a directory spec into a VMS-style path.
5882 ** tounixspec() - convert any file spec into a Unix-style file spec.
5883 ** tovmsspec() - convert any file spec into a VMS-style spec.
5884 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5886 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
5887 ** Permission is given to distribute this code as part of the Perl
5888 ** standard distribution under the terms of the GNU General Public
5889 ** License or the Perl Artistic License. Copies of each may be
5890 ** found in the Perl standard distribution.
5893 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5895 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
5897 unsigned long int dirlen, retlen, hasfilename = 0;
5898 char *cp1, *cp2, *lastdir;
5899 char *trndir, *vmsdir;
5900 unsigned short int trnlnm_iter_count;
5902 if (utf8_fl != NULL)
5905 if (!dir || !*dir) {
5906 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5908 dirlen = strlen(dir);
5909 while (dirlen && dir[dirlen-1] == '/') --dirlen;
5910 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
5911 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
5918 if (dirlen > (VMS_MAXRSS - 1)) {
5919 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
5922 trndir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5923 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5924 if (!strpbrk(dir+1,"/]>:") &&
5925 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
5926 strcpy(trndir,*dir == '/' ? dir + 1: dir);
5927 trnlnm_iter_count = 0;
5928 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
5929 trnlnm_iter_count++;
5930 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5932 dirlen = strlen(trndir);
5935 memcpy(trndir, dir, dirlen);
5936 trndir[dirlen] = '\0';
5939 /* At this point we are done with *dir and use *trndir which is a
5940 * copy that can be modified. *dir must not be modified.
5943 /* If we were handed a rooted logical name or spec, treat it like a
5944 * simple directory, so that
5945 * $ Define myroot dev:[dir.]
5946 * ... do_fileify_dirspec("myroot",buf,1) ...
5947 * does something useful.
5949 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
5950 trndir[--dirlen] = '\0';
5951 trndir[dirlen-1] = ']';
5953 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
5954 trndir[--dirlen] = '\0';
5955 trndir[dirlen-1] = '>';
5958 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
5959 /* If we've got an explicit filename, we can just shuffle the string. */
5960 if (*(cp1+1)) hasfilename = 1;
5961 /* Similarly, we can just back up a level if we've got multiple levels
5962 of explicit directories in a VMS spec which ends with directories. */
5964 for (cp2 = cp1; cp2 > trndir; cp2--) {
5966 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
5967 /* fix-me, can not scan EFS file specs backward like this */
5968 *cp2 = *cp1; *cp1 = '\0';
5973 if (*cp2 == '[' || *cp2 == '<') break;
5978 vmsdir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5979 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5980 cp1 = strpbrk(trndir,"]:>");
5981 if (cp1 && *(cp1+1) == ':') /* DECNet node spec with :: */
5982 cp1 = strpbrk(cp1+2,"]:>");
5984 if (hasfilename || !cp1) { /* filename present or not VMS */
5986 if (trndir[0] == '.') {
5987 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
5988 PerlMem_free(trndir);
5989 PerlMem_free(vmsdir);
5990 return int_fileify_dirspec("[]", buf, NULL);
5992 else if (trndir[1] == '.' &&
5993 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
5994 PerlMem_free(trndir);
5995 PerlMem_free(vmsdir);
5996 return int_fileify_dirspec("[-]", buf, NULL);
5999 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
6000 dirlen -= 1; /* to last element */
6001 lastdir = strrchr(trndir,'/');
6003 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
6004 /* If we have "/." or "/..", VMSify it and let the VMS code
6005 * below expand it, rather than repeating the code to handle
6006 * relative components of a filespec here */
6008 if (*(cp1+2) == '.') cp1++;
6009 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6011 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6012 PerlMem_free(trndir);
6013 PerlMem_free(vmsdir);
6016 if (strchr(vmsdir,'/') != NULL) {
6017 /* If int_tovmsspec() returned it, it must have VMS syntax
6018 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6019 * the time to check this here only so we avoid a recursion
6020 * loop; otherwise, gigo.
6022 PerlMem_free(trndir);
6023 PerlMem_free(vmsdir);
6024 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6027 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6028 PerlMem_free(trndir);
6029 PerlMem_free(vmsdir);
6032 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6033 PerlMem_free(trndir);
6034 PerlMem_free(vmsdir);
6038 } while ((cp1 = strstr(cp1,"/.")) != NULL);
6039 lastdir = strrchr(trndir,'/');
6041 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6043 /* Ditto for specs that end in an MFD -- let the VMS code
6044 * figure out whether it's a real device or a rooted logical. */
6046 /* This should not happen any more. Allowing the fake /000000
6047 * in a UNIX pathname causes all sorts of problems when trying
6048 * to run in UNIX emulation. So the VMS to UNIX conversions
6049 * now remove the fake /000000 directories.
6052 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6053 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6054 PerlMem_free(trndir);
6055 PerlMem_free(vmsdir);
6058 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6059 PerlMem_free(trndir);
6060 PerlMem_free(vmsdir);
6063 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6064 PerlMem_free(trndir);
6065 PerlMem_free(vmsdir);
6070 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6071 !(lastdir = cp1 = strrchr(trndir,']')) &&
6072 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6074 cp2 = strrchr(cp1,'.');
6076 int e_len, vs_len = 0;
6079 cp3 = strchr(cp2,';');
6080 e_len = strlen(cp2);
6082 vs_len = strlen(cp3);
6083 e_len = e_len - vs_len;
6085 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6087 if (!decc_efs_charset) {
6088 /* If this is not EFS, then not a directory */
6089 PerlMem_free(trndir);
6090 PerlMem_free(vmsdir);
6092 set_vaxc_errno(RMS$_DIR);
6096 /* Ok, here we have an issue, technically if a .dir shows */
6097 /* from inside a directory, then we should treat it as */
6098 /* xxx^.dir.dir. But we do not have that context at this */
6099 /* point unless this is totally restructured, so we remove */
6100 /* The .dir for now, and fix this better later */
6101 dirlen = cp2 - trndir;
6103 if (decc_efs_charset && !strchr(trndir,'/')) {
6104 /* Dots are allowed in dir names, so escape them if input not in Unix syntax. */
6105 char *cp4 = is_dir ? (cp2 - 1) : cp2;
6107 for (; cp4 > cp1; cp4--) {
6109 if ((cp4 - 1 > trndir) && (*(cp4 - 1) != '^')) {
6110 memmove(cp4 + 1, cp4, trndir + dirlen - cp4 + 1);
6121 retlen = dirlen + 6;
6122 memcpy(buf, trndir, dirlen);
6125 /* We've picked up everything up to the directory file name.
6126 Now just add the type and version, and we're set. */
6127 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
6131 if (!decc_filename_unix_no_version)
6133 PerlMem_free(trndir);
6134 PerlMem_free(vmsdir);
6137 else { /* VMS-style directory spec */
6139 char *esa, *esal, term, *cp;
6142 unsigned long int cmplen, haslower = 0;
6143 struct FAB dirfab = cc$rms_fab;
6144 rms_setup_nam(savnam);
6145 rms_setup_nam(dirnam);
6147 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
6148 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6150 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6151 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
6152 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6154 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6155 rms_bind_fab_nam(dirfab, dirnam);
6156 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6157 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6158 #ifdef NAM$M_NO_SHORT_UPCASE
6159 if (decc_efs_case_preserve)
6160 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6163 for (cp = trndir; *cp; cp++)
6164 if (islower(*cp)) { haslower = 1; break; }
6165 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6166 if ((dirfab.fab$l_sts == RMS$_DIR) ||
6167 (dirfab.fab$l_sts == RMS$_DNF) ||
6168 (dirfab.fab$l_sts == RMS$_PRV)) {
6169 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6170 sts = sys$parse(&dirfab);
6176 PerlMem_free(trndir);
6177 PerlMem_free(vmsdir);
6179 set_vaxc_errno(dirfab.fab$l_sts);
6185 /* Does the file really exist? */
6186 if (sys$search(&dirfab)& STS$K_SUCCESS) {
6187 /* Yes; fake the fnb bits so we'll check type below */
6188 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6190 else { /* No; just work with potential name */
6191 if (dirfab.fab$l_sts == RMS$_FNF
6192 || dirfab.fab$l_sts == RMS$_DNF
6193 || dirfab.fab$l_sts == RMS$_FND)
6197 fab_sts = dirfab.fab$l_sts;
6198 sts = rms_free_search_context(&dirfab);
6202 PerlMem_free(trndir);
6203 PerlMem_free(vmsdir);
6204 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
6210 /* Make sure we are using the right buffer */
6211 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6214 my_esa_len = rms_nam_esll(dirnam);
6218 my_esa_len = rms_nam_esl(dirnam);
6219 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6222 my_esa[my_esa_len] = '\0';
6223 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6224 cp1 = strchr(my_esa,']');
6225 if (!cp1) cp1 = strchr(my_esa,'>');
6226 if (cp1) { /* Should always be true */
6227 my_esa_len -= cp1 - my_esa - 1;
6228 memmove(my_esa, cp1 + 1, my_esa_len);
6231 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6232 /* Yep; check version while we're at it, if it's there. */
6233 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6234 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6235 /* Something other than .DIR[;1]. Bzzt. */
6236 sts = rms_free_search_context(&dirfab);
6240 PerlMem_free(trndir);
6241 PerlMem_free(vmsdir);
6243 set_vaxc_errno(RMS$_DIR);
6248 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6249 /* They provided at least the name; we added the type, if necessary, */
6250 my_strlcpy(buf, my_esa, VMS_MAXRSS);
6251 sts = rms_free_search_context(&dirfab);
6252 PerlMem_free(trndir);
6256 PerlMem_free(vmsdir);
6259 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6260 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6264 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6265 if (cp1 == NULL) { /* should never happen */
6266 sts = rms_free_search_context(&dirfab);
6267 PerlMem_free(trndir);
6271 PerlMem_free(vmsdir);
6276 retlen = strlen(my_esa);
6277 cp1 = strrchr(my_esa,'.');
6278 /* ODS-5 directory specifications can have extra "." in them. */
6279 /* Fix-me, can not scan EFS file specifications backwards */
6280 while (cp1 != NULL) {
6281 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6285 while ((cp1 > my_esa) && (*cp1 != '.'))
6292 if ((cp1) != NULL) {
6293 /* There's more than one directory in the path. Just roll back. */
6295 my_strlcpy(buf, my_esa, VMS_MAXRSS);
6298 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6299 /* Go back and expand rooted logical name */
6300 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6301 #ifdef NAM$M_NO_SHORT_UPCASE
6302 if (decc_efs_case_preserve)
6303 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6305 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6306 sts = rms_free_search_context(&dirfab);
6310 PerlMem_free(trndir);
6311 PerlMem_free(vmsdir);
6313 set_vaxc_errno(dirfab.fab$l_sts);
6317 /* This changes the length of the string of course */
6319 my_esa_len = rms_nam_esll(dirnam);
6321 my_esa_len = rms_nam_esl(dirnam);
6324 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6325 cp1 = strstr(my_esa,"][");
6326 if (!cp1) cp1 = strstr(my_esa,"]<");
6327 dirlen = cp1 - my_esa;
6328 memcpy(buf, my_esa, dirlen);
6329 if (!strncmp(cp1+2,"000000]",7)) {
6330 buf[dirlen-1] = '\0';
6331 /* fix-me Not full ODS-5, just extra dots in directories for now */
6332 cp1 = buf + dirlen - 1;
6338 if (*(cp1-1) != '^')
6343 if (*cp1 == '.') *cp1 = ']';
6345 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6346 memmove(cp1+1,"000000]",7);
6350 memmove(buf+dirlen, cp1+2, retlen-dirlen);
6352 /* Convert last '.' to ']' */
6354 while (*cp != '[') {
6357 /* Do not trip on extra dots in ODS-5 directories */
6358 if ((cp1 == buf) || (*(cp1-1) != '^'))
6362 if (*cp1 == '.') *cp1 = ']';
6364 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6365 memmove(cp1+1,"000000]",7);
6369 else { /* This is a top-level dir. Add the MFD to the path. */
6370 cp1 = strrchr(my_esa, ':');
6372 memmove(buf, my_esa, cp1 - my_esa + 1);
6373 memmove(buf + (cp1 - my_esa) + 1, "[000000]", 8);
6374 memmove(buf + (cp1 - my_esa) + 9, cp1 + 2, retlen - (cp1 - my_esa + 2));
6375 buf[retlen + 7] = '\0'; /* We've inserted '000000]' */
6378 sts = rms_free_search_context(&dirfab);
6379 /* We've set up the string up through the filename. Add the
6380 type and version, and we're done. */
6381 strcat(buf,".DIR;1");
6383 /* $PARSE may have upcased filespec, so convert output to lower
6384 * case if input contained any lowercase characters. */
6385 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
6386 PerlMem_free(trndir);
6390 PerlMem_free(vmsdir);
6393 } /* end of int_fileify_dirspec() */
6396 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6397 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6399 static char __fileify_retbuf[VMS_MAXRSS];
6400 char * fileified, *ret_spec, *ret_buf;
6404 if (ret_buf == NULL) {
6406 Newx(fileified, VMS_MAXRSS, char);
6407 if (fileified == NULL)
6408 _ckvmssts(SS$_INSFMEM);
6409 ret_buf = fileified;
6411 ret_buf = __fileify_retbuf;
6415 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6417 if (ret_spec == NULL) {
6418 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6420 Safefree(fileified);
6424 } /* end of do_fileify_dirspec() */
6427 /* External entry points */
6428 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6429 { return do_fileify_dirspec(dir,buf,0,NULL); }
6430 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6431 { return do_fileify_dirspec(dir,buf,1,NULL); }
6432 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6433 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6434 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6435 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6437 static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6438 char * v_spec, int v_len, char * r_spec, int r_len,
6439 char * d_spec, int d_len, char * n_spec, int n_len,
6440 char * e_spec, int e_len, char * vs_spec, int vs_len) {
6442 /* VMS specification - Try to do this the simple way */
6443 if ((v_len + r_len > 0) || (d_len > 0)) {
6446 /* No name or extension component, already a directory */
6447 if ((n_len + e_len + vs_len) == 0) {
6452 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6453 /* This results from catfile() being used instead of catdir() */
6454 /* So even though it should not work, we need to allow it */
6456 /* If this is .DIR;1 then do a simple conversion */
6457 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6458 if (is_dir || (e_len == 0) && (d_len > 0)) {
6460 len = v_len + r_len + d_len - 1;
6461 char dclose = d_spec[d_len - 1];
6462 memcpy(buf, dir, len);
6465 memcpy(&buf[len], n_spec, n_len);
6468 buf[len + 1] = '\0';
6473 else if (d_len > 0) {
6474 /* In the olden days, a directory needed to have a .DIR */
6475 /* extension to be a valid directory, but now it could */
6476 /* be a symbolic link */
6478 len = v_len + r_len + d_len - 1;
6479 char dclose = d_spec[d_len - 1];
6480 memcpy(buf, dir, len);
6483 memcpy(&buf[len], n_spec, n_len);
6486 if (decc_efs_charset) {
6488 && (toupper(e_spec[1]) == 'D')
6489 && (toupper(e_spec[2]) == 'I')
6490 && (toupper(e_spec[3]) == 'R')) {
6492 /* Corner case: directory spec with invalid version.
6493 * Valid would have followed is_dir path above.
6495 SETERRNO(ENOTDIR, RMS$_DIR);
6501 memcpy(&buf[len], e_spec, e_len);
6506 SETERRNO(ENOTDIR, RMS$_DIR);
6511 buf[len + 1] = '\0';
6516 set_vaxc_errno(RMS$_DIR);
6522 set_vaxc_errno(RMS$_DIR);
6528 /* Internal routine to make sure or convert a directory to be in a */
6529 /* path specification. No utf8 flag because it is not changed or used */
6530 static char *int_pathify_dirspec(const char *dir, char *buf)
6532 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6533 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6534 char * exp_spec, *ret_spec;
6536 unsigned short int trnlnm_iter_count;
6540 if (vms_debug_fileify) {
6542 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6544 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6547 /* We may need to lower case the result if we translated */
6548 /* a logical name or got the current working directory */
6551 if (!dir || !*dir) {
6553 set_vaxc_errno(SS$_BADPARAM);
6557 trndir = (char *)PerlMem_malloc(VMS_MAXRSS);
6559 _ckvmssts_noperl(SS$_INSFMEM);
6561 /* If no directory specified use the current default */
6563 my_strlcpy(trndir, dir, VMS_MAXRSS);
6565 getcwd(trndir, VMS_MAXRSS - 1);
6569 /* now deal with bare names that could be logical names */
6570 trnlnm_iter_count = 0;
6571 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6572 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6573 trnlnm_iter_count++;
6575 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6577 trnlen = strlen(trndir);
6579 /* Trap simple rooted lnms, and return lnm:[000000] */
6580 if (!strcmp(trndir+trnlen-2,".]")) {
6581 my_strlcpy(buf, dir, VMS_MAXRSS);
6582 strcat(buf, ":[000000]");
6583 PerlMem_free(trndir);
6585 if (vms_debug_fileify) {
6586 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6592 /* At this point we do not work with *dir, but the copy in *trndir */
6594 if (need_to_lower && !decc_efs_case_preserve) {
6595 /* Legacy mode, lower case the returned value */
6596 __mystrtolower(trndir);
6600 /* Some special cases, '..', '.' */
6602 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6603 /* Force UNIX filespec */
6607 /* Is this Unix or VMS format? */
6608 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6609 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6610 &e_len, &vs_spec, &vs_len);
6613 /* Just a filename? */
6614 if ((v_len + r_len + d_len) == 0) {
6616 /* Now we have a problem, this could be Unix or VMS */
6617 /* We have to guess. .DIR usually means VMS */
6619 /* In UNIX report mode, the .DIR extension is removed */
6620 /* if one shows up, it is for a non-directory or a directory */
6621 /* in EFS charset mode */
6623 /* So if we are in Unix report mode, assume that this */
6624 /* is a relative Unix directory specification */
6627 if (!decc_filename_unix_report && decc_efs_charset) {
6629 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6632 /* Traditional mode, assume .DIR is directory */
6635 memcpy(&buf[2], n_spec, n_len);
6636 buf[n_len + 2] = ']';
6637 buf[n_len + 3] = '\0';
6638 PerlMem_free(trndir);
6639 if (vms_debug_fileify) {
6641 "int_pathify_dirspec: buf = %s\n",
6651 ret_spec = int_pathify_dirspec_simple(trndir, buf,
6652 v_spec, v_len, r_spec, r_len,
6653 d_spec, d_len, n_spec, n_len,
6654 e_spec, e_len, vs_spec, vs_len);
6656 if (ret_spec != NULL) {
6657 PerlMem_free(trndir);
6658 if (vms_debug_fileify) {
6660 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6665 /* Simple way did not work, which means that a logical name */
6666 /* was present for the directory specification. */
6667 /* Need to use an rmsexpand variant to decode it completely */
6668 exp_spec = (char *)PerlMem_malloc(VMS_MAXRSS);
6669 if (exp_spec == NULL)
6670 _ckvmssts_noperl(SS$_INSFMEM);
6672 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6673 if (ret_spec != NULL) {
6674 sts = vms_split_path(exp_spec, &v_spec, &v_len,
6675 &r_spec, &r_len, &d_spec, &d_len,
6676 &n_spec, &n_len, &e_spec,
6677 &e_len, &vs_spec, &vs_len);
6679 ret_spec = int_pathify_dirspec_simple(
6680 exp_spec, buf, v_spec, v_len, r_spec, r_len,
6681 d_spec, d_len, n_spec, n_len,
6682 e_spec, e_len, vs_spec, vs_len);
6684 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6685 /* Legacy mode, lower case the returned value */
6686 __mystrtolower(ret_spec);
6689 set_vaxc_errno(RMS$_DIR);
6694 PerlMem_free(exp_spec);
6695 PerlMem_free(trndir);
6696 if (vms_debug_fileify) {
6697 if (ret_spec == NULL)
6698 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6701 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6706 /* Unix specification, Could be trivial conversion, */
6707 /* but have to deal with trailing '.dir' or extra '.' */
6712 STRLEN dir_len = strlen(trndir);
6714 lastslash = strrchr(trndir, '/');
6715 if (lastslash == NULL)
6722 /* '..' or '.' are valid directory components */
6724 if (lastslash[0] == '.') {
6725 if (lastslash[1] == '\0') {
6727 } else if (lastslash[1] == '.') {
6728 if (lastslash[2] == '\0') {
6731 /* And finally allow '...' */
6732 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6740 lastdot = strrchr(lastslash, '.');
6742 if (lastdot != NULL) {
6744 /* '.dir' is discarded, and any other '.' is invalid */
6745 e_len = strlen(lastdot);
6747 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6750 dir_len = dir_len - 4;
6754 my_strlcpy(buf, trndir, VMS_MAXRSS);
6755 if (buf[dir_len - 1] != '/') {
6757 buf[dir_len + 1] = '\0';
6760 /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6761 if (!decc_efs_charset) {
6764 if (str[0] == '.') {
6767 while ((dots[cnt] == '.') && (cnt < 3))
6770 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6776 for (; *str; ++str) {
6777 while (*str == '/') {
6783 /* Have to skip up to three dots which could be */
6784 /* directories, 3 dots being a VMS extension for Perl */
6787 while ((dots[cnt] == '.') && (cnt < 3)) {
6790 if (dots[cnt] == '\0')
6792 if ((cnt > 1) && (dots[cnt] != '/')) {
6798 /* too many dots? */
6799 if ((cnt == 0) || (cnt > 3)) {
6803 if (!dir_start && (*str == '.')) {
6808 PerlMem_free(trndir);
6810 if (vms_debug_fileify) {
6811 if (ret_spec == NULL)
6812 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6815 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6821 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6822 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6824 static char __pathify_retbuf[VMS_MAXRSS];
6825 char * pathified, *ret_spec, *ret_buf;
6829 if (ret_buf == NULL) {
6831 Newx(pathified, VMS_MAXRSS, char);
6832 if (pathified == NULL)
6833 _ckvmssts(SS$_INSFMEM);
6834 ret_buf = pathified;
6836 ret_buf = __pathify_retbuf;
6840 ret_spec = int_pathify_dirspec(dir, ret_buf);
6842 if (ret_spec == NULL) {
6843 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6845 Safefree(pathified);
6850 } /* end of do_pathify_dirspec() */
6853 /* External entry points */
6854 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6855 { return do_pathify_dirspec(dir,buf,0,NULL); }
6856 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
6857 { return do_pathify_dirspec(dir,buf,1,NULL); }
6858 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
6859 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
6860 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
6861 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
6863 /* Internal tounixspec routine that does not use a thread context */
6864 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
6865 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
6867 char *dirend, *cp1, *cp3, *tmp;
6870 unsigned short int trnlnm_iter_count;
6871 int cmp_rslt, outchars_added;
6872 if (utf8_fl != NULL)
6875 if (vms_debug_fileify) {
6877 fprintf(stderr, "int_tounixspec: spec = NULL\n");
6879 fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
6885 set_vaxc_errno(SS$_BADPARAM);
6888 if (strlen(spec) > (VMS_MAXRSS-1)) {
6890 set_vaxc_errno(SS$_BUFFEROVF);
6894 /* New VMS specific format needs translation
6895 * glob passes filenames with trailing '\n' and expects this preserved.
6897 if (decc_posix_compliant_pathnames) {
6898 if (strncmp(spec, "\"^UP^", 5) == 0) {
6904 tunix = (char *)PerlMem_malloc(VMS_MAXRSS);
6905 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6906 tunix_len = my_strlcpy(tunix, spec, VMS_MAXRSS);
6908 if (tunix[tunix_len - 1] == '\n') {
6909 tunix[tunix_len - 1] = '\"';
6910 tunix[tunix_len] = '\0';
6914 uspec = decc$translate_vms(tunix);
6915 PerlMem_free(tunix);
6916 if ((int)uspec > 0) {
6917 my_strlcpy(rslt, uspec, VMS_MAXRSS);
6922 /* If we can not translate it, makemaker wants as-is */
6923 my_strlcpy(rslt, spec, VMS_MAXRSS);
6930 cmp_rslt = 0; /* Presume VMS */
6931 cp1 = strchr(spec, '/');
6935 /* Look for EFS ^/ */
6936 if (decc_efs_charset) {
6937 while (cp1 != NULL) {
6940 /* Found illegal VMS, assume UNIX */
6945 cp1 = strchr(cp1, '/');
6949 /* Look for "." and ".." */
6950 if (decc_filename_unix_report) {
6951 if (spec[0] == '.') {
6952 if ((spec[1] == '\0') || (spec[1] == '\n')) {
6956 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
6966 /* This is already UNIX or at least nothing VMS understands,
6967 * so all we can reasonably do is unescape extended chars.
6971 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
6972 cp1 += outchars_added;
6975 if (vms_debug_fileify) {
6976 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6981 dirend = strrchr(spec,']');
6982 if (dirend == NULL) dirend = strrchr(spec,'>');
6983 if (dirend == NULL) dirend = strchr(spec,':');
6984 if (dirend == NULL) {
6986 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
6987 cp1 += outchars_added;
6990 if (vms_debug_fileify) {
6991 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6996 /* Special case 1 - sys$posix_root = / */
6997 if (!decc_disable_posix_root) {
6998 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7005 /* Special case 2 - Convert NLA0: to /dev/null */
7006 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7007 if (cmp_rslt == 0) {
7008 strcpy(rslt, "/dev/null");
7011 if (spec[6] != '\0') {
7018 /* Also handle special case "SYS$SCRATCH:" */
7019 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7020 tmp = (char *)PerlMem_malloc(VMS_MAXRSS);
7021 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7022 if (cmp_rslt == 0) {
7025 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7027 strcpy(rslt, "/tmp");
7030 if (spec[12] != '\0') {
7038 if (*cp2 != '[' && *cp2 != '<') {
7041 else { /* the VMS spec begins with directories */
7043 if (*cp2 == ']' || *cp2 == '>') {
7044 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
7048 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7049 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7051 if (vms_debug_fileify) {
7052 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7056 trnlnm_iter_count = 0;
7059 while (*cp3 != ':' && *cp3) cp3++;
7061 if (strchr(cp3,']') != NULL) break;
7062 trnlnm_iter_count++;
7063 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7064 } while (vmstrnenv(tmp,tmp,0,fildev,0));
7069 *(cp1++) = *(cp3++);
7070 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7072 set_errno(ENAMETOOLONG);
7073 set_vaxc_errno(SS$_BUFFEROVF);
7074 if (vms_debug_fileify) {
7075 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7077 return NULL; /* No room */
7082 if ((*cp2 == '^')) {
7083 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7084 cp1 += outchars_added;
7086 else if ( *cp2 == '.') {
7087 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7088 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7095 for (; cp2 <= dirend; cp2++) {
7096 if ((*cp2 == '^')) {
7097 /* EFS file escape, pass the next character as is */
7098 /* Fix me: HEX encoding for Unicode not implemented */
7099 *(cp1++) = *(++cp2);
7100 /* An escaped dot stays as is -- don't convert to slash */
7101 if (*cp2 == '.') cp2++;
7105 if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7107 else if (*cp2 == ']' || *cp2 == '>') {
7108 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7110 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7112 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7113 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7114 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7115 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7116 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7118 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7119 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7123 else if (*cp2 == '-') {
7124 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7125 while (*cp2 == '-') {
7127 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7129 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7130 /* filespecs like */
7131 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
7132 if (vms_debug_fileify) {
7133 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7138 else *(cp1++) = *cp2;
7140 else *(cp1++) = *cp2;
7142 /* Translate the rest of the filename. */
7146 /* Fixme - for compatibility with the CRTL we should be removing */
7147 /* spaces from the file specifications, but this may show that */
7148 /* some tests that were appearing to pass are not really passing */
7154 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7155 cp1 += outchars_added;
7158 if (decc_filename_unix_no_version) {
7159 /* Easy, drop the version */
7164 /* Punt - passing the version as a dot will probably */
7165 /* break perl in weird ways, but so did passing */
7166 /* through the ; as a version. Follow the CRTL and */
7167 /* hope for the best. */
7174 /* We will need to fix this properly later */
7175 /* As Perl may be installed on an ODS-5 volume, but not */
7176 /* have the EFS_CHARSET enabled, it still may encounter */
7177 /* filenames with extra dots in them, and a precedent got */
7178 /* set which allowed them to work, that we will uphold here */
7179 /* If extra dots are present in a name and no ^ is on them */
7180 /* VMS assumes that the first one is the extension delimiter */
7181 /* the rest have an implied ^. */
7183 /* this is also a conflict as the . is also a version */
7184 /* delimiter in VMS, */
7186 *(cp1++) = *(cp2++);
7190 /* This is an extension */
7191 if (decc_readdir_dropdotnotype) {
7193 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7194 /* Drop the dot for the extension */
7202 *(cp1++) = *(cp2++);
7207 /* This still leaves /000000/ when working with a
7208 * VMS device root or concealed root.
7214 ulen = strlen(rslt);
7216 /* Get rid of "000000/ in rooted filespecs */
7218 zeros = strstr(rslt, "/000000/");
7219 if (zeros != NULL) {
7221 mlen = ulen - (zeros - rslt) - 7;
7222 memmove(zeros, &zeros[7], mlen);
7229 if (vms_debug_fileify) {
7230 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7234 } /* end of int_tounixspec() */
7237 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7238 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7240 static char __tounixspec_retbuf[VMS_MAXRSS];
7241 char * unixspec, *ret_spec, *ret_buf;
7245 if (ret_buf == NULL) {
7247 Newx(unixspec, VMS_MAXRSS, char);
7248 if (unixspec == NULL)
7249 _ckvmssts(SS$_INSFMEM);
7252 ret_buf = __tounixspec_retbuf;
7256 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7258 if (ret_spec == NULL) {
7259 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7266 } /* end of do_tounixspec() */
7268 /* External entry points */
7269 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7270 { return do_tounixspec(spec,buf,0, NULL); }
7271 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7272 { return do_tounixspec(spec,buf,1, NULL); }
7273 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7274 { return do_tounixspec(spec,buf,0, utf8_fl); }
7275 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7276 { return do_tounixspec(spec,buf,1, utf8_fl); }
7278 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7281 This procedure is used to identify if a path is based in either
7282 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7283 it returns the OpenVMS format directory for it.
7285 It is expecting specifications of only '/' or '/xxxx/'
7287 If a posix root does not exist, or 'xxxx' is not a directory
7288 in the posix root, it returns a failure.
7290 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7292 It is used only internally by posix_to_vmsspec_hardway().
7295 static int posix_root_to_vms
7296 (char *vmspath, int vmspath_len,
7297 const char *unixpath,
7298 const int * utf8_fl)
7301 struct FAB myfab = cc$rms_fab;
7302 rms_setup_nam(mynam);
7303 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7304 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7305 char * esa, * esal, * rsa, * rsal;
7311 unixlen = strlen(unixpath);
7316 #if __CRTL_VER >= 80200000
7317 /* If not a posix spec already, convert it */
7318 if (decc_posix_compliant_pathnames) {
7319 if (strncmp(unixpath,"\"^UP^",5) != 0) {
7320 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7323 /* This is already a VMS specification, no conversion */
7325 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7334 /* Check to see if this is under the POSIX root */
7335 if (decc_disable_posix_root) {
7339 /* Skip leading / */
7340 if (unixpath[0] == '/') {
7346 strcpy(vmspath,"SYS$POSIX_ROOT:");
7348 /* If this is only the / , or blank, then... */
7349 if (unixpath[0] == '\0') {
7350 /* by definition, this is the answer */
7354 /* Need to look up a directory */
7358 /* Copy and add '^' escape characters as needed */
7361 while (unixpath[i] != 0) {
7364 j += copy_expand_unix_filename_escape
7365 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7369 path_len = strlen(vmspath);
7370 if (vmspath[path_len - 1] == '/')
7372 vmspath[path_len] = ']';
7374 vmspath[path_len] = '\0';
7377 vmspath[vmspath_len] = 0;
7378 if (unixpath[unixlen - 1] == '/')
7380 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
7381 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7382 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7383 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7384 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
7385 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7386 rsa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7387 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7388 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7389 rms_bind_fab_nam(myfab, mynam);
7390 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7391 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7392 if (decc_efs_case_preserve)
7393 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7394 #ifdef NAML$M_OPEN_SPECIAL
7395 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7398 /* Set up the remaining naml fields */
7399 sts = sys$parse(&myfab);
7401 /* It failed! Try again as a UNIX filespec */
7410 /* get the Device ID and the FID */
7411 sts = sys$search(&myfab);
7413 /* These are no longer needed */
7418 /* on any failure, returned the POSIX ^UP^ filespec */
7423 specdsc.dsc$a_pointer = vmspath;
7424 specdsc.dsc$w_length = vmspath_len;
7426 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7427 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7428 sts = lib$fid_to_name
7429 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7431 /* on any failure, returned the POSIX ^UP^ filespec */
7433 /* This can happen if user does not have permission to read directories */
7434 if (strncmp(unixpath,"\"^UP^",5) != 0)
7435 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7437 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7440 vmspath[specdsc.dsc$w_length] = 0;
7442 /* Are we expecting a directory? */
7443 if (dir_flag != 0) {
7449 i = specdsc.dsc$w_length - 1;
7453 /* Version must be '1' */
7454 if (vmspath[i--] != '1')
7456 /* Version delimiter is one of ".;" */
7457 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7460 if (vmspath[i--] != 'R')
7462 if (vmspath[i--] != 'I')
7464 if (vmspath[i--] != 'D')
7466 if (vmspath[i--] != '.')
7468 eptr = &vmspath[i+1];
7470 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7471 if (vmspath[i-1] != '^') {
7479 /* Get rid of 6 imaginary zero directory filename */
7480 vmspath[i+1] = '\0';
7484 if (vmspath[i] == '0')
7498 /* /dev/mumble needs to be handled special.
7499 /dev/null becomes NLA0:, And there is the potential for other stuff
7500 like /dev/tty which may need to be mapped to something.
7504 slash_dev_special_to_vms
7505 (const char * unixptr,
7514 nextslash = strchr(unixptr, '/');
7515 len = strlen(unixptr);
7516 if (nextslash != NULL)
7517 len = nextslash - unixptr;
7518 cmp = strncmp("null", unixptr, 5);
7520 if (vmspath_len >= 6) {
7521 strcpy(vmspath, "_NLA0:");
7529 /* The built in routines do not understand perl's special needs, so
7530 doing a manual conversion from UNIX to VMS
7532 If the utf8_fl is not null and points to a non-zero value, then
7533 treat 8 bit characters as UTF-8.
7535 The sequence starting with '$(' and ending with ')' will be passed
7536 through with out interpretation instead of being escaped.
7539 static int posix_to_vmsspec_hardway
7540 (char *vmspath, int vmspath_len,
7541 const char *unixpath,
7546 const char *unixptr;
7547 const char *unixend;
7549 const char *lastslash;
7550 const char *lastdot;
7556 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7557 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7559 if (utf8_fl != NULL)
7565 /* Ignore leading "/" characters */
7566 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7569 unixlen = strlen(unixptr);
7571 /* Do nothing with blank paths */
7578 /* This could have a "^UP^ on the front */
7579 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7585 lastslash = strrchr(unixptr,'/');
7586 lastdot = strrchr(unixptr,'.');
7587 unixend = strrchr(unixptr,'\"');
7588 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7589 unixend = unixptr + unixlen;
7592 /* last dot is last dot or past end of string */
7593 if (lastdot == NULL)
7594 lastdot = unixptr + unixlen;
7596 /* if no directories, set last slash to beginning of string */
7597 if (lastslash == NULL) {
7598 lastslash = unixptr;
7601 /* Watch out for trailing "." after last slash, still a directory */
7602 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7603 lastslash = unixptr + unixlen;
7606 /* Watch out for trailing ".." after last slash, still a directory */
7607 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7608 lastslash = unixptr + unixlen;
7611 /* dots in directories are aways escaped */
7612 if (lastdot < lastslash)
7613 lastdot = unixptr + unixlen;
7616 /* if (unixptr < lastslash) then we are in a directory */
7623 /* Start with the UNIX path */
7624 if (*unixptr != '/') {
7625 /* relative paths */
7627 /* If allowing logical names on relative pathnames, then handle here */
7628 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7629 !decc_posix_compliant_pathnames) {
7635 /* Find the next slash */
7636 nextslash = strchr(unixptr,'/');
7638 esa = (char *)PerlMem_malloc(vmspath_len);
7639 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7641 trn = (char *)PerlMem_malloc(VMS_MAXRSS);
7642 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7644 if (nextslash != NULL) {
7646 seg_len = nextslash - unixptr;
7647 memcpy(esa, unixptr, seg_len);
7651 seg_len = my_strlcpy(esa, unixptr, sizeof(esa));
7653 /* trnlnm(section) */
7654 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7657 /* Now fix up the directory */
7659 /* Split up the path to find the components */
7660 sts = vms_split_path
7678 /* A logical name must be a directory or the full
7679 specification. It is only a full specification if
7680 it is the only component */
7681 if ((unixptr[seg_len] == '\0') ||
7682 (unixptr[seg_len+1] == '\0')) {
7684 /* Is a directory being required? */
7685 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7686 /* Not a logical name */
7691 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7692 /* This must be a directory */
7693 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7694 vmslen = my_strlcpy(vmsptr, esa, vmspath_len - 1);
7695 vmsptr[vmslen] = ':';
7697 vmsptr[vmslen] = '\0';
7705 /* must be dev/directory - ignore version */
7706 if ((n_len + e_len) != 0)
7709 /* transfer the volume */
7710 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7711 memcpy(vmsptr, v_spec, v_len);
7717 /* unroot the rooted directory */
7718 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7720 r_spec[r_len - 1] = ']';
7722 /* This should not be there, but nothing is perfect */
7724 cmp = strcmp(&r_spec[1], "000000.");
7734 memcpy(vmsptr, r_spec, r_len);
7740 /* Bring over the directory. */
7742 ((d_len + vmslen) < vmspath_len)) {
7744 d_spec[d_len - 1] = ']';
7746 cmp = strcmp(&d_spec[1], "000000.");
7757 /* Remove the redundant root */
7765 memcpy(vmsptr, d_spec, d_len);
7779 if (lastslash > unixptr) {
7782 /* skip leading ./ */
7784 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7790 /* Are we still in a directory? */
7791 if (unixptr <= lastslash) {
7796 /* if not backing up, then it is relative forward. */
7797 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7798 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7806 /* Perl wants an empty directory here to tell the difference
7807 * between a DCL command and a filename
7816 /* Handle two special files . and .. */
7817 if (unixptr[0] == '.') {
7818 if (&unixptr[1] == unixend) {
7825 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7836 else { /* Absolute PATH handling */
7840 /* Need to find out where root is */
7842 /* In theory, this procedure should never get an absolute POSIX pathname
7843 * that can not be found on the POSIX root.
7844 * In practice, that can not be relied on, and things will show up
7845 * here that are a VMS device name or concealed logical name instead.
7846 * So to make things work, this procedure must be tolerant.
7848 esa = (char *)PerlMem_malloc(vmspath_len);
7849 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7852 nextslash = strchr(&unixptr[1],'/');
7854 if (nextslash != NULL) {
7856 seg_len = nextslash - &unixptr[1];
7857 my_strlcpy(vmspath, unixptr, seg_len + 2);
7860 cmp = strncmp(vmspath, "dev", 4);
7862 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
7863 if (sts == SS$_NORMAL)
7867 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
7870 if ($VMS_STATUS_SUCCESS(sts)) {
7871 /* This is verified to be a real path */
7873 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
7874 if ($VMS_STATUS_SUCCESS(sts)) {
7875 vmslen = my_strlcpy(vmspath, esa, vmspath_len + 1);
7876 vmsptr = vmspath + vmslen;
7878 if (unixptr < lastslash) {
7887 cmp = strcmp(rptr,"000000.");
7892 } /* removing 6 zeros */
7893 } /* vmslen < 7, no 6 zeros possible */
7894 } /* Not in a directory */
7895 } /* Posix root found */
7897 /* No posix root, fall back to default directory */
7898 strcpy(vmspath, "SYS$DISK:[");
7899 vmsptr = &vmspath[10];
7901 if (unixptr > lastslash) {
7910 } /* end of verified real path handling */
7915 /* Ok, we have a device or a concealed root that is not in POSIX
7916 * or we have garbage. Make the best of it.
7919 /* Posix to VMS destroyed this, so copy it again */
7920 my_strlcpy(vmspath, &unixptr[1], seg_len + 1);
7921 vmslen = strlen(vmspath); /* We know we're truncating. */
7922 vmsptr = &vmsptr[vmslen];
7925 /* Now do we need to add the fake 6 zero directory to it? */
7927 if ((*lastslash == '/') && (nextslash < lastslash)) {
7928 /* No there is another directory */
7935 /* now we have foo:bar or foo:[000000]bar to decide from */
7936 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
7938 if (!islnm && !decc_posix_compliant_pathnames) {
7940 cmp = strncmp("bin", vmspath, 4);
7942 /* bin => SYS$SYSTEM: */
7943 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
7946 /* tmp => SYS$SCRATCH: */
7947 cmp = strncmp("tmp", vmspath, 4);
7949 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
7954 trnend = islnm ? islnm - 1 : 0;
7956 /* if this was a logical name, ']' or '>' must be present */
7957 /* if not a logical name, then assume a device and hope. */
7958 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
7960 /* if log name and trailing '.' then rooted - treat as device */
7961 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
7963 /* Fix me, if not a logical name, a device lookup should be
7964 * done to see if the device is file structured. If the device
7965 * is not file structured, the 6 zeros should not be put on.
7967 * As it is, perl is occasionally looking for dev:[000000]tty.
7968 * which looks a little strange.
7970 * Not that easy to detect as "/dev" may be file structured with
7971 * special device files.
7974 if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
7975 (&nextslash[1] == unixend)) {
7976 /* No real directory present */
7981 /* Put the device delimiter on */
7984 unixptr = nextslash;
7987 /* Start directory if needed */
7988 if (!islnm || add_6zero) {
7994 /* add fake 000000] if needed */
8007 } /* non-POSIX translation */
8009 } /* End of relative/absolute path handling */
8011 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
8018 if (dir_start != 0) {
8020 /* First characters in a directory are handled special */
8021 while ((*unixptr == '/') ||
8022 ((*unixptr == '.') &&
8023 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8024 (&unixptr[1]==unixend)))) {
8029 /* Skip redundant / in specification */
8030 while ((*unixptr == '/') && (dir_start != 0)) {
8033 if (unixptr == lastslash)
8036 if (unixptr == lastslash)
8039 /* Skip redundant ./ characters */
8040 while ((*unixptr == '.') &&
8041 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8044 if (unixptr == lastslash)
8046 if (*unixptr == '/')
8049 if (unixptr == lastslash)
8052 /* Skip redundant ../ characters */
8053 while ((*unixptr == '.') && (unixptr[1] == '.') &&
8054 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8055 /* Set the backing up flag */
8061 unixptr++; /* first . */
8062 unixptr++; /* second . */
8063 if (unixptr == lastslash)
8065 if (*unixptr == '/') /* The slash */
8068 if (unixptr == lastslash)
8071 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8072 /* Not needed when VMS is pretending to be UNIX. */
8074 /* Is this loop stuck because of too many dots? */
8075 if (loop_flag == 0) {
8076 /* Exit the loop and pass the rest through */
8081 /* Are we done with directories yet? */
8082 if (unixptr >= lastslash) {
8084 /* Watch out for trailing dots */
8093 if (*unixptr == '/')
8097 /* Have we stopped backing up? */
8102 /* dir_start continues to be = 1 */
8104 if (*unixptr == '-') {
8106 *vmsptr++ = *unixptr++;
8110 /* Now are we done with directories yet? */
8111 if (unixptr >= lastslash) {
8113 /* Watch out for trailing dots */
8129 if (unixptr >= unixend)
8132 /* Normal characters - More EFS work probably needed */
8138 /* remove multiple / */
8139 while (unixptr[1] == '/') {
8142 if (unixptr == lastslash) {
8143 /* Watch out for trailing dots */
8155 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8156 /* Not needed when VMS is pretending to be UNIX. */
8160 if (unixptr != unixend)
8165 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8166 (&unixptr[1] == unixend)) {
8172 /* trailing dot ==> '^..' on VMS */
8173 if (unixptr == unixend) {
8181 *vmsptr++ = *unixptr++;
8185 if (quoted && (&unixptr[1] == unixend)) {
8189 in_cnt = copy_expand_unix_filename_escape
8190 (vmsptr, unixptr, &out_cnt, utf8_fl);
8200 in_cnt = copy_expand_unix_filename_escape
8201 (vmsptr, unixptr, &out_cnt, utf8_fl);
8208 /* Make sure directory is closed */
8209 if (unixptr == lastslash) {
8211 vmsptr2 = vmsptr - 1;
8213 if (*vmsptr2 != ']') {
8216 /* directories do not end in a dot bracket */
8217 if (*vmsptr2 == '.') {
8221 if (*vmsptr2 != '^') {
8222 vmsptr--; /* back up over the dot */
8230 /* Add a trailing dot if a file with no extension */
8231 vmsptr2 = vmsptr - 1;
8233 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8234 (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
8245 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8246 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8251 /* If a UTF8 flag is being passed, honor it */
8253 if (utf8_fl != NULL) {
8254 utf8_flag = *utf8_fl;
8259 /* If there is a possibility of UTF8, then if any UTF8 characters
8260 are present, then they must be converted to VTF-7
8262 result = strcpy(rslt, path); /* FIX-ME */
8265 result = strcpy(rslt, path);
8270 /* A convenience macro for copying dots in filenames and escaping
8271 * them when they haven't already been escaped, with guards to
8272 * avoid checking before the start of the buffer or advancing
8273 * beyond the end of it (allowing room for the NUL terminator).
8275 #define VMSEFS_DOT_WITH_ESCAPE(vmsefsdot,vmsefsbuf,vmsefsbufsiz) STMT_START { \
8276 if ( ((vmsefsdot) > (vmsefsbuf) && *((vmsefsdot) - 1) != '^' \
8277 || ((vmsefsdot) == (vmsefsbuf))) \
8278 && (vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 3 \
8280 *((vmsefsdot)++) = '^'; \
8282 if ((vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 2) \
8283 *((vmsefsdot)++) = '.'; \
8286 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8287 static char *int_tovmsspec
8288 (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8293 unsigned long int infront = 0, hasdir = 1;
8296 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8297 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8299 if (vms_debug_fileify) {
8301 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8303 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8307 /* If we fail, we should be setting errno */
8309 set_vaxc_errno(SS$_BADPARAM);
8312 rslt_len = VMS_MAXRSS-1;
8314 /* '.' and '..' are "[]" and "[-]" for a quick check */
8315 if (path[0] == '.') {
8316 if (path[1] == '\0') {
8318 if (utf8_flag != NULL)
8323 if (path[1] == '.' && path[2] == '\0') {
8325 if (utf8_flag != NULL)
8332 /* Posix specifications are now a native VMS format */
8333 /*--------------------------------------------------*/
8334 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8335 if (decc_posix_compliant_pathnames) {
8336 if (strncmp(path,"\"^UP^",5) == 0) {
8337 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8343 /* This is really the only way to see if this is already in VMS format */
8344 sts = vms_split_path
8359 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8360 replacement, because the above parse just took care of most of
8361 what is needed to do vmspath when the specification is already
8364 And if it is not already, it is easier to do the conversion as
8365 part of this routine than to call this routine and then work on
8369 /* If VMS punctuation was found, it is already VMS format */
8370 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8371 if (utf8_flag != NULL)
8373 my_strlcpy(rslt, path, VMS_MAXRSS);
8374 if (vms_debug_fileify) {
8375 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8379 /* Now, what to do with trailing "." cases where there is no
8380 extension? If this is a UNIX specification, and EFS characters
8381 are enabled, then the trailing "." should be converted to a "^.".
8382 But if this was already a VMS specification, then it should be
8385 So in the case of ambiguity, leave the specification alone.
8389 /* If there is a possibility of UTF8, then if any UTF8 characters
8390 are present, then they must be converted to VTF-7
8392 if (utf8_flag != NULL)
8394 my_strlcpy(rslt, path, VMS_MAXRSS);
8395 if (vms_debug_fileify) {
8396 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8401 dirend = strrchr(path,'/');
8403 if (dirend == NULL) {
8404 /* If we get here with no Unix directory delimiters, then this is an
8405 * ambiguous file specification, such as a Unix glob specification, a
8406 * shell or make macro, or a filespec that would be valid except for
8407 * unescaped extended characters. The safest thing if it's a macro
8408 * is to pass it through as-is.
8410 if (strstr(path, "$(")) {
8411 my_strlcpy(rslt, path, VMS_MAXRSS);
8412 if (vms_debug_fileify) {
8413 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8419 else if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
8420 if (!*(dirend+2)) dirend +=2;
8421 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8422 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8427 lastdot = strrchr(cp2,'.');
8433 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8435 if (decc_disable_posix_root) {
8436 strcpy(rslt,"sys$disk:[000000]");
8439 strcpy(rslt,"sys$posix_root:[000000]");
8441 if (utf8_flag != NULL)
8443 if (vms_debug_fileify) {
8444 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8448 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8450 trndev = (char *)PerlMem_malloc(VMS_MAXRSS);
8451 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8452 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8454 /* DECC special handling */
8456 if (strcmp(rslt,"bin") == 0) {
8457 strcpy(rslt,"sys$system");
8460 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8462 else if (strcmp(rslt,"tmp") == 0) {
8463 strcpy(rslt,"sys$scratch");
8466 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8468 else if (!decc_disable_posix_root) {
8469 strcpy(rslt, "sys$posix_root");
8473 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8474 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8476 else if (strcmp(rslt,"dev") == 0) {
8477 if (strncmp(cp2,"/null", 5) == 0) {
8478 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8479 strcpy(rslt,"NLA0");
8483 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8489 trnend = islnm ? strlen(trndev) - 1 : 0;
8490 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8491 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8492 /* If the first element of the path is a logical name, determine
8493 * whether it has to be translated so we can add more directories. */
8494 if (!islnm || rooted) {
8497 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8501 if (cp2 != dirend) {
8502 my_strlcpy(rslt, trndev, VMS_MAXRSS);
8503 cp1 = rslt + trnend;
8510 if (decc_disable_posix_root) {
8516 PerlMem_free(trndev);
8521 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8522 cp2 += 2; /* skip over "./" - it's redundant */
8523 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8525 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8526 *(cp1++) = '-'; /* "../" --> "-" */
8529 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8530 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8531 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8532 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8535 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8536 /* Escape the extra dots in EFS file specifications */
8539 if (cp2 > dirend) cp2 = dirend;
8541 else *(cp1++) = '.';
8543 for (; cp2 < dirend; cp2++) {
8545 if (*(cp2-1) == '/') continue;
8546 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.';
8549 else if (!infront && *cp2 == '.') {
8550 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8551 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
8552 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8553 if (cp1 > rslt && (*(cp1-1) == '-' || *(cp1-1) == '[')) *(cp1++) = '-'; /* handle "../" */
8554 else if (cp1 > rslt + 1 && *(cp1-2) == '[') *(cp1-1) = '-';
8559 if (cp2 == dirend) break;
8561 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8562 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8563 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8564 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8566 *(cp1++) = '.'; /* Simulate trailing '/' */
8567 cp2 += 2; /* for loop will incr this to == dirend */
8569 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8572 if (decc_efs_charset == 0) {
8573 if (cp1 > rslt && *(cp1-1) == '^')
8574 cp1--; /* remove the escape, if any */
8575 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8578 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8583 if (!infront && cp1 > rslt && *(cp1-1) == '-') *(cp1++) = '.';
8585 if (decc_efs_charset == 0) {
8586 if (cp1 > rslt && *(cp1-1) == '^')
8587 cp1--; /* remove the escape, if any */
8591 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8594 else *(cp1++) = *cp2;
8598 if (cp1 > rslt && *(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8599 if (hasdir) *(cp1++) = ']';
8600 if (*cp2 && *cp2 == '/') cp2++; /* check in case we ended with trailing '/' */
8607 if (decc_efs_charset == 0)
8613 if (cp2 >= path && (cp2 == path || *(cp2-1) != '^')) /* not previously escaped */
8619 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8620 decc_readdir_dropdotnotype) {
8621 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8624 /* trailing dot ==> '^..' on VMS */
8631 *(cp1++) = *(cp2++);
8636 /* This could be a macro to be passed through */
8637 *(cp1++) = *(cp2++);
8639 const char * save_cp2;
8643 /* paranoid check */
8649 *(cp1++) = *(cp2++);
8650 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8651 *(cp1++) = *(cp2++);
8652 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8653 *(cp1++) = *(cp2++);
8656 *(cp1++) = *(cp2++);
8660 if (is_macro == 0) {
8661 /* Not really a macro - never mind */
8674 /* Don't escape again if following character is
8675 * already something we escape.
8677 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8678 *(cp1++) = *(cp2++);
8681 /* But otherwise fall through and escape it. */
8698 if (cp2 > path && *(cp2-1) != '^') /* not previously escaped */
8700 *(cp1++) = *(cp2++);
8703 /* If it doesn't look like the beginning of a version number,
8704 * or we've been promised there are no version numbers, then
8707 if (decc_filename_unix_no_version) {
8711 size_t all_nums = strspn(cp2+1, "0123456789");
8712 if (all_nums > 5 || *(cp2 + all_nums + 1) != '\0')
8715 *(cp1++) = *(cp2++);
8718 *(cp1++) = *(cp2++);
8721 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8725 /* Fix me for "^]", but that requires making sure that you do
8726 * not back up past the start of the filename
8728 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8733 if (utf8_flag != NULL)
8735 if (vms_debug_fileify) {
8736 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8740 } /* end of int_tovmsspec() */
8743 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8744 static char *mp_do_tovmsspec
8745 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
8746 static char __tovmsspec_retbuf[VMS_MAXRSS];
8747 char * vmsspec, *ret_spec, *ret_buf;
8751 if (ret_buf == NULL) {
8753 Newx(vmsspec, VMS_MAXRSS, char);
8754 if (vmsspec == NULL)
8755 _ckvmssts(SS$_INSFMEM);
8758 ret_buf = __tovmsspec_retbuf;
8762 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8764 if (ret_spec == NULL) {
8765 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8772 } /* end of mp_do_tovmsspec() */
8774 /* External entry points */
8775 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8776 { return do_tovmsspec(path,buf,0,NULL); }
8777 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8778 { return do_tovmsspec(path,buf,1,NULL); }
8779 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8780 { return do_tovmsspec(path,buf,0,utf8_fl); }
8781 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8782 { return do_tovmsspec(path,buf,1,utf8_fl); }
8784 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
8785 /* Internal routine for use with out an explicit context present */
8786 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
8788 char * ret_spec, *pathified;
8793 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8794 if (pathified == NULL)
8795 _ckvmssts_noperl(SS$_INSFMEM);
8797 ret_spec = int_pathify_dirspec(path, pathified);
8799 if (ret_spec == NULL) {
8800 PerlMem_free(pathified);
8804 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
8806 PerlMem_free(pathified);
8811 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8812 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8813 static char __tovmspath_retbuf[VMS_MAXRSS];
8815 char *pathified, *vmsified, *cp;
8817 if (path == NULL) return NULL;
8818 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8819 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8820 if (int_pathify_dirspec(path, pathified) == NULL) {
8821 PerlMem_free(pathified);
8827 Newx(vmsified, VMS_MAXRSS, char);
8828 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8829 PerlMem_free(pathified);
8830 if (vmsified) Safefree(vmsified);
8833 PerlMem_free(pathified);
8838 vmslen = strlen(vmsified);
8839 Newx(cp,vmslen+1,char);
8840 memcpy(cp,vmsified,vmslen);
8846 my_strlcpy(__tovmspath_retbuf, vmsified, sizeof(__tovmspath_retbuf));
8848 return __tovmspath_retbuf;
8851 } /* end of do_tovmspath() */
8853 /* External entry points */
8854 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
8855 { return do_tovmspath(path,buf,0, NULL); }
8856 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
8857 { return do_tovmspath(path,buf,1, NULL); }
8858 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
8859 { return do_tovmspath(path,buf,0,utf8_fl); }
8860 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
8861 { return do_tovmspath(path,buf,1,utf8_fl); }
8864 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
8865 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8866 static char __tounixpath_retbuf[VMS_MAXRSS];
8868 char *pathified, *unixified, *cp;
8870 if (path == NULL) return NULL;
8871 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8872 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8873 if (int_pathify_dirspec(path, pathified) == NULL) {
8874 PerlMem_free(pathified);
8880 Newx(unixified, VMS_MAXRSS, char);
8882 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
8883 PerlMem_free(pathified);
8884 if (unixified) Safefree(unixified);
8887 PerlMem_free(pathified);
8892 unixlen = strlen(unixified);
8893 Newx(cp,unixlen+1,char);
8894 memcpy(cp,unixified,unixlen);
8896 Safefree(unixified);
8900 my_strlcpy(__tounixpath_retbuf, unixified, sizeof(__tounixpath_retbuf));
8901 Safefree(unixified);
8902 return __tounixpath_retbuf;
8905 } /* end of do_tounixpath() */
8907 /* External entry points */
8908 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
8909 { return do_tounixpath(path,buf,0,NULL); }
8910 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
8911 { return do_tounixpath(path,buf,1,NULL); }
8912 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8913 { return do_tounixpath(path,buf,0,utf8_fl); }
8914 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8915 { return do_tounixpath(path,buf,1,utf8_fl); }
8918 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
8920 *****************************************************************************
8922 * Copyright (C) 1989-1994, 2007 by *
8923 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
8925 * Permission is hereby granted for the reproduction of this software *
8926 * on condition that this copyright notice is included in source *
8927 * distributions of the software. The code may be modified and *
8928 * distributed under the same terms as Perl itself. *
8930 * 27-Aug-1994 Modified for inclusion in perl5 *
8931 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
8932 *****************************************************************************
8936 * getredirection() is intended to aid in porting C programs
8937 * to VMS (Vax-11 C). The native VMS environment does not support
8938 * '>' and '<' I/O redirection, or command line wild card expansion,
8939 * or a command line pipe mechanism using the '|' AND background
8940 * command execution '&'. All of these capabilities are provided to any
8941 * C program which calls this procedure as the first thing in the
8943 * The piping mechanism will probably work with almost any 'filter' type
8944 * of program. With suitable modification, it may useful for other
8945 * portability problems as well.
8947 * Author: Mark Pizzolato (mark AT infocomm DOT com)
8951 struct list_item *next;
8955 static void add_item(struct list_item **head,
8956 struct list_item **tail,
8960 static void mp_expand_wild_cards(pTHX_ char *item,
8961 struct list_item **head,
8962 struct list_item **tail,
8965 static int background_process(pTHX_ int argc, char **argv);
8967 static void pipe_and_fork(pTHX_ char **cmargv);
8969 /*{{{ void getredirection(int *ac, char ***av)*/
8971 mp_getredirection(pTHX_ int *ac, char ***av)
8973 * Process vms redirection arg's. Exit if any error is seen.
8974 * If getredirection() processes an argument, it is erased
8975 * from the vector. getredirection() returns a new argc and argv value.
8976 * In the event that a background command is requested (by a trailing "&"),
8977 * this routine creates a background subprocess, and simply exits the program.
8979 * Warning: do not try to simplify the code for vms. The code
8980 * presupposes that getredirection() is called before any data is
8981 * read from stdin or written to stdout.
8983 * Normal usage is as follows:
8989 * getredirection(&argc, &argv);
8993 int argc = *ac; /* Argument Count */
8994 char **argv = *av; /* Argument Vector */
8995 char *ap; /* Argument pointer */
8996 int j; /* argv[] index */
8997 int item_count = 0; /* Count of Items in List */
8998 struct list_item *list_head = 0; /* First Item in List */
8999 struct list_item *list_tail; /* Last Item in List */
9000 char *in = NULL; /* Input File Name */
9001 char *out = NULL; /* Output File Name */
9002 char *outmode = "w"; /* Mode to Open Output File */
9003 char *err = NULL; /* Error File Name */
9004 char *errmode = "w"; /* Mode to Open Error File */
9005 int cmargc = 0; /* Piped Command Arg Count */
9006 char **cmargv = NULL;/* Piped Command Arg Vector */
9009 * First handle the case where the last thing on the line ends with
9010 * a '&'. This indicates the desire for the command to be run in a
9011 * subprocess, so we satisfy that desire.
9014 if (0 == strcmp("&", ap))
9015 exit(background_process(aTHX_ --argc, argv));
9016 if (*ap && '&' == ap[strlen(ap)-1])
9018 ap[strlen(ap)-1] = '\0';
9019 exit(background_process(aTHX_ argc, argv));
9022 * Now we handle the general redirection cases that involve '>', '>>',
9023 * '<', and pipes '|'.
9025 for (j = 0; j < argc; ++j)
9027 if (0 == strcmp("<", argv[j]))
9031 fprintf(stderr,"No input file after < on command line");
9032 exit(LIB$_WRONUMARG);
9037 if ('<' == *(ap = argv[j]))
9042 if (0 == strcmp(">", ap))
9046 fprintf(stderr,"No output file after > on command line");
9047 exit(LIB$_WRONUMARG);
9066 fprintf(stderr,"No output file after > or >> on command line");
9067 exit(LIB$_WRONUMARG);
9071 if (('2' == *ap) && ('>' == ap[1]))
9088 fprintf(stderr,"No output file after 2> or 2>> on command line");
9089 exit(LIB$_WRONUMARG);
9093 if (0 == strcmp("|", argv[j]))
9097 fprintf(stderr,"No command into which to pipe on command line");
9098 exit(LIB$_WRONUMARG);
9100 cmargc = argc-(j+1);
9101 cmargv = &argv[j+1];
9105 if ('|' == *(ap = argv[j]))
9113 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9116 * Allocate and fill in the new argument vector, Some Unix's terminate
9117 * the list with an extra null pointer.
9119 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9120 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9122 for (j = 0; j < item_count; ++j, list_head = list_head->next)
9123 argv[j] = list_head->value;
9129 fprintf(stderr,"'|' and '>' may not both be specified on command line");
9130 exit(LIB$_INVARGORD);
9132 pipe_and_fork(aTHX_ cmargv);
9135 /* Check for input from a pipe (mailbox) */
9137 if (in == NULL && 1 == isapipe(0))
9139 char mbxname[L_tmpnam];
9141 long int dvi_item = DVI$_DEVBUFSIZ;
9142 $DESCRIPTOR(mbxnam, "");
9143 $DESCRIPTOR(mbxdevnam, "");
9145 /* Input from a pipe, reopen it in binary mode to disable */
9146 /* carriage control processing. */
9148 fgetname(stdin, mbxname, 1);
9149 mbxnam.dsc$a_pointer = mbxname;
9150 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9151 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9152 mbxdevnam.dsc$a_pointer = mbxname;
9153 mbxdevnam.dsc$w_length = sizeof(mbxname);
9154 dvi_item = DVI$_DEVNAM;
9155 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9156 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9159 freopen(mbxname, "rb", stdin);
9162 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9166 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9168 fprintf(stderr,"Can't open input file %s as stdin",in);
9171 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9173 fprintf(stderr,"Can't open output file %s as stdout",out);
9176 if (out != NULL) vmssetuserlnm("SYS$OUTPUT", out);
9179 if (strcmp(err,"&1") == 0) {
9180 dup2(fileno(stdout), fileno(stderr));
9181 vmssetuserlnm("SYS$ERROR", "SYS$OUTPUT");
9184 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9186 fprintf(stderr,"Can't open error file %s as stderr",err);
9190 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9194 vmssetuserlnm("SYS$ERROR", err);
9197 #ifdef ARGPROC_DEBUG
9198 PerlIO_printf(Perl_debug_log, "Arglist:\n");
9199 for (j = 0; j < *ac; ++j)
9200 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9202 /* Clear errors we may have hit expanding wildcards, so they don't
9203 show up in Perl's $! later */
9204 set_errno(0); set_vaxc_errno(1);
9205 } /* end of getredirection() */
9208 static void add_item(struct list_item **head,
9209 struct list_item **tail,
9215 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9216 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9220 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9221 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9222 *tail = (*tail)->next;
9224 (*tail)->value = value;
9228 static void mp_expand_wild_cards(pTHX_ char *item,
9229 struct list_item **head,
9230 struct list_item **tail,
9234 unsigned long int context = 0;
9242 $DESCRIPTOR(filespec, "");
9243 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9244 $DESCRIPTOR(resultspec, "");
9245 unsigned long int lff_flags = 0;
9249 #ifdef VMS_LONGNAME_SUPPORT
9250 lff_flags = LIB$M_FIL_LONG_NAMES;
9253 for (cp = item; *cp; cp++) {
9254 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9255 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9257 if (!*cp || isspace(*cp))
9259 add_item(head, tail, item, count);
9264 /* "double quoted" wild card expressions pass as is */
9265 /* From DCL that means using e.g.: */
9266 /* perl program """perl.*""" */
9267 item_len = strlen(item);
9268 if ( '"' == *item && '"' == item[item_len-1] )
9271 item[item_len-2] = '\0';
9272 add_item(head, tail, item, count);
9276 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9277 resultspec.dsc$b_class = DSC$K_CLASS_D;
9278 resultspec.dsc$a_pointer = NULL;
9279 vmsspec = (char *)PerlMem_malloc(VMS_MAXRSS);
9280 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9281 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9282 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9283 if (!isunix || !filespec.dsc$a_pointer)
9284 filespec.dsc$a_pointer = item;
9285 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9287 * Only return version specs, if the caller specified a version
9289 had_version = strchr(item, ';');
9291 * Only return device and directory specs, if the caller specified either.
9293 had_device = strchr(item, ':');
9294 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9296 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9297 (&filespec, &resultspec, &context,
9298 &defaultspec, 0, &rms_sts, &lff_flags)))
9303 string = (char *)PerlMem_malloc(resultspec.dsc$w_length+1);
9304 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9305 my_strlcpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length+1);
9306 if (NULL == had_version)
9307 *(strrchr(string, ';')) = '\0';
9308 if ((!had_directory) && (had_device == NULL))
9310 if (NULL == (devdir = strrchr(string, ']')))
9311 devdir = strrchr(string, '>');
9312 my_strlcpy(string, devdir + 1, resultspec.dsc$w_length+1);
9315 * Be consistent with what the C RTL has already done to the rest of
9316 * the argv items and lowercase all of these names.
9318 if (!decc_efs_case_preserve) {
9319 for (c = string; *c; ++c)
9323 if (isunix) trim_unixpath(string,item,1);
9324 add_item(head, tail, string, count);
9327 PerlMem_free(vmsspec);
9328 if (sts != RMS$_NMF)
9330 set_vaxc_errno(sts);
9333 case RMS$_FNF: case RMS$_DNF:
9334 set_errno(ENOENT); break;
9336 set_errno(ENOTDIR); break;
9338 set_errno(ENODEV); break;
9339 case RMS$_FNM: case RMS$_SYN:
9340 set_errno(EINVAL); break;
9342 set_errno(EACCES); break;
9344 _ckvmssts_noperl(sts);
9348 add_item(head, tail, item, count);
9349 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9350 _ckvmssts_noperl(lib$find_file_end(&context));
9353 static int child_st[2];/* Event Flag set when child process completes */
9355 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
9357 static unsigned long int exit_handler(void)
9361 if (0 == child_st[0])
9363 #ifdef ARGPROC_DEBUG
9364 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
9366 fflush(stdout); /* Have to flush pipe for binary data to */
9367 /* terminate properly -- <tp@mccall.com> */
9368 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9369 sys$dassgn(child_chan);
9371 sys$synch(0, child_st);
9376 static void sig_child(int chan)
9378 #ifdef ARGPROC_DEBUG
9379 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
9381 if (child_st[0] == 0)
9385 static struct exit_control_block exit_block =
9390 &exit_block.exit_status,
9395 pipe_and_fork(pTHX_ char **cmargv)
9398 struct dsc$descriptor_s *vmscmd;
9399 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9400 int sts, j, l, ismcr, quote, tquote = 0;
9402 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
9403 vms_execfree(vmscmd);
9408 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
9409 && toupper(*(q+2)) == 'R' && !*(q+3);
9411 while (q && l < MAX_DCL_LINE_LENGTH) {
9413 if (j > 0 && quote) {
9419 if (ismcr && j > 1) quote = 1;
9420 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9423 if (quote || tquote) {
9429 if ((quote||tquote) && *q == '"') {
9439 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9441 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9445 static int background_process(pTHX_ int argc, char **argv)
9447 char command[MAX_DCL_SYMBOL + 1] = "$";
9448 $DESCRIPTOR(value, "");
9449 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9450 static $DESCRIPTOR(null, "NLA0:");
9451 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9453 $DESCRIPTOR(pidstr, "");
9455 unsigned long int flags = 17, one = 1, retsts;
9458 len = my_strlcat(command, argv[0], sizeof(command));
9459 while (--argc && (len < MAX_DCL_SYMBOL))
9461 my_strlcat(command, " \"", sizeof(command));
9462 my_strlcat(command, *(++argv), sizeof(command));
9463 len = my_strlcat(command, "\"", sizeof(command));
9465 value.dsc$a_pointer = command;
9466 value.dsc$w_length = strlen(value.dsc$a_pointer);
9467 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9468 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9469 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9470 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9473 _ckvmssts_noperl(retsts);
9475 #ifdef ARGPROC_DEBUG
9476 PerlIO_printf(Perl_debug_log, "%s\n", command);
9478 sprintf(pidstring, "%08X", pid);
9479 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9480 pidstr.dsc$a_pointer = pidstring;
9481 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9482 lib$set_symbol(&pidsymbol, &pidstr);
9486 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9489 /* OS-specific initialization at image activation (not thread startup) */
9490 /* Older VAXC header files lack these constants */
9491 #ifndef JPI$_RIGHTS_SIZE
9492 # define JPI$_RIGHTS_SIZE 817
9494 #ifndef KGB$M_SUBSYSTEM
9495 # define KGB$M_SUBSYSTEM 0x8
9498 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9500 /*{{{void vms_image_init(int *, char ***)*/
9502 vms_image_init(int *argcp, char ***argvp)
9505 char eqv[LNM$C_NAMLENGTH+1] = "";
9506 unsigned int len, tabct = 8, tabidx = 0;
9507 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9508 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9509 unsigned short int dummy, rlen;
9510 struct dsc$descriptor_s **tabvec;
9511 #if defined(PERL_IMPLICIT_CONTEXT)
9514 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9515 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9516 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9519 #ifdef KILL_BY_SIGPRC
9520 Perl_csighandler_init();
9523 #if __CRTL_VER >= 70300000 && !defined(__VAX)
9524 /* This was moved from the pre-image init handler because on threaded */
9525 /* Perl it was always returning 0 for the default value. */
9526 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9529 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9532 initial = decc$feature_get_value(s, 4);
9534 /* initial is: 0 if nothing has set the feature */
9535 /* -1 if initialized to default */
9536 /* 1 if set by logical name */
9537 /* 2 if set by decc$feature_set_value */
9538 decc_disable_posix_root = decc$feature_get_value(s, 1);
9540 /* If the value is not valid, force the feature off */
9541 if (decc_disable_posix_root < 0) {
9542 decc$feature_set_value(s, 1, 1);
9543 decc_disable_posix_root = 1;
9547 /* Nothing has asked for it explicitly, so use our own default. */
9548 decc_disable_posix_root = 1;
9549 decc$feature_set_value(s, 1, 1);
9555 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9556 _ckvmssts_noperl(iosb[0]);
9557 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9558 if (iprv[i]) { /* Running image installed with privs? */
9559 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
9564 /* Rights identifiers might trigger tainting as well. */
9565 if (!will_taint && (rlen || rsz)) {
9566 while (rlen < rsz) {
9567 /* We didn't get all the identifiers on the first pass. Allocate a
9568 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9569 * were needed to hold all identifiers at time of last call; we'll
9570 * allocate that many unsigned long ints), and go back and get 'em.
9571 * If it gave us less than it wanted to despite ample buffer space,
9572 * something's broken. Is your system missing a system identifier?
9574 if (rsz <= jpilist[1].buflen) {
9575 /* Perl_croak accvios when used this early in startup. */
9576 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9577 rsz, (unsigned long) jpilist[1].buflen,
9578 "Check your rights database for corruption.\n");
9581 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9582 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9583 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9584 jpilist[1].buflen = rsz * sizeof(unsigned long int);
9585 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9586 _ckvmssts_noperl(iosb[0]);
9588 mask = (unsigned long int *)jpilist[1].bufadr;
9589 /* Check attribute flags for each identifier (2nd longword); protected
9590 * subsystem identifiers trigger tainting.
9592 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9593 if (mask[i] & KGB$M_SUBSYSTEM) {
9598 if (mask != rlst) PerlMem_free(mask);
9601 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9602 * logical, some versions of the CRTL will add a phanthom /000000/
9603 * directory. This needs to be removed.
9605 if (decc_filename_unix_report) {
9608 ulen = strlen(argvp[0][0]);
9610 zeros = strstr(argvp[0][0], "/000000/");
9611 if (zeros != NULL) {
9613 mlen = ulen - (zeros - argvp[0][0]) - 7;
9614 memmove(zeros, &zeros[7], mlen);
9616 argvp[0][0][ulen] = '\0';
9619 /* It also may have a trailing dot that needs to be removed otherwise
9620 * it will be converted to VMS mode incorrectly.
9623 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9624 argvp[0][0][ulen] = '\0';
9627 /* We need to use this hack to tell Perl it should run with tainting,
9628 * since its tainting flag may be part of the PL_curinterp struct, which
9629 * hasn't been allocated when vms_image_init() is called.
9632 char **newargv, **oldargv;
9634 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9635 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9636 newargv[0] = oldargv[0];
9637 newargv[1] = (char *)PerlMem_malloc(3 * sizeof(char));
9638 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9639 strcpy(newargv[1], "-T");
9640 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9642 newargv[*argcp] = NULL;
9643 /* We orphan the old argv, since we don't know where it's come from,
9644 * so we don't know how to free it.
9648 else { /* Did user explicitly request tainting? */
9650 char *cp, **av = *argvp;
9651 for (i = 1; i < *argcp; i++) {
9652 if (*av[i] != '-') break;
9653 for (cp = av[i]+1; *cp; cp++) {
9654 if (*cp == 'T') { will_taint = 1; break; }
9655 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9656 strchr("DFIiMmx",*cp)) break;
9658 if (will_taint) break;
9663 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9666 tabvec = (struct dsc$descriptor_s **)
9667 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9668 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9670 else if (tabidx >= tabct) {
9672 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9673 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9675 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9676 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9677 tabvec[tabidx]->dsc$w_length = len;
9678 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9679 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_S;
9680 tabvec[tabidx]->dsc$a_pointer = (char *)PerlMem_malloc(len + 1);
9681 if (tabvec[tabidx]->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9682 my_strlcpy(tabvec[tabidx]->dsc$a_pointer, eqv, len + 1);
9684 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9686 getredirection(argcp,argvp);
9687 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9689 # include <reentrancy.h>
9690 decc$set_reentrancy(C$C_MULTITHREAD);
9699 * Trim Unix-style prefix off filespec, so it looks like what a shell
9700 * glob expansion would return (i.e. from specified prefix on, not
9701 * full path). Note that returned filespec is Unix-style, regardless
9702 * of whether input filespec was VMS-style or Unix-style.
9704 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9705 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9706 * vector of options; at present, only bit 0 is used, and if set tells
9707 * trim unixpath to try the current default directory as a prefix when
9708 * presented with a possibly ambiguous ... wildcard.
9710 * Returns !=0 on success, with trimmed filespec replacing contents of
9711 * fspec, and 0 on failure, with contents of fpsec unchanged.
9713 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9715 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9717 char *unixified, *unixwild, *tplate, *base, *end, *cp1, *cp2;
9718 int tmplen, reslen = 0, dirs = 0;
9720 if (!wildspec || !fspec) return 0;
9722 unixwild = (char *)PerlMem_malloc(VMS_MAXRSS);
9723 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9725 if (strpbrk(wildspec,"]>:") != NULL) {
9726 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
9727 PerlMem_free(unixwild);
9732 my_strlcpy(unixwild, wildspec, VMS_MAXRSS);
9734 unixified = (char *)PerlMem_malloc(VMS_MAXRSS);
9735 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9736 if (strpbrk(fspec,"]>:") != NULL) {
9737 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
9738 PerlMem_free(unixwild);
9739 PerlMem_free(unixified);
9742 else base = unixified;
9743 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9744 * check to see that final result fits into (isn't longer than) fspec */
9745 reslen = strlen(fspec);
9749 /* No prefix or absolute path on wildcard, so nothing to remove */
9750 if (!*tplate || *tplate == '/') {
9751 PerlMem_free(unixwild);
9752 if (base == fspec) {
9753 PerlMem_free(unixified);
9756 tmplen = strlen(unixified);
9757 if (tmplen > reslen) {
9758 PerlMem_free(unixified);
9759 return 0; /* not enough space */
9761 /* Copy unixified resultant, including trailing NUL */
9762 memmove(fspec,unixified,tmplen+1);
9763 PerlMem_free(unixified);
9767 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9768 if ((cp1 = strstr(tplate,".../")) == NULL) { /* No ...; just count elts */
9769 for (cp1 = tplate; *cp1; cp1++) if (*cp1 == '/') dirs++;
9770 for (cp1 = end ;cp1 >= base; cp1--)
9771 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9773 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9774 PerlMem_free(unixified);
9775 PerlMem_free(unixwild);
9780 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9781 int ells = 1, totells, segdirs, match;
9782 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9783 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9785 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9787 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9788 tpl = (char *)PerlMem_malloc(VMS_MAXRSS);
9789 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9790 if (ellipsis == tplate && opts & 1) {
9791 /* Template begins with an ellipsis. Since we can't tell how many
9792 * directory names at the front of the resultant to keep for an
9793 * arbitrary starting point, we arbitrarily choose the current
9794 * default directory as a starting point. If it's there as a prefix,
9795 * clip it off. If not, fall through and act as if the leading
9796 * ellipsis weren't there (i.e. return shortest possible path that
9797 * could match template).
9799 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9801 PerlMem_free(unixified);
9802 PerlMem_free(unixwild);
9805 if (!decc_efs_case_preserve) {
9806 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9807 if (_tolower(*cp1) != _tolower(*cp2)) break;
9809 segdirs = dirs - totells; /* Min # of dirs we must have left */
9810 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9811 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9812 memmove(fspec,cp2+1,end - cp2);
9814 PerlMem_free(unixified);
9815 PerlMem_free(unixwild);
9819 /* First off, back up over constant elements at end of path */
9821 for (front = end ; front >= base; front--)
9822 if (*front == '/' && !dirs--) { front++; break; }
9824 lcres = (char *)PerlMem_malloc(VMS_MAXRSS);
9825 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9826 for (cp1=tplate,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9828 if (!decc_efs_case_preserve) {
9829 *cp2 = _tolower(*cp1); /* Make lc copy for match */
9837 PerlMem_free(unixified);
9838 PerlMem_free(unixwild);
9839 PerlMem_free(lcres);
9840 return 0; /* Path too long. */
9843 *cp2 = '\0'; /* Pick up with memcpy later */
9844 lcfront = lcres + (front - base);
9845 /* Now skip over each ellipsis and try to match the path in front of it. */
9847 for (cp1 = ellipsis - 2; cp1 >= tplate; cp1--)
9848 if (*(cp1) == '.' && *(cp1+1) == '.' &&
9849 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
9850 if (cp1 < tplate) break; /* template started with an ellipsis */
9851 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9852 ellipsis = cp1; continue;
9854 wilddsc.dsc$a_pointer = tpl;
9855 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9857 for (segdirs = 0, cp2 = tpl;
9858 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
9860 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
9862 if (!decc_efs_case_preserve) {
9863 *cp2 = _tolower(*cp1); /* else lowercase for match */
9866 *cp2 = *cp1; /* else preserve case for match */
9869 if (*cp2 == '/') segdirs++;
9871 if (cp1 != ellipsis - 1) {
9873 PerlMem_free(unixified);
9874 PerlMem_free(unixwild);
9875 PerlMem_free(lcres);
9876 return 0; /* Path too long */
9878 /* Back up at least as many dirs as in template before matching */
9879 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
9880 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
9881 for (match = 0; cp1 > lcres;) {
9882 resdsc.dsc$a_pointer = cp1;
9883 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
9885 if (match == 1) lcfront = cp1;
9887 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
9891 PerlMem_free(unixified);
9892 PerlMem_free(unixwild);
9893 PerlMem_free(lcres);
9894 return 0; /* Can't find prefix ??? */
9896 if (match > 1 && opts & 1) {
9897 /* This ... wildcard could cover more than one set of dirs (i.e.
9898 * a set of similar dir names is repeated). If the template
9899 * contains more than 1 ..., upstream elements could resolve the
9900 * ambiguity, but it's not worth a full backtracking setup here.
9901 * As a quick heuristic, clip off the current default directory
9902 * if it's present to find the trimmed spec, else use the
9903 * shortest string that this ... could cover.
9905 char def[NAM$C_MAXRSS+1], *st;
9907 if (getcwd(def, sizeof def,0) == NULL) {
9908 PerlMem_free(unixified);
9909 PerlMem_free(unixwild);
9910 PerlMem_free(lcres);
9914 if (!decc_efs_case_preserve) {
9915 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9916 if (_tolower(*cp1) != _tolower(*cp2)) break;
9918 segdirs = dirs - totells; /* Min # of dirs we must have left */
9919 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
9920 if (*cp1 == '\0' && *cp2 == '/') {
9921 memmove(fspec,cp2+1,end - cp2);
9923 PerlMem_free(unixified);
9924 PerlMem_free(unixwild);
9925 PerlMem_free(lcres);
9928 /* Nope -- stick with lcfront from above and keep going. */
9931 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
9933 PerlMem_free(unixified);
9934 PerlMem_free(unixwild);
9935 PerlMem_free(lcres);
9939 } /* end of trim_unixpath() */
9944 * VMS readdir() routines.
9945 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
9947 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
9948 * Minor modifications to original routines.
9951 /* readdir may have been redefined by reentr.h, so make sure we get
9952 * the local version for what we do here.
9957 #if !defined(PERL_IMPLICIT_CONTEXT)
9958 # define readdir Perl_readdir
9960 # define readdir(a) Perl_readdir(aTHX_ a)
9963 /* Number of elements in vms_versions array */
9964 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
9967 * Open a directory, return a handle for later use.
9969 /*{{{ DIR *opendir(char*name) */
9971 Perl_opendir(pTHX_ const char *name)
9977 Newx(dir, VMS_MAXRSS, char);
9978 if (int_tovmspath(name, dir, NULL) == NULL) {
9982 /* Check access before stat; otherwise stat does not
9983 * accurately report whether it's a directory.
9985 if (!strstr(dir, "::") /* sys$check_access doesn't do remotes */
9986 && !cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
9987 /* cando_by_name has already set errno */
9991 if (flex_stat(dir,&sb) == -1) return NULL;
9992 if (!S_ISDIR(sb.st_mode)) {
9994 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
9997 /* Get memory for the handle, and the pattern. */
9999 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
10001 /* Fill in the fields; mainly playing with the descriptor. */
10002 sprintf(dd->pattern, "%s*.*",dir);
10007 /* By saying we want the result of readdir() in unix format, we are really
10008 * saying we want all the escapes removed, translating characters that
10009 * must be escaped in a VMS-format name to their unescaped form, which is
10010 * presumably allowed in a Unix-format name.
10012 dd->flags = decc_filename_unix_report ? PERL_VMSDIR_M_UNIXSPECS : 0;
10013 dd->pat.dsc$a_pointer = dd->pattern;
10014 dd->pat.dsc$w_length = strlen(dd->pattern);
10015 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10016 dd->pat.dsc$b_class = DSC$K_CLASS_S;
10017 #if defined(USE_ITHREADS)
10018 Newx(dd->mutex,1,perl_mutex);
10019 MUTEX_INIT( (perl_mutex *) dd->mutex );
10025 } /* end of opendir() */
10029 * Set the flag to indicate we want versions or not.
10031 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10033 vmsreaddirversions(DIR *dd, int flag)
10036 dd->flags |= PERL_VMSDIR_M_VERSIONS;
10038 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10043 * Free up an opened directory.
10045 /*{{{ void closedir(DIR *dd)*/
10047 Perl_closedir(DIR *dd)
10051 sts = lib$find_file_end(&dd->context);
10052 Safefree(dd->pattern);
10053 #if defined(USE_ITHREADS)
10054 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10055 Safefree(dd->mutex);
10062 * Collect all the version numbers for the current file.
10065 collectversions(pTHX_ DIR *dd)
10067 struct dsc$descriptor_s pat;
10068 struct dsc$descriptor_s res;
10070 char *p, *text, *buff;
10072 unsigned long context, tmpsts;
10074 /* Convenient shorthand. */
10077 /* Add the version wildcard, ignoring the "*.*" put on before */
10078 i = strlen(dd->pattern);
10079 Newx(text,i + e->d_namlen + 3,char);
10080 my_strlcpy(text, dd->pattern, i + 1);
10081 sprintf(&text[i - 3], "%s;*", e->d_name);
10083 /* Set up the pattern descriptor. */
10084 pat.dsc$a_pointer = text;
10085 pat.dsc$w_length = i + e->d_namlen - 1;
10086 pat.dsc$b_dtype = DSC$K_DTYPE_T;
10087 pat.dsc$b_class = DSC$K_CLASS_S;
10089 /* Set up result descriptor. */
10090 Newx(buff, VMS_MAXRSS, char);
10091 res.dsc$a_pointer = buff;
10092 res.dsc$w_length = VMS_MAXRSS - 1;
10093 res.dsc$b_dtype = DSC$K_DTYPE_T;
10094 res.dsc$b_class = DSC$K_CLASS_S;
10096 /* Read files, collecting versions. */
10097 for (context = 0, e->vms_verscount = 0;
10098 e->vms_verscount < VERSIZE(e);
10099 e->vms_verscount++) {
10100 unsigned long rsts;
10101 unsigned long flags = 0;
10103 #ifdef VMS_LONGNAME_SUPPORT
10104 flags = LIB$M_FIL_LONG_NAMES;
10106 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10107 if (tmpsts == RMS$_NMF || context == 0) break;
10109 buff[VMS_MAXRSS - 1] = '\0';
10110 if ((p = strchr(buff, ';')))
10111 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10113 e->vms_versions[e->vms_verscount] = -1;
10116 _ckvmssts(lib$find_file_end(&context));
10120 } /* end of collectversions() */
10123 * Read the next entry from the directory.
10125 /*{{{ struct dirent *readdir(DIR *dd)*/
10127 Perl_readdir(pTHX_ DIR *dd)
10129 struct dsc$descriptor_s res;
10131 unsigned long int tmpsts;
10132 unsigned long rsts;
10133 unsigned long flags = 0;
10134 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10135 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10137 /* Set up result descriptor, and get next file. */
10138 Newx(buff, VMS_MAXRSS, char);
10139 res.dsc$a_pointer = buff;
10140 res.dsc$w_length = VMS_MAXRSS - 1;
10141 res.dsc$b_dtype = DSC$K_DTYPE_T;
10142 res.dsc$b_class = DSC$K_CLASS_S;
10144 #ifdef VMS_LONGNAME_SUPPORT
10145 flags = LIB$M_FIL_LONG_NAMES;
10148 tmpsts = lib$find_file
10149 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10150 if (dd->context == 0)
10151 tmpsts = RMS$_NMF; /* None left. (should be set, but make sure) */
10153 if (!(tmpsts & 1)) {
10156 break; /* no more files considered success */
10158 SETERRNO(EACCES, tmpsts); break;
10160 SETERRNO(ENODEV, tmpsts); break;
10162 SETERRNO(ENOTDIR, tmpsts); break;
10163 case RMS$_FNF: case RMS$_DNF:
10164 SETERRNO(ENOENT, tmpsts); break;
10166 SETERRNO(EVMSERR, tmpsts);
10172 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10173 buff[res.dsc$w_length] = '\0';
10174 p = buff + res.dsc$w_length;
10175 while (--p >= buff) if (!isspace(*p)) break;
10177 if (!decc_efs_case_preserve) {
10178 for (p = buff; *p; p++) *p = _tolower(*p);
10181 /* Skip any directory component and just copy the name. */
10182 sts = vms_split_path
10197 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10199 /* In Unix report mode, remove the ".dir;1" from the name */
10200 /* if it is a real directory. */
10201 if (decc_filename_unix_report && decc_efs_charset) {
10202 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10206 ret_sts = flex_lstat(buff, &statbuf);
10207 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10214 /* Drop NULL extensions on UNIX file specification */
10215 if ((e_len == 1) && decc_readdir_dropdotnotype) {
10221 memcpy(dd->entry.d_name, n_spec, n_len + e_len);
10222 dd->entry.d_name[n_len + e_len] = '\0';
10223 dd->entry.d_namlen = n_len + e_len;
10225 /* Convert the filename to UNIX format if needed */
10226 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10228 /* Translate the encoded characters. */
10229 /* Fixme: Unicode handling could result in embedded 0 characters */
10230 if (strchr(dd->entry.d_name, '^') != NULL) {
10231 char new_name[256];
10233 p = dd->entry.d_name;
10236 int inchars_read, outchars_added;
10237 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10239 q += outchars_added;
10241 /* if outchars_added > 1, then this is a wide file specification */
10242 /* Wide file specifications need to be passed in Perl */
10243 /* counted strings apparently with a Unicode flag */
10246 dd->entry.d_namlen = my_strlcpy(dd->entry.d_name, new_name, sizeof(dd->entry.d_name));
10250 dd->entry.vms_verscount = 0;
10251 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10255 } /* end of readdir() */
10259 * Read the next entry from the directory -- thread-safe version.
10261 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10263 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10267 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10269 entry = readdir(dd);
10271 retval = ( *result == NULL ? errno : 0 );
10273 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10277 } /* end of readdir_r() */
10281 * Return something that can be used in a seekdir later.
10283 /*{{{ long telldir(DIR *dd)*/
10285 Perl_telldir(DIR *dd)
10292 * Return to a spot where we used to be. Brute force.
10294 /*{{{ void seekdir(DIR *dd,long count)*/
10296 Perl_seekdir(pTHX_ DIR *dd, long count)
10300 /* If we haven't done anything yet... */
10301 if (dd->count == 0)
10304 /* Remember some state, and clear it. */
10305 old_flags = dd->flags;
10306 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10307 _ckvmssts(lib$find_file_end(&dd->context));
10310 /* The increment is in readdir(). */
10311 for (dd->count = 0; dd->count < count; )
10314 dd->flags = old_flags;
10316 } /* end of seekdir() */
10319 /* VMS subprocess management
10321 * my_vfork() - just a vfork(), after setting a flag to record that
10322 * the current script is trying a Unix-style fork/exec.
10324 * vms_do_aexec() and vms_do_exec() are called in response to the
10325 * perl 'exec' function. If this follows a vfork call, then they
10326 * call out the regular perl routines in doio.c which do an
10327 * execvp (for those who really want to try this under VMS).
10328 * Otherwise, they do exactly what the perl docs say exec should
10329 * do - terminate the current script and invoke a new command
10330 * (See below for notes on command syntax.)
10332 * do_aspawn() and do_spawn() implement the VMS side of the perl
10333 * 'system' function.
10335 * Note on command arguments to perl 'exec' and 'system': When handled
10336 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10337 * are concatenated to form a DCL command string. If the first non-numeric
10338 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10339 * the command string is handed off to DCL directly. Otherwise,
10340 * the first token of the command is taken as the filespec of an image
10341 * to run. The filespec is expanded using a default type of '.EXE' and
10342 * the process defaults for device, directory, etc., and if found, the resultant
10343 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10344 * the command string as parameters. This is perhaps a bit complicated,
10345 * but I hope it will form a happy medium between what VMS folks expect
10346 * from lib$spawn and what Unix folks expect from exec.
10349 static int vfork_called;
10351 /*{{{int my_vfork(void)*/
10362 vms_execfree(struct dsc$descriptor_s *vmscmd)
10365 if (vmscmd->dsc$a_pointer) {
10366 PerlMem_free(vmscmd->dsc$a_pointer);
10368 PerlMem_free(vmscmd);
10373 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10375 char *junk, *tmps = NULL;
10383 tmps = SvPV(really,rlen);
10385 cmdlen += rlen + 1;
10390 for (idx++; idx <= sp; idx++) {
10392 junk = SvPVx(*idx,rlen);
10393 cmdlen += rlen ? rlen + 1 : 0;
10396 Newx(PL_Cmd, cmdlen+1, char);
10398 if (tmps && *tmps) {
10399 my_strlcpy(PL_Cmd, tmps, cmdlen + 1);
10402 else *PL_Cmd = '\0';
10403 while (++mark <= sp) {
10405 char *s = SvPVx(*mark,n_a);
10407 if (*PL_Cmd) my_strlcat(PL_Cmd, " ", cmdlen+1);
10408 my_strlcat(PL_Cmd, s, cmdlen+1);
10413 } /* end of setup_argstr() */
10416 static unsigned long int
10417 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10418 struct dsc$descriptor_s **pvmscmd)
10422 char image_name[NAM$C_MAXRSS+1];
10423 char image_argv[NAM$C_MAXRSS+1];
10424 $DESCRIPTOR(defdsc,".EXE");
10425 $DESCRIPTOR(defdsc2,".");
10426 struct dsc$descriptor_s resdsc;
10427 struct dsc$descriptor_s *vmscmd;
10428 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10429 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10430 char *s, *rest, *cp, *wordbreak;
10435 vmscmd = (struct dsc$descriptor_s *)PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10436 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10438 /* vmsspec is a DCL command buffer, not just a filename */
10439 vmsspec = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10440 if (vmsspec == NULL)
10441 _ckvmssts_noperl(SS$_INSFMEM);
10443 resspec = (char *)PerlMem_malloc(VMS_MAXRSS);
10444 if (resspec == NULL)
10445 _ckvmssts_noperl(SS$_INSFMEM);
10447 /* Make a copy for modification */
10448 cmdlen = strlen(incmd);
10449 cmd = (char *)PerlMem_malloc(cmdlen+1);
10450 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10451 my_strlcpy(cmd, incmd, cmdlen + 1);
10455 resdsc.dsc$a_pointer = resspec;
10456 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10457 resdsc.dsc$b_class = DSC$K_CLASS_S;
10458 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10460 vmscmd->dsc$a_pointer = NULL;
10461 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10462 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10463 vmscmd->dsc$w_length = 0;
10464 if (pvmscmd) *pvmscmd = vmscmd;
10466 if (suggest_quote) *suggest_quote = 0;
10468 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10470 PerlMem_free(vmsspec);
10471 PerlMem_free(resspec);
10472 return CLI$_BUFOVF; /* continuation lines currently unsupported */
10477 while (*s && isspace(*s)) s++;
10479 if (*s == '@' || *s == '$') {
10480 vmsspec[0] = *s; rest = s + 1;
10481 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10483 else { cp = vmsspec; rest = s; }
10485 /* If the first word is quoted, then we need to unquote it and
10486 * escape spaces within it. We'll expand into the resspec buffer,
10487 * then copy back into the cmd buffer, expanding the latter if
10490 if (*rest == '"') {
10495 int soff = s - cmd;
10497 for (cp2 = resspec;
10498 *rest && cp2 - resspec < (VMS_MAXRSS - 1);
10501 if (*rest == ' ') { /* Escape ' ' to '^_'. */
10507 else if (*rest == '"') {
10509 if (in_quote) { /* Must be closing quote. */
10522 /* Expand the command buffer if necessary. */
10523 if (clen > cmdlen) {
10524 cmd = (char *)PerlMem_realloc(cmd, clen);
10526 _ckvmssts_noperl(SS$_INSFMEM);
10527 /* Where we are may have changed, so recompute offsets */
10528 r = cmd + (r - s - soff);
10529 rest = cmd + (rest - s - soff);
10533 /* Shift the non-verb portion of the command (if any) up or
10534 * down as necessary.
10537 memmove(rest + clen - cmdlen, rest, s - soff + cmdlen - rest);
10539 /* Copy the unquoted and escaped command verb into place. */
10540 memcpy(r, resspec, cp2 - resspec);
10543 rest = r; /* Rewind for subsequent operations. */
10546 if (*rest == '.' || *rest == '/') {
10548 for (cp2 = resspec;
10549 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10550 rest++, cp2++) *cp2 = *rest;
10552 if (int_tovmsspec(resspec, cp, 0, NULL)) {
10555 /* When a UNIX spec with no file type is translated to VMS, */
10556 /* A trailing '.' is appended under ODS-5 rules. */
10557 /* Here we do not want that trailing "." as it prevents */
10558 /* Looking for a implied ".exe" type. */
10559 if (decc_efs_charset) {
10561 i = strlen(vmsspec);
10562 if (vmsspec[i-1] == '.') {
10563 vmsspec[i-1] = '\0';
10568 for (cp2 = vmsspec + strlen(vmsspec);
10569 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10570 rest++, cp2++) *cp2 = *rest;
10575 /* Intuit whether verb (first word of cmd) is a DCL command:
10576 * - if first nonspace char is '@', it's a DCL indirection
10578 * - if verb contains a filespec separator, it's not a DCL command
10579 * - if it doesn't, caller tells us whether to default to a DCL
10580 * command, or to a local image unless told it's DCL (by leading '$')
10584 if (suggest_quote) *suggest_quote = 1;
10586 char *filespec = strpbrk(s,":<[.;");
10587 rest = wordbreak = strpbrk(s," \"\t/");
10588 if (!wordbreak) wordbreak = s + strlen(s);
10589 if (*s == '$') check_img = 0;
10590 if (filespec && (filespec < wordbreak)) isdcl = 0;
10591 else isdcl = !check_img;
10596 imgdsc.dsc$a_pointer = s;
10597 imgdsc.dsc$w_length = wordbreak - s;
10598 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10600 _ckvmssts_noperl(lib$find_file_end(&cxt));
10601 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10602 if (!(retsts & 1) && *s == '$') {
10603 _ckvmssts_noperl(lib$find_file_end(&cxt));
10604 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10605 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10607 _ckvmssts_noperl(lib$find_file_end(&cxt));
10608 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10612 _ckvmssts_noperl(lib$find_file_end(&cxt));
10617 while (*s && !isspace(*s)) s++;
10620 /* check that it's really not DCL with no file extension */
10621 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10623 char b[256] = {0,0,0,0};
10624 read(fileno(fp), b, 256);
10625 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10629 /* Check for script */
10631 if ((b[0] == '#') && (b[1] == '!'))
10633 #ifdef ALTERNATE_SHEBANG
10635 shebang_len = strlen(ALTERNATE_SHEBANG);
10636 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10638 perlstr = strstr("perl",b);
10639 if (perlstr == NULL)
10647 if (shebang_len > 0) {
10650 char tmpspec[NAM$C_MAXRSS + 1];
10653 /* Image is following after white space */
10654 /*--------------------------------------*/
10655 while (isprint(b[i]) && isspace(b[i]))
10659 while (isprint(b[i]) && !isspace(b[i])) {
10660 tmpspec[j++] = b[i++];
10661 if (j >= NAM$C_MAXRSS)
10666 /* There may be some default parameters to the image */
10667 /*---------------------------------------------------*/
10669 while (isprint(b[i])) {
10670 image_argv[j++] = b[i++];
10671 if (j >= NAM$C_MAXRSS)
10674 while ((j > 0) && !isprint(image_argv[j-1]))
10678 /* It will need to be converted to VMS format and validated */
10679 if (tmpspec[0] != '\0') {
10682 /* Try to find the exact program requested to be run */
10683 /*---------------------------------------------------*/
10684 iname = int_rmsexpand
10685 (tmpspec, image_name, ".exe",
10686 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10687 if (iname != NULL) {
10688 if (cando_by_name_int
10689 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10690 /* MCR prefix needed */
10694 /* Try again with a null type */
10695 /*----------------------------*/
10696 iname = int_rmsexpand
10697 (tmpspec, image_name, ".",
10698 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10699 if (iname != NULL) {
10700 if (cando_by_name_int
10701 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10702 /* MCR prefix needed */
10708 /* Did we find the image to run the script? */
10709 /*------------------------------------------*/
10713 /* Assume DCL or foreign command exists */
10714 /*--------------------------------------*/
10715 tchr = strrchr(tmpspec, '/');
10716 if (tchr != NULL) {
10722 my_strlcpy(image_name, tchr, sizeof(image_name));
10730 if (check_img && isdcl) {
10732 PerlMem_free(resspec);
10733 PerlMem_free(vmsspec);
10737 if (cando_by_name(S_IXUSR,0,resspec)) {
10738 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10739 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10741 my_strlcpy(vmscmd->dsc$a_pointer,"$ MCR ", MAX_DCL_LINE_LENGTH);
10742 if (image_name[0] != 0) {
10743 my_strlcat(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10744 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10746 } else if (image_name[0] != 0) {
10747 my_strlcpy(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10748 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10750 my_strlcpy(vmscmd->dsc$a_pointer, "@", MAX_DCL_LINE_LENGTH);
10752 if (suggest_quote) *suggest_quote = 1;
10754 /* If there is an image name, use original command */
10755 if (image_name[0] == 0)
10756 my_strlcat(vmscmd->dsc$a_pointer, resspec, MAX_DCL_LINE_LENGTH);
10759 while (*rest && isspace(*rest)) rest++;
10762 if (image_argv[0] != 0) {
10763 my_strlcat(vmscmd->dsc$a_pointer, image_argv, MAX_DCL_LINE_LENGTH);
10764 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10770 rest_len = strlen(rest);
10771 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10772 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10773 my_strlcat(vmscmd->dsc$a_pointer, rest, MAX_DCL_LINE_LENGTH);
10775 retsts = CLI$_BUFOVF;
10777 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10779 PerlMem_free(vmsspec);
10780 PerlMem_free(resspec);
10781 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10787 /* It's either a DCL command or we couldn't find a suitable image */
10788 vmscmd->dsc$w_length = strlen(cmd);
10790 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(vmscmd->dsc$w_length + 1);
10791 my_strlcpy(vmscmd->dsc$a_pointer, cmd, vmscmd->dsc$w_length + 1);
10794 PerlMem_free(resspec);
10795 PerlMem_free(vmsspec);
10797 /* check if it's a symbol (for quoting purposes) */
10798 if (suggest_quote && !*suggest_quote) {
10800 char equiv[LNM$C_NAMLENGTH];
10801 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10802 eqvdsc.dsc$a_pointer = equiv;
10804 iss = lib$get_symbol(vmscmd,&eqvdsc);
10805 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10807 if (!(retsts & 1)) {
10808 /* just hand off status values likely to be due to user error */
10809 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10810 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10811 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10812 else { _ckvmssts_noperl(retsts); }
10815 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10817 } /* end of setup_cmddsc() */
10820 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10822 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10828 if (vfork_called) { /* this follows a vfork - act Unixish */
10830 if (vfork_called < 0) {
10831 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10834 else return do_aexec(really,mark,sp);
10836 /* no vfork - act VMSish */
10837 cmd = setup_argstr(aTHX_ really,mark,sp);
10838 exec_sts = vms_do_exec(cmd);
10839 Safefree(cmd); /* Clean up from setup_argstr() */
10844 } /* end of vms_do_aexec() */
10847 /* {{{bool vms_do_exec(char *cmd) */
10849 Perl_vms_do_exec(pTHX_ const char *cmd)
10851 struct dsc$descriptor_s *vmscmd;
10853 if (vfork_called) { /* this follows a vfork - act Unixish */
10855 if (vfork_called < 0) {
10856 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10859 else return do_exec(cmd);
10862 { /* no vfork - act VMSish */
10863 unsigned long int retsts;
10866 TAINT_PROPER("exec");
10867 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10868 retsts = lib$do_command(vmscmd);
10871 case RMS$_FNF: case RMS$_DNF:
10872 set_errno(ENOENT); break;
10874 set_errno(ENOTDIR); break;
10876 set_errno(ENODEV); break;
10878 set_errno(EACCES); break;
10880 set_errno(EINVAL); break;
10881 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10882 set_errno(E2BIG); break;
10883 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10884 _ckvmssts_noperl(retsts); /* fall through */
10885 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10886 set_errno(EVMSERR);
10888 set_vaxc_errno(retsts);
10889 if (ckWARN(WARN_EXEC)) {
10890 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
10891 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
10893 vms_execfree(vmscmd);
10898 } /* end of vms_do_exec() */
10901 int do_spawn2(pTHX_ const char *, int);
10904 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
10906 unsigned long int sts;
10912 /* We'll copy the (undocumented?) Win32 behavior and allow a
10913 * numeric first argument. But the only value we'll support
10914 * through do_aspawn is a value of 1, which means spawn without
10915 * waiting for completion -- other values are ignored.
10917 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
10919 flags = SvIVx(*mark);
10922 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
10923 flags = CLI$M_NOWAIT;
10927 cmd = setup_argstr(aTHX_ really, mark, sp);
10928 sts = do_spawn2(aTHX_ cmd, flags);
10929 /* pp_sys will clean up cmd */
10933 } /* end of do_aspawn() */
10937 /* {{{int do_spawn(char* cmd) */
10939 Perl_do_spawn(pTHX_ char* cmd)
10941 PERL_ARGS_ASSERT_DO_SPAWN;
10943 return do_spawn2(aTHX_ cmd, 0);
10947 /* {{{int do_spawn_nowait(char* cmd) */
10949 Perl_do_spawn_nowait(pTHX_ char* cmd)
10951 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
10953 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
10957 /* {{{int do_spawn2(char *cmd) */
10959 do_spawn2(pTHX_ const char *cmd, int flags)
10961 unsigned long int sts, substs;
10963 /* The caller of this routine expects to Safefree(PL_Cmd) */
10964 Newx(PL_Cmd,10,char);
10967 TAINT_PROPER("spawn");
10968 if (!cmd || !*cmd) {
10969 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
10972 case RMS$_FNF: case RMS$_DNF:
10973 set_errno(ENOENT); break;
10975 set_errno(ENOTDIR); break;
10977 set_errno(ENODEV); break;
10979 set_errno(EACCES); break;
10981 set_errno(EINVAL); break;
10982 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10983 set_errno(E2BIG); break;
10984 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10985 _ckvmssts_noperl(sts); /* fall through */
10986 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10987 set_errno(EVMSERR);
10989 set_vaxc_errno(sts);
10990 if (ckWARN(WARN_EXEC)) {
10991 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
11000 if (flags & CLI$M_NOWAIT)
11003 strcpy(mode, "nW");
11005 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
11008 /* sts will be the pid in the nowait case */
11011 } /* end of do_spawn2() */
11015 static unsigned int *sockflags, sockflagsize;
11018 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11019 * routines found in some versions of the CRTL can't deal with sockets.
11020 * We don't shim the other file open routines since a socket isn't
11021 * likely to be opened by a name.
11023 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11024 FILE *my_fdopen(int fd, const char *mode)
11026 FILE *fp = fdopen(fd, mode);
11029 unsigned int fdoff = fd / sizeof(unsigned int);
11030 Stat_t sbuf; /* native stat; we don't need flex_stat */
11031 if (!sockflagsize || fdoff > sockflagsize) {
11032 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
11033 else Newx (sockflags,fdoff+2,unsigned int);
11034 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11035 sockflagsize = fdoff + 2;
11037 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11038 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11047 * Clear the corresponding bit when the (possibly) socket stream is closed.
11048 * There still a small hole: we miss an implicit close which might occur
11049 * via freopen(). >> Todo
11051 /*{{{ int my_fclose(FILE *fp)*/
11052 int my_fclose(FILE *fp) {
11054 unsigned int fd = fileno(fp);
11055 unsigned int fdoff = fd / sizeof(unsigned int);
11057 if (sockflagsize && fdoff < sockflagsize)
11058 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11066 * A simple fwrite replacement which outputs itmsz*nitm chars without
11067 * introducing record boundaries every itmsz chars.
11068 * We are using fputs, which depends on a terminating null. We may
11069 * well be writing binary data, so we need to accommodate not only
11070 * data with nulls sprinkled in the middle but also data with no null
11073 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11075 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11077 char *cp, *end, *cpd;
11079 unsigned int fd = fileno(dest);
11080 unsigned int fdoff = fd / sizeof(unsigned int);
11082 int bufsize = itmsz * nitm + 1;
11084 if (fdoff < sockflagsize &&
11085 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11086 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11090 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11091 memcpy( data, src, itmsz*nitm );
11092 data[itmsz*nitm] = '\0';
11094 end = data + itmsz * nitm;
11095 retval = (int) nitm; /* on success return # items written */
11098 while (cpd <= end) {
11099 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11100 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11102 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11106 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11109 } /* end of my_fwrite() */
11112 /*{{{ int my_flush(FILE *fp)*/
11114 Perl_my_flush(pTHX_ FILE *fp)
11117 if ((res = fflush(fp)) == 0 && fp) {
11118 #ifdef VMS_DO_SOCKETS
11120 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11122 res = fsync(fileno(fp));
11125 * If the flush succeeded but set end-of-file, we need to clear
11126 * the error because our caller may check ferror(). BTW, this
11127 * probably means we just flushed an empty file.
11129 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11135 /* fgetname() is not returning the correct file specifications when
11136 * decc_filename_unix_report mode is active. So we have to have it
11137 * aways return filenames in VMS mode and convert it ourselves.
11140 /*{{{ char * my_fgetname(FILE *fp, buf)*/
11142 Perl_my_fgetname(FILE *fp, char * buf) {
11146 retname = fgetname(fp, buf, 1);
11148 /* If we are in VMS mode, then we are done */
11149 if (!decc_filename_unix_report || (retname == NULL)) {
11153 /* Convert this to Unix format */
11154 vms_name = (char *)PerlMem_malloc(VMS_MAXRSS);
11155 my_strlcpy(vms_name, retname, VMS_MAXRSS);
11156 retname = int_tounixspec(vms_name, buf, NULL);
11157 PerlMem_free(vms_name);
11164 * Here are replacements for the following Unix routines in the VMS environment:
11165 * getpwuid Get information for a particular UIC or UID
11166 * getpwnam Get information for a named user
11167 * getpwent Get information for each user in the rights database
11168 * setpwent Reset search to the start of the rights database
11169 * endpwent Finish searching for users in the rights database
11171 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11172 * (defined in pwd.h), which contains the following fields:-
11174 * char *pw_name; Username (in lower case)
11175 * char *pw_passwd; Hashed password
11176 * unsigned int pw_uid; UIC
11177 * unsigned int pw_gid; UIC group number
11178 * char *pw_unixdir; Default device/directory (VMS-style)
11179 * char *pw_gecos; Owner name
11180 * char *pw_dir; Default device/directory (Unix-style)
11181 * char *pw_shell; Default CLI name (eg. DCL)
11183 * If the specified user does not exist, getpwuid and getpwnam return NULL.
11185 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11186 * not the UIC member number (eg. what's returned by getuid()),
11187 * getpwuid() can accept either as input (if uid is specified, the caller's
11188 * UIC group is used), though it won't recognise gid=0.
11190 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11191 * information about other users in your group or in other groups, respectively.
11192 * If the required privilege is not available, then these routines fill only
11193 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11196 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11199 /* sizes of various UAF record fields */
11200 #define UAI$S_USERNAME 12
11201 #define UAI$S_IDENT 31
11202 #define UAI$S_OWNER 31
11203 #define UAI$S_DEFDEV 31
11204 #define UAI$S_DEFDIR 63
11205 #define UAI$S_DEFCLI 31
11206 #define UAI$S_PWD 8
11208 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
11209 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11210 (uic).uic$v_group != UIC$K_WILD_GROUP)
11212 static char __empty[]= "";
11213 static struct passwd __passwd_empty=
11214 {(char *) __empty, (char *) __empty, 0, 0,
11215 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11216 static int contxt= 0;
11217 static struct passwd __pwdcache;
11218 static char __pw_namecache[UAI$S_IDENT+1];
11221 * This routine does most of the work extracting the user information.
11223 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11226 unsigned char length;
11227 char pw_gecos[UAI$S_OWNER+1];
11229 static union uicdef uic;
11231 unsigned char length;
11232 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11235 unsigned char length;
11236 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11239 unsigned char length;
11240 char pw_shell[UAI$S_DEFCLI+1];
11242 static char pw_passwd[UAI$S_PWD+1];
11244 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11245 struct dsc$descriptor_s name_desc;
11246 unsigned long int sts;
11248 static struct itmlst_3 itmlst[]= {
11249 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
11250 {sizeof(uic), UAI$_UIC, &uic, &luic},
11251 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
11252 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
11253 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
11254 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
11255 {0, 0, NULL, NULL}};
11257 name_desc.dsc$w_length= strlen(name);
11258 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11259 name_desc.dsc$b_class= DSC$K_CLASS_S;
11260 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11262 /* Note that sys$getuai returns many fields as counted strings. */
11263 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11264 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11265 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11267 else { _ckvmssts(sts); }
11268 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
11270 if ((int) owner.length < lowner) lowner= (int) owner.length;
11271 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11272 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11273 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11274 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11275 owner.pw_gecos[lowner]= '\0';
11276 defdev.pw_dir[ldefdev+ldefdir]= '\0';
11277 defcli.pw_shell[ldefcli]= '\0';
11278 if (valid_uic(uic)) {
11279 pwd->pw_uid= uic.uic$l_uic;
11280 pwd->pw_gid= uic.uic$v_group;
11283 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11284 pwd->pw_passwd= pw_passwd;
11285 pwd->pw_gecos= owner.pw_gecos;
11286 pwd->pw_dir= defdev.pw_dir;
11287 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11288 pwd->pw_shell= defcli.pw_shell;
11289 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11291 ldir= strlen(pwd->pw_unixdir) - 1;
11292 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11295 my_strlcpy(pwd->pw_unixdir, pwd->pw_dir, sizeof(pwd->pw_unixdir));
11296 if (!decc_efs_case_preserve)
11297 __mystrtolower(pwd->pw_unixdir);
11302 * Get information for a named user.
11304 /*{{{struct passwd *getpwnam(char *name)*/
11305 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
11307 struct dsc$descriptor_s name_desc;
11309 unsigned long int sts;
11311 __pwdcache = __passwd_empty;
11312 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11313 /* We still may be able to determine pw_uid and pw_gid */
11314 name_desc.dsc$w_length= strlen(name);
11315 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11316 name_desc.dsc$b_class= DSC$K_CLASS_S;
11317 name_desc.dsc$a_pointer= (char *) name;
11318 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11319 __pwdcache.pw_uid= uic.uic$l_uic;
11320 __pwdcache.pw_gid= uic.uic$v_group;
11323 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11324 set_vaxc_errno(sts);
11325 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11328 else { _ckvmssts(sts); }
11331 my_strlcpy(__pw_namecache, name, sizeof(__pw_namecache));
11332 __pwdcache.pw_name= __pw_namecache;
11333 return &__pwdcache;
11334 } /* end of my_getpwnam() */
11338 * Get information for a particular UIC or UID.
11339 * Called by my_getpwent with uid=-1 to list all users.
11341 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11342 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
11344 const $DESCRIPTOR(name_desc,__pw_namecache);
11345 unsigned short lname;
11347 unsigned long int status;
11349 if (uid == (unsigned int) -1) {
11351 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11352 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11353 set_vaxc_errno(status);
11354 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11358 else { _ckvmssts(status); }
11359 } while (!valid_uic (uic));
11362 uic.uic$l_uic= uid;
11363 if (!uic.uic$v_group)
11364 uic.uic$v_group= PerlProc_getgid();
11365 if (valid_uic(uic))
11366 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11367 else status = SS$_IVIDENT;
11368 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11369 status == RMS$_PRV) {
11370 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11373 else { _ckvmssts(status); }
11375 __pw_namecache[lname]= '\0';
11376 __mystrtolower(__pw_namecache);
11378 __pwdcache = __passwd_empty;
11379 __pwdcache.pw_name = __pw_namecache;
11381 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11382 The identifier's value is usually the UIC, but it doesn't have to be,
11383 so if we can, we let fillpasswd update this. */
11384 __pwdcache.pw_uid = uic.uic$l_uic;
11385 __pwdcache.pw_gid = uic.uic$v_group;
11387 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11388 return &__pwdcache;
11390 } /* end of my_getpwuid() */
11394 * Get information for next user.
11396 /*{{{struct passwd *my_getpwent()*/
11397 struct passwd *Perl_my_getpwent(pTHX)
11399 return (my_getpwuid((unsigned int) -1));
11404 * Finish searching rights database for users.
11406 /*{{{void my_endpwent()*/
11407 void Perl_my_endpwent(pTHX)
11410 _ckvmssts(sys$finish_rdb(&contxt));
11416 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11417 * my_utime(), and flex_stat(), all of which operate on UTC unless
11418 * VMSISH_TIMES is true.
11420 /* method used to handle UTC conversions:
11421 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
11423 static int gmtime_emulation_type;
11424 /* number of secs to add to UTC POSIX-style time to get local time */
11425 static long int utc_offset_secs;
11427 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11428 * in vmsish.h. #undef them here so we can call the CRTL routines
11436 static time_t toutc_dst(time_t loc) {
11439 if ((rsltmp = localtime(&loc)) == NULL) return -1u;
11440 loc -= utc_offset_secs;
11441 if (rsltmp->tm_isdst) loc -= 3600;
11444 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11445 ((gmtime_emulation_type || my_time(NULL)), \
11446 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11447 ((secs) - utc_offset_secs))))
11449 static time_t toloc_dst(time_t utc) {
11452 utc += utc_offset_secs;
11453 if ((rsltmp = localtime(&utc)) == NULL) return -1u;
11454 if (rsltmp->tm_isdst) utc += 3600;
11457 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11458 ((gmtime_emulation_type || my_time(NULL)), \
11459 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11460 ((secs) + utc_offset_secs))))
11462 /* my_time(), my_localtime(), my_gmtime()
11463 * By default traffic in UTC time values, using CRTL gmtime() or
11464 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11465 * Note: We need to use these functions even when the CRTL has working
11466 * UTC support, since they also handle C<use vmsish qw(times);>
11468 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
11469 * Modified by Charles Bailey <bailey@newman.upenn.edu>
11472 /*{{{time_t my_time(time_t *timep)*/
11473 time_t Perl_my_time(pTHX_ time_t *timep)
11478 if (gmtime_emulation_type == 0) {
11479 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
11480 /* results of calls to gmtime() and localtime() */
11481 /* for same &base */
11483 gmtime_emulation_type++;
11484 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11485 char off[LNM$C_NAMLENGTH+1];;
11487 gmtime_emulation_type++;
11488 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11489 gmtime_emulation_type++;
11490 utc_offset_secs = 0;
11491 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11493 else { utc_offset_secs = atol(off); }
11495 else { /* We've got a working gmtime() */
11496 struct tm gmt, local;
11499 tm_p = localtime(&base);
11501 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
11502 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11503 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
11504 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
11509 # ifdef VMSISH_TIME
11510 if (VMSISH_TIME) when = _toloc(when);
11512 if (timep != NULL) *timep = when;
11515 } /* end of my_time() */
11519 /*{{{struct tm *my_gmtime(const time_t *timep)*/
11521 Perl_my_gmtime(pTHX_ const time_t *timep)
11526 if (timep == NULL) {
11527 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11530 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11533 # ifdef VMSISH_TIME
11534 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11536 return gmtime(&when);
11537 } /* end of my_gmtime() */
11541 /*{{{struct tm *my_localtime(const time_t *timep)*/
11543 Perl_my_localtime(pTHX_ const time_t *timep)
11547 if (timep == NULL) {
11548 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11551 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11552 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
11555 # ifdef VMSISH_TIME
11556 if (VMSISH_TIME) when = _toutc(when);
11558 /* CRTL localtime() wants UTC as input, does tz correction itself */
11559 return localtime(&when);
11560 } /* end of my_localtime() */
11563 /* Reset definitions for later calls */
11564 #define gmtime(t) my_gmtime(t)
11565 #define localtime(t) my_localtime(t)
11566 #define time(t) my_time(t)
11569 /* my_utime - update modification/access time of a file
11571 * VMS 7.3 and later implementation
11572 * Only the UTC translation is home-grown. The rest is handled by the
11573 * CRTL utime(), which will take into account the relevant feature
11574 * logicals and ODS-5 volume characteristics for true access times.
11576 * pre VMS 7.3 implementation:
11577 * The calling sequence is identical to POSIX utime(), but under
11578 * VMS with ODS-2, only the modification time is changed; ODS-2 does
11579 * not maintain access times. Restrictions differ from the POSIX
11580 * definition in that the time can be changed as long as the
11581 * caller has permission to execute the necessary IO$_MODIFY $QIO;
11582 * no separate checks are made to insure that the caller is the
11583 * owner of the file or has special privs enabled.
11584 * Code here is based on Joe Meadows' FILE utility.
11588 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11589 * to VMS epoch (01-JAN-1858 00:00:00.00)
11590 * in 100 ns intervals.
11592 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11594 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11595 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
11597 #if __CRTL_VER >= 70300000
11598 struct utimbuf utc_utimes, *utc_utimesp;
11600 if (utimes != NULL) {
11601 utc_utimes.actime = utimes->actime;
11602 utc_utimes.modtime = utimes->modtime;
11603 # ifdef VMSISH_TIME
11604 /* If input was local; convert to UTC for sys svc */
11606 utc_utimes.actime = _toutc(utimes->actime);
11607 utc_utimes.modtime = _toutc(utimes->modtime);
11610 utc_utimesp = &utc_utimes;
11613 utc_utimesp = NULL;
11616 return utime(file, utc_utimesp);
11618 #else /* __CRTL_VER < 70300000 */
11622 long int bintime[2], len = 2, lowbit, unixtime,
11623 secscale = 10000000; /* seconds --> 100 ns intervals */
11624 unsigned long int chan, iosb[2], retsts;
11625 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
11626 struct FAB myfab = cc$rms_fab;
11627 struct NAM mynam = cc$rms_nam;
11628 #if defined (__DECC) && defined (__VAX)
11629 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
11630 * at least through VMS V6.1, which causes a type-conversion warning.
11632 # pragma message save
11633 # pragma message disable cvtdiftypes
11635 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
11636 struct fibdef myfib;
11637 #if defined (__DECC) && defined (__VAX)
11638 /* This should be right after the declaration of myatr, but due
11639 * to a bug in VAX DEC C, this takes effect a statement early.
11641 # pragma message restore
11643 /* cast ok for read only parameter */
11644 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
11645 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
11646 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
11648 if (file == NULL || *file == '\0') {
11649 SETERRNO(ENOENT, LIB$_INVARG);
11653 /* Convert to VMS format ensuring that it will fit in 255 characters */
11654 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
11655 SETERRNO(ENOENT, LIB$_INVARG);
11658 if (utimes != NULL) {
11659 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
11660 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
11661 * Since time_t is unsigned long int, and lib$emul takes a signed long int
11662 * as input, we force the sign bit to be clear by shifting unixtime right
11663 * one bit, then multiplying by an extra factor of 2 in lib$emul().
11665 lowbit = (utimes->modtime & 1) ? secscale : 0;
11666 unixtime = (long int) utimes->modtime;
11667 # ifdef VMSISH_TIME
11668 /* If input was UTC; convert to local for sys svc */
11669 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
11671 unixtime >>= 1; secscale <<= 1;
11672 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
11673 if (!(retsts & 1)) {
11674 SETERRNO(EVMSERR, retsts);
11677 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
11678 if (!(retsts & 1)) {
11679 SETERRNO(EVMSERR, retsts);
11684 /* Just get the current time in VMS format directly */
11685 retsts = sys$gettim(bintime);
11686 if (!(retsts & 1)) {
11687 SETERRNO(EVMSERR, retsts);
11692 myfab.fab$l_fna = vmsspec;
11693 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
11694 myfab.fab$l_nam = &mynam;
11695 mynam.nam$l_esa = esa;
11696 mynam.nam$b_ess = (unsigned char) sizeof esa;
11697 mynam.nam$l_rsa = rsa;
11698 mynam.nam$b_rss = (unsigned char) sizeof rsa;
11699 if (decc_efs_case_preserve)
11700 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
11702 /* Look for the file to be affected, letting RMS parse the file
11703 * specification for us as well. I have set errno using only
11704 * values documented in the utime() man page for VMS POSIX.
11706 retsts = sys$parse(&myfab,0,0);
11707 if (!(retsts & 1)) {
11708 set_vaxc_errno(retsts);
11709 if (retsts == RMS$_PRV) set_errno(EACCES);
11710 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
11711 else set_errno(EVMSERR);
11714 retsts = sys$search(&myfab,0,0);
11715 if (!(retsts & 1)) {
11716 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11717 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11718 set_vaxc_errno(retsts);
11719 if (retsts == RMS$_PRV) set_errno(EACCES);
11720 else if (retsts == RMS$_FNF) set_errno(ENOENT);
11721 else set_errno(EVMSERR);
11725 devdsc.dsc$w_length = mynam.nam$b_dev;
11726 /* cast ok for read only parameter */
11727 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
11729 retsts = sys$assign(&devdsc,&chan,0,0);
11730 if (!(retsts & 1)) {
11731 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11732 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11733 set_vaxc_errno(retsts);
11734 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
11735 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
11736 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
11737 else set_errno(EVMSERR);
11741 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
11742 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
11744 memset((void *) &myfib, 0, sizeof myfib);
11745 #if defined(__DECC) || defined(__DECCXX)
11746 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
11747 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
11748 /* This prevents the revision time of the file being reset to the current
11749 * time as a result of our IO$_MODIFY $QIO. */
11750 myfib.fib$l_acctl = FIB$M_NORECORD;
11752 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
11753 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
11754 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
11756 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
11757 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11758 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11759 _ckvmssts(sys$dassgn(chan));
11760 if (retsts & 1) retsts = iosb[0];
11761 if (!(retsts & 1)) {
11762 set_vaxc_errno(retsts);
11763 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11764 else set_errno(EVMSERR);
11770 #endif /* #if __CRTL_VER >= 70300000 */
11772 } /* end of my_utime() */
11776 * flex_stat, flex_lstat, flex_fstat
11777 * basic stat, but gets it right when asked to stat
11778 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
11781 #ifndef _USE_STD_STAT
11782 /* encode_dev packs a VMS device name string into an integer to allow
11783 * simple comparisons. This can be used, for example, to check whether two
11784 * files are located on the same device, by comparing their encoded device
11785 * names. Even a string comparison would not do, because stat() reuses the
11786 * device name buffer for each call; so without encode_dev, it would be
11787 * necessary to save the buffer and use strcmp (this would mean a number of
11788 * changes to the standard Perl code, to say nothing of what a Perl script
11789 * would have to do.
11791 * The device lock id, if it exists, should be unique (unless perhaps compared
11792 * with lock ids transferred from other nodes). We have a lock id if the disk is
11793 * mounted cluster-wide, which is when we tend to get long (host-qualified)
11794 * device names. Thus we use the lock id in preference, and only if that isn't
11795 * available, do we try to pack the device name into an integer (flagged by
11796 * the sign bit (LOCKID_MASK) being set).
11798 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
11799 * name and its encoded form, but it seems very unlikely that we will find
11800 * two files on different disks that share the same encoded device names,
11801 * and even more remote that they will share the same file id (if the test
11802 * is to check for the same file).
11804 * A better method might be to use sys$device_scan on the first call, and to
11805 * search for the device, returning an index into the cached array.
11806 * The number returned would be more intelligible.
11807 * This is probably not worth it, and anyway would take quite a bit longer
11808 * on the first call.
11810 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
11811 static mydev_t encode_dev (pTHX_ const char *dev)
11814 unsigned long int f;
11819 if (!dev || !dev[0]) return 0;
11823 struct dsc$descriptor_s dev_desc;
11824 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
11826 /* For cluster-mounted disks, the disk lock identifier is unique, so we
11827 can try that first. */
11828 dev_desc.dsc$w_length = strlen (dev);
11829 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
11830 dev_desc.dsc$b_class = DSC$K_CLASS_S;
11831 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
11832 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
11833 if (!$VMS_STATUS_SUCCESS(status)) {
11835 case SS$_NOSUCHDEV:
11836 SETERRNO(ENODEV, status);
11842 if (lockid) return (lockid & ~LOCKID_MASK);
11846 /* Otherwise we try to encode the device name */
11850 for (q = dev + strlen(dev); q--; q >= dev) {
11855 else if (isalpha (toupper (*q)))
11856 c= toupper (*q) - 'A' + (char)10;
11858 continue; /* Skip '$'s */
11860 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
11862 enc += f * (unsigned long int) c;
11864 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
11866 } /* end of encode_dev() */
11867 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11868 device_no = encode_dev(aTHX_ devname)
11870 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11871 device_no = new_dev_no
11875 is_null_device(const char *name)
11877 if (decc_bug_devnull != 0) {
11878 if (strncmp("/dev/null", name, 9) == 0)
11881 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
11882 The underscore prefix, controller letter, and unit number are
11883 independently optional; for our purposes, the colon punctuation
11884 is not. The colon can be trailed by optional directory and/or
11885 filename, but two consecutive colons indicates a nodename rather
11886 than a device. [pr] */
11887 if (*name == '_') ++name;
11888 if (tolower(*name++) != 'n') return 0;
11889 if (tolower(*name++) != 'l') return 0;
11890 if (tolower(*name) == 'a') ++name;
11891 if (*name == '0') ++name;
11892 return (*name++ == ':') && (*name != ':');
11896 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
11898 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
11901 Perl_cando_by_name_int
11902 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
11904 char usrname[L_cuserid];
11905 struct dsc$descriptor_s usrdsc =
11906 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
11907 char *vmsname = NULL, *fileified = NULL;
11908 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
11909 unsigned short int retlen, trnlnm_iter_count;
11910 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11911 union prvdef curprv;
11912 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
11913 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
11914 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
11915 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
11916 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
11918 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
11920 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11922 static int profile_context = -1;
11924 if (!fname || !*fname) return FALSE;
11926 /* Make sure we expand logical names, since sys$check_access doesn't */
11927 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
11928 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11929 if (!strpbrk(fname,"/]>:")) {
11930 my_strlcpy(fileified, fname, VMS_MAXRSS);
11931 trnlnm_iter_count = 0;
11932 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
11933 trnlnm_iter_count++;
11934 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
11939 vmsname = (char *)PerlMem_malloc(VMS_MAXRSS);
11940 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11941 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
11942 /* Don't know if already in VMS format, so make sure */
11943 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
11944 PerlMem_free(fileified);
11945 PerlMem_free(vmsname);
11950 my_strlcpy(vmsname, fname, VMS_MAXRSS);
11953 /* sys$check_access needs a file spec, not a directory spec.
11954 * flex_stat now will handle a null thread context during startup.
11957 retlen = namdsc.dsc$w_length = strlen(vmsname);
11958 if (vmsname[retlen-1] == ']'
11959 || vmsname[retlen-1] == '>'
11960 || vmsname[retlen-1] == ':'
11961 || (!flex_stat_int(vmsname, &st, 1) &&
11962 S_ISDIR(st.st_mode))) {
11964 if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
11965 PerlMem_free(fileified);
11966 PerlMem_free(vmsname);
11975 retlen = namdsc.dsc$w_length = strlen(fname);
11976 namdsc.dsc$a_pointer = (char *)fname;
11979 case S_IXUSR: case S_IXGRP: case S_IXOTH:
11980 access = ARM$M_EXECUTE;
11981 flags = CHP$M_READ;
11983 case S_IRUSR: case S_IRGRP: case S_IROTH:
11984 access = ARM$M_READ;
11985 flags = CHP$M_READ | CHP$M_USEREADALL;
11987 case S_IWUSR: case S_IWGRP: case S_IWOTH:
11988 access = ARM$M_WRITE;
11989 flags = CHP$M_READ | CHP$M_WRITE;
11991 case S_IDUSR: case S_IDGRP: case S_IDOTH:
11992 access = ARM$M_DELETE;
11993 flags = CHP$M_READ | CHP$M_WRITE;
11996 if (fileified != NULL)
11997 PerlMem_free(fileified);
11998 if (vmsname != NULL)
11999 PerlMem_free(vmsname);
12003 /* Before we call $check_access, create a user profile with the current
12004 * process privs since otherwise it just uses the default privs from the
12005 * UAF and might give false positives or negatives. This only works on
12006 * VMS versions v6.0 and later since that's when sys$create_user_profile
12007 * became available.
12010 /* get current process privs and username */
12011 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
12012 _ckvmssts_noperl(iosb[0]);
12014 /* find out the space required for the profile */
12015 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
12016 &usrprodsc.dsc$w_length,&profile_context));
12018 /* allocate space for the profile and get it filled in */
12019 usrprodsc.dsc$a_pointer = (char *)PerlMem_malloc(usrprodsc.dsc$w_length);
12020 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12021 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
12022 &usrprodsc.dsc$w_length,&profile_context));
12024 /* use the profile to check access to the file; free profile & analyze results */
12025 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
12026 PerlMem_free(usrprodsc.dsc$a_pointer);
12027 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
12029 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
12030 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
12031 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
12032 set_vaxc_errno(retsts);
12033 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12034 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12035 else set_errno(ENOENT);
12036 if (fileified != NULL)
12037 PerlMem_free(fileified);
12038 if (vmsname != NULL)
12039 PerlMem_free(vmsname);
12042 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12043 if (fileified != NULL)
12044 PerlMem_free(fileified);
12045 if (vmsname != NULL)
12046 PerlMem_free(vmsname);
12049 _ckvmssts_noperl(retsts);
12051 if (fileified != NULL)
12052 PerlMem_free(fileified);
12053 if (vmsname != NULL)
12054 PerlMem_free(vmsname);
12055 return FALSE; /* Should never get here */
12059 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
12060 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12061 * subset of the applicable information.
12064 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12066 return cando_by_name_int
12067 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12068 } /* end of cando() */
12072 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12074 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12076 return cando_by_name_int(bit, effective, fname, 0);
12078 } /* end of cando_by_name() */
12082 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12084 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12086 dSAVE_ERRNO; /* fstat may set this even on success */
12087 if (!fstat(fd, &statbufp->crtl_stat)) {
12089 char *vms_filename;
12090 vms_filename = (char *)PerlMem_malloc(VMS_MAXRSS);
12091 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12093 /* Save name for cando by name in VMS format */
12094 cptr = getname(fd, vms_filename, 1);
12096 /* This should not happen, but just in case */
12097 if (cptr == NULL) {
12098 statbufp->st_devnam[0] = 0;
12101 /* Make sure that the saved name fits in 255 characters */
12102 cptr = int_rmsexpand_vms
12104 statbufp->st_devnam,
12107 statbufp->st_devnam[0] = 0;
12109 PerlMem_free(vms_filename);
12111 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12113 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12115 # ifdef VMSISH_TIME
12117 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12118 statbufp->st_atime = _toloc(statbufp->st_atime);
12119 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12127 } /* end of flex_fstat() */
12131 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12133 char *temp_fspec = NULL;
12134 char *fileified = NULL;
12135 const char *save_spec;
12139 char already_fileified = 0;
12147 if (decc_bug_devnull != 0) {
12148 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12149 memset(statbufp,0,sizeof *statbufp);
12150 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12151 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12152 statbufp->st_uid = 0x00010001;
12153 statbufp->st_gid = 0x0001;
12154 time((time_t *)&statbufp->st_mtime);
12155 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12162 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12164 * If we are in POSIX filespec mode, accept the filename as is.
12166 if (decc_posix_compliant_pathnames == 0) {
12169 /* Try for a simple stat first. If fspec contains a filename without
12170 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12171 * and sea:[wine.dark]water. exist, the CRTL prefers the directory here.
12172 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12173 * not sea:[wine.dark]., if the latter exists. If the intended target is
12174 * the file with null type, specify this by calling flex_stat() with
12175 * a '.' at the end of fspec.
12178 if (lstat_flag == 0)
12179 retval = stat(fspec, &statbufp->crtl_stat);
12181 retval = lstat(fspec, &statbufp->crtl_stat);
12187 /* In the odd case where we have write but not read access
12188 * to a directory, stat('foo.DIR') works but stat('foo') doesn't.
12190 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12191 if (fileified == NULL)
12192 _ckvmssts_noperl(SS$_INSFMEM);
12194 ret_spec = int_fileify_dirspec(fspec, fileified, NULL);
12195 if (ret_spec != NULL) {
12196 if (lstat_flag == 0)
12197 retval = stat(fileified, &statbufp->crtl_stat);
12199 retval = lstat(fileified, &statbufp->crtl_stat);
12200 save_spec = fileified;
12201 already_fileified = 1;
12205 if (retval && vms_bug_stat_filename) {
12207 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
12208 if (temp_fspec == NULL)
12209 _ckvmssts_noperl(SS$_INSFMEM);
12211 /* We should try again as a vmsified file specification. */
12213 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12214 if (ret_spec != NULL) {
12215 if (lstat_flag == 0)
12216 retval = stat(temp_fspec, &statbufp->crtl_stat);
12218 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12219 save_spec = temp_fspec;
12224 /* Last chance - allow multiple dots without EFS CHARSET */
12225 /* The CRTL stat() falls down hard on multi-dot filenames in unix
12226 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12227 * enable it if it isn't already.
12229 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12230 if (!decc_efs_charset && (decc_efs_charset_index > 0))
12231 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12233 if (lstat_flag == 0)
12234 retval = stat(fspec, &statbufp->crtl_stat);
12236 retval = lstat(fspec, &statbufp->crtl_stat);
12238 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12239 if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12240 decc$feature_set_value(decc_efs_charset_index, 1, 0);
12246 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12248 if (lstat_flag == 0)
12249 retval = stat(temp_fspec, &statbufp->crtl_stat);
12251 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12252 save_spec = temp_fspec;
12256 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12257 /* As you were... */
12258 if (!decc_efs_charset)
12259 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12264 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12266 /* If this is an lstat, do not follow the link */
12268 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12270 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12271 /* If we used the efs_hack above, we must also use it here for */
12272 /* perl_cando to work */
12273 if (efs_hack && (decc_efs_charset_index > 0)) {
12274 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12278 /* If we've got a directory, save a fileified, expanded version of it
12279 * in st_devnam. If not a directory, just an expanded version.
12281 if (S_ISDIR(statbufp->st_mode) && !already_fileified) {
12282 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12283 if (fileified == NULL)
12284 _ckvmssts_noperl(SS$_INSFMEM);
12286 cptr = do_fileify_dirspec(save_spec, fileified, 0, NULL);
12288 save_spec = fileified;
12291 cptr = int_rmsexpand(save_spec,
12292 statbufp->st_devnam,
12298 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12299 if (efs_hack && (decc_efs_charset_index > 0)) {
12300 decc$feature_set_value(decc_efs_charset, 1, 0);
12304 /* Fix me: If this is NULL then stat found a file, and we could */
12305 /* not convert the specification to VMS - Should never happen */
12307 statbufp->st_devnam[0] = 0;
12309 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12311 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12312 # ifdef VMSISH_TIME
12314 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12315 statbufp->st_atime = _toloc(statbufp->st_atime);
12316 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12320 /* If we were successful, leave errno where we found it */
12321 if (retval == 0) RESTORE_ERRNO;
12323 PerlMem_free(temp_fspec);
12325 PerlMem_free(fileified);
12328 } /* end of flex_stat_int() */
12331 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12333 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12335 return flex_stat_int(fspec, statbufp, 0);
12339 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12341 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12343 return flex_stat_int(fspec, statbufp, 1);
12348 /*{{{char *my_getlogin()*/
12349 /* VMS cuserid == Unix getlogin, except calling sequence */
12353 static char user[L_cuserid];
12354 return cuserid(user);
12359 /* rmscopy - copy a file using VMS RMS routines
12361 * Copies contents and attributes of spec_in to spec_out, except owner
12362 * and protection information. Name and type of spec_in are used as
12363 * defaults for spec_out. The third parameter specifies whether rmscopy()
12364 * should try to propagate timestamps from the input file to the output file.
12365 * If it is less than 0, no timestamps are preserved. If it is 0, then
12366 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12367 * propagated to the output file at creation iff the output file specification
12368 * did not contain an explicit name or type, and the revision date is always
12369 * updated at the end of the copy operation. If it is greater than 0, then
12370 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12371 * other than the revision date should be propagated, and bit 1 indicates
12372 * that the revision date should be propagated.
12374 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12376 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12377 * Incorporates, with permission, some code from EZCOPY by Tim Adye
12378 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12379 * as part of the Perl standard distribution under the terms of the
12380 * GNU General Public License or the Perl Artistic License. Copies
12381 * of each may be found in the Perl standard distribution.
12383 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12385 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12387 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12388 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12389 unsigned long int sts;
12391 struct FAB fab_in, fab_out;
12392 struct RAB rab_in, rab_out;
12393 rms_setup_nam(nam);
12394 rms_setup_nam(nam_out);
12395 struct XABDAT xabdat;
12396 struct XABFHC xabfhc;
12397 struct XABRDT xabrdt;
12398 struct XABSUM xabsum;
12400 vmsin = (char *)PerlMem_malloc(VMS_MAXRSS);
12401 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12402 vmsout = (char *)PerlMem_malloc(VMS_MAXRSS);
12403 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12404 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12405 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
12406 PerlMem_free(vmsin);
12407 PerlMem_free(vmsout);
12408 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12412 esa = (char *)PerlMem_malloc(VMS_MAXRSS);
12413 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12415 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12416 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
12417 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12419 fab_in = cc$rms_fab;
12420 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12421 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12422 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12423 fab_in.fab$l_fop = FAB$M_SQO;
12424 rms_bind_fab_nam(fab_in, nam);
12425 fab_in.fab$l_xab = (void *) &xabdat;
12427 rsa = (char *)PerlMem_malloc(VMS_MAXRSS);
12428 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12430 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12431 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
12432 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12434 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12435 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12436 rms_nam_esl(nam) = 0;
12437 rms_nam_rsl(nam) = 0;
12438 rms_nam_esll(nam) = 0;
12439 rms_nam_rsll(nam) = 0;
12440 #ifdef NAM$M_NO_SHORT_UPCASE
12441 if (decc_efs_case_preserve)
12442 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
12445 xabdat = cc$rms_xabdat; /* To get creation date */
12446 xabdat.xab$l_nxt = (void *) &xabfhc;
12448 xabfhc = cc$rms_xabfhc; /* To get record length */
12449 xabfhc.xab$l_nxt = (void *) &xabsum;
12451 xabsum = cc$rms_xabsum; /* To get key and area information */
12453 if (!((sts = sys$open(&fab_in)) & 1)) {
12454 PerlMem_free(vmsin);
12455 PerlMem_free(vmsout);
12458 PerlMem_free(esal);
12461 PerlMem_free(rsal);
12462 set_vaxc_errno(sts);
12464 case RMS$_FNF: case RMS$_DNF:
12465 set_errno(ENOENT); break;
12467 set_errno(ENOTDIR); break;
12469 set_errno(ENODEV); break;
12471 set_errno(EINVAL); break;
12473 set_errno(EACCES); break;
12475 set_errno(EVMSERR);
12482 fab_out.fab$w_ifi = 0;
12483 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12484 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12485 fab_out.fab$l_fop = FAB$M_SQO;
12486 rms_bind_fab_nam(fab_out, nam_out);
12487 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12488 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12489 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
12490 esa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12491 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12492 rsa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12493 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12496 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12497 esal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12498 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12499 rsal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12500 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12502 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
12503 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
12505 if (preserve_dates == 0) { /* Act like DCL COPY */
12506 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
12507 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
12508 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
12509 PerlMem_free(vmsin);
12510 PerlMem_free(vmsout);
12513 PerlMem_free(esal);
12516 PerlMem_free(rsal);
12517 PerlMem_free(esa_out);
12518 if (esal_out != NULL)
12519 PerlMem_free(esal_out);
12520 PerlMem_free(rsa_out);
12521 if (rsal_out != NULL)
12522 PerlMem_free(rsal_out);
12523 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12524 set_vaxc_errno(sts);
12527 fab_out.fab$l_xab = (void *) &xabdat;
12528 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12529 preserve_dates = 1;
12531 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
12532 preserve_dates =0; /* bitmask from this point forward */
12534 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
12535 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
12536 PerlMem_free(vmsin);
12537 PerlMem_free(vmsout);
12540 PerlMem_free(esal);
12543 PerlMem_free(rsal);
12544 PerlMem_free(esa_out);
12545 if (esal_out != NULL)
12546 PerlMem_free(esal_out);
12547 PerlMem_free(rsa_out);
12548 if (rsal_out != NULL)
12549 PerlMem_free(rsal_out);
12550 set_vaxc_errno(sts);
12553 set_errno(ENOENT); break;
12555 set_errno(ENOTDIR); break;
12557 set_errno(ENODEV); break;
12559 set_errno(EINVAL); break;
12561 set_errno(EACCES); break;
12563 set_errno(EVMSERR);
12567 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
12568 if (preserve_dates & 2) {
12569 /* sys$close() will process xabrdt, not xabdat */
12570 xabrdt = cc$rms_xabrdt;
12572 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12574 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
12575 * is unsigned long[2], while DECC & VAXC use a struct */
12576 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
12578 fab_out.fab$l_xab = (void *) &xabrdt;
12581 ubf = (char *)PerlMem_malloc(32256);
12582 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12583 rab_in = cc$rms_rab;
12584 rab_in.rab$l_fab = &fab_in;
12585 rab_in.rab$l_rop = RAB$M_BIO;
12586 rab_in.rab$l_ubf = ubf;
12587 rab_in.rab$w_usz = 32256;
12588 if (!((sts = sys$connect(&rab_in)) & 1)) {
12589 sys$close(&fab_in); sys$close(&fab_out);
12590 PerlMem_free(vmsin);
12591 PerlMem_free(vmsout);
12595 PerlMem_free(esal);
12598 PerlMem_free(rsal);
12599 PerlMem_free(esa_out);
12600 if (esal_out != NULL)
12601 PerlMem_free(esal_out);
12602 PerlMem_free(rsa_out);
12603 if (rsal_out != NULL)
12604 PerlMem_free(rsal_out);
12605 set_errno(EVMSERR); set_vaxc_errno(sts);
12609 rab_out = cc$rms_rab;
12610 rab_out.rab$l_fab = &fab_out;
12611 rab_out.rab$l_rbf = ubf;
12612 if (!((sts = sys$connect(&rab_out)) & 1)) {
12613 sys$close(&fab_in); sys$close(&fab_out);
12614 PerlMem_free(vmsin);
12615 PerlMem_free(vmsout);
12619 PerlMem_free(esal);
12622 PerlMem_free(rsal);
12623 PerlMem_free(esa_out);
12624 if (esal_out != NULL)
12625 PerlMem_free(esal_out);
12626 PerlMem_free(rsa_out);
12627 if (rsal_out != NULL)
12628 PerlMem_free(rsal_out);
12629 set_errno(EVMSERR); set_vaxc_errno(sts);
12633 while ((sts = sys$read(&rab_in))) { /* always true */
12634 if (sts == RMS$_EOF) break;
12635 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12636 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
12637 sys$close(&fab_in); sys$close(&fab_out);
12638 PerlMem_free(vmsin);
12639 PerlMem_free(vmsout);
12643 PerlMem_free(esal);
12646 PerlMem_free(rsal);
12647 PerlMem_free(esa_out);
12648 if (esal_out != NULL)
12649 PerlMem_free(esal_out);
12650 PerlMem_free(rsa_out);
12651 if (rsal_out != NULL)
12652 PerlMem_free(rsal_out);
12653 set_errno(EVMSERR); set_vaxc_errno(sts);
12659 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
12660 sys$close(&fab_in); sys$close(&fab_out);
12661 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
12663 PerlMem_free(vmsin);
12664 PerlMem_free(vmsout);
12668 PerlMem_free(esal);
12671 PerlMem_free(rsal);
12672 PerlMem_free(esa_out);
12673 if (esal_out != NULL)
12674 PerlMem_free(esal_out);
12675 PerlMem_free(rsa_out);
12676 if (rsal_out != NULL)
12677 PerlMem_free(rsal_out);
12680 set_errno(EVMSERR); set_vaxc_errno(sts);
12686 } /* end of rmscopy() */
12690 /*** The following glue provides 'hooks' to make some of the routines
12691 * from this file available from Perl. These routines are sufficiently
12692 * basic, and are required sufficiently early in the build process,
12693 * that's it's nice to have them available to miniperl as well as the
12694 * full Perl, so they're set up here instead of in an extension. The
12695 * Perl code which handles importation of these names into a given
12696 * package lives in [.VMS]Filespec.pm in @INC.
12700 rmsexpand_fromperl(pTHX_ CV *cv)
12703 char *fspec, *defspec = NULL, *rslt;
12705 int fs_utf8, dfs_utf8;
12709 if (!items || items > 2)
12710 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
12711 fspec = SvPV(ST(0),n_a);
12712 fs_utf8 = SvUTF8(ST(0));
12713 if (!fspec || !*fspec) XSRETURN_UNDEF;
12715 defspec = SvPV(ST(1),n_a);
12716 dfs_utf8 = SvUTF8(ST(1));
12718 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
12719 ST(0) = sv_newmortal();
12720 if (rslt != NULL) {
12721 sv_usepvn(ST(0),rslt,strlen(rslt));
12730 vmsify_fromperl(pTHX_ CV *cv)
12737 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
12738 utf8_fl = SvUTF8(ST(0));
12739 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12740 ST(0) = sv_newmortal();
12741 if (vmsified != NULL) {
12742 sv_usepvn(ST(0),vmsified,strlen(vmsified));
12751 unixify_fromperl(pTHX_ CV *cv)
12758 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
12759 utf8_fl = SvUTF8(ST(0));
12760 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12761 ST(0) = sv_newmortal();
12762 if (unixified != NULL) {
12763 sv_usepvn(ST(0),unixified,strlen(unixified));
12772 fileify_fromperl(pTHX_ CV *cv)
12779 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
12780 utf8_fl = SvUTF8(ST(0));
12781 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12782 ST(0) = sv_newmortal();
12783 if (fileified != NULL) {
12784 sv_usepvn(ST(0),fileified,strlen(fileified));
12793 pathify_fromperl(pTHX_ CV *cv)
12800 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
12801 utf8_fl = SvUTF8(ST(0));
12802 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12803 ST(0) = sv_newmortal();
12804 if (pathified != NULL) {
12805 sv_usepvn(ST(0),pathified,strlen(pathified));
12814 vmspath_fromperl(pTHX_ CV *cv)
12821 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
12822 utf8_fl = SvUTF8(ST(0));
12823 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12824 ST(0) = sv_newmortal();
12825 if (vmspath != NULL) {
12826 sv_usepvn(ST(0),vmspath,strlen(vmspath));
12835 unixpath_fromperl(pTHX_ CV *cv)
12842 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
12843 utf8_fl = SvUTF8(ST(0));
12844 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12845 ST(0) = sv_newmortal();
12846 if (unixpath != NULL) {
12847 sv_usepvn(ST(0),unixpath,strlen(unixpath));
12856 candelete_fromperl(pTHX_ CV *cv)
12864 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
12866 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12867 Newx(fspec, VMS_MAXRSS, char);
12868 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
12869 if (isGV_with_GP(mysv)) {
12870 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
12871 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12879 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
12880 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12887 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
12893 rmscopy_fromperl(pTHX_ CV *cv)
12896 char *inspec, *outspec, *inp, *outp;
12902 if (items < 2 || items > 3)
12903 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
12905 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12906 Newx(inspec, VMS_MAXRSS, char);
12907 if (isGV_with_GP(mysv)) {
12908 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
12909 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12910 ST(0) = sv_2mortal(newSViv(0));
12917 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
12918 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12919 ST(0) = sv_2mortal(newSViv(0));
12924 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
12925 Newx(outspec, VMS_MAXRSS, char);
12926 if (isGV_with_GP(mysv)) {
12927 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
12928 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12929 ST(0) = sv_2mortal(newSViv(0));
12937 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
12938 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12939 ST(0) = sv_2mortal(newSViv(0));
12945 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
12947 ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag)));
12953 /* The mod2fname is limited to shorter filenames by design, so it should
12954 * not be modified to support longer EFS pathnames
12957 mod2fname(pTHX_ CV *cv)
12960 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
12961 workbuff[NAM$C_MAXRSS*1 + 1];
12962 SSize_t counter, num_entries;
12963 /* ODS-5 ups this, but we want to be consistent, so... */
12964 int max_name_len = 39;
12965 AV *in_array = (AV *)SvRV(ST(0));
12967 num_entries = av_tindex(in_array);
12969 /* All the names start with PL_. */
12970 strcpy(ultimate_name, "PL_");
12972 /* Clean up our working buffer */
12973 Zero(work_name, sizeof(work_name), char);
12975 /* Run through the entries and build up a working name */
12976 for(counter = 0; counter <= num_entries; counter++) {
12977 /* If it's not the first name then tack on a __ */
12979 my_strlcat(work_name, "__", sizeof(work_name));
12981 my_strlcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)), sizeof(work_name));
12984 /* Check to see if we actually have to bother...*/
12985 if (strlen(work_name) + 3 <= max_name_len) {
12986 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
12988 /* It's too darned big, so we need to go strip. We use the same */
12989 /* algorithm as xsubpp does. First, strip out doubled __ */
12990 char *source, *dest, last;
12993 for (source = work_name; *source; source++) {
12994 if (last == *source && last == '_') {
13000 /* Go put it back */
13001 my_strlcpy(work_name, workbuff, sizeof(work_name));
13002 /* Is it still too big? */
13003 if (strlen(work_name) + 3 > max_name_len) {
13004 /* Strip duplicate letters */
13007 for (source = work_name; *source; source++) {
13008 if (last == toupper(*source)) {
13012 last = toupper(*source);
13014 my_strlcpy(work_name, workbuff, sizeof(work_name));
13017 /* Is it *still* too big? */
13018 if (strlen(work_name) + 3 > max_name_len) {
13019 /* Too bad, we truncate */
13020 work_name[max_name_len - 2] = 0;
13022 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
13025 /* Okay, return it */
13026 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13031 hushexit_fromperl(pTHX_ CV *cv)
13036 VMSISH_HUSHED = SvTRUE(ST(0));
13038 ST(0) = boolSV(VMSISH_HUSHED);
13044 Perl_vms_start_glob
13045 (pTHX_ SV *tmpglob,
13049 struct vs_str_st *rslt;
13053 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13056 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13057 struct dsc$descriptor_vs rsdsc;
13058 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13059 unsigned long hasver = 0, isunix = 0;
13060 unsigned long int lff_flags = 0;
13062 int vms_old_glob = 1;
13064 if (!SvOK(tmpglob)) {
13065 SETERRNO(ENOENT,RMS$_FNF);
13069 vms_old_glob = !decc_filename_unix_report;
13071 #ifdef VMS_LONGNAME_SUPPORT
13072 lff_flags = LIB$M_FIL_LONG_NAMES;
13074 /* The Newx macro will not allow me to assign a smaller array
13075 * to the rslt pointer, so we will assign it to the begin char pointer
13076 * and then copy the value into the rslt pointer.
13078 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13079 rslt = (struct vs_str_st *)begin;
13081 rstr = &rslt->str[0];
13082 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13083 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13084 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13085 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13087 Newx(vmsspec, VMS_MAXRSS, char);
13089 /* We could find out if there's an explicit dev/dir or version
13090 by peeking into lib$find_file's internal context at
13091 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13092 but that's unsupported, so I don't want to do it now and
13093 have it bite someone in the future. */
13094 /* Fix-me: vms_split_path() is the only way to do this, the
13095 existing method will fail with many legal EFS or UNIX specifications
13098 cp = SvPV(tmpglob,i);
13101 if (cp[i] == ';') hasver = 1;
13102 if (cp[i] == '.') {
13103 if (sts) hasver = 1;
13106 if (cp[i] == '/') {
13107 hasdir = isunix = 1;
13110 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13116 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13117 if ((hasdir == 0) && decc_filename_unix_report) {
13121 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13122 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13123 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13129 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13130 if (!stat_sts && S_ISDIR(st.st_mode)) {
13132 const char * fname;
13135 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13136 /* path delimiter of ':>]', if so, then the old behavior has */
13137 /* obviously been specifically requested */
13139 fname = SvPVX_const(tmpglob);
13140 fname_len = strlen(fname);
13141 vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13142 if (vms_old_glob || (vms_dir != NULL)) {
13143 wilddsc.dsc$a_pointer = tovmspath_utf8(
13144 SvPVX(tmpglob),vmsspec,NULL);
13145 ok = (wilddsc.dsc$a_pointer != NULL);
13146 /* maybe passed 'foo' rather than '[.foo]', thus not
13150 /* Operate just on the directory, the special stat/fstat for */
13151 /* leaves the fileified specification in the st_devnam */
13153 wilddsc.dsc$a_pointer = st.st_devnam;
13158 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13159 ok = (wilddsc.dsc$a_pointer != NULL);
13162 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13164 /* If not extended character set, replace ? with % */
13165 /* With extended character set, ? is a wildcard single character */
13166 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13169 if (!decc_efs_charset)
13171 } else if (*cp == '%') {
13173 } else if (*cp == '*') {
13179 wv_sts = vms_split_path(
13180 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13181 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13182 &wvs_spec, &wvs_len);
13191 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13192 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13193 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13197 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13198 &dfltdsc,NULL,&rms_sts,&lff_flags);
13199 if (!$VMS_STATUS_SUCCESS(sts))
13202 /* with varying string, 1st word of buffer contains result length */
13203 rstr[rslt->length] = '\0';
13205 /* Find where all the components are */
13206 v_sts = vms_split_path
13221 /* If no version on input, truncate the version on output */
13222 if (!hasver && (vs_len > 0)) {
13229 /* In Unix report mode, remove the ".dir;1" from the name */
13230 /* if it is a real directory */
13231 if (decc_filename_unix_report && decc_efs_charset) {
13232 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13236 ret_sts = flex_lstat(rstr, &statbuf);
13237 if ((ret_sts == 0) &&
13238 S_ISDIR(statbuf.st_mode)) {
13245 /* No version & a null extension on UNIX handling */
13246 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13252 if (!decc_efs_case_preserve) {
13253 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13256 /* Find File treats a Null extension as return all extensions */
13257 /* This is contrary to Perl expectations */
13259 if (wildstar || wildquery || vms_old_glob) {
13260 /* really need to see if the returned file name matched */
13261 /* but for now will assume that it matches */
13264 /* Exact Match requested */
13265 /* How are directories handled? - like a file */
13266 if ((e_len == we_len) && (n_len == wn_len)) {
13270 t1 = strncmp(e_spec, we_spec, e_len);
13274 t1 = strncmp(n_spec, we_spec, n_len);
13285 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13289 /* Start with the name */
13292 strcat(begin,"\n");
13293 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13296 if (cxt) (void)lib$find_file_end(&cxt);
13299 /* Be POSIXish: return the input pattern when no matches */
13300 my_strlcpy(rstr, SvPVX(tmpglob), VMS_MAXRSS);
13302 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13305 if (ok && sts != RMS$_NMF &&
13306 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13309 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13311 PerlIO_close(tmpfp);
13315 PerlIO_rewind(tmpfp);
13316 IoTYPE(io) = IoTYPE_RDONLY;
13317 IoIFP(io) = fp = tmpfp;
13318 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13328 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13332 unixrealpath_fromperl(pTHX_ CV *cv)
13335 char *fspec, *rslt_spec, *rslt;
13338 if (!items || items != 1)
13339 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13341 fspec = SvPV(ST(0),n_a);
13342 if (!fspec || !*fspec) XSRETURN_UNDEF;
13344 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13345 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13347 ST(0) = sv_newmortal();
13349 sv_usepvn(ST(0),rslt,strlen(rslt));
13351 Safefree(rslt_spec);
13356 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13360 vmsrealpath_fromperl(pTHX_ CV *cv)
13363 char *fspec, *rslt_spec, *rslt;
13366 if (!items || items != 1)
13367 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13369 fspec = SvPV(ST(0),n_a);
13370 if (!fspec || !*fspec) XSRETURN_UNDEF;
13372 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13373 rslt = do_vms_realname(fspec, rslt_spec, NULL);
13375 ST(0) = sv_newmortal();
13377 sv_usepvn(ST(0),rslt,strlen(rslt));
13379 Safefree(rslt_spec);
13385 * A thin wrapper around decc$symlink to make sure we follow the
13386 * standard and do not create a symlink with a zero-length name,
13387 * and convert the target to Unix format, as the CRTL can't handle
13388 * targets in VMS format.
13390 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13392 Perl_my_symlink(pTHX_ const char *contents, const char *link_name)
13397 if (!link_name || !*link_name) {
13398 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13402 utarget = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
13403 /* An untranslatable filename should be passed through. */
13404 (void) int_tounixspec(contents, utarget, NULL);
13405 sts = symlink(utarget, link_name);
13406 PerlMem_free(utarget);
13411 #endif /* HAS_SYMLINK */
13413 int do_vms_case_tolerant(void);
13416 case_tolerant_process_fromperl(pTHX_ CV *cv)
13419 ST(0) = boolSV(do_vms_case_tolerant());
13423 #ifdef USE_ITHREADS
13426 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
13427 struct interp_intern *dst)
13429 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13431 memcpy(dst,src,sizeof(struct interp_intern));
13437 Perl_sys_intern_clear(pTHX)
13442 Perl_sys_intern_init(pTHX)
13444 unsigned int ix = RAND_MAX;
13449 MY_POSIX_EXIT = vms_posix_exit;
13452 MY_INV_RAND_MAX = 1./x;
13456 init_os_extras(void)
13459 char* file = __FILE__;
13460 if (decc_disable_to_vms_logname_translation) {
13461 no_translate_barewords = TRUE;
13463 no_translate_barewords = FALSE;
13466 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
13467 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
13468 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
13469 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
13470 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
13471 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
13472 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
13473 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
13474 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
13475 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
13476 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
13477 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
13478 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
13479 newXSproto("VMS::Filespec::case_tolerant_process",
13480 case_tolerant_process_fromperl,file,"");
13482 store_pipelocs(aTHX); /* will redo any earlier attempts */
13487 #if __CRTL_VER == 80200000
13488 /* This missed getting in to the DECC SDK for 8.2 */
13489 char *realpath(const char *file_name, char * resolved_name, ...);
13492 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
13493 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
13494 * The perl fallback routine to provide realpath() is not as efficient
13502 /* Hack, use old stat() as fastest way of getting ino_t and device */
13503 int decc$stat(const char *name, void * statbuf);
13504 #if !defined(__VAX) && __CRTL_VER >= 80200000
13505 int decc$lstat(const char *name, void * statbuf);
13507 #define decc$lstat decc$stat
13515 /* Realpath is fragile. In 8.3 it does not work if the feature
13516 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
13517 * links are implemented in RMS, not the CRTL. It also can fail if the
13518 * user does not have read/execute access to some of the directories.
13519 * So in order for Do What I Mean mode to work, if realpath() fails,
13520 * fall back to looking up the filename by the device name and FID.
13523 int vms_fid_to_name(char * outname, int outlen,
13524 const char * name, int lstat_flag, mode_t * mode)
13526 #pragma message save
13527 #pragma message disable MISALGNDSTRCT
13528 #pragma message disable MISALGNDMEM
13529 #pragma member_alignment save
13530 #pragma nomember_alignment
13533 unsigned short st_ino[3];
13534 unsigned short old_st_mode;
13535 unsigned long padl[30]; /* plenty of room */
13537 #pragma message restore
13538 #pragma member_alignment restore
13541 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13542 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13547 /* Need to follow the mostly the same rules as flex_stat_int, or we may get
13548 * unexpected answers
13551 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
13552 if (fileified == NULL)
13553 _ckvmssts_noperl(SS$_INSFMEM);
13555 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
13556 if (temp_fspec == NULL)
13557 _ckvmssts_noperl(SS$_INSFMEM);
13560 /* First need to try as a directory */
13561 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13562 if (ret_spec != NULL) {
13563 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
13564 if (ret_spec != NULL) {
13565 if (lstat_flag == 0)
13566 sts = decc$stat(fileified, &statbuf);
13568 sts = decc$lstat(fileified, &statbuf);
13572 /* Then as a VMS file spec */
13574 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
13575 if (ret_spec != NULL) {
13576 if (lstat_flag == 0) {
13577 sts = decc$stat(temp_fspec, &statbuf);
13579 sts = decc$lstat(temp_fspec, &statbuf);
13585 /* Next try - allow multiple dots with out EFS CHARSET */
13586 /* The CRTL stat() falls down hard on multi-dot filenames in unix
13587 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
13588 * enable it if it isn't already.
13590 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13591 if (!decc_efs_charset && (decc_efs_charset_index > 0))
13592 decc$feature_set_value(decc_efs_charset_index, 1, 1);
13594 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13595 if (lstat_flag == 0) {
13596 sts = decc$stat(name, &statbuf);
13598 sts = decc$lstat(name, &statbuf);
13600 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13601 if (!decc_efs_charset && (decc_efs_charset_index > 0))
13602 decc$feature_set_value(decc_efs_charset_index, 1, 0);
13607 /* and then because the Perl Unix to VMS conversion is not perfect */
13608 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
13609 /* characters from filenames so we need to try it as-is */
13611 if (lstat_flag == 0) {
13612 sts = decc$stat(name, &statbuf);
13614 sts = decc$lstat(name, &statbuf);
13621 dvidsc.dsc$a_pointer=statbuf.st_dev;
13622 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
13624 specdsc.dsc$a_pointer = outname;
13625 specdsc.dsc$w_length = outlen-1;
13627 vms_sts = lib$fid_to_name
13628 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
13629 if ($VMS_STATUS_SUCCESS(vms_sts)) {
13630 outname[specdsc.dsc$w_length] = 0;
13632 /* Return the mode */
13634 *mode = statbuf.old_st_mode;
13638 PerlMem_free(temp_fspec);
13639 PerlMem_free(fileified);
13646 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
13649 char * rslt = NULL;
13652 if (decc_posix_compliant_pathnames > 0 ) {
13653 /* realpath currently only works if posix compliant pathnames are
13654 * enabled. It may start working when they are not, but in that
13655 * case we still want the fallback behavior for backwards compatibility
13657 rslt = realpath(filespec, outbuf);
13661 if (rslt == NULL) {
13663 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13664 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13667 /* Fall back to fid_to_name */
13669 Newx(vms_spec, VMS_MAXRSS + 1, char);
13671 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
13675 /* Now need to trim the version off */
13676 sts = vms_split_path
13696 /* Trim off the version */
13697 int file_len = v_len + r_len + d_len + n_len + e_len;
13698 vms_spec[file_len] = 0;
13700 /* Trim off the .DIR if this is a directory */
13701 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13702 if (S_ISDIR(my_mode)) {
13708 /* Drop NULL extensions on UNIX file specification */
13709 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13714 /* The result is expected to be in UNIX format */
13715 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
13717 /* Downcase if input had any lower case letters and
13718 * case preservation is not in effect.
13720 if (!decc_efs_case_preserve) {
13721 for (cp = filespec; *cp; cp++)
13722 if (islower(*cp)) { haslower = 1; break; }
13724 if (haslower) __mystrtolower(rslt);
13729 /* Now for some hacks to deal with backwards and forward */
13730 /* compatibility */
13731 if (!decc_efs_charset) {
13733 /* 1. ODS-2 mode wants to do a syntax only translation */
13734 rslt = int_rmsexpand(filespec, outbuf,
13735 NULL, 0, NULL, utf8_fl);
13738 if (decc_filename_unix_report) {
13740 char * vms_dir_name;
13743 /* 2. ODS-5 / UNIX report mode should return a failure */
13744 /* if the parent directory also does not exist */
13745 /* Otherwise, get the real path for the parent */
13746 /* and add the child to it. */
13748 /* basename / dirname only available for VMS 7.0+ */
13749 /* So we may need to implement them as common routines */
13751 Newx(dir_name, VMS_MAXRSS + 1, char);
13752 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
13753 dir_name[0] = '\0';
13756 /* First try a VMS parse */
13757 sts = vms_split_path
13775 int dir_len = v_len + r_len + d_len + n_len;
13777 memcpy(dir_name, filespec, dir_len);
13778 dir_name[dir_len] = '\0';
13779 file_name = (char *)&filespec[dir_len + 1];
13782 /* This must be UNIX */
13785 tchar = strrchr(filespec, '/');
13787 if (tchar != NULL) {
13788 int dir_len = tchar - filespec;
13789 memcpy(dir_name, filespec, dir_len);
13790 dir_name[dir_len] = '\0';
13791 file_name = (char *) &filespec[dir_len + 1];
13795 /* Dir name is defaulted */
13796 if (dir_name[0] == 0) {
13798 dir_name[1] = '\0';
13801 /* Need realpath for the directory */
13802 sts = vms_fid_to_name(vms_dir_name,
13804 dir_name, 0, NULL);
13807 /* Now need to pathify it. */
13808 char *tdir = int_pathify_dirspec(vms_dir_name,
13811 /* And now add the original filespec to it */
13812 if (file_name != NULL) {
13813 my_strlcat(outbuf, file_name, VMS_MAXRSS);
13817 Safefree(vms_dir_name);
13818 Safefree(dir_name);
13822 Safefree(vms_spec);
13828 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
13831 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13832 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13834 /* Fall back to fid_to_name */
13836 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
13843 /* Now need to trim the version off */
13844 sts = vms_split_path
13864 /* Trim off the version */
13865 int file_len = v_len + r_len + d_len + n_len + e_len;
13866 outbuf[file_len] = 0;
13868 /* Downcase if input had any lower case letters and
13869 * case preservation is not in effect.
13871 if (!decc_efs_case_preserve) {
13872 for (cp = filespec; *cp; cp++)
13873 if (islower(*cp)) { haslower = 1; break; }
13875 if (haslower) __mystrtolower(outbuf);
13884 /* External entry points */
13885 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13886 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
13888 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13889 { return do_vms_realname(filespec, outbuf, utf8_fl); }
13891 /* case_tolerant */
13893 /*{{{int do_vms_case_tolerant(void)*/
13894 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
13895 * controlled by a process setting.
13897 int do_vms_case_tolerant(void)
13899 return vms_process_case_tolerant;
13902 /* External entry points */
13903 #if __CRTL_VER >= 70301000 && !defined(__VAX)
13904 int Perl_vms_case_tolerant(void)
13905 { return do_vms_case_tolerant(); }
13907 int Perl_vms_case_tolerant(void)
13908 { return vms_process_case_tolerant; }
13912 /* Start of DECC RTL Feature handling */
13914 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13917 set_feature_default(const char *name, int value)
13923 /* If the feature has been explicitly disabled in the environment,
13924 * then don't enable it here.
13927 status = simple_trnlnm(name, val_str, sizeof(val_str));
13929 val_str[0] = _toupper(val_str[0]);
13930 if (val_str[0] == 'D' || val_str[0] == '0' || val_str[0] == 'F')
13935 index = decc$feature_get_index(name);
13937 status = decc$feature_set_value(index, 1, value);
13938 if (index == -1 || (status == -1)) {
13942 status = decc$feature_get_value(index, 1);
13943 if (status != value) {
13947 /* Various things may check for an environment setting
13948 * rather than the feature directly, so set that too.
13950 vmssetuserlnm(name, value ? "ENABLE" : "DISABLE");
13957 /* C RTL Feature settings */
13959 #if defined(__DECC) || defined(__DECCXX)
13966 vmsperl_set_features(void)
13971 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
13972 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
13973 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
13974 unsigned long case_perm;
13975 unsigned long case_image;
13978 /* Allow an exception to bring Perl into the VMS debugger */
13979 vms_debug_on_exception = 0;
13980 status = simple_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
13982 val_str[0] = _toupper(val_str[0]);
13983 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13984 vms_debug_on_exception = 1;
13986 vms_debug_on_exception = 0;
13989 /* Debug unix/vms file translation routines */
13990 vms_debug_fileify = 0;
13991 status = simple_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
13993 val_str[0] = _toupper(val_str[0]);
13994 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13995 vms_debug_fileify = 1;
13997 vms_debug_fileify = 0;
14001 /* Historically PERL has been doing vmsify / stat differently than */
14002 /* the CRTL. In particular, under some conditions the CRTL will */
14003 /* remove some illegal characters like spaces from filenames */
14004 /* resulting in some differences. The stat()/lstat() wrapper has */
14005 /* been reporting such file names as invalid and fails to stat them */
14006 /* fixing this bug so that stat()/lstat() accept these like the */
14007 /* CRTL does will result in several tests failing. */
14008 /* This should really be fixed, but for now, set up a feature to */
14009 /* enable it so that the impact can be studied. */
14010 vms_bug_stat_filename = 0;
14011 status = simple_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
14013 val_str[0] = _toupper(val_str[0]);
14014 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14015 vms_bug_stat_filename = 1;
14017 vms_bug_stat_filename = 0;
14021 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
14022 vms_vtf7_filenames = 0;
14023 status = simple_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14025 val_str[0] = _toupper(val_str[0]);
14026 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14027 vms_vtf7_filenames = 1;
14029 vms_vtf7_filenames = 0;
14032 /* unlink all versions on unlink() or rename() */
14033 vms_unlink_all_versions = 0;
14034 status = simple_trnlnm("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14036 val_str[0] = _toupper(val_str[0]);
14037 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14038 vms_unlink_all_versions = 1;
14040 vms_unlink_all_versions = 0;
14043 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14044 /* Detect running under GNV Bash or other UNIX like shell */
14045 gnv_unix_shell = 0;
14046 status = simple_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14048 gnv_unix_shell = 1;
14049 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14050 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14051 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14052 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
14053 vms_unlink_all_versions = 1;
14054 vms_posix_exit = 1;
14055 /* Reverse default ordering of PERL_ENV_TABLES. */
14056 defenv[0] = &crtlenvdsc;
14057 defenv[1] = &fildevdsc;
14059 /* Some reasonable defaults that are not CRTL defaults */
14060 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14061 set_feature_default("DECC$ARGV_PARSE_STYLE", 1); /* Requires extended parse. */
14062 set_feature_default("DECC$EFS_CHARSET", 1);
14065 /* hacks to see if known bugs are still present for testing */
14067 /* PCP mode requires creating /dev/null special device file */
14068 decc_bug_devnull = 0;
14069 status = simple_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14071 val_str[0] = _toupper(val_str[0]);
14072 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14073 decc_bug_devnull = 1;
14075 decc_bug_devnull = 0;
14078 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14079 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14081 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14082 if (decc_disable_to_vms_logname_translation < 0)
14083 decc_disable_to_vms_logname_translation = 0;
14086 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14088 decc_efs_case_preserve = decc$feature_get_value(s, 1);
14089 if (decc_efs_case_preserve < 0)
14090 decc_efs_case_preserve = 0;
14093 s = decc$feature_get_index("DECC$EFS_CHARSET");
14094 decc_efs_charset_index = s;
14096 decc_efs_charset = decc$feature_get_value(s, 1);
14097 if (decc_efs_charset < 0)
14098 decc_efs_charset = 0;
14101 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14103 decc_filename_unix_report = decc$feature_get_value(s, 1);
14104 if (decc_filename_unix_report > 0) {
14105 decc_filename_unix_report = 1;
14106 vms_posix_exit = 1;
14109 decc_filename_unix_report = 0;
14112 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14114 decc_filename_unix_only = decc$feature_get_value(s, 1);
14115 if (decc_filename_unix_only > 0) {
14116 decc_filename_unix_only = 1;
14119 decc_filename_unix_only = 0;
14123 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14125 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14126 if (decc_filename_unix_no_version < 0)
14127 decc_filename_unix_no_version = 0;
14130 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14132 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14133 if (decc_readdir_dropdotnotype < 0)
14134 decc_readdir_dropdotnotype = 0;
14137 #if __CRTL_VER >= 80200000
14138 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14140 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14141 if (decc_posix_compliant_pathnames < 0)
14142 decc_posix_compliant_pathnames = 0;
14143 if (decc_posix_compliant_pathnames > 4)
14144 decc_posix_compliant_pathnames = 0;
14149 status = simple_trnlnm
14150 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14152 val_str[0] = _toupper(val_str[0]);
14153 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14154 decc_disable_to_vms_logname_translation = 1;
14159 status = simple_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14161 val_str[0] = _toupper(val_str[0]);
14162 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14163 decc_efs_case_preserve = 1;
14168 status = simple_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14170 val_str[0] = _toupper(val_str[0]);
14171 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14172 decc_filename_unix_report = 1;
14175 status = simple_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14177 val_str[0] = _toupper(val_str[0]);
14178 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14179 decc_filename_unix_only = 1;
14180 decc_filename_unix_report = 1;
14183 status = simple_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14185 val_str[0] = _toupper(val_str[0]);
14186 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14187 decc_filename_unix_no_version = 1;
14190 status = simple_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14192 val_str[0] = _toupper(val_str[0]);
14193 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14194 decc_readdir_dropdotnotype = 1;
14199 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14201 /* Report true case tolerance */
14202 /*----------------------------*/
14203 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14204 if (!$VMS_STATUS_SUCCESS(status))
14205 case_perm = PPROP$K_CASE_BLIND;
14206 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14207 if (!$VMS_STATUS_SUCCESS(status))
14208 case_image = PPROP$K_CASE_BLIND;
14209 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14210 (case_image == PPROP$K_CASE_SENSITIVE))
14211 vms_process_case_tolerant = 0;
14215 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
14216 /* for strict backward compatibility */
14217 status = simple_trnlnm("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14219 val_str[0] = _toupper(val_str[0]);
14220 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14221 vms_posix_exit = 1;
14223 vms_posix_exit = 0;
14227 /* Use 32-bit pointers because that's what the image activator
14228 * assumes for the LIB$INITIALZE psect.
14230 #if __INITIAL_POINTER_SIZE
14231 #pragma pointer_size save
14232 #pragma pointer_size 32
14235 /* Create a reference to the LIB$INITIALIZE function. */
14236 extern void LIB$INITIALIZE(void);
14237 extern void (*vmsperl_unused_global_1)(void) = LIB$INITIALIZE;
14239 /* Create an array of pointers to the init functions in the special
14240 * LIB$INITIALIZE section. In our case, the array only has one entry.
14242 #pragma extern_model save
14243 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic,gbl,nowrt,noshr,long
14244 extern void (* const vmsperl_unused_global_2[])() =
14246 vmsperl_set_features,
14248 #pragma extern_model restore
14250 #if __INITIAL_POINTER_SIZE
14251 #pragma pointer_size restore
14258 #endif /* defined(__DECC) || defined(__DECCXX) */