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 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
883 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
884 struct dsc$descriptor_s **tabvec, unsigned long int flags)
887 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
888 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
889 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
891 unsigned char acmode;
892 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
893 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
894 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
895 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
897 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
898 #if defined(PERL_IMPLICIT_CONTEXT)
901 aTHX = PERL_GET_INTERP;
907 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
908 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
910 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
911 *cp2 = _toupper(*cp1);
912 if (cp1 - lnm > LNM$C_NAMLENGTH) {
913 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
917 lnmdsc.dsc$w_length = cp1 - lnm;
918 lnmdsc.dsc$a_pointer = uplnm;
919 uplnm[lnmdsc.dsc$w_length] = '\0';
920 secure = flags & PERL__TRNENV_SECURE;
921 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
922 if (!tabvec || !*tabvec) tabvec = env_tables;
924 for (curtab = 0; tabvec[curtab]; curtab++) {
925 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
926 if (!ivenv && !secure) {
931 #if defined(PERL_IMPLICIT_CONTEXT)
934 "Can't read CRTL environ\n");
937 Perl_warn(aTHX_ "Can't read CRTL environ\n");
940 retsts = SS$_NOLOGNAM;
941 for (i = 0; environ[i]; i++) {
942 if ((eq = strchr(environ[i],'=')) &&
943 lnmdsc.dsc$w_length == (eq - environ[i]) &&
944 !strncmp(environ[i],uplnm,eq - environ[i])) {
946 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
947 if (!eqvlen) continue;
952 if (retsts != SS$_NOLOGNAM) break;
955 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
956 !str$case_blind_compare(&tmpdsc,&clisym)) {
957 if (!ivsym && !secure) {
958 unsigned short int deflen = LNM$C_NAMLENGTH;
959 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
960 /* dynamic dsc to accommodate possible long value */
961 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
962 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
964 if (eqvlen > MAX_DCL_SYMBOL) {
965 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
966 eqvlen = MAX_DCL_SYMBOL;
967 /* Special hack--we might be called before the interpreter's */
968 /* fully initialized, in which case either thr or PL_curcop */
969 /* might be bogus. We have to check, since ckWARN needs them */
970 /* both to be valid if running threaded */
971 #if defined(PERL_IMPLICIT_CONTEXT)
974 "Value of CLI symbol \"%s\" too long",lnm);
977 if (ckWARN(WARN_MISC)) {
978 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
981 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
983 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
984 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
985 if (retsts == LIB$_NOSUCHSYM) continue;
990 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
991 midx = my_maxidx(lnm);
992 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
993 lnmlst[1].bufadr = cp2;
995 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
996 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
997 if (retsts == SS$_NOLOGNAM) break;
998 /* PPFs have a prefix */
1001 *((int *)uplnm) == *((int *)"SYS$") &&
1003 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
1004 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
1005 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
1006 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
1007 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
1008 memmove(eqv,eqv+4,eqvlen-4);
1014 if ((retsts == SS$_IVLOGNAM) ||
1015 (retsts == SS$_NOLOGNAM)) { continue; }
1018 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1019 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1020 if (retsts == SS$_NOLOGNAM) continue;
1023 eqvlen = strlen(eqv);
1027 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1028 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1029 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
1030 retsts == SS$_NOLOGNAM) {
1031 set_errno(EINVAL); set_vaxc_errno(retsts);
1033 else _ckvmssts_noperl(retsts);
1035 } /* end of vmstrnenv */
1038 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1039 /* Define as a function so we can access statics. */
1040 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1044 #if defined(PERL_IMPLICIT_CONTEXT)
1047 #ifdef SECURE_INTERNAL_GETENV
1048 flags = (PL_curinterp ? TAINTING_get : will_taint) ?
1049 PERL__TRNENV_SECURE : 0;
1052 return vmstrnenv(lnm, eqv, idx, fildev, flags);
1057 * Note: Uses Perl temp to store result so char * can be returned to
1058 * caller; this pointer will be invalidated at next Perl statement
1060 * We define this as a function rather than a macro in terms of my_getenv_len()
1061 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1064 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1066 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1069 static char *__my_getenv_eqv = NULL;
1070 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1071 unsigned long int idx = 0;
1072 int success, secure, saverr, savvmserr;
1076 midx = my_maxidx(lnm) + 1;
1078 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1079 /* Set up a temporary buffer for the return value; Perl will
1080 * clean it up at the next statement transition */
1081 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1082 if (!tmpsv) return NULL;
1086 /* Assume no interpreter ==> single thread */
1087 if (__my_getenv_eqv != NULL) {
1088 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1091 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1093 eqv = __my_getenv_eqv;
1096 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1097 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1099 getcwd(eqv,LNM$C_NAMLENGTH);
1103 /* Get rid of "000000/ in rooted filespecs */
1106 zeros = strstr(eqv, "/000000/");
1107 if (zeros != NULL) {
1109 mlen = len - (zeros - eqv) - 7;
1110 memmove(zeros, &zeros[7], mlen);
1118 /* Impose security constraints only if tainting */
1120 /* Impose security constraints only if tainting */
1121 secure = PL_curinterp ? TAINTING_get : will_taint;
1122 saverr = errno; savvmserr = vaxc$errno;
1129 #ifdef SECURE_INTERNAL_GETENV
1130 secure ? PERL__TRNENV_SECURE : 0
1136 /* For the getenv interface we combine all the equivalence names
1137 * of a search list logical into one value to acquire a maximum
1138 * value length of 255*128 (assuming %ENV is using logicals).
1140 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1142 /* If the name contains a semicolon-delimited index, parse it
1143 * off and make sure we only retrieve the equivalence name for
1145 if ((cp2 = strchr(lnm,';')) != NULL) {
1146 my_strlcpy(uplnm, lnm, cp2 - lnm + 1);
1147 idx = strtoul(cp2+1,NULL,0);
1149 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1152 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1154 /* Discard NOLOGNAM on internal calls since we're often looking
1155 * for an optional name, and this "error" often shows up as the
1156 * (bogus) exit status for a die() call later on. */
1157 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1158 return success ? eqv : NULL;
1161 } /* end of my_getenv() */
1165 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1167 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1171 unsigned long idx = 0;
1173 static char *__my_getenv_len_eqv = NULL;
1174 int secure, saverr, savvmserr;
1177 midx = my_maxidx(lnm) + 1;
1179 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1180 /* Set up a temporary buffer for the return value; Perl will
1181 * clean it up at the next statement transition */
1182 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1183 if (!tmpsv) return NULL;
1187 /* Assume no interpreter ==> single thread */
1188 if (__my_getenv_len_eqv != NULL) {
1189 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1192 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1194 buf = __my_getenv_len_eqv;
1197 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1198 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1201 getcwd(buf,LNM$C_NAMLENGTH);
1204 /* Get rid of "000000/ in rooted filespecs */
1206 zeros = strstr(buf, "/000000/");
1207 if (zeros != NULL) {
1209 mlen = *len - (zeros - buf) - 7;
1210 memmove(zeros, &zeros[7], mlen);
1219 /* Impose security constraints only if tainting */
1220 secure = PL_curinterp ? TAINTING_get : will_taint;
1221 saverr = errno; savvmserr = vaxc$errno;
1228 #ifdef SECURE_INTERNAL_GETENV
1229 secure ? PERL__TRNENV_SECURE : 0
1235 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1237 if ((cp2 = strchr(lnm,';')) != NULL) {
1238 my_strlcpy(buf, lnm, cp2 - lnm + 1);
1239 idx = strtoul(cp2+1,NULL,0);
1241 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1244 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1246 /* Get rid of "000000/ in rooted filespecs */
1249 zeros = strstr(buf, "/000000/");
1250 if (zeros != NULL) {
1252 mlen = *len - (zeros - buf) - 7;
1253 memmove(zeros, &zeros[7], mlen);
1259 /* Discard NOLOGNAM on internal calls since we're often looking
1260 * for an optional name, and this "error" often shows up as the
1261 * (bogus) exit status for a die() call later on. */
1262 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1263 return *len ? buf : NULL;
1266 } /* end of my_getenv_len() */
1269 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1271 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1273 /*{{{ void prime_env_iter() */
1275 prime_env_iter(void)
1276 /* Fill the %ENV associative array with all logical names we can
1277 * find, in preparation for iterating over it.
1280 static int primed = 0;
1281 HV *seenhv = NULL, *envhv;
1283 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1284 unsigned short int chan;
1285 #ifndef CLI$M_TRUSTED
1286 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1288 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1289 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0;
1291 bool have_sym = FALSE, have_lnm = FALSE;
1292 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1293 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1294 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1295 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1296 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1297 #if defined(PERL_IMPLICIT_CONTEXT)
1300 #if defined(USE_ITHREADS)
1301 static perl_mutex primenv_mutex;
1302 MUTEX_INIT(&primenv_mutex);
1305 #if defined(PERL_IMPLICIT_CONTEXT)
1306 /* We jump through these hoops because we can be called at */
1307 /* platform-specific initialization time, which is before anything is */
1308 /* set up--we can't even do a plain dTHX since that relies on the */
1309 /* interpreter structure to be initialized */
1311 aTHX = PERL_GET_INTERP;
1313 /* we never get here because the NULL pointer will cause the */
1314 /* several of the routines called by this routine to access violate */
1316 /* This routine is only called by hv.c/hv_iterinit which has a */
1317 /* context, so the real fix may be to pass it through instead of */
1318 /* the hoops above */
1323 if (primed || !PL_envgv) return;
1324 MUTEX_LOCK(&primenv_mutex);
1325 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1326 envhv = GvHVn(PL_envgv);
1327 /* Perform a dummy fetch as an lval to insure that the hash table is
1328 * set up. Otherwise, the hv_store() will turn into a nullop. */
1329 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1331 for (i = 0; env_tables[i]; i++) {
1332 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1333 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1334 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1336 if (have_sym || have_lnm) {
1337 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1338 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1339 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1340 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1343 for (i--; i >= 0; i--) {
1344 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1347 for (j = 0; environ[j]; j++) {
1348 if (!(start = strchr(environ[j],'='))) {
1349 if (ckWARN(WARN_INTERNAL))
1350 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1354 sv = newSVpv(start,0);
1356 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1361 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1362 !str$case_blind_compare(&tmpdsc,&clisym)) {
1363 my_strlcpy(cmd, "Show Symbol/Global *", sizeof(cmd));
1364 cmddsc.dsc$w_length = 20;
1365 if (env_tables[i]->dsc$w_length == 12 &&
1366 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1367 !str$case_blind_compare(&tmpdsc,&local)) my_strlcpy(cmd+12, "Local *", sizeof(cmd)-12);
1368 flags = defflags | CLI$M_NOLOGNAM;
1371 my_strlcpy(cmd, "Show Logical *", sizeof(cmd));
1372 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1373 my_strlcat(cmd," /Table=", sizeof(cmd));
1374 cmddsc.dsc$w_length = my_strlcat(cmd, env_tables[i]->dsc$a_pointer, env_tables[i]->dsc$w_length + 1);
1376 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1377 flags = defflags | CLI$M_NOCLISYM;
1380 /* Create a new subprocess to execute each command, to exclude the
1381 * remote possibility that someone could subvert a mbx or file used
1382 * to write multiple commands to a single subprocess.
1385 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1386 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1387 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1388 defflags &= ~CLI$M_TRUSTED;
1389 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1391 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1392 if (seenhv) SvREFCNT_dec(seenhv);
1395 char *cp1, *cp2, *key;
1396 unsigned long int sts, iosb[2], retlen, keylen;
1399 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1400 if (sts & 1) sts = iosb[0] & 0xffff;
1401 if (sts == SS$_ENDOFFILE) {
1403 while (substs == 0) { sys$hiber(); wakect++;}
1404 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1409 retlen = iosb[0] >> 16;
1410 if (!retlen) continue; /* blank line */
1412 if (iosb[1] != subpid) {
1414 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1418 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1419 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1421 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1422 if (*cp1 == '(' || /* Logical name table name */
1423 *cp1 == '=' /* Next eqv of searchlist */) continue;
1424 if (*cp1 == '"') cp1++;
1425 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1426 key = cp1; keylen = cp2 - cp1;
1427 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1428 while (*cp2 && *cp2 != '=') cp2++;
1429 while (*cp2 && *cp2 == '=') cp2++;
1430 while (*cp2 && *cp2 == ' ') cp2++;
1431 if (*cp2 == '"') { /* String translation; may embed "" */
1432 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1433 cp2++; cp1--; /* Skip "" surrounding translation */
1435 else { /* Numeric translation */
1436 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1437 cp1--; /* stop on last non-space char */
1439 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1440 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1443 PERL_HASH(hash,key,keylen);
1445 if (cp1 == cp2 && *cp2 == '.') {
1446 /* A single dot usually means an unprintable character, such as a null
1447 * to indicate a zero-length value. Get the actual value to make sure.
1449 char lnm[LNM$C_NAMLENGTH+1];
1450 char eqv[MAX_DCL_SYMBOL+1];
1452 strncpy(lnm, key, keylen);
1453 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1454 sv = newSVpvn(eqv, strlen(eqv));
1457 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1461 hv_store(envhv,key,keylen,sv,hash);
1462 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1464 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1465 /* get the PPFs for this process, not the subprocess */
1466 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1467 char eqv[LNM$C_NAMLENGTH+1];
1469 for (i = 0; ppfs[i]; i++) {
1470 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1471 sv = newSVpv(eqv,trnlen);
1473 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1478 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1479 if (buf) Safefree(buf);
1480 if (seenhv) SvREFCNT_dec(seenhv);
1481 MUTEX_UNLOCK(&primenv_mutex);
1484 } /* end of prime_env_iter */
1488 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1489 /* Define or delete an element in the same "environment" as
1490 * vmstrnenv(). If an element is to be deleted, it's removed from
1491 * the first place it's found. If it's to be set, it's set in the
1492 * place designated by the first element of the table vector.
1493 * Like setenv() returns 0 for success, non-zero on error.
1496 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1499 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1500 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1502 unsigned long int retsts, usermode = PSL$C_USER;
1503 struct itmlst_3 *ile, *ilist;
1504 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1505 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1506 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1507 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1508 $DESCRIPTOR(local,"_LOCAL");
1511 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1512 return SS$_IVLOGNAM;
1515 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1516 *cp2 = _toupper(*cp1);
1517 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1518 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1519 return SS$_IVLOGNAM;
1522 lnmdsc.dsc$w_length = cp1 - lnm;
1523 if (!tabvec || !*tabvec) tabvec = env_tables;
1525 if (!eqv) { /* we're deleting n element */
1526 for (curtab = 0; tabvec[curtab]; curtab++) {
1527 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1529 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1530 if ((cp1 = strchr(environ[i],'=')) &&
1531 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1532 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1534 return setenv(lnm,"",1) ? vaxc$errno : 0;
1537 ivenv = 1; retsts = SS$_NOLOGNAM;
1539 if (ckWARN(WARN_INTERNAL))
1540 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1541 ivenv = 1; retsts = SS$_NOSUCHPGM;
1547 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1548 !str$case_blind_compare(&tmpdsc,&clisym)) {
1549 unsigned int symtype;
1550 if (tabvec[curtab]->dsc$w_length == 12 &&
1551 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1552 !str$case_blind_compare(&tmpdsc,&local))
1553 symtype = LIB$K_CLI_LOCAL_SYM;
1554 else symtype = LIB$K_CLI_GLOBAL_SYM;
1555 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1556 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1557 if (retsts == LIB$_NOSUCHSYM) continue;
1561 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1562 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1563 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1564 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1565 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1569 else { /* we're defining a value */
1570 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1572 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1574 if (ckWARN(WARN_INTERNAL))
1575 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1576 retsts = SS$_NOSUCHPGM;
1580 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1581 eqvdsc.dsc$w_length = strlen(eqv);
1582 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1583 !str$case_blind_compare(&tmpdsc,&clisym)) {
1584 unsigned int symtype;
1585 if (tabvec[0]->dsc$w_length == 12 &&
1586 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1587 !str$case_blind_compare(&tmpdsc,&local))
1588 symtype = LIB$K_CLI_LOCAL_SYM;
1589 else symtype = LIB$K_CLI_GLOBAL_SYM;
1590 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1593 if (!*eqv) eqvdsc.dsc$w_length = 1;
1594 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1596 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1597 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1598 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1599 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1600 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1601 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1604 Newx(ilist,nseg+1,struct itmlst_3);
1607 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1610 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1612 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1613 ile->itmcode = LNM$_STRING;
1615 if ((j+1) == nseg) {
1616 ile->buflen = strlen(c);
1617 /* in case we are truncating one that's too long */
1618 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1621 ile->buflen = LNM$C_NAMLENGTH;
1625 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1629 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1634 if (!(retsts & 1)) {
1636 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1637 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1638 set_errno(EVMSERR); break;
1639 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1640 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1641 set_errno(EINVAL); break;
1643 set_errno(EACCES); break;
1648 set_vaxc_errno(retsts);
1649 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1652 /* We reset error values on success because Perl does an hv_fetch()
1653 * before each hv_store(), and if the thing we're setting didn't
1654 * previously exist, we've got a leftover error message. (Of course,
1655 * this fails in the face of
1656 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1657 * in that the error reported in $! isn't spurious,
1658 * but it's right more often than not.)
1660 set_errno(0); set_vaxc_errno(retsts);
1664 } /* end of vmssetenv() */
1667 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1668 /* This has to be a function since there's a prototype for it in proto.h */
1670 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1673 int len = strlen(lnm);
1677 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1678 if (!strcmp(uplnm,"DEFAULT")) {
1679 if (eqv && *eqv) my_chdir(eqv);
1684 (void) vmssetenv(lnm,eqv,NULL);
1688 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1690 * sets a user-mode logical in the process logical name table
1691 * used for redirection of sys$error
1694 Perl_vmssetuserlnm(const char *name, const char *eqv)
1696 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1697 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1698 unsigned long int iss, attr = LNM$M_CONFINE;
1699 unsigned char acmode = PSL$C_USER;
1700 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1702 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1703 d_name.dsc$w_length = strlen(name);
1705 lnmlst[0].buflen = strlen(eqv);
1706 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1708 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1709 if (!(iss&1)) lib$signal(iss);
1714 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1715 /* my_crypt - VMS password hashing
1716 * my_crypt() provides an interface compatible with the Unix crypt()
1717 * C library function, and uses sys$hash_password() to perform VMS
1718 * password hashing. The quadword hashed password value is returned
1719 * as a NUL-terminated 8 character string. my_crypt() does not change
1720 * the case of its string arguments; in order to match the behavior
1721 * of LOGINOUT et al., alphabetic characters in both arguments must
1722 * be upcased by the caller.
1724 * - fix me to call ACM services when available
1727 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1729 # ifndef UAI$C_PREFERRED_ALGORITHM
1730 # define UAI$C_PREFERRED_ALGORITHM 127
1732 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1733 unsigned short int salt = 0;
1734 unsigned long int sts;
1736 unsigned short int dsc$w_length;
1737 unsigned char dsc$b_type;
1738 unsigned char dsc$b_class;
1739 const char * dsc$a_pointer;
1740 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1741 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1742 struct itmlst_3 uailst[3] = {
1743 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1744 { sizeof salt, UAI$_SALT, &salt, 0},
1745 { 0, 0, NULL, NULL}};
1746 static char hash[9];
1748 usrdsc.dsc$w_length = strlen(usrname);
1749 usrdsc.dsc$a_pointer = usrname;
1750 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1752 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1756 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1761 set_vaxc_errno(sts);
1762 if (sts != RMS$_RNF) return NULL;
1765 txtdsc.dsc$w_length = strlen(textpasswd);
1766 txtdsc.dsc$a_pointer = textpasswd;
1767 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1768 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1771 return (char *) hash;
1773 } /* end of my_crypt() */
1777 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1778 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1779 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1781 /* fixup barenames that are directories for internal use.
1782 * There have been problems with the consistent handling of UNIX
1783 * style directory names when routines are presented with a name that
1784 * has no directory delimiters at all. So this routine will eventually
1787 static char * fixup_bare_dirnames(const char * name)
1789 if (decc_disable_to_vms_logname_translation) {
1795 /* 8.3, remove() is now broken on symbolic links */
1796 static int rms_erase(const char * vmsname);
1800 * A little hack to get around a bug in some implementation of remove()
1801 * that do not know how to delete a directory
1803 * Delete any file to which user has control access, regardless of whether
1804 * delete access is explicitly allowed.
1805 * Limitations: User must have write access to parent directory.
1806 * Does not block signals or ASTs; if interrupted in midstream
1807 * may leave file with an altered ACL.
1810 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1812 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1816 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1817 unsigned long int cxt = 0, aclsts, fndsts;
1819 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1821 unsigned char myace$b_length;
1822 unsigned char myace$b_type;
1823 unsigned short int myace$w_flags;
1824 unsigned long int myace$l_access;
1825 unsigned long int myace$l_ident;
1826 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1827 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1828 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1830 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1831 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1832 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1833 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1834 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1835 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1837 /* Expand the input spec using RMS, since the CRTL remove() and
1838 * system services won't do this by themselves, so we may miss
1839 * a file "hiding" behind a logical name or search list. */
1840 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
1841 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1843 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
1845 PerlMem_free(vmsname);
1849 /* Erase the file */
1850 rmsts = rms_erase(vmsname);
1852 /* Did it succeed */
1853 if ($VMS_STATUS_SUCCESS(rmsts)) {
1854 PerlMem_free(vmsname);
1858 /* If not, can changing protections help? */
1859 if (rmsts != RMS$_PRV) {
1860 set_vaxc_errno(rmsts);
1861 PerlMem_free(vmsname);
1865 /* No, so we get our own UIC to use as a rights identifier,
1866 * and the insert an ACE at the head of the ACL which allows us
1867 * to delete the file.
1869 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1870 fildsc.dsc$w_length = strlen(vmsname);
1871 fildsc.dsc$a_pointer = vmsname;
1873 newace.myace$l_ident = oldace.myace$l_ident;
1875 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1877 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1878 set_errno(ENOENT); break;
1880 set_errno(ENOTDIR); break;
1882 set_errno(ENODEV); break;
1883 case RMS$_SYN: case SS$_INVFILFOROP:
1884 set_errno(EINVAL); break;
1886 set_errno(EACCES); break;
1888 _ckvmssts_noperl(aclsts);
1890 set_vaxc_errno(aclsts);
1891 PerlMem_free(vmsname);
1894 /* Grab any existing ACEs with this identifier in case we fail */
1895 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1896 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1897 || fndsts == SS$_NOMOREACE ) {
1898 /* Add the new ACE . . . */
1899 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1902 rmsts = rms_erase(vmsname);
1903 if ($VMS_STATUS_SUCCESS(rmsts)) {
1908 /* We blew it - dir with files in it, no write priv for
1909 * parent directory, etc. Put things back the way they were. */
1910 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1913 addlst[0].bufadr = &oldace;
1914 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1921 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1922 /* We just deleted it, so of course it's not there. Some versions of
1923 * VMS seem to return success on the unlock operation anyhow (after all
1924 * the unlock is successful), but others don't.
1926 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1927 if (aclsts & 1) aclsts = fndsts;
1928 if (!(aclsts & 1)) {
1930 set_vaxc_errno(aclsts);
1933 PerlMem_free(vmsname);
1936 } /* end of kill_file() */
1940 /*{{{int do_rmdir(char *name)*/
1942 Perl_do_rmdir(pTHX_ const char *name)
1948 /* lstat returns a VMS fileified specification of the name */
1949 /* that is looked up, and also lets verifies that this is a directory */
1951 retval = flex_lstat(name, &st);
1955 /* Due to a historical feature, flex_stat/lstat can not see some */
1956 /* Unix format file names that the rest of the CRTL can see */
1957 /* Fixing that feature will cause some perl tests to fail */
1958 /* So try this one more time. */
1960 retval = lstat(name, &st.crtl_stat);
1964 /* force it to a file spec for the kill file to work. */
1965 ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
1966 if (ret_spec == NULL) {
1972 if (!S_ISDIR(st.st_mode)) {
1977 dirfile = st.st_devnam;
1979 /* It may be possible for flex_stat to find a file and vmsify() to */
1980 /* fail with ODS-2 specifications. mp_do_kill_file can not deal */
1981 /* with that case, so fail it */
1982 if (dirfile[0] == 0) {
1987 retval = mp_do_kill_file(aTHX_ dirfile, 1);
1992 } /* end of do_rmdir */
1996 * Delete any file to which user has control access, regardless of whether
1997 * delete access is explicitly allowed.
1998 * Limitations: User must have write access to parent directory.
1999 * Does not block signals or ASTs; if interrupted in midstream
2000 * may leave file with an altered ACL.
2003 /*{{{int kill_file(char *name)*/
2005 Perl_kill_file(pTHX_ const char *name)
2011 /* Convert the filename to VMS format and see if it is a directory */
2012 /* flex_lstat returns a vmsified file specification */
2013 rmsts = flex_lstat(name, &st);
2016 /* Due to a historical feature, flex_stat/lstat can not see some */
2017 /* Unix format file names that the rest of the CRTL can see when */
2018 /* ODS-2 file specifications are in use. */
2019 /* Fixing that feature will cause some perl tests to fail */
2020 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2022 vmsfile = (char *) name; /* cast ok */
2025 vmsfile = st.st_devnam;
2026 if (vmsfile[0] == 0) {
2027 /* It may be possible for flex_stat to find a file and vmsify() */
2028 /* to fail with ODS-2 specifications. mp_do_kill_file can not */
2029 /* deal with that case, so fail it */
2035 /* Remove() is allowed to delete directories, according to the X/Open
2037 * This may need special handling to work with the ACL hacks.
2039 if (S_ISDIR(st.st_mode)) {
2040 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2044 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2046 /* Need to delete all versions ? */
2047 if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2050 /* Just use lstat() here as do not need st_dev */
2051 /* and we know that the file is in VMS format or that */
2052 /* because of a historical bug, flex_stat can not see the file */
2053 while (lstat(vmsfile, (stat_t *)&st) == 0) {
2054 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2059 /* Make sure that we do not loop forever */
2070 } /* end of kill_file() */
2074 /*{{{int my_mkdir(char *,Mode_t)*/
2076 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2078 STRLEN dirlen = strlen(dir);
2080 /* zero length string sometimes gives ACCVIO */
2081 if (dirlen == 0) return -1;
2083 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2084 * null file name/type. However, it's commonplace under Unix,
2085 * so we'll allow it for a gain in portability.
2087 if (dir[dirlen-1] == '/') {
2088 char *newdir = savepvn(dir,dirlen-1);
2089 int ret = mkdir(newdir,mode);
2093 else return mkdir(dir,mode);
2094 } /* end of my_mkdir */
2097 /*{{{int my_chdir(char *)*/
2099 Perl_my_chdir(pTHX_ const char *dir)
2101 STRLEN dirlen = strlen(dir);
2102 const char *dir1 = dir;
2104 /* zero length string sometimes gives ACCVIO */
2106 SETERRNO(EINVAL, SS$_BADPARAM);
2110 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2111 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2112 * so that existing scripts do not need to be changed.
2114 while ((dirlen > 0) && (*dir1 == ' ')) {
2119 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2121 * null file name/type. However, it's commonplace under Unix,
2122 * so we'll allow it for a gain in portability.
2124 * '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
2126 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2129 newdir = (char *)PerlMem_malloc(dirlen);
2131 _ckvmssts_noperl(SS$_INSFMEM);
2132 memcpy(newdir, dir1, dirlen-1);
2133 newdir[dirlen-1] = '\0';
2134 ret = chdir(newdir);
2135 PerlMem_free(newdir);
2138 else return chdir(dir1);
2139 } /* end of my_chdir */
2143 /*{{{int my_chmod(char *, mode_t)*/
2145 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2150 STRLEN speclen = strlen(file_spec);
2152 /* zero length string sometimes gives ACCVIO */
2153 if (speclen == 0) return -1;
2155 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2156 * that implies null file name/type. However, it's commonplace under Unix,
2157 * so we'll allow it for a gain in portability.
2159 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2160 * in VMS file.dir notation.
2162 changefile = (char *) file_spec; /* cast ok */
2163 ret = flex_lstat(file_spec, &st);
2166 /* Due to a historical feature, flex_stat/lstat can not see some */
2167 /* Unix format file names that the rest of the CRTL can see when */
2168 /* ODS-2 file specifications are in use. */
2169 /* Fixing that feature will cause some perl tests to fail */
2170 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2174 /* It may be possible to get here with nothing in st_devname */
2175 /* chmod still may work though */
2176 if (st.st_devnam[0] != 0) {
2177 changefile = st.st_devnam;
2180 ret = chmod(changefile, mode);
2182 } /* end of my_chmod */
2186 /*{{{FILE *my_tmpfile()*/
2193 if ((fp = tmpfile())) return fp;
2195 cp = (char *)PerlMem_malloc(L_tmpnam+24);
2196 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2198 if (decc_filename_unix_only == 0)
2199 strcpy(cp,"Sys$Scratch:");
2202 tmpnam(cp+strlen(cp));
2203 strcat(cp,".Perltmp");
2204 fp = fopen(cp,"w+","fop=dlt");
2212 * The C RTL's sigaction fails to check for invalid signal numbers so we
2213 * help it out a bit. The docs are correct, but the actual routine doesn't
2214 * do what the docs say it will.
2216 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2218 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2219 struct sigaction* oact)
2221 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2222 SETERRNO(EINVAL, SS$_INVARG);
2225 return sigaction(sig, act, oact);
2229 #ifdef KILL_BY_SIGPRC
2230 #include <errnodef.h>
2232 /* We implement our own kill() using the undocumented system service
2233 sys$sigprc for one of two reasons:
2235 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2236 target process to do a sys$exit, which usually can't be handled
2237 gracefully...certainly not by Perl and the %SIG{} mechanism.
2239 2.) If the kill() in the CRTL can't be called from a signal
2240 handler without disappearing into the ether, i.e., the signal
2241 it purportedly sends is never trapped. Still true as of VMS 7.3.
2243 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2244 in the target process rather than calling sys$exit.
2246 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2247 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2248 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2249 with condition codes C$_SIG0+nsig*8, catching the exception on the
2250 target process and resignaling with appropriate arguments.
2252 But we don't have that VMS 7.0+ exception handler, so if you
2253 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2255 Also note that SIGTERM is listed in the docs as being "unimplemented",
2256 yet always seems to be signaled with a VMS condition code of 4 (and
2257 correctly handled for that code). So we hardwire it in.
2259 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2260 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2261 than signalling with an unrecognized (and unhandled by CRTL) code.
2264 #define _MY_SIG_MAX 28
2267 Perl_sig_to_vmscondition_int(int sig)
2269 static unsigned int sig_code[_MY_SIG_MAX+1] =
2272 SS$_HANGUP, /* 1 SIGHUP */
2273 SS$_CONTROLC, /* 2 SIGINT */
2274 SS$_CONTROLY, /* 3 SIGQUIT */
2275 SS$_RADRMOD, /* 4 SIGILL */
2276 SS$_BREAK, /* 5 SIGTRAP */
2277 SS$_OPCCUS, /* 6 SIGABRT */
2278 SS$_COMPAT, /* 7 SIGEMT */
2280 SS$_FLTOVF, /* 8 SIGFPE VAX */
2282 SS$_HPARITH, /* 8 SIGFPE AXP */
2284 SS$_ABORT, /* 9 SIGKILL */
2285 SS$_ACCVIO, /* 10 SIGBUS */
2286 SS$_ACCVIO, /* 11 SIGSEGV */
2287 SS$_BADPARAM, /* 12 SIGSYS */
2288 SS$_NOMBX, /* 13 SIGPIPE */
2289 SS$_ASTFLT, /* 14 SIGALRM */
2306 static int initted = 0;
2309 sig_code[16] = C$_SIGUSR1;
2310 sig_code[17] = C$_SIGUSR2;
2311 sig_code[20] = C$_SIGCHLD;
2312 #if __CRTL_VER >= 70300000
2313 sig_code[28] = C$_SIGWINCH;
2317 if (sig < _SIG_MIN) return 0;
2318 if (sig > _MY_SIG_MAX) return 0;
2319 return sig_code[sig];
2323 Perl_sig_to_vmscondition(int sig)
2326 if (vms_debug_on_exception != 0)
2327 lib$signal(SS$_DEBUG);
2329 return Perl_sig_to_vmscondition_int(sig);
2333 #define sys$sigprc SYS$SIGPRC
2337 int sys$sigprc(unsigned int *pidadr,
2338 struct dsc$descriptor_s *prcname,
2345 Perl_my_kill(int pid, int sig)
2350 /* sig 0 means validate the PID */
2351 /*------------------------------*/
2353 const unsigned long int jpicode = JPI$_PID;
2356 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2357 if ($VMS_STATUS_SUCCESS(status))
2360 case SS$_NOSUCHNODE:
2361 case SS$_UNREACHABLE:
2375 code = Perl_sig_to_vmscondition_int(sig);
2378 SETERRNO(EINVAL, SS$_BADPARAM);
2382 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2383 * signals are to be sent to multiple processes.
2384 * pid = 0 - all processes in group except ones that the system exempts
2385 * pid = -1 - all processes except ones that the system exempts
2386 * pid = -n - all processes in group (abs(n)) except ...
2387 * For now, just report as not supported.
2391 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2395 iss = sys$sigprc((unsigned int *)&pid,0,code);
2396 if (iss&1) return 0;
2400 set_errno(EPERM); break;
2402 case SS$_NOSUCHNODE:
2403 case SS$_UNREACHABLE:
2404 set_errno(ESRCH); break;
2406 set_errno(ENOMEM); break;
2408 _ckvmssts_noperl(iss);
2411 set_vaxc_errno(iss);
2417 /* Routine to convert a VMS status code to a UNIX status code.
2418 ** More tricky than it appears because of conflicting conventions with
2421 ** VMS status codes are a bit mask, with the least significant bit set for
2424 ** Special UNIX status of EVMSERR indicates that no translation is currently
2425 ** available, and programs should check the VMS status code.
2427 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2431 #ifndef C_FACILITY_NO
2432 #define C_FACILITY_NO 0x350000
2435 #define DCL_IVVERB 0x38090
2438 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2446 /* Assume the best or the worst */
2447 if (vms_status & STS$M_SUCCESS)
2450 unix_status = EVMSERR;
2452 msg_status = vms_status & ~STS$M_CONTROL;
2454 facility = vms_status & STS$M_FAC_NO;
2455 fac_sp = vms_status & STS$M_FAC_SP;
2456 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2458 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2464 unix_status = EFAULT;
2466 case SS$_DEVOFFLINE:
2467 unix_status = EBUSY;
2470 unix_status = ENOTCONN;
2478 case SS$_INVFILFOROP:
2482 unix_status = EINVAL;
2484 case SS$_UNSUPPORTED:
2485 unix_status = ENOTSUP;
2490 unix_status = EACCES;
2492 case SS$_DEVICEFULL:
2493 unix_status = ENOSPC;
2496 unix_status = ENODEV;
2498 case SS$_NOSUCHFILE:
2499 case SS$_NOSUCHOBJECT:
2500 unix_status = ENOENT;
2502 case SS$_ABORT: /* Fatal case */
2503 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2504 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2505 unix_status = EINTR;
2508 unix_status = E2BIG;
2511 unix_status = ENOMEM;
2514 unix_status = EPERM;
2516 case SS$_NOSUCHNODE:
2517 case SS$_UNREACHABLE:
2518 unix_status = ESRCH;
2521 unix_status = ECHILD;
2524 if ((facility == 0) && (msg_no < 8)) {
2525 /* These are not real VMS status codes so assume that they are
2526 ** already UNIX status codes
2528 unix_status = msg_no;
2534 /* Translate a POSIX exit code to a UNIX exit code */
2535 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2536 unix_status = (msg_no & 0x07F8) >> 3;
2540 /* Documented traditional behavior for handling VMS child exits */
2541 /*--------------------------------------------------------------*/
2542 if (child_flag != 0) {
2544 /* Success / Informational return 0 */
2545 /*----------------------------------*/
2546 if (msg_no & STS$K_SUCCESS)
2549 /* Warning returns 1 */
2550 /*-------------------*/
2551 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2554 /* Everything else pass through the severity bits */
2555 /*------------------------------------------------*/
2556 return (msg_no & STS$M_SEVERITY);
2559 /* Normal VMS status to ERRNO mapping attempt */
2560 /*--------------------------------------------*/
2561 switch(msg_status) {
2562 /* case RMS$_EOF: */ /* End of File */
2563 case RMS$_FNF: /* File Not Found */
2564 case RMS$_DNF: /* Dir Not Found */
2565 unix_status = ENOENT;
2567 case RMS$_RNF: /* Record Not Found */
2568 unix_status = ESRCH;
2571 unix_status = ENOTDIR;
2574 unix_status = ENODEV;
2579 unix_status = EBADF;
2582 unix_status = EEXIST;
2586 case LIB$_INVSTRDES:
2588 case LIB$_NOSUCHSYM:
2589 case LIB$_INVSYMNAM:
2591 unix_status = EINVAL;
2597 unix_status = E2BIG;
2599 case RMS$_PRV: /* No privilege */
2600 case RMS$_ACC: /* ACP file access failed */
2601 case RMS$_WLK: /* Device write locked */
2602 unix_status = EACCES;
2604 case RMS$_MKD: /* Failed to mark for delete */
2605 unix_status = EPERM;
2607 /* case RMS$_NMF: */ /* No more files */
2615 /* Try to guess at what VMS error status should go with a UNIX errno
2616 * value. This is hard to do as there could be many possible VMS
2617 * error statuses that caused the errno value to be set.
2620 int Perl_unix_status_to_vms(int unix_status)
2622 int test_unix_status;
2624 /* Trivial cases first */
2625 /*---------------------*/
2626 if (unix_status == EVMSERR)
2629 /* Is vaxc$errno sane? */
2630 /*---------------------*/
2631 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2632 if (test_unix_status == unix_status)
2635 /* If way out of range, must be VMS code already */
2636 /*-----------------------------------------------*/
2637 if (unix_status > EVMSERR)
2640 /* If out of range, punt */
2641 /*-----------------------*/
2642 if (unix_status > __ERRNO_MAX)
2646 /* Ok, now we have to do it the hard way. */
2647 /*----------------------------------------*/
2648 switch(unix_status) {
2649 case 0: return SS$_NORMAL;
2650 case EPERM: return SS$_NOPRIV;
2651 case ENOENT: return SS$_NOSUCHOBJECT;
2652 case ESRCH: return SS$_UNREACHABLE;
2653 case EINTR: return SS$_ABORT;
2656 case E2BIG: return SS$_BUFFEROVF;
2658 case EBADF: return RMS$_IFI;
2659 case ECHILD: return SS$_NONEXPR;
2661 case ENOMEM: return SS$_INSFMEM;
2662 case EACCES: return SS$_FILACCERR;
2663 case EFAULT: return SS$_ACCVIO;
2665 case EBUSY: return SS$_DEVOFFLINE;
2666 case EEXIST: return RMS$_FEX;
2668 case ENODEV: return SS$_NOSUCHDEV;
2669 case ENOTDIR: return RMS$_DIR;
2671 case EINVAL: return SS$_INVARG;
2677 case ENOSPC: return SS$_DEVICEFULL;
2678 case ESPIPE: return LIB$_INVARG;
2683 case ERANGE: return LIB$_INVARG;
2684 /* case EWOULDBLOCK */
2685 /* case EINPROGRESS */
2688 /* case EDESTADDRREQ */
2690 /* case EPROTOTYPE */
2691 /* case ENOPROTOOPT */
2692 /* case EPROTONOSUPPORT */
2693 /* case ESOCKTNOSUPPORT */
2694 /* case EOPNOTSUPP */
2695 /* case EPFNOSUPPORT */
2696 /* case EAFNOSUPPORT */
2697 /* case EADDRINUSE */
2698 /* case EADDRNOTAVAIL */
2700 /* case ENETUNREACH */
2701 /* case ENETRESET */
2702 /* case ECONNABORTED */
2703 /* case ECONNRESET */
2706 case ENOTCONN: return SS$_CLEARED;
2707 /* case ESHUTDOWN */
2708 /* case ETOOMANYREFS */
2709 /* case ETIMEDOUT */
2710 /* case ECONNREFUSED */
2712 /* case ENAMETOOLONG */
2713 /* case EHOSTDOWN */
2714 /* case EHOSTUNREACH */
2715 /* case ENOTEMPTY */
2727 /* case ECANCELED */
2731 return SS$_UNSUPPORTED;
2737 /* case EABANDONED */
2739 return SS$_ABORT; /* punt */
2744 /* default piping mailbox size */
2746 # define PERL_BUFSIZ 512
2748 # define PERL_BUFSIZ 8192
2753 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2755 unsigned long int mbxbufsiz;
2756 static unsigned long int syssize = 0;
2757 unsigned long int dviitm = DVI$_DEVNAM;
2758 char csize[LNM$C_NAMLENGTH+1];
2762 unsigned long syiitm = SYI$_MAXBUF;
2764 * Get the SYSGEN parameter MAXBUF
2766 * If the logical 'PERL_MBX_SIZE' is defined
2767 * use the value of the logical instead of PERL_BUFSIZ, but
2768 * keep the size between 128 and MAXBUF.
2771 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2774 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2775 mbxbufsiz = atoi(csize);
2777 mbxbufsiz = PERL_BUFSIZ;
2779 if (mbxbufsiz < 128) mbxbufsiz = 128;
2780 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2782 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2784 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2785 _ckvmssts_noperl(sts);
2786 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2788 } /* end of create_mbx() */
2791 /*{{{ my_popen and my_pclose*/
2793 typedef struct _iosb IOSB;
2794 typedef struct _iosb* pIOSB;
2795 typedef struct _pipe Pipe;
2796 typedef struct _pipe* pPipe;
2797 typedef struct pipe_details Info;
2798 typedef struct pipe_details* pInfo;
2799 typedef struct _srqp RQE;
2800 typedef struct _srqp* pRQE;
2801 typedef struct _tochildbuf CBuf;
2802 typedef struct _tochildbuf* pCBuf;
2805 unsigned short status;
2806 unsigned short count;
2807 unsigned long dvispec;
2810 #pragma member_alignment save
2811 #pragma nomember_alignment quadword
2812 struct _srqp { /* VMS self-relative queue entry */
2813 unsigned long qptr[2];
2815 #pragma member_alignment restore
2816 static RQE RQE_ZERO = {0,0};
2818 struct _tochildbuf {
2821 unsigned short size;
2829 unsigned short chan_in;
2830 unsigned short chan_out;
2832 unsigned int bufsize;
2844 #if defined(PERL_IMPLICIT_CONTEXT)
2845 void *thx; /* Either a thread or an interpreter */
2846 /* pointer, depending on how we're built */
2854 PerlIO *fp; /* file pointer to pipe mailbox */
2855 int useFILE; /* using stdio, not perlio */
2856 int pid; /* PID of subprocess */
2857 int mode; /* == 'r' if pipe open for reading */
2858 int done; /* subprocess has completed */
2859 int waiting; /* waiting for completion/closure */
2860 int closing; /* my_pclose is closing this pipe */
2861 unsigned long completion; /* termination status of subprocess */
2862 pPipe in; /* pipe in to sub */
2863 pPipe out; /* pipe out of sub */
2864 pPipe err; /* pipe of sub's sys$error */
2865 int in_done; /* true when in pipe finished */
2868 unsigned short xchan; /* channel to debug xterm */
2869 unsigned short xchan_valid; /* channel is assigned */
2872 struct exit_control_block
2874 struct exit_control_block *flink;
2875 unsigned long int (*exit_routine)(void);
2876 unsigned long int arg_count;
2877 unsigned long int *status_address;
2878 unsigned long int exit_status;
2881 typedef struct _closed_pipes Xpipe;
2882 typedef struct _closed_pipes* pXpipe;
2884 struct _closed_pipes {
2885 int pid; /* PID of subprocess */
2886 unsigned long completion; /* termination status of subprocess */
2888 #define NKEEPCLOSED 50
2889 static Xpipe closed_list[NKEEPCLOSED];
2890 static int closed_index = 0;
2891 static int closed_num = 0;
2893 #define RETRY_DELAY "0 ::0.20"
2894 #define MAX_RETRY 50
2896 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2897 static unsigned long mypid;
2898 static unsigned long delaytime[2];
2900 static pInfo open_pipes = NULL;
2901 static $DESCRIPTOR(nl_desc, "NL:");
2903 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2907 static unsigned long int
2908 pipe_exit_routine(void)
2911 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2912 int sts, did_stuff, j;
2915 * Flush any pending i/o, but since we are in process run-down, be
2916 * careful about referencing PerlIO structures that may already have
2917 * been deallocated. We may not even have an interpreter anymore.
2922 #if defined(PERL_IMPLICIT_CONTEXT)
2923 /* We need to use the Perl context of the thread that created */
2927 aTHX = info->err->thx;
2929 aTHX = info->out->thx;
2931 aTHX = info->in->thx;
2934 #if defined(USE_ITHREADS)
2938 && PL_perlio_fd_refcnt
2941 PerlIO_flush(info->fp);
2943 fflush((FILE *)info->fp);
2949 next we try sending an EOF...ignore if doesn't work, make sure we
2956 _ckvmssts_noperl(sys$setast(0));
2957 if (info->in && !info->in->shut_on_empty) {
2958 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2963 _ckvmssts_noperl(sys$setast(1));
2967 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2969 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2974 _ckvmssts_noperl(sys$setast(0));
2975 if (info->waiting && info->done)
2977 nwait += info->waiting;
2978 _ckvmssts_noperl(sys$setast(1));
2988 _ckvmssts_noperl(sys$setast(0));
2989 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2990 sts = sys$forcex(&info->pid,0,&abort);
2991 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2994 _ckvmssts_noperl(sys$setast(1));
2998 /* again, wait for effect */
3000 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3005 _ckvmssts_noperl(sys$setast(0));
3006 if (info->waiting && info->done)
3008 nwait += info->waiting;
3009 _ckvmssts_noperl(sys$setast(1));
3018 _ckvmssts_noperl(sys$setast(0));
3019 if (!info->done) { /* We tried to be nice . . . */
3020 sts = sys$delprc(&info->pid,0);
3021 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3022 info->done = 1; /* sys$delprc is as done as we're going to get. */
3024 _ckvmssts_noperl(sys$setast(1));
3030 #if defined(PERL_IMPLICIT_CONTEXT)
3031 /* We need to use the Perl context of the thread that created */
3034 if (open_pipes->err)
3035 aTHX = open_pipes->err->thx;
3036 else if (open_pipes->out)
3037 aTHX = open_pipes->out->thx;
3038 else if (open_pipes->in)
3039 aTHX = open_pipes->in->thx;
3041 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3042 else if (!(sts & 1)) retsts = sts;
3047 static struct exit_control_block pipe_exitblock =
3048 {(struct exit_control_block *) 0,
3049 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3051 static void pipe_mbxtofd_ast(pPipe p);
3052 static void pipe_tochild1_ast(pPipe p);
3053 static void pipe_tochild2_ast(pPipe p);
3056 popen_completion_ast(pInfo info)
3058 pInfo i = open_pipes;
3061 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3062 closed_list[closed_index].pid = info->pid;
3063 closed_list[closed_index].completion = info->completion;
3065 if (closed_index == NKEEPCLOSED)
3070 if (i == info) break;
3073 if (!i) return; /* unlinked, probably freed too */
3078 Writing to subprocess ...
3079 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3081 chan_out may be waiting for "done" flag, or hung waiting
3082 for i/o completion to child...cancel the i/o. This will
3083 put it into "snarf mode" (done but no EOF yet) that discards
3086 Output from subprocess (stdout, stderr) needs to be flushed and
3087 shut down. We try sending an EOF, but if the mbx is full the pipe
3088 routine should still catch the "shut_on_empty" flag, telling it to
3089 use immediate-style reads so that "mbx empty" -> EOF.
3093 if (info->in && !info->in_done) { /* only for mode=w */
3094 if (info->in->shut_on_empty && info->in->need_wake) {
3095 info->in->need_wake = FALSE;
3096 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3098 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3102 if (info->out && !info->out_done) { /* were we also piping output? */
3103 info->out->shut_on_empty = TRUE;
3104 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3105 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3106 _ckvmssts_noperl(iss);
3109 if (info->err && !info->err_done) { /* we were piping stderr */
3110 info->err->shut_on_empty = TRUE;
3111 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3112 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3113 _ckvmssts_noperl(iss);
3115 _ckvmssts_noperl(sys$setef(pipe_ef));
3119 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3120 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3121 static void pipe_infromchild_ast(pPipe p);
3124 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3125 inside an AST routine without worrying about reentrancy and which Perl
3126 memory allocator is being used.
3128 We read data and queue up the buffers, then spit them out one at a
3129 time to the output mailbox when the output mailbox is ready for one.
3132 #define INITIAL_TOCHILDQUEUE 2
3135 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3139 char mbx1[64], mbx2[64];
3140 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3141 DSC$K_CLASS_S, mbx1},
3142 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3143 DSC$K_CLASS_S, mbx2};
3144 unsigned int dviitm = DVI$_DEVBUFSIZ;
3148 _ckvmssts_noperl(lib$get_vm(&n, &p));
3150 create_mbx(&p->chan_in , &d_mbx1);
3151 create_mbx(&p->chan_out, &d_mbx2);
3152 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3155 p->shut_on_empty = FALSE;
3156 p->need_wake = FALSE;
3159 p->iosb.status = SS$_NORMAL;
3160 p->iosb2.status = SS$_NORMAL;
3166 #ifdef PERL_IMPLICIT_CONTEXT
3170 n = sizeof(CBuf) + p->bufsize;
3172 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3173 _ckvmssts_noperl(lib$get_vm(&n, &b));
3174 b->buf = (char *) b + sizeof(CBuf);
3175 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3178 pipe_tochild2_ast(p);
3179 pipe_tochild1_ast(p);
3185 /* reads the MBX Perl is writing, and queues */
3188 pipe_tochild1_ast(pPipe p)
3191 int iss = p->iosb.status;
3192 int eof = (iss == SS$_ENDOFFILE);
3194 #ifdef PERL_IMPLICIT_CONTEXT
3200 p->shut_on_empty = TRUE;
3202 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3204 _ckvmssts_noperl(iss);
3208 b->size = p->iosb.count;
3209 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3211 p->need_wake = FALSE;
3212 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3215 p->retry = 1; /* initial call */
3218 if (eof) { /* flush the free queue, return when done */
3219 int n = sizeof(CBuf) + p->bufsize;
3221 iss = lib$remqti(&p->free, &b);
3222 if (iss == LIB$_QUEWASEMP) return;
3223 _ckvmssts_noperl(iss);
3224 _ckvmssts_noperl(lib$free_vm(&n, &b));
3228 iss = lib$remqti(&p->free, &b);
3229 if (iss == LIB$_QUEWASEMP) {
3230 int n = sizeof(CBuf) + p->bufsize;
3231 _ckvmssts_noperl(lib$get_vm(&n, &b));
3232 b->buf = (char *) b + sizeof(CBuf);
3234 _ckvmssts_noperl(iss);
3238 iss = sys$qio(0,p->chan_in,
3239 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3241 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3242 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3243 _ckvmssts_noperl(iss);
3247 /* writes queued buffers to output, waits for each to complete before
3251 pipe_tochild2_ast(pPipe p)
3254 int iss = p->iosb2.status;
3255 int n = sizeof(CBuf) + p->bufsize;
3256 int done = (p->info && p->info->done) ||
3257 iss == SS$_CANCEL || iss == SS$_ABORT;
3258 #if defined(PERL_IMPLICIT_CONTEXT)
3263 if (p->type) { /* type=1 has old buffer, dispose */
3264 if (p->shut_on_empty) {
3265 _ckvmssts_noperl(lib$free_vm(&n, &b));
3267 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3272 iss = lib$remqti(&p->wait, &b);
3273 if (iss == LIB$_QUEWASEMP) {
3274 if (p->shut_on_empty) {
3276 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3277 *p->pipe_done = TRUE;
3278 _ckvmssts_noperl(sys$setef(pipe_ef));
3280 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3281 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3285 p->need_wake = TRUE;
3288 _ckvmssts_noperl(iss);
3295 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3296 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3298 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3299 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3308 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3311 char mbx1[64], mbx2[64];
3312 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3313 DSC$K_CLASS_S, mbx1},
3314 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3315 DSC$K_CLASS_S, mbx2};
3316 unsigned int dviitm = DVI$_DEVBUFSIZ;
3318 int n = sizeof(Pipe);
3319 _ckvmssts_noperl(lib$get_vm(&n, &p));
3320 create_mbx(&p->chan_in , &d_mbx1);
3321 create_mbx(&p->chan_out, &d_mbx2);
3323 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3324 n = p->bufsize * sizeof(char);
3325 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3326 p->shut_on_empty = FALSE;
3329 p->iosb.status = SS$_NORMAL;
3330 #if defined(PERL_IMPLICIT_CONTEXT)
3333 pipe_infromchild_ast(p);
3341 pipe_infromchild_ast(pPipe p)
3343 int iss = p->iosb.status;
3344 int eof = (iss == SS$_ENDOFFILE);
3345 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3346 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3347 #if defined(PERL_IMPLICIT_CONTEXT)
3351 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3352 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3357 input shutdown if EOF from self (done or shut_on_empty)
3358 output shutdown if closing flag set (my_pclose)
3359 send data/eof from child or eof from self
3360 otherwise, re-read (snarf of data from child)
3365 if (myeof && p->chan_in) { /* input shutdown */
3366 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3371 if (myeof || kideof) { /* pass EOF to parent */
3372 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3373 pipe_infromchild_ast, p,
3376 } else if (eof) { /* eat EOF --- fall through to read*/
3378 } else { /* transmit data */
3379 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3380 pipe_infromchild_ast,p,
3381 p->buf, p->iosb.count, 0, 0, 0, 0));
3387 /* everything shut? flag as done */
3389 if (!p->chan_in && !p->chan_out) {
3390 *p->pipe_done = TRUE;
3391 _ckvmssts_noperl(sys$setef(pipe_ef));
3395 /* write completed (or read, if snarfing from child)
3396 if still have input active,
3397 queue read...immediate mode if shut_on_empty so we get EOF if empty
3399 check if Perl reading, generate EOFs as needed
3405 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3406 pipe_infromchild_ast,p,
3407 p->buf, p->bufsize, 0, 0, 0, 0);
3408 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3409 _ckvmssts_noperl(iss);
3410 } else { /* send EOFs for extra reads */
3411 p->iosb.status = SS$_ENDOFFILE;
3412 p->iosb.dvispec = 0;
3413 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3415 pipe_infromchild_ast, p, 0, 0, 0, 0));
3421 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3425 unsigned long dviitm = DVI$_DEVBUFSIZ;
3427 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3428 DSC$K_CLASS_S, mbx};
3429 int n = sizeof(Pipe);
3431 /* things like terminals and mbx's don't need this filter */
3432 if (fd && fstat(fd,&s) == 0) {
3433 unsigned long devchar;
3435 unsigned short dev_len;
3436 struct dsc$descriptor_s d_dev;
3438 struct item_list_3 items[3];
3440 unsigned short dvi_iosb[4];
3442 cptr = getname(fd, out, 1);
3443 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3444 d_dev.dsc$a_pointer = out;
3445 d_dev.dsc$w_length = strlen(out);
3446 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3447 d_dev.dsc$b_class = DSC$K_CLASS_S;
3450 items[0].code = DVI$_DEVCHAR;
3451 items[0].bufadr = &devchar;
3452 items[0].retadr = NULL;
3454 items[1].code = DVI$_FULLDEVNAM;
3455 items[1].bufadr = device;
3456 items[1].retadr = &dev_len;
3460 status = sys$getdviw
3461 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3462 _ckvmssts_noperl(status);
3463 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3464 device[dev_len] = 0;
3466 if (!(devchar & DEV$M_DIR)) {
3467 strcpy(out, device);
3473 _ckvmssts_noperl(lib$get_vm(&n, &p));
3474 p->fd_out = dup(fd);
3475 create_mbx(&p->chan_in, &d_mbx);
3476 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3477 n = (p->bufsize+1) * sizeof(char);
3478 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3479 p->shut_on_empty = FALSE;
3484 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3485 pipe_mbxtofd_ast, p,
3486 p->buf, p->bufsize, 0, 0, 0, 0));
3492 pipe_mbxtofd_ast(pPipe p)
3494 int iss = p->iosb.status;
3495 int done = p->info->done;
3497 int eof = (iss == SS$_ENDOFFILE);
3498 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3499 int err = !(iss&1) && !eof;
3500 #if defined(PERL_IMPLICIT_CONTEXT)
3504 if (done && myeof) { /* end piping */
3506 sys$dassgn(p->chan_in);
3507 *p->pipe_done = TRUE;
3508 _ckvmssts_noperl(sys$setef(pipe_ef));
3512 if (!err && !eof) { /* good data to send to file */
3513 p->buf[p->iosb.count] = '\n';
3514 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3517 if (p->retry < MAX_RETRY) {
3518 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3524 _ckvmssts_noperl(iss);
3528 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3529 pipe_mbxtofd_ast, p,
3530 p->buf, p->bufsize, 0, 0, 0, 0);
3531 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3532 _ckvmssts_noperl(iss);
3536 typedef struct _pipeloc PLOC;
3537 typedef struct _pipeloc* pPLOC;
3541 char dir[NAM$C_MAXRSS+1];
3543 static pPLOC head_PLOC = 0;
3546 free_pipelocs(pTHX_ void *head)
3549 pPLOC *pHead = (pPLOC *)head;
3561 store_pipelocs(pTHX)
3569 char temp[NAM$C_MAXRSS+1];
3573 free_pipelocs(aTHX_ &head_PLOC);
3575 /* the . directory from @INC comes last */
3577 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3578 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3579 p->next = head_PLOC;
3581 strcpy(p->dir,"./");
3583 /* get the directory from $^X */
3585 unixdir = (char *)PerlMem_malloc(VMS_MAXRSS);
3586 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3588 #ifdef PERL_IMPLICIT_CONTEXT
3589 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3591 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3593 my_strlcpy(temp, PL_origargv[0], sizeof(temp));
3594 x = strrchr(temp,']');
3596 x = strrchr(temp,'>');
3598 /* It could be a UNIX path */
3599 x = strrchr(temp,'/');
3605 /* Got a bare name, so use default directory */
3610 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3611 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3612 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3613 p->next = head_PLOC;
3615 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3619 /* reverse order of @INC entries, skip "." since entered above */
3621 #ifdef PERL_IMPLICIT_CONTEXT
3624 if (PL_incgv) av = GvAVn(PL_incgv);
3626 for (i = 0; av && i <= AvFILL(av); i++) {
3627 dirsv = *av_fetch(av,i,TRUE);
3629 if (SvROK(dirsv)) continue;
3630 dir = SvPVx(dirsv,n_a);
3631 if (strcmp(dir,".") == 0) continue;
3632 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3635 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3636 p->next = head_PLOC;
3638 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3641 /* most likely spot (ARCHLIB) put first in the list */
3644 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3645 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3646 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3647 p->next = head_PLOC;
3649 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3652 PerlMem_free(unixdir);
3656 Perl_cando_by_name_int
3657 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3658 #if !defined(PERL_IMPLICIT_CONTEXT)
3659 #define cando_by_name_int Perl_cando_by_name_int
3661 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3667 static int vmspipe_file_status = 0;
3668 static char vmspipe_file[NAM$C_MAXRSS+1];
3670 /* already found? Check and use ... need read+execute permission */
3672 if (vmspipe_file_status == 1) {
3673 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3674 && cando_by_name_int
3675 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3676 return vmspipe_file;
3678 vmspipe_file_status = 0;
3681 /* scan through stored @INC, $^X */
3683 if (vmspipe_file_status == 0) {
3684 char file[NAM$C_MAXRSS+1];
3685 pPLOC p = head_PLOC;
3690 dirlen = my_strlcpy(file, p->dir, sizeof(file));
3691 my_strlcat(file, "vmspipe.com", sizeof(file));
3694 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3695 if (!exp_res) continue;
3697 if (cando_by_name_int
3698 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3699 && cando_by_name_int
3700 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3701 vmspipe_file_status = 1;
3702 return vmspipe_file;
3705 vmspipe_file_status = -1; /* failed, use tempfiles */
3712 vmspipe_tempfile(pTHX)
3714 char file[NAM$C_MAXRSS+1];
3716 static int index = 0;
3720 /* create a tempfile */
3722 /* we can't go from W, shr=get to R, shr=get without
3723 an intermediate vulnerable state, so don't bother trying...
3725 and lib$spawn doesn't shr=put, so have to close the write
3727 So... match up the creation date/time and the FID to
3728 make sure we're dealing with the same file
3733 if (!decc_filename_unix_only) {
3734 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3735 fp = fopen(file,"w");
3737 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3738 fp = fopen(file,"w");
3740 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3741 fp = fopen(file,"w");
3746 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3747 fp = fopen(file,"w");
3749 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3750 fp = fopen(file,"w");
3752 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3753 fp = fopen(file,"w");
3757 if (!fp) return 0; /* we're hosed */
3759 fprintf(fp,"$! 'f$verify(0)'\n");
3760 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3761 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3762 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3763 fprintf(fp,"$ perl_on = \"set noon\"\n");
3764 fprintf(fp,"$ perl_exit = \"exit\"\n");
3765 fprintf(fp,"$ perl_del = \"delete\"\n");
3766 fprintf(fp,"$ pif = \"if\"\n");
3767 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3768 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3769 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3770 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3771 fprintf(fp,"$! --- build command line to get max possible length\n");
3772 fprintf(fp,"$c=perl_popen_cmd0\n");
3773 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3774 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3775 fprintf(fp,"$x=perl_popen_cmd3\n");
3776 fprintf(fp,"$c=c+x\n");
3777 fprintf(fp,"$ perl_on\n");
3778 fprintf(fp,"$ 'c'\n");
3779 fprintf(fp,"$ perl_status = $STATUS\n");
3780 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3781 fprintf(fp,"$ perl_exit 'perl_status'\n");
3784 fgetname(fp, file, 1);
3785 fstat(fileno(fp), &s0.crtl_stat);
3788 if (decc_filename_unix_only)
3789 int_tounixspec(file, file, NULL);
3790 fp = fopen(file,"r","shr=get");
3792 fstat(fileno(fp), &s1.crtl_stat);
3794 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3795 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3804 static int vms_is_syscommand_xterm(void)
3806 const static struct dsc$descriptor_s syscommand_dsc =
3807 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3809 const static struct dsc$descriptor_s decwdisplay_dsc =
3810 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3812 struct item_list_3 items[2];
3813 unsigned short dvi_iosb[4];
3814 unsigned long devchar;
3815 unsigned long devclass;
3818 /* Very simple check to guess if sys$command is a decterm? */
3819 /* First see if the DECW$DISPLAY: device exists */
3821 items[0].code = DVI$_DEVCHAR;
3822 items[0].bufadr = &devchar;
3823 items[0].retadr = NULL;
3827 status = sys$getdviw
3828 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3830 if ($VMS_STATUS_SUCCESS(status)) {
3831 status = dvi_iosb[0];
3834 if (!$VMS_STATUS_SUCCESS(status)) {
3835 SETERRNO(EVMSERR, status);
3839 /* If it does, then for now assume that we are on a workstation */
3840 /* Now verify that SYS$COMMAND is a terminal */
3841 /* for creating the debugger DECTerm */
3844 items[0].code = DVI$_DEVCLASS;
3845 items[0].bufadr = &devclass;
3846 items[0].retadr = NULL;
3850 status = sys$getdviw
3851 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3853 if ($VMS_STATUS_SUCCESS(status)) {
3854 status = dvi_iosb[0];
3857 if (!$VMS_STATUS_SUCCESS(status)) {
3858 SETERRNO(EVMSERR, status);
3862 if (devclass == DC$_TERM) {
3869 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3870 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3875 char device_name[65];
3876 unsigned short device_name_len;
3877 struct dsc$descriptor_s customization_dsc;
3878 struct dsc$descriptor_s device_name_dsc;
3880 char customization[200];
3884 unsigned short p_chan;
3886 unsigned short iosb[4];
3887 const char * cust_str =
3888 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3889 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3890 DSC$K_CLASS_S, mbx1};
3892 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
3893 /*---------------------------------------*/
3894 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
3897 /* Make sure that this is from the Perl debugger */
3898 ret_char = strstr(cmd," xterm ");
3899 if (ret_char == NULL)
3901 cptr = ret_char + 7;
3902 ret_char = strstr(cmd,"tty");
3903 if (ret_char == NULL)
3905 ret_char = strstr(cmd,"sleep");
3906 if (ret_char == NULL)
3909 if (decw_term_port == 0) {
3910 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
3911 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
3912 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
3914 status = lib$find_image_symbol
3916 &decw_term_port_dsc,
3917 (void *)&decw_term_port,
3921 /* Try again with the other image name */
3922 if (!$VMS_STATUS_SUCCESS(status)) {
3924 status = lib$find_image_symbol
3926 &decw_term_port_dsc,
3927 (void *)&decw_term_port,
3936 /* No decw$term_port, give it up */
3937 if (!$VMS_STATUS_SUCCESS(status))
3940 /* Are we on a workstation? */
3941 /* to do: capture the rows / columns and pass their properties */
3942 ret_stat = vms_is_syscommand_xterm();
3946 /* Make the title: */
3947 ret_char = strstr(cptr,"-title");
3948 if (ret_char != NULL) {
3949 while ((*cptr != 0) && (*cptr != '\"')) {
3955 while ((*cptr != 0) && (*cptr != '\"')) {
3968 strcpy(title,"Perl Debug DECTerm");
3970 sprintf(customization, cust_str, title);
3972 customization_dsc.dsc$a_pointer = customization;
3973 customization_dsc.dsc$w_length = strlen(customization);
3974 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3975 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
3977 device_name_dsc.dsc$a_pointer = device_name;
3978 device_name_dsc.dsc$w_length = sizeof device_name -1;
3979 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3980 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
3982 device_name_len = 0;
3984 /* Try to create the window */
3985 status = (*decw_term_port)
3994 if (!$VMS_STATUS_SUCCESS(status)) {
3995 SETERRNO(EVMSERR, status);
3999 device_name[device_name_len] = '\0';
4001 /* Need to set this up to look like a pipe for cleanup */
4003 status = lib$get_vm(&n, &info);
4004 if (!$VMS_STATUS_SUCCESS(status)) {
4005 SETERRNO(ENOMEM, status);
4011 info->completion = 0;
4012 info->closing = FALSE;
4019 info->in_done = TRUE;
4020 info->out_done = TRUE;
4021 info->err_done = TRUE;
4023 /* Assign a channel on this so that it will persist, and not login */
4024 /* We stash this channel in the info structure for reference. */
4025 /* The created xterm self destructs when the last channel is removed */
4026 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4027 /* So leave this assigned. */
4028 device_name_dsc.dsc$w_length = device_name_len;
4029 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4030 if (!$VMS_STATUS_SUCCESS(status)) {
4031 SETERRNO(EVMSERR, status);
4034 info->xchan_valid = 1;
4036 /* Now create a mailbox to be read by the application */
4038 create_mbx(&p_chan, &d_mbx1);
4040 /* write the name of the created terminal to the mailbox */
4041 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4042 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4044 if (!$VMS_STATUS_SUCCESS(status)) {
4045 SETERRNO(EVMSERR, status);
4049 info->fp = PerlIO_open(mbx1, mode);
4051 /* Done with this channel */
4054 /* If any errors, then clean up */
4057 _ckvmssts_noperl(lib$free_vm(&n, &info));
4065 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4068 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4070 static int handler_set_up = FALSE;
4072 unsigned long int sts, flags = CLI$M_NOWAIT;
4073 /* The use of a GLOBAL table (as was done previously) rendered
4074 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4075 * environment. Hence we've switched to LOCAL symbol table.
4077 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4079 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4080 char *in, *out, *err, mbx[512];
4082 char tfilebuf[NAM$C_MAXRSS+1];
4084 char cmd_sym_name[20];
4085 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4086 DSC$K_CLASS_S, symbol};
4087 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4089 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4090 DSC$K_CLASS_S, cmd_sym_name};
4091 struct dsc$descriptor_s *vmscmd;
4092 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4093 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4094 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4096 /* Check here for Xterm create request. This means looking for
4097 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4098 * is possible to create an xterm.
4100 if (*in_mode == 'r') {
4103 #if defined(PERL_IMPLICIT_CONTEXT)
4104 /* Can not fork an xterm with a NULL context */
4105 /* This probably could never happen */
4109 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4110 if (xterm_fd != NULL)
4114 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4116 /* once-per-program initialization...
4117 note that the SETAST calls and the dual test of pipe_ef
4118 makes sure that only the FIRST thread through here does
4119 the initialization...all other threads wait until it's
4122 Yeah, uglier than a pthread call, it's got all the stuff inline
4123 rather than in a separate routine.
4127 _ckvmssts_noperl(sys$setast(0));
4129 unsigned long int pidcode = JPI$_PID;
4130 $DESCRIPTOR(d_delay, RETRY_DELAY);
4131 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4132 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4133 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4135 if (!handler_set_up) {
4136 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4137 handler_set_up = TRUE;
4139 _ckvmssts_noperl(sys$setast(1));
4142 /* see if we can find a VMSPIPE.COM */
4145 vmspipe = find_vmspipe(aTHX);
4147 vmspipedsc.dsc$w_length = my_strlcpy(tfilebuf+1, vmspipe, sizeof(tfilebuf)-1) + 1;
4148 } else { /* uh, oh...we're in tempfile hell */
4149 tpipe = vmspipe_tempfile(aTHX);
4150 if (!tpipe) { /* a fish popular in Boston */
4151 if (ckWARN(WARN_PIPE)) {
4152 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4156 fgetname(tpipe,tfilebuf+1,1);
4157 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4159 vmspipedsc.dsc$a_pointer = tfilebuf;
4161 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4164 case RMS$_FNF: case RMS$_DNF:
4165 set_errno(ENOENT); break;
4167 set_errno(ENOTDIR); break;
4169 set_errno(ENODEV); break;
4171 set_errno(EACCES); break;
4173 set_errno(EINVAL); break;
4174 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4175 set_errno(E2BIG); break;
4176 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4177 _ckvmssts_noperl(sts); /* fall through */
4178 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4181 set_vaxc_errno(sts);
4182 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4183 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4189 _ckvmssts_noperl(lib$get_vm(&n, &info));
4191 my_strlcpy(mode, in_mode, sizeof(mode));
4194 info->completion = 0;
4195 info->closing = FALSE;
4202 info->in_done = TRUE;
4203 info->out_done = TRUE;
4204 info->err_done = TRUE;
4206 info->xchan_valid = 0;
4208 in = (char *)PerlMem_malloc(VMS_MAXRSS);
4209 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4210 out = (char *)PerlMem_malloc(VMS_MAXRSS);
4211 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4212 err = (char *)PerlMem_malloc(VMS_MAXRSS);
4213 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4215 in[0] = out[0] = err[0] = '\0';
4217 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4221 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4226 if (*mode == 'r') { /* piping from subroutine */
4228 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4230 info->out->pipe_done = &info->out_done;
4231 info->out_done = FALSE;
4232 info->out->info = info;
4234 if (!info->useFILE) {
4235 info->fp = PerlIO_open(mbx, mode);
4237 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4238 vmssetuserlnm("SYS$INPUT", mbx);
4241 if (!info->fp && info->out) {
4242 sys$cancel(info->out->chan_out);
4244 while (!info->out_done) {
4246 _ckvmssts_noperl(sys$setast(0));
4247 done = info->out_done;
4248 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4249 _ckvmssts_noperl(sys$setast(1));
4250 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4253 if (info->out->buf) {
4254 n = info->out->bufsize * sizeof(char);
4255 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4258 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4260 _ckvmssts_noperl(lib$free_vm(&n, &info));
4265 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4267 info->err->pipe_done = &info->err_done;
4268 info->err_done = FALSE;
4269 info->err->info = info;
4272 } else if (*mode == 'w') { /* piping to subroutine */
4274 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4276 info->out->pipe_done = &info->out_done;
4277 info->out_done = FALSE;
4278 info->out->info = info;
4281 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4283 info->err->pipe_done = &info->err_done;
4284 info->err_done = FALSE;
4285 info->err->info = info;
4288 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4289 if (!info->useFILE) {
4290 info->fp = PerlIO_open(mbx, mode);
4292 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4293 vmssetuserlnm("SYS$OUTPUT", mbx);
4297 info->in->pipe_done = &info->in_done;
4298 info->in_done = FALSE;
4299 info->in->info = info;
4303 if (!info->fp && info->in) {
4305 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4306 0, 0, 0, 0, 0, 0, 0, 0));
4308 while (!info->in_done) {
4310 _ckvmssts_noperl(sys$setast(0));
4311 done = info->in_done;
4312 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4313 _ckvmssts_noperl(sys$setast(1));
4314 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4317 if (info->in->buf) {
4318 n = info->in->bufsize * sizeof(char);
4319 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4322 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4324 _ckvmssts_noperl(lib$free_vm(&n, &info));
4330 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4331 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4333 info->out->pipe_done = &info->out_done;
4334 info->out_done = FALSE;
4335 info->out->info = info;
4338 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4340 info->err->pipe_done = &info->err_done;
4341 info->err_done = FALSE;
4342 info->err->info = info;
4346 d_symbol.dsc$w_length = my_strlcpy(symbol, in, sizeof(symbol));
4347 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4349 d_symbol.dsc$w_length = my_strlcpy(symbol, err, sizeof(symbol));
4350 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4352 d_symbol.dsc$w_length = my_strlcpy(symbol, out, sizeof(symbol));
4353 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4355 /* Done with the names for the pipes */
4360 p = vmscmd->dsc$a_pointer;
4361 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4362 if (*p == '$') p++; /* remove leading $ */
4363 while (*p == ' ' || *p == '\t') p++;
4365 for (j = 0; j < 4; j++) {
4366 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4367 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4369 d_symbol.dsc$w_length = my_strlcpy(symbol, p, sizeof(symbol));
4370 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4372 if (strlen(p) > MAX_DCL_SYMBOL) {
4373 p += MAX_DCL_SYMBOL;
4378 _ckvmssts_noperl(sys$setast(0));
4379 info->next=open_pipes; /* prepend to list */
4381 _ckvmssts_noperl(sys$setast(1));
4382 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4383 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4384 * have SYS$COMMAND if we need it.
4386 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4387 0, &info->pid, &info->completion,
4388 0, popen_completion_ast,info,0,0,0));
4390 /* if we were using a tempfile, close it now */
4392 if (tpipe) fclose(tpipe);
4394 /* once the subprocess is spawned, it has copied the symbols and
4395 we can get rid of ours */
4397 for (j = 0; j < 4; j++) {
4398 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4399 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4400 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4402 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4403 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4404 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4405 vms_execfree(vmscmd);
4407 #ifdef PERL_IMPLICIT_CONTEXT
4410 PL_forkprocess = info->pid;
4417 _ckvmssts_noperl(sys$setast(0));
4419 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4420 _ckvmssts_noperl(sys$setast(1));
4421 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4423 *psts = info->completion;
4424 /* Caller thinks it is open and tries to close it. */
4425 /* This causes some problems, as it changes the error status */
4426 /* my_pclose(info->fp); */
4428 /* If we did not have a file pointer open, then we have to */
4429 /* clean up here or eventually we will run out of something */
4431 if (info->fp == NULL) {
4432 my_pclose_pinfo(aTHX_ info);
4440 } /* end of safe_popen */
4443 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4445 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4449 TAINT_PROPER("popen");
4450 PERL_FLUSHALL_FOR_CHILD;
4451 return safe_popen(aTHX_ cmd,mode,&sts);
4457 /* Routine to close and cleanup a pipe info structure */
4459 static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4461 unsigned long int retsts;
4465 /* If we were writing to a subprocess, insure that someone reading from
4466 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4467 * produce an EOF record in the mailbox.
4469 * well, at least sometimes it *does*, so we have to watch out for
4470 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4474 #if defined(USE_ITHREADS)
4478 && PL_perlio_fd_refcnt
4481 PerlIO_flush(info->fp);
4483 fflush((FILE *)info->fp);
4486 _ckvmssts(sys$setast(0));
4487 info->closing = TRUE;
4488 done = info->done && info->in_done && info->out_done && info->err_done;
4489 /* hanging on write to Perl's input? cancel it */
4490 if (info->mode == 'r' && info->out && !info->out_done) {
4491 if (info->out->chan_out) {
4492 _ckvmssts(sys$cancel(info->out->chan_out));
4493 if (!info->out->chan_in) { /* EOF generation, need AST */
4494 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4498 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4499 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4501 _ckvmssts(sys$setast(1));
4504 #if defined(USE_ITHREADS)
4508 && PL_perlio_fd_refcnt
4511 PerlIO_close(info->fp);
4513 fclose((FILE *)info->fp);
4516 we have to wait until subprocess completes, but ALSO wait until all
4517 the i/o completes...otherwise we'll be freeing the "info" structure
4518 that the i/o ASTs could still be using...
4522 _ckvmssts(sys$setast(0));
4523 done = info->done && info->in_done && info->out_done && info->err_done;
4524 if (!done) _ckvmssts(sys$clref(pipe_ef));
4525 _ckvmssts(sys$setast(1));
4526 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4528 retsts = info->completion;
4530 /* remove from list of open pipes */
4531 _ckvmssts(sys$setast(0));
4533 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4539 last->next = info->next;
4541 open_pipes = info->next;
4542 _ckvmssts(sys$setast(1));
4544 /* free buffers and structures */
4547 if (info->in->buf) {
4548 n = info->in->bufsize * sizeof(char);
4549 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4552 _ckvmssts(lib$free_vm(&n, &info->in));
4555 if (info->out->buf) {
4556 n = info->out->bufsize * sizeof(char);
4557 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4560 _ckvmssts(lib$free_vm(&n, &info->out));
4563 if (info->err->buf) {
4564 n = info->err->bufsize * sizeof(char);
4565 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4568 _ckvmssts(lib$free_vm(&n, &info->err));
4571 _ckvmssts(lib$free_vm(&n, &info));
4577 /*{{{ I32 my_pclose(PerlIO *fp)*/
4578 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4580 pInfo info, last = NULL;
4583 /* Fixme - need ast and mutex protection here */
4584 for (info = open_pipes; info != NULL; last = info, info = info->next)
4585 if (info->fp == fp) break;
4587 if (info == NULL) { /* no such pipe open */
4588 set_errno(ECHILD); /* quoth POSIX */
4589 set_vaxc_errno(SS$_NONEXPR);
4593 ret_status = my_pclose_pinfo(aTHX_ info);
4597 } /* end of my_pclose() */
4599 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4600 /* Roll our own prototype because we want this regardless of whether
4601 * _VMS_WAIT is defined.
4607 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4613 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4614 created with popen(); otherwise partially emulate waitpid() unless
4615 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4616 Also check processes not considered by the CRTL waitpid().
4618 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4620 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4627 if (statusp) *statusp = 0;
4629 for (info = open_pipes; info != NULL; info = info->next)
4630 if (info->pid == pid) break;
4632 if (info != NULL) { /* we know about this child */
4633 while (!info->done) {
4634 _ckvmssts(sys$setast(0));
4636 if (!done) _ckvmssts(sys$clref(pipe_ef));
4637 _ckvmssts(sys$setast(1));
4638 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4641 if (statusp) *statusp = info->completion;
4645 /* child that already terminated? */
4647 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4648 if (closed_list[j].pid == pid) {
4649 if (statusp) *statusp = closed_list[j].completion;
4654 /* fall through if this child is not one of our own pipe children */
4656 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4658 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4659 * in 7.2 did we get a version that fills in the VMS completion
4660 * status as Perl has always tried to do.
4663 sts = __vms_waitpid( pid, statusp, flags );
4665 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4668 /* If the real waitpid tells us the child does not exist, we
4669 * fall through here to implement waiting for a child that
4670 * was created by some means other than exec() (say, spawned
4671 * from DCL) or to wait for a process that is not a subprocess
4672 * of the current process.
4675 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4678 $DESCRIPTOR(intdsc,"0 00:00:01");
4679 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4680 unsigned long int pidcode = JPI$_PID, mypid;
4681 unsigned long int interval[2];
4682 unsigned int jpi_iosb[2];
4683 struct itmlst_3 jpilist[2] = {
4684 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4689 /* Sorry folks, we don't presently implement rooting around for
4690 the first child we can find, and we definitely don't want to
4691 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4697 /* Get the owner of the child so I can warn if it's not mine. If the
4698 * process doesn't exist or I don't have the privs to look at it,
4699 * I can go home early.
4701 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4702 if (sts & 1) sts = jpi_iosb[0];
4714 set_vaxc_errno(sts);
4718 if (ckWARN(WARN_EXEC)) {
4719 /* remind folks they are asking for non-standard waitpid behavior */
4720 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4721 if (ownerpid != mypid)
4722 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4723 "waitpid: process %x is not a child of process %x",
4727 /* simply check on it once a second until it's not there anymore. */
4729 _ckvmssts(sys$bintim(&intdsc,interval));
4730 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4731 _ckvmssts(sys$schdwk(0,0,interval,0));
4732 _ckvmssts(sys$hiber());
4734 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4739 } /* end of waitpid() */
4744 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4746 my_gconvert(double val, int ndig, int trail, char *buf)
4748 static char __gcvtbuf[DBL_DIG+1];
4751 loc = buf ? buf : __gcvtbuf;
4754 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4755 return gcvt(val,ndig,loc);
4758 loc[0] = '0'; loc[1] = '\0';
4765 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4766 static int rms_free_search_context(struct FAB * fab)
4770 nam = fab->fab$l_nam;
4771 nam->nam$b_nop |= NAM$M_SYNCHK;
4772 nam->nam$l_rlf = NULL;
4774 return sys$parse(fab, NULL, NULL);
4777 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4778 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4779 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4780 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4781 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4782 #define rms_nam_esll(nam) nam.nam$b_esl
4783 #define rms_nam_esl(nam) nam.nam$b_esl
4784 #define rms_nam_name(nam) nam.nam$l_name
4785 #define rms_nam_namel(nam) nam.nam$l_name
4786 #define rms_nam_type(nam) nam.nam$l_type
4787 #define rms_nam_typel(nam) nam.nam$l_type
4788 #define rms_nam_ver(nam) nam.nam$l_ver
4789 #define rms_nam_verl(nam) nam.nam$l_ver
4790 #define rms_nam_rsll(nam) nam.nam$b_rsl
4791 #define rms_nam_rsl(nam) nam.nam$b_rsl
4792 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4793 #define rms_set_fna(fab, nam, name, size) \
4794 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4795 #define rms_get_fna(fab, nam) fab.fab$l_fna
4796 #define rms_set_dna(fab, nam, name, size) \
4797 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4798 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4799 #define rms_set_esa(nam, name, size) \
4800 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4801 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4802 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4803 #define rms_set_rsa(nam, name, size) \
4804 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4805 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4806 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4807 #define rms_nam_name_type_l_size(nam) \
4808 (nam.nam$b_name + nam.nam$b_type)
4810 static int rms_free_search_context(struct FAB * fab)
4814 nam = fab->fab$l_naml;
4815 nam->naml$b_nop |= NAM$M_SYNCHK;
4816 nam->naml$l_rlf = NULL;
4817 nam->naml$l_long_defname_size = 0;
4820 return sys$parse(fab, NULL, NULL);
4823 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4824 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4825 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4826 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4827 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4828 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4829 #define rms_nam_esl(nam) nam.naml$b_esl
4830 #define rms_nam_name(nam) nam.naml$l_name
4831 #define rms_nam_namel(nam) nam.naml$l_long_name
4832 #define rms_nam_type(nam) nam.naml$l_type
4833 #define rms_nam_typel(nam) nam.naml$l_long_type
4834 #define rms_nam_ver(nam) nam.naml$l_ver
4835 #define rms_nam_verl(nam) nam.naml$l_long_ver
4836 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4837 #define rms_nam_rsl(nam) nam.naml$b_rsl
4838 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4839 #define rms_set_fna(fab, nam, name, size) \
4840 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4841 nam.naml$l_long_filename_size = size; \
4842 nam.naml$l_long_filename = name;}
4843 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4844 #define rms_set_dna(fab, nam, name, size) \
4845 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4846 nam.naml$l_long_defname_size = size; \
4847 nam.naml$l_long_defname = name; }
4848 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4849 #define rms_set_esa(nam, name, size) \
4850 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4851 nam.naml$l_long_expand_alloc = size; \
4852 nam.naml$l_long_expand = name; }
4853 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4854 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4855 nam.naml$l_long_expand = l_name; \
4856 nam.naml$l_long_expand_alloc = l_size; }
4857 #define rms_set_rsa(nam, name, size) \
4858 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4859 nam.naml$l_long_result = name; \
4860 nam.naml$l_long_result_alloc = size; }
4861 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4862 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4863 nam.naml$l_long_result = l_name; \
4864 nam.naml$l_long_result_alloc = l_size; }
4865 #define rms_nam_name_type_l_size(nam) \
4866 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4871 * The CRTL for 8.3 and later can create symbolic links in any mode,
4872 * however in 8.3 the unlink/remove/delete routines will only properly handle
4873 * them if one of the PCP modes is active.
4875 static int rms_erase(const char * vmsname)
4878 struct FAB myfab = cc$rms_fab;
4879 rms_setup_nam(mynam);
4881 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4882 rms_bind_fab_nam(myfab, mynam);
4884 #ifdef NAML$M_OPEN_SPECIAL
4885 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
4888 status = sys$erase(&myfab, 0, 0);
4895 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
4896 const struct dsc$descriptor_s * vms_dst_dsc,
4897 unsigned long flags)
4899 /* VMS and UNIX handle file permissions differently and the
4900 * the same ACL trick may be needed for renaming files,
4901 * especially if they are directories.
4904 /* todo: get kill_file and rename to share common code */
4905 /* I can not find online documentation for $change_acl
4906 * it appears to be replaced by $set_security some time ago */
4908 const unsigned int access_mode = 0;
4909 $DESCRIPTOR(obj_file_dsc,"FILE");
4912 unsigned long int jpicode = JPI$_UIC;
4913 int aclsts, fndsts, rnsts = -1;
4914 unsigned int ctx = 0;
4915 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4916 struct dsc$descriptor_s * clean_dsc;
4919 unsigned char myace$b_length;
4920 unsigned char myace$b_type;
4921 unsigned short int myace$w_flags;
4922 unsigned long int myace$l_access;
4923 unsigned long int myace$l_ident;
4924 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
4925 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
4927 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
4930 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
4931 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
4933 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
4934 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
4938 /* Expand the input spec using RMS, since we do not want to put
4939 * ACLs on the target of a symbolic link */
4940 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
4941 if (vmsname == NULL)
4944 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
4946 PERL_RMSEXPAND_M_SYMLINK);
4948 PerlMem_free(vmsname);
4952 /* So we get our own UIC to use as a rights identifier,
4953 * and the insert an ACE at the head of the ACL which allows us
4954 * to delete the file.
4956 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
4958 fildsc.dsc$w_length = strlen(vmsname);
4959 fildsc.dsc$a_pointer = vmsname;
4961 newace.myace$l_ident = oldace.myace$l_ident;
4964 /* Grab any existing ACEs with this identifier in case we fail */
4965 clean_dsc = &fildsc;
4966 aclsts = fndsts = sys$get_security(&obj_file_dsc,
4974 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
4975 /* Add the new ACE . . . */
4977 /* if the sys$get_security succeeded, then ctx is valid, and the
4978 * object/file descriptors will be ignored. But otherwise they
4981 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
4982 OSS$M_RELCTX, addlst, &ctx, &access_mode);
4983 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
4985 set_vaxc_errno(aclsts);
4986 PerlMem_free(vmsname);
4990 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
4993 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
4995 if ($VMS_STATUS_SUCCESS(rnsts)) {
4996 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
4999 /* Put things back the way they were. */
5001 aclsts = sys$get_security(&obj_file_dsc,
5009 if ($VMS_STATUS_SUCCESS(aclsts)) {
5013 if (!$VMS_STATUS_SUCCESS(fndsts))
5014 sec_flags = OSS$M_RELCTX;
5016 /* Get rid of the new ACE */
5017 aclsts = sys$set_security(NULL, NULL, NULL,
5018 sec_flags, dellst, &ctx, &access_mode);
5020 /* If there was an old ACE, put it back */
5021 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5022 addlst[0].bufadr = &oldace;
5023 aclsts = sys$set_security(NULL, NULL, NULL,
5024 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5025 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5027 set_vaxc_errno(aclsts);
5033 /* Try to clear the lock on the ACL list */
5034 aclsts2 = sys$set_security(NULL, NULL, NULL,
5035 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5037 /* Rename errors are most important */
5038 if (!$VMS_STATUS_SUCCESS(rnsts))
5041 set_vaxc_errno(aclsts);
5046 if (aclsts != SS$_ACLEMPTY)
5053 PerlMem_free(vmsname);
5058 /*{{{int rename(const char *, const char * */
5059 /* Not exactly what X/Open says to do, but doing it absolutely right
5060 * and efficiently would require a lot more work. This should be close
5061 * enough to pass all but the most strict X/Open compliance test.
5064 Perl_rename(pTHX_ const char *src, const char * dst)
5073 /* Validate the source file */
5074 src_sts = flex_lstat(src, &src_st);
5077 /* No source file or other problem */
5080 if (src_st.st_devnam[0] == 0) {
5081 /* This may be possible so fail if it is seen. */
5086 dst_sts = flex_lstat(dst, &dst_st);
5089 if (dst_st.st_dev != src_st.st_dev) {
5090 /* Must be on the same device */
5095 /* VMS_INO_T_COMPARE is true if the inodes are different
5096 * to match the output of memcmp
5099 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5100 /* That was easy, the files are the same! */
5104 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5105 /* If source is a directory, so must be dest */
5113 if ((dst_sts == 0) &&
5114 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5116 /* We have issues here if vms_unlink_all_versions is set
5117 * If the destination exists, and is not a directory, then
5118 * we must delete in advance.
5120 * If the src is a directory, then we must always pre-delete
5123 * If we successfully delete the dst in advance, and the rename fails
5124 * X/Open requires that errno be EIO.
5128 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5130 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
5131 S_ISDIR(dst_st.st_mode));
5133 /* Need to delete all versions ? */
5134 if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5137 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
5138 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
5143 /* Make sure that we do not loop forever */
5155 /* We killed the destination, so only errno now is EIO */
5160 /* Originally the idea was to call the CRTL rename() and only
5161 * try the lib$rename_file if it failed.
5162 * It turns out that there are too many variants in what the
5163 * the CRTL rename might do, so only use lib$rename_file
5168 /* Is the source and dest both in VMS format */
5169 /* if the source is a directory, then need to fileify */
5170 /* and dest must be a directory or non-existent. */
5175 unsigned long flags;
5176 struct dsc$descriptor_s old_file_dsc;
5177 struct dsc$descriptor_s new_file_dsc;
5179 /* We need to modify the src and dst depending
5180 * on if one or more of them are directories.
5183 vms_dst = (char *)PerlMem_malloc(VMS_MAXRSS);
5184 if (vms_dst == NULL)
5185 _ckvmssts_noperl(SS$_INSFMEM);
5187 if (S_ISDIR(src_st.st_mode)) {
5189 char * vms_dir_file;
5191 vms_dir_file = (char *)PerlMem_malloc(VMS_MAXRSS);
5192 if (vms_dir_file == NULL)
5193 _ckvmssts_noperl(SS$_INSFMEM);
5195 /* If the dest is a directory, we must remove it */
5198 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
5200 PerlMem_free(vms_dst);
5208 /* The dest must be a VMS file specification */
5209 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5210 if (ret_str == NULL) {
5211 PerlMem_free(vms_dst);
5216 /* The source must be a file specification */
5217 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5218 if (ret_str == NULL) {
5219 PerlMem_free(vms_dst);
5220 PerlMem_free(vms_dir_file);
5224 PerlMem_free(vms_dst);
5225 vms_dst = vms_dir_file;
5228 /* File to file or file to new dir */
5230 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5231 /* VMS pathify a dir target */
5232 ret_str = int_tovmspath(dst, vms_dst, NULL);
5233 if (ret_str == NULL) {
5234 PerlMem_free(vms_dst);
5239 char * v_spec, * r_spec, * d_spec, * n_spec;
5240 char * e_spec, * vs_spec;
5241 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5243 /* fileify a target VMS file specification */
5244 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5245 if (ret_str == NULL) {
5246 PerlMem_free(vms_dst);
5251 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5252 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5253 &e_len, &vs_spec, &vs_len);
5256 /* Get rid of the version */
5260 /* Need to specify a '.' so that the extension */
5261 /* is not inherited */
5262 strcat(vms_dst,".");
5268 old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5269 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
5270 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5271 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5273 new_file_dsc.dsc$a_pointer = vms_dst;
5274 new_file_dsc.dsc$w_length = strlen(vms_dst);
5275 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5276 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5279 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5280 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5283 sts = lib$rename_file(&old_file_dsc,
5287 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5288 if (!$VMS_STATUS_SUCCESS(sts)) {
5290 /* We could have failed because VMS style permissions do not
5291 * permit renames that UNIX will allow. Just like the hack
5294 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5297 PerlMem_free(vms_dst);
5298 if (!$VMS_STATUS_SUCCESS(sts)) {
5305 if (vms_unlink_all_versions) {
5306 /* Now get rid of any previous versions of the source file that
5312 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5313 S_ISDIR(src_st.st_mode));
5314 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
5315 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5316 S_ISDIR(src_st.st_mode));
5321 /* Make sure that we do not loop forever */
5330 /* We deleted the destination, so must force the error to be EIO */
5331 if ((retval != 0) && (pre_delete != 0))
5339 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5340 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5341 * to expand file specification. Allows for a single default file
5342 * specification and a simple mask of options. If outbuf is non-NULL,
5343 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5344 * the resultant file specification is placed. If outbuf is NULL, the
5345 * resultant file specification is placed into a static buffer.
5346 * The third argument, if non-NULL, is taken to be a default file
5347 * specification string. The fourth argument is unused at present.
5348 * rmesexpand() returns the address of the resultant string if
5349 * successful, and NULL on error.
5351 * New functionality for previously unused opts value:
5352 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5353 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5354 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5355 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5357 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5361 (const char *filespec,
5363 const char *defspec,
5369 const char * in_spec;
5371 const char * def_spec;
5372 char * vmsfspec, *vmsdefspec;
5376 struct FAB myfab = cc$rms_fab;
5377 rms_setup_nam(mynam);
5379 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5382 /* temp hack until UTF8 is actually implemented */
5383 if (fs_utf8 != NULL)
5386 if (!filespec || !*filespec) {
5387 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5397 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5398 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5399 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5401 /* If this is a UNIX file spec, convert it to VMS */
5402 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5403 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5404 &e_len, &vs_spec, &vs_len);
5409 vmsfspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5410 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5411 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5412 if (ret_spec == NULL) {
5413 PerlMem_free(vmsfspec);
5416 in_spec = (const char *)vmsfspec;
5418 /* Unless we are forcing to VMS format, a UNIX input means
5419 * UNIX output, and that requires long names to be used
5421 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5422 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5423 opts |= PERL_RMSEXPAND_M_LONG;
5433 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5434 rms_bind_fab_nam(myfab, mynam);
5436 /* Process the default file specification if present */
5438 if (defspec && *defspec) {
5440 t_isunix = is_unix_filespec(defspec);
5442 vmsdefspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5443 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5444 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5446 if (ret_spec == NULL) {
5447 /* Clean up and bail */
5448 PerlMem_free(vmsdefspec);
5449 if (vmsfspec != NULL)
5450 PerlMem_free(vmsfspec);
5453 def_spec = (const char *)vmsdefspec;
5455 rms_set_dna(myfab, mynam,
5456 (char *)def_spec, strlen(def_spec)); /* cast ok */
5459 /* Now we need the expansion buffers */
5460 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
5461 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5462 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5463 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
5464 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5466 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5468 /* If a NAML block is used RMS always writes to the long and short
5469 * addresses unless you suppress the short name.
5471 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5472 outbufl = (char *)PerlMem_malloc(VMS_MAXRSS);
5473 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5475 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5477 #ifdef NAM$M_NO_SHORT_UPCASE
5478 if (decc_efs_case_preserve)
5479 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5482 /* We may not want to follow symbolic links */
5483 #ifdef NAML$M_OPEN_SPECIAL
5484 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5485 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5488 /* First attempt to parse as an existing file */
5489 retsts = sys$parse(&myfab,0,0);
5490 if (!(retsts & STS$K_SUCCESS)) {
5492 /* Could not find the file, try as syntax only if error is not fatal */
5493 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5494 if (retsts == RMS$_DNF ||
5495 retsts == RMS$_DIR ||
5496 retsts == RMS$_DEV ||
5497 retsts == RMS$_PRV) {
5498 retsts = sys$parse(&myfab,0,0);
5499 if (retsts & STS$K_SUCCESS) goto int_expanded;
5502 /* Still could not parse the file specification */
5503 /*----------------------------------------------*/
5504 sts = rms_free_search_context(&myfab); /* Free search context */
5505 if (vmsdefspec != NULL)
5506 PerlMem_free(vmsdefspec);
5507 if (vmsfspec != NULL)
5508 PerlMem_free(vmsfspec);
5509 if (outbufl != NULL)
5510 PerlMem_free(outbufl);
5514 set_vaxc_errno(retsts);
5515 if (retsts == RMS$_PRV) set_errno(EACCES);
5516 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5517 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5518 else set_errno(EVMSERR);
5521 retsts = sys$search(&myfab,0,0);
5522 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5523 sts = rms_free_search_context(&myfab); /* Free search context */
5524 if (vmsdefspec != NULL)
5525 PerlMem_free(vmsdefspec);
5526 if (vmsfspec != NULL)
5527 PerlMem_free(vmsfspec);
5528 if (outbufl != NULL)
5529 PerlMem_free(outbufl);
5533 set_vaxc_errno(retsts);
5534 if (retsts == RMS$_PRV) set_errno(EACCES);
5535 else set_errno(EVMSERR);
5539 /* If the input filespec contained any lowercase characters,
5540 * downcase the result for compatibility with Unix-minded code. */
5542 if (!decc_efs_case_preserve) {
5544 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5545 if (islower(*tbuf)) { haslower = 1; break; }
5548 /* Is a long or a short name expected */
5549 /*------------------------------------*/
5551 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5552 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5553 if (rms_nam_rsll(mynam)) {
5555 speclen = rms_nam_rsll(mynam);
5558 spec_buf = esal; /* Not esa */
5559 speclen = rms_nam_esll(mynam);
5564 if (rms_nam_rsl(mynam)) {
5566 speclen = rms_nam_rsl(mynam);
5569 spec_buf = esa; /* Not esal */
5570 speclen = rms_nam_esl(mynam);
5572 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5575 spec_buf[speclen] = '\0';
5577 /* Trim off null fields added by $PARSE
5578 * If type > 1 char, must have been specified in original or default spec
5579 * (not true for version; $SEARCH may have added version of existing file).
5581 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5582 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5583 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5584 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5587 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5588 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5590 if (trimver || trimtype) {
5591 if (defspec && *defspec) {
5592 char *defesal = NULL;
5593 char *defesa = NULL;
5594 defesa = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5595 if (defesa != NULL) {
5596 struct FAB deffab = cc$rms_fab;
5597 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5598 defesal = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5599 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5601 rms_setup_nam(defnam);
5603 rms_bind_fab_nam(deffab, defnam);
5607 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5609 /* RMS needs the esa/esal as a work area if wildcards are involved */
5610 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5612 rms_clear_nam_nop(defnam);
5613 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5614 #ifdef NAM$M_NO_SHORT_UPCASE
5615 if (decc_efs_case_preserve)
5616 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5618 #ifdef NAML$M_OPEN_SPECIAL
5619 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5620 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5622 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5624 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5627 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5630 if (defesal != NULL)
5631 PerlMem_free(defesal);
5632 PerlMem_free(defesa);
5634 _ckvmssts_noperl(SS$_INSFMEM);
5638 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5639 if (*(rms_nam_verl(mynam)) != '\"')
5640 speclen = rms_nam_verl(mynam) - spec_buf;
5643 if (*(rms_nam_ver(mynam)) != '\"')
5644 speclen = rms_nam_ver(mynam) - spec_buf;
5648 /* If we didn't already trim version, copy down */
5649 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5650 if (speclen > rms_nam_verl(mynam) - spec_buf)
5652 (rms_nam_typel(mynam),
5653 rms_nam_verl(mynam),
5654 speclen - (rms_nam_verl(mynam) - spec_buf));
5655 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5658 if (speclen > rms_nam_ver(mynam) - spec_buf)
5660 (rms_nam_type(mynam),
5662 speclen - (rms_nam_ver(mynam) - spec_buf));
5663 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5668 /* Done with these copies of the input files */
5669 /*-------------------------------------------*/
5670 if (vmsfspec != NULL)
5671 PerlMem_free(vmsfspec);
5672 if (vmsdefspec != NULL)
5673 PerlMem_free(vmsdefspec);
5675 /* If we just had a directory spec on input, $PARSE "helpfully"
5676 * adds an empty name and type for us */
5677 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5678 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5679 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5680 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5681 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5682 speclen = rms_nam_namel(mynam) - spec_buf;
5687 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5688 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5689 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5690 speclen = rms_nam_name(mynam) - spec_buf;
5693 /* Posix format specifications must have matching quotes */
5694 if (speclen < (VMS_MAXRSS - 1)) {
5695 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5696 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5697 spec_buf[speclen] = '\"';
5702 spec_buf[speclen] = '\0';
5703 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5705 /* Have we been working with an expanded, but not resultant, spec? */
5706 /* Also, convert back to Unix syntax if necessary. */
5710 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5711 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5712 rsl = rms_nam_rsll(mynam);
5716 rsl = rms_nam_rsl(mynam);
5719 /* rsl is not present, it means that spec_buf is either */
5720 /* esa or esal, and needs to be copied to outbuf */
5721 /* convert to Unix if desired */
5723 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5725 /* VMS file specs are not in UTF-8 */
5726 if (fs_utf8 != NULL)
5728 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5733 /* Now spec_buf is either outbuf or outbufl */
5734 /* We need the result into outbuf */
5736 /* If we need this in UNIX, then we need another buffer */
5737 /* to keep things in order */
5739 char * new_src = NULL;
5740 if (spec_buf == outbuf) {
5741 new_src = (char *)PerlMem_malloc(VMS_MAXRSS);
5742 my_strlcpy(new_src, spec_buf, VMS_MAXRSS);
5746 ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5748 PerlMem_free(new_src);
5751 /* VMS file specs are not in UTF-8 */
5752 if (fs_utf8 != NULL)
5755 /* Copy the buffer if needed */
5756 if (outbuf != spec_buf)
5757 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5763 /* Need to clean up the search context */
5764 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5765 sts = rms_free_search_context(&myfab); /* Free search context */
5767 /* Clean up the extra buffers */
5771 if (outbufl != NULL)
5772 PerlMem_free(outbufl);
5774 /* Return the result */
5778 /* Common simple case - Expand an already VMS spec */
5780 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5781 opts |= PERL_RMSEXPAND_M_VMS_IN;
5782 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5785 /* Common simple case - Expand to a VMS spec */
5787 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5788 opts |= PERL_RMSEXPAND_M_VMS;
5789 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5793 /* Entry point used by perl routines */
5796 (pTHX_ const char *filespec,
5799 const char *defspec,
5804 static char __rmsexpand_retbuf[VMS_MAXRSS];
5805 char * expanded, *ret_spec, *ret_buf;
5809 if (ret_buf == NULL) {
5811 Newx(expanded, VMS_MAXRSS, char);
5812 if (expanded == NULL)
5813 _ckvmssts(SS$_INSFMEM);
5816 ret_buf = __rmsexpand_retbuf;
5821 ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5822 opts, fs_utf8, dfs_utf8);
5824 if (ret_spec == NULL) {
5825 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5833 /* External entry points */
5834 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5835 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5836 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5837 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5838 char *Perl_rmsexpand_utf8
5839 (pTHX_ const char *spec, char *buf, const char *def,
5840 unsigned opt, int * fs_utf8, int * dfs_utf8)
5841 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5842 char *Perl_rmsexpand_utf8_ts
5843 (pTHX_ const char *spec, char *buf, const char *def,
5844 unsigned opt, int * fs_utf8, int * dfs_utf8)
5845 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
5849 ** The following routines are provided to make life easier when
5850 ** converting among VMS-style and Unix-style directory specifications.
5851 ** All will take input specifications in either VMS or Unix syntax. On
5852 ** failure, all return NULL. If successful, the routines listed below
5853 ** return a pointer to a buffer containing the appropriately
5854 ** reformatted spec (and, therefore, subsequent calls to that routine
5855 ** will clobber the result), while the routines of the same names with
5856 ** a _ts suffix appended will return a pointer to a mallocd string
5857 ** containing the appropriately reformatted spec.
5858 ** In all cases, only explicit syntax is altered; no check is made that
5859 ** the resulting string is valid or that the directory in question
5862 ** fileify_dirspec() - convert a directory spec into the name of the
5863 ** directory file (i.e. what you can stat() to see if it's a dir).
5864 ** The style (VMS or Unix) of the result is the same as the style
5865 ** of the parameter passed in.
5866 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5867 ** what you prepend to a filename to indicate what directory it's in).
5868 ** The style (VMS or Unix) of the result is the same as the style
5869 ** of the parameter passed in.
5870 ** tounixpath() - convert a directory spec into a Unix-style path.
5871 ** tovmspath() - convert a directory spec into a VMS-style path.
5872 ** tounixspec() - convert any file spec into a Unix-style file spec.
5873 ** tovmsspec() - convert any file spec into a VMS-style spec.
5874 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5876 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
5877 ** Permission is given to distribute this code as part of the Perl
5878 ** standard distribution under the terms of the GNU General Public
5879 ** License or the Perl Artistic License. Copies of each may be
5880 ** found in the Perl standard distribution.
5883 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5885 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
5887 unsigned long int dirlen, retlen, hasfilename = 0;
5888 char *cp1, *cp2, *lastdir;
5889 char *trndir, *vmsdir;
5890 unsigned short int trnlnm_iter_count;
5892 if (utf8_fl != NULL)
5895 if (!dir || !*dir) {
5896 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5898 dirlen = strlen(dir);
5899 while (dirlen && dir[dirlen-1] == '/') --dirlen;
5900 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
5901 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
5908 if (dirlen > (VMS_MAXRSS - 1)) {
5909 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
5912 trndir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5913 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5914 if (!strpbrk(dir+1,"/]>:") &&
5915 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
5916 strcpy(trndir,*dir == '/' ? dir + 1: dir);
5917 trnlnm_iter_count = 0;
5918 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
5919 trnlnm_iter_count++;
5920 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5922 dirlen = strlen(trndir);
5925 memcpy(trndir, dir, dirlen);
5926 trndir[dirlen] = '\0';
5929 /* At this point we are done with *dir and use *trndir which is a
5930 * copy that can be modified. *dir must not be modified.
5933 /* If we were handed a rooted logical name or spec, treat it like a
5934 * simple directory, so that
5935 * $ Define myroot dev:[dir.]
5936 * ... do_fileify_dirspec("myroot",buf,1) ...
5937 * does something useful.
5939 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
5940 trndir[--dirlen] = '\0';
5941 trndir[dirlen-1] = ']';
5943 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
5944 trndir[--dirlen] = '\0';
5945 trndir[dirlen-1] = '>';
5948 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
5949 /* If we've got an explicit filename, we can just shuffle the string. */
5950 if (*(cp1+1)) hasfilename = 1;
5951 /* Similarly, we can just back up a level if we've got multiple levels
5952 of explicit directories in a VMS spec which ends with directories. */
5954 for (cp2 = cp1; cp2 > trndir; cp2--) {
5956 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
5957 /* fix-me, can not scan EFS file specs backward like this */
5958 *cp2 = *cp1; *cp1 = '\0';
5963 if (*cp2 == '[' || *cp2 == '<') break;
5968 vmsdir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5969 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5970 cp1 = strpbrk(trndir,"]:>");
5971 if (hasfilename || !cp1) { /* filename present or not VMS */
5973 if (trndir[0] == '.') {
5974 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
5975 PerlMem_free(trndir);
5976 PerlMem_free(vmsdir);
5977 return int_fileify_dirspec("[]", buf, NULL);
5979 else if (trndir[1] == '.' &&
5980 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
5981 PerlMem_free(trndir);
5982 PerlMem_free(vmsdir);
5983 return int_fileify_dirspec("[-]", buf, NULL);
5986 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
5987 dirlen -= 1; /* to last element */
5988 lastdir = strrchr(trndir,'/');
5990 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
5991 /* If we have "/." or "/..", VMSify it and let the VMS code
5992 * below expand it, rather than repeating the code to handle
5993 * relative components of a filespec here */
5995 if (*(cp1+2) == '.') cp1++;
5996 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
5998 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
5999 PerlMem_free(trndir);
6000 PerlMem_free(vmsdir);
6003 if (strchr(vmsdir,'/') != NULL) {
6004 /* If int_tovmsspec() returned it, it must have VMS syntax
6005 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6006 * the time to check this here only so we avoid a recursion
6007 * loop; otherwise, gigo.
6009 PerlMem_free(trndir);
6010 PerlMem_free(vmsdir);
6011 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6014 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6015 PerlMem_free(trndir);
6016 PerlMem_free(vmsdir);
6019 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6020 PerlMem_free(trndir);
6021 PerlMem_free(vmsdir);
6025 } while ((cp1 = strstr(cp1,"/.")) != NULL);
6026 lastdir = strrchr(trndir,'/');
6028 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6030 /* Ditto for specs that end in an MFD -- let the VMS code
6031 * figure out whether it's a real device or a rooted logical. */
6033 /* This should not happen any more. Allowing the fake /000000
6034 * in a UNIX pathname causes all sorts of problems when trying
6035 * to run in UNIX emulation. So the VMS to UNIX conversions
6036 * now remove the fake /000000 directories.
6039 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6040 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6041 PerlMem_free(trndir);
6042 PerlMem_free(vmsdir);
6045 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6046 PerlMem_free(trndir);
6047 PerlMem_free(vmsdir);
6050 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6051 PerlMem_free(trndir);
6052 PerlMem_free(vmsdir);
6057 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6058 !(lastdir = cp1 = strrchr(trndir,']')) &&
6059 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6061 cp2 = strrchr(cp1,'.');
6063 int e_len, vs_len = 0;
6066 cp3 = strchr(cp2,';');
6067 e_len = strlen(cp2);
6069 vs_len = strlen(cp3);
6070 e_len = e_len - vs_len;
6072 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6074 if (!decc_efs_charset) {
6075 /* If this is not EFS, then not a directory */
6076 PerlMem_free(trndir);
6077 PerlMem_free(vmsdir);
6079 set_vaxc_errno(RMS$_DIR);
6083 /* Ok, here we have an issue, technically if a .dir shows */
6084 /* from inside a directory, then we should treat it as */
6085 /* xxx^.dir.dir. But we do not have that context at this */
6086 /* point unless this is totally restructured, so we remove */
6087 /* The .dir for now, and fix this better later */
6088 dirlen = cp2 - trndir;
6090 if (decc_efs_charset && !strchr(trndir,'/')) {
6091 /* Dots are allowed in dir names, so escape them if input not in Unix syntax. */
6092 char *cp4 = is_dir ? (cp2 - 1) : cp2;
6094 for (; cp4 > cp1; cp4--) {
6096 if ((cp4 - 1 > trndir) && (*(cp4 - 1) != '^')) {
6097 memmove(cp4 + 1, cp4, trndir + dirlen - cp4 + 1);
6108 retlen = dirlen + 6;
6109 memcpy(buf, trndir, dirlen);
6112 /* We've picked up everything up to the directory file name.
6113 Now just add the type and version, and we're set. */
6114 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
6115 strcat(buf,".dir;1");
6117 strcat(buf,".DIR;1");
6118 PerlMem_free(trndir);
6119 PerlMem_free(vmsdir);
6122 else { /* VMS-style directory spec */
6124 char *esa, *esal, term, *cp;
6127 unsigned long int cmplen, haslower = 0;
6128 struct FAB dirfab = cc$rms_fab;
6129 rms_setup_nam(savnam);
6130 rms_setup_nam(dirnam);
6132 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
6133 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6135 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6136 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
6137 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6139 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6140 rms_bind_fab_nam(dirfab, dirnam);
6141 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6142 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6143 #ifdef NAM$M_NO_SHORT_UPCASE
6144 if (decc_efs_case_preserve)
6145 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6148 for (cp = trndir; *cp; cp++)
6149 if (islower(*cp)) { haslower = 1; break; }
6150 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6151 if ((dirfab.fab$l_sts == RMS$_DIR) ||
6152 (dirfab.fab$l_sts == RMS$_DNF) ||
6153 (dirfab.fab$l_sts == RMS$_PRV)) {
6154 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6155 sts = sys$parse(&dirfab);
6161 PerlMem_free(trndir);
6162 PerlMem_free(vmsdir);
6164 set_vaxc_errno(dirfab.fab$l_sts);
6170 /* Does the file really exist? */
6171 if (sys$search(&dirfab)& STS$K_SUCCESS) {
6172 /* Yes; fake the fnb bits so we'll check type below */
6173 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6175 else { /* No; just work with potential name */
6176 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6179 fab_sts = dirfab.fab$l_sts;
6180 sts = rms_free_search_context(&dirfab);
6184 PerlMem_free(trndir);
6185 PerlMem_free(vmsdir);
6186 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
6192 /* Make sure we are using the right buffer */
6193 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6196 my_esa_len = rms_nam_esll(dirnam);
6200 my_esa_len = rms_nam_esl(dirnam);
6201 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6204 my_esa[my_esa_len] = '\0';
6205 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6206 cp1 = strchr(my_esa,']');
6207 if (!cp1) cp1 = strchr(my_esa,'>');
6208 if (cp1) { /* Should always be true */
6209 my_esa_len -= cp1 - my_esa - 1;
6210 memmove(my_esa, cp1 + 1, my_esa_len);
6213 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6214 /* Yep; check version while we're at it, if it's there. */
6215 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6216 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6217 /* Something other than .DIR[;1]. Bzzt. */
6218 sts = rms_free_search_context(&dirfab);
6222 PerlMem_free(trndir);
6223 PerlMem_free(vmsdir);
6225 set_vaxc_errno(RMS$_DIR);
6230 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6231 /* They provided at least the name; we added the type, if necessary, */
6232 my_strlcpy(buf, my_esa, VMS_MAXRSS);
6233 sts = rms_free_search_context(&dirfab);
6234 PerlMem_free(trndir);
6238 PerlMem_free(vmsdir);
6241 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6242 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6246 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6247 if (cp1 == NULL) { /* should never happen */
6248 sts = rms_free_search_context(&dirfab);
6249 PerlMem_free(trndir);
6253 PerlMem_free(vmsdir);
6258 retlen = strlen(my_esa);
6259 cp1 = strrchr(my_esa,'.');
6260 /* ODS-5 directory specifications can have extra "." in them. */
6261 /* Fix-me, can not scan EFS file specifications backwards */
6262 while (cp1 != NULL) {
6263 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6267 while ((cp1 > my_esa) && (*cp1 != '.'))
6274 if ((cp1) != NULL) {
6275 /* There's more than one directory in the path. Just roll back. */
6277 my_strlcpy(buf, my_esa, VMS_MAXRSS);
6280 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6281 /* Go back and expand rooted logical name */
6282 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6283 #ifdef NAM$M_NO_SHORT_UPCASE
6284 if (decc_efs_case_preserve)
6285 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6287 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6288 sts = rms_free_search_context(&dirfab);
6292 PerlMem_free(trndir);
6293 PerlMem_free(vmsdir);
6295 set_vaxc_errno(dirfab.fab$l_sts);
6299 /* This changes the length of the string of course */
6301 my_esa_len = rms_nam_esll(dirnam);
6303 my_esa_len = rms_nam_esl(dirnam);
6306 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6307 cp1 = strstr(my_esa,"][");
6308 if (!cp1) cp1 = strstr(my_esa,"]<");
6309 dirlen = cp1 - my_esa;
6310 memcpy(buf, my_esa, dirlen);
6311 if (!strncmp(cp1+2,"000000]",7)) {
6312 buf[dirlen-1] = '\0';
6313 /* fix-me Not full ODS-5, just extra dots in directories for now */
6314 cp1 = buf + dirlen - 1;
6320 if (*(cp1-1) != '^')
6325 if (*cp1 == '.') *cp1 = ']';
6327 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6328 memmove(cp1+1,"000000]",7);
6332 memmove(buf+dirlen, cp1+2, retlen-dirlen);
6334 /* Convert last '.' to ']' */
6336 while (*cp != '[') {
6339 /* Do not trip on extra dots in ODS-5 directories */
6340 if ((cp1 == buf) || (*(cp1-1) != '^'))
6344 if (*cp1 == '.') *cp1 = ']';
6346 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6347 memmove(cp1+1,"000000]",7);
6351 else { /* This is a top-level dir. Add the MFD to the path. */
6354 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
6355 strcpy(cp2,":[000000]");
6360 sts = rms_free_search_context(&dirfab);
6361 /* We've set up the string up through the filename. Add the
6362 type and version, and we're done. */
6363 strcat(buf,".DIR;1");
6365 /* $PARSE may have upcased filespec, so convert output to lower
6366 * case if input contained any lowercase characters. */
6367 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
6368 PerlMem_free(trndir);
6372 PerlMem_free(vmsdir);
6375 } /* end of int_fileify_dirspec() */
6378 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6379 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6381 static char __fileify_retbuf[VMS_MAXRSS];
6382 char * fileified, *ret_spec, *ret_buf;
6386 if (ret_buf == NULL) {
6388 Newx(fileified, VMS_MAXRSS, char);
6389 if (fileified == NULL)
6390 _ckvmssts(SS$_INSFMEM);
6391 ret_buf = fileified;
6393 ret_buf = __fileify_retbuf;
6397 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6399 if (ret_spec == NULL) {
6400 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6402 Safefree(fileified);
6406 } /* end of do_fileify_dirspec() */
6409 /* External entry points */
6410 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6411 { return do_fileify_dirspec(dir,buf,0,NULL); }
6412 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6413 { return do_fileify_dirspec(dir,buf,1,NULL); }
6414 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6415 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6416 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6417 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6419 static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6420 char * v_spec, int v_len, char * r_spec, int r_len,
6421 char * d_spec, int d_len, char * n_spec, int n_len,
6422 char * e_spec, int e_len, char * vs_spec, int vs_len) {
6424 /* VMS specification - Try to do this the simple way */
6425 if ((v_len + r_len > 0) || (d_len > 0)) {
6428 /* No name or extension component, already a directory */
6429 if ((n_len + e_len + vs_len) == 0) {
6434 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6435 /* This results from catfile() being used instead of catdir() */
6436 /* So even though it should not work, we need to allow it */
6438 /* If this is .DIR;1 then do a simple conversion */
6439 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6440 if (is_dir || (e_len == 0) && (d_len > 0)) {
6442 len = v_len + r_len + d_len - 1;
6443 char dclose = d_spec[d_len - 1];
6444 memcpy(buf, dir, len);
6447 memcpy(&buf[len], n_spec, n_len);
6450 buf[len + 1] = '\0';
6455 else if (d_len > 0) {
6456 /* In the olden days, a directory needed to have a .DIR */
6457 /* extension to be a valid directory, but now it could */
6458 /* be a symbolic link */
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 if (decc_efs_charset) {
6470 && (toupper(e_spec[1]) == 'D')
6471 && (toupper(e_spec[2]) == 'I')
6472 && (toupper(e_spec[3]) == 'R')) {
6474 /* Corner case: directory spec with invalid version.
6475 * Valid would have followed is_dir path above.
6477 SETERRNO(ENOTDIR, RMS$_DIR);
6483 memcpy(&buf[len], e_spec, e_len);
6488 SETERRNO(ENOTDIR, RMS$_DIR);
6493 buf[len + 1] = '\0';
6498 set_vaxc_errno(RMS$_DIR);
6504 set_vaxc_errno(RMS$_DIR);
6510 /* Internal routine to make sure or convert a directory to be in a */
6511 /* path specification. No utf8 flag because it is not changed or used */
6512 static char *int_pathify_dirspec(const char *dir, char *buf)
6514 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6515 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6516 char * exp_spec, *ret_spec;
6518 unsigned short int trnlnm_iter_count;
6522 if (vms_debug_fileify) {
6524 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6526 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6529 /* We may need to lower case the result if we translated */
6530 /* a logical name or got the current working directory */
6533 if (!dir || !*dir) {
6535 set_vaxc_errno(SS$_BADPARAM);
6539 trndir = (char *)PerlMem_malloc(VMS_MAXRSS);
6541 _ckvmssts_noperl(SS$_INSFMEM);
6543 /* If no directory specified use the current default */
6545 my_strlcpy(trndir, dir, VMS_MAXRSS);
6547 getcwd(trndir, VMS_MAXRSS - 1);
6551 /* now deal with bare names that could be logical names */
6552 trnlnm_iter_count = 0;
6553 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6554 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6555 trnlnm_iter_count++;
6557 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6559 trnlen = strlen(trndir);
6561 /* Trap simple rooted lnms, and return lnm:[000000] */
6562 if (!strcmp(trndir+trnlen-2,".]")) {
6563 my_strlcpy(buf, dir, VMS_MAXRSS);
6564 strcat(buf, ":[000000]");
6565 PerlMem_free(trndir);
6567 if (vms_debug_fileify) {
6568 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6574 /* At this point we do not work with *dir, but the copy in *trndir */
6576 if (need_to_lower && !decc_efs_case_preserve) {
6577 /* Legacy mode, lower case the returned value */
6578 __mystrtolower(trndir);
6582 /* Some special cases, '..', '.' */
6584 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6585 /* Force UNIX filespec */
6589 /* Is this Unix or VMS format? */
6590 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6591 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6592 &e_len, &vs_spec, &vs_len);
6595 /* Just a filename? */
6596 if ((v_len + r_len + d_len) == 0) {
6598 /* Now we have a problem, this could be Unix or VMS */
6599 /* We have to guess. .DIR usually means VMS */
6601 /* In UNIX report mode, the .DIR extension is removed */
6602 /* if one shows up, it is for a non-directory or a directory */
6603 /* in EFS charset mode */
6605 /* So if we are in Unix report mode, assume that this */
6606 /* is a relative Unix directory specification */
6609 if (!decc_filename_unix_report && decc_efs_charset) {
6611 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6614 /* Traditional mode, assume .DIR is directory */
6617 memcpy(&buf[2], n_spec, n_len);
6618 buf[n_len + 2] = ']';
6619 buf[n_len + 3] = '\0';
6620 PerlMem_free(trndir);
6621 if (vms_debug_fileify) {
6623 "int_pathify_dirspec: buf = %s\n",
6633 ret_spec = int_pathify_dirspec_simple(trndir, buf,
6634 v_spec, v_len, r_spec, r_len,
6635 d_spec, d_len, n_spec, n_len,
6636 e_spec, e_len, vs_spec, vs_len);
6638 if (ret_spec != NULL) {
6639 PerlMem_free(trndir);
6640 if (vms_debug_fileify) {
6642 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6647 /* Simple way did not work, which means that a logical name */
6648 /* was present for the directory specification. */
6649 /* Need to use an rmsexpand variant to decode it completely */
6650 exp_spec = (char *)PerlMem_malloc(VMS_MAXRSS);
6651 if (exp_spec == NULL)
6652 _ckvmssts_noperl(SS$_INSFMEM);
6654 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6655 if (ret_spec != NULL) {
6656 sts = vms_split_path(exp_spec, &v_spec, &v_len,
6657 &r_spec, &r_len, &d_spec, &d_len,
6658 &n_spec, &n_len, &e_spec,
6659 &e_len, &vs_spec, &vs_len);
6661 ret_spec = int_pathify_dirspec_simple(
6662 exp_spec, buf, v_spec, v_len, r_spec, r_len,
6663 d_spec, d_len, n_spec, n_len,
6664 e_spec, e_len, vs_spec, vs_len);
6666 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6667 /* Legacy mode, lower case the returned value */
6668 __mystrtolower(ret_spec);
6671 set_vaxc_errno(RMS$_DIR);
6676 PerlMem_free(exp_spec);
6677 PerlMem_free(trndir);
6678 if (vms_debug_fileify) {
6679 if (ret_spec == NULL)
6680 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6683 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6688 /* Unix specification, Could be trivial conversion, */
6689 /* but have to deal with trailing '.dir' or extra '.' */
6694 STRLEN dir_len = strlen(trndir);
6696 lastslash = strrchr(trndir, '/');
6697 if (lastslash == NULL)
6704 /* '..' or '.' are valid directory components */
6706 if (lastslash[0] == '.') {
6707 if (lastslash[1] == '\0') {
6709 } else if (lastslash[1] == '.') {
6710 if (lastslash[2] == '\0') {
6713 /* And finally allow '...' */
6714 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6722 lastdot = strrchr(lastslash, '.');
6724 if (lastdot != NULL) {
6726 /* '.dir' is discarded, and any other '.' is invalid */
6727 e_len = strlen(lastdot);
6729 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6732 dir_len = dir_len - 4;
6736 my_strlcpy(buf, trndir, VMS_MAXRSS);
6737 if (buf[dir_len - 1] != '/') {
6739 buf[dir_len + 1] = '\0';
6742 /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6743 if (!decc_efs_charset) {
6746 if (str[0] == '.') {
6749 while ((dots[cnt] == '.') && (cnt < 3))
6752 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6758 for (; *str; ++str) {
6759 while (*str == '/') {
6765 /* Have to skip up to three dots which could be */
6766 /* directories, 3 dots being a VMS extension for Perl */
6769 while ((dots[cnt] == '.') && (cnt < 3)) {
6772 if (dots[cnt] == '\0')
6774 if ((cnt > 1) && (dots[cnt] != '/')) {
6780 /* too many dots? */
6781 if ((cnt == 0) || (cnt > 3)) {
6785 if (!dir_start && (*str == '.')) {
6790 PerlMem_free(trndir);
6792 if (vms_debug_fileify) {
6793 if (ret_spec == NULL)
6794 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6797 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6803 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6804 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6806 static char __pathify_retbuf[VMS_MAXRSS];
6807 char * pathified, *ret_spec, *ret_buf;
6811 if (ret_buf == NULL) {
6813 Newx(pathified, VMS_MAXRSS, char);
6814 if (pathified == NULL)
6815 _ckvmssts(SS$_INSFMEM);
6816 ret_buf = pathified;
6818 ret_buf = __pathify_retbuf;
6822 ret_spec = int_pathify_dirspec(dir, ret_buf);
6824 if (ret_spec == NULL) {
6825 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6827 Safefree(pathified);
6832 } /* end of do_pathify_dirspec() */
6835 /* External entry points */
6836 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6837 { return do_pathify_dirspec(dir,buf,0,NULL); }
6838 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
6839 { return do_pathify_dirspec(dir,buf,1,NULL); }
6840 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
6841 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
6842 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
6843 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
6845 /* Internal tounixspec routine that does not use a thread context */
6846 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
6847 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
6849 char *dirend, *cp1, *cp3, *tmp;
6852 unsigned short int trnlnm_iter_count;
6853 int cmp_rslt, outchars_added;
6854 if (utf8_fl != NULL)
6857 if (vms_debug_fileify) {
6859 fprintf(stderr, "int_tounixspec: spec = NULL\n");
6861 fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
6867 set_vaxc_errno(SS$_BADPARAM);
6870 if (strlen(spec) > (VMS_MAXRSS-1)) {
6872 set_vaxc_errno(SS$_BUFFEROVF);
6876 /* New VMS specific format needs translation
6877 * glob passes filenames with trailing '\n' and expects this preserved.
6879 if (decc_posix_compliant_pathnames) {
6880 if (strncmp(spec, "\"^UP^", 5) == 0) {
6886 tunix = (char *)PerlMem_malloc(VMS_MAXRSS);
6887 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6888 tunix_len = my_strlcpy(tunix, spec, VMS_MAXRSS);
6890 if (tunix[tunix_len - 1] == '\n') {
6891 tunix[tunix_len - 1] = '\"';
6892 tunix[tunix_len] = '\0';
6896 uspec = decc$translate_vms(tunix);
6897 PerlMem_free(tunix);
6898 if ((int)uspec > 0) {
6899 my_strlcpy(rslt, uspec, VMS_MAXRSS);
6904 /* If we can not translate it, makemaker wants as-is */
6905 my_strlcpy(rslt, spec, VMS_MAXRSS);
6912 cmp_rslt = 0; /* Presume VMS */
6913 cp1 = strchr(spec, '/');
6917 /* Look for EFS ^/ */
6918 if (decc_efs_charset) {
6919 while (cp1 != NULL) {
6922 /* Found illegal VMS, assume UNIX */
6927 cp1 = strchr(cp1, '/');
6931 /* Look for "." and ".." */
6932 if (decc_filename_unix_report) {
6933 if (spec[0] == '.') {
6934 if ((spec[1] == '\0') || (spec[1] == '\n')) {
6938 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
6948 /* This is already UNIX or at least nothing VMS understands,
6949 * so all we can reasonably do is unescape extended chars.
6953 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
6954 cp1 += outchars_added;
6957 if (vms_debug_fileify) {
6958 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6963 dirend = strrchr(spec,']');
6964 if (dirend == NULL) dirend = strrchr(spec,'>');
6965 if (dirend == NULL) dirend = strchr(spec,':');
6966 if (dirend == NULL) {
6968 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
6969 cp1 += outchars_added;
6972 if (vms_debug_fileify) {
6973 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6978 /* Special case 1 - sys$posix_root = / */
6979 if (!decc_disable_posix_root) {
6980 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
6987 /* Special case 2 - Convert NLA0: to /dev/null */
6988 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
6989 if (cmp_rslt == 0) {
6990 strcpy(rslt, "/dev/null");
6993 if (spec[6] != '\0') {
7000 /* Also handle special case "SYS$SCRATCH:" */
7001 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7002 tmp = (char *)PerlMem_malloc(VMS_MAXRSS);
7003 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7004 if (cmp_rslt == 0) {
7007 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7009 strcpy(rslt, "/tmp");
7012 if (spec[12] != '\0') {
7020 if (*cp2 != '[' && *cp2 != '<') {
7023 else { /* the VMS spec begins with directories */
7025 if (*cp2 == ']' || *cp2 == '>') {
7026 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
7030 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7031 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7033 if (vms_debug_fileify) {
7034 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7038 trnlnm_iter_count = 0;
7041 while (*cp3 != ':' && *cp3) cp3++;
7043 if (strchr(cp3,']') != NULL) break;
7044 trnlnm_iter_count++;
7045 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7046 } while (vmstrnenv(tmp,tmp,0,fildev,0));
7051 *(cp1++) = *(cp3++);
7052 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7054 set_errno(ENAMETOOLONG);
7055 set_vaxc_errno(SS$_BUFFEROVF);
7056 if (vms_debug_fileify) {
7057 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7059 return NULL; /* No room */
7064 if ((*cp2 == '^')) {
7065 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7066 cp1 += outchars_added;
7068 else if ( *cp2 == '.') {
7069 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7070 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7077 for (; cp2 <= dirend; cp2++) {
7078 if ((*cp2 == '^')) {
7079 /* EFS file escape, pass the next character as is */
7080 /* Fix me: HEX encoding for Unicode not implemented */
7081 *(cp1++) = *(++cp2);
7082 /* An escaped dot stays as is -- don't convert to slash */
7083 if (*cp2 == '.') cp2++;
7087 if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7089 else if (*cp2 == ']' || *cp2 == '>') {
7090 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7092 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7094 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7095 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7096 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7097 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7098 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7100 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7101 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7105 else if (*cp2 == '-') {
7106 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7107 while (*cp2 == '-') {
7109 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7111 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7112 /* filespecs like */
7113 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
7114 if (vms_debug_fileify) {
7115 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7120 else *(cp1++) = *cp2;
7122 else *(cp1++) = *cp2;
7124 /* Translate the rest of the filename. */
7128 /* Fixme - for compatibility with the CRTL we should be removing */
7129 /* spaces from the file specifications, but this may show that */
7130 /* some tests that were appearing to pass are not really passing */
7136 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7137 cp1 += outchars_added;
7140 if (decc_filename_unix_no_version) {
7141 /* Easy, drop the version */
7146 /* Punt - passing the version as a dot will probably */
7147 /* break perl in weird ways, but so did passing */
7148 /* through the ; as a version. Follow the CRTL and */
7149 /* hope for the best. */
7156 /* We will need to fix this properly later */
7157 /* As Perl may be installed on an ODS-5 volume, but not */
7158 /* have the EFS_CHARSET enabled, it still may encounter */
7159 /* filenames with extra dots in them, and a precedent got */
7160 /* set which allowed them to work, that we will uphold here */
7161 /* If extra dots are present in a name and no ^ is on them */
7162 /* VMS assumes that the first one is the extension delimiter */
7163 /* the rest have an implied ^. */
7165 /* this is also a conflict as the . is also a version */
7166 /* delimiter in VMS, */
7168 *(cp1++) = *(cp2++);
7172 /* This is an extension */
7173 if (decc_readdir_dropdotnotype) {
7175 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7176 /* Drop the dot for the extension */
7184 *(cp1++) = *(cp2++);
7189 /* This still leaves /000000/ when working with a
7190 * VMS device root or concealed root.
7196 ulen = strlen(rslt);
7198 /* Get rid of "000000/ in rooted filespecs */
7200 zeros = strstr(rslt, "/000000/");
7201 if (zeros != NULL) {
7203 mlen = ulen - (zeros - rslt) - 7;
7204 memmove(zeros, &zeros[7], mlen);
7211 if (vms_debug_fileify) {
7212 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7216 } /* end of int_tounixspec() */
7219 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7220 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7222 static char __tounixspec_retbuf[VMS_MAXRSS];
7223 char * unixspec, *ret_spec, *ret_buf;
7227 if (ret_buf == NULL) {
7229 Newx(unixspec, VMS_MAXRSS, char);
7230 if (unixspec == NULL)
7231 _ckvmssts(SS$_INSFMEM);
7234 ret_buf = __tounixspec_retbuf;
7238 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7240 if (ret_spec == NULL) {
7241 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7248 } /* end of do_tounixspec() */
7250 /* External entry points */
7251 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7252 { return do_tounixspec(spec,buf,0, NULL); }
7253 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7254 { return do_tounixspec(spec,buf,1, NULL); }
7255 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7256 { return do_tounixspec(spec,buf,0, utf8_fl); }
7257 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7258 { return do_tounixspec(spec,buf,1, utf8_fl); }
7260 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7263 This procedure is used to identify if a path is based in either
7264 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7265 it returns the OpenVMS format directory for it.
7267 It is expecting specifications of only '/' or '/xxxx/'
7269 If a posix root does not exist, or 'xxxx' is not a directory
7270 in the posix root, it returns a failure.
7272 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7274 It is used only internally by posix_to_vmsspec_hardway().
7277 static int posix_root_to_vms
7278 (char *vmspath, int vmspath_len,
7279 const char *unixpath,
7280 const int * utf8_fl)
7283 struct FAB myfab = cc$rms_fab;
7284 rms_setup_nam(mynam);
7285 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7286 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7287 char * esa, * esal, * rsa, * rsal;
7293 unixlen = strlen(unixpath);
7298 #if __CRTL_VER >= 80200000
7299 /* If not a posix spec already, convert it */
7300 if (decc_posix_compliant_pathnames) {
7301 if (strncmp(unixpath,"\"^UP^",5) != 0) {
7302 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7305 /* This is already a VMS specification, no conversion */
7307 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7316 /* Check to see if this is under the POSIX root */
7317 if (decc_disable_posix_root) {
7321 /* Skip leading / */
7322 if (unixpath[0] == '/') {
7328 strcpy(vmspath,"SYS$POSIX_ROOT:");
7330 /* If this is only the / , or blank, then... */
7331 if (unixpath[0] == '\0') {
7332 /* by definition, this is the answer */
7336 /* Need to look up a directory */
7340 /* Copy and add '^' escape characters as needed */
7343 while (unixpath[i] != 0) {
7346 j += copy_expand_unix_filename_escape
7347 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7351 path_len = strlen(vmspath);
7352 if (vmspath[path_len - 1] == '/')
7354 vmspath[path_len] = ']';
7356 vmspath[path_len] = '\0';
7359 vmspath[vmspath_len] = 0;
7360 if (unixpath[unixlen - 1] == '/')
7362 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
7363 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7364 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7365 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7366 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
7367 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7368 rsa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7369 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7370 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7371 rms_bind_fab_nam(myfab, mynam);
7372 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7373 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7374 if (decc_efs_case_preserve)
7375 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7376 #ifdef NAML$M_OPEN_SPECIAL
7377 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7380 /* Set up the remaining naml fields */
7381 sts = sys$parse(&myfab);
7383 /* It failed! Try again as a UNIX filespec */
7392 /* get the Device ID and the FID */
7393 sts = sys$search(&myfab);
7395 /* These are no longer needed */
7400 /* on any failure, returned the POSIX ^UP^ filespec */
7405 specdsc.dsc$a_pointer = vmspath;
7406 specdsc.dsc$w_length = vmspath_len;
7408 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7409 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7410 sts = lib$fid_to_name
7411 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7413 /* on any failure, returned the POSIX ^UP^ filespec */
7415 /* This can happen if user does not have permission to read directories */
7416 if (strncmp(unixpath,"\"^UP^",5) != 0)
7417 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7419 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7422 vmspath[specdsc.dsc$w_length] = 0;
7424 /* Are we expecting a directory? */
7425 if (dir_flag != 0) {
7431 i = specdsc.dsc$w_length - 1;
7435 /* Version must be '1' */
7436 if (vmspath[i--] != '1')
7438 /* Version delimiter is one of ".;" */
7439 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7442 if (vmspath[i--] != 'R')
7444 if (vmspath[i--] != 'I')
7446 if (vmspath[i--] != 'D')
7448 if (vmspath[i--] != '.')
7450 eptr = &vmspath[i+1];
7452 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7453 if (vmspath[i-1] != '^') {
7461 /* Get rid of 6 imaginary zero directory filename */
7462 vmspath[i+1] = '\0';
7466 if (vmspath[i] == '0')
7480 /* /dev/mumble needs to be handled special.
7481 /dev/null becomes NLA0:, And there is the potential for other stuff
7482 like /dev/tty which may need to be mapped to something.
7486 slash_dev_special_to_vms
7487 (const char * unixptr,
7496 nextslash = strchr(unixptr, '/');
7497 len = strlen(unixptr);
7498 if (nextslash != NULL)
7499 len = nextslash - unixptr;
7500 cmp = strncmp("null", unixptr, 5);
7502 if (vmspath_len >= 6) {
7503 strcpy(vmspath, "_NLA0:");
7511 /* The built in routines do not understand perl's special needs, so
7512 doing a manual conversion from UNIX to VMS
7514 If the utf8_fl is not null and points to a non-zero value, then
7515 treat 8 bit characters as UTF-8.
7517 The sequence starting with '$(' and ending with ')' will be passed
7518 through with out interpretation instead of being escaped.
7521 static int posix_to_vmsspec_hardway
7522 (char *vmspath, int vmspath_len,
7523 const char *unixpath,
7528 const char *unixptr;
7529 const char *unixend;
7531 const char *lastslash;
7532 const char *lastdot;
7538 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7539 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7541 if (utf8_fl != NULL)
7547 /* Ignore leading "/" characters */
7548 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7551 unixlen = strlen(unixptr);
7553 /* Do nothing with blank paths */
7560 /* This could have a "^UP^ on the front */
7561 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7567 lastslash = strrchr(unixptr,'/');
7568 lastdot = strrchr(unixptr,'.');
7569 unixend = strrchr(unixptr,'\"');
7570 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7571 unixend = unixptr + unixlen;
7574 /* last dot is last dot or past end of string */
7575 if (lastdot == NULL)
7576 lastdot = unixptr + unixlen;
7578 /* if no directories, set last slash to beginning of string */
7579 if (lastslash == NULL) {
7580 lastslash = unixptr;
7583 /* Watch out for trailing "." after last slash, still a directory */
7584 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7585 lastslash = unixptr + unixlen;
7588 /* Watch out for trailing ".." after last slash, still a directory */
7589 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7590 lastslash = unixptr + unixlen;
7593 /* dots in directories are aways escaped */
7594 if (lastdot < lastslash)
7595 lastdot = unixptr + unixlen;
7598 /* if (unixptr < lastslash) then we are in a directory */
7605 /* Start with the UNIX path */
7606 if (*unixptr != '/') {
7607 /* relative paths */
7609 /* If allowing logical names on relative pathnames, then handle here */
7610 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7611 !decc_posix_compliant_pathnames) {
7617 /* Find the next slash */
7618 nextslash = strchr(unixptr,'/');
7620 esa = (char *)PerlMem_malloc(vmspath_len);
7621 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7623 trn = (char *)PerlMem_malloc(VMS_MAXRSS);
7624 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7626 if (nextslash != NULL) {
7628 seg_len = nextslash - unixptr;
7629 memcpy(esa, unixptr, seg_len);
7633 seg_len = my_strlcpy(esa, unixptr, sizeof(esa));
7635 /* trnlnm(section) */
7636 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7639 /* Now fix up the directory */
7641 /* Split up the path to find the components */
7642 sts = vms_split_path
7660 /* A logical name must be a directory or the full
7661 specification. It is only a full specification if
7662 it is the only component */
7663 if ((unixptr[seg_len] == '\0') ||
7664 (unixptr[seg_len+1] == '\0')) {
7666 /* Is a directory being required? */
7667 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7668 /* Not a logical name */
7673 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7674 /* This must be a directory */
7675 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7676 vmslen = my_strlcpy(vmsptr, esa, vmspath_len - 1);
7677 vmsptr[vmslen] = ':';
7679 vmsptr[vmslen] = '\0';
7687 /* must be dev/directory - ignore version */
7688 if ((n_len + e_len) != 0)
7691 /* transfer the volume */
7692 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7693 memcpy(vmsptr, v_spec, v_len);
7699 /* unroot the rooted directory */
7700 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7702 r_spec[r_len - 1] = ']';
7704 /* This should not be there, but nothing is perfect */
7706 cmp = strcmp(&r_spec[1], "000000.");
7716 memcpy(vmsptr, r_spec, r_len);
7722 /* Bring over the directory. */
7724 ((d_len + vmslen) < vmspath_len)) {
7726 d_spec[d_len - 1] = ']';
7728 cmp = strcmp(&d_spec[1], "000000.");
7739 /* Remove the redundant root */
7747 memcpy(vmsptr, d_spec, d_len);
7761 if (lastslash > unixptr) {
7764 /* skip leading ./ */
7766 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7772 /* Are we still in a directory? */
7773 if (unixptr <= lastslash) {
7778 /* if not backing up, then it is relative forward. */
7779 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7780 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7788 /* Perl wants an empty directory here to tell the difference
7789 * between a DCL command and a filename
7798 /* Handle two special files . and .. */
7799 if (unixptr[0] == '.') {
7800 if (&unixptr[1] == unixend) {
7807 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7818 else { /* Absolute PATH handling */
7822 /* Need to find out where root is */
7824 /* In theory, this procedure should never get an absolute POSIX pathname
7825 * that can not be found on the POSIX root.
7826 * In practice, that can not be relied on, and things will show up
7827 * here that are a VMS device name or concealed logical name instead.
7828 * So to make things work, this procedure must be tolerant.
7830 esa = (char *)PerlMem_malloc(vmspath_len);
7831 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7834 nextslash = strchr(&unixptr[1],'/');
7836 if (nextslash != NULL) {
7838 seg_len = nextslash - &unixptr[1];
7839 my_strlcpy(vmspath, unixptr, seg_len + 2);
7842 cmp = strncmp(vmspath, "dev", 4);
7844 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
7845 if (sts == SS$_NORMAL)
7849 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
7852 if ($VMS_STATUS_SUCCESS(sts)) {
7853 /* This is verified to be a real path */
7855 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
7856 if ($VMS_STATUS_SUCCESS(sts)) {
7857 vmslen = my_strlcpy(vmspath, esa, vmspath_len + 1);
7858 vmsptr = vmspath + vmslen;
7860 if (unixptr < lastslash) {
7869 cmp = strcmp(rptr,"000000.");
7874 } /* removing 6 zeros */
7875 } /* vmslen < 7, no 6 zeros possible */
7876 } /* Not in a directory */
7877 } /* Posix root found */
7879 /* No posix root, fall back to default directory */
7880 strcpy(vmspath, "SYS$DISK:[");
7881 vmsptr = &vmspath[10];
7883 if (unixptr > lastslash) {
7892 } /* end of verified real path handling */
7897 /* Ok, we have a device or a concealed root that is not in POSIX
7898 * or we have garbage. Make the best of it.
7901 /* Posix to VMS destroyed this, so copy it again */
7902 my_strlcpy(vmspath, &unixptr[1], seg_len + 1);
7903 vmslen = strlen(vmspath); /* We know we're truncating. */
7904 vmsptr = &vmsptr[vmslen];
7907 /* Now do we need to add the fake 6 zero directory to it? */
7909 if ((*lastslash == '/') && (nextslash < lastslash)) {
7910 /* No there is another directory */
7917 /* now we have foo:bar or foo:[000000]bar to decide from */
7918 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
7920 if (!islnm && !decc_posix_compliant_pathnames) {
7922 cmp = strncmp("bin", vmspath, 4);
7924 /* bin => SYS$SYSTEM: */
7925 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
7928 /* tmp => SYS$SCRATCH: */
7929 cmp = strncmp("tmp", vmspath, 4);
7931 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
7936 trnend = islnm ? islnm - 1 : 0;
7938 /* if this was a logical name, ']' or '>' must be present */
7939 /* if not a logical name, then assume a device and hope. */
7940 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
7942 /* if log name and trailing '.' then rooted - treat as device */
7943 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
7945 /* Fix me, if not a logical name, a device lookup should be
7946 * done to see if the device is file structured. If the device
7947 * is not file structured, the 6 zeros should not be put on.
7949 * As it is, perl is occasionally looking for dev:[000000]tty.
7950 * which looks a little strange.
7952 * Not that easy to detect as "/dev" may be file structured with
7953 * special device files.
7956 if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
7957 (&nextslash[1] == unixend)) {
7958 /* No real directory present */
7963 /* Put the device delimiter on */
7966 unixptr = nextslash;
7969 /* Start directory if needed */
7970 if (!islnm || add_6zero) {
7976 /* add fake 000000] if needed */
7989 } /* non-POSIX translation */
7991 } /* End of relative/absolute path handling */
7993 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
8000 if (dir_start != 0) {
8002 /* First characters in a directory are handled special */
8003 while ((*unixptr == '/') ||
8004 ((*unixptr == '.') &&
8005 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8006 (&unixptr[1]==unixend)))) {
8011 /* Skip redundant / in specification */
8012 while ((*unixptr == '/') && (dir_start != 0)) {
8015 if (unixptr == lastslash)
8018 if (unixptr == lastslash)
8021 /* Skip redundant ./ characters */
8022 while ((*unixptr == '.') &&
8023 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8026 if (unixptr == lastslash)
8028 if (*unixptr == '/')
8031 if (unixptr == lastslash)
8034 /* Skip redundant ../ characters */
8035 while ((*unixptr == '.') && (unixptr[1] == '.') &&
8036 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8037 /* Set the backing up flag */
8043 unixptr++; /* first . */
8044 unixptr++; /* second . */
8045 if (unixptr == lastslash)
8047 if (*unixptr == '/') /* The slash */
8050 if (unixptr == lastslash)
8053 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8054 /* Not needed when VMS is pretending to be UNIX. */
8056 /* Is this loop stuck because of too many dots? */
8057 if (loop_flag == 0) {
8058 /* Exit the loop and pass the rest through */
8063 /* Are we done with directories yet? */
8064 if (unixptr >= lastslash) {
8066 /* Watch out for trailing dots */
8075 if (*unixptr == '/')
8079 /* Have we stopped backing up? */
8084 /* dir_start continues to be = 1 */
8086 if (*unixptr == '-') {
8088 *vmsptr++ = *unixptr++;
8092 /* Now are we done with directories yet? */
8093 if (unixptr >= lastslash) {
8095 /* Watch out for trailing dots */
8111 if (unixptr >= unixend)
8114 /* Normal characters - More EFS work probably needed */
8120 /* remove multiple / */
8121 while (unixptr[1] == '/') {
8124 if (unixptr == lastslash) {
8125 /* Watch out for trailing dots */
8137 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8138 /* Not needed when VMS is pretending to be UNIX. */
8142 if (unixptr != unixend)
8147 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8148 (&unixptr[1] == unixend)) {
8154 /* trailing dot ==> '^..' on VMS */
8155 if (unixptr == unixend) {
8163 *vmsptr++ = *unixptr++;
8167 if (quoted && (&unixptr[1] == unixend)) {
8171 in_cnt = copy_expand_unix_filename_escape
8172 (vmsptr, unixptr, &out_cnt, utf8_fl);
8182 in_cnt = copy_expand_unix_filename_escape
8183 (vmsptr, unixptr, &out_cnt, utf8_fl);
8190 /* Make sure directory is closed */
8191 if (unixptr == lastslash) {
8193 vmsptr2 = vmsptr - 1;
8195 if (*vmsptr2 != ']') {
8198 /* directories do not end in a dot bracket */
8199 if (*vmsptr2 == '.') {
8203 if (*vmsptr2 != '^') {
8204 vmsptr--; /* back up over the dot */
8212 /* Add a trailing dot if a file with no extension */
8213 vmsptr2 = vmsptr - 1;
8215 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8216 (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
8227 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8228 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8233 /* If a UTF8 flag is being passed, honor it */
8235 if (utf8_fl != NULL) {
8236 utf8_flag = *utf8_fl;
8241 /* If there is a possibility of UTF8, then if any UTF8 characters
8242 are present, then they must be converted to VTF-7
8244 result = strcpy(rslt, path); /* FIX-ME */
8247 result = strcpy(rslt, path);
8252 /* A convenience macro for copying dots in filenames and escaping
8253 * them when they haven't already been escaped, with guards to
8254 * avoid checking before the start of the buffer or advancing
8255 * beyond the end of it (allowing room for the NUL terminator).
8257 #define VMSEFS_DOT_WITH_ESCAPE(vmsefsdot,vmsefsbuf,vmsefsbufsiz) STMT_START { \
8258 if ( ((vmsefsdot) > (vmsefsbuf) && *((vmsefsdot) - 1) != '^' \
8259 || ((vmsefsdot) == (vmsefsbuf))) \
8260 && (vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 3 \
8262 *((vmsefsdot)++) = '^'; \
8264 if ((vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 2) \
8265 *((vmsefsdot)++) = '.'; \
8268 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8269 static char *int_tovmsspec
8270 (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8275 unsigned long int infront = 0, hasdir = 1;
8278 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8279 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8281 if (vms_debug_fileify) {
8283 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8285 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8289 /* If we fail, we should be setting errno */
8291 set_vaxc_errno(SS$_BADPARAM);
8294 rslt_len = VMS_MAXRSS-1;
8296 /* '.' and '..' are "[]" and "[-]" for a quick check */
8297 if (path[0] == '.') {
8298 if (path[1] == '\0') {
8300 if (utf8_flag != NULL)
8305 if (path[1] == '.' && path[2] == '\0') {
8307 if (utf8_flag != NULL)
8314 /* Posix specifications are now a native VMS format */
8315 /*--------------------------------------------------*/
8316 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8317 if (decc_posix_compliant_pathnames) {
8318 if (strncmp(path,"\"^UP^",5) == 0) {
8319 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8325 /* This is really the only way to see if this is already in VMS format */
8326 sts = vms_split_path
8341 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8342 replacement, because the above parse just took care of most of
8343 what is needed to do vmspath when the specification is already
8346 And if it is not already, it is easier to do the conversion as
8347 part of this routine than to call this routine and then work on
8351 /* If VMS punctuation was found, it is already VMS format */
8352 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8353 if (utf8_flag != NULL)
8355 my_strlcpy(rslt, path, VMS_MAXRSS);
8356 if (vms_debug_fileify) {
8357 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8361 /* Now, what to do with trailing "." cases where there is no
8362 extension? If this is a UNIX specification, and EFS characters
8363 are enabled, then the trailing "." should be converted to a "^.".
8364 But if this was already a VMS specification, then it should be
8367 So in the case of ambiguity, leave the specification alone.
8371 /* If there is a possibility of UTF8, then if any UTF8 characters
8372 are present, then they must be converted to VTF-7
8374 if (utf8_flag != NULL)
8376 my_strlcpy(rslt, path, VMS_MAXRSS);
8377 if (vms_debug_fileify) {
8378 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8383 dirend = strrchr(path,'/');
8385 if (dirend == NULL) {
8386 /* If we get here with no Unix directory delimiters, then this is an
8387 * ambiguous file specification, such as a Unix glob specification, a
8388 * shell or make macro, or a filespec that would be valid except for
8389 * unescaped extended characters. The safest thing if it's a macro
8390 * is to pass it through as-is.
8392 if (strstr(path, "$(")) {
8393 my_strlcpy(rslt, path, VMS_MAXRSS);
8394 if (vms_debug_fileify) {
8395 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8401 else if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
8402 if (!*(dirend+2)) dirend +=2;
8403 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8404 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8409 lastdot = strrchr(cp2,'.');
8415 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8417 if (decc_disable_posix_root) {
8418 strcpy(rslt,"sys$disk:[000000]");
8421 strcpy(rslt,"sys$posix_root:[000000]");
8423 if (utf8_flag != NULL)
8425 if (vms_debug_fileify) {
8426 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8430 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8432 trndev = (char *)PerlMem_malloc(VMS_MAXRSS);
8433 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8434 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8436 /* DECC special handling */
8438 if (strcmp(rslt,"bin") == 0) {
8439 strcpy(rslt,"sys$system");
8442 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8444 else if (strcmp(rslt,"tmp") == 0) {
8445 strcpy(rslt,"sys$scratch");
8448 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8450 else if (!decc_disable_posix_root) {
8451 strcpy(rslt, "sys$posix_root");
8455 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8456 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8458 else if (strcmp(rslt,"dev") == 0) {
8459 if (strncmp(cp2,"/null", 5) == 0) {
8460 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8461 strcpy(rslt,"NLA0");
8465 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8471 trnend = islnm ? strlen(trndev) - 1 : 0;
8472 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8473 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8474 /* If the first element of the path is a logical name, determine
8475 * whether it has to be translated so we can add more directories. */
8476 if (!islnm || rooted) {
8479 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8483 if (cp2 != dirend) {
8484 my_strlcpy(rslt, trndev, VMS_MAXRSS);
8485 cp1 = rslt + trnend;
8492 if (decc_disable_posix_root) {
8498 PerlMem_free(trndev);
8503 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8504 cp2 += 2; /* skip over "./" - it's redundant */
8505 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8507 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8508 *(cp1++) = '-'; /* "../" --> "-" */
8511 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8512 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8513 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8514 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8517 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8518 /* Escape the extra dots in EFS file specifications */
8521 if (cp2 > dirend) cp2 = dirend;
8523 else *(cp1++) = '.';
8528 for (; cp2 < dirend; cp2++) {
8530 if (*(cp2-1) == '/') continue;
8531 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.';
8534 else if (!infront && *cp2 == '.') {
8535 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8536 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
8537 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8538 if (cp1 > rslt && (*(cp1-1) == '-' || *(cp1-1) == '[')) *(cp1++) = '-'; /* handle "../" */
8539 else if (cp1 > rslt + 1 && *(cp1-2) == '[') *(cp1-1) = '-';
8544 if (cp2 == dirend) break;
8546 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8547 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8548 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8549 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8551 *(cp1++) = '.'; /* Simulate trailing '/' */
8552 cp2 += 2; /* for loop will incr this to == dirend */
8554 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8557 if (decc_efs_charset == 0) {
8558 if (cp1 > rslt && *(cp1-1) == '^')
8559 cp1--; /* remove the escape, if any */
8560 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8563 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8568 if (!infront && cp1 > rslt && *(cp1-1) == '-') *(cp1++) = '.';
8570 if (decc_efs_charset == 0) {
8571 if (cp1 > rslt && *(cp1-1) == '^')
8572 cp1--; /* remove the escape, if any */
8576 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8579 else *(cp1++) = *cp2;
8583 if (cp1 > rslt && *(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8584 if (hasdir) *(cp1++) = ']';
8585 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
8586 /* fixme for ODS5 */
8593 if (decc_efs_charset == 0)
8599 if (cp2 > path && *(cp2-1) != '^') /* not previously escaped */
8605 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8606 decc_readdir_dropdotnotype) {
8607 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8610 /* trailing dot ==> '^..' on VMS */
8617 *(cp1++) = *(cp2++);
8622 /* This could be a macro to be passed through */
8623 *(cp1++) = *(cp2++);
8625 const char * save_cp2;
8629 /* paranoid check */
8635 *(cp1++) = *(cp2++);
8636 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8637 *(cp1++) = *(cp2++);
8638 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8639 *(cp1++) = *(cp2++);
8642 *(cp1++) = *(cp2++);
8646 if (is_macro == 0) {
8647 /* Not really a macro - never mind */
8660 /* Don't escape again if following character is
8661 * already something we escape.
8663 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8664 *(cp1++) = *(cp2++);
8667 /* But otherwise fall through and escape it. */
8684 if (cp2 > path && *(cp2-1) != '^') /* not previously escaped */
8686 *(cp1++) = *(cp2++);
8689 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
8690 * which is wrong. UNIX notation should be ".dir." unless
8691 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8692 * changing this behavior could break more things at this time.
8693 * efs character set effectively does not allow "." to be a version
8694 * delimiter as a further complication about changing this.
8696 if (decc_filename_unix_report != 0) {
8699 *(cp1++) = *(cp2++);
8702 *(cp1++) = *(cp2++);
8705 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8709 /* Fix me for "^]", but that requires making sure that you do
8710 * not back up past the start of the filename
8712 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8717 if (utf8_flag != NULL)
8719 if (vms_debug_fileify) {
8720 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8724 } /* end of int_tovmsspec() */
8727 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8728 static char *mp_do_tovmsspec
8729 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
8730 static char __tovmsspec_retbuf[VMS_MAXRSS];
8731 char * vmsspec, *ret_spec, *ret_buf;
8735 if (ret_buf == NULL) {
8737 Newx(vmsspec, VMS_MAXRSS, char);
8738 if (vmsspec == NULL)
8739 _ckvmssts(SS$_INSFMEM);
8742 ret_buf = __tovmsspec_retbuf;
8746 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8748 if (ret_spec == NULL) {
8749 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8756 } /* end of mp_do_tovmsspec() */
8758 /* External entry points */
8759 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8760 { return do_tovmsspec(path,buf,0,NULL); }
8761 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8762 { return do_tovmsspec(path,buf,1,NULL); }
8763 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8764 { return do_tovmsspec(path,buf,0,utf8_fl); }
8765 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8766 { return do_tovmsspec(path,buf,1,utf8_fl); }
8768 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
8769 /* Internal routine for use with out an explicit context present */
8770 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
8772 char * ret_spec, *pathified;
8777 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8778 if (pathified == NULL)
8779 _ckvmssts_noperl(SS$_INSFMEM);
8781 ret_spec = int_pathify_dirspec(path, pathified);
8783 if (ret_spec == NULL) {
8784 PerlMem_free(pathified);
8788 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
8790 PerlMem_free(pathified);
8795 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8796 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8797 static char __tovmspath_retbuf[VMS_MAXRSS];
8799 char *pathified, *vmsified, *cp;
8801 if (path == NULL) return NULL;
8802 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8803 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8804 if (int_pathify_dirspec(path, pathified) == NULL) {
8805 PerlMem_free(pathified);
8811 Newx(vmsified, VMS_MAXRSS, char);
8812 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8813 PerlMem_free(pathified);
8814 if (vmsified) Safefree(vmsified);
8817 PerlMem_free(pathified);
8822 vmslen = strlen(vmsified);
8823 Newx(cp,vmslen+1,char);
8824 memcpy(cp,vmsified,vmslen);
8830 my_strlcpy(__tovmspath_retbuf, vmsified, sizeof(__tovmspath_retbuf));
8832 return __tovmspath_retbuf;
8835 } /* end of do_tovmspath() */
8837 /* External entry points */
8838 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
8839 { return do_tovmspath(path,buf,0, NULL); }
8840 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
8841 { return do_tovmspath(path,buf,1, NULL); }
8842 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
8843 { return do_tovmspath(path,buf,0,utf8_fl); }
8844 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
8845 { return do_tovmspath(path,buf,1,utf8_fl); }
8848 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
8849 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8850 static char __tounixpath_retbuf[VMS_MAXRSS];
8852 char *pathified, *unixified, *cp;
8854 if (path == NULL) return NULL;
8855 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8856 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8857 if (int_pathify_dirspec(path, pathified) == NULL) {
8858 PerlMem_free(pathified);
8864 Newx(unixified, VMS_MAXRSS, char);
8866 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
8867 PerlMem_free(pathified);
8868 if (unixified) Safefree(unixified);
8871 PerlMem_free(pathified);
8876 unixlen = strlen(unixified);
8877 Newx(cp,unixlen+1,char);
8878 memcpy(cp,unixified,unixlen);
8880 Safefree(unixified);
8884 my_strlcpy(__tounixpath_retbuf, unixified, sizeof(__tounixpath_retbuf));
8885 Safefree(unixified);
8886 return __tounixpath_retbuf;
8889 } /* end of do_tounixpath() */
8891 /* External entry points */
8892 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
8893 { return do_tounixpath(path,buf,0,NULL); }
8894 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
8895 { return do_tounixpath(path,buf,1,NULL); }
8896 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8897 { return do_tounixpath(path,buf,0,utf8_fl); }
8898 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8899 { return do_tounixpath(path,buf,1,utf8_fl); }
8902 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
8904 *****************************************************************************
8906 * Copyright (C) 1989-1994, 2007 by *
8907 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
8909 * Permission is hereby granted for the reproduction of this software *
8910 * on condition that this copyright notice is included in source *
8911 * distributions of the software. The code may be modified and *
8912 * distributed under the same terms as Perl itself. *
8914 * 27-Aug-1994 Modified for inclusion in perl5 *
8915 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
8916 *****************************************************************************
8920 * getredirection() is intended to aid in porting C programs
8921 * to VMS (Vax-11 C). The native VMS environment does not support
8922 * '>' and '<' I/O redirection, or command line wild card expansion,
8923 * or a command line pipe mechanism using the '|' AND background
8924 * command execution '&'. All of these capabilities are provided to any
8925 * C program which calls this procedure as the first thing in the
8927 * The piping mechanism will probably work with almost any 'filter' type
8928 * of program. With suitable modification, it may useful for other
8929 * portability problems as well.
8931 * Author: Mark Pizzolato (mark AT infocomm DOT com)
8935 struct list_item *next;
8939 static void add_item(struct list_item **head,
8940 struct list_item **tail,
8944 static void mp_expand_wild_cards(pTHX_ char *item,
8945 struct list_item **head,
8946 struct list_item **tail,
8949 static int background_process(pTHX_ int argc, char **argv);
8951 static void pipe_and_fork(pTHX_ char **cmargv);
8953 /*{{{ void getredirection(int *ac, char ***av)*/
8955 mp_getredirection(pTHX_ int *ac, char ***av)
8957 * Process vms redirection arg's. Exit if any error is seen.
8958 * If getredirection() processes an argument, it is erased
8959 * from the vector. getredirection() returns a new argc and argv value.
8960 * In the event that a background command is requested (by a trailing "&"),
8961 * this routine creates a background subprocess, and simply exits the program.
8963 * Warning: do not try to simplify the code for vms. The code
8964 * presupposes that getredirection() is called before any data is
8965 * read from stdin or written to stdout.
8967 * Normal usage is as follows:
8973 * getredirection(&argc, &argv);
8977 int argc = *ac; /* Argument Count */
8978 char **argv = *av; /* Argument Vector */
8979 char *ap; /* Argument pointer */
8980 int j; /* argv[] index */
8981 int item_count = 0; /* Count of Items in List */
8982 struct list_item *list_head = 0; /* First Item in List */
8983 struct list_item *list_tail; /* Last Item in List */
8984 char *in = NULL; /* Input File Name */
8985 char *out = NULL; /* Output File Name */
8986 char *outmode = "w"; /* Mode to Open Output File */
8987 char *err = NULL; /* Error File Name */
8988 char *errmode = "w"; /* Mode to Open Error File */
8989 int cmargc = 0; /* Piped Command Arg Count */
8990 char **cmargv = NULL;/* Piped Command Arg Vector */
8993 * First handle the case where the last thing on the line ends with
8994 * a '&'. This indicates the desire for the command to be run in a
8995 * subprocess, so we satisfy that desire.
8998 if (0 == strcmp("&", ap))
8999 exit(background_process(aTHX_ --argc, argv));
9000 if (*ap && '&' == ap[strlen(ap)-1])
9002 ap[strlen(ap)-1] = '\0';
9003 exit(background_process(aTHX_ argc, argv));
9006 * Now we handle the general redirection cases that involve '>', '>>',
9007 * '<', and pipes '|'.
9009 for (j = 0; j < argc; ++j)
9011 if (0 == strcmp("<", argv[j]))
9015 fprintf(stderr,"No input file after < on command line");
9016 exit(LIB$_WRONUMARG);
9021 if ('<' == *(ap = argv[j]))
9026 if (0 == strcmp(">", ap))
9030 fprintf(stderr,"No output file after > on command line");
9031 exit(LIB$_WRONUMARG);
9050 fprintf(stderr,"No output file after > or >> on command line");
9051 exit(LIB$_WRONUMARG);
9055 if (('2' == *ap) && ('>' == ap[1]))
9072 fprintf(stderr,"No output file after 2> or 2>> on command line");
9073 exit(LIB$_WRONUMARG);
9077 if (0 == strcmp("|", argv[j]))
9081 fprintf(stderr,"No command into which to pipe on command line");
9082 exit(LIB$_WRONUMARG);
9084 cmargc = argc-(j+1);
9085 cmargv = &argv[j+1];
9089 if ('|' == *(ap = argv[j]))
9097 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9100 * Allocate and fill in the new argument vector, Some Unix's terminate
9101 * the list with an extra null pointer.
9103 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9104 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9106 for (j = 0; j < item_count; ++j, list_head = list_head->next)
9107 argv[j] = list_head->value;
9113 fprintf(stderr,"'|' and '>' may not both be specified on command line");
9114 exit(LIB$_INVARGORD);
9116 pipe_and_fork(aTHX_ cmargv);
9119 /* Check for input from a pipe (mailbox) */
9121 if (in == NULL && 1 == isapipe(0))
9123 char mbxname[L_tmpnam];
9125 long int dvi_item = DVI$_DEVBUFSIZ;
9126 $DESCRIPTOR(mbxnam, "");
9127 $DESCRIPTOR(mbxdevnam, "");
9129 /* Input from a pipe, reopen it in binary mode to disable */
9130 /* carriage control processing. */
9132 fgetname(stdin, mbxname, 1);
9133 mbxnam.dsc$a_pointer = mbxname;
9134 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9135 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9136 mbxdevnam.dsc$a_pointer = mbxname;
9137 mbxdevnam.dsc$w_length = sizeof(mbxname);
9138 dvi_item = DVI$_DEVNAM;
9139 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9140 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9143 freopen(mbxname, "rb", stdin);
9146 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9150 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9152 fprintf(stderr,"Can't open input file %s as stdin",in);
9155 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9157 fprintf(stderr,"Can't open output file %s as stdout",out);
9160 if (out != NULL) vmssetuserlnm("SYS$OUTPUT", out);
9163 if (strcmp(err,"&1") == 0) {
9164 dup2(fileno(stdout), fileno(stderr));
9165 vmssetuserlnm("SYS$ERROR", "SYS$OUTPUT");
9168 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9170 fprintf(stderr,"Can't open error file %s as stderr",err);
9174 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9178 vmssetuserlnm("SYS$ERROR", err);
9181 #ifdef ARGPROC_DEBUG
9182 PerlIO_printf(Perl_debug_log, "Arglist:\n");
9183 for (j = 0; j < *ac; ++j)
9184 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9186 /* Clear errors we may have hit expanding wildcards, so they don't
9187 show up in Perl's $! later */
9188 set_errno(0); set_vaxc_errno(1);
9189 } /* end of getredirection() */
9192 static void add_item(struct list_item **head,
9193 struct list_item **tail,
9199 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9200 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9204 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9205 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9206 *tail = (*tail)->next;
9208 (*tail)->value = value;
9212 static void mp_expand_wild_cards(pTHX_ char *item,
9213 struct list_item **head,
9214 struct list_item **tail,
9218 unsigned long int context = 0;
9226 $DESCRIPTOR(filespec, "");
9227 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9228 $DESCRIPTOR(resultspec, "");
9229 unsigned long int lff_flags = 0;
9233 #ifdef VMS_LONGNAME_SUPPORT
9234 lff_flags = LIB$M_FIL_LONG_NAMES;
9237 for (cp = item; *cp; cp++) {
9238 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9239 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9241 if (!*cp || isspace(*cp))
9243 add_item(head, tail, item, count);
9248 /* "double quoted" wild card expressions pass as is */
9249 /* From DCL that means using e.g.: */
9250 /* perl program """perl.*""" */
9251 item_len = strlen(item);
9252 if ( '"' == *item && '"' == item[item_len-1] )
9255 item[item_len-2] = '\0';
9256 add_item(head, tail, item, count);
9260 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9261 resultspec.dsc$b_class = DSC$K_CLASS_D;
9262 resultspec.dsc$a_pointer = NULL;
9263 vmsspec = (char *)PerlMem_malloc(VMS_MAXRSS);
9264 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9265 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9266 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9267 if (!isunix || !filespec.dsc$a_pointer)
9268 filespec.dsc$a_pointer = item;
9269 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9271 * Only return version specs, if the caller specified a version
9273 had_version = strchr(item, ';');
9275 * Only return device and directory specs, if the caller specified either.
9277 had_device = strchr(item, ':');
9278 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9280 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9281 (&filespec, &resultspec, &context,
9282 &defaultspec, 0, &rms_sts, &lff_flags)))
9287 string = (char *)PerlMem_malloc(resultspec.dsc$w_length+1);
9288 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9289 my_strlcpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length+1);
9290 if (NULL == had_version)
9291 *(strrchr(string, ';')) = '\0';
9292 if ((!had_directory) && (had_device == NULL))
9294 if (NULL == (devdir = strrchr(string, ']')))
9295 devdir = strrchr(string, '>');
9296 my_strlcpy(string, devdir + 1, resultspec.dsc$w_length+1);
9299 * Be consistent with what the C RTL has already done to the rest of
9300 * the argv items and lowercase all of these names.
9302 if (!decc_efs_case_preserve) {
9303 for (c = string; *c; ++c)
9307 if (isunix) trim_unixpath(string,item,1);
9308 add_item(head, tail, string, count);
9311 PerlMem_free(vmsspec);
9312 if (sts != RMS$_NMF)
9314 set_vaxc_errno(sts);
9317 case RMS$_FNF: case RMS$_DNF:
9318 set_errno(ENOENT); break;
9320 set_errno(ENOTDIR); break;
9322 set_errno(ENODEV); break;
9323 case RMS$_FNM: case RMS$_SYN:
9324 set_errno(EINVAL); break;
9326 set_errno(EACCES); break;
9328 _ckvmssts_noperl(sts);
9332 add_item(head, tail, item, count);
9333 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9334 _ckvmssts_noperl(lib$find_file_end(&context));
9337 static int child_st[2];/* Event Flag set when child process completes */
9339 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
9341 static unsigned long int exit_handler(void)
9345 if (0 == child_st[0])
9347 #ifdef ARGPROC_DEBUG
9348 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
9350 fflush(stdout); /* Have to flush pipe for binary data to */
9351 /* terminate properly -- <tp@mccall.com> */
9352 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9353 sys$dassgn(child_chan);
9355 sys$synch(0, child_st);
9360 static void sig_child(int chan)
9362 #ifdef ARGPROC_DEBUG
9363 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
9365 if (child_st[0] == 0)
9369 static struct exit_control_block exit_block =
9374 &exit_block.exit_status,
9379 pipe_and_fork(pTHX_ char **cmargv)
9382 struct dsc$descriptor_s *vmscmd;
9383 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9384 int sts, j, l, ismcr, quote, tquote = 0;
9386 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
9387 vms_execfree(vmscmd);
9392 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
9393 && toupper(*(q+2)) == 'R' && !*(q+3);
9395 while (q && l < MAX_DCL_LINE_LENGTH) {
9397 if (j > 0 && quote) {
9403 if (ismcr && j > 1) quote = 1;
9404 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9407 if (quote || tquote) {
9413 if ((quote||tquote) && *q == '"') {
9423 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9425 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9429 static int background_process(pTHX_ int argc, char **argv)
9431 char command[MAX_DCL_SYMBOL + 1] = "$";
9432 $DESCRIPTOR(value, "");
9433 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9434 static $DESCRIPTOR(null, "NLA0:");
9435 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9437 $DESCRIPTOR(pidstr, "");
9439 unsigned long int flags = 17, one = 1, retsts;
9442 len = my_strlcat(command, argv[0], sizeof(command));
9443 while (--argc && (len < MAX_DCL_SYMBOL))
9445 my_strlcat(command, " \"", sizeof(command));
9446 my_strlcat(command, *(++argv), sizeof(command));
9447 len = my_strlcat(command, "\"", sizeof(command));
9449 value.dsc$a_pointer = command;
9450 value.dsc$w_length = strlen(value.dsc$a_pointer);
9451 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9452 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9453 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9454 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9457 _ckvmssts_noperl(retsts);
9459 #ifdef ARGPROC_DEBUG
9460 PerlIO_printf(Perl_debug_log, "%s\n", command);
9462 sprintf(pidstring, "%08X", pid);
9463 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9464 pidstr.dsc$a_pointer = pidstring;
9465 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9466 lib$set_symbol(&pidsymbol, &pidstr);
9470 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9473 /* OS-specific initialization at image activation (not thread startup) */
9474 /* Older VAXC header files lack these constants */
9475 #ifndef JPI$_RIGHTS_SIZE
9476 # define JPI$_RIGHTS_SIZE 817
9478 #ifndef KGB$M_SUBSYSTEM
9479 # define KGB$M_SUBSYSTEM 0x8
9482 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9484 /*{{{void vms_image_init(int *, char ***)*/
9486 vms_image_init(int *argcp, char ***argvp)
9489 char eqv[LNM$C_NAMLENGTH+1] = "";
9490 unsigned int len, tabct = 8, tabidx = 0;
9491 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9492 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9493 unsigned short int dummy, rlen;
9494 struct dsc$descriptor_s **tabvec;
9495 #if defined(PERL_IMPLICIT_CONTEXT)
9498 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9499 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9500 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9503 #ifdef KILL_BY_SIGPRC
9504 Perl_csighandler_init();
9507 #if __CRTL_VER >= 70300000 && !defined(__VAX)
9508 /* This was moved from the pre-image init handler because on threaded */
9509 /* Perl it was always returning 0 for the default value. */
9510 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9513 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9516 initial = decc$feature_get_value(s, 4);
9518 /* initial is: 0 if nothing has set the feature */
9519 /* -1 if initialized to default */
9520 /* 1 if set by logical name */
9521 /* 2 if set by decc$feature_set_value */
9522 decc_disable_posix_root = decc$feature_get_value(s, 1);
9524 /* If the value is not valid, force the feature off */
9525 if (decc_disable_posix_root < 0) {
9526 decc$feature_set_value(s, 1, 1);
9527 decc_disable_posix_root = 1;
9531 /* Nothing has asked for it explicitly, so use our own default. */
9532 decc_disable_posix_root = 1;
9533 decc$feature_set_value(s, 1, 1);
9539 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9540 _ckvmssts_noperl(iosb[0]);
9541 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9542 if (iprv[i]) { /* Running image installed with privs? */
9543 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
9548 /* Rights identifiers might trigger tainting as well. */
9549 if (!will_taint && (rlen || rsz)) {
9550 while (rlen < rsz) {
9551 /* We didn't get all the identifiers on the first pass. Allocate a
9552 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9553 * were needed to hold all identifiers at time of last call; we'll
9554 * allocate that many unsigned long ints), and go back and get 'em.
9555 * If it gave us less than it wanted to despite ample buffer space,
9556 * something's broken. Is your system missing a system identifier?
9558 if (rsz <= jpilist[1].buflen) {
9559 /* Perl_croak accvios when used this early in startup. */
9560 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9561 rsz, (unsigned long) jpilist[1].buflen,
9562 "Check your rights database for corruption.\n");
9565 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9566 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9567 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9568 jpilist[1].buflen = rsz * sizeof(unsigned long int);
9569 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9570 _ckvmssts_noperl(iosb[0]);
9572 mask = (unsigned long int *)jpilist[1].bufadr;
9573 /* Check attribute flags for each identifier (2nd longword); protected
9574 * subsystem identifiers trigger tainting.
9576 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9577 if (mask[i] & KGB$M_SUBSYSTEM) {
9582 if (mask != rlst) PerlMem_free(mask);
9585 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9586 * logical, some versions of the CRTL will add a phanthom /000000/
9587 * directory. This needs to be removed.
9589 if (decc_filename_unix_report) {
9592 ulen = strlen(argvp[0][0]);
9594 zeros = strstr(argvp[0][0], "/000000/");
9595 if (zeros != NULL) {
9597 mlen = ulen - (zeros - argvp[0][0]) - 7;
9598 memmove(zeros, &zeros[7], mlen);
9600 argvp[0][0][ulen] = '\0';
9603 /* It also may have a trailing dot that needs to be removed otherwise
9604 * it will be converted to VMS mode incorrectly.
9607 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9608 argvp[0][0][ulen] = '\0';
9611 /* We need to use this hack to tell Perl it should run with tainting,
9612 * since its tainting flag may be part of the PL_curinterp struct, which
9613 * hasn't been allocated when vms_image_init() is called.
9616 char **newargv, **oldargv;
9618 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9619 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9620 newargv[0] = oldargv[0];
9621 newargv[1] = (char *)PerlMem_malloc(3 * sizeof(char));
9622 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9623 strcpy(newargv[1], "-T");
9624 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9626 newargv[*argcp] = NULL;
9627 /* We orphan the old argv, since we don't know where it's come from,
9628 * so we don't know how to free it.
9632 else { /* Did user explicitly request tainting? */
9634 char *cp, **av = *argvp;
9635 for (i = 1; i < *argcp; i++) {
9636 if (*av[i] != '-') break;
9637 for (cp = av[i]+1; *cp; cp++) {
9638 if (*cp == 'T') { will_taint = 1; break; }
9639 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9640 strchr("DFIiMmx",*cp)) break;
9642 if (will_taint) break;
9647 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9650 tabvec = (struct dsc$descriptor_s **)
9651 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9652 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9654 else if (tabidx >= tabct) {
9656 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9657 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9659 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9660 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9661 tabvec[tabidx]->dsc$w_length = 0;
9662 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9663 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
9664 tabvec[tabidx]->dsc$a_pointer = NULL;
9665 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
9667 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9669 getredirection(argcp,argvp);
9670 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9672 # include <reentrancy.h>
9673 decc$set_reentrancy(C$C_MULTITHREAD);
9682 * Trim Unix-style prefix off filespec, so it looks like what a shell
9683 * glob expansion would return (i.e. from specified prefix on, not
9684 * full path). Note that returned filespec is Unix-style, regardless
9685 * of whether input filespec was VMS-style or Unix-style.
9687 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9688 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9689 * vector of options; at present, only bit 0 is used, and if set tells
9690 * trim unixpath to try the current default directory as a prefix when
9691 * presented with a possibly ambiguous ... wildcard.
9693 * Returns !=0 on success, with trimmed filespec replacing contents of
9694 * fspec, and 0 on failure, with contents of fpsec unchanged.
9696 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9698 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9700 char *unixified, *unixwild, *tplate, *base, *end, *cp1, *cp2;
9701 int tmplen, reslen = 0, dirs = 0;
9703 if (!wildspec || !fspec) return 0;
9705 unixwild = (char *)PerlMem_malloc(VMS_MAXRSS);
9706 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9708 if (strpbrk(wildspec,"]>:") != NULL) {
9709 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
9710 PerlMem_free(unixwild);
9715 my_strlcpy(unixwild, wildspec, VMS_MAXRSS);
9717 unixified = (char *)PerlMem_malloc(VMS_MAXRSS);
9718 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9719 if (strpbrk(fspec,"]>:") != NULL) {
9720 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
9721 PerlMem_free(unixwild);
9722 PerlMem_free(unixified);
9725 else base = unixified;
9726 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9727 * check to see that final result fits into (isn't longer than) fspec */
9728 reslen = strlen(fspec);
9732 /* No prefix or absolute path on wildcard, so nothing to remove */
9733 if (!*tplate || *tplate == '/') {
9734 PerlMem_free(unixwild);
9735 if (base == fspec) {
9736 PerlMem_free(unixified);
9739 tmplen = strlen(unixified);
9740 if (tmplen > reslen) {
9741 PerlMem_free(unixified);
9742 return 0; /* not enough space */
9744 /* Copy unixified resultant, including trailing NUL */
9745 memmove(fspec,unixified,tmplen+1);
9746 PerlMem_free(unixified);
9750 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9751 if ((cp1 = strstr(tplate,".../")) == NULL) { /* No ...; just count elts */
9752 for (cp1 = tplate; *cp1; cp1++) if (*cp1 == '/') dirs++;
9753 for (cp1 = end ;cp1 >= base; cp1--)
9754 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9756 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9757 PerlMem_free(unixified);
9758 PerlMem_free(unixwild);
9763 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9764 int ells = 1, totells, segdirs, match;
9765 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9766 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9768 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9770 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9771 tpl = (char *)PerlMem_malloc(VMS_MAXRSS);
9772 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9773 if (ellipsis == tplate && opts & 1) {
9774 /* Template begins with an ellipsis. Since we can't tell how many
9775 * directory names at the front of the resultant to keep for an
9776 * arbitrary starting point, we arbitrarily choose the current
9777 * default directory as a starting point. If it's there as a prefix,
9778 * clip it off. If not, fall through and act as if the leading
9779 * ellipsis weren't there (i.e. return shortest possible path that
9780 * could match template).
9782 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9784 PerlMem_free(unixified);
9785 PerlMem_free(unixwild);
9788 if (!decc_efs_case_preserve) {
9789 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9790 if (_tolower(*cp1) != _tolower(*cp2)) break;
9792 segdirs = dirs - totells; /* Min # of dirs we must have left */
9793 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9794 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9795 memmove(fspec,cp2+1,end - cp2);
9797 PerlMem_free(unixified);
9798 PerlMem_free(unixwild);
9802 /* First off, back up over constant elements at end of path */
9804 for (front = end ; front >= base; front--)
9805 if (*front == '/' && !dirs--) { front++; break; }
9807 lcres = (char *)PerlMem_malloc(VMS_MAXRSS);
9808 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9809 for (cp1=tplate,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9811 if (!decc_efs_case_preserve) {
9812 *cp2 = _tolower(*cp1); /* Make lc copy for match */
9820 PerlMem_free(unixified);
9821 PerlMem_free(unixwild);
9822 PerlMem_free(lcres);
9823 return 0; /* Path too long. */
9826 *cp2 = '\0'; /* Pick up with memcpy later */
9827 lcfront = lcres + (front - base);
9828 /* Now skip over each ellipsis and try to match the path in front of it. */
9830 for (cp1 = ellipsis - 2; cp1 >= tplate; cp1--)
9831 if (*(cp1) == '.' && *(cp1+1) == '.' &&
9832 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
9833 if (cp1 < tplate) break; /* template started with an ellipsis */
9834 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9835 ellipsis = cp1; continue;
9837 wilddsc.dsc$a_pointer = tpl;
9838 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9840 for (segdirs = 0, cp2 = tpl;
9841 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
9843 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
9845 if (!decc_efs_case_preserve) {
9846 *cp2 = _tolower(*cp1); /* else lowercase for match */
9849 *cp2 = *cp1; /* else preserve case for match */
9852 if (*cp2 == '/') segdirs++;
9854 if (cp1 != ellipsis - 1) {
9856 PerlMem_free(unixified);
9857 PerlMem_free(unixwild);
9858 PerlMem_free(lcres);
9859 return 0; /* Path too long */
9861 /* Back up at least as many dirs as in template before matching */
9862 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
9863 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
9864 for (match = 0; cp1 > lcres;) {
9865 resdsc.dsc$a_pointer = cp1;
9866 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
9868 if (match == 1) lcfront = cp1;
9870 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
9874 PerlMem_free(unixified);
9875 PerlMem_free(unixwild);
9876 PerlMem_free(lcres);
9877 return 0; /* Can't find prefix ??? */
9879 if (match > 1 && opts & 1) {
9880 /* This ... wildcard could cover more than one set of dirs (i.e.
9881 * a set of similar dir names is repeated). If the template
9882 * contains more than 1 ..., upstream elements could resolve the
9883 * ambiguity, but it's not worth a full backtracking setup here.
9884 * As a quick heuristic, clip off the current default directory
9885 * if it's present to find the trimmed spec, else use the
9886 * shortest string that this ... could cover.
9888 char def[NAM$C_MAXRSS+1], *st;
9890 if (getcwd(def, sizeof def,0) == NULL) {
9891 PerlMem_free(unixified);
9892 PerlMem_free(unixwild);
9893 PerlMem_free(lcres);
9897 if (!decc_efs_case_preserve) {
9898 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9899 if (_tolower(*cp1) != _tolower(*cp2)) break;
9901 segdirs = dirs - totells; /* Min # of dirs we must have left */
9902 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
9903 if (*cp1 == '\0' && *cp2 == '/') {
9904 memmove(fspec,cp2+1,end - cp2);
9906 PerlMem_free(unixified);
9907 PerlMem_free(unixwild);
9908 PerlMem_free(lcres);
9911 /* Nope -- stick with lcfront from above and keep going. */
9914 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
9916 PerlMem_free(unixified);
9917 PerlMem_free(unixwild);
9918 PerlMem_free(lcres);
9922 } /* end of trim_unixpath() */
9927 * VMS readdir() routines.
9928 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
9930 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
9931 * Minor modifications to original routines.
9934 /* readdir may have been redefined by reentr.h, so make sure we get
9935 * the local version for what we do here.
9940 #if !defined(PERL_IMPLICIT_CONTEXT)
9941 # define readdir Perl_readdir
9943 # define readdir(a) Perl_readdir(aTHX_ a)
9946 /* Number of elements in vms_versions array */
9947 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
9950 * Open a directory, return a handle for later use.
9952 /*{{{ DIR *opendir(char*name) */
9954 Perl_opendir(pTHX_ const char *name)
9960 Newx(dir, VMS_MAXRSS, char);
9961 if (int_tovmspath(name, dir, NULL) == NULL) {
9965 /* Check access before stat; otherwise stat does not
9966 * accurately report whether it's a directory.
9968 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
9969 /* cando_by_name has already set errno */
9973 if (flex_stat(dir,&sb) == -1) return NULL;
9974 if (!S_ISDIR(sb.st_mode)) {
9976 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
9979 /* Get memory for the handle, and the pattern. */
9981 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
9983 /* Fill in the fields; mainly playing with the descriptor. */
9984 sprintf(dd->pattern, "%s*.*",dir);
9989 /* By saying we want the result of readdir() in unix format, we are really
9990 * saying we want all the escapes removed, translating characters that
9991 * must be escaped in a VMS-format name to their unescaped form, which is
9992 * presumably allowed in a Unix-format name.
9994 dd->flags = decc_filename_unix_report ? PERL_VMSDIR_M_UNIXSPECS : 0;
9995 dd->pat.dsc$a_pointer = dd->pattern;
9996 dd->pat.dsc$w_length = strlen(dd->pattern);
9997 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
9998 dd->pat.dsc$b_class = DSC$K_CLASS_S;
9999 #if defined(USE_ITHREADS)
10000 Newx(dd->mutex,1,perl_mutex);
10001 MUTEX_INIT( (perl_mutex *) dd->mutex );
10007 } /* end of opendir() */
10011 * Set the flag to indicate we want versions or not.
10013 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10015 vmsreaddirversions(DIR *dd, int flag)
10018 dd->flags |= PERL_VMSDIR_M_VERSIONS;
10020 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10025 * Free up an opened directory.
10027 /*{{{ void closedir(DIR *dd)*/
10029 Perl_closedir(DIR *dd)
10033 sts = lib$find_file_end(&dd->context);
10034 Safefree(dd->pattern);
10035 #if defined(USE_ITHREADS)
10036 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10037 Safefree(dd->mutex);
10044 * Collect all the version numbers for the current file.
10047 collectversions(pTHX_ DIR *dd)
10049 struct dsc$descriptor_s pat;
10050 struct dsc$descriptor_s res;
10052 char *p, *text, *buff;
10054 unsigned long context, tmpsts;
10056 /* Convenient shorthand. */
10059 /* Add the version wildcard, ignoring the "*.*" put on before */
10060 i = strlen(dd->pattern);
10061 Newx(text,i + e->d_namlen + 3,char);
10062 my_strlcpy(text, dd->pattern, i + 1);
10063 sprintf(&text[i - 3], "%s;*", e->d_name);
10065 /* Set up the pattern descriptor. */
10066 pat.dsc$a_pointer = text;
10067 pat.dsc$w_length = i + e->d_namlen - 1;
10068 pat.dsc$b_dtype = DSC$K_DTYPE_T;
10069 pat.dsc$b_class = DSC$K_CLASS_S;
10071 /* Set up result descriptor. */
10072 Newx(buff, VMS_MAXRSS, char);
10073 res.dsc$a_pointer = buff;
10074 res.dsc$w_length = VMS_MAXRSS - 1;
10075 res.dsc$b_dtype = DSC$K_DTYPE_T;
10076 res.dsc$b_class = DSC$K_CLASS_S;
10078 /* Read files, collecting versions. */
10079 for (context = 0, e->vms_verscount = 0;
10080 e->vms_verscount < VERSIZE(e);
10081 e->vms_verscount++) {
10082 unsigned long rsts;
10083 unsigned long flags = 0;
10085 #ifdef VMS_LONGNAME_SUPPORT
10086 flags = LIB$M_FIL_LONG_NAMES;
10088 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10089 if (tmpsts == RMS$_NMF || context == 0) break;
10091 buff[VMS_MAXRSS - 1] = '\0';
10092 if ((p = strchr(buff, ';')))
10093 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10095 e->vms_versions[e->vms_verscount] = -1;
10098 _ckvmssts(lib$find_file_end(&context));
10102 } /* end of collectversions() */
10105 * Read the next entry from the directory.
10107 /*{{{ struct dirent *readdir(DIR *dd)*/
10109 Perl_readdir(pTHX_ DIR *dd)
10111 struct dsc$descriptor_s res;
10113 unsigned long int tmpsts;
10114 unsigned long rsts;
10115 unsigned long flags = 0;
10116 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10117 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10119 /* Set up result descriptor, and get next file. */
10120 Newx(buff, VMS_MAXRSS, char);
10121 res.dsc$a_pointer = buff;
10122 res.dsc$w_length = VMS_MAXRSS - 1;
10123 res.dsc$b_dtype = DSC$K_DTYPE_T;
10124 res.dsc$b_class = DSC$K_CLASS_S;
10126 #ifdef VMS_LONGNAME_SUPPORT
10127 flags = LIB$M_FIL_LONG_NAMES;
10130 tmpsts = lib$find_file
10131 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10132 if (dd->context == 0)
10133 tmpsts = RMS$_NMF; /* None left. (should be set, but make sure) */
10135 if (!(tmpsts & 1)) {
10138 break; /* no more files considered success */
10140 SETERRNO(EACCES, tmpsts); break;
10142 SETERRNO(ENODEV, tmpsts); break;
10144 SETERRNO(ENOTDIR, tmpsts); break;
10145 case RMS$_FNF: case RMS$_DNF:
10146 SETERRNO(ENOENT, tmpsts); break;
10148 SETERRNO(EVMSERR, tmpsts);
10154 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10155 buff[res.dsc$w_length] = '\0';
10156 p = buff + res.dsc$w_length;
10157 while (--p >= buff) if (!isspace(*p)) break;
10159 if (!decc_efs_case_preserve) {
10160 for (p = buff; *p; p++) *p = _tolower(*p);
10163 /* Skip any directory component and just copy the name. */
10164 sts = vms_split_path
10179 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10181 /* In Unix report mode, remove the ".dir;1" from the name */
10182 /* if it is a real directory. */
10183 if (decc_filename_unix_report && decc_efs_charset) {
10184 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10188 ret_sts = flex_lstat(buff, &statbuf);
10189 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10196 /* Drop NULL extensions on UNIX file specification */
10197 if ((e_len == 1) && decc_readdir_dropdotnotype) {
10203 memcpy(dd->entry.d_name, n_spec, n_len + e_len);
10204 dd->entry.d_name[n_len + e_len] = '\0';
10205 dd->entry.d_namlen = n_len + e_len;
10207 /* Convert the filename to UNIX format if needed */
10208 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10210 /* Translate the encoded characters. */
10211 /* Fixme: Unicode handling could result in embedded 0 characters */
10212 if (strchr(dd->entry.d_name, '^') != NULL) {
10213 char new_name[256];
10215 p = dd->entry.d_name;
10218 int inchars_read, outchars_added;
10219 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10221 q += outchars_added;
10223 /* if outchars_added > 1, then this is a wide file specification */
10224 /* Wide file specifications need to be passed in Perl */
10225 /* counted strings apparently with a Unicode flag */
10228 dd->entry.d_namlen = my_strlcpy(dd->entry.d_name, new_name, sizeof(dd->entry.d_name));
10232 dd->entry.vms_verscount = 0;
10233 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10237 } /* end of readdir() */
10241 * Read the next entry from the directory -- thread-safe version.
10243 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10245 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10249 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10251 entry = readdir(dd);
10253 retval = ( *result == NULL ? errno : 0 );
10255 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10259 } /* end of readdir_r() */
10263 * Return something that can be used in a seekdir later.
10265 /*{{{ long telldir(DIR *dd)*/
10267 Perl_telldir(DIR *dd)
10274 * Return to a spot where we used to be. Brute force.
10276 /*{{{ void seekdir(DIR *dd,long count)*/
10278 Perl_seekdir(pTHX_ DIR *dd, long count)
10282 /* If we haven't done anything yet... */
10283 if (dd->count == 0)
10286 /* Remember some state, and clear it. */
10287 old_flags = dd->flags;
10288 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10289 _ckvmssts(lib$find_file_end(&dd->context));
10292 /* The increment is in readdir(). */
10293 for (dd->count = 0; dd->count < count; )
10296 dd->flags = old_flags;
10298 } /* end of seekdir() */
10301 /* VMS subprocess management
10303 * my_vfork() - just a vfork(), after setting a flag to record that
10304 * the current script is trying a Unix-style fork/exec.
10306 * vms_do_aexec() and vms_do_exec() are called in response to the
10307 * perl 'exec' function. If this follows a vfork call, then they
10308 * call out the regular perl routines in doio.c which do an
10309 * execvp (for those who really want to try this under VMS).
10310 * Otherwise, they do exactly what the perl docs say exec should
10311 * do - terminate the current script and invoke a new command
10312 * (See below for notes on command syntax.)
10314 * do_aspawn() and do_spawn() implement the VMS side of the perl
10315 * 'system' function.
10317 * Note on command arguments to perl 'exec' and 'system': When handled
10318 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10319 * are concatenated to form a DCL command string. If the first non-numeric
10320 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10321 * the command string is handed off to DCL directly. Otherwise,
10322 * the first token of the command is taken as the filespec of an image
10323 * to run. The filespec is expanded using a default type of '.EXE' and
10324 * the process defaults for device, directory, etc., and if found, the resultant
10325 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10326 * the command string as parameters. This is perhaps a bit complicated,
10327 * but I hope it will form a happy medium between what VMS folks expect
10328 * from lib$spawn and what Unix folks expect from exec.
10331 static int vfork_called;
10333 /*{{{int my_vfork(void)*/
10344 vms_execfree(struct dsc$descriptor_s *vmscmd)
10347 if (vmscmd->dsc$a_pointer) {
10348 PerlMem_free(vmscmd->dsc$a_pointer);
10350 PerlMem_free(vmscmd);
10355 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10357 char *junk, *tmps = NULL;
10365 tmps = SvPV(really,rlen);
10367 cmdlen += rlen + 1;
10372 for (idx++; idx <= sp; idx++) {
10374 junk = SvPVx(*idx,rlen);
10375 cmdlen += rlen ? rlen + 1 : 0;
10378 Newx(PL_Cmd, cmdlen+1, char);
10380 if (tmps && *tmps) {
10381 my_strlcpy(PL_Cmd, tmps, cmdlen + 1);
10384 else *PL_Cmd = '\0';
10385 while (++mark <= sp) {
10387 char *s = SvPVx(*mark,n_a);
10389 if (*PL_Cmd) my_strlcat(PL_Cmd, " ", cmdlen+1);
10390 my_strlcat(PL_Cmd, s, cmdlen+1);
10395 } /* end of setup_argstr() */
10398 static unsigned long int
10399 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10400 struct dsc$descriptor_s **pvmscmd)
10404 char image_name[NAM$C_MAXRSS+1];
10405 char image_argv[NAM$C_MAXRSS+1];
10406 $DESCRIPTOR(defdsc,".EXE");
10407 $DESCRIPTOR(defdsc2,".");
10408 struct dsc$descriptor_s resdsc;
10409 struct dsc$descriptor_s *vmscmd;
10410 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10411 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10412 char *s, *rest, *cp, *wordbreak;
10417 vmscmd = (struct dsc$descriptor_s *)PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10418 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10420 /* vmsspec is a DCL command buffer, not just a filename */
10421 vmsspec = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10422 if (vmsspec == NULL)
10423 _ckvmssts_noperl(SS$_INSFMEM);
10425 resspec = (char *)PerlMem_malloc(VMS_MAXRSS);
10426 if (resspec == NULL)
10427 _ckvmssts_noperl(SS$_INSFMEM);
10429 /* Make a copy for modification */
10430 cmdlen = strlen(incmd);
10431 cmd = (char *)PerlMem_malloc(cmdlen+1);
10432 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10433 my_strlcpy(cmd, incmd, cmdlen + 1);
10437 resdsc.dsc$a_pointer = resspec;
10438 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10439 resdsc.dsc$b_class = DSC$K_CLASS_S;
10440 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10442 vmscmd->dsc$a_pointer = NULL;
10443 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10444 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10445 vmscmd->dsc$w_length = 0;
10446 if (pvmscmd) *pvmscmd = vmscmd;
10448 if (suggest_quote) *suggest_quote = 0;
10450 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10452 PerlMem_free(vmsspec);
10453 PerlMem_free(resspec);
10454 return CLI$_BUFOVF; /* continuation lines currently unsupported */
10459 while (*s && isspace(*s)) s++;
10461 if (*s == '@' || *s == '$') {
10462 vmsspec[0] = *s; rest = s + 1;
10463 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10465 else { cp = vmsspec; rest = s; }
10467 /* If the first word is quoted, then we need to unquote it and
10468 * escape spaces within it. We'll expand into the resspec buffer,
10469 * then copy back into the cmd buffer, expanding the latter if
10472 if (*rest == '"') {
10477 int soff = s - cmd;
10479 for (cp2 = resspec;
10480 *rest && cp2 - resspec < (VMS_MAXRSS - 1);
10483 if (*rest == ' ') { /* Escape ' ' to '^_'. */
10489 else if (*rest == '"') {
10491 if (in_quote) { /* Must be closing quote. */
10504 /* Expand the command buffer if necessary. */
10505 if (clen > cmdlen) {
10506 cmd = (char *)PerlMem_realloc(cmd, clen);
10508 _ckvmssts_noperl(SS$_INSFMEM);
10509 /* Where we are may have changed, so recompute offsets */
10510 r = cmd + (r - s - soff);
10511 rest = cmd + (rest - s - soff);
10515 /* Shift the non-verb portion of the command (if any) up or
10516 * down as necessary.
10519 memmove(rest + clen - cmdlen, rest, s - soff + cmdlen - rest);
10521 /* Copy the unquoted and escaped command verb into place. */
10522 memcpy(r, resspec, cp2 - resspec);
10525 rest = r; /* Rewind for subsequent operations. */
10528 if (*rest == '.' || *rest == '/') {
10530 for (cp2 = resspec;
10531 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10532 rest++, cp2++) *cp2 = *rest;
10534 if (int_tovmsspec(resspec, cp, 0, NULL)) {
10537 /* When a UNIX spec with no file type is translated to VMS, */
10538 /* A trailing '.' is appended under ODS-5 rules. */
10539 /* Here we do not want that trailing "." as it prevents */
10540 /* Looking for a implied ".exe" type. */
10541 if (decc_efs_charset) {
10543 i = strlen(vmsspec);
10544 if (vmsspec[i-1] == '.') {
10545 vmsspec[i-1] = '\0';
10550 for (cp2 = vmsspec + strlen(vmsspec);
10551 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10552 rest++, cp2++) *cp2 = *rest;
10557 /* Intuit whether verb (first word of cmd) is a DCL command:
10558 * - if first nonspace char is '@', it's a DCL indirection
10560 * - if verb contains a filespec separator, it's not a DCL command
10561 * - if it doesn't, caller tells us whether to default to a DCL
10562 * command, or to a local image unless told it's DCL (by leading '$')
10566 if (suggest_quote) *suggest_quote = 1;
10568 char *filespec = strpbrk(s,":<[.;");
10569 rest = wordbreak = strpbrk(s," \"\t/");
10570 if (!wordbreak) wordbreak = s + strlen(s);
10571 if (*s == '$') check_img = 0;
10572 if (filespec && (filespec < wordbreak)) isdcl = 0;
10573 else isdcl = !check_img;
10578 imgdsc.dsc$a_pointer = s;
10579 imgdsc.dsc$w_length = wordbreak - s;
10580 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10582 _ckvmssts_noperl(lib$find_file_end(&cxt));
10583 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10584 if (!(retsts & 1) && *s == '$') {
10585 _ckvmssts_noperl(lib$find_file_end(&cxt));
10586 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10587 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10589 _ckvmssts_noperl(lib$find_file_end(&cxt));
10590 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10594 _ckvmssts_noperl(lib$find_file_end(&cxt));
10599 while (*s && !isspace(*s)) s++;
10602 /* check that it's really not DCL with no file extension */
10603 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10605 char b[256] = {0,0,0,0};
10606 read(fileno(fp), b, 256);
10607 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10611 /* Check for script */
10613 if ((b[0] == '#') && (b[1] == '!'))
10615 #ifdef ALTERNATE_SHEBANG
10617 shebang_len = strlen(ALTERNATE_SHEBANG);
10618 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10620 perlstr = strstr("perl",b);
10621 if (perlstr == NULL)
10629 if (shebang_len > 0) {
10632 char tmpspec[NAM$C_MAXRSS + 1];
10635 /* Image is following after white space */
10636 /*--------------------------------------*/
10637 while (isprint(b[i]) && isspace(b[i]))
10641 while (isprint(b[i]) && !isspace(b[i])) {
10642 tmpspec[j++] = b[i++];
10643 if (j >= NAM$C_MAXRSS)
10648 /* There may be some default parameters to the image */
10649 /*---------------------------------------------------*/
10651 while (isprint(b[i])) {
10652 image_argv[j++] = b[i++];
10653 if (j >= NAM$C_MAXRSS)
10656 while ((j > 0) && !isprint(image_argv[j-1]))
10660 /* It will need to be converted to VMS format and validated */
10661 if (tmpspec[0] != '\0') {
10664 /* Try to find the exact program requested to be run */
10665 /*---------------------------------------------------*/
10666 iname = int_rmsexpand
10667 (tmpspec, image_name, ".exe",
10668 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10669 if (iname != NULL) {
10670 if (cando_by_name_int
10671 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10672 /* MCR prefix needed */
10676 /* Try again with a null type */
10677 /*----------------------------*/
10678 iname = int_rmsexpand
10679 (tmpspec, image_name, ".",
10680 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10681 if (iname != NULL) {
10682 if (cando_by_name_int
10683 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10684 /* MCR prefix needed */
10690 /* Did we find the image to run the script? */
10691 /*------------------------------------------*/
10695 /* Assume DCL or foreign command exists */
10696 /*--------------------------------------*/
10697 tchr = strrchr(tmpspec, '/');
10698 if (tchr != NULL) {
10704 my_strlcpy(image_name, tchr, sizeof(image_name));
10712 if (check_img && isdcl) {
10714 PerlMem_free(resspec);
10715 PerlMem_free(vmsspec);
10719 if (cando_by_name(S_IXUSR,0,resspec)) {
10720 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10721 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10723 my_strlcpy(vmscmd->dsc$a_pointer,"$ MCR ", MAX_DCL_LINE_LENGTH);
10724 if (image_name[0] != 0) {
10725 my_strlcat(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10726 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10728 } else if (image_name[0] != 0) {
10729 my_strlcpy(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10730 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10732 my_strlcpy(vmscmd->dsc$a_pointer, "@", MAX_DCL_LINE_LENGTH);
10734 if (suggest_quote) *suggest_quote = 1;
10736 /* If there is an image name, use original command */
10737 if (image_name[0] == 0)
10738 my_strlcat(vmscmd->dsc$a_pointer, resspec, MAX_DCL_LINE_LENGTH);
10741 while (*rest && isspace(*rest)) rest++;
10744 if (image_argv[0] != 0) {
10745 my_strlcat(vmscmd->dsc$a_pointer, image_argv, MAX_DCL_LINE_LENGTH);
10746 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10752 rest_len = strlen(rest);
10753 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10754 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10755 my_strlcat(vmscmd->dsc$a_pointer, rest, MAX_DCL_LINE_LENGTH);
10757 retsts = CLI$_BUFOVF;
10759 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10761 PerlMem_free(vmsspec);
10762 PerlMem_free(resspec);
10763 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10769 /* It's either a DCL command or we couldn't find a suitable image */
10770 vmscmd->dsc$w_length = strlen(cmd);
10772 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(vmscmd->dsc$w_length + 1);
10773 my_strlcpy(vmscmd->dsc$a_pointer, cmd, vmscmd->dsc$w_length + 1);
10776 PerlMem_free(resspec);
10777 PerlMem_free(vmsspec);
10779 /* check if it's a symbol (for quoting purposes) */
10780 if (suggest_quote && !*suggest_quote) {
10782 char equiv[LNM$C_NAMLENGTH];
10783 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10784 eqvdsc.dsc$a_pointer = equiv;
10786 iss = lib$get_symbol(vmscmd,&eqvdsc);
10787 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10789 if (!(retsts & 1)) {
10790 /* just hand off status values likely to be due to user error */
10791 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10792 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10793 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10794 else { _ckvmssts_noperl(retsts); }
10797 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10799 } /* end of setup_cmddsc() */
10802 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10804 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10810 if (vfork_called) { /* this follows a vfork - act Unixish */
10812 if (vfork_called < 0) {
10813 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10816 else return do_aexec(really,mark,sp);
10818 /* no vfork - act VMSish */
10819 cmd = setup_argstr(aTHX_ really,mark,sp);
10820 exec_sts = vms_do_exec(cmd);
10821 Safefree(cmd); /* Clean up from setup_argstr() */
10826 } /* end of vms_do_aexec() */
10829 /* {{{bool vms_do_exec(char *cmd) */
10831 Perl_vms_do_exec(pTHX_ const char *cmd)
10833 struct dsc$descriptor_s *vmscmd;
10835 if (vfork_called) { /* this follows a vfork - act Unixish */
10837 if (vfork_called < 0) {
10838 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10841 else return do_exec(cmd);
10844 { /* no vfork - act VMSish */
10845 unsigned long int retsts;
10848 TAINT_PROPER("exec");
10849 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10850 retsts = lib$do_command(vmscmd);
10853 case RMS$_FNF: case RMS$_DNF:
10854 set_errno(ENOENT); break;
10856 set_errno(ENOTDIR); break;
10858 set_errno(ENODEV); break;
10860 set_errno(EACCES); break;
10862 set_errno(EINVAL); break;
10863 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10864 set_errno(E2BIG); break;
10865 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10866 _ckvmssts_noperl(retsts); /* fall through */
10867 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10868 set_errno(EVMSERR);
10870 set_vaxc_errno(retsts);
10871 if (ckWARN(WARN_EXEC)) {
10872 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
10873 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
10875 vms_execfree(vmscmd);
10880 } /* end of vms_do_exec() */
10883 int do_spawn2(pTHX_ const char *, int);
10886 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
10888 unsigned long int sts;
10894 /* We'll copy the (undocumented?) Win32 behavior and allow a
10895 * numeric first argument. But the only value we'll support
10896 * through do_aspawn is a value of 1, which means spawn without
10897 * waiting for completion -- other values are ignored.
10899 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
10901 flags = SvIVx(*mark);
10904 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
10905 flags = CLI$M_NOWAIT;
10909 cmd = setup_argstr(aTHX_ really, mark, sp);
10910 sts = do_spawn2(aTHX_ cmd, flags);
10911 /* pp_sys will clean up cmd */
10915 } /* end of do_aspawn() */
10919 /* {{{int do_spawn(char* cmd) */
10921 Perl_do_spawn(pTHX_ char* cmd)
10923 PERL_ARGS_ASSERT_DO_SPAWN;
10925 return do_spawn2(aTHX_ cmd, 0);
10929 /* {{{int do_spawn_nowait(char* cmd) */
10931 Perl_do_spawn_nowait(pTHX_ char* cmd)
10933 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
10935 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
10939 /* {{{int do_spawn2(char *cmd) */
10941 do_spawn2(pTHX_ const char *cmd, int flags)
10943 unsigned long int sts, substs;
10945 /* The caller of this routine expects to Safefree(PL_Cmd) */
10946 Newx(PL_Cmd,10,char);
10949 TAINT_PROPER("spawn");
10950 if (!cmd || !*cmd) {
10951 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
10954 case RMS$_FNF: case RMS$_DNF:
10955 set_errno(ENOENT); break;
10957 set_errno(ENOTDIR); break;
10959 set_errno(ENODEV); break;
10961 set_errno(EACCES); break;
10963 set_errno(EINVAL); break;
10964 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10965 set_errno(E2BIG); break;
10966 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10967 _ckvmssts_noperl(sts); /* fall through */
10968 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10969 set_errno(EVMSERR);
10971 set_vaxc_errno(sts);
10972 if (ckWARN(WARN_EXEC)) {
10973 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
10982 if (flags & CLI$M_NOWAIT)
10985 strcpy(mode, "nW");
10987 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
10990 /* sts will be the pid in the nowait case */
10993 } /* end of do_spawn2() */
10997 static unsigned int *sockflags, sockflagsize;
11000 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11001 * routines found in some versions of the CRTL can't deal with sockets.
11002 * We don't shim the other file open routines since a socket isn't
11003 * likely to be opened by a name.
11005 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11006 FILE *my_fdopen(int fd, const char *mode)
11008 FILE *fp = fdopen(fd, mode);
11011 unsigned int fdoff = fd / sizeof(unsigned int);
11012 Stat_t sbuf; /* native stat; we don't need flex_stat */
11013 if (!sockflagsize || fdoff > sockflagsize) {
11014 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
11015 else Newx (sockflags,fdoff+2,unsigned int);
11016 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11017 sockflagsize = fdoff + 2;
11019 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11020 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11029 * Clear the corresponding bit when the (possibly) socket stream is closed.
11030 * There still a small hole: we miss an implicit close which might occur
11031 * via freopen(). >> Todo
11033 /*{{{ int my_fclose(FILE *fp)*/
11034 int my_fclose(FILE *fp) {
11036 unsigned int fd = fileno(fp);
11037 unsigned int fdoff = fd / sizeof(unsigned int);
11039 if (sockflagsize && fdoff < sockflagsize)
11040 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11048 * A simple fwrite replacement which outputs itmsz*nitm chars without
11049 * introducing record boundaries every itmsz chars.
11050 * We are using fputs, which depends on a terminating null. We may
11051 * well be writing binary data, so we need to accommodate not only
11052 * data with nulls sprinkled in the middle but also data with no null
11055 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11057 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11059 char *cp, *end, *cpd;
11061 unsigned int fd = fileno(dest);
11062 unsigned int fdoff = fd / sizeof(unsigned int);
11064 int bufsize = itmsz * nitm + 1;
11066 if (fdoff < sockflagsize &&
11067 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11068 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11072 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11073 memcpy( data, src, itmsz*nitm );
11074 data[itmsz*nitm] = '\0';
11076 end = data + itmsz * nitm;
11077 retval = (int) nitm; /* on success return # items written */
11080 while (cpd <= end) {
11081 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11082 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11084 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11088 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11091 } /* end of my_fwrite() */
11094 /*{{{ int my_flush(FILE *fp)*/
11096 Perl_my_flush(pTHX_ FILE *fp)
11099 if ((res = fflush(fp)) == 0 && fp) {
11100 #ifdef VMS_DO_SOCKETS
11102 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11104 res = fsync(fileno(fp));
11107 * If the flush succeeded but set end-of-file, we need to clear
11108 * the error because our caller may check ferror(). BTW, this
11109 * probably means we just flushed an empty file.
11111 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11117 /* fgetname() is not returning the correct file specifications when
11118 * decc_filename_unix_report mode is active. So we have to have it
11119 * aways return filenames in VMS mode and convert it ourselves.
11122 /*{{{ char * my_fgetname(FILE *fp, buf)*/
11124 Perl_my_fgetname(FILE *fp, char * buf) {
11128 retname = fgetname(fp, buf, 1);
11130 /* If we are in VMS mode, then we are done */
11131 if (!decc_filename_unix_report || (retname == NULL)) {
11135 /* Convert this to Unix format */
11136 vms_name = (char *)PerlMem_malloc(VMS_MAXRSS);
11137 my_strlcpy(vms_name, retname, VMS_MAXRSS);
11138 retname = int_tounixspec(vms_name, buf, NULL);
11139 PerlMem_free(vms_name);
11146 * Here are replacements for the following Unix routines in the VMS environment:
11147 * getpwuid Get information for a particular UIC or UID
11148 * getpwnam Get information for a named user
11149 * getpwent Get information for each user in the rights database
11150 * setpwent Reset search to the start of the rights database
11151 * endpwent Finish searching for users in the rights database
11153 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11154 * (defined in pwd.h), which contains the following fields:-
11156 * char *pw_name; Username (in lower case)
11157 * char *pw_passwd; Hashed password
11158 * unsigned int pw_uid; UIC
11159 * unsigned int pw_gid; UIC group number
11160 * char *pw_unixdir; Default device/directory (VMS-style)
11161 * char *pw_gecos; Owner name
11162 * char *pw_dir; Default device/directory (Unix-style)
11163 * char *pw_shell; Default CLI name (eg. DCL)
11165 * If the specified user does not exist, getpwuid and getpwnam return NULL.
11167 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11168 * not the UIC member number (eg. what's returned by getuid()),
11169 * getpwuid() can accept either as input (if uid is specified, the caller's
11170 * UIC group is used), though it won't recognise gid=0.
11172 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11173 * information about other users in your group or in other groups, respectively.
11174 * If the required privilege is not available, then these routines fill only
11175 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11178 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11181 /* sizes of various UAF record fields */
11182 #define UAI$S_USERNAME 12
11183 #define UAI$S_IDENT 31
11184 #define UAI$S_OWNER 31
11185 #define UAI$S_DEFDEV 31
11186 #define UAI$S_DEFDIR 63
11187 #define UAI$S_DEFCLI 31
11188 #define UAI$S_PWD 8
11190 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
11191 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11192 (uic).uic$v_group != UIC$K_WILD_GROUP)
11194 static char __empty[]= "";
11195 static struct passwd __passwd_empty=
11196 {(char *) __empty, (char *) __empty, 0, 0,
11197 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11198 static int contxt= 0;
11199 static struct passwd __pwdcache;
11200 static char __pw_namecache[UAI$S_IDENT+1];
11203 * This routine does most of the work extracting the user information.
11205 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11208 unsigned char length;
11209 char pw_gecos[UAI$S_OWNER+1];
11211 static union uicdef uic;
11213 unsigned char length;
11214 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11217 unsigned char length;
11218 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11221 unsigned char length;
11222 char pw_shell[UAI$S_DEFCLI+1];
11224 static char pw_passwd[UAI$S_PWD+1];
11226 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11227 struct dsc$descriptor_s name_desc;
11228 unsigned long int sts;
11230 static struct itmlst_3 itmlst[]= {
11231 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
11232 {sizeof(uic), UAI$_UIC, &uic, &luic},
11233 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
11234 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
11235 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
11236 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
11237 {0, 0, NULL, NULL}};
11239 name_desc.dsc$w_length= strlen(name);
11240 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11241 name_desc.dsc$b_class= DSC$K_CLASS_S;
11242 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11244 /* Note that sys$getuai returns many fields as counted strings. */
11245 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11246 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11247 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11249 else { _ckvmssts(sts); }
11250 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
11252 if ((int) owner.length < lowner) lowner= (int) owner.length;
11253 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11254 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11255 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11256 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11257 owner.pw_gecos[lowner]= '\0';
11258 defdev.pw_dir[ldefdev+ldefdir]= '\0';
11259 defcli.pw_shell[ldefcli]= '\0';
11260 if (valid_uic(uic)) {
11261 pwd->pw_uid= uic.uic$l_uic;
11262 pwd->pw_gid= uic.uic$v_group;
11265 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11266 pwd->pw_passwd= pw_passwd;
11267 pwd->pw_gecos= owner.pw_gecos;
11268 pwd->pw_dir= defdev.pw_dir;
11269 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11270 pwd->pw_shell= defcli.pw_shell;
11271 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11273 ldir= strlen(pwd->pw_unixdir) - 1;
11274 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11277 my_strlcpy(pwd->pw_unixdir, pwd->pw_dir, sizeof(pwd->pw_unixdir));
11278 if (!decc_efs_case_preserve)
11279 __mystrtolower(pwd->pw_unixdir);
11284 * Get information for a named user.
11286 /*{{{struct passwd *getpwnam(char *name)*/
11287 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
11289 struct dsc$descriptor_s name_desc;
11291 unsigned long int sts;
11293 __pwdcache = __passwd_empty;
11294 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11295 /* We still may be able to determine pw_uid and pw_gid */
11296 name_desc.dsc$w_length= strlen(name);
11297 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11298 name_desc.dsc$b_class= DSC$K_CLASS_S;
11299 name_desc.dsc$a_pointer= (char *) name;
11300 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11301 __pwdcache.pw_uid= uic.uic$l_uic;
11302 __pwdcache.pw_gid= uic.uic$v_group;
11305 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11306 set_vaxc_errno(sts);
11307 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11310 else { _ckvmssts(sts); }
11313 my_strlcpy(__pw_namecache, name, sizeof(__pw_namecache));
11314 __pwdcache.pw_name= __pw_namecache;
11315 return &__pwdcache;
11316 } /* end of my_getpwnam() */
11320 * Get information for a particular UIC or UID.
11321 * Called by my_getpwent with uid=-1 to list all users.
11323 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11324 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
11326 const $DESCRIPTOR(name_desc,__pw_namecache);
11327 unsigned short lname;
11329 unsigned long int status;
11331 if (uid == (unsigned int) -1) {
11333 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11334 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11335 set_vaxc_errno(status);
11336 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11340 else { _ckvmssts(status); }
11341 } while (!valid_uic (uic));
11344 uic.uic$l_uic= uid;
11345 if (!uic.uic$v_group)
11346 uic.uic$v_group= PerlProc_getgid();
11347 if (valid_uic(uic))
11348 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11349 else status = SS$_IVIDENT;
11350 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11351 status == RMS$_PRV) {
11352 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11355 else { _ckvmssts(status); }
11357 __pw_namecache[lname]= '\0';
11358 __mystrtolower(__pw_namecache);
11360 __pwdcache = __passwd_empty;
11361 __pwdcache.pw_name = __pw_namecache;
11363 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11364 The identifier's value is usually the UIC, but it doesn't have to be,
11365 so if we can, we let fillpasswd update this. */
11366 __pwdcache.pw_uid = uic.uic$l_uic;
11367 __pwdcache.pw_gid = uic.uic$v_group;
11369 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11370 return &__pwdcache;
11372 } /* end of my_getpwuid() */
11376 * Get information for next user.
11378 /*{{{struct passwd *my_getpwent()*/
11379 struct passwd *Perl_my_getpwent(pTHX)
11381 return (my_getpwuid((unsigned int) -1));
11386 * Finish searching rights database for users.
11388 /*{{{void my_endpwent()*/
11389 void Perl_my_endpwent(pTHX)
11392 _ckvmssts(sys$finish_rdb(&contxt));
11398 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11399 * my_utime(), and flex_stat(), all of which operate on UTC unless
11400 * VMSISH_TIMES is true.
11402 /* method used to handle UTC conversions:
11403 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
11405 static int gmtime_emulation_type;
11406 /* number of secs to add to UTC POSIX-style time to get local time */
11407 static long int utc_offset_secs;
11409 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11410 * in vmsish.h. #undef them here so we can call the CRTL routines
11418 static time_t toutc_dst(time_t loc) {
11421 if ((rsltmp = localtime(&loc)) == NULL) return -1u;
11422 loc -= utc_offset_secs;
11423 if (rsltmp->tm_isdst) loc -= 3600;
11426 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11427 ((gmtime_emulation_type || my_time(NULL)), \
11428 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11429 ((secs) - utc_offset_secs))))
11431 static time_t toloc_dst(time_t utc) {
11434 utc += utc_offset_secs;
11435 if ((rsltmp = localtime(&utc)) == NULL) return -1u;
11436 if (rsltmp->tm_isdst) utc += 3600;
11439 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11440 ((gmtime_emulation_type || my_time(NULL)), \
11441 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11442 ((secs) + utc_offset_secs))))
11444 /* my_time(), my_localtime(), my_gmtime()
11445 * By default traffic in UTC time values, using CRTL gmtime() or
11446 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11447 * Note: We need to use these functions even when the CRTL has working
11448 * UTC support, since they also handle C<use vmsish qw(times);>
11450 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
11451 * Modified by Charles Bailey <bailey@newman.upenn.edu>
11454 /*{{{time_t my_time(time_t *timep)*/
11455 time_t Perl_my_time(pTHX_ time_t *timep)
11460 if (gmtime_emulation_type == 0) {
11461 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
11462 /* results of calls to gmtime() and localtime() */
11463 /* for same &base */
11465 gmtime_emulation_type++;
11466 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11467 char off[LNM$C_NAMLENGTH+1];;
11469 gmtime_emulation_type++;
11470 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11471 gmtime_emulation_type++;
11472 utc_offset_secs = 0;
11473 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11475 else { utc_offset_secs = atol(off); }
11477 else { /* We've got a working gmtime() */
11478 struct tm gmt, local;
11481 tm_p = localtime(&base);
11483 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
11484 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11485 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
11486 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
11491 # ifdef VMSISH_TIME
11492 if (VMSISH_TIME) when = _toloc(when);
11494 if (timep != NULL) *timep = when;
11497 } /* end of my_time() */
11501 /*{{{struct tm *my_gmtime(const time_t *timep)*/
11503 Perl_my_gmtime(pTHX_ const time_t *timep)
11508 if (timep == NULL) {
11509 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11512 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11515 # ifdef VMSISH_TIME
11516 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11518 return gmtime(&when);
11519 } /* end of my_gmtime() */
11523 /*{{{struct tm *my_localtime(const time_t *timep)*/
11525 Perl_my_localtime(pTHX_ const time_t *timep)
11529 if (timep == NULL) {
11530 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11533 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11534 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
11537 # ifdef VMSISH_TIME
11538 if (VMSISH_TIME) when = _toutc(when);
11540 /* CRTL localtime() wants UTC as input, does tz correction itself */
11541 return localtime(&when);
11542 } /* end of my_localtime() */
11545 /* Reset definitions for later calls */
11546 #define gmtime(t) my_gmtime(t)
11547 #define localtime(t) my_localtime(t)
11548 #define time(t) my_time(t)
11551 /* my_utime - update modification/access time of a file
11553 * VMS 7.3 and later implementation
11554 * Only the UTC translation is home-grown. The rest is handled by the
11555 * CRTL utime(), which will take into account the relevant feature
11556 * logicals and ODS-5 volume characteristics for true access times.
11558 * pre VMS 7.3 implementation:
11559 * The calling sequence is identical to POSIX utime(), but under
11560 * VMS with ODS-2, only the modification time is changed; ODS-2 does
11561 * not maintain access times. Restrictions differ from the POSIX
11562 * definition in that the time can be changed as long as the
11563 * caller has permission to execute the necessary IO$_MODIFY $QIO;
11564 * no separate checks are made to insure that the caller is the
11565 * owner of the file or has special privs enabled.
11566 * Code here is based on Joe Meadows' FILE utility.
11570 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11571 * to VMS epoch (01-JAN-1858 00:00:00.00)
11572 * in 100 ns intervals.
11574 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11576 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11577 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
11579 #if __CRTL_VER >= 70300000
11580 struct utimbuf utc_utimes, *utc_utimesp;
11582 if (utimes != NULL) {
11583 utc_utimes.actime = utimes->actime;
11584 utc_utimes.modtime = utimes->modtime;
11585 # ifdef VMSISH_TIME
11586 /* If input was local; convert to UTC for sys svc */
11588 utc_utimes.actime = _toutc(utimes->actime);
11589 utc_utimes.modtime = _toutc(utimes->modtime);
11592 utc_utimesp = &utc_utimes;
11595 utc_utimesp = NULL;
11598 return utime(file, utc_utimesp);
11600 #else /* __CRTL_VER < 70300000 */
11604 long int bintime[2], len = 2, lowbit, unixtime,
11605 secscale = 10000000; /* seconds --> 100 ns intervals */
11606 unsigned long int chan, iosb[2], retsts;
11607 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
11608 struct FAB myfab = cc$rms_fab;
11609 struct NAM mynam = cc$rms_nam;
11610 #if defined (__DECC) && defined (__VAX)
11611 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
11612 * at least through VMS V6.1, which causes a type-conversion warning.
11614 # pragma message save
11615 # pragma message disable cvtdiftypes
11617 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
11618 struct fibdef myfib;
11619 #if defined (__DECC) && defined (__VAX)
11620 /* This should be right after the declaration of myatr, but due
11621 * to a bug in VAX DEC C, this takes effect a statement early.
11623 # pragma message restore
11625 /* cast ok for read only parameter */
11626 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
11627 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
11628 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
11630 if (file == NULL || *file == '\0') {
11631 SETERRNO(ENOENT, LIB$_INVARG);
11635 /* Convert to VMS format ensuring that it will fit in 255 characters */
11636 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
11637 SETERRNO(ENOENT, LIB$_INVARG);
11640 if (utimes != NULL) {
11641 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
11642 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
11643 * Since time_t is unsigned long int, and lib$emul takes a signed long int
11644 * as input, we force the sign bit to be clear by shifting unixtime right
11645 * one bit, then multiplying by an extra factor of 2 in lib$emul().
11647 lowbit = (utimes->modtime & 1) ? secscale : 0;
11648 unixtime = (long int) utimes->modtime;
11649 # ifdef VMSISH_TIME
11650 /* If input was UTC; convert to local for sys svc */
11651 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
11653 unixtime >>= 1; secscale <<= 1;
11654 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
11655 if (!(retsts & 1)) {
11656 SETERRNO(EVMSERR, retsts);
11659 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
11660 if (!(retsts & 1)) {
11661 SETERRNO(EVMSERR, retsts);
11666 /* Just get the current time in VMS format directly */
11667 retsts = sys$gettim(bintime);
11668 if (!(retsts & 1)) {
11669 SETERRNO(EVMSERR, retsts);
11674 myfab.fab$l_fna = vmsspec;
11675 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
11676 myfab.fab$l_nam = &mynam;
11677 mynam.nam$l_esa = esa;
11678 mynam.nam$b_ess = (unsigned char) sizeof esa;
11679 mynam.nam$l_rsa = rsa;
11680 mynam.nam$b_rss = (unsigned char) sizeof rsa;
11681 if (decc_efs_case_preserve)
11682 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
11684 /* Look for the file to be affected, letting RMS parse the file
11685 * specification for us as well. I have set errno using only
11686 * values documented in the utime() man page for VMS POSIX.
11688 retsts = sys$parse(&myfab,0,0);
11689 if (!(retsts & 1)) {
11690 set_vaxc_errno(retsts);
11691 if (retsts == RMS$_PRV) set_errno(EACCES);
11692 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
11693 else set_errno(EVMSERR);
11696 retsts = sys$search(&myfab,0,0);
11697 if (!(retsts & 1)) {
11698 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11699 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11700 set_vaxc_errno(retsts);
11701 if (retsts == RMS$_PRV) set_errno(EACCES);
11702 else if (retsts == RMS$_FNF) set_errno(ENOENT);
11703 else set_errno(EVMSERR);
11707 devdsc.dsc$w_length = mynam.nam$b_dev;
11708 /* cast ok for read only parameter */
11709 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
11711 retsts = sys$assign(&devdsc,&chan,0,0);
11712 if (!(retsts & 1)) {
11713 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11714 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11715 set_vaxc_errno(retsts);
11716 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
11717 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
11718 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
11719 else set_errno(EVMSERR);
11723 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
11724 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
11726 memset((void *) &myfib, 0, sizeof myfib);
11727 #if defined(__DECC) || defined(__DECCXX)
11728 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
11729 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
11730 /* This prevents the revision time of the file being reset to the current
11731 * time as a result of our IO$_MODIFY $QIO. */
11732 myfib.fib$l_acctl = FIB$M_NORECORD;
11734 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
11735 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
11736 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
11738 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
11739 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11740 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11741 _ckvmssts(sys$dassgn(chan));
11742 if (retsts & 1) retsts = iosb[0];
11743 if (!(retsts & 1)) {
11744 set_vaxc_errno(retsts);
11745 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11746 else set_errno(EVMSERR);
11752 #endif /* #if __CRTL_VER >= 70300000 */
11754 } /* end of my_utime() */
11758 * flex_stat, flex_lstat, flex_fstat
11759 * basic stat, but gets it right when asked to stat
11760 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
11763 #ifndef _USE_STD_STAT
11764 /* encode_dev packs a VMS device name string into an integer to allow
11765 * simple comparisons. This can be used, for example, to check whether two
11766 * files are located on the same device, by comparing their encoded device
11767 * names. Even a string comparison would not do, because stat() reuses the
11768 * device name buffer for each call; so without encode_dev, it would be
11769 * necessary to save the buffer and use strcmp (this would mean a number of
11770 * changes to the standard Perl code, to say nothing of what a Perl script
11771 * would have to do.
11773 * The device lock id, if it exists, should be unique (unless perhaps compared
11774 * with lock ids transferred from other nodes). We have a lock id if the disk is
11775 * mounted cluster-wide, which is when we tend to get long (host-qualified)
11776 * device names. Thus we use the lock id in preference, and only if that isn't
11777 * available, do we try to pack the device name into an integer (flagged by
11778 * the sign bit (LOCKID_MASK) being set).
11780 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
11781 * name and its encoded form, but it seems very unlikely that we will find
11782 * two files on different disks that share the same encoded device names,
11783 * and even more remote that they will share the same file id (if the test
11784 * is to check for the same file).
11786 * A better method might be to use sys$device_scan on the first call, and to
11787 * search for the device, returning an index into the cached array.
11788 * The number returned would be more intelligible.
11789 * This is probably not worth it, and anyway would take quite a bit longer
11790 * on the first call.
11792 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
11793 static mydev_t encode_dev (pTHX_ const char *dev)
11796 unsigned long int f;
11801 if (!dev || !dev[0]) return 0;
11805 struct dsc$descriptor_s dev_desc;
11806 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
11808 /* For cluster-mounted disks, the disk lock identifier is unique, so we
11809 can try that first. */
11810 dev_desc.dsc$w_length = strlen (dev);
11811 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
11812 dev_desc.dsc$b_class = DSC$K_CLASS_S;
11813 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
11814 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
11815 if (!$VMS_STATUS_SUCCESS(status)) {
11817 case SS$_NOSUCHDEV:
11818 SETERRNO(ENODEV, status);
11824 if (lockid) return (lockid & ~LOCKID_MASK);
11828 /* Otherwise we try to encode the device name */
11832 for (q = dev + strlen(dev); q--; q >= dev) {
11837 else if (isalpha (toupper (*q)))
11838 c= toupper (*q) - 'A' + (char)10;
11840 continue; /* Skip '$'s */
11842 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
11844 enc += f * (unsigned long int) c;
11846 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
11848 } /* end of encode_dev() */
11849 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11850 device_no = encode_dev(aTHX_ devname)
11852 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11853 device_no = new_dev_no
11857 is_null_device(const char *name)
11859 if (decc_bug_devnull != 0) {
11860 if (strncmp("/dev/null", name, 9) == 0)
11863 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
11864 The underscore prefix, controller letter, and unit number are
11865 independently optional; for our purposes, the colon punctuation
11866 is not. The colon can be trailed by optional directory and/or
11867 filename, but two consecutive colons indicates a nodename rather
11868 than a device. [pr] */
11869 if (*name == '_') ++name;
11870 if (tolower(*name++) != 'n') return 0;
11871 if (tolower(*name++) != 'l') return 0;
11872 if (tolower(*name) == 'a') ++name;
11873 if (*name == '0') ++name;
11874 return (*name++ == ':') && (*name != ':');
11878 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
11880 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
11883 Perl_cando_by_name_int
11884 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
11886 char usrname[L_cuserid];
11887 struct dsc$descriptor_s usrdsc =
11888 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
11889 char *vmsname = NULL, *fileified = NULL;
11890 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
11891 unsigned short int retlen, trnlnm_iter_count;
11892 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11893 union prvdef curprv;
11894 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
11895 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
11896 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
11897 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
11898 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
11900 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
11902 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11904 static int profile_context = -1;
11906 if (!fname || !*fname) return FALSE;
11908 /* Make sure we expand logical names, since sys$check_access doesn't */
11909 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
11910 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11911 if (!strpbrk(fname,"/]>:")) {
11912 my_strlcpy(fileified, fname, VMS_MAXRSS);
11913 trnlnm_iter_count = 0;
11914 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
11915 trnlnm_iter_count++;
11916 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
11921 vmsname = (char *)PerlMem_malloc(VMS_MAXRSS);
11922 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11923 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
11924 /* Don't know if already in VMS format, so make sure */
11925 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
11926 PerlMem_free(fileified);
11927 PerlMem_free(vmsname);
11932 my_strlcpy(vmsname, fname, VMS_MAXRSS);
11935 /* sys$check_access needs a file spec, not a directory spec.
11936 * flex_stat now will handle a null thread context during startup.
11939 retlen = namdsc.dsc$w_length = strlen(vmsname);
11940 if (vmsname[retlen-1] == ']'
11941 || vmsname[retlen-1] == '>'
11942 || vmsname[retlen-1] == ':'
11943 || (!flex_stat_int(vmsname, &st, 1) &&
11944 S_ISDIR(st.st_mode))) {
11946 if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
11947 PerlMem_free(fileified);
11948 PerlMem_free(vmsname);
11957 retlen = namdsc.dsc$w_length = strlen(fname);
11958 namdsc.dsc$a_pointer = (char *)fname;
11961 case S_IXUSR: case S_IXGRP: case S_IXOTH:
11962 access = ARM$M_EXECUTE;
11963 flags = CHP$M_READ;
11965 case S_IRUSR: case S_IRGRP: case S_IROTH:
11966 access = ARM$M_READ;
11967 flags = CHP$M_READ | CHP$M_USEREADALL;
11969 case S_IWUSR: case S_IWGRP: case S_IWOTH:
11970 access = ARM$M_WRITE;
11971 flags = CHP$M_READ | CHP$M_WRITE;
11973 case S_IDUSR: case S_IDGRP: case S_IDOTH:
11974 access = ARM$M_DELETE;
11975 flags = CHP$M_READ | CHP$M_WRITE;
11978 if (fileified != NULL)
11979 PerlMem_free(fileified);
11980 if (vmsname != NULL)
11981 PerlMem_free(vmsname);
11985 /* Before we call $check_access, create a user profile with the current
11986 * process privs since otherwise it just uses the default privs from the
11987 * UAF and might give false positives or negatives. This only works on
11988 * VMS versions v6.0 and later since that's when sys$create_user_profile
11989 * became available.
11992 /* get current process privs and username */
11993 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
11994 _ckvmssts_noperl(iosb[0]);
11996 /* find out the space required for the profile */
11997 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
11998 &usrprodsc.dsc$w_length,&profile_context));
12000 /* allocate space for the profile and get it filled in */
12001 usrprodsc.dsc$a_pointer = (char *)PerlMem_malloc(usrprodsc.dsc$w_length);
12002 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12003 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
12004 &usrprodsc.dsc$w_length,&profile_context));
12006 /* use the profile to check access to the file; free profile & analyze results */
12007 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
12008 PerlMem_free(usrprodsc.dsc$a_pointer);
12009 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
12011 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
12012 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
12013 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
12014 set_vaxc_errno(retsts);
12015 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12016 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12017 else set_errno(ENOENT);
12018 if (fileified != NULL)
12019 PerlMem_free(fileified);
12020 if (vmsname != NULL)
12021 PerlMem_free(vmsname);
12024 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12025 if (fileified != NULL)
12026 PerlMem_free(fileified);
12027 if (vmsname != NULL)
12028 PerlMem_free(vmsname);
12031 _ckvmssts_noperl(retsts);
12033 if (fileified != NULL)
12034 PerlMem_free(fileified);
12035 if (vmsname != NULL)
12036 PerlMem_free(vmsname);
12037 return FALSE; /* Should never get here */
12041 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
12042 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12043 * subset of the applicable information.
12046 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12048 return cando_by_name_int
12049 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12050 } /* end of cando() */
12054 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12056 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12058 return cando_by_name_int(bit, effective, fname, 0);
12060 } /* end of cando_by_name() */
12064 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12066 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12068 dSAVE_ERRNO; /* fstat may set this even on success */
12069 if (!fstat(fd, &statbufp->crtl_stat)) {
12071 char *vms_filename;
12072 vms_filename = (char *)PerlMem_malloc(VMS_MAXRSS);
12073 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12075 /* Save name for cando by name in VMS format */
12076 cptr = getname(fd, vms_filename, 1);
12078 /* This should not happen, but just in case */
12079 if (cptr == NULL) {
12080 statbufp->st_devnam[0] = 0;
12083 /* Make sure that the saved name fits in 255 characters */
12084 cptr = int_rmsexpand_vms
12086 statbufp->st_devnam,
12089 statbufp->st_devnam[0] = 0;
12091 PerlMem_free(vms_filename);
12093 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12095 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12097 # ifdef VMSISH_TIME
12099 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12100 statbufp->st_atime = _toloc(statbufp->st_atime);
12101 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12109 } /* end of flex_fstat() */
12113 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12115 char *temp_fspec = NULL;
12116 char *fileified = NULL;
12117 const char *save_spec;
12121 char already_fileified = 0;
12129 if (decc_bug_devnull != 0) {
12130 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12131 memset(statbufp,0,sizeof *statbufp);
12132 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12133 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12134 statbufp->st_uid = 0x00010001;
12135 statbufp->st_gid = 0x0001;
12136 time((time_t *)&statbufp->st_mtime);
12137 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12144 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12146 * If we are in POSIX filespec mode, accept the filename as is.
12148 if (decc_posix_compliant_pathnames == 0) {
12151 /* Try for a simple stat first. If fspec contains a filename without
12152 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12153 * and sea:[wine.dark]water. exist, the CRTL prefers the directory here.
12154 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12155 * not sea:[wine.dark]., if the latter exists. If the intended target is
12156 * the file with null type, specify this by calling flex_stat() with
12157 * a '.' at the end of fspec.
12160 if (lstat_flag == 0)
12161 retval = stat(fspec, &statbufp->crtl_stat);
12163 retval = lstat(fspec, &statbufp->crtl_stat);
12169 /* In the odd case where we have write but not read access
12170 * to a directory, stat('foo.DIR') works but stat('foo') doesn't.
12172 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12173 if (fileified == NULL)
12174 _ckvmssts_noperl(SS$_INSFMEM);
12176 ret_spec = int_fileify_dirspec(fspec, fileified, NULL);
12177 if (ret_spec != NULL) {
12178 if (lstat_flag == 0)
12179 retval = stat(fileified, &statbufp->crtl_stat);
12181 retval = lstat(fileified, &statbufp->crtl_stat);
12182 save_spec = fileified;
12183 already_fileified = 1;
12187 if (retval && vms_bug_stat_filename) {
12189 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
12190 if (temp_fspec == NULL)
12191 _ckvmssts_noperl(SS$_INSFMEM);
12193 /* We should try again as a vmsified file specification. */
12195 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12196 if (ret_spec != NULL) {
12197 if (lstat_flag == 0)
12198 retval = stat(temp_fspec, &statbufp->crtl_stat);
12200 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12201 save_spec = temp_fspec;
12206 /* Last chance - allow multiple dots without EFS CHARSET */
12207 /* The CRTL stat() falls down hard on multi-dot filenames in unix
12208 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12209 * enable it if it isn't already.
12211 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12212 if (!decc_efs_charset && (decc_efs_charset_index > 0))
12213 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12215 if (lstat_flag == 0)
12216 retval = stat(fspec, &statbufp->crtl_stat);
12218 retval = lstat(fspec, &statbufp->crtl_stat);
12220 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12221 if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12222 decc$feature_set_value(decc_efs_charset_index, 1, 0);
12228 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12230 if (lstat_flag == 0)
12231 retval = stat(temp_fspec, &statbufp->crtl_stat);
12233 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12234 save_spec = temp_fspec;
12238 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12239 /* As you were... */
12240 if (!decc_efs_charset)
12241 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12246 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12248 /* If this is an lstat, do not follow the link */
12250 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12252 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12253 /* If we used the efs_hack above, we must also use it here for */
12254 /* perl_cando to work */
12255 if (efs_hack && (decc_efs_charset_index > 0)) {
12256 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12260 /* If we've got a directory, save a fileified, expanded version of it
12261 * in st_devnam. If not a directory, just an expanded version.
12263 if (S_ISDIR(statbufp->st_mode) && !already_fileified) {
12264 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12265 if (fileified == NULL)
12266 _ckvmssts_noperl(SS$_INSFMEM);
12268 cptr = do_fileify_dirspec(save_spec, fileified, 0, NULL);
12270 save_spec = fileified;
12273 cptr = int_rmsexpand(save_spec,
12274 statbufp->st_devnam,
12280 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12281 if (efs_hack && (decc_efs_charset_index > 0)) {
12282 decc$feature_set_value(decc_efs_charset, 1, 0);
12286 /* Fix me: If this is NULL then stat found a file, and we could */
12287 /* not convert the specification to VMS - Should never happen */
12289 statbufp->st_devnam[0] = 0;
12291 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12293 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12294 # ifdef VMSISH_TIME
12296 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12297 statbufp->st_atime = _toloc(statbufp->st_atime);
12298 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12302 /* If we were successful, leave errno where we found it */
12303 if (retval == 0) RESTORE_ERRNO;
12305 PerlMem_free(temp_fspec);
12307 PerlMem_free(fileified);
12310 } /* end of flex_stat_int() */
12313 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12315 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12317 return flex_stat_int(fspec, statbufp, 0);
12321 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12323 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12325 return flex_stat_int(fspec, statbufp, 1);
12330 /*{{{char *my_getlogin()*/
12331 /* VMS cuserid == Unix getlogin, except calling sequence */
12335 static char user[L_cuserid];
12336 return cuserid(user);
12341 /* rmscopy - copy a file using VMS RMS routines
12343 * Copies contents and attributes of spec_in to spec_out, except owner
12344 * and protection information. Name and type of spec_in are used as
12345 * defaults for spec_out. The third parameter specifies whether rmscopy()
12346 * should try to propagate timestamps from the input file to the output file.
12347 * If it is less than 0, no timestamps are preserved. If it is 0, then
12348 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12349 * propagated to the output file at creation iff the output file specification
12350 * did not contain an explicit name or type, and the revision date is always
12351 * updated at the end of the copy operation. If it is greater than 0, then
12352 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12353 * other than the revision date should be propagated, and bit 1 indicates
12354 * that the revision date should be propagated.
12356 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12358 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12359 * Incorporates, with permission, some code from EZCOPY by Tim Adye
12360 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12361 * as part of the Perl standard distribution under the terms of the
12362 * GNU General Public License or the Perl Artistic License. Copies
12363 * of each may be found in the Perl standard distribution.
12365 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12367 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12369 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12370 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12371 unsigned long int sts;
12373 struct FAB fab_in, fab_out;
12374 struct RAB rab_in, rab_out;
12375 rms_setup_nam(nam);
12376 rms_setup_nam(nam_out);
12377 struct XABDAT xabdat;
12378 struct XABFHC xabfhc;
12379 struct XABRDT xabrdt;
12380 struct XABSUM xabsum;
12382 vmsin = (char *)PerlMem_malloc(VMS_MAXRSS);
12383 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12384 vmsout = (char *)PerlMem_malloc(VMS_MAXRSS);
12385 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12386 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12387 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
12388 PerlMem_free(vmsin);
12389 PerlMem_free(vmsout);
12390 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12394 esa = (char *)PerlMem_malloc(VMS_MAXRSS);
12395 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12397 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12398 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
12399 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12401 fab_in = cc$rms_fab;
12402 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12403 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12404 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12405 fab_in.fab$l_fop = FAB$M_SQO;
12406 rms_bind_fab_nam(fab_in, nam);
12407 fab_in.fab$l_xab = (void *) &xabdat;
12409 rsa = (char *)PerlMem_malloc(VMS_MAXRSS);
12410 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12412 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12413 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
12414 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12416 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12417 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12418 rms_nam_esl(nam) = 0;
12419 rms_nam_rsl(nam) = 0;
12420 rms_nam_esll(nam) = 0;
12421 rms_nam_rsll(nam) = 0;
12422 #ifdef NAM$M_NO_SHORT_UPCASE
12423 if (decc_efs_case_preserve)
12424 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
12427 xabdat = cc$rms_xabdat; /* To get creation date */
12428 xabdat.xab$l_nxt = (void *) &xabfhc;
12430 xabfhc = cc$rms_xabfhc; /* To get record length */
12431 xabfhc.xab$l_nxt = (void *) &xabsum;
12433 xabsum = cc$rms_xabsum; /* To get key and area information */
12435 if (!((sts = sys$open(&fab_in)) & 1)) {
12436 PerlMem_free(vmsin);
12437 PerlMem_free(vmsout);
12440 PerlMem_free(esal);
12443 PerlMem_free(rsal);
12444 set_vaxc_errno(sts);
12446 case RMS$_FNF: case RMS$_DNF:
12447 set_errno(ENOENT); break;
12449 set_errno(ENOTDIR); break;
12451 set_errno(ENODEV); break;
12453 set_errno(EINVAL); break;
12455 set_errno(EACCES); break;
12457 set_errno(EVMSERR);
12464 fab_out.fab$w_ifi = 0;
12465 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12466 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12467 fab_out.fab$l_fop = FAB$M_SQO;
12468 rms_bind_fab_nam(fab_out, nam_out);
12469 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12470 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12471 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
12472 esa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12473 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12474 rsa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12475 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12478 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12479 esal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12480 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12481 rsal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12482 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12484 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
12485 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
12487 if (preserve_dates == 0) { /* Act like DCL COPY */
12488 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
12489 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
12490 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
12491 PerlMem_free(vmsin);
12492 PerlMem_free(vmsout);
12495 PerlMem_free(esal);
12498 PerlMem_free(rsal);
12499 PerlMem_free(esa_out);
12500 if (esal_out != NULL)
12501 PerlMem_free(esal_out);
12502 PerlMem_free(rsa_out);
12503 if (rsal_out != NULL)
12504 PerlMem_free(rsal_out);
12505 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12506 set_vaxc_errno(sts);
12509 fab_out.fab$l_xab = (void *) &xabdat;
12510 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12511 preserve_dates = 1;
12513 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
12514 preserve_dates =0; /* bitmask from this point forward */
12516 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
12517 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
12518 PerlMem_free(vmsin);
12519 PerlMem_free(vmsout);
12522 PerlMem_free(esal);
12525 PerlMem_free(rsal);
12526 PerlMem_free(esa_out);
12527 if (esal_out != NULL)
12528 PerlMem_free(esal_out);
12529 PerlMem_free(rsa_out);
12530 if (rsal_out != NULL)
12531 PerlMem_free(rsal_out);
12532 set_vaxc_errno(sts);
12535 set_errno(ENOENT); break;
12537 set_errno(ENOTDIR); break;
12539 set_errno(ENODEV); break;
12541 set_errno(EINVAL); break;
12543 set_errno(EACCES); break;
12545 set_errno(EVMSERR);
12549 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
12550 if (preserve_dates & 2) {
12551 /* sys$close() will process xabrdt, not xabdat */
12552 xabrdt = cc$rms_xabrdt;
12554 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12556 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
12557 * is unsigned long[2], while DECC & VAXC use a struct */
12558 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
12560 fab_out.fab$l_xab = (void *) &xabrdt;
12563 ubf = (char *)PerlMem_malloc(32256);
12564 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12565 rab_in = cc$rms_rab;
12566 rab_in.rab$l_fab = &fab_in;
12567 rab_in.rab$l_rop = RAB$M_BIO;
12568 rab_in.rab$l_ubf = ubf;
12569 rab_in.rab$w_usz = 32256;
12570 if (!((sts = sys$connect(&rab_in)) & 1)) {
12571 sys$close(&fab_in); sys$close(&fab_out);
12572 PerlMem_free(vmsin);
12573 PerlMem_free(vmsout);
12577 PerlMem_free(esal);
12580 PerlMem_free(rsal);
12581 PerlMem_free(esa_out);
12582 if (esal_out != NULL)
12583 PerlMem_free(esal_out);
12584 PerlMem_free(rsa_out);
12585 if (rsal_out != NULL)
12586 PerlMem_free(rsal_out);
12587 set_errno(EVMSERR); set_vaxc_errno(sts);
12591 rab_out = cc$rms_rab;
12592 rab_out.rab$l_fab = &fab_out;
12593 rab_out.rab$l_rbf = ubf;
12594 if (!((sts = sys$connect(&rab_out)) & 1)) {
12595 sys$close(&fab_in); sys$close(&fab_out);
12596 PerlMem_free(vmsin);
12597 PerlMem_free(vmsout);
12601 PerlMem_free(esal);
12604 PerlMem_free(rsal);
12605 PerlMem_free(esa_out);
12606 if (esal_out != NULL)
12607 PerlMem_free(esal_out);
12608 PerlMem_free(rsa_out);
12609 if (rsal_out != NULL)
12610 PerlMem_free(rsal_out);
12611 set_errno(EVMSERR); set_vaxc_errno(sts);
12615 while ((sts = sys$read(&rab_in))) { /* always true */
12616 if (sts == RMS$_EOF) break;
12617 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12618 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
12619 sys$close(&fab_in); sys$close(&fab_out);
12620 PerlMem_free(vmsin);
12621 PerlMem_free(vmsout);
12625 PerlMem_free(esal);
12628 PerlMem_free(rsal);
12629 PerlMem_free(esa_out);
12630 if (esal_out != NULL)
12631 PerlMem_free(esal_out);
12632 PerlMem_free(rsa_out);
12633 if (rsal_out != NULL)
12634 PerlMem_free(rsal_out);
12635 set_errno(EVMSERR); set_vaxc_errno(sts);
12641 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
12642 sys$close(&fab_in); sys$close(&fab_out);
12643 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
12645 PerlMem_free(vmsin);
12646 PerlMem_free(vmsout);
12650 PerlMem_free(esal);
12653 PerlMem_free(rsal);
12654 PerlMem_free(esa_out);
12655 if (esal_out != NULL)
12656 PerlMem_free(esal_out);
12657 PerlMem_free(rsa_out);
12658 if (rsal_out != NULL)
12659 PerlMem_free(rsal_out);
12662 set_errno(EVMSERR); set_vaxc_errno(sts);
12668 } /* end of rmscopy() */
12672 /*** The following glue provides 'hooks' to make some of the routines
12673 * from this file available from Perl. These routines are sufficiently
12674 * basic, and are required sufficiently early in the build process,
12675 * that's it's nice to have them available to miniperl as well as the
12676 * full Perl, so they're set up here instead of in an extension. The
12677 * Perl code which handles importation of these names into a given
12678 * package lives in [.VMS]Filespec.pm in @INC.
12682 rmsexpand_fromperl(pTHX_ CV *cv)
12685 char *fspec, *defspec = NULL, *rslt;
12687 int fs_utf8, dfs_utf8;
12691 if (!items || items > 2)
12692 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
12693 fspec = SvPV(ST(0),n_a);
12694 fs_utf8 = SvUTF8(ST(0));
12695 if (!fspec || !*fspec) XSRETURN_UNDEF;
12697 defspec = SvPV(ST(1),n_a);
12698 dfs_utf8 = SvUTF8(ST(1));
12700 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
12701 ST(0) = sv_newmortal();
12702 if (rslt != NULL) {
12703 sv_usepvn(ST(0),rslt,strlen(rslt));
12712 vmsify_fromperl(pTHX_ CV *cv)
12719 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
12720 utf8_fl = SvUTF8(ST(0));
12721 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12722 ST(0) = sv_newmortal();
12723 if (vmsified != NULL) {
12724 sv_usepvn(ST(0),vmsified,strlen(vmsified));
12733 unixify_fromperl(pTHX_ CV *cv)
12740 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
12741 utf8_fl = SvUTF8(ST(0));
12742 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12743 ST(0) = sv_newmortal();
12744 if (unixified != NULL) {
12745 sv_usepvn(ST(0),unixified,strlen(unixified));
12754 fileify_fromperl(pTHX_ CV *cv)
12761 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
12762 utf8_fl = SvUTF8(ST(0));
12763 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12764 ST(0) = sv_newmortal();
12765 if (fileified != NULL) {
12766 sv_usepvn(ST(0),fileified,strlen(fileified));
12775 pathify_fromperl(pTHX_ CV *cv)
12782 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
12783 utf8_fl = SvUTF8(ST(0));
12784 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12785 ST(0) = sv_newmortal();
12786 if (pathified != NULL) {
12787 sv_usepvn(ST(0),pathified,strlen(pathified));
12796 vmspath_fromperl(pTHX_ CV *cv)
12803 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
12804 utf8_fl = SvUTF8(ST(0));
12805 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12806 ST(0) = sv_newmortal();
12807 if (vmspath != NULL) {
12808 sv_usepvn(ST(0),vmspath,strlen(vmspath));
12817 unixpath_fromperl(pTHX_ CV *cv)
12824 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
12825 utf8_fl = SvUTF8(ST(0));
12826 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12827 ST(0) = sv_newmortal();
12828 if (unixpath != NULL) {
12829 sv_usepvn(ST(0),unixpath,strlen(unixpath));
12838 candelete_fromperl(pTHX_ CV *cv)
12846 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
12848 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12849 Newx(fspec, VMS_MAXRSS, char);
12850 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
12851 if (isGV_with_GP(mysv)) {
12852 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
12853 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12861 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
12862 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12869 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
12875 rmscopy_fromperl(pTHX_ CV *cv)
12878 char *inspec, *outspec, *inp, *outp;
12884 if (items < 2 || items > 3)
12885 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
12887 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12888 Newx(inspec, VMS_MAXRSS, char);
12889 if (isGV_with_GP(mysv)) {
12890 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
12891 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12892 ST(0) = sv_2mortal(newSViv(0));
12899 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
12900 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12901 ST(0) = sv_2mortal(newSViv(0));
12906 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
12907 Newx(outspec, VMS_MAXRSS, char);
12908 if (isGV_with_GP(mysv)) {
12909 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
12910 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12911 ST(0) = sv_2mortal(newSViv(0));
12919 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
12920 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12921 ST(0) = sv_2mortal(newSViv(0));
12927 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
12929 ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag)));
12935 /* The mod2fname is limited to shorter filenames by design, so it should
12936 * not be modified to support longer EFS pathnames
12939 mod2fname(pTHX_ CV *cv)
12942 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
12943 workbuff[NAM$C_MAXRSS*1 + 1];
12944 SSize_t counter, num_entries;
12945 /* ODS-5 ups this, but we want to be consistent, so... */
12946 int max_name_len = 39;
12947 AV *in_array = (AV *)SvRV(ST(0));
12949 num_entries = av_len(in_array);
12951 /* All the names start with PL_. */
12952 strcpy(ultimate_name, "PL_");
12954 /* Clean up our working buffer */
12955 Zero(work_name, sizeof(work_name), char);
12957 /* Run through the entries and build up a working name */
12958 for(counter = 0; counter <= num_entries; counter++) {
12959 /* If it's not the first name then tack on a __ */
12961 my_strlcat(work_name, "__", sizeof(work_name));
12963 my_strlcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)), sizeof(work_name));
12966 /* Check to see if we actually have to bother...*/
12967 if (strlen(work_name) + 3 <= max_name_len) {
12968 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
12970 /* It's too darned big, so we need to go strip. We use the same */
12971 /* algorithm as xsubpp does. First, strip out doubled __ */
12972 char *source, *dest, last;
12975 for (source = work_name; *source; source++) {
12976 if (last == *source && last == '_') {
12982 /* Go put it back */
12983 my_strlcpy(work_name, workbuff, sizeof(work_name));
12984 /* Is it still too big? */
12985 if (strlen(work_name) + 3 > max_name_len) {
12986 /* Strip duplicate letters */
12989 for (source = work_name; *source; source++) {
12990 if (last == toupper(*source)) {
12994 last = toupper(*source);
12996 my_strlcpy(work_name, workbuff, sizeof(work_name));
12999 /* Is it *still* too big? */
13000 if (strlen(work_name) + 3 > max_name_len) {
13001 /* Too bad, we truncate */
13002 work_name[max_name_len - 2] = 0;
13004 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
13007 /* Okay, return it */
13008 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13013 hushexit_fromperl(pTHX_ CV *cv)
13018 VMSISH_HUSHED = SvTRUE(ST(0));
13020 ST(0) = boolSV(VMSISH_HUSHED);
13026 Perl_vms_start_glob
13027 (pTHX_ SV *tmpglob,
13031 struct vs_str_st *rslt;
13035 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13038 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13039 struct dsc$descriptor_vs rsdsc;
13040 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13041 unsigned long hasver = 0, isunix = 0;
13042 unsigned long int lff_flags = 0;
13044 int vms_old_glob = 1;
13046 if (!SvOK(tmpglob)) {
13047 SETERRNO(ENOENT,RMS$_FNF);
13051 vms_old_glob = !decc_filename_unix_report;
13053 #ifdef VMS_LONGNAME_SUPPORT
13054 lff_flags = LIB$M_FIL_LONG_NAMES;
13056 /* The Newx macro will not allow me to assign a smaller array
13057 * to the rslt pointer, so we will assign it to the begin char pointer
13058 * and then copy the value into the rslt pointer.
13060 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13061 rslt = (struct vs_str_st *)begin;
13063 rstr = &rslt->str[0];
13064 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13065 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13066 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13067 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13069 Newx(vmsspec, VMS_MAXRSS, char);
13071 /* We could find out if there's an explicit dev/dir or version
13072 by peeking into lib$find_file's internal context at
13073 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13074 but that's unsupported, so I don't want to do it now and
13075 have it bite someone in the future. */
13076 /* Fix-me: vms_split_path() is the only way to do this, the
13077 existing method will fail with many legal EFS or UNIX specifications
13080 cp = SvPV(tmpglob,i);
13083 if (cp[i] == ';') hasver = 1;
13084 if (cp[i] == '.') {
13085 if (sts) hasver = 1;
13088 if (cp[i] == '/') {
13089 hasdir = isunix = 1;
13092 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13098 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13099 if ((hasdir == 0) && decc_filename_unix_report) {
13103 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13104 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13105 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13111 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13112 if (!stat_sts && S_ISDIR(st.st_mode)) {
13114 const char * fname;
13117 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13118 /* path delimiter of ':>]', if so, then the old behavior has */
13119 /* obviously been specifically requested */
13121 fname = SvPVX_const(tmpglob);
13122 fname_len = strlen(fname);
13123 vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13124 if (vms_old_glob || (vms_dir != NULL)) {
13125 wilddsc.dsc$a_pointer = tovmspath_utf8(
13126 SvPVX(tmpglob),vmsspec,NULL);
13127 ok = (wilddsc.dsc$a_pointer != NULL);
13128 /* maybe passed 'foo' rather than '[.foo]', thus not
13132 /* Operate just on the directory, the special stat/fstat for */
13133 /* leaves the fileified specification in the st_devnam */
13135 wilddsc.dsc$a_pointer = st.st_devnam;
13140 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13141 ok = (wilddsc.dsc$a_pointer != NULL);
13144 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13146 /* If not extended character set, replace ? with % */
13147 /* With extended character set, ? is a wildcard single character */
13148 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13151 if (!decc_efs_charset)
13153 } else if (*cp == '%') {
13155 } else if (*cp == '*') {
13161 wv_sts = vms_split_path(
13162 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13163 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13164 &wvs_spec, &wvs_len);
13173 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13174 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13175 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13179 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13180 &dfltdsc,NULL,&rms_sts,&lff_flags);
13181 if (!$VMS_STATUS_SUCCESS(sts))
13184 /* with varying string, 1st word of buffer contains result length */
13185 rstr[rslt->length] = '\0';
13187 /* Find where all the components are */
13188 v_sts = vms_split_path
13203 /* If no version on input, truncate the version on output */
13204 if (!hasver && (vs_len > 0)) {
13211 /* In Unix report mode, remove the ".dir;1" from the name */
13212 /* if it is a real directory */
13213 if (decc_filename_unix_report && decc_efs_charset) {
13214 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13218 ret_sts = flex_lstat(rstr, &statbuf);
13219 if ((ret_sts == 0) &&
13220 S_ISDIR(statbuf.st_mode)) {
13227 /* No version & a null extension on UNIX handling */
13228 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13234 if (!decc_efs_case_preserve) {
13235 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13238 /* Find File treats a Null extension as return all extensions */
13239 /* This is contrary to Perl expectations */
13241 if (wildstar || wildquery || vms_old_glob) {
13242 /* really need to see if the returned file name matched */
13243 /* but for now will assume that it matches */
13246 /* Exact Match requested */
13247 /* How are directories handled? - like a file */
13248 if ((e_len == we_len) && (n_len == wn_len)) {
13252 t1 = strncmp(e_spec, we_spec, e_len);
13256 t1 = strncmp(n_spec, we_spec, n_len);
13267 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13271 /* Start with the name */
13274 strcat(begin,"\n");
13275 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13278 if (cxt) (void)lib$find_file_end(&cxt);
13281 /* Be POSIXish: return the input pattern when no matches */
13282 my_strlcpy(rstr, SvPVX(tmpglob), VMS_MAXRSS);
13284 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13287 if (ok && sts != RMS$_NMF &&
13288 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13291 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13293 PerlIO_close(tmpfp);
13297 PerlIO_rewind(tmpfp);
13298 IoTYPE(io) = IoTYPE_RDONLY;
13299 IoIFP(io) = fp = tmpfp;
13300 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13310 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13314 unixrealpath_fromperl(pTHX_ CV *cv)
13317 char *fspec, *rslt_spec, *rslt;
13320 if (!items || items != 1)
13321 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13323 fspec = SvPV(ST(0),n_a);
13324 if (!fspec || !*fspec) XSRETURN_UNDEF;
13326 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13327 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13329 ST(0) = sv_newmortal();
13331 sv_usepvn(ST(0),rslt,strlen(rslt));
13333 Safefree(rslt_spec);
13338 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13342 vmsrealpath_fromperl(pTHX_ CV *cv)
13345 char *fspec, *rslt_spec, *rslt;
13348 if (!items || items != 1)
13349 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13351 fspec = SvPV(ST(0),n_a);
13352 if (!fspec || !*fspec) XSRETURN_UNDEF;
13354 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13355 rslt = do_vms_realname(fspec, rslt_spec, NULL);
13357 ST(0) = sv_newmortal();
13359 sv_usepvn(ST(0),rslt,strlen(rslt));
13361 Safefree(rslt_spec);
13367 * A thin wrapper around decc$symlink to make sure we follow the
13368 * standard and do not create a symlink with a zero-length name,
13369 * and convert the target to Unix format, as the CRTL can't handle
13370 * targets in VMS format.
13372 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13374 Perl_my_symlink(pTHX_ const char *contents, const char *link_name)
13379 if (!link_name || !*link_name) {
13380 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13384 utarget = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
13385 /* An untranslatable filename should be passed through. */
13386 (void) int_tounixspec(contents, utarget, NULL);
13387 sts = symlink(utarget, link_name);
13388 PerlMem_free(utarget);
13393 #endif /* HAS_SYMLINK */
13395 int do_vms_case_tolerant(void);
13398 case_tolerant_process_fromperl(pTHX_ CV *cv)
13401 ST(0) = boolSV(do_vms_case_tolerant());
13405 #ifdef USE_ITHREADS
13408 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
13409 struct interp_intern *dst)
13411 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13413 memcpy(dst,src,sizeof(struct interp_intern));
13419 Perl_sys_intern_clear(pTHX)
13424 Perl_sys_intern_init(pTHX)
13426 unsigned int ix = RAND_MAX;
13431 MY_POSIX_EXIT = vms_posix_exit;
13434 MY_INV_RAND_MAX = 1./x;
13438 init_os_extras(void)
13441 char* file = __FILE__;
13442 if (decc_disable_to_vms_logname_translation) {
13443 no_translate_barewords = TRUE;
13445 no_translate_barewords = FALSE;
13448 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
13449 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
13450 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
13451 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
13452 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
13453 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
13454 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
13455 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
13456 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
13457 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
13458 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
13459 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
13460 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
13461 newXSproto("VMS::Filespec::case_tolerant_process",
13462 case_tolerant_process_fromperl,file,"");
13464 store_pipelocs(aTHX); /* will redo any earlier attempts */
13469 #if __CRTL_VER == 80200000
13470 /* This missed getting in to the DECC SDK for 8.2 */
13471 char *realpath(const char *file_name, char * resolved_name, ...);
13474 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
13475 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
13476 * The perl fallback routine to provide realpath() is not as efficient
13484 /* Hack, use old stat() as fastest way of getting ino_t and device */
13485 int decc$stat(const char *name, void * statbuf);
13486 #if !defined(__VAX) && __CRTL_VER >= 80200000
13487 int decc$lstat(const char *name, void * statbuf);
13489 #define decc$lstat decc$stat
13497 /* Realpath is fragile. In 8.3 it does not work if the feature
13498 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
13499 * links are implemented in RMS, not the CRTL. It also can fail if the
13500 * user does not have read/execute access to some of the directories.
13501 * So in order for Do What I Mean mode to work, if realpath() fails,
13502 * fall back to looking up the filename by the device name and FID.
13505 int vms_fid_to_name(char * outname, int outlen,
13506 const char * name, int lstat_flag, mode_t * mode)
13508 #pragma message save
13509 #pragma message disable MISALGNDSTRCT
13510 #pragma message disable MISALGNDMEM
13511 #pragma member_alignment save
13512 #pragma nomember_alignment
13515 unsigned short st_ino[3];
13516 unsigned short old_st_mode;
13517 unsigned long padl[30]; /* plenty of room */
13519 #pragma message restore
13520 #pragma member_alignment restore
13523 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13524 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13529 /* Need to follow the mostly the same rules as flex_stat_int, or we may get
13530 * unexpected answers
13533 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
13534 if (fileified == NULL)
13535 _ckvmssts_noperl(SS$_INSFMEM);
13537 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
13538 if (temp_fspec == NULL)
13539 _ckvmssts_noperl(SS$_INSFMEM);
13542 /* First need to try as a directory */
13543 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13544 if (ret_spec != NULL) {
13545 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
13546 if (ret_spec != NULL) {
13547 if (lstat_flag == 0)
13548 sts = decc$stat(fileified, &statbuf);
13550 sts = decc$lstat(fileified, &statbuf);
13554 /* Then as a VMS file spec */
13556 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
13557 if (ret_spec != NULL) {
13558 if (lstat_flag == 0) {
13559 sts = decc$stat(temp_fspec, &statbuf);
13561 sts = decc$lstat(temp_fspec, &statbuf);
13567 /* Next try - allow multiple dots with out EFS CHARSET */
13568 /* The CRTL stat() falls down hard on multi-dot filenames in unix
13569 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
13570 * enable it if it isn't already.
13572 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13573 if (!decc_efs_charset && (decc_efs_charset_index > 0))
13574 decc$feature_set_value(decc_efs_charset_index, 1, 1);
13576 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13577 if (lstat_flag == 0) {
13578 sts = decc$stat(name, &statbuf);
13580 sts = decc$lstat(name, &statbuf);
13582 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13583 if (!decc_efs_charset && (decc_efs_charset_index > 0))
13584 decc$feature_set_value(decc_efs_charset_index, 1, 0);
13589 /* and then because the Perl Unix to VMS conversion is not perfect */
13590 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
13591 /* characters from filenames so we need to try it as-is */
13593 if (lstat_flag == 0) {
13594 sts = decc$stat(name, &statbuf);
13596 sts = decc$lstat(name, &statbuf);
13603 dvidsc.dsc$a_pointer=statbuf.st_dev;
13604 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
13606 specdsc.dsc$a_pointer = outname;
13607 specdsc.dsc$w_length = outlen-1;
13609 vms_sts = lib$fid_to_name
13610 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
13611 if ($VMS_STATUS_SUCCESS(vms_sts)) {
13612 outname[specdsc.dsc$w_length] = 0;
13614 /* Return the mode */
13616 *mode = statbuf.old_st_mode;
13620 PerlMem_free(temp_fspec);
13621 PerlMem_free(fileified);
13628 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
13631 char * rslt = NULL;
13634 if (decc_posix_compliant_pathnames > 0 ) {
13635 /* realpath currently only works if posix compliant pathnames are
13636 * enabled. It may start working when they are not, but in that
13637 * case we still want the fallback behavior for backwards compatibility
13639 rslt = realpath(filespec, outbuf);
13643 if (rslt == NULL) {
13645 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13646 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13649 /* Fall back to fid_to_name */
13651 Newx(vms_spec, VMS_MAXRSS + 1, char);
13653 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
13657 /* Now need to trim the version off */
13658 sts = vms_split_path
13678 /* Trim off the version */
13679 int file_len = v_len + r_len + d_len + n_len + e_len;
13680 vms_spec[file_len] = 0;
13682 /* Trim off the .DIR if this is a directory */
13683 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13684 if (S_ISDIR(my_mode)) {
13690 /* Drop NULL extensions on UNIX file specification */
13691 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13696 /* The result is expected to be in UNIX format */
13697 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
13699 /* Downcase if input had any lower case letters and
13700 * case preservation is not in effect.
13702 if (!decc_efs_case_preserve) {
13703 for (cp = filespec; *cp; cp++)
13704 if (islower(*cp)) { haslower = 1; break; }
13706 if (haslower) __mystrtolower(rslt);
13711 /* Now for some hacks to deal with backwards and forward */
13712 /* compatibility */
13713 if (!decc_efs_charset) {
13715 /* 1. ODS-2 mode wants to do a syntax only translation */
13716 rslt = int_rmsexpand(filespec, outbuf,
13717 NULL, 0, NULL, utf8_fl);
13720 if (decc_filename_unix_report) {
13722 char * vms_dir_name;
13725 /* 2. ODS-5 / UNIX report mode should return a failure */
13726 /* if the parent directory also does not exist */
13727 /* Otherwise, get the real path for the parent */
13728 /* and add the child to it. */
13730 /* basename / dirname only available for VMS 7.0+ */
13731 /* So we may need to implement them as common routines */
13733 Newx(dir_name, VMS_MAXRSS + 1, char);
13734 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
13735 dir_name[0] = '\0';
13738 /* First try a VMS parse */
13739 sts = vms_split_path
13757 int dir_len = v_len + r_len + d_len + n_len;
13759 memcpy(dir_name, filespec, dir_len);
13760 dir_name[dir_len] = '\0';
13761 file_name = (char *)&filespec[dir_len + 1];
13764 /* This must be UNIX */
13767 tchar = strrchr(filespec, '/');
13769 if (tchar != NULL) {
13770 int dir_len = tchar - filespec;
13771 memcpy(dir_name, filespec, dir_len);
13772 dir_name[dir_len] = '\0';
13773 file_name = (char *) &filespec[dir_len + 1];
13777 /* Dir name is defaulted */
13778 if (dir_name[0] == 0) {
13780 dir_name[1] = '\0';
13783 /* Need realpath for the directory */
13784 sts = vms_fid_to_name(vms_dir_name,
13786 dir_name, 0, NULL);
13789 /* Now need to pathify it. */
13790 char *tdir = int_pathify_dirspec(vms_dir_name,
13793 /* And now add the original filespec to it */
13794 if (file_name != NULL) {
13795 my_strlcat(outbuf, file_name, VMS_MAXRSS);
13799 Safefree(vms_dir_name);
13800 Safefree(dir_name);
13804 Safefree(vms_spec);
13810 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
13813 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13814 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13816 /* Fall back to fid_to_name */
13818 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
13825 /* Now need to trim the version off */
13826 sts = vms_split_path
13846 /* Trim off the version */
13847 int file_len = v_len + r_len + d_len + n_len + e_len;
13848 outbuf[file_len] = 0;
13850 /* Downcase if input had any lower case letters and
13851 * case preservation is not in effect.
13853 if (!decc_efs_case_preserve) {
13854 for (cp = filespec; *cp; cp++)
13855 if (islower(*cp)) { haslower = 1; break; }
13857 if (haslower) __mystrtolower(outbuf);
13866 /* External entry points */
13867 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13868 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
13870 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13871 { return do_vms_realname(filespec, outbuf, utf8_fl); }
13873 /* case_tolerant */
13875 /*{{{int do_vms_case_tolerant(void)*/
13876 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
13877 * controlled by a process setting.
13879 int do_vms_case_tolerant(void)
13881 return vms_process_case_tolerant;
13884 /* External entry points */
13885 #if __CRTL_VER >= 70301000 && !defined(__VAX)
13886 int Perl_vms_case_tolerant(void)
13887 { return do_vms_case_tolerant(); }
13889 int Perl_vms_case_tolerant(void)
13890 { return vms_process_case_tolerant; }
13894 /* Start of DECC RTL Feature handling */
13896 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13899 set_feature_default(const char *name, int value)
13905 /* If the feature has been explicitly disabled in the environment,
13906 * then don't enable it here.
13909 status = simple_trnlnm(name, val_str, sizeof(val_str));
13910 if ($VMS_STATUS_SUCCESS(status)) {
13911 val_str[0] = _toupper(val_str[0]);
13912 if (val_str[0] == 'D' || val_str[0] == '0' || val_str[0] == 'F')
13917 index = decc$feature_get_index(name);
13919 status = decc$feature_set_value(index, 1, value);
13920 if (index == -1 || (status == -1)) {
13924 status = decc$feature_get_value(index, 1);
13925 if (status != value) {
13929 /* Various things may check for an environment setting
13930 * rather than the feature directly, so set that too.
13932 vmssetuserlnm(name, value ? "ENABLE" : "DISABLE");
13939 /* C RTL Feature settings */
13941 #if defined(__DECC) || defined(__DECCXX)
13948 vmsperl_set_features(void)
13953 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
13954 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
13955 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
13956 unsigned long case_perm;
13957 unsigned long case_image;
13960 /* Allow an exception to bring Perl into the VMS debugger */
13961 vms_debug_on_exception = 0;
13962 status = simple_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
13963 if ($VMS_STATUS_SUCCESS(status)) {
13964 val_str[0] = _toupper(val_str[0]);
13965 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13966 vms_debug_on_exception = 1;
13968 vms_debug_on_exception = 0;
13971 /* Debug unix/vms file translation routines */
13972 vms_debug_fileify = 0;
13973 status = simple_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
13974 if ($VMS_STATUS_SUCCESS(status)) {
13975 val_str[0] = _toupper(val_str[0]);
13976 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13977 vms_debug_fileify = 1;
13979 vms_debug_fileify = 0;
13983 /* Historically PERL has been doing vmsify / stat differently than */
13984 /* the CRTL. In particular, under some conditions the CRTL will */
13985 /* remove some illegal characters like spaces from filenames */
13986 /* resulting in some differences. The stat()/lstat() wrapper has */
13987 /* been reporting such file names as invalid and fails to stat them */
13988 /* fixing this bug so that stat()/lstat() accept these like the */
13989 /* CRTL does will result in several tests failing. */
13990 /* This should really be fixed, but for now, set up a feature to */
13991 /* enable it so that the impact can be studied. */
13992 vms_bug_stat_filename = 0;
13993 status = simple_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
13994 if ($VMS_STATUS_SUCCESS(status)) {
13995 val_str[0] = _toupper(val_str[0]);
13996 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13997 vms_bug_stat_filename = 1;
13999 vms_bug_stat_filename = 0;
14003 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
14004 vms_vtf7_filenames = 0;
14005 status = simple_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14006 if ($VMS_STATUS_SUCCESS(status)) {
14007 val_str[0] = _toupper(val_str[0]);
14008 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14009 vms_vtf7_filenames = 1;
14011 vms_vtf7_filenames = 0;
14014 /* unlink all versions on unlink() or rename() */
14015 vms_unlink_all_versions = 0;
14016 status = simple_trnlnm
14017 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14018 if ($VMS_STATUS_SUCCESS(status)) {
14019 val_str[0] = _toupper(val_str[0]);
14020 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14021 vms_unlink_all_versions = 1;
14023 vms_unlink_all_versions = 0;
14026 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14027 /* Detect running under GNV Bash or other UNIX like shell */
14028 gnv_unix_shell = 0;
14029 status = simple_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14030 if ($VMS_STATUS_SUCCESS(status)) {
14031 gnv_unix_shell = 1;
14032 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14033 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14034 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14035 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
14036 vms_unlink_all_versions = 1;
14037 vms_posix_exit = 1;
14039 /* Some reasonable defaults that are not CRTL defaults */
14040 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14041 set_feature_default("DECC$ARGV_PARSE_STYLE", 1); /* Requires extended parse. */
14042 set_feature_default("DECC$EFS_CHARSET", 1);
14045 /* hacks to see if known bugs are still present for testing */
14047 /* PCP mode requires creating /dev/null special device file */
14048 decc_bug_devnull = 0;
14049 status = simple_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14050 if ($VMS_STATUS_SUCCESS(status)) {
14051 val_str[0] = _toupper(val_str[0]);
14052 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14053 decc_bug_devnull = 1;
14055 decc_bug_devnull = 0;
14058 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14059 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14061 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14062 if (decc_disable_to_vms_logname_translation < 0)
14063 decc_disable_to_vms_logname_translation = 0;
14066 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14068 decc_efs_case_preserve = decc$feature_get_value(s, 1);
14069 if (decc_efs_case_preserve < 0)
14070 decc_efs_case_preserve = 0;
14073 s = decc$feature_get_index("DECC$EFS_CHARSET");
14074 decc_efs_charset_index = s;
14076 decc_efs_charset = decc$feature_get_value(s, 1);
14077 if (decc_efs_charset < 0)
14078 decc_efs_charset = 0;
14081 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14083 decc_filename_unix_report = decc$feature_get_value(s, 1);
14084 if (decc_filename_unix_report > 0) {
14085 decc_filename_unix_report = 1;
14086 vms_posix_exit = 1;
14089 decc_filename_unix_report = 0;
14092 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14094 decc_filename_unix_only = decc$feature_get_value(s, 1);
14095 if (decc_filename_unix_only > 0) {
14096 decc_filename_unix_only = 1;
14099 decc_filename_unix_only = 0;
14103 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14105 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14106 if (decc_filename_unix_no_version < 0)
14107 decc_filename_unix_no_version = 0;
14110 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14112 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14113 if (decc_readdir_dropdotnotype < 0)
14114 decc_readdir_dropdotnotype = 0;
14117 #if __CRTL_VER >= 80200000
14118 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14120 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14121 if (decc_posix_compliant_pathnames < 0)
14122 decc_posix_compliant_pathnames = 0;
14123 if (decc_posix_compliant_pathnames > 4)
14124 decc_posix_compliant_pathnames = 0;
14129 status = simple_trnlnm
14130 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14131 if ($VMS_STATUS_SUCCESS(status)) {
14132 val_str[0] = _toupper(val_str[0]);
14133 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14134 decc_disable_to_vms_logname_translation = 1;
14139 status = simple_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14140 if ($VMS_STATUS_SUCCESS(status)) {
14141 val_str[0] = _toupper(val_str[0]);
14142 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14143 decc_efs_case_preserve = 1;
14148 status = simple_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14149 if ($VMS_STATUS_SUCCESS(status)) {
14150 val_str[0] = _toupper(val_str[0]);
14151 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14152 decc_filename_unix_report = 1;
14155 status = simple_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14156 if ($VMS_STATUS_SUCCESS(status)) {
14157 val_str[0] = _toupper(val_str[0]);
14158 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14159 decc_filename_unix_only = 1;
14160 decc_filename_unix_report = 1;
14163 status = simple_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14164 if ($VMS_STATUS_SUCCESS(status)) {
14165 val_str[0] = _toupper(val_str[0]);
14166 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14167 decc_filename_unix_no_version = 1;
14170 status = simple_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14171 if ($VMS_STATUS_SUCCESS(status)) {
14172 val_str[0] = _toupper(val_str[0]);
14173 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14174 decc_readdir_dropdotnotype = 1;
14179 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14181 /* Report true case tolerance */
14182 /*----------------------------*/
14183 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14184 if (!$VMS_STATUS_SUCCESS(status))
14185 case_perm = PPROP$K_CASE_BLIND;
14186 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14187 if (!$VMS_STATUS_SUCCESS(status))
14188 case_image = PPROP$K_CASE_BLIND;
14189 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14190 (case_image == PPROP$K_CASE_SENSITIVE))
14191 vms_process_case_tolerant = 0;
14195 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
14196 /* for strict backward compatibility */
14197 status = simple_trnlnm
14198 ("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14199 if ($VMS_STATUS_SUCCESS(status)) {
14200 val_str[0] = _toupper(val_str[0]);
14201 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14202 vms_posix_exit = 1;
14204 vms_posix_exit = 0;
14208 /* Use 32-bit pointers because that's what the image activator
14209 * assumes for the LIB$INITIALZE psect.
14211 #if __INITIAL_POINTER_SIZE
14212 #pragma pointer_size save
14213 #pragma pointer_size 32
14216 /* Create a reference to the LIB$INITIALIZE function. */
14217 extern void LIB$INITIALIZE(void);
14218 extern void (*vmsperl_unused_global_1)(void) = LIB$INITIALIZE;
14220 /* Create an array of pointers to the init functions in the special
14221 * LIB$INITIALIZE section. In our case, the array only has one entry.
14223 #pragma extern_model save
14224 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic,gbl,nowrt,noshr,long
14225 extern void (* const vmsperl_unused_global_2[])() =
14227 vmsperl_set_features,
14229 #pragma extern_model restore
14231 #if __INITIAL_POINTER_SIZE
14232 #pragma pointer_size restore
14239 #endif /* defined(__DECC) || defined(__DECCXX) */