3 * VMS-specific routines for perl5
5 * Copyright (C) 1993-2013 by Charles Bailey and others.
7 * You may distribute under the terms of either the GNU General Public
8 * License or the Artistic License, as specified in the README file.
12 * Yet small as was their hunted band
13 * still fell and fearless was each hand,
14 * and strong deeds they wrought yet oft,
15 * and loved the woods, whose ways more soft
16 * them seemed than thralls of that black throne
17 * to live and languish in halls of stone.
18 * "The Lay of Leithian", Canto II, lines 135-40
20 * [p.162 of _The Lays of Beleriand_]
26 #if __CRTL_VER < 70300000
27 /* needed for home-rolled utime() */
33 #include <climsgdef.h>
43 #include <libclidef.h>
45 #include <lib$routines.h>
48 #if __CRTL_VER >= 70301000 && !defined(__VAX)
58 #include <str$routines.h>
64 #define NO_EFN EFN$C_ENF
66 #if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
67 int decc$feature_get_index(const char *name);
68 char* decc$feature_get_name(int index);
69 int decc$feature_get_value(int index, int mode);
70 int decc$feature_set_value(int index, int mode, int value);
75 #pragma member_alignment save
76 #pragma nomember_alignment longword
81 unsigned short * retadr;
83 #pragma member_alignment restore
85 /* Older versions of ssdef.h don't have these */
86 #ifndef SS$_INVFILFOROP
87 # define SS$_INVFILFOROP 3930
89 #ifndef SS$_NOSUCHOBJECT
90 # define SS$_NOSUCHOBJECT 2696
93 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
94 #define PERLIO_NOT_STDIO 0
96 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
97 * code below needs to get to the underlying CRTL routines. */
98 #define DONT_MASK_RTL_CALLS
102 /* Anticipating future expansion in lexical warnings . . . */
103 #ifndef WARN_INTERNAL
104 # define WARN_INTERNAL WARN_MISC
107 #ifdef VMS_LONGNAME_SUPPORT
108 #include <libfildef.h>
111 #if !defined(__VAX) && __CRTL_VER >= 80200000
119 #define lstat(_x, _y) stat(_x, _y)
122 /* Routine to create a decterm for use with the Perl debugger */
123 /* No headers, this information was found in the Programming Concepts Manual */
125 static int (*decw_term_port)
126 (const struct dsc$descriptor_s * display,
127 const struct dsc$descriptor_s * setup_file,
128 const struct dsc$descriptor_s * customization,
129 struct dsc$descriptor_s * result_device_name,
130 unsigned short * result_device_name_length,
133 void * char_change_buffer) = 0;
135 /* gcc's header files don't #define direct access macros
136 * corresponding to VAXC's variant structs */
138 # define uic$v_format uic$r_uic_form.uic$v_format
139 # define uic$v_group uic$r_uic_form.uic$v_group
140 # define uic$v_member uic$r_uic_form.uic$v_member
141 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
142 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
143 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
144 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
147 #if defined(NEED_AN_H_ERRNO)
151 #if defined(__DECC) || defined(__DECCXX)
152 #pragma member_alignment save
153 #pragma nomember_alignment longword
155 #pragma message disable misalgndmem
158 unsigned short int buflen;
159 unsigned short int itmcode;
161 unsigned short int *retlen;
164 struct filescan_itmlst_2 {
165 unsigned short length;
166 unsigned short itmcode;
171 unsigned short length;
172 char str[VMS_MAXRSS];
173 unsigned short pad; /* for longword struct alignment */
176 #if defined(__DECC) || defined(__DECCXX)
177 #pragma message restore
178 #pragma member_alignment restore
181 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
182 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
183 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
184 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
185 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
186 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
187 #define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c)
188 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
189 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
190 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
191 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
192 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
194 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
195 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
196 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
197 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
199 static char * int_rmsexpand_vms(
200 const char * filespec, char * outbuf, unsigned opts);
201 static char * int_rmsexpand_tovms(
202 const char * filespec, char * outbuf, unsigned opts);
203 static char *int_tovmsspec
204 (const char *path, char *buf, int dir_flag, int * utf8_flag);
205 static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl);
206 static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
207 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl);
209 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
210 #define PERL_LNM_MAX_ALLOWED_INDEX 127
212 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
213 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
216 #define PERL_LNM_MAX_ITER 10
218 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
219 #if __CRTL_VER >= 70302000 && !defined(__VAX)
220 #define MAX_DCL_SYMBOL (8192)
221 #define MAX_DCL_LINE_LENGTH (4096 - 4)
223 #define MAX_DCL_SYMBOL (1024)
224 #define MAX_DCL_LINE_LENGTH (1024 - 4)
227 static char *__mystrtolower(char *str)
229 if (str) for (; *str; ++str) *str= tolower(*str);
233 static struct dsc$descriptor_s fildevdsc =
234 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
235 static struct dsc$descriptor_s crtlenvdsc =
236 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
237 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
238 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
239 static struct dsc$descriptor_s **env_tables = defenv;
240 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
242 /* True if we shouldn't treat barewords as logicals during directory */
244 static int no_translate_barewords;
246 /* DECC Features that may need to affect how Perl interprets
247 * displays filename information
249 static int decc_disable_to_vms_logname_translation = 1;
250 static int decc_disable_posix_root = 1;
251 int decc_efs_case_preserve = 0;
252 static int decc_efs_charset = 0;
253 static int decc_efs_charset_index = -1;
254 static int decc_filename_unix_no_version = 0;
255 static int decc_filename_unix_only = 0;
256 int decc_filename_unix_report = 0;
257 int decc_posix_compliant_pathnames = 0;
258 int decc_readdir_dropdotnotype = 0;
259 static int vms_process_case_tolerant = 1;
260 int vms_vtf7_filenames = 0;
261 int gnv_unix_shell = 0;
262 static int vms_unlink_all_versions = 0;
263 static int vms_posix_exit = 0;
265 /* bug workarounds if needed */
266 int decc_bug_devnull = 1;
267 int vms_bug_stat_filename = 0;
269 static int vms_debug_on_exception = 0;
270 static int vms_debug_fileify = 0;
272 /* Simple logical name translation */
273 static int simple_trnlnm
274 (const char * logname,
278 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
279 const unsigned long attr = LNM$M_CASE_BLIND;
280 struct dsc$descriptor_s name_dsc;
282 unsigned short result;
283 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
286 name_dsc.dsc$w_length = strlen(logname);
287 name_dsc.dsc$a_pointer = (char *)logname;
288 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
289 name_dsc.dsc$b_class = DSC$K_CLASS_S;
291 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
293 if ($VMS_STATUS_SUCCESS(status)) {
295 /* Null terminate and return the string */
296 /*--------------------------------------*/
305 /* Is this a UNIX file specification?
306 * No longer a simple check with EFS file specs
307 * For now, not a full check, but need to
308 * handle POSIX ^UP^ specifications
309 * Fixing to handle ^/ cases would require
310 * changes to many other conversion routines.
313 static int is_unix_filespec(const char *path)
319 if (strncmp(path,"\"^UP^",5) != 0) {
320 pch1 = strchr(path, '/');
325 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
326 if (decc_filename_unix_report || decc_filename_unix_only) {
327 if (strcmp(path,".") == 0)
335 /* This routine converts a UCS-2 character to be VTF-7 encoded.
338 static void ucs2_to_vtf7
340 unsigned long ucs2_char,
343 unsigned char * ucs_ptr;
346 ucs_ptr = (unsigned char *)&ucs2_char;
350 hex = (ucs_ptr[1] >> 4) & 0xf;
352 outspec[2] = hex + '0';
354 outspec[2] = (hex - 9) + 'A';
355 hex = ucs_ptr[1] & 0xF;
357 outspec[3] = hex + '0';
359 outspec[3] = (hex - 9) + 'A';
361 hex = (ucs_ptr[0] >> 4) & 0xf;
363 outspec[4] = hex + '0';
365 outspec[4] = (hex - 9) + 'A';
366 hex = ucs_ptr[1] & 0xF;
368 outspec[5] = hex + '0';
370 outspec[5] = (hex - 9) + 'A';
376 /* This handles the conversion of a UNIX extended character set to a ^
377 * escaped VMS character.
378 * in a UNIX file specification.
380 * The output count variable contains the number of characters added
381 * to the output string.
383 * The return value is the number of characters read from the input string
385 static int copy_expand_unix_filename_escape
386 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
393 utf8_flag = *utf8_fl;
397 if (*inspec >= 0x80) {
398 if (utf8_fl && vms_vtf7_filenames) {
399 unsigned long ucs_char;
403 if ((*inspec & 0xE0) == 0xC0) {
405 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
406 if (ucs_char >= 0x80) {
407 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
410 } else if ((*inspec & 0xF0) == 0xE0) {
412 ucs_char = ((inspec[0] & 0xF) << 12) +
413 ((inspec[1] & 0x3f) << 6) +
415 if (ucs_char >= 0x800) {
416 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
420 #if 0 /* I do not see longer sequences supported by OpenVMS */
421 /* Maybe some one can fix this later */
422 } else if ((*inspec & 0xF8) == 0xF0) {
425 } else if ((*inspec & 0xFC) == 0xF8) {
428 } else if ((*inspec & 0xFE) == 0xFC) {
435 /* High bit set, but not a Unicode character! */
437 /* Non printing DECMCS or ISO Latin-1 character? */
438 if ((unsigned char)*inspec <= 0x9F) {
442 hex = (*inspec >> 4) & 0xF;
444 outspec[1] = hex + '0';
446 outspec[1] = (hex - 9) + 'A';
450 outspec[2] = hex + '0';
452 outspec[2] = (hex - 9) + 'A';
456 } else if ((unsigned char)*inspec == 0xA0) {
462 } else if ((unsigned char)*inspec == 0xFF) {
474 /* Is this a macro that needs to be passed through?
475 * Macros start with $( and an alpha character, followed
476 * by a string of alpha numeric characters ending with a )
477 * If this does not match, then encode it as ODS-5.
479 if ((inspec[0] == '$') && (inspec[1] == '(')) {
482 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
484 outspec[0] = inspec[0];
485 outspec[1] = inspec[1];
486 outspec[2] = inspec[2];
488 while(isalnum(inspec[tcnt]) ||
489 (inspec[2] == '.') || (inspec[2] == '_')) {
490 outspec[tcnt] = inspec[tcnt];
493 if (inspec[tcnt] == ')') {
494 outspec[tcnt] = inspec[tcnt];
511 if (decc_efs_charset == 0)
538 /* Don't escape again if following character is
539 * already something we escape.
541 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
547 /* But otherwise fall through and escape it. */
549 /* Assume that this is to be escaped */
551 outspec[1] = *inspec;
555 case ' ': /* space */
556 /* Assume that this is to be escaped */
572 /* This handles the expansion of a '^' prefix to the proper character
573 * in a UNIX file specification.
575 * The output count variable contains the number of characters added
576 * to the output string.
578 * The return value is the number of characters read from the input
581 static int copy_expand_vms_filename_escape
582 (char *outspec, const char *inspec, int *output_cnt)
589 if (*inspec == '^') {
592 /* Spaces and non-trailing dots should just be passed through,
593 * but eat the escape character.
600 case '_': /* space */
606 /* Hmm. Better leave the escape escaped. */
612 case 'U': /* Unicode - FIX-ME this is wrong. */
615 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
618 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
619 outspec[0] = c1 & 0xff;
620 outspec[1] = c2 & 0xff;
627 /* Error - do best we can to continue */
637 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
641 scnt = sscanf(inspec, "%2x", &c1);
642 outspec[0] = c1 & 0xff;
663 /* vms_split_path - Verify that the input file specification is a
664 * VMS format file specification, and provide pointers to the components of
665 * it. With EFS format filenames, this is virtually the only way to
666 * parse a VMS path specification into components.
668 * If the sum of the components do not add up to the length of the
669 * string, then the passed file specification is probably a UNIX style
672 static int vms_split_path
687 struct dsc$descriptor path_desc;
691 struct filescan_itmlst_2 item_list[9];
692 const int filespec = 0;
693 const int nodespec = 1;
694 const int devspec = 2;
695 const int rootspec = 3;
696 const int dirspec = 4;
697 const int namespec = 5;
698 const int typespec = 6;
699 const int verspec = 7;
701 /* Assume the worst for an easy exit */
715 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
716 path_desc.dsc$w_length = strlen(path);
717 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
718 path_desc.dsc$b_class = DSC$K_CLASS_S;
720 /* Get the total length, if it is shorter than the string passed
721 * then this was probably not a VMS formatted file specification
723 item_list[filespec].itmcode = FSCN$_FILESPEC;
724 item_list[filespec].length = 0;
725 item_list[filespec].component = NULL;
727 /* If the node is present, then it gets considered as part of the
728 * volume name to hopefully make things simple.
730 item_list[nodespec].itmcode = FSCN$_NODE;
731 item_list[nodespec].length = 0;
732 item_list[nodespec].component = NULL;
734 item_list[devspec].itmcode = FSCN$_DEVICE;
735 item_list[devspec].length = 0;
736 item_list[devspec].component = NULL;
738 /* root is a special case, adding it to either the directory or
739 * the device components will probably complicate things for the
740 * callers of this routine, so leave it separate.
742 item_list[rootspec].itmcode = FSCN$_ROOT;
743 item_list[rootspec].length = 0;
744 item_list[rootspec].component = NULL;
746 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
747 item_list[dirspec].length = 0;
748 item_list[dirspec].component = NULL;
750 item_list[namespec].itmcode = FSCN$_NAME;
751 item_list[namespec].length = 0;
752 item_list[namespec].component = NULL;
754 item_list[typespec].itmcode = FSCN$_TYPE;
755 item_list[typespec].length = 0;
756 item_list[typespec].component = NULL;
758 item_list[verspec].itmcode = FSCN$_VERSION;
759 item_list[verspec].length = 0;
760 item_list[verspec].component = NULL;
762 item_list[8].itmcode = 0;
763 item_list[8].length = 0;
764 item_list[8].component = NULL;
766 status = sys$filescan
767 ((const struct dsc$descriptor_s *)&path_desc, item_list,
769 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
771 /* If we parsed it successfully these two lengths should be the same */
772 if (path_desc.dsc$w_length != item_list[filespec].length)
775 /* If we got here, then it is a VMS file specification */
778 /* set the volume name */
779 if (item_list[nodespec].length > 0) {
780 *volume = item_list[nodespec].component;
781 *vol_len = item_list[nodespec].length + item_list[devspec].length;
784 *volume = item_list[devspec].component;
785 *vol_len = item_list[devspec].length;
788 *root = item_list[rootspec].component;
789 *root_len = item_list[rootspec].length;
791 *dir = item_list[dirspec].component;
792 *dir_len = item_list[dirspec].length;
794 /* Now fun with versions and EFS file specifications
795 * The parser can not tell the difference when a "." is a version
796 * delimiter or a part of the file specification.
798 if ((decc_efs_charset) &&
799 (item_list[verspec].length > 0) &&
800 (item_list[verspec].component[0] == '.')) {
801 *name = item_list[namespec].component;
802 *name_len = item_list[namespec].length + item_list[typespec].length;
803 *ext = item_list[verspec].component;
804 *ext_len = item_list[verspec].length;
809 *name = item_list[namespec].component;
810 *name_len = item_list[namespec].length;
811 *ext = item_list[typespec].component;
812 *ext_len = item_list[typespec].length;
813 *version = item_list[verspec].component;
814 *ver_len = item_list[verspec].length;
819 /* Routine to determine if the file specification ends with .dir */
820 static int is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len) {
822 /* e_len must be 4, and version must be <= 2 characters */
823 if (e_len != 4 || vs_len > 2)
826 /* If a version number is present, it needs to be one */
827 if ((vs_len == 2) && (vs_spec[1] != '1'))
830 /* Look for the DIR on the extension */
831 if (vms_process_case_tolerant) {
832 if ((toupper(e_spec[1]) == 'D') &&
833 (toupper(e_spec[2]) == 'I') &&
834 (toupper(e_spec[3]) == 'R')) {
838 /* Directory extensions are supposed to be in upper case only */
839 /* I would not be surprised if this rule can not be enforced */
840 /* if and when someone fully debugs the case sensitive mode */
841 if ((e_spec[1] == 'D') &&
842 (e_spec[2] == 'I') &&
843 (e_spec[3] == 'R')) {
852 * Routine to retrieve the maximum equivalence index for an input
853 * logical name. Some calls to this routine have no knowledge if
854 * the variable is a logical or not. So on error we return a max
857 /*{{{int my_maxidx(const char *lnm) */
859 my_maxidx(const char *lnm)
863 int attr = LNM$M_CASE_BLIND;
864 struct dsc$descriptor lnmdsc;
865 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
868 lnmdsc.dsc$w_length = strlen(lnm);
869 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
870 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
871 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
873 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
874 if ((status & 1) == 0)
881 /* Routine to remove the 2-byte prefix from the translation of a
882 * process-permanent file (PPF).
884 static inline unsigned short int
885 S_remove_ppf_prefix(const char *lnm, char *eqv, unsigned short int eqvlen)
887 if (*((int *)lnm) == *((int *)"SYS$") &&
888 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
889 ( (lnm[4] == 'O' && !strcmp(lnm,"SYS$OUTPUT")) ||
890 (lnm[4] == 'I' && !strcmp(lnm,"SYS$INPUT")) ||
891 (lnm[4] == 'E' && !strcmp(lnm,"SYS$ERROR")) ||
892 (lnm[4] == 'C' && !strcmp(lnm,"SYS$COMMAND")) ) ) {
894 memmove(eqv, eqv+4, eqvlen-4);
900 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
902 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
903 struct dsc$descriptor_s **tabvec, unsigned long int flags)
906 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
907 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
908 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
910 unsigned char acmode;
911 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
912 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
913 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
914 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
916 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
917 #if defined(PERL_IMPLICIT_CONTEXT)
920 aTHX = PERL_GET_INTERP;
926 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
927 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
929 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
930 *cp2 = _toupper(*cp1);
931 if (cp1 - lnm > LNM$C_NAMLENGTH) {
932 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
936 lnmdsc.dsc$w_length = cp1 - lnm;
937 lnmdsc.dsc$a_pointer = uplnm;
938 uplnm[lnmdsc.dsc$w_length] = '\0';
939 secure = flags & PERL__TRNENV_SECURE;
940 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
941 if (!tabvec || !*tabvec) tabvec = env_tables;
943 for (curtab = 0; tabvec[curtab]; curtab++) {
944 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
945 if (!ivenv && !secure) {
950 #if defined(PERL_IMPLICIT_CONTEXT)
953 "Can't read CRTL environ\n");
956 Perl_warn(aTHX_ "Can't read CRTL environ\n");
959 retsts = SS$_NOLOGNAM;
960 for (i = 0; environ[i]; i++) {
961 if ((eq = strchr(environ[i],'=')) &&
962 lnmdsc.dsc$w_length == (eq - environ[i]) &&
963 !strncmp(environ[i],uplnm,eq - environ[i])) {
965 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
966 if (!eqvlen) continue;
971 if (retsts != SS$_NOLOGNAM) break;
974 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
975 !str$case_blind_compare(&tmpdsc,&clisym)) {
976 if (!ivsym && !secure) {
977 unsigned short int deflen = LNM$C_NAMLENGTH;
978 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
979 /* dynamic dsc to accommodate possible long value */
980 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
981 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
983 if (eqvlen > MAX_DCL_SYMBOL) {
984 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
985 eqvlen = MAX_DCL_SYMBOL;
986 /* Special hack--we might be called before the interpreter's */
987 /* fully initialized, in which case either thr or PL_curcop */
988 /* might be bogus. We have to check, since ckWARN needs them */
989 /* both to be valid if running threaded */
990 #if defined(PERL_IMPLICIT_CONTEXT)
993 "Value of CLI symbol \"%s\" too long",lnm);
996 if (ckWARN(WARN_MISC)) {
997 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
1000 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
1002 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
1003 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1004 if (retsts == LIB$_NOSUCHSYM) continue;
1009 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
1010 midx = my_maxidx(lnm);
1011 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1012 lnmlst[1].bufadr = cp2;
1014 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1015 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1016 if (retsts == SS$_NOLOGNAM) break;
1017 eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen);
1021 if ((retsts == SS$_IVLOGNAM) ||
1022 (retsts == SS$_NOLOGNAM)) { continue; }
1023 eqvlen = strlen(eqv);
1026 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1027 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1028 if (retsts == SS$_NOLOGNAM) continue;
1029 eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen);
1035 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1036 else if (retsts == LIB$_NOSUCHSYM ||
1037 retsts == SS$_NOLOGNAM) {
1038 /* Unsuccessful lookup is normal -- no need to set errno */
1041 else if (retsts == LIB$_INVSYMNAM ||
1042 retsts == SS$_IVLOGNAM ||
1043 retsts == SS$_IVLOGTAB) {
1044 set_errno(EINVAL); set_vaxc_errno(retsts);
1046 else _ckvmssts_noperl(retsts);
1048 } /* end of vmstrnenv */
1051 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1052 /* Define as a function so we can access statics. */
1053 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1057 #if defined(PERL_IMPLICIT_CONTEXT)
1060 #ifdef SECURE_INTERNAL_GETENV
1061 flags = (PL_curinterp ? TAINTING_get : will_taint) ?
1062 PERL__TRNENV_SECURE : 0;
1065 return vmstrnenv(lnm, eqv, idx, fildev, flags);
1070 * Note: Uses Perl temp to store result so char * can be returned to
1071 * caller; this pointer will be invalidated at next Perl statement
1073 * We define this as a function rather than a macro in terms of my_getenv_len()
1074 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1077 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1079 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1082 static char *__my_getenv_eqv = NULL;
1083 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1084 unsigned long int idx = 0;
1085 int success, secure;
1089 midx = my_maxidx(lnm) + 1;
1091 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1092 /* Set up a temporary buffer for the return value; Perl will
1093 * clean it up at the next statement transition */
1094 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1095 if (!tmpsv) return NULL;
1099 /* Assume no interpreter ==> single thread */
1100 if (__my_getenv_eqv != NULL) {
1101 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1104 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1106 eqv = __my_getenv_eqv;
1109 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1110 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1112 getcwd(eqv,LNM$C_NAMLENGTH);
1116 /* Get rid of "000000/ in rooted filespecs */
1119 zeros = strstr(eqv, "/000000/");
1120 if (zeros != NULL) {
1122 mlen = len - (zeros - eqv) - 7;
1123 memmove(zeros, &zeros[7], mlen);
1131 /* Impose security constraints only if tainting */
1133 /* Impose security constraints only if tainting */
1134 secure = PL_curinterp ? TAINTING_get : will_taint;
1141 #ifdef SECURE_INTERNAL_GETENV
1142 secure ? PERL__TRNENV_SECURE : 0
1148 /* For the getenv interface we combine all the equivalence names
1149 * of a search list logical into one value to acquire a maximum
1150 * value length of 255*128 (assuming %ENV is using logicals).
1152 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1154 /* If the name contains a semicolon-delimited index, parse it
1155 * off and make sure we only retrieve the equivalence name for
1157 if ((cp2 = strchr(lnm,';')) != NULL) {
1158 my_strlcpy(uplnm, lnm, cp2 - lnm + 1);
1159 idx = strtoul(cp2+1,NULL,0);
1161 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1164 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1166 return success ? eqv : NULL;
1169 } /* end of my_getenv() */
1173 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1175 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1179 unsigned long idx = 0;
1181 static char *__my_getenv_len_eqv = NULL;
1185 midx = my_maxidx(lnm) + 1;
1187 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1188 /* Set up a temporary buffer for the return value; Perl will
1189 * clean it up at the next statement transition */
1190 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1191 if (!tmpsv) return NULL;
1195 /* Assume no interpreter ==> single thread */
1196 if (__my_getenv_len_eqv != NULL) {
1197 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1200 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1202 buf = __my_getenv_len_eqv;
1205 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1206 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1209 getcwd(buf,LNM$C_NAMLENGTH);
1212 /* Get rid of "000000/ in rooted filespecs */
1214 zeros = strstr(buf, "/000000/");
1215 if (zeros != NULL) {
1217 mlen = *len - (zeros - buf) - 7;
1218 memmove(zeros, &zeros[7], mlen);
1227 /* Impose security constraints only if tainting */
1228 secure = PL_curinterp ? TAINTING_get : will_taint;
1235 #ifdef SECURE_INTERNAL_GETENV
1236 secure ? PERL__TRNENV_SECURE : 0
1242 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1244 if ((cp2 = strchr(lnm,';')) != NULL) {
1245 my_strlcpy(buf, lnm, cp2 - lnm + 1);
1246 idx = strtoul(cp2+1,NULL,0);
1248 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1251 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1253 /* Get rid of "000000/ in rooted filespecs */
1256 zeros = strstr(buf, "/000000/");
1257 if (zeros != NULL) {
1259 mlen = *len - (zeros - buf) - 7;
1260 memmove(zeros, &zeros[7], mlen);
1266 return *len ? buf : NULL;
1269 } /* end of my_getenv_len() */
1272 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1274 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1276 /*{{{ void prime_env_iter() */
1278 prime_env_iter(void)
1279 /* Fill the %ENV associative array with all logical names we can
1280 * find, in preparation for iterating over it.
1283 static int primed = 0;
1284 HV *seenhv = NULL, *envhv;
1286 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1287 unsigned short int chan;
1288 #ifndef CLI$M_TRUSTED
1289 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1291 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1292 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0;
1294 bool have_sym = FALSE, have_lnm = FALSE;
1295 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1296 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1297 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1298 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1299 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1300 #if defined(PERL_IMPLICIT_CONTEXT)
1303 #if defined(USE_ITHREADS)
1304 static perl_mutex primenv_mutex;
1305 MUTEX_INIT(&primenv_mutex);
1308 #if defined(PERL_IMPLICIT_CONTEXT)
1309 /* We jump through these hoops because we can be called at */
1310 /* platform-specific initialization time, which is before anything is */
1311 /* set up--we can't even do a plain dTHX since that relies on the */
1312 /* interpreter structure to be initialized */
1314 aTHX = PERL_GET_INTERP;
1316 /* we never get here because the NULL pointer will cause the */
1317 /* several of the routines called by this routine to access violate */
1319 /* This routine is only called by hv.c/hv_iterinit which has a */
1320 /* context, so the real fix may be to pass it through instead of */
1321 /* the hoops above */
1326 if (primed || !PL_envgv) return;
1327 MUTEX_LOCK(&primenv_mutex);
1328 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1329 envhv = GvHVn(PL_envgv);
1330 /* Perform a dummy fetch as an lval to insure that the hash table is
1331 * set up. Otherwise, the hv_store() will turn into a nullop. */
1332 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1334 for (i = 0; env_tables[i]; i++) {
1335 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1336 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1337 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1339 if (have_sym || have_lnm) {
1340 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1341 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1342 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1343 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1346 for (i--; i >= 0; i--) {
1347 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1350 for (j = 0; environ[j]; j++) {
1351 if (!(start = strchr(environ[j],'='))) {
1352 if (ckWARN(WARN_INTERNAL))
1353 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1357 sv = newSVpv(start,0);
1359 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1364 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1365 !str$case_blind_compare(&tmpdsc,&clisym)) {
1366 my_strlcpy(cmd, "Show Symbol/Global *", sizeof(cmd));
1367 cmddsc.dsc$w_length = 20;
1368 if (env_tables[i]->dsc$w_length == 12 &&
1369 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1370 !str$case_blind_compare(&tmpdsc,&local)) my_strlcpy(cmd+12, "Local *", sizeof(cmd)-12);
1371 flags = defflags | CLI$M_NOLOGNAM;
1374 my_strlcpy(cmd, "Show Logical *", sizeof(cmd));
1375 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1376 my_strlcat(cmd," /Table=", sizeof(cmd));
1377 cmddsc.dsc$w_length = my_strlcat(cmd, env_tables[i]->dsc$a_pointer, sizeof(cmd));
1379 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1380 flags = defflags | CLI$M_NOCLISYM;
1383 /* Create a new subprocess to execute each command, to exclude the
1384 * remote possibility that someone could subvert a mbx or file used
1385 * to write multiple commands to a single subprocess.
1388 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1389 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1390 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1391 defflags &= ~CLI$M_TRUSTED;
1392 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1394 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1395 if (seenhv) SvREFCNT_dec(seenhv);
1398 char *cp1, *cp2, *key;
1399 unsigned long int sts, iosb[2], retlen, keylen;
1402 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1403 if (sts & 1) sts = iosb[0] & 0xffff;
1404 if (sts == SS$_ENDOFFILE) {
1406 while (substs == 0) { sys$hiber(); wakect++;}
1407 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1412 retlen = iosb[0] >> 16;
1413 if (!retlen) continue; /* blank line */
1415 if (iosb[1] != subpid) {
1417 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1421 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1422 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1424 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1425 if (*cp1 == '(' || /* Logical name table name */
1426 *cp1 == '=' /* Next eqv of searchlist */) continue;
1427 if (*cp1 == '"') cp1++;
1428 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1429 key = cp1; keylen = cp2 - cp1;
1430 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1431 while (*cp2 && *cp2 != '=') cp2++;
1432 while (*cp2 && *cp2 == '=') cp2++;
1433 while (*cp2 && *cp2 == ' ') cp2++;
1434 if (*cp2 == '"') { /* String translation; may embed "" */
1435 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1436 cp2++; cp1--; /* Skip "" surrounding translation */
1438 else { /* Numeric translation */
1439 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1440 cp1--; /* stop on last non-space char */
1442 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1443 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1446 PERL_HASH(hash,key,keylen);
1448 if (cp1 == cp2 && *cp2 == '.') {
1449 /* A single dot usually means an unprintable character, such as a null
1450 * to indicate a zero-length value. Get the actual value to make sure.
1452 char lnm[LNM$C_NAMLENGTH+1];
1453 char eqv[MAX_DCL_SYMBOL+1];
1455 strncpy(lnm, key, keylen);
1456 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1457 sv = newSVpvn(eqv, strlen(eqv));
1460 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1464 hv_store(envhv,key,keylen,sv,hash);
1465 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1467 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1468 /* get the PPFs for this process, not the subprocess */
1469 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1470 char eqv[LNM$C_NAMLENGTH+1];
1472 for (i = 0; ppfs[i]; i++) {
1473 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1474 sv = newSVpv(eqv,trnlen);
1476 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1481 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1482 if (buf) Safefree(buf);
1483 if (seenhv) SvREFCNT_dec(seenhv);
1484 MUTEX_UNLOCK(&primenv_mutex);
1487 } /* end of prime_env_iter */
1491 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1492 /* Define or delete an element in the same "environment" as
1493 * vmstrnenv(). If an element is to be deleted, it's removed from
1494 * the first place it's found. If it's to be set, it's set in the
1495 * place designated by the first element of the table vector.
1496 * Like setenv() returns 0 for success, non-zero on error.
1499 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1502 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1503 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1505 unsigned long int retsts, usermode = PSL$C_USER;
1506 struct itmlst_3 *ile, *ilist;
1507 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1508 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1509 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1510 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1511 $DESCRIPTOR(local,"_LOCAL");
1514 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1515 return SS$_IVLOGNAM;
1518 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1519 *cp2 = _toupper(*cp1);
1520 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1521 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1522 return SS$_IVLOGNAM;
1525 lnmdsc.dsc$w_length = cp1 - lnm;
1526 if (!tabvec || !*tabvec) tabvec = env_tables;
1528 if (!eqv) { /* we're deleting n element */
1529 for (curtab = 0; tabvec[curtab]; curtab++) {
1530 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1532 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1533 if ((cp1 = strchr(environ[i],'=')) &&
1534 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1535 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1537 return setenv(lnm,"",1) ? vaxc$errno : 0;
1540 ivenv = 1; retsts = SS$_NOLOGNAM;
1542 if (ckWARN(WARN_INTERNAL))
1543 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1544 ivenv = 1; retsts = SS$_NOSUCHPGM;
1550 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1551 !str$case_blind_compare(&tmpdsc,&clisym)) {
1552 unsigned int symtype;
1553 if (tabvec[curtab]->dsc$w_length == 12 &&
1554 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1555 !str$case_blind_compare(&tmpdsc,&local))
1556 symtype = LIB$K_CLI_LOCAL_SYM;
1557 else symtype = LIB$K_CLI_GLOBAL_SYM;
1558 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1559 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1560 if (retsts == LIB$_NOSUCHSYM) continue;
1564 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1565 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1566 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1567 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1568 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1572 else { /* we're defining a value */
1573 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1575 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1577 if (ckWARN(WARN_INTERNAL))
1578 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1579 retsts = SS$_NOSUCHPGM;
1583 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1584 eqvdsc.dsc$w_length = strlen(eqv);
1585 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1586 !str$case_blind_compare(&tmpdsc,&clisym)) {
1587 unsigned int symtype;
1588 if (tabvec[0]->dsc$w_length == 12 &&
1589 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1590 !str$case_blind_compare(&tmpdsc,&local))
1591 symtype = LIB$K_CLI_LOCAL_SYM;
1592 else symtype = LIB$K_CLI_GLOBAL_SYM;
1593 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1596 if (!*eqv) eqvdsc.dsc$w_length = 1;
1597 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1599 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1600 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1601 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1602 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1603 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1604 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1607 Newx(ilist,nseg+1,struct itmlst_3);
1610 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1613 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1615 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1616 ile->itmcode = LNM$_STRING;
1618 if ((j+1) == nseg) {
1619 ile->buflen = strlen(c);
1620 /* in case we are truncating one that's too long */
1621 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1624 ile->buflen = LNM$C_NAMLENGTH;
1628 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1632 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1637 if (!(retsts & 1)) {
1639 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1640 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1641 set_errno(EVMSERR); break;
1642 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1643 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1644 set_errno(EINVAL); break;
1646 set_errno(EACCES); break;
1651 set_vaxc_errno(retsts);
1652 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1655 /* We reset error values on success because Perl does an hv_fetch()
1656 * before each hv_store(), and if the thing we're setting didn't
1657 * previously exist, we've got a leftover error message. (Of course,
1658 * this fails in the face of
1659 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1660 * in that the error reported in $! isn't spurious,
1661 * but it's right more often than not.)
1663 set_errno(0); set_vaxc_errno(retsts);
1667 } /* end of vmssetenv() */
1670 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1671 /* This has to be a function since there's a prototype for it in proto.h */
1673 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1676 int len = strlen(lnm);
1680 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1681 if (!strcmp(uplnm,"DEFAULT")) {
1682 if (eqv && *eqv) my_chdir(eqv);
1687 (void) vmssetenv(lnm,eqv,NULL);
1691 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1693 * sets a user-mode logical in the process logical name table
1694 * used for redirection of sys$error
1697 Perl_vmssetuserlnm(const char *name, const char *eqv)
1699 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1700 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1701 unsigned long int iss, attr = LNM$M_CONFINE;
1702 unsigned char acmode = PSL$C_USER;
1703 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1705 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1706 d_name.dsc$w_length = strlen(name);
1708 lnmlst[0].buflen = strlen(eqv);
1709 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1711 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1712 if (!(iss&1)) lib$signal(iss);
1717 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1718 /* my_crypt - VMS password hashing
1719 * my_crypt() provides an interface compatible with the Unix crypt()
1720 * C library function, and uses sys$hash_password() to perform VMS
1721 * password hashing. The quadword hashed password value is returned
1722 * as a NUL-terminated 8 character string. my_crypt() does not change
1723 * the case of its string arguments; in order to match the behavior
1724 * of LOGINOUT et al., alphabetic characters in both arguments must
1725 * be upcased by the caller.
1727 * - fix me to call ACM services when available
1730 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1732 # ifndef UAI$C_PREFERRED_ALGORITHM
1733 # define UAI$C_PREFERRED_ALGORITHM 127
1735 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1736 unsigned short int salt = 0;
1737 unsigned long int sts;
1739 unsigned short int dsc$w_length;
1740 unsigned char dsc$b_type;
1741 unsigned char dsc$b_class;
1742 const char * dsc$a_pointer;
1743 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1744 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1745 struct itmlst_3 uailst[3] = {
1746 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1747 { sizeof salt, UAI$_SALT, &salt, 0},
1748 { 0, 0, NULL, NULL}};
1749 static char hash[9];
1751 usrdsc.dsc$w_length = strlen(usrname);
1752 usrdsc.dsc$a_pointer = usrname;
1753 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1755 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1759 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1764 set_vaxc_errno(sts);
1765 if (sts != RMS$_RNF) return NULL;
1768 txtdsc.dsc$w_length = strlen(textpasswd);
1769 txtdsc.dsc$a_pointer = textpasswd;
1770 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1771 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1774 return (char *) hash;
1776 } /* end of my_crypt() */
1780 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1781 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1782 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1784 /* fixup barenames that are directories for internal use.
1785 * There have been problems with the consistent handling of UNIX
1786 * style directory names when routines are presented with a name that
1787 * has no directory delimiters at all. So this routine will eventually
1790 static char * fixup_bare_dirnames(const char * name)
1792 if (decc_disable_to_vms_logname_translation) {
1798 /* 8.3, remove() is now broken on symbolic links */
1799 static int rms_erase(const char * vmsname);
1803 * A little hack to get around a bug in some implementation of remove()
1804 * that do not know how to delete a directory
1806 * Delete any file to which user has control access, regardless of whether
1807 * delete access is explicitly allowed.
1808 * Limitations: User must have write access to parent directory.
1809 * Does not block signals or ASTs; if interrupted in midstream
1810 * may leave file with an altered ACL.
1813 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1815 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1819 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1820 unsigned long int cxt = 0, aclsts, fndsts;
1822 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1824 unsigned char myace$b_length;
1825 unsigned char myace$b_type;
1826 unsigned short int myace$w_flags;
1827 unsigned long int myace$l_access;
1828 unsigned long int myace$l_ident;
1829 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1830 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1831 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1833 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1834 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1835 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1836 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1837 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1838 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1840 /* Expand the input spec using RMS, since the CRTL remove() and
1841 * system services won't do this by themselves, so we may miss
1842 * a file "hiding" behind a logical name or search list. */
1843 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
1844 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1846 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
1848 PerlMem_free(vmsname);
1852 /* Erase the file */
1853 rmsts = rms_erase(vmsname);
1855 /* Did it succeed */
1856 if ($VMS_STATUS_SUCCESS(rmsts)) {
1857 PerlMem_free(vmsname);
1861 /* If not, can changing protections help? */
1862 if (rmsts != RMS$_PRV) {
1863 set_vaxc_errno(rmsts);
1864 PerlMem_free(vmsname);
1868 /* No, so we get our own UIC to use as a rights identifier,
1869 * and the insert an ACE at the head of the ACL which allows us
1870 * to delete the file.
1872 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1873 fildsc.dsc$w_length = strlen(vmsname);
1874 fildsc.dsc$a_pointer = vmsname;
1876 newace.myace$l_ident = oldace.myace$l_ident;
1878 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1880 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1881 set_errno(ENOENT); break;
1883 set_errno(ENOTDIR); break;
1885 set_errno(ENODEV); break;
1886 case RMS$_SYN: case SS$_INVFILFOROP:
1887 set_errno(EINVAL); break;
1889 set_errno(EACCES); break;
1891 _ckvmssts_noperl(aclsts);
1893 set_vaxc_errno(aclsts);
1894 PerlMem_free(vmsname);
1897 /* Grab any existing ACEs with this identifier in case we fail */
1898 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1899 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1900 || fndsts == SS$_NOMOREACE ) {
1901 /* Add the new ACE . . . */
1902 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1905 rmsts = rms_erase(vmsname);
1906 if ($VMS_STATUS_SUCCESS(rmsts)) {
1911 /* We blew it - dir with files in it, no write priv for
1912 * parent directory, etc. Put things back the way they were. */
1913 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1916 addlst[0].bufadr = &oldace;
1917 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1924 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1925 /* We just deleted it, so of course it's not there. Some versions of
1926 * VMS seem to return success on the unlock operation anyhow (after all
1927 * the unlock is successful), but others don't.
1929 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1930 if (aclsts & 1) aclsts = fndsts;
1931 if (!(aclsts & 1)) {
1933 set_vaxc_errno(aclsts);
1936 PerlMem_free(vmsname);
1939 } /* end of kill_file() */
1943 /*{{{int do_rmdir(char *name)*/
1945 Perl_do_rmdir(pTHX_ const char *name)
1951 /* lstat returns a VMS fileified specification of the name */
1952 /* that is looked up, and also lets verifies that this is a directory */
1954 retval = flex_lstat(name, &st);
1958 /* Due to a historical feature, flex_stat/lstat can not see some */
1959 /* Unix format file names that the rest of the CRTL can see */
1960 /* Fixing that feature will cause some perl tests to fail */
1961 /* So try this one more time. */
1963 retval = lstat(name, &st.crtl_stat);
1967 /* force it to a file spec for the kill file to work. */
1968 ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
1969 if (ret_spec == NULL) {
1975 if (!S_ISDIR(st.st_mode)) {
1980 dirfile = st.st_devnam;
1982 /* It may be possible for flex_stat to find a file and vmsify() to */
1983 /* fail with ODS-2 specifications. mp_do_kill_file can not deal */
1984 /* with that case, so fail it */
1985 if (dirfile[0] == 0) {
1990 retval = mp_do_kill_file(aTHX_ dirfile, 1);
1995 } /* end of do_rmdir */
1999 * Delete any file to which user has control access, regardless of whether
2000 * delete access is explicitly allowed.
2001 * Limitations: User must have write access to parent directory.
2002 * Does not block signals or ASTs; if interrupted in midstream
2003 * may leave file with an altered ACL.
2006 /*{{{int kill_file(char *name)*/
2008 Perl_kill_file(pTHX_ const char *name)
2014 /* Convert the filename to VMS format and see if it is a directory */
2015 /* flex_lstat returns a vmsified file specification */
2016 rmsts = flex_lstat(name, &st);
2019 /* Due to a historical feature, flex_stat/lstat can not see some */
2020 /* Unix format file names that the rest of the CRTL can see when */
2021 /* ODS-2 file specifications are in use. */
2022 /* Fixing that feature will cause some perl tests to fail */
2023 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2025 vmsfile = (char *) name; /* cast ok */
2028 vmsfile = st.st_devnam;
2029 if (vmsfile[0] == 0) {
2030 /* It may be possible for flex_stat to find a file and vmsify() */
2031 /* to fail with ODS-2 specifications. mp_do_kill_file can not */
2032 /* deal with that case, so fail it */
2038 /* Remove() is allowed to delete directories, according to the X/Open
2040 * This may need special handling to work with the ACL hacks.
2042 if (S_ISDIR(st.st_mode)) {
2043 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2047 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2049 /* Need to delete all versions ? */
2050 if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2053 /* Just use lstat() here as do not need st_dev */
2054 /* and we know that the file is in VMS format or that */
2055 /* because of a historical bug, flex_stat can not see the file */
2056 while (lstat(vmsfile, (stat_t *)&st) == 0) {
2057 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2062 /* Make sure that we do not loop forever */
2073 } /* end of kill_file() */
2077 /*{{{int my_mkdir(char *,Mode_t)*/
2079 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2081 STRLEN dirlen = strlen(dir);
2083 /* zero length string sometimes gives ACCVIO */
2084 if (dirlen == 0) return -1;
2086 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2087 * null file name/type. However, it's commonplace under Unix,
2088 * so we'll allow it for a gain in portability.
2090 if (dir[dirlen-1] == '/') {
2091 char *newdir = savepvn(dir,dirlen-1);
2092 int ret = mkdir(newdir,mode);
2096 else return mkdir(dir,mode);
2097 } /* end of my_mkdir */
2100 /*{{{int my_chdir(char *)*/
2102 Perl_my_chdir(pTHX_ const char *dir)
2104 STRLEN dirlen = strlen(dir);
2105 const char *dir1 = dir;
2107 /* zero length string sometimes gives ACCVIO */
2109 SETERRNO(EINVAL, SS$_BADPARAM);
2113 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2114 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2115 * so that existing scripts do not need to be changed.
2117 while ((dirlen > 0) && (*dir1 == ' ')) {
2122 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2124 * null file name/type. However, it's commonplace under Unix,
2125 * so we'll allow it for a gain in portability.
2127 * '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
2129 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2132 newdir = (char *)PerlMem_malloc(dirlen);
2134 _ckvmssts_noperl(SS$_INSFMEM);
2135 memcpy(newdir, dir1, dirlen-1);
2136 newdir[dirlen-1] = '\0';
2137 ret = chdir(newdir);
2138 PerlMem_free(newdir);
2141 else return chdir(dir1);
2142 } /* end of my_chdir */
2146 /*{{{int my_chmod(char *, mode_t)*/
2148 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2153 STRLEN speclen = strlen(file_spec);
2155 /* zero length string sometimes gives ACCVIO */
2156 if (speclen == 0) return -1;
2158 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2159 * that implies null file name/type. However, it's commonplace under Unix,
2160 * so we'll allow it for a gain in portability.
2162 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2163 * in VMS file.dir notation.
2165 changefile = (char *) file_spec; /* cast ok */
2166 ret = flex_lstat(file_spec, &st);
2169 /* Due to a historical feature, flex_stat/lstat can not see some */
2170 /* Unix format file names that the rest of the CRTL can see when */
2171 /* ODS-2 file specifications are in use. */
2172 /* Fixing that feature will cause some perl tests to fail */
2173 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2177 /* It may be possible to get here with nothing in st_devname */
2178 /* chmod still may work though */
2179 if (st.st_devnam[0] != 0) {
2180 changefile = st.st_devnam;
2183 ret = chmod(changefile, mode);
2185 } /* end of my_chmod */
2189 /*{{{FILE *my_tmpfile()*/
2196 if ((fp = tmpfile())) return fp;
2198 cp = (char *)PerlMem_malloc(L_tmpnam+24);
2199 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2201 if (decc_filename_unix_only == 0)
2202 strcpy(cp,"Sys$Scratch:");
2205 tmpnam(cp+strlen(cp));
2206 strcat(cp,".Perltmp");
2207 fp = fopen(cp,"w+","fop=dlt");
2215 * The C RTL's sigaction fails to check for invalid signal numbers so we
2216 * help it out a bit. The docs are correct, but the actual routine doesn't
2217 * do what the docs say it will.
2219 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2221 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2222 struct sigaction* oact)
2224 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2225 SETERRNO(EINVAL, SS$_INVARG);
2228 return sigaction(sig, act, oact);
2232 #ifdef KILL_BY_SIGPRC
2233 #include <errnodef.h>
2235 /* We implement our own kill() using the undocumented system service
2236 sys$sigprc for one of two reasons:
2238 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2239 target process to do a sys$exit, which usually can't be handled
2240 gracefully...certainly not by Perl and the %SIG{} mechanism.
2242 2.) If the kill() in the CRTL can't be called from a signal
2243 handler without disappearing into the ether, i.e., the signal
2244 it purportedly sends is never trapped. Still true as of VMS 7.3.
2246 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2247 in the target process rather than calling sys$exit.
2249 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2250 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2251 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2252 with condition codes C$_SIG0+nsig*8, catching the exception on the
2253 target process and resignaling with appropriate arguments.
2255 But we don't have that VMS 7.0+ exception handler, so if you
2256 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2258 Also note that SIGTERM is listed in the docs as being "unimplemented",
2259 yet always seems to be signaled with a VMS condition code of 4 (and
2260 correctly handled for that code). So we hardwire it in.
2262 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2263 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2264 than signalling with an unrecognized (and unhandled by CRTL) code.
2267 #define _MY_SIG_MAX 28
2270 Perl_sig_to_vmscondition_int(int sig)
2272 static unsigned int sig_code[_MY_SIG_MAX+1] =
2275 SS$_HANGUP, /* 1 SIGHUP */
2276 SS$_CONTROLC, /* 2 SIGINT */
2277 SS$_CONTROLY, /* 3 SIGQUIT */
2278 SS$_RADRMOD, /* 4 SIGILL */
2279 SS$_BREAK, /* 5 SIGTRAP */
2280 SS$_OPCCUS, /* 6 SIGABRT */
2281 SS$_COMPAT, /* 7 SIGEMT */
2283 SS$_FLTOVF, /* 8 SIGFPE VAX */
2285 SS$_HPARITH, /* 8 SIGFPE AXP */
2287 SS$_ABORT, /* 9 SIGKILL */
2288 SS$_ACCVIO, /* 10 SIGBUS */
2289 SS$_ACCVIO, /* 11 SIGSEGV */
2290 SS$_BADPARAM, /* 12 SIGSYS */
2291 SS$_NOMBX, /* 13 SIGPIPE */
2292 SS$_ASTFLT, /* 14 SIGALRM */
2309 static int initted = 0;
2312 sig_code[16] = C$_SIGUSR1;
2313 sig_code[17] = C$_SIGUSR2;
2314 sig_code[20] = C$_SIGCHLD;
2315 #if __CRTL_VER >= 70300000
2316 sig_code[28] = C$_SIGWINCH;
2320 if (sig < _SIG_MIN) return 0;
2321 if (sig > _MY_SIG_MAX) return 0;
2322 return sig_code[sig];
2326 Perl_sig_to_vmscondition(int sig)
2329 if (vms_debug_on_exception != 0)
2330 lib$signal(SS$_DEBUG);
2332 return Perl_sig_to_vmscondition_int(sig);
2336 #define sys$sigprc SYS$SIGPRC
2340 int sys$sigprc(unsigned int *pidadr,
2341 struct dsc$descriptor_s *prcname,
2348 Perl_my_kill(int pid, int sig)
2353 /* sig 0 means validate the PID */
2354 /*------------------------------*/
2356 const unsigned long int jpicode = JPI$_PID;
2359 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2360 if ($VMS_STATUS_SUCCESS(status))
2363 case SS$_NOSUCHNODE:
2364 case SS$_UNREACHABLE:
2378 code = Perl_sig_to_vmscondition_int(sig);
2381 SETERRNO(EINVAL, SS$_BADPARAM);
2385 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2386 * signals are to be sent to multiple processes.
2387 * pid = 0 - all processes in group except ones that the system exempts
2388 * pid = -1 - all processes except ones that the system exempts
2389 * pid = -n - all processes in group (abs(n)) except ...
2390 * For now, just report as not supported.
2394 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2398 iss = sys$sigprc((unsigned int *)&pid,0,code);
2399 if (iss&1) return 0;
2403 set_errno(EPERM); break;
2405 case SS$_NOSUCHNODE:
2406 case SS$_UNREACHABLE:
2407 set_errno(ESRCH); break;
2409 set_errno(ENOMEM); break;
2411 _ckvmssts_noperl(iss);
2414 set_vaxc_errno(iss);
2420 /* Routine to convert a VMS status code to a UNIX status code.
2421 ** More tricky than it appears because of conflicting conventions with
2424 ** VMS status codes are a bit mask, with the least significant bit set for
2427 ** Special UNIX status of EVMSERR indicates that no translation is currently
2428 ** available, and programs should check the VMS status code.
2430 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2434 #ifndef C_FACILITY_NO
2435 #define C_FACILITY_NO 0x350000
2438 #define DCL_IVVERB 0x38090
2441 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2449 /* Assume the best or the worst */
2450 if (vms_status & STS$M_SUCCESS)
2453 unix_status = EVMSERR;
2455 msg_status = vms_status & ~STS$M_CONTROL;
2457 facility = vms_status & STS$M_FAC_NO;
2458 fac_sp = vms_status & STS$M_FAC_SP;
2459 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2461 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2467 unix_status = EFAULT;
2469 case SS$_DEVOFFLINE:
2470 unix_status = EBUSY;
2473 unix_status = ENOTCONN;
2481 case SS$_INVFILFOROP:
2485 unix_status = EINVAL;
2487 case SS$_UNSUPPORTED:
2488 unix_status = ENOTSUP;
2493 unix_status = EACCES;
2495 case SS$_DEVICEFULL:
2496 unix_status = ENOSPC;
2499 unix_status = ENODEV;
2501 case SS$_NOSUCHFILE:
2502 case SS$_NOSUCHOBJECT:
2503 unix_status = ENOENT;
2505 case SS$_ABORT: /* Fatal case */
2506 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2507 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2508 unix_status = EINTR;
2511 unix_status = E2BIG;
2514 unix_status = ENOMEM;
2517 unix_status = EPERM;
2519 case SS$_NOSUCHNODE:
2520 case SS$_UNREACHABLE:
2521 unix_status = ESRCH;
2524 unix_status = ECHILD;
2527 if ((facility == 0) && (msg_no < 8)) {
2528 /* These are not real VMS status codes so assume that they are
2529 ** already UNIX status codes
2531 unix_status = msg_no;
2537 /* Translate a POSIX exit code to a UNIX exit code */
2538 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2539 unix_status = (msg_no & 0x07F8) >> 3;
2543 /* Documented traditional behavior for handling VMS child exits */
2544 /*--------------------------------------------------------------*/
2545 if (child_flag != 0) {
2547 /* Success / Informational return 0 */
2548 /*----------------------------------*/
2549 if (msg_no & STS$K_SUCCESS)
2552 /* Warning returns 1 */
2553 /*-------------------*/
2554 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2557 /* Everything else pass through the severity bits */
2558 /*------------------------------------------------*/
2559 return (msg_no & STS$M_SEVERITY);
2562 /* Normal VMS status to ERRNO mapping attempt */
2563 /*--------------------------------------------*/
2564 switch(msg_status) {
2565 /* case RMS$_EOF: */ /* End of File */
2566 case RMS$_FNF: /* File Not Found */
2567 case RMS$_DNF: /* Dir Not Found */
2568 unix_status = ENOENT;
2570 case RMS$_RNF: /* Record Not Found */
2571 unix_status = ESRCH;
2574 unix_status = ENOTDIR;
2577 unix_status = ENODEV;
2582 unix_status = EBADF;
2585 unix_status = EEXIST;
2589 case LIB$_INVSTRDES:
2591 case LIB$_NOSUCHSYM:
2592 case LIB$_INVSYMNAM:
2594 unix_status = EINVAL;
2600 unix_status = E2BIG;
2602 case RMS$_PRV: /* No privilege */
2603 case RMS$_ACC: /* ACP file access failed */
2604 case RMS$_WLK: /* Device write locked */
2605 unix_status = EACCES;
2607 case RMS$_MKD: /* Failed to mark for delete */
2608 unix_status = EPERM;
2610 /* case RMS$_NMF: */ /* No more files */
2618 /* Try to guess at what VMS error status should go with a UNIX errno
2619 * value. This is hard to do as there could be many possible VMS
2620 * error statuses that caused the errno value to be set.
2623 int Perl_unix_status_to_vms(int unix_status)
2625 int test_unix_status;
2627 /* Trivial cases first */
2628 /*---------------------*/
2629 if (unix_status == EVMSERR)
2632 /* Is vaxc$errno sane? */
2633 /*---------------------*/
2634 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2635 if (test_unix_status == unix_status)
2638 /* If way out of range, must be VMS code already */
2639 /*-----------------------------------------------*/
2640 if (unix_status > EVMSERR)
2643 /* If out of range, punt */
2644 /*-----------------------*/
2645 if (unix_status > __ERRNO_MAX)
2649 /* Ok, now we have to do it the hard way. */
2650 /*----------------------------------------*/
2651 switch(unix_status) {
2652 case 0: return SS$_NORMAL;
2653 case EPERM: return SS$_NOPRIV;
2654 case ENOENT: return SS$_NOSUCHOBJECT;
2655 case ESRCH: return SS$_UNREACHABLE;
2656 case EINTR: return SS$_ABORT;
2659 case E2BIG: return SS$_BUFFEROVF;
2661 case EBADF: return RMS$_IFI;
2662 case ECHILD: return SS$_NONEXPR;
2664 case ENOMEM: return SS$_INSFMEM;
2665 case EACCES: return SS$_FILACCERR;
2666 case EFAULT: return SS$_ACCVIO;
2668 case EBUSY: return SS$_DEVOFFLINE;
2669 case EEXIST: return RMS$_FEX;
2671 case ENODEV: return SS$_NOSUCHDEV;
2672 case ENOTDIR: return RMS$_DIR;
2674 case EINVAL: return SS$_INVARG;
2680 case ENOSPC: return SS$_DEVICEFULL;
2681 case ESPIPE: return LIB$_INVARG;
2686 case ERANGE: return LIB$_INVARG;
2687 /* case EWOULDBLOCK */
2688 /* case EINPROGRESS */
2691 /* case EDESTADDRREQ */
2693 /* case EPROTOTYPE */
2694 /* case ENOPROTOOPT */
2695 /* case EPROTONOSUPPORT */
2696 /* case ESOCKTNOSUPPORT */
2697 /* case EOPNOTSUPP */
2698 /* case EPFNOSUPPORT */
2699 /* case EAFNOSUPPORT */
2700 /* case EADDRINUSE */
2701 /* case EADDRNOTAVAIL */
2703 /* case ENETUNREACH */
2704 /* case ENETRESET */
2705 /* case ECONNABORTED */
2706 /* case ECONNRESET */
2709 case ENOTCONN: return SS$_CLEARED;
2710 /* case ESHUTDOWN */
2711 /* case ETOOMANYREFS */
2712 /* case ETIMEDOUT */
2713 /* case ECONNREFUSED */
2715 /* case ENAMETOOLONG */
2716 /* case EHOSTDOWN */
2717 /* case EHOSTUNREACH */
2718 /* case ENOTEMPTY */
2730 /* case ECANCELED */
2734 return SS$_UNSUPPORTED;
2740 /* case EABANDONED */
2742 return SS$_ABORT; /* punt */
2747 /* default piping mailbox size */
2749 # define PERL_BUFSIZ 512
2751 # define PERL_BUFSIZ 8192
2756 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2758 unsigned long int mbxbufsiz;
2759 static unsigned long int syssize = 0;
2760 unsigned long int dviitm = DVI$_DEVNAM;
2761 char csize[LNM$C_NAMLENGTH+1];
2765 unsigned long syiitm = SYI$_MAXBUF;
2767 * Get the SYSGEN parameter MAXBUF
2769 * If the logical 'PERL_MBX_SIZE' is defined
2770 * use the value of the logical instead of PERL_BUFSIZ, but
2771 * keep the size between 128 and MAXBUF.
2774 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2777 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2778 mbxbufsiz = atoi(csize);
2780 mbxbufsiz = PERL_BUFSIZ;
2782 if (mbxbufsiz < 128) mbxbufsiz = 128;
2783 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2785 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2787 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2788 _ckvmssts_noperl(sts);
2789 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2791 } /* end of create_mbx() */
2794 /*{{{ my_popen and my_pclose*/
2796 typedef struct _iosb IOSB;
2797 typedef struct _iosb* pIOSB;
2798 typedef struct _pipe Pipe;
2799 typedef struct _pipe* pPipe;
2800 typedef struct pipe_details Info;
2801 typedef struct pipe_details* pInfo;
2802 typedef struct _srqp RQE;
2803 typedef struct _srqp* pRQE;
2804 typedef struct _tochildbuf CBuf;
2805 typedef struct _tochildbuf* pCBuf;
2808 unsigned short status;
2809 unsigned short count;
2810 unsigned long dvispec;
2813 #pragma member_alignment save
2814 #pragma nomember_alignment quadword
2815 struct _srqp { /* VMS self-relative queue entry */
2816 unsigned long qptr[2];
2818 #pragma member_alignment restore
2819 static RQE RQE_ZERO = {0,0};
2821 struct _tochildbuf {
2824 unsigned short size;
2832 unsigned short chan_in;
2833 unsigned short chan_out;
2835 unsigned int bufsize;
2847 #if defined(PERL_IMPLICIT_CONTEXT)
2848 void *thx; /* Either a thread or an interpreter */
2849 /* pointer, depending on how we're built */
2857 PerlIO *fp; /* file pointer to pipe mailbox */
2858 int useFILE; /* using stdio, not perlio */
2859 int pid; /* PID of subprocess */
2860 int mode; /* == 'r' if pipe open for reading */
2861 int done; /* subprocess has completed */
2862 int waiting; /* waiting for completion/closure */
2863 int closing; /* my_pclose is closing this pipe */
2864 unsigned long completion; /* termination status of subprocess */
2865 pPipe in; /* pipe in to sub */
2866 pPipe out; /* pipe out of sub */
2867 pPipe err; /* pipe of sub's sys$error */
2868 int in_done; /* true when in pipe finished */
2871 unsigned short xchan; /* channel to debug xterm */
2872 unsigned short xchan_valid; /* channel is assigned */
2875 struct exit_control_block
2877 struct exit_control_block *flink;
2878 unsigned long int (*exit_routine)(void);
2879 unsigned long int arg_count;
2880 unsigned long int *status_address;
2881 unsigned long int exit_status;
2884 typedef struct _closed_pipes Xpipe;
2885 typedef struct _closed_pipes* pXpipe;
2887 struct _closed_pipes {
2888 int pid; /* PID of subprocess */
2889 unsigned long completion; /* termination status of subprocess */
2891 #define NKEEPCLOSED 50
2892 static Xpipe closed_list[NKEEPCLOSED];
2893 static int closed_index = 0;
2894 static int closed_num = 0;
2896 #define RETRY_DELAY "0 ::0.20"
2897 #define MAX_RETRY 50
2899 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2900 static unsigned long mypid;
2901 static unsigned long delaytime[2];
2903 static pInfo open_pipes = NULL;
2904 static $DESCRIPTOR(nl_desc, "NL:");
2906 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2910 static unsigned long int
2911 pipe_exit_routine(void)
2914 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2915 int sts, did_stuff, j;
2918 * Flush any pending i/o, but since we are in process run-down, be
2919 * careful about referencing PerlIO structures that may already have
2920 * been deallocated. We may not even have an interpreter anymore.
2925 #if defined(PERL_IMPLICIT_CONTEXT)
2926 /* We need to use the Perl context of the thread that created */
2930 aTHX = info->err->thx;
2932 aTHX = info->out->thx;
2934 aTHX = info->in->thx;
2937 #if defined(USE_ITHREADS)
2941 && PL_perlio_fd_refcnt
2944 PerlIO_flush(info->fp);
2946 fflush((FILE *)info->fp);
2952 next we try sending an EOF...ignore if doesn't work, make sure we
2959 _ckvmssts_noperl(sys$setast(0));
2960 if (info->in && !info->in->shut_on_empty) {
2961 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2966 _ckvmssts_noperl(sys$setast(1));
2970 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2972 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2977 _ckvmssts_noperl(sys$setast(0));
2978 if (info->waiting && info->done)
2980 nwait += info->waiting;
2981 _ckvmssts_noperl(sys$setast(1));
2991 _ckvmssts_noperl(sys$setast(0));
2992 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2993 sts = sys$forcex(&info->pid,0,&abort);
2994 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2997 _ckvmssts_noperl(sys$setast(1));
3001 /* again, wait for effect */
3003 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3008 _ckvmssts_noperl(sys$setast(0));
3009 if (info->waiting && info->done)
3011 nwait += info->waiting;
3012 _ckvmssts_noperl(sys$setast(1));
3021 _ckvmssts_noperl(sys$setast(0));
3022 if (!info->done) { /* We tried to be nice . . . */
3023 sts = sys$delprc(&info->pid,0);
3024 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3025 info->done = 1; /* sys$delprc is as done as we're going to get. */
3027 _ckvmssts_noperl(sys$setast(1));
3033 #if defined(PERL_IMPLICIT_CONTEXT)
3034 /* We need to use the Perl context of the thread that created */
3037 if (open_pipes->err)
3038 aTHX = open_pipes->err->thx;
3039 else if (open_pipes->out)
3040 aTHX = open_pipes->out->thx;
3041 else if (open_pipes->in)
3042 aTHX = open_pipes->in->thx;
3044 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3045 else if (!(sts & 1)) retsts = sts;
3050 static struct exit_control_block pipe_exitblock =
3051 {(struct exit_control_block *) 0,
3052 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3054 static void pipe_mbxtofd_ast(pPipe p);
3055 static void pipe_tochild1_ast(pPipe p);
3056 static void pipe_tochild2_ast(pPipe p);
3059 popen_completion_ast(pInfo info)
3061 pInfo i = open_pipes;
3064 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3065 closed_list[closed_index].pid = info->pid;
3066 closed_list[closed_index].completion = info->completion;
3068 if (closed_index == NKEEPCLOSED)
3073 if (i == info) break;
3076 if (!i) return; /* unlinked, probably freed too */
3081 Writing to subprocess ...
3082 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3084 chan_out may be waiting for "done" flag, or hung waiting
3085 for i/o completion to child...cancel the i/o. This will
3086 put it into "snarf mode" (done but no EOF yet) that discards
3089 Output from subprocess (stdout, stderr) needs to be flushed and
3090 shut down. We try sending an EOF, but if the mbx is full the pipe
3091 routine should still catch the "shut_on_empty" flag, telling it to
3092 use immediate-style reads so that "mbx empty" -> EOF.
3096 if (info->in && !info->in_done) { /* only for mode=w */
3097 if (info->in->shut_on_empty && info->in->need_wake) {
3098 info->in->need_wake = FALSE;
3099 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3101 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3105 if (info->out && !info->out_done) { /* were we also piping output? */
3106 info->out->shut_on_empty = TRUE;
3107 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3108 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3109 _ckvmssts_noperl(iss);
3112 if (info->err && !info->err_done) { /* we were piping stderr */
3113 info->err->shut_on_empty = TRUE;
3114 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3115 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3116 _ckvmssts_noperl(iss);
3118 _ckvmssts_noperl(sys$setef(pipe_ef));
3122 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3123 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3124 static void pipe_infromchild_ast(pPipe p);
3127 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3128 inside an AST routine without worrying about reentrancy and which Perl
3129 memory allocator is being used.
3131 We read data and queue up the buffers, then spit them out one at a
3132 time to the output mailbox when the output mailbox is ready for one.
3135 #define INITIAL_TOCHILDQUEUE 2
3138 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3142 char mbx1[64], mbx2[64];
3143 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3144 DSC$K_CLASS_S, mbx1},
3145 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3146 DSC$K_CLASS_S, mbx2};
3147 unsigned int dviitm = DVI$_DEVBUFSIZ;
3151 _ckvmssts_noperl(lib$get_vm(&n, &p));
3153 create_mbx(&p->chan_in , &d_mbx1);
3154 create_mbx(&p->chan_out, &d_mbx2);
3155 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3158 p->shut_on_empty = FALSE;
3159 p->need_wake = FALSE;
3162 p->iosb.status = SS$_NORMAL;
3163 p->iosb2.status = SS$_NORMAL;
3169 #ifdef PERL_IMPLICIT_CONTEXT
3173 n = sizeof(CBuf) + p->bufsize;
3175 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3176 _ckvmssts_noperl(lib$get_vm(&n, &b));
3177 b->buf = (char *) b + sizeof(CBuf);
3178 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3181 pipe_tochild2_ast(p);
3182 pipe_tochild1_ast(p);
3188 /* reads the MBX Perl is writing, and queues */
3191 pipe_tochild1_ast(pPipe p)
3194 int iss = p->iosb.status;
3195 int eof = (iss == SS$_ENDOFFILE);
3197 #ifdef PERL_IMPLICIT_CONTEXT
3203 p->shut_on_empty = TRUE;
3205 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3207 _ckvmssts_noperl(iss);
3211 b->size = p->iosb.count;
3212 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3214 p->need_wake = FALSE;
3215 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3218 p->retry = 1; /* initial call */
3221 if (eof) { /* flush the free queue, return when done */
3222 int n = sizeof(CBuf) + p->bufsize;
3224 iss = lib$remqti(&p->free, &b);
3225 if (iss == LIB$_QUEWASEMP) return;
3226 _ckvmssts_noperl(iss);
3227 _ckvmssts_noperl(lib$free_vm(&n, &b));
3231 iss = lib$remqti(&p->free, &b);
3232 if (iss == LIB$_QUEWASEMP) {
3233 int n = sizeof(CBuf) + p->bufsize;
3234 _ckvmssts_noperl(lib$get_vm(&n, &b));
3235 b->buf = (char *) b + sizeof(CBuf);
3237 _ckvmssts_noperl(iss);
3241 iss = sys$qio(0,p->chan_in,
3242 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3244 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3245 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3246 _ckvmssts_noperl(iss);
3250 /* writes queued buffers to output, waits for each to complete before
3254 pipe_tochild2_ast(pPipe p)
3257 int iss = p->iosb2.status;
3258 int n = sizeof(CBuf) + p->bufsize;
3259 int done = (p->info && p->info->done) ||
3260 iss == SS$_CANCEL || iss == SS$_ABORT;
3261 #if defined(PERL_IMPLICIT_CONTEXT)
3266 if (p->type) { /* type=1 has old buffer, dispose */
3267 if (p->shut_on_empty) {
3268 _ckvmssts_noperl(lib$free_vm(&n, &b));
3270 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3275 iss = lib$remqti(&p->wait, &b);
3276 if (iss == LIB$_QUEWASEMP) {
3277 if (p->shut_on_empty) {
3279 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3280 *p->pipe_done = TRUE;
3281 _ckvmssts_noperl(sys$setef(pipe_ef));
3283 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3284 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3288 p->need_wake = TRUE;
3291 _ckvmssts_noperl(iss);
3298 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3299 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3301 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3302 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3311 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3314 char mbx1[64], mbx2[64];
3315 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3316 DSC$K_CLASS_S, mbx1},
3317 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3318 DSC$K_CLASS_S, mbx2};
3319 unsigned int dviitm = DVI$_DEVBUFSIZ;
3321 int n = sizeof(Pipe);
3322 _ckvmssts_noperl(lib$get_vm(&n, &p));
3323 create_mbx(&p->chan_in , &d_mbx1);
3324 create_mbx(&p->chan_out, &d_mbx2);
3326 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3327 n = p->bufsize * sizeof(char);
3328 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3329 p->shut_on_empty = FALSE;
3332 p->iosb.status = SS$_NORMAL;
3333 #if defined(PERL_IMPLICIT_CONTEXT)
3336 pipe_infromchild_ast(p);
3344 pipe_infromchild_ast(pPipe p)
3346 int iss = p->iosb.status;
3347 int eof = (iss == SS$_ENDOFFILE);
3348 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3349 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3350 #if defined(PERL_IMPLICIT_CONTEXT)
3354 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3355 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3360 input shutdown if EOF from self (done or shut_on_empty)
3361 output shutdown if closing flag set (my_pclose)
3362 send data/eof from child or eof from self
3363 otherwise, re-read (snarf of data from child)
3368 if (myeof && p->chan_in) { /* input shutdown */
3369 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3374 if (myeof || kideof) { /* pass EOF to parent */
3375 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3376 pipe_infromchild_ast, p,
3379 } else if (eof) { /* eat EOF --- fall through to read*/
3381 } else { /* transmit data */
3382 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3383 pipe_infromchild_ast,p,
3384 p->buf, p->iosb.count, 0, 0, 0, 0));
3390 /* everything shut? flag as done */
3392 if (!p->chan_in && !p->chan_out) {
3393 *p->pipe_done = TRUE;
3394 _ckvmssts_noperl(sys$setef(pipe_ef));
3398 /* write completed (or read, if snarfing from child)
3399 if still have input active,
3400 queue read...immediate mode if shut_on_empty so we get EOF if empty
3402 check if Perl reading, generate EOFs as needed
3408 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3409 pipe_infromchild_ast,p,
3410 p->buf, p->bufsize, 0, 0, 0, 0);
3411 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3412 _ckvmssts_noperl(iss);
3413 } else { /* send EOFs for extra reads */
3414 p->iosb.status = SS$_ENDOFFILE;
3415 p->iosb.dvispec = 0;
3416 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3418 pipe_infromchild_ast, p, 0, 0, 0, 0));
3424 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3428 unsigned long dviitm = DVI$_DEVBUFSIZ;
3430 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3431 DSC$K_CLASS_S, mbx};
3432 int n = sizeof(Pipe);
3434 /* things like terminals and mbx's don't need this filter */
3435 if (fd && fstat(fd,&s) == 0) {
3436 unsigned long devchar;
3438 unsigned short dev_len;
3439 struct dsc$descriptor_s d_dev;
3441 struct item_list_3 items[3];
3443 unsigned short dvi_iosb[4];
3445 cptr = getname(fd, out, 1);
3446 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3447 d_dev.dsc$a_pointer = out;
3448 d_dev.dsc$w_length = strlen(out);
3449 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3450 d_dev.dsc$b_class = DSC$K_CLASS_S;
3453 items[0].code = DVI$_DEVCHAR;
3454 items[0].bufadr = &devchar;
3455 items[0].retadr = NULL;
3457 items[1].code = DVI$_FULLDEVNAM;
3458 items[1].bufadr = device;
3459 items[1].retadr = &dev_len;
3463 status = sys$getdviw
3464 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3465 _ckvmssts_noperl(status);
3466 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3467 device[dev_len] = 0;
3469 if (!(devchar & DEV$M_DIR)) {
3470 strcpy(out, device);
3476 _ckvmssts_noperl(lib$get_vm(&n, &p));
3477 p->fd_out = dup(fd);
3478 create_mbx(&p->chan_in, &d_mbx);
3479 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3480 n = (p->bufsize+1) * sizeof(char);
3481 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3482 p->shut_on_empty = FALSE;
3487 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3488 pipe_mbxtofd_ast, p,
3489 p->buf, p->bufsize, 0, 0, 0, 0));
3495 pipe_mbxtofd_ast(pPipe p)
3497 int iss = p->iosb.status;
3498 int done = p->info->done;
3500 int eof = (iss == SS$_ENDOFFILE);
3501 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3502 int err = !(iss&1) && !eof;
3503 #if defined(PERL_IMPLICIT_CONTEXT)
3507 if (done && myeof) { /* end piping */
3509 sys$dassgn(p->chan_in);
3510 *p->pipe_done = TRUE;
3511 _ckvmssts_noperl(sys$setef(pipe_ef));
3515 if (!err && !eof) { /* good data to send to file */
3516 p->buf[p->iosb.count] = '\n';
3517 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3520 if (p->retry < MAX_RETRY) {
3521 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3527 _ckvmssts_noperl(iss);
3531 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3532 pipe_mbxtofd_ast, p,
3533 p->buf, p->bufsize, 0, 0, 0, 0);
3534 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3535 _ckvmssts_noperl(iss);
3539 typedef struct _pipeloc PLOC;
3540 typedef struct _pipeloc* pPLOC;
3544 char dir[NAM$C_MAXRSS+1];
3546 static pPLOC head_PLOC = 0;
3549 free_pipelocs(pTHX_ void *head)
3552 pPLOC *pHead = (pPLOC *)head;
3564 store_pipelocs(pTHX)
3572 char temp[NAM$C_MAXRSS+1];
3576 free_pipelocs(aTHX_ &head_PLOC);
3578 /* the . directory from @INC comes last */
3580 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3581 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3582 p->next = head_PLOC;
3584 strcpy(p->dir,"./");
3586 /* get the directory from $^X */
3588 unixdir = (char *)PerlMem_malloc(VMS_MAXRSS);
3589 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3591 #ifdef PERL_IMPLICIT_CONTEXT
3592 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3594 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3596 my_strlcpy(temp, PL_origargv[0], sizeof(temp));
3597 x = strrchr(temp,']');
3599 x = strrchr(temp,'>');
3601 /* It could be a UNIX path */
3602 x = strrchr(temp,'/');
3608 /* Got a bare name, so use default directory */
3613 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3614 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3615 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3616 p->next = head_PLOC;
3618 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3622 /* reverse order of @INC entries, skip "." since entered above */
3624 #ifdef PERL_IMPLICIT_CONTEXT
3627 if (PL_incgv) av = GvAVn(PL_incgv);
3629 for (i = 0; av && i <= AvFILL(av); i++) {
3630 dirsv = *av_fetch(av,i,TRUE);
3632 if (SvROK(dirsv)) continue;
3633 dir = SvPVx(dirsv,n_a);
3634 if (strcmp(dir,".") == 0) continue;
3635 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3638 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3639 p->next = head_PLOC;
3641 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3644 /* most likely spot (ARCHLIB) put first in the list */
3647 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3648 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3649 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3650 p->next = head_PLOC;
3652 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3655 PerlMem_free(unixdir);
3659 Perl_cando_by_name_int
3660 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3661 #if !defined(PERL_IMPLICIT_CONTEXT)
3662 #define cando_by_name_int Perl_cando_by_name_int
3664 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3670 static int vmspipe_file_status = 0;
3671 static char vmspipe_file[NAM$C_MAXRSS+1];
3673 /* already found? Check and use ... need read+execute permission */
3675 if (vmspipe_file_status == 1) {
3676 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3677 && cando_by_name_int
3678 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3679 return vmspipe_file;
3681 vmspipe_file_status = 0;
3684 /* scan through stored @INC, $^X */
3686 if (vmspipe_file_status == 0) {
3687 char file[NAM$C_MAXRSS+1];
3688 pPLOC p = head_PLOC;
3693 dirlen = my_strlcpy(file, p->dir, sizeof(file));
3694 my_strlcat(file, "vmspipe.com", sizeof(file));
3697 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3698 if (!exp_res) continue;
3700 if (cando_by_name_int
3701 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3702 && cando_by_name_int
3703 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3704 vmspipe_file_status = 1;
3705 return vmspipe_file;
3708 vmspipe_file_status = -1; /* failed, use tempfiles */
3715 vmspipe_tempfile(pTHX)
3717 char file[NAM$C_MAXRSS+1];
3719 static int index = 0;
3723 /* create a tempfile */
3725 /* we can't go from W, shr=get to R, shr=get without
3726 an intermediate vulnerable state, so don't bother trying...
3728 and lib$spawn doesn't shr=put, so have to close the write
3730 So... match up the creation date/time and the FID to
3731 make sure we're dealing with the same file
3736 if (!decc_filename_unix_only) {
3737 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3738 fp = fopen(file,"w");
3740 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3741 fp = fopen(file,"w");
3743 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3744 fp = fopen(file,"w");
3749 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3750 fp = fopen(file,"w");
3752 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3753 fp = fopen(file,"w");
3755 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3756 fp = fopen(file,"w");
3760 if (!fp) return 0; /* we're hosed */
3762 fprintf(fp,"$! 'f$verify(0)'\n");
3763 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3764 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3765 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3766 fprintf(fp,"$ perl_on = \"set noon\"\n");
3767 fprintf(fp,"$ perl_exit = \"exit\"\n");
3768 fprintf(fp,"$ perl_del = \"delete\"\n");
3769 fprintf(fp,"$ pif = \"if\"\n");
3770 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3771 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3772 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3773 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3774 fprintf(fp,"$! --- build command line to get max possible length\n");
3775 fprintf(fp,"$c=perl_popen_cmd0\n");
3776 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3777 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3778 fprintf(fp,"$x=perl_popen_cmd3\n");
3779 fprintf(fp,"$c=c+x\n");
3780 fprintf(fp,"$ perl_on\n");
3781 fprintf(fp,"$ 'c'\n");
3782 fprintf(fp,"$ perl_status = $STATUS\n");
3783 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3784 fprintf(fp,"$ perl_exit 'perl_status'\n");
3787 fgetname(fp, file, 1);
3788 fstat(fileno(fp), &s0.crtl_stat);
3791 if (decc_filename_unix_only)
3792 int_tounixspec(file, file, NULL);
3793 fp = fopen(file,"r","shr=get");
3795 fstat(fileno(fp), &s1.crtl_stat);
3797 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3798 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3807 static int vms_is_syscommand_xterm(void)
3809 const static struct dsc$descriptor_s syscommand_dsc =
3810 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3812 const static struct dsc$descriptor_s decwdisplay_dsc =
3813 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3815 struct item_list_3 items[2];
3816 unsigned short dvi_iosb[4];
3817 unsigned long devchar;
3818 unsigned long devclass;
3821 /* Very simple check to guess if sys$command is a decterm? */
3822 /* First see if the DECW$DISPLAY: device exists */
3824 items[0].code = DVI$_DEVCHAR;
3825 items[0].bufadr = &devchar;
3826 items[0].retadr = NULL;
3830 status = sys$getdviw
3831 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3833 if ($VMS_STATUS_SUCCESS(status)) {
3834 status = dvi_iosb[0];
3837 if (!$VMS_STATUS_SUCCESS(status)) {
3838 SETERRNO(EVMSERR, status);
3842 /* If it does, then for now assume that we are on a workstation */
3843 /* Now verify that SYS$COMMAND is a terminal */
3844 /* for creating the debugger DECTerm */
3847 items[0].code = DVI$_DEVCLASS;
3848 items[0].bufadr = &devclass;
3849 items[0].retadr = NULL;
3853 status = sys$getdviw
3854 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3856 if ($VMS_STATUS_SUCCESS(status)) {
3857 status = dvi_iosb[0];
3860 if (!$VMS_STATUS_SUCCESS(status)) {
3861 SETERRNO(EVMSERR, status);
3865 if (devclass == DC$_TERM) {
3872 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3873 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3878 char device_name[65];
3879 unsigned short device_name_len;
3880 struct dsc$descriptor_s customization_dsc;
3881 struct dsc$descriptor_s device_name_dsc;
3883 char customization[200];
3887 unsigned short p_chan;
3889 unsigned short iosb[4];
3890 const char * cust_str =
3891 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3892 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3893 DSC$K_CLASS_S, mbx1};
3895 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
3896 /*---------------------------------------*/
3897 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
3900 /* Make sure that this is from the Perl debugger */
3901 ret_char = strstr(cmd," xterm ");
3902 if (ret_char == NULL)
3904 cptr = ret_char + 7;
3905 ret_char = strstr(cmd,"tty");
3906 if (ret_char == NULL)
3908 ret_char = strstr(cmd,"sleep");
3909 if (ret_char == NULL)
3912 if (decw_term_port == 0) {
3913 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
3914 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
3915 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
3917 status = lib$find_image_symbol
3919 &decw_term_port_dsc,
3920 (void *)&decw_term_port,
3924 /* Try again with the other image name */
3925 if (!$VMS_STATUS_SUCCESS(status)) {
3927 status = lib$find_image_symbol
3929 &decw_term_port_dsc,
3930 (void *)&decw_term_port,
3939 /* No decw$term_port, give it up */
3940 if (!$VMS_STATUS_SUCCESS(status))
3943 /* Are we on a workstation? */
3944 /* to do: capture the rows / columns and pass their properties */
3945 ret_stat = vms_is_syscommand_xterm();
3949 /* Make the title: */
3950 ret_char = strstr(cptr,"-title");
3951 if (ret_char != NULL) {
3952 while ((*cptr != 0) && (*cptr != '\"')) {
3958 while ((*cptr != 0) && (*cptr != '\"')) {
3971 strcpy(title,"Perl Debug DECTerm");
3973 sprintf(customization, cust_str, title);
3975 customization_dsc.dsc$a_pointer = customization;
3976 customization_dsc.dsc$w_length = strlen(customization);
3977 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3978 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
3980 device_name_dsc.dsc$a_pointer = device_name;
3981 device_name_dsc.dsc$w_length = sizeof device_name -1;
3982 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3983 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
3985 device_name_len = 0;
3987 /* Try to create the window */
3988 status = (*decw_term_port)
3997 if (!$VMS_STATUS_SUCCESS(status)) {
3998 SETERRNO(EVMSERR, status);
4002 device_name[device_name_len] = '\0';
4004 /* Need to set this up to look like a pipe for cleanup */
4006 status = lib$get_vm(&n, &info);
4007 if (!$VMS_STATUS_SUCCESS(status)) {
4008 SETERRNO(ENOMEM, status);
4014 info->completion = 0;
4015 info->closing = FALSE;
4022 info->in_done = TRUE;
4023 info->out_done = TRUE;
4024 info->err_done = TRUE;
4026 /* Assign a channel on this so that it will persist, and not login */
4027 /* We stash this channel in the info structure for reference. */
4028 /* The created xterm self destructs when the last channel is removed */
4029 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4030 /* So leave this assigned. */
4031 device_name_dsc.dsc$w_length = device_name_len;
4032 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4033 if (!$VMS_STATUS_SUCCESS(status)) {
4034 SETERRNO(EVMSERR, status);
4037 info->xchan_valid = 1;
4039 /* Now create a mailbox to be read by the application */
4041 create_mbx(&p_chan, &d_mbx1);
4043 /* write the name of the created terminal to the mailbox */
4044 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4045 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4047 if (!$VMS_STATUS_SUCCESS(status)) {
4048 SETERRNO(EVMSERR, status);
4052 info->fp = PerlIO_open(mbx1, mode);
4054 /* Done with this channel */
4057 /* If any errors, then clean up */
4060 _ckvmssts_noperl(lib$free_vm(&n, &info));
4068 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4071 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4073 static int handler_set_up = FALSE;
4075 unsigned long int sts, flags = CLI$M_NOWAIT;
4076 /* The use of a GLOBAL table (as was done previously) rendered
4077 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4078 * environment. Hence we've switched to LOCAL symbol table.
4080 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4082 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4083 char *in, *out, *err, mbx[512];
4085 char tfilebuf[NAM$C_MAXRSS+1];
4087 char cmd_sym_name[20];
4088 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4089 DSC$K_CLASS_S, symbol};
4090 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4092 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4093 DSC$K_CLASS_S, cmd_sym_name};
4094 struct dsc$descriptor_s *vmscmd;
4095 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4096 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4097 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4099 /* Check here for Xterm create request. This means looking for
4100 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4101 * is possible to create an xterm.
4103 if (*in_mode == 'r') {
4106 #if defined(PERL_IMPLICIT_CONTEXT)
4107 /* Can not fork an xterm with a NULL context */
4108 /* This probably could never happen */
4112 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4113 if (xterm_fd != NULL)
4117 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4119 /* once-per-program initialization...
4120 note that the SETAST calls and the dual test of pipe_ef
4121 makes sure that only the FIRST thread through here does
4122 the initialization...all other threads wait until it's
4125 Yeah, uglier than a pthread call, it's got all the stuff inline
4126 rather than in a separate routine.
4130 _ckvmssts_noperl(sys$setast(0));
4132 unsigned long int pidcode = JPI$_PID;
4133 $DESCRIPTOR(d_delay, RETRY_DELAY);
4134 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4135 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4136 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4138 if (!handler_set_up) {
4139 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4140 handler_set_up = TRUE;
4142 _ckvmssts_noperl(sys$setast(1));
4145 /* see if we can find a VMSPIPE.COM */
4148 vmspipe = find_vmspipe(aTHX);
4150 vmspipedsc.dsc$w_length = my_strlcpy(tfilebuf+1, vmspipe, sizeof(tfilebuf)-1) + 1;
4151 } else { /* uh, oh...we're in tempfile hell */
4152 tpipe = vmspipe_tempfile(aTHX);
4153 if (!tpipe) { /* a fish popular in Boston */
4154 if (ckWARN(WARN_PIPE)) {
4155 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4159 fgetname(tpipe,tfilebuf+1,1);
4160 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4162 vmspipedsc.dsc$a_pointer = tfilebuf;
4164 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4167 case RMS$_FNF: case RMS$_DNF:
4168 set_errno(ENOENT); break;
4170 set_errno(ENOTDIR); break;
4172 set_errno(ENODEV); break;
4174 set_errno(EACCES); break;
4176 set_errno(EINVAL); break;
4177 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4178 set_errno(E2BIG); break;
4179 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4180 _ckvmssts_noperl(sts); /* fall through */
4181 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4184 set_vaxc_errno(sts);
4185 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4186 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4192 _ckvmssts_noperl(lib$get_vm(&n, &info));
4194 my_strlcpy(mode, in_mode, sizeof(mode));
4197 info->completion = 0;
4198 info->closing = FALSE;
4205 info->in_done = TRUE;
4206 info->out_done = TRUE;
4207 info->err_done = TRUE;
4209 info->xchan_valid = 0;
4211 in = (char *)PerlMem_malloc(VMS_MAXRSS);
4212 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4213 out = (char *)PerlMem_malloc(VMS_MAXRSS);
4214 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4215 err = (char *)PerlMem_malloc(VMS_MAXRSS);
4216 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4218 in[0] = out[0] = err[0] = '\0';
4220 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4224 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4229 if (*mode == 'r') { /* piping from subroutine */
4231 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4233 info->out->pipe_done = &info->out_done;
4234 info->out_done = FALSE;
4235 info->out->info = info;
4237 if (!info->useFILE) {
4238 info->fp = PerlIO_open(mbx, mode);
4240 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4241 vmssetuserlnm("SYS$INPUT", mbx);
4244 if (!info->fp && info->out) {
4245 sys$cancel(info->out->chan_out);
4247 while (!info->out_done) {
4249 _ckvmssts_noperl(sys$setast(0));
4250 done = info->out_done;
4251 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4252 _ckvmssts_noperl(sys$setast(1));
4253 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4256 if (info->out->buf) {
4257 n = info->out->bufsize * sizeof(char);
4258 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4261 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4263 _ckvmssts_noperl(lib$free_vm(&n, &info));
4268 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4270 info->err->pipe_done = &info->err_done;
4271 info->err_done = FALSE;
4272 info->err->info = info;
4275 } else if (*mode == 'w') { /* piping to subroutine */
4277 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4279 info->out->pipe_done = &info->out_done;
4280 info->out_done = FALSE;
4281 info->out->info = info;
4284 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4286 info->err->pipe_done = &info->err_done;
4287 info->err_done = FALSE;
4288 info->err->info = info;
4291 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4292 if (!info->useFILE) {
4293 info->fp = PerlIO_open(mbx, mode);
4295 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4296 vmssetuserlnm("SYS$OUTPUT", mbx);
4300 info->in->pipe_done = &info->in_done;
4301 info->in_done = FALSE;
4302 info->in->info = info;
4306 if (!info->fp && info->in) {
4308 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4309 0, 0, 0, 0, 0, 0, 0, 0));
4311 while (!info->in_done) {
4313 _ckvmssts_noperl(sys$setast(0));
4314 done = info->in_done;
4315 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4316 _ckvmssts_noperl(sys$setast(1));
4317 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4320 if (info->in->buf) {
4321 n = info->in->bufsize * sizeof(char);
4322 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4325 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4327 _ckvmssts_noperl(lib$free_vm(&n, &info));
4333 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4334 /* Let the child inherit standard input, unless it's a directory. */
4336 if (my_trnlnm("SYS$INPUT", in, 0)) {
4337 if (flex_stat(in, &st) != 0 || S_ISDIR(st.st_mode))
4341 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4343 info->out->pipe_done = &info->out_done;
4344 info->out_done = FALSE;
4345 info->out->info = info;
4348 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4350 info->err->pipe_done = &info->err_done;
4351 info->err_done = FALSE;
4352 info->err->info = info;
4356 d_symbol.dsc$w_length = my_strlcpy(symbol, in, sizeof(symbol));
4357 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4359 d_symbol.dsc$w_length = my_strlcpy(symbol, err, sizeof(symbol));
4360 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4362 d_symbol.dsc$w_length = my_strlcpy(symbol, out, sizeof(symbol));
4363 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4365 /* Done with the names for the pipes */
4370 p = vmscmd->dsc$a_pointer;
4371 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4372 if (*p == '$') p++; /* remove leading $ */
4373 while (*p == ' ' || *p == '\t') p++;
4375 for (j = 0; j < 4; j++) {
4376 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4377 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4379 d_symbol.dsc$w_length = my_strlcpy(symbol, p, sizeof(symbol));
4380 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4382 if (strlen(p) > MAX_DCL_SYMBOL) {
4383 p += MAX_DCL_SYMBOL;
4388 _ckvmssts_noperl(sys$setast(0));
4389 info->next=open_pipes; /* prepend to list */
4391 _ckvmssts_noperl(sys$setast(1));
4392 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4393 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4394 * have SYS$COMMAND if we need it.
4396 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4397 0, &info->pid, &info->completion,
4398 0, popen_completion_ast,info,0,0,0));
4400 /* if we were using a tempfile, close it now */
4402 if (tpipe) fclose(tpipe);
4404 /* once the subprocess is spawned, it has copied the symbols and
4405 we can get rid of ours */
4407 for (j = 0; j < 4; j++) {
4408 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4409 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4410 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4412 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4413 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4414 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4415 vms_execfree(vmscmd);
4417 #ifdef PERL_IMPLICIT_CONTEXT
4420 PL_forkprocess = info->pid;
4427 _ckvmssts_noperl(sys$setast(0));
4429 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4430 _ckvmssts_noperl(sys$setast(1));
4431 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4433 *psts = info->completion;
4434 /* Caller thinks it is open and tries to close it. */
4435 /* This causes some problems, as it changes the error status */
4436 /* my_pclose(info->fp); */
4438 /* If we did not have a file pointer open, then we have to */
4439 /* clean up here or eventually we will run out of something */
4441 if (info->fp == NULL) {
4442 my_pclose_pinfo(aTHX_ info);
4450 } /* end of safe_popen */
4453 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4455 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4459 TAINT_PROPER("popen");
4460 PERL_FLUSHALL_FOR_CHILD;
4461 return safe_popen(aTHX_ cmd,mode,&sts);
4467 /* Routine to close and cleanup a pipe info structure */
4469 static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4471 unsigned long int retsts;
4475 /* If we were writing to a subprocess, insure that someone reading from
4476 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4477 * produce an EOF record in the mailbox.
4479 * well, at least sometimes it *does*, so we have to watch out for
4480 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4484 #if defined(USE_ITHREADS)
4488 && PL_perlio_fd_refcnt
4491 PerlIO_flush(info->fp);
4493 fflush((FILE *)info->fp);
4496 _ckvmssts(sys$setast(0));
4497 info->closing = TRUE;
4498 done = info->done && info->in_done && info->out_done && info->err_done;
4499 /* hanging on write to Perl's input? cancel it */
4500 if (info->mode == 'r' && info->out && !info->out_done) {
4501 if (info->out->chan_out) {
4502 _ckvmssts(sys$cancel(info->out->chan_out));
4503 if (!info->out->chan_in) { /* EOF generation, need AST */
4504 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4508 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4509 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4511 _ckvmssts(sys$setast(1));
4514 #if defined(USE_ITHREADS)
4518 && PL_perlio_fd_refcnt
4521 PerlIO_close(info->fp);
4523 fclose((FILE *)info->fp);
4526 we have to wait until subprocess completes, but ALSO wait until all
4527 the i/o completes...otherwise we'll be freeing the "info" structure
4528 that the i/o ASTs could still be using...
4532 _ckvmssts(sys$setast(0));
4533 done = info->done && info->in_done && info->out_done && info->err_done;
4534 if (!done) _ckvmssts(sys$clref(pipe_ef));
4535 _ckvmssts(sys$setast(1));
4536 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4538 retsts = info->completion;
4540 /* remove from list of open pipes */
4541 _ckvmssts(sys$setast(0));
4543 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4549 last->next = info->next;
4551 open_pipes = info->next;
4552 _ckvmssts(sys$setast(1));
4554 /* free buffers and structures */
4557 if (info->in->buf) {
4558 n = info->in->bufsize * sizeof(char);
4559 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4562 _ckvmssts(lib$free_vm(&n, &info->in));
4565 if (info->out->buf) {
4566 n = info->out->bufsize * sizeof(char);
4567 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4570 _ckvmssts(lib$free_vm(&n, &info->out));
4573 if (info->err->buf) {
4574 n = info->err->bufsize * sizeof(char);
4575 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4578 _ckvmssts(lib$free_vm(&n, &info->err));
4581 _ckvmssts(lib$free_vm(&n, &info));
4587 /*{{{ I32 my_pclose(PerlIO *fp)*/
4588 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4590 pInfo info, last = NULL;
4593 /* Fixme - need ast and mutex protection here */
4594 for (info = open_pipes; info != NULL; last = info, info = info->next)
4595 if (info->fp == fp) break;
4597 if (info == NULL) { /* no such pipe open */
4598 set_errno(ECHILD); /* quoth POSIX */
4599 set_vaxc_errno(SS$_NONEXPR);
4603 ret_status = my_pclose_pinfo(aTHX_ info);
4607 } /* end of my_pclose() */
4609 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4610 /* Roll our own prototype because we want this regardless of whether
4611 * _VMS_WAIT is defined.
4617 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4623 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4624 created with popen(); otherwise partially emulate waitpid() unless
4625 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4626 Also check processes not considered by the CRTL waitpid().
4628 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4630 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4637 if (statusp) *statusp = 0;
4639 for (info = open_pipes; info != NULL; info = info->next)
4640 if (info->pid == pid) break;
4642 if (info != NULL) { /* we know about this child */
4643 while (!info->done) {
4644 _ckvmssts(sys$setast(0));
4646 if (!done) _ckvmssts(sys$clref(pipe_ef));
4647 _ckvmssts(sys$setast(1));
4648 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4651 if (statusp) *statusp = info->completion;
4655 /* child that already terminated? */
4657 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4658 if (closed_list[j].pid == pid) {
4659 if (statusp) *statusp = closed_list[j].completion;
4664 /* fall through if this child is not one of our own pipe children */
4666 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4668 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4669 * in 7.2 did we get a version that fills in the VMS completion
4670 * status as Perl has always tried to do.
4673 sts = __vms_waitpid( pid, statusp, flags );
4675 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4678 /* If the real waitpid tells us the child does not exist, we
4679 * fall through here to implement waiting for a child that
4680 * was created by some means other than exec() (say, spawned
4681 * from DCL) or to wait for a process that is not a subprocess
4682 * of the current process.
4685 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4688 $DESCRIPTOR(intdsc,"0 00:00:01");
4689 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4690 unsigned long int pidcode = JPI$_PID, mypid;
4691 unsigned long int interval[2];
4692 unsigned int jpi_iosb[2];
4693 struct itmlst_3 jpilist[2] = {
4694 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4699 /* Sorry folks, we don't presently implement rooting around for
4700 the first child we can find, and we definitely don't want to
4701 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4707 /* Get the owner of the child so I can warn if it's not mine. If the
4708 * process doesn't exist or I don't have the privs to look at it,
4709 * I can go home early.
4711 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4712 if (sts & 1) sts = jpi_iosb[0];
4724 set_vaxc_errno(sts);
4728 if (ckWARN(WARN_EXEC)) {
4729 /* remind folks they are asking for non-standard waitpid behavior */
4730 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4731 if (ownerpid != mypid)
4732 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4733 "waitpid: process %x is not a child of process %x",
4737 /* simply check on it once a second until it's not there anymore. */
4739 _ckvmssts(sys$bintim(&intdsc,interval));
4740 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4741 _ckvmssts(sys$schdwk(0,0,interval,0));
4742 _ckvmssts(sys$hiber());
4744 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4749 } /* end of waitpid() */
4754 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4756 my_gconvert(double val, int ndig, int trail, char *buf)
4758 static char __gcvtbuf[DBL_DIG+1];
4761 loc = buf ? buf : __gcvtbuf;
4764 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4765 return gcvt(val,ndig,loc);
4768 loc[0] = '0'; loc[1] = '\0';
4775 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4776 static int rms_free_search_context(struct FAB * fab)
4780 nam = fab->fab$l_nam;
4781 nam->nam$b_nop |= NAM$M_SYNCHK;
4782 nam->nam$l_rlf = NULL;
4784 return sys$parse(fab, NULL, NULL);
4787 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4788 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4789 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4790 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4791 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4792 #define rms_nam_esll(nam) nam.nam$b_esl
4793 #define rms_nam_esl(nam) nam.nam$b_esl
4794 #define rms_nam_name(nam) nam.nam$l_name
4795 #define rms_nam_namel(nam) nam.nam$l_name
4796 #define rms_nam_type(nam) nam.nam$l_type
4797 #define rms_nam_typel(nam) nam.nam$l_type
4798 #define rms_nam_ver(nam) nam.nam$l_ver
4799 #define rms_nam_verl(nam) nam.nam$l_ver
4800 #define rms_nam_rsll(nam) nam.nam$b_rsl
4801 #define rms_nam_rsl(nam) nam.nam$b_rsl
4802 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4803 #define rms_set_fna(fab, nam, name, size) \
4804 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4805 #define rms_get_fna(fab, nam) fab.fab$l_fna
4806 #define rms_set_dna(fab, nam, name, size) \
4807 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4808 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4809 #define rms_set_esa(nam, name, size) \
4810 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4811 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4812 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4813 #define rms_set_rsa(nam, name, size) \
4814 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4815 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4816 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4817 #define rms_nam_name_type_l_size(nam) \
4818 (nam.nam$b_name + nam.nam$b_type)
4820 static int rms_free_search_context(struct FAB * fab)
4824 nam = fab->fab$l_naml;
4825 nam->naml$b_nop |= NAM$M_SYNCHK;
4826 nam->naml$l_rlf = NULL;
4827 nam->naml$l_long_defname_size = 0;
4830 return sys$parse(fab, NULL, NULL);
4833 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4834 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4835 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4836 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4837 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4838 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4839 #define rms_nam_esl(nam) nam.naml$b_esl
4840 #define rms_nam_name(nam) nam.naml$l_name
4841 #define rms_nam_namel(nam) nam.naml$l_long_name
4842 #define rms_nam_type(nam) nam.naml$l_type
4843 #define rms_nam_typel(nam) nam.naml$l_long_type
4844 #define rms_nam_ver(nam) nam.naml$l_ver
4845 #define rms_nam_verl(nam) nam.naml$l_long_ver
4846 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4847 #define rms_nam_rsl(nam) nam.naml$b_rsl
4848 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4849 #define rms_set_fna(fab, nam, name, size) \
4850 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4851 nam.naml$l_long_filename_size = size; \
4852 nam.naml$l_long_filename = name;}
4853 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4854 #define rms_set_dna(fab, nam, name, size) \
4855 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4856 nam.naml$l_long_defname_size = size; \
4857 nam.naml$l_long_defname = name; }
4858 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4859 #define rms_set_esa(nam, name, size) \
4860 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4861 nam.naml$l_long_expand_alloc = size; \
4862 nam.naml$l_long_expand = name; }
4863 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4864 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4865 nam.naml$l_long_expand = l_name; \
4866 nam.naml$l_long_expand_alloc = l_size; }
4867 #define rms_set_rsa(nam, name, size) \
4868 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4869 nam.naml$l_long_result = name; \
4870 nam.naml$l_long_result_alloc = size; }
4871 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4872 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4873 nam.naml$l_long_result = l_name; \
4874 nam.naml$l_long_result_alloc = l_size; }
4875 #define rms_nam_name_type_l_size(nam) \
4876 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4881 * The CRTL for 8.3 and later can create symbolic links in any mode,
4882 * however in 8.3 the unlink/remove/delete routines will only properly handle
4883 * them if one of the PCP modes is active.
4885 static int rms_erase(const char * vmsname)
4888 struct FAB myfab = cc$rms_fab;
4889 rms_setup_nam(mynam);
4891 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4892 rms_bind_fab_nam(myfab, mynam);
4894 #ifdef NAML$M_OPEN_SPECIAL
4895 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
4898 status = sys$erase(&myfab, 0, 0);
4905 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
4906 const struct dsc$descriptor_s * vms_dst_dsc,
4907 unsigned long flags)
4909 /* VMS and UNIX handle file permissions differently and the
4910 * the same ACL trick may be needed for renaming files,
4911 * especially if they are directories.
4914 /* todo: get kill_file and rename to share common code */
4915 /* I can not find online documentation for $change_acl
4916 * it appears to be replaced by $set_security some time ago */
4918 const unsigned int access_mode = 0;
4919 $DESCRIPTOR(obj_file_dsc,"FILE");
4922 unsigned long int jpicode = JPI$_UIC;
4923 int aclsts, fndsts, rnsts = -1;
4924 unsigned int ctx = 0;
4925 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4926 struct dsc$descriptor_s * clean_dsc;
4929 unsigned char myace$b_length;
4930 unsigned char myace$b_type;
4931 unsigned short int myace$w_flags;
4932 unsigned long int myace$l_access;
4933 unsigned long int myace$l_ident;
4934 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
4935 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
4937 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
4940 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
4941 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
4943 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
4944 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
4948 /* Expand the input spec using RMS, since we do not want to put
4949 * ACLs on the target of a symbolic link */
4950 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
4951 if (vmsname == NULL)
4954 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
4956 PERL_RMSEXPAND_M_SYMLINK);
4958 PerlMem_free(vmsname);
4962 /* So we get our own UIC to use as a rights identifier,
4963 * and the insert an ACE at the head of the ACL which allows us
4964 * to delete the file.
4966 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
4968 fildsc.dsc$w_length = strlen(vmsname);
4969 fildsc.dsc$a_pointer = vmsname;
4971 newace.myace$l_ident = oldace.myace$l_ident;
4974 /* Grab any existing ACEs with this identifier in case we fail */
4975 clean_dsc = &fildsc;
4976 aclsts = fndsts = sys$get_security(&obj_file_dsc,
4984 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
4985 /* Add the new ACE . . . */
4987 /* if the sys$get_security succeeded, then ctx is valid, and the
4988 * object/file descriptors will be ignored. But otherwise they
4991 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
4992 OSS$M_RELCTX, addlst, &ctx, &access_mode);
4993 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
4995 set_vaxc_errno(aclsts);
4996 PerlMem_free(vmsname);
5000 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5003 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5005 if ($VMS_STATUS_SUCCESS(rnsts)) {
5006 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5009 /* Put things back the way they were. */
5011 aclsts = sys$get_security(&obj_file_dsc,
5019 if ($VMS_STATUS_SUCCESS(aclsts)) {
5023 if (!$VMS_STATUS_SUCCESS(fndsts))
5024 sec_flags = OSS$M_RELCTX;
5026 /* Get rid of the new ACE */
5027 aclsts = sys$set_security(NULL, NULL, NULL,
5028 sec_flags, dellst, &ctx, &access_mode);
5030 /* If there was an old ACE, put it back */
5031 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5032 addlst[0].bufadr = &oldace;
5033 aclsts = sys$set_security(NULL, NULL, NULL,
5034 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5035 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5037 set_vaxc_errno(aclsts);
5043 /* Try to clear the lock on the ACL list */
5044 aclsts2 = sys$set_security(NULL, NULL, NULL,
5045 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5047 /* Rename errors are most important */
5048 if (!$VMS_STATUS_SUCCESS(rnsts))
5051 set_vaxc_errno(aclsts);
5056 if (aclsts != SS$_ACLEMPTY)
5063 PerlMem_free(vmsname);
5068 /*{{{int rename(const char *, const char * */
5069 /* Not exactly what X/Open says to do, but doing it absolutely right
5070 * and efficiently would require a lot more work. This should be close
5071 * enough to pass all but the most strict X/Open compliance test.
5074 Perl_rename(pTHX_ const char *src, const char * dst)
5083 /* Validate the source file */
5084 src_sts = flex_lstat(src, &src_st);
5087 /* No source file or other problem */
5090 if (src_st.st_devnam[0] == 0) {
5091 /* This may be possible so fail if it is seen. */
5096 dst_sts = flex_lstat(dst, &dst_st);
5099 if (dst_st.st_dev != src_st.st_dev) {
5100 /* Must be on the same device */
5105 /* VMS_INO_T_COMPARE is true if the inodes are different
5106 * to match the output of memcmp
5109 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5110 /* That was easy, the files are the same! */
5114 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5115 /* If source is a directory, so must be dest */
5123 if ((dst_sts == 0) &&
5124 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5126 /* We have issues here if vms_unlink_all_versions is set
5127 * If the destination exists, and is not a directory, then
5128 * we must delete in advance.
5130 * If the src is a directory, then we must always pre-delete
5133 * If we successfully delete the dst in advance, and the rename fails
5134 * X/Open requires that errno be EIO.
5138 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5140 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
5141 S_ISDIR(dst_st.st_mode));
5143 /* Need to delete all versions ? */
5144 if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5147 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
5148 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
5153 /* Make sure that we do not loop forever */
5165 /* We killed the destination, so only errno now is EIO */
5170 /* Originally the idea was to call the CRTL rename() and only
5171 * try the lib$rename_file if it failed.
5172 * It turns out that there are too many variants in what the
5173 * the CRTL rename might do, so only use lib$rename_file
5178 /* Is the source and dest both in VMS format */
5179 /* if the source is a directory, then need to fileify */
5180 /* and dest must be a directory or non-existent. */
5185 unsigned long flags;
5186 struct dsc$descriptor_s old_file_dsc;
5187 struct dsc$descriptor_s new_file_dsc;
5189 /* We need to modify the src and dst depending
5190 * on if one or more of them are directories.
5193 vms_dst = (char *)PerlMem_malloc(VMS_MAXRSS);
5194 if (vms_dst == NULL)
5195 _ckvmssts_noperl(SS$_INSFMEM);
5197 if (S_ISDIR(src_st.st_mode)) {
5199 char * vms_dir_file;
5201 vms_dir_file = (char *)PerlMem_malloc(VMS_MAXRSS);
5202 if (vms_dir_file == NULL)
5203 _ckvmssts_noperl(SS$_INSFMEM);
5205 /* If the dest is a directory, we must remove it */
5208 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
5210 PerlMem_free(vms_dst);
5218 /* The dest must be a VMS file specification */
5219 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5220 if (ret_str == NULL) {
5221 PerlMem_free(vms_dst);
5226 /* The source must be a file specification */
5227 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5228 if (ret_str == NULL) {
5229 PerlMem_free(vms_dst);
5230 PerlMem_free(vms_dir_file);
5234 PerlMem_free(vms_dst);
5235 vms_dst = vms_dir_file;
5238 /* File to file or file to new dir */
5240 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5241 /* VMS pathify a dir target */
5242 ret_str = int_tovmspath(dst, vms_dst, NULL);
5243 if (ret_str == NULL) {
5244 PerlMem_free(vms_dst);
5249 char * v_spec, * r_spec, * d_spec, * n_spec;
5250 char * e_spec, * vs_spec;
5251 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5253 /* fileify a target VMS file specification */
5254 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5255 if (ret_str == NULL) {
5256 PerlMem_free(vms_dst);
5261 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5262 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5263 &e_len, &vs_spec, &vs_len);
5266 /* Get rid of the version */
5270 /* Need to specify a '.' so that the extension */
5271 /* is not inherited */
5272 strcat(vms_dst,".");
5278 old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5279 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
5280 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5281 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5283 new_file_dsc.dsc$a_pointer = vms_dst;
5284 new_file_dsc.dsc$w_length = strlen(vms_dst);
5285 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5286 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5289 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5290 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5293 sts = lib$rename_file(&old_file_dsc,
5297 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5298 if (!$VMS_STATUS_SUCCESS(sts)) {
5300 /* We could have failed because VMS style permissions do not
5301 * permit renames that UNIX will allow. Just like the hack
5304 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5307 PerlMem_free(vms_dst);
5308 if (!$VMS_STATUS_SUCCESS(sts)) {
5315 if (vms_unlink_all_versions) {
5316 /* Now get rid of any previous versions of the source file that
5322 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5323 S_ISDIR(src_st.st_mode));
5324 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
5325 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5326 S_ISDIR(src_st.st_mode));
5331 /* Make sure that we do not loop forever */
5340 /* We deleted the destination, so must force the error to be EIO */
5341 if ((retval != 0) && (pre_delete != 0))
5349 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5350 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5351 * to expand file specification. Allows for a single default file
5352 * specification and a simple mask of options. If outbuf is non-NULL,
5353 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5354 * the resultant file specification is placed. If outbuf is NULL, the
5355 * resultant file specification is placed into a static buffer.
5356 * The third argument, if non-NULL, is taken to be a default file
5357 * specification string. The fourth argument is unused at present.
5358 * rmesexpand() returns the address of the resultant string if
5359 * successful, and NULL on error.
5361 * New functionality for previously unused opts value:
5362 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5363 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5364 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5365 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5367 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5371 (const char *filespec,
5373 const char *defspec,
5379 const char * in_spec;
5381 const char * def_spec;
5382 char * vmsfspec, *vmsdefspec;
5386 struct FAB myfab = cc$rms_fab;
5387 rms_setup_nam(mynam);
5389 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5392 /* temp hack until UTF8 is actually implemented */
5393 if (fs_utf8 != NULL)
5396 if (!filespec || !*filespec) {
5397 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5407 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5408 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5409 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5411 /* If this is a UNIX file spec, convert it to VMS */
5412 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5413 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5414 &e_len, &vs_spec, &vs_len);
5419 vmsfspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5420 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5421 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5422 if (ret_spec == NULL) {
5423 PerlMem_free(vmsfspec);
5426 in_spec = (const char *)vmsfspec;
5428 /* Unless we are forcing to VMS format, a UNIX input means
5429 * UNIX output, and that requires long names to be used
5431 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5432 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5433 opts |= PERL_RMSEXPAND_M_LONG;
5443 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5444 rms_bind_fab_nam(myfab, mynam);
5446 /* Process the default file specification if present */
5448 if (defspec && *defspec) {
5450 t_isunix = is_unix_filespec(defspec);
5452 vmsdefspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5453 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5454 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5456 if (ret_spec == NULL) {
5457 /* Clean up and bail */
5458 PerlMem_free(vmsdefspec);
5459 if (vmsfspec != NULL)
5460 PerlMem_free(vmsfspec);
5463 def_spec = (const char *)vmsdefspec;
5465 rms_set_dna(myfab, mynam,
5466 (char *)def_spec, strlen(def_spec)); /* cast ok */
5469 /* Now we need the expansion buffers */
5470 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
5471 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5472 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5473 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
5474 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5476 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5478 /* If a NAML block is used RMS always writes to the long and short
5479 * addresses unless you suppress the short name.
5481 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5482 outbufl = (char *)PerlMem_malloc(VMS_MAXRSS);
5483 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5485 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5487 #ifdef NAM$M_NO_SHORT_UPCASE
5488 if (decc_efs_case_preserve)
5489 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5492 /* We may not want to follow symbolic links */
5493 #ifdef NAML$M_OPEN_SPECIAL
5494 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5495 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5498 /* First attempt to parse as an existing file */
5499 retsts = sys$parse(&myfab,0,0);
5500 if (!(retsts & STS$K_SUCCESS)) {
5502 /* Could not find the file, try as syntax only if error is not fatal */
5503 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5504 if (retsts == RMS$_DNF ||
5505 retsts == RMS$_DIR ||
5506 retsts == RMS$_DEV ||
5507 retsts == RMS$_PRV) {
5508 retsts = sys$parse(&myfab,0,0);
5509 if (retsts & STS$K_SUCCESS) goto int_expanded;
5512 /* Still could not parse the file specification */
5513 /*----------------------------------------------*/
5514 sts = rms_free_search_context(&myfab); /* Free search context */
5515 if (vmsdefspec != NULL)
5516 PerlMem_free(vmsdefspec);
5517 if (vmsfspec != NULL)
5518 PerlMem_free(vmsfspec);
5519 if (outbufl != NULL)
5520 PerlMem_free(outbufl);
5524 set_vaxc_errno(retsts);
5525 if (retsts == RMS$_PRV) set_errno(EACCES);
5526 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5527 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5528 else set_errno(EVMSERR);
5531 retsts = sys$search(&myfab,0,0);
5532 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5533 sts = rms_free_search_context(&myfab); /* Free search context */
5534 if (vmsdefspec != NULL)
5535 PerlMem_free(vmsdefspec);
5536 if (vmsfspec != NULL)
5537 PerlMem_free(vmsfspec);
5538 if (outbufl != NULL)
5539 PerlMem_free(outbufl);
5543 set_vaxc_errno(retsts);
5544 if (retsts == RMS$_PRV) set_errno(EACCES);
5545 else set_errno(EVMSERR);
5549 /* If the input filespec contained any lowercase characters,
5550 * downcase the result for compatibility with Unix-minded code. */
5552 if (!decc_efs_case_preserve) {
5554 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5555 if (islower(*tbuf)) { haslower = 1; break; }
5558 /* Is a long or a short name expected */
5559 /*------------------------------------*/
5561 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5562 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5563 if (rms_nam_rsll(mynam)) {
5565 speclen = rms_nam_rsll(mynam);
5568 spec_buf = esal; /* Not esa */
5569 speclen = rms_nam_esll(mynam);
5574 if (rms_nam_rsl(mynam)) {
5576 speclen = rms_nam_rsl(mynam);
5579 spec_buf = esa; /* Not esal */
5580 speclen = rms_nam_esl(mynam);
5582 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5585 spec_buf[speclen] = '\0';
5587 /* Trim off null fields added by $PARSE
5588 * If type > 1 char, must have been specified in original or default spec
5589 * (not true for version; $SEARCH may have added version of existing file).
5591 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5592 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5593 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5594 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5597 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5598 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5600 if (trimver || trimtype) {
5601 if (defspec && *defspec) {
5602 char *defesal = NULL;
5603 char *defesa = NULL;
5604 defesa = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5605 if (defesa != NULL) {
5606 struct FAB deffab = cc$rms_fab;
5607 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5608 defesal = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5609 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5611 rms_setup_nam(defnam);
5613 rms_bind_fab_nam(deffab, defnam);
5617 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5619 /* RMS needs the esa/esal as a work area if wildcards are involved */
5620 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5622 rms_clear_nam_nop(defnam);
5623 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5624 #ifdef NAM$M_NO_SHORT_UPCASE
5625 if (decc_efs_case_preserve)
5626 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5628 #ifdef NAML$M_OPEN_SPECIAL
5629 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5630 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5632 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5634 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5637 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5640 if (defesal != NULL)
5641 PerlMem_free(defesal);
5642 PerlMem_free(defesa);
5644 _ckvmssts_noperl(SS$_INSFMEM);
5648 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5649 if (*(rms_nam_verl(mynam)) != '\"')
5650 speclen = rms_nam_verl(mynam) - spec_buf;
5653 if (*(rms_nam_ver(mynam)) != '\"')
5654 speclen = rms_nam_ver(mynam) - spec_buf;
5658 /* If we didn't already trim version, copy down */
5659 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5660 if (speclen > rms_nam_verl(mynam) - spec_buf)
5662 (rms_nam_typel(mynam),
5663 rms_nam_verl(mynam),
5664 speclen - (rms_nam_verl(mynam) - spec_buf));
5665 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5668 if (speclen > rms_nam_ver(mynam) - spec_buf)
5670 (rms_nam_type(mynam),
5672 speclen - (rms_nam_ver(mynam) - spec_buf));
5673 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5678 /* Done with these copies of the input files */
5679 /*-------------------------------------------*/
5680 if (vmsfspec != NULL)
5681 PerlMem_free(vmsfspec);
5682 if (vmsdefspec != NULL)
5683 PerlMem_free(vmsdefspec);
5685 /* If we just had a directory spec on input, $PARSE "helpfully"
5686 * adds an empty name and type for us */
5687 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5688 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5689 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5690 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5691 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5692 speclen = rms_nam_namel(mynam) - spec_buf;
5697 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5698 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5699 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5700 speclen = rms_nam_name(mynam) - spec_buf;
5703 /* Posix format specifications must have matching quotes */
5704 if (speclen < (VMS_MAXRSS - 1)) {
5705 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5706 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5707 spec_buf[speclen] = '\"';
5712 spec_buf[speclen] = '\0';
5713 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5715 /* Have we been working with an expanded, but not resultant, spec? */
5716 /* Also, convert back to Unix syntax if necessary. */
5720 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5721 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5722 rsl = rms_nam_rsll(mynam);
5726 rsl = rms_nam_rsl(mynam);
5729 /* rsl is not present, it means that spec_buf is either */
5730 /* esa or esal, and needs to be copied to outbuf */
5731 /* convert to Unix if desired */
5733 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5735 /* VMS file specs are not in UTF-8 */
5736 if (fs_utf8 != NULL)
5738 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5743 /* Now spec_buf is either outbuf or outbufl */
5744 /* We need the result into outbuf */
5746 /* If we need this in UNIX, then we need another buffer */
5747 /* to keep things in order */
5749 char * new_src = NULL;
5750 if (spec_buf == outbuf) {
5751 new_src = (char *)PerlMem_malloc(VMS_MAXRSS);
5752 my_strlcpy(new_src, spec_buf, VMS_MAXRSS);
5756 ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5758 PerlMem_free(new_src);
5761 /* VMS file specs are not in UTF-8 */
5762 if (fs_utf8 != NULL)
5765 /* Copy the buffer if needed */
5766 if (outbuf != spec_buf)
5767 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5773 /* Need to clean up the search context */
5774 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5775 sts = rms_free_search_context(&myfab); /* Free search context */
5777 /* Clean up the extra buffers */
5781 if (outbufl != NULL)
5782 PerlMem_free(outbufl);
5784 /* Return the result */
5788 /* Common simple case - Expand an already VMS spec */
5790 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5791 opts |= PERL_RMSEXPAND_M_VMS_IN;
5792 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5795 /* Common simple case - Expand to a VMS spec */
5797 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5798 opts |= PERL_RMSEXPAND_M_VMS;
5799 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5803 /* Entry point used by perl routines */
5806 (pTHX_ const char *filespec,
5809 const char *defspec,
5814 static char __rmsexpand_retbuf[VMS_MAXRSS];
5815 char * expanded, *ret_spec, *ret_buf;
5819 if (ret_buf == NULL) {
5821 Newx(expanded, VMS_MAXRSS, char);
5822 if (expanded == NULL)
5823 _ckvmssts(SS$_INSFMEM);
5826 ret_buf = __rmsexpand_retbuf;
5831 ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5832 opts, fs_utf8, dfs_utf8);
5834 if (ret_spec == NULL) {
5835 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5843 /* External entry points */
5844 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5845 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5846 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5847 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5848 char *Perl_rmsexpand_utf8
5849 (pTHX_ const char *spec, char *buf, const char *def,
5850 unsigned opt, int * fs_utf8, int * dfs_utf8)
5851 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5852 char *Perl_rmsexpand_utf8_ts
5853 (pTHX_ const char *spec, char *buf, const char *def,
5854 unsigned opt, int * fs_utf8, int * dfs_utf8)
5855 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
5859 ** The following routines are provided to make life easier when
5860 ** converting among VMS-style and Unix-style directory specifications.
5861 ** All will take input specifications in either VMS or Unix syntax. On
5862 ** failure, all return NULL. If successful, the routines listed below
5863 ** return a pointer to a buffer containing the appropriately
5864 ** reformatted spec (and, therefore, subsequent calls to that routine
5865 ** will clobber the result), while the routines of the same names with
5866 ** a _ts suffix appended will return a pointer to a mallocd string
5867 ** containing the appropriately reformatted spec.
5868 ** In all cases, only explicit syntax is altered; no check is made that
5869 ** the resulting string is valid or that the directory in question
5872 ** fileify_dirspec() - convert a directory spec into the name of the
5873 ** directory file (i.e. what you can stat() to see if it's a dir).
5874 ** The style (VMS or Unix) of the result is the same as the style
5875 ** of the parameter passed in.
5876 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5877 ** what you prepend to a filename to indicate what directory it's in).
5878 ** The style (VMS or Unix) of the result is the same as the style
5879 ** of the parameter passed in.
5880 ** tounixpath() - convert a directory spec into a Unix-style path.
5881 ** tovmspath() - convert a directory spec into a VMS-style path.
5882 ** tounixspec() - convert any file spec into a Unix-style file spec.
5883 ** tovmsspec() - convert any file spec into a VMS-style spec.
5884 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5886 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
5887 ** Permission is given to distribute this code as part of the Perl
5888 ** standard distribution under the terms of the GNU General Public
5889 ** License or the Perl Artistic License. Copies of each may be
5890 ** found in the Perl standard distribution.
5893 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5895 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
5897 unsigned long int dirlen, retlen, hasfilename = 0;
5898 char *cp1, *cp2, *lastdir;
5899 char *trndir, *vmsdir;
5900 unsigned short int trnlnm_iter_count;
5902 if (utf8_fl != NULL)
5905 if (!dir || !*dir) {
5906 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5908 dirlen = strlen(dir);
5909 while (dirlen && dir[dirlen-1] == '/') --dirlen;
5910 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
5911 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
5918 if (dirlen > (VMS_MAXRSS - 1)) {
5919 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
5922 trndir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5923 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5924 if (!strpbrk(dir+1,"/]>:") &&
5925 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
5926 strcpy(trndir,*dir == '/' ? dir + 1: dir);
5927 trnlnm_iter_count = 0;
5928 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
5929 trnlnm_iter_count++;
5930 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5932 dirlen = strlen(trndir);
5935 memcpy(trndir, dir, dirlen);
5936 trndir[dirlen] = '\0';
5939 /* At this point we are done with *dir and use *trndir which is a
5940 * copy that can be modified. *dir must not be modified.
5943 /* If we were handed a rooted logical name or spec, treat it like a
5944 * simple directory, so that
5945 * $ Define myroot dev:[dir.]
5946 * ... do_fileify_dirspec("myroot",buf,1) ...
5947 * does something useful.
5949 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
5950 trndir[--dirlen] = '\0';
5951 trndir[dirlen-1] = ']';
5953 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
5954 trndir[--dirlen] = '\0';
5955 trndir[dirlen-1] = '>';
5958 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
5959 /* If we've got an explicit filename, we can just shuffle the string. */
5960 if (*(cp1+1)) hasfilename = 1;
5961 /* Similarly, we can just back up a level if we've got multiple levels
5962 of explicit directories in a VMS spec which ends with directories. */
5964 for (cp2 = cp1; cp2 > trndir; cp2--) {
5966 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
5967 /* fix-me, can not scan EFS file specs backward like this */
5968 *cp2 = *cp1; *cp1 = '\0';
5973 if (*cp2 == '[' || *cp2 == '<') break;
5978 vmsdir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5979 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5980 cp1 = strpbrk(trndir,"]:>");
5981 if (cp1 && *(cp1+1) == ':') /* DECNet node spec with :: */
5982 cp1 = strpbrk(cp1+2,"]:>");
5984 if (hasfilename || !cp1) { /* filename present or not VMS */
5986 if (trndir[0] == '.') {
5987 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
5988 PerlMem_free(trndir);
5989 PerlMem_free(vmsdir);
5990 return int_fileify_dirspec("[]", buf, NULL);
5992 else if (trndir[1] == '.' &&
5993 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
5994 PerlMem_free(trndir);
5995 PerlMem_free(vmsdir);
5996 return int_fileify_dirspec("[-]", buf, NULL);
5999 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
6000 dirlen -= 1; /* to last element */
6001 lastdir = strrchr(trndir,'/');
6003 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
6004 /* If we have "/." or "/..", VMSify it and let the VMS code
6005 * below expand it, rather than repeating the code to handle
6006 * relative components of a filespec here */
6008 if (*(cp1+2) == '.') cp1++;
6009 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6011 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6012 PerlMem_free(trndir);
6013 PerlMem_free(vmsdir);
6016 if (strchr(vmsdir,'/') != NULL) {
6017 /* If int_tovmsspec() returned it, it must have VMS syntax
6018 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6019 * the time to check this here only so we avoid a recursion
6020 * loop; otherwise, gigo.
6022 PerlMem_free(trndir);
6023 PerlMem_free(vmsdir);
6024 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6027 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6028 PerlMem_free(trndir);
6029 PerlMem_free(vmsdir);
6032 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6033 PerlMem_free(trndir);
6034 PerlMem_free(vmsdir);
6038 } while ((cp1 = strstr(cp1,"/.")) != NULL);
6039 lastdir = strrchr(trndir,'/');
6041 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6043 /* Ditto for specs that end in an MFD -- let the VMS code
6044 * figure out whether it's a real device or a rooted logical. */
6046 /* This should not happen any more. Allowing the fake /000000
6047 * in a UNIX pathname causes all sorts of problems when trying
6048 * to run in UNIX emulation. So the VMS to UNIX conversions
6049 * now remove the fake /000000 directories.
6052 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6053 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6054 PerlMem_free(trndir);
6055 PerlMem_free(vmsdir);
6058 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6059 PerlMem_free(trndir);
6060 PerlMem_free(vmsdir);
6063 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6064 PerlMem_free(trndir);
6065 PerlMem_free(vmsdir);
6070 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6071 !(lastdir = cp1 = strrchr(trndir,']')) &&
6072 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6074 cp2 = strrchr(cp1,'.');
6076 int e_len, vs_len = 0;
6079 cp3 = strchr(cp2,';');
6080 e_len = strlen(cp2);
6082 vs_len = strlen(cp3);
6083 e_len = e_len - vs_len;
6085 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6087 if (!decc_efs_charset) {
6088 /* If this is not EFS, then not a directory */
6089 PerlMem_free(trndir);
6090 PerlMem_free(vmsdir);
6092 set_vaxc_errno(RMS$_DIR);
6096 /* Ok, here we have an issue, technically if a .dir shows */
6097 /* from inside a directory, then we should treat it as */
6098 /* xxx^.dir.dir. But we do not have that context at this */
6099 /* point unless this is totally restructured, so we remove */
6100 /* The .dir for now, and fix this better later */
6101 dirlen = cp2 - trndir;
6103 if (decc_efs_charset && !strchr(trndir,'/')) {
6104 /* Dots are allowed in dir names, so escape them if input not in Unix syntax. */
6105 char *cp4 = is_dir ? (cp2 - 1) : cp2;
6107 for (; cp4 > cp1; cp4--) {
6109 if ((cp4 - 1 > trndir) && (*(cp4 - 1) != '^')) {
6110 memmove(cp4 + 1, cp4, trndir + dirlen - cp4 + 1);
6121 retlen = dirlen + 6;
6122 memcpy(buf, trndir, dirlen);
6125 /* We've picked up everything up to the directory file name.
6126 Now just add the type and version, and we're set. */
6127 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
6131 if (!decc_filename_unix_no_version)
6133 PerlMem_free(trndir);
6134 PerlMem_free(vmsdir);
6137 else { /* VMS-style directory spec */
6139 char *esa, *esal, term, *cp;
6142 unsigned long int cmplen, haslower = 0;
6143 struct FAB dirfab = cc$rms_fab;
6144 rms_setup_nam(savnam);
6145 rms_setup_nam(dirnam);
6147 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
6148 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6150 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6151 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
6152 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6154 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6155 rms_bind_fab_nam(dirfab, dirnam);
6156 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6157 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6158 #ifdef NAM$M_NO_SHORT_UPCASE
6159 if (decc_efs_case_preserve)
6160 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6163 for (cp = trndir; *cp; cp++)
6164 if (islower(*cp)) { haslower = 1; break; }
6165 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6166 if ((dirfab.fab$l_sts == RMS$_DIR) ||
6167 (dirfab.fab$l_sts == RMS$_DNF) ||
6168 (dirfab.fab$l_sts == RMS$_PRV)) {
6169 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6170 sts = sys$parse(&dirfab);
6176 PerlMem_free(trndir);
6177 PerlMem_free(vmsdir);
6179 set_vaxc_errno(dirfab.fab$l_sts);
6185 /* Does the file really exist? */
6186 if (sys$search(&dirfab)& STS$K_SUCCESS) {
6187 /* Yes; fake the fnb bits so we'll check type below */
6188 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6190 else { /* No; just work with potential name */
6191 if (dirfab.fab$l_sts == RMS$_FNF
6192 || dirfab.fab$l_sts == RMS$_DNF
6193 || dirfab.fab$l_sts == RMS$_FND)
6197 fab_sts = dirfab.fab$l_sts;
6198 sts = rms_free_search_context(&dirfab);
6202 PerlMem_free(trndir);
6203 PerlMem_free(vmsdir);
6204 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
6210 /* Make sure we are using the right buffer */
6211 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6214 my_esa_len = rms_nam_esll(dirnam);
6218 my_esa_len = rms_nam_esl(dirnam);
6219 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6222 my_esa[my_esa_len] = '\0';
6223 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6224 cp1 = strchr(my_esa,']');
6225 if (!cp1) cp1 = strchr(my_esa,'>');
6226 if (cp1) { /* Should always be true */
6227 my_esa_len -= cp1 - my_esa - 1;
6228 memmove(my_esa, cp1 + 1, my_esa_len);
6231 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6232 /* Yep; check version while we're at it, if it's there. */
6233 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6234 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6235 /* Something other than .DIR[;1]. Bzzt. */
6236 sts = rms_free_search_context(&dirfab);
6240 PerlMem_free(trndir);
6241 PerlMem_free(vmsdir);
6243 set_vaxc_errno(RMS$_DIR);
6248 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6249 /* They provided at least the name; we added the type, if necessary, */
6250 my_strlcpy(buf, my_esa, VMS_MAXRSS);
6251 sts = rms_free_search_context(&dirfab);
6252 PerlMem_free(trndir);
6256 PerlMem_free(vmsdir);
6259 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6260 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6264 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6265 if (cp1 == NULL) { /* should never happen */
6266 sts = rms_free_search_context(&dirfab);
6267 PerlMem_free(trndir);
6271 PerlMem_free(vmsdir);
6276 retlen = strlen(my_esa);
6277 cp1 = strrchr(my_esa,'.');
6278 /* ODS-5 directory specifications can have extra "." in them. */
6279 /* Fix-me, can not scan EFS file specifications backwards */
6280 while (cp1 != NULL) {
6281 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6285 while ((cp1 > my_esa) && (*cp1 != '.'))
6292 if ((cp1) != NULL) {
6293 /* There's more than one directory in the path. Just roll back. */
6295 my_strlcpy(buf, my_esa, VMS_MAXRSS);
6298 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6299 /* Go back and expand rooted logical name */
6300 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6301 #ifdef NAM$M_NO_SHORT_UPCASE
6302 if (decc_efs_case_preserve)
6303 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6305 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6306 sts = rms_free_search_context(&dirfab);
6310 PerlMem_free(trndir);
6311 PerlMem_free(vmsdir);
6313 set_vaxc_errno(dirfab.fab$l_sts);
6317 /* This changes the length of the string of course */
6319 my_esa_len = rms_nam_esll(dirnam);
6321 my_esa_len = rms_nam_esl(dirnam);
6324 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6325 cp1 = strstr(my_esa,"][");
6326 if (!cp1) cp1 = strstr(my_esa,"]<");
6327 dirlen = cp1 - my_esa;
6328 memcpy(buf, my_esa, dirlen);
6329 if (!strncmp(cp1+2,"000000]",7)) {
6330 buf[dirlen-1] = '\0';
6331 /* fix-me Not full ODS-5, just extra dots in directories for now */
6332 cp1 = buf + dirlen - 1;
6338 if (*(cp1-1) != '^')
6343 if (*cp1 == '.') *cp1 = ']';
6345 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6346 memmove(cp1+1,"000000]",7);
6350 memmove(buf+dirlen, cp1+2, retlen-dirlen);
6352 /* Convert last '.' to ']' */
6354 while (*cp != '[') {
6357 /* Do not trip on extra dots in ODS-5 directories */
6358 if ((cp1 == buf) || (*(cp1-1) != '^'))
6362 if (*cp1 == '.') *cp1 = ']';
6364 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6365 memmove(cp1+1,"000000]",7);
6369 else { /* This is a top-level dir. Add the MFD to the path. */
6370 cp1 = strrchr(my_esa, ':');
6372 memmove(buf, my_esa, cp1 - my_esa + 1);
6373 memmove(buf + (cp1 - my_esa) + 1, "[000000]", 8);
6374 memmove(buf + (cp1 - my_esa) + 9, cp1 + 2, retlen - (cp1 - my_esa + 2));
6375 buf[retlen + 7] = '\0'; /* We've inserted '000000]' */
6378 sts = rms_free_search_context(&dirfab);
6379 /* We've set up the string up through the filename. Add the
6380 type and version, and we're done. */
6381 strcat(buf,".DIR;1");
6383 /* $PARSE may have upcased filespec, so convert output to lower
6384 * case if input contained any lowercase characters. */
6385 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
6386 PerlMem_free(trndir);
6390 PerlMem_free(vmsdir);
6393 } /* end of int_fileify_dirspec() */
6396 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6397 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6399 static char __fileify_retbuf[VMS_MAXRSS];
6400 char * fileified, *ret_spec, *ret_buf;
6404 if (ret_buf == NULL) {
6406 Newx(fileified, VMS_MAXRSS, char);
6407 if (fileified == NULL)
6408 _ckvmssts(SS$_INSFMEM);
6409 ret_buf = fileified;
6411 ret_buf = __fileify_retbuf;
6415 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6417 if (ret_spec == NULL) {
6418 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6420 Safefree(fileified);
6424 } /* end of do_fileify_dirspec() */
6427 /* External entry points */
6428 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6429 { return do_fileify_dirspec(dir,buf,0,NULL); }
6430 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6431 { return do_fileify_dirspec(dir,buf,1,NULL); }
6432 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6433 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6434 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6435 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6437 static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6438 char * v_spec, int v_len, char * r_spec, int r_len,
6439 char * d_spec, int d_len, char * n_spec, int n_len,
6440 char * e_spec, int e_len, char * vs_spec, int vs_len) {
6442 /* VMS specification - Try to do this the simple way */
6443 if ((v_len + r_len > 0) || (d_len > 0)) {
6446 /* No name or extension component, already a directory */
6447 if ((n_len + e_len + vs_len) == 0) {
6452 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6453 /* This results from catfile() being used instead of catdir() */
6454 /* So even though it should not work, we need to allow it */
6456 /* If this is .DIR;1 then do a simple conversion */
6457 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6458 if (is_dir || (e_len == 0) && (d_len > 0)) {
6460 len = v_len + r_len + d_len - 1;
6461 char dclose = d_spec[d_len - 1];
6462 memcpy(buf, dir, len);
6465 memcpy(&buf[len], n_spec, n_len);
6468 buf[len + 1] = '\0';
6473 else if (d_len > 0) {
6474 /* In the olden days, a directory needed to have a .DIR */
6475 /* extension to be a valid directory, but now it could */
6476 /* be a symbolic link */
6478 len = v_len + r_len + d_len - 1;
6479 char dclose = d_spec[d_len - 1];
6480 memcpy(buf, dir, len);
6483 memcpy(&buf[len], n_spec, n_len);
6486 if (decc_efs_charset) {
6488 && (toupper(e_spec[1]) == 'D')
6489 && (toupper(e_spec[2]) == 'I')
6490 && (toupper(e_spec[3]) == 'R')) {
6492 /* Corner case: directory spec with invalid version.
6493 * Valid would have followed is_dir path above.
6495 SETERRNO(ENOTDIR, RMS$_DIR);
6501 memcpy(&buf[len], e_spec, e_len);
6506 SETERRNO(ENOTDIR, RMS$_DIR);
6511 buf[len + 1] = '\0';
6516 set_vaxc_errno(RMS$_DIR);
6522 set_vaxc_errno(RMS$_DIR);
6528 /* Internal routine to make sure or convert a directory to be in a */
6529 /* path specification. No utf8 flag because it is not changed or used */
6530 static char *int_pathify_dirspec(const char *dir, char *buf)
6532 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6533 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6534 char * exp_spec, *ret_spec;
6536 unsigned short int trnlnm_iter_count;
6540 if (vms_debug_fileify) {
6542 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6544 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6547 /* We may need to lower case the result if we translated */
6548 /* a logical name or got the current working directory */
6551 if (!dir || !*dir) {
6553 set_vaxc_errno(SS$_BADPARAM);
6557 trndir = (char *)PerlMem_malloc(VMS_MAXRSS);
6559 _ckvmssts_noperl(SS$_INSFMEM);
6561 /* If no directory specified use the current default */
6563 my_strlcpy(trndir, dir, VMS_MAXRSS);
6565 getcwd(trndir, VMS_MAXRSS - 1);
6569 /* now deal with bare names that could be logical names */
6570 trnlnm_iter_count = 0;
6571 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6572 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6573 trnlnm_iter_count++;
6575 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6577 trnlen = strlen(trndir);
6579 /* Trap simple rooted lnms, and return lnm:[000000] */
6580 if (!strcmp(trndir+trnlen-2,".]")) {
6581 my_strlcpy(buf, dir, VMS_MAXRSS);
6582 strcat(buf, ":[000000]");
6583 PerlMem_free(trndir);
6585 if (vms_debug_fileify) {
6586 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6592 /* At this point we do not work with *dir, but the copy in *trndir */
6594 if (need_to_lower && !decc_efs_case_preserve) {
6595 /* Legacy mode, lower case the returned value */
6596 __mystrtolower(trndir);
6600 /* Some special cases, '..', '.' */
6602 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6603 /* Force UNIX filespec */
6607 /* Is this Unix or VMS format? */
6608 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6609 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6610 &e_len, &vs_spec, &vs_len);
6613 /* Just a filename? */
6614 if ((v_len + r_len + d_len) == 0) {
6616 /* Now we have a problem, this could be Unix or VMS */
6617 /* We have to guess. .DIR usually means VMS */
6619 /* In UNIX report mode, the .DIR extension is removed */
6620 /* if one shows up, it is for a non-directory or a directory */
6621 /* in EFS charset mode */
6623 /* So if we are in Unix report mode, assume that this */
6624 /* is a relative Unix directory specification */
6627 if (!decc_filename_unix_report && decc_efs_charset) {
6629 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6632 /* Traditional mode, assume .DIR is directory */
6635 memcpy(&buf[2], n_spec, n_len);
6636 buf[n_len + 2] = ']';
6637 buf[n_len + 3] = '\0';
6638 PerlMem_free(trndir);
6639 if (vms_debug_fileify) {
6641 "int_pathify_dirspec: buf = %s\n",
6651 ret_spec = int_pathify_dirspec_simple(trndir, buf,
6652 v_spec, v_len, r_spec, r_len,
6653 d_spec, d_len, n_spec, n_len,
6654 e_spec, e_len, vs_spec, vs_len);
6656 if (ret_spec != NULL) {
6657 PerlMem_free(trndir);
6658 if (vms_debug_fileify) {
6660 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6665 /* Simple way did not work, which means that a logical name */
6666 /* was present for the directory specification. */
6667 /* Need to use an rmsexpand variant to decode it completely */
6668 exp_spec = (char *)PerlMem_malloc(VMS_MAXRSS);
6669 if (exp_spec == NULL)
6670 _ckvmssts_noperl(SS$_INSFMEM);
6672 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6673 if (ret_spec != NULL) {
6674 sts = vms_split_path(exp_spec, &v_spec, &v_len,
6675 &r_spec, &r_len, &d_spec, &d_len,
6676 &n_spec, &n_len, &e_spec,
6677 &e_len, &vs_spec, &vs_len);
6679 ret_spec = int_pathify_dirspec_simple(
6680 exp_spec, buf, v_spec, v_len, r_spec, r_len,
6681 d_spec, d_len, n_spec, n_len,
6682 e_spec, e_len, vs_spec, vs_len);
6684 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6685 /* Legacy mode, lower case the returned value */
6686 __mystrtolower(ret_spec);
6689 set_vaxc_errno(RMS$_DIR);
6694 PerlMem_free(exp_spec);
6695 PerlMem_free(trndir);
6696 if (vms_debug_fileify) {
6697 if (ret_spec == NULL)
6698 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6701 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6706 /* Unix specification, Could be trivial conversion, */
6707 /* but have to deal with trailing '.dir' or extra '.' */
6712 STRLEN dir_len = strlen(trndir);
6714 lastslash = strrchr(trndir, '/');
6715 if (lastslash == NULL)
6722 /* '..' or '.' are valid directory components */
6724 if (lastslash[0] == '.') {
6725 if (lastslash[1] == '\0') {
6727 } else if (lastslash[1] == '.') {
6728 if (lastslash[2] == '\0') {
6731 /* And finally allow '...' */
6732 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6740 lastdot = strrchr(lastslash, '.');
6742 if (lastdot != NULL) {
6744 /* '.dir' is discarded, and any other '.' is invalid */
6745 e_len = strlen(lastdot);
6747 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6750 dir_len = dir_len - 4;
6754 my_strlcpy(buf, trndir, VMS_MAXRSS);
6755 if (buf[dir_len - 1] != '/') {
6757 buf[dir_len + 1] = '\0';
6760 /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6761 if (!decc_efs_charset) {
6764 if (str[0] == '.') {
6767 while ((dots[cnt] == '.') && (cnt < 3))
6770 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6776 for (; *str; ++str) {
6777 while (*str == '/') {
6783 /* Have to skip up to three dots which could be */
6784 /* directories, 3 dots being a VMS extension for Perl */
6787 while ((dots[cnt] == '.') && (cnt < 3)) {
6790 if (dots[cnt] == '\0')
6792 if ((cnt > 1) && (dots[cnt] != '/')) {
6798 /* too many dots? */
6799 if ((cnt == 0) || (cnt > 3)) {
6803 if (!dir_start && (*str == '.')) {
6808 PerlMem_free(trndir);
6810 if (vms_debug_fileify) {
6811 if (ret_spec == NULL)
6812 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6815 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6821 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6822 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6824 static char __pathify_retbuf[VMS_MAXRSS];
6825 char * pathified, *ret_spec, *ret_buf;
6829 if (ret_buf == NULL) {
6831 Newx(pathified, VMS_MAXRSS, char);
6832 if (pathified == NULL)
6833 _ckvmssts(SS$_INSFMEM);
6834 ret_buf = pathified;
6836 ret_buf = __pathify_retbuf;
6840 ret_spec = int_pathify_dirspec(dir, ret_buf);
6842 if (ret_spec == NULL) {
6843 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6845 Safefree(pathified);
6850 } /* end of do_pathify_dirspec() */
6853 /* External entry points */
6854 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6855 { return do_pathify_dirspec(dir,buf,0,NULL); }
6856 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
6857 { return do_pathify_dirspec(dir,buf,1,NULL); }
6858 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
6859 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
6860 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
6861 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
6863 /* Internal tounixspec routine that does not use a thread context */
6864 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
6865 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
6867 char *dirend, *cp1, *cp3, *tmp;
6870 unsigned short int trnlnm_iter_count;
6871 int cmp_rslt, outchars_added;
6872 if (utf8_fl != NULL)
6875 if (vms_debug_fileify) {
6877 fprintf(stderr, "int_tounixspec: spec = NULL\n");
6879 fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
6885 set_vaxc_errno(SS$_BADPARAM);
6888 if (strlen(spec) > (VMS_MAXRSS-1)) {
6890 set_vaxc_errno(SS$_BUFFEROVF);
6894 /* New VMS specific format needs translation
6895 * glob passes filenames with trailing '\n' and expects this preserved.
6897 if (decc_posix_compliant_pathnames) {
6898 if (strncmp(spec, "\"^UP^", 5) == 0) {
6904 tunix = (char *)PerlMem_malloc(VMS_MAXRSS);
6905 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6906 tunix_len = my_strlcpy(tunix, spec, VMS_MAXRSS);
6908 if (tunix[tunix_len - 1] == '\n') {
6909 tunix[tunix_len - 1] = '\"';
6910 tunix[tunix_len] = '\0';
6914 uspec = decc$translate_vms(tunix);
6915 PerlMem_free(tunix);
6916 if ((int)uspec > 0) {
6917 my_strlcpy(rslt, uspec, VMS_MAXRSS);
6922 /* If we can not translate it, makemaker wants as-is */
6923 my_strlcpy(rslt, spec, VMS_MAXRSS);
6930 cmp_rslt = 0; /* Presume VMS */
6931 cp1 = strchr(spec, '/');
6935 /* Look for EFS ^/ */
6936 if (decc_efs_charset) {
6937 while (cp1 != NULL) {
6940 /* Found illegal VMS, assume UNIX */
6945 cp1 = strchr(cp1, '/');
6949 /* Look for "." and ".." */
6950 if (decc_filename_unix_report) {
6951 if (spec[0] == '.') {
6952 if ((spec[1] == '\0') || (spec[1] == '\n')) {
6956 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
6966 /* This is already UNIX or at least nothing VMS understands,
6967 * so all we can reasonably do is unescape extended chars.
6971 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
6972 cp1 += outchars_added;
6975 if (vms_debug_fileify) {
6976 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6981 dirend = strrchr(spec,']');
6982 if (dirend == NULL) dirend = strrchr(spec,'>');
6983 if (dirend == NULL) dirend = strchr(spec,':');
6984 if (dirend == NULL) {
6986 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
6987 cp1 += outchars_added;
6990 if (vms_debug_fileify) {
6991 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
6996 /* Special case 1 - sys$posix_root = / */
6997 if (!decc_disable_posix_root) {
6998 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7005 /* Special case 2 - Convert NLA0: to /dev/null */
7006 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7007 if (cmp_rslt == 0) {
7008 strcpy(rslt, "/dev/null");
7011 if (spec[6] != '\0') {
7018 /* Also handle special case "SYS$SCRATCH:" */
7019 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7020 tmp = (char *)PerlMem_malloc(VMS_MAXRSS);
7021 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7022 if (cmp_rslt == 0) {
7025 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7027 strcpy(rslt, "/tmp");
7030 if (spec[12] != '\0') {
7038 if (*cp2 != '[' && *cp2 != '<') {
7041 else { /* the VMS spec begins with directories */
7043 if (*cp2 == ']' || *cp2 == '>') {
7044 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
7048 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7049 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7051 if (vms_debug_fileify) {
7052 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7056 trnlnm_iter_count = 0;
7059 while (*cp3 != ':' && *cp3) cp3++;
7061 if (strchr(cp3,']') != NULL) break;
7062 trnlnm_iter_count++;
7063 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7064 } while (vmstrnenv(tmp,tmp,0,fildev,0));
7069 *(cp1++) = *(cp3++);
7070 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7072 set_errno(ENAMETOOLONG);
7073 set_vaxc_errno(SS$_BUFFEROVF);
7074 if (vms_debug_fileify) {
7075 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7077 return NULL; /* No room */
7082 if ((*cp2 == '^')) {
7083 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7084 cp1 += outchars_added;
7086 else if ( *cp2 == '.') {
7087 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7088 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7095 for (; cp2 <= dirend; cp2++) {
7096 if ((*cp2 == '^')) {
7097 /* EFS file escape -- unescape it. */
7098 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added) - 1;
7099 cp1 += outchars_added;
7101 else if (*cp2 == ':') {
7103 if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7105 else if (*cp2 == ']' || *cp2 == '>') {
7106 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7108 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7110 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7111 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7112 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7113 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7114 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7116 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7117 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7121 else if (*cp2 == '-') {
7122 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7123 while (*cp2 == '-') {
7125 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7127 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7128 /* filespecs like */
7129 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
7130 if (vms_debug_fileify) {
7131 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7136 else *(cp1++) = *cp2;
7138 else *(cp1++) = *cp2;
7140 /* Translate the rest of the filename. */
7144 /* Fixme - for compatibility with the CRTL we should be removing */
7145 /* spaces from the file specifications, but this may show that */
7146 /* some tests that were appearing to pass are not really passing */
7152 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7153 cp1 += outchars_added;
7156 if (decc_filename_unix_no_version) {
7157 /* Easy, drop the version */
7162 /* Punt - passing the version as a dot will probably */
7163 /* break perl in weird ways, but so did passing */
7164 /* through the ; as a version. Follow the CRTL and */
7165 /* hope for the best. */
7172 /* We will need to fix this properly later */
7173 /* As Perl may be installed on an ODS-5 volume, but not */
7174 /* have the EFS_CHARSET enabled, it still may encounter */
7175 /* filenames with extra dots in them, and a precedent got */
7176 /* set which allowed them to work, that we will uphold here */
7177 /* If extra dots are present in a name and no ^ is on them */
7178 /* VMS assumes that the first one is the extension delimiter */
7179 /* the rest have an implied ^. */
7181 /* this is also a conflict as the . is also a version */
7182 /* delimiter in VMS, */
7184 *(cp1++) = *(cp2++);
7188 /* This is an extension */
7189 if (decc_readdir_dropdotnotype) {
7191 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7192 /* Drop the dot for the extension */
7200 *(cp1++) = *(cp2++);
7205 /* This still leaves /000000/ when working with a
7206 * VMS device root or concealed root.
7212 ulen = strlen(rslt);
7214 /* Get rid of "000000/ in rooted filespecs */
7216 zeros = strstr(rslt, "/000000/");
7217 if (zeros != NULL) {
7219 mlen = ulen - (zeros - rslt) - 7;
7220 memmove(zeros, &zeros[7], mlen);
7227 if (vms_debug_fileify) {
7228 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7232 } /* end of int_tounixspec() */
7235 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7236 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7238 static char __tounixspec_retbuf[VMS_MAXRSS];
7239 char * unixspec, *ret_spec, *ret_buf;
7243 if (ret_buf == NULL) {
7245 Newx(unixspec, VMS_MAXRSS, char);
7246 if (unixspec == NULL)
7247 _ckvmssts(SS$_INSFMEM);
7250 ret_buf = __tounixspec_retbuf;
7254 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7256 if (ret_spec == NULL) {
7257 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7264 } /* end of do_tounixspec() */
7266 /* External entry points */
7267 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7268 { return do_tounixspec(spec,buf,0, NULL); }
7269 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7270 { return do_tounixspec(spec,buf,1, NULL); }
7271 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7272 { return do_tounixspec(spec,buf,0, utf8_fl); }
7273 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7274 { return do_tounixspec(spec,buf,1, utf8_fl); }
7276 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7279 This procedure is used to identify if a path is based in either
7280 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7281 it returns the OpenVMS format directory for it.
7283 It is expecting specifications of only '/' or '/xxxx/'
7285 If a posix root does not exist, or 'xxxx' is not a directory
7286 in the posix root, it returns a failure.
7288 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7290 It is used only internally by posix_to_vmsspec_hardway().
7293 static int posix_root_to_vms
7294 (char *vmspath, int vmspath_len,
7295 const char *unixpath,
7296 const int * utf8_fl)
7299 struct FAB myfab = cc$rms_fab;
7300 rms_setup_nam(mynam);
7301 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7302 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7303 char * esa, * esal, * rsa, * rsal;
7309 unixlen = strlen(unixpath);
7314 #if __CRTL_VER >= 80200000
7315 /* If not a posix spec already, convert it */
7316 if (decc_posix_compliant_pathnames) {
7317 if (strncmp(unixpath,"\"^UP^",5) != 0) {
7318 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7321 /* This is already a VMS specification, no conversion */
7323 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7332 /* Check to see if this is under the POSIX root */
7333 if (decc_disable_posix_root) {
7337 /* Skip leading / */
7338 if (unixpath[0] == '/') {
7344 strcpy(vmspath,"SYS$POSIX_ROOT:");
7346 /* If this is only the / , or blank, then... */
7347 if (unixpath[0] == '\0') {
7348 /* by definition, this is the answer */
7352 /* Need to look up a directory */
7356 /* Copy and add '^' escape characters as needed */
7359 while (unixpath[i] != 0) {
7362 j += copy_expand_unix_filename_escape
7363 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7367 path_len = strlen(vmspath);
7368 if (vmspath[path_len - 1] == '/')
7370 vmspath[path_len] = ']';
7372 vmspath[path_len] = '\0';
7375 vmspath[vmspath_len] = 0;
7376 if (unixpath[unixlen - 1] == '/')
7378 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
7379 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7380 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7381 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7382 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
7383 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7384 rsa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7385 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7386 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7387 rms_bind_fab_nam(myfab, mynam);
7388 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7389 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7390 if (decc_efs_case_preserve)
7391 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7392 #ifdef NAML$M_OPEN_SPECIAL
7393 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7396 /* Set up the remaining naml fields */
7397 sts = sys$parse(&myfab);
7399 /* It failed! Try again as a UNIX filespec */
7408 /* get the Device ID and the FID */
7409 sts = sys$search(&myfab);
7411 /* These are no longer needed */
7416 /* on any failure, returned the POSIX ^UP^ filespec */
7421 specdsc.dsc$a_pointer = vmspath;
7422 specdsc.dsc$w_length = vmspath_len;
7424 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7425 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7426 sts = lib$fid_to_name
7427 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7429 /* on any failure, returned the POSIX ^UP^ filespec */
7431 /* This can happen if user does not have permission to read directories */
7432 if (strncmp(unixpath,"\"^UP^",5) != 0)
7433 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7435 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7438 vmspath[specdsc.dsc$w_length] = 0;
7440 /* Are we expecting a directory? */
7441 if (dir_flag != 0) {
7447 i = specdsc.dsc$w_length - 1;
7451 /* Version must be '1' */
7452 if (vmspath[i--] != '1')
7454 /* Version delimiter is one of ".;" */
7455 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7458 if (vmspath[i--] != 'R')
7460 if (vmspath[i--] != 'I')
7462 if (vmspath[i--] != 'D')
7464 if (vmspath[i--] != '.')
7466 eptr = &vmspath[i+1];
7468 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7469 if (vmspath[i-1] != '^') {
7477 /* Get rid of 6 imaginary zero directory filename */
7478 vmspath[i+1] = '\0';
7482 if (vmspath[i] == '0')
7496 /* /dev/mumble needs to be handled special.
7497 /dev/null becomes NLA0:, And there is the potential for other stuff
7498 like /dev/tty which may need to be mapped to something.
7502 slash_dev_special_to_vms
7503 (const char * unixptr,
7512 nextslash = strchr(unixptr, '/');
7513 len = strlen(unixptr);
7514 if (nextslash != NULL)
7515 len = nextslash - unixptr;
7516 cmp = strncmp("null", unixptr, 5);
7518 if (vmspath_len >= 6) {
7519 strcpy(vmspath, "_NLA0:");
7527 /* The built in routines do not understand perl's special needs, so
7528 doing a manual conversion from UNIX to VMS
7530 If the utf8_fl is not null and points to a non-zero value, then
7531 treat 8 bit characters as UTF-8.
7533 The sequence starting with '$(' and ending with ')' will be passed
7534 through with out interpretation instead of being escaped.
7537 static int posix_to_vmsspec_hardway
7538 (char *vmspath, int vmspath_len,
7539 const char *unixpath,
7544 const char *unixptr;
7545 const char *unixend;
7547 const char *lastslash;
7548 const char *lastdot;
7554 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7555 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7557 if (utf8_fl != NULL)
7563 /* Ignore leading "/" characters */
7564 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7567 unixlen = strlen(unixptr);
7569 /* Do nothing with blank paths */
7576 /* This could have a "^UP^ on the front */
7577 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7583 lastslash = strrchr(unixptr,'/');
7584 lastdot = strrchr(unixptr,'.');
7585 unixend = strrchr(unixptr,'\"');
7586 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7587 unixend = unixptr + unixlen;
7590 /* last dot is last dot or past end of string */
7591 if (lastdot == NULL)
7592 lastdot = unixptr + unixlen;
7594 /* if no directories, set last slash to beginning of string */
7595 if (lastslash == NULL) {
7596 lastslash = unixptr;
7599 /* Watch out for trailing "." after last slash, still a directory */
7600 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7601 lastslash = unixptr + unixlen;
7604 /* Watch out for trailing ".." after last slash, still a directory */
7605 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7606 lastslash = unixptr + unixlen;
7609 /* dots in directories are aways escaped */
7610 if (lastdot < lastslash)
7611 lastdot = unixptr + unixlen;
7614 /* if (unixptr < lastslash) then we are in a directory */
7621 /* Start with the UNIX path */
7622 if (*unixptr != '/') {
7623 /* relative paths */
7625 /* If allowing logical names on relative pathnames, then handle here */
7626 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7627 !decc_posix_compliant_pathnames) {
7633 /* Find the next slash */
7634 nextslash = strchr(unixptr,'/');
7636 esa = (char *)PerlMem_malloc(vmspath_len);
7637 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7639 trn = (char *)PerlMem_malloc(VMS_MAXRSS);
7640 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7642 if (nextslash != NULL) {
7644 seg_len = nextslash - unixptr;
7645 memcpy(esa, unixptr, seg_len);
7649 seg_len = my_strlcpy(esa, unixptr, sizeof(esa));
7651 /* trnlnm(section) */
7652 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7655 /* Now fix up the directory */
7657 /* Split up the path to find the components */
7658 sts = vms_split_path
7676 /* A logical name must be a directory or the full
7677 specification. It is only a full specification if
7678 it is the only component */
7679 if ((unixptr[seg_len] == '\0') ||
7680 (unixptr[seg_len+1] == '\0')) {
7682 /* Is a directory being required? */
7683 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7684 /* Not a logical name */
7689 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7690 /* This must be a directory */
7691 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7692 vmslen = my_strlcpy(vmsptr, esa, vmspath_len - 1);
7693 vmsptr[vmslen] = ':';
7695 vmsptr[vmslen] = '\0';
7703 /* must be dev/directory - ignore version */
7704 if ((n_len + e_len) != 0)
7707 /* transfer the volume */
7708 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7709 memcpy(vmsptr, v_spec, v_len);
7715 /* unroot the rooted directory */
7716 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7718 r_spec[r_len - 1] = ']';
7720 /* This should not be there, but nothing is perfect */
7722 cmp = strcmp(&r_spec[1], "000000.");
7732 memcpy(vmsptr, r_spec, r_len);
7738 /* Bring over the directory. */
7740 ((d_len + vmslen) < vmspath_len)) {
7742 d_spec[d_len - 1] = ']';
7744 cmp = strcmp(&d_spec[1], "000000.");
7755 /* Remove the redundant root */
7763 memcpy(vmsptr, d_spec, d_len);
7777 if (lastslash > unixptr) {
7780 /* skip leading ./ */
7782 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7788 /* Are we still in a directory? */
7789 if (unixptr <= lastslash) {
7794 /* if not backing up, then it is relative forward. */
7795 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7796 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7804 /* Perl wants an empty directory here to tell the difference
7805 * between a DCL command and a filename
7814 /* Handle two special files . and .. */
7815 if (unixptr[0] == '.') {
7816 if (&unixptr[1] == unixend) {
7823 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7834 else { /* Absolute PATH handling */
7838 /* Need to find out where root is */
7840 /* In theory, this procedure should never get an absolute POSIX pathname
7841 * that can not be found on the POSIX root.
7842 * In practice, that can not be relied on, and things will show up
7843 * here that are a VMS device name or concealed logical name instead.
7844 * So to make things work, this procedure must be tolerant.
7846 esa = (char *)PerlMem_malloc(vmspath_len);
7847 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7850 nextslash = strchr(&unixptr[1],'/');
7852 if (nextslash != NULL) {
7854 seg_len = nextslash - &unixptr[1];
7855 my_strlcpy(vmspath, unixptr, seg_len + 2);
7858 cmp = strncmp(vmspath, "dev", 4);
7860 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
7861 if (sts == SS$_NORMAL)
7865 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
7868 if ($VMS_STATUS_SUCCESS(sts)) {
7869 /* This is verified to be a real path */
7871 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
7872 if ($VMS_STATUS_SUCCESS(sts)) {
7873 vmslen = my_strlcpy(vmspath, esa, vmspath_len + 1);
7874 vmsptr = vmspath + vmslen;
7876 if (unixptr < lastslash) {
7885 cmp = strcmp(rptr,"000000.");
7890 } /* removing 6 zeros */
7891 } /* vmslen < 7, no 6 zeros possible */
7892 } /* Not in a directory */
7893 } /* Posix root found */
7895 /* No posix root, fall back to default directory */
7896 strcpy(vmspath, "SYS$DISK:[");
7897 vmsptr = &vmspath[10];
7899 if (unixptr > lastslash) {
7908 } /* end of verified real path handling */
7913 /* Ok, we have a device or a concealed root that is not in POSIX
7914 * or we have garbage. Make the best of it.
7917 /* Posix to VMS destroyed this, so copy it again */
7918 my_strlcpy(vmspath, &unixptr[1], seg_len + 1);
7919 vmslen = strlen(vmspath); /* We know we're truncating. */
7920 vmsptr = &vmsptr[vmslen];
7923 /* Now do we need to add the fake 6 zero directory to it? */
7925 if ((*lastslash == '/') && (nextslash < lastslash)) {
7926 /* No there is another directory */
7933 /* now we have foo:bar or foo:[000000]bar to decide from */
7934 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
7936 if (!islnm && !decc_posix_compliant_pathnames) {
7938 cmp = strncmp("bin", vmspath, 4);
7940 /* bin => SYS$SYSTEM: */
7941 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
7944 /* tmp => SYS$SCRATCH: */
7945 cmp = strncmp("tmp", vmspath, 4);
7947 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
7952 trnend = islnm ? islnm - 1 : 0;
7954 /* if this was a logical name, ']' or '>' must be present */
7955 /* if not a logical name, then assume a device and hope. */
7956 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
7958 /* if log name and trailing '.' then rooted - treat as device */
7959 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
7961 /* Fix me, if not a logical name, a device lookup should be
7962 * done to see if the device is file structured. If the device
7963 * is not file structured, the 6 zeros should not be put on.
7965 * As it is, perl is occasionally looking for dev:[000000]tty.
7966 * which looks a little strange.
7968 * Not that easy to detect as "/dev" may be file structured with
7969 * special device files.
7972 if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
7973 (&nextslash[1] == unixend)) {
7974 /* No real directory present */
7979 /* Put the device delimiter on */
7982 unixptr = nextslash;
7985 /* Start directory if needed */
7986 if (!islnm || add_6zero) {
7992 /* add fake 000000] if needed */
8005 } /* non-POSIX translation */
8007 } /* End of relative/absolute path handling */
8009 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
8016 if (dir_start != 0) {
8018 /* First characters in a directory are handled special */
8019 while ((*unixptr == '/') ||
8020 ((*unixptr == '.') &&
8021 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8022 (&unixptr[1]==unixend)))) {
8027 /* Skip redundant / in specification */
8028 while ((*unixptr == '/') && (dir_start != 0)) {
8031 if (unixptr == lastslash)
8034 if (unixptr == lastslash)
8037 /* Skip redundant ./ characters */
8038 while ((*unixptr == '.') &&
8039 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8042 if (unixptr == lastslash)
8044 if (*unixptr == '/')
8047 if (unixptr == lastslash)
8050 /* Skip redundant ../ characters */
8051 while ((*unixptr == '.') && (unixptr[1] == '.') &&
8052 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8053 /* Set the backing up flag */
8059 unixptr++; /* first . */
8060 unixptr++; /* second . */
8061 if (unixptr == lastslash)
8063 if (*unixptr == '/') /* The slash */
8066 if (unixptr == lastslash)
8069 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8070 /* Not needed when VMS is pretending to be UNIX. */
8072 /* Is this loop stuck because of too many dots? */
8073 if (loop_flag == 0) {
8074 /* Exit the loop and pass the rest through */
8079 /* Are we done with directories yet? */
8080 if (unixptr >= lastslash) {
8082 /* Watch out for trailing dots */
8091 if (*unixptr == '/')
8095 /* Have we stopped backing up? */
8100 /* dir_start continues to be = 1 */
8102 if (*unixptr == '-') {
8104 *vmsptr++ = *unixptr++;
8108 /* Now are we done with directories yet? */
8109 if (unixptr >= lastslash) {
8111 /* Watch out for trailing dots */
8127 if (unixptr >= unixend)
8130 /* Normal characters - More EFS work probably needed */
8136 /* remove multiple / */
8137 while (unixptr[1] == '/') {
8140 if (unixptr == lastslash) {
8141 /* Watch out for trailing dots */
8153 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8154 /* Not needed when VMS is pretending to be UNIX. */
8158 if (unixptr != unixend)
8163 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8164 (&unixptr[1] == unixend)) {
8170 /* trailing dot ==> '^..' on VMS */
8171 if (unixptr == unixend) {
8179 *vmsptr++ = *unixptr++;
8183 if (quoted && (&unixptr[1] == unixend)) {
8187 in_cnt = copy_expand_unix_filename_escape
8188 (vmsptr, unixptr, &out_cnt, utf8_fl);
8198 in_cnt = copy_expand_unix_filename_escape
8199 (vmsptr, unixptr, &out_cnt, utf8_fl);
8206 /* Make sure directory is closed */
8207 if (unixptr == lastslash) {
8209 vmsptr2 = vmsptr - 1;
8211 if (*vmsptr2 != ']') {
8214 /* directories do not end in a dot bracket */
8215 if (*vmsptr2 == '.') {
8219 if (*vmsptr2 != '^') {
8220 vmsptr--; /* back up over the dot */
8228 /* Add a trailing dot if a file with no extension */
8229 vmsptr2 = vmsptr - 1;
8231 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8232 (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
8243 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8244 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8249 /* If a UTF8 flag is being passed, honor it */
8251 if (utf8_fl != NULL) {
8252 utf8_flag = *utf8_fl;
8257 /* If there is a possibility of UTF8, then if any UTF8 characters
8258 are present, then they must be converted to VTF-7
8260 result = strcpy(rslt, path); /* FIX-ME */
8263 result = strcpy(rslt, path);
8268 /* A convenience macro for copying dots in filenames and escaping
8269 * them when they haven't already been escaped, with guards to
8270 * avoid checking before the start of the buffer or advancing
8271 * beyond the end of it (allowing room for the NUL terminator).
8273 #define VMSEFS_DOT_WITH_ESCAPE(vmsefsdot,vmsefsbuf,vmsefsbufsiz) STMT_START { \
8274 if ( ((vmsefsdot) > (vmsefsbuf) && *((vmsefsdot) - 1) != '^' \
8275 || ((vmsefsdot) == (vmsefsbuf))) \
8276 && (vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 3 \
8278 *((vmsefsdot)++) = '^'; \
8280 if ((vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 2) \
8281 *((vmsefsdot)++) = '.'; \
8284 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8285 static char *int_tovmsspec
8286 (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8291 unsigned long int infront = 0, hasdir = 1;
8294 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8295 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8297 if (vms_debug_fileify) {
8299 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8301 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8305 /* If we fail, we should be setting errno */
8307 set_vaxc_errno(SS$_BADPARAM);
8310 rslt_len = VMS_MAXRSS-1;
8312 /* '.' and '..' are "[]" and "[-]" for a quick check */
8313 if (path[0] == '.') {
8314 if (path[1] == '\0') {
8316 if (utf8_flag != NULL)
8321 if (path[1] == '.' && path[2] == '\0') {
8323 if (utf8_flag != NULL)
8330 /* Posix specifications are now a native VMS format */
8331 /*--------------------------------------------------*/
8332 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8333 if (decc_posix_compliant_pathnames) {
8334 if (strncmp(path,"\"^UP^",5) == 0) {
8335 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8341 /* This is really the only way to see if this is already in VMS format */
8342 sts = vms_split_path
8357 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8358 replacement, because the above parse just took care of most of
8359 what is needed to do vmspath when the specification is already
8362 And if it is not already, it is easier to do the conversion as
8363 part of this routine than to call this routine and then work on
8367 /* If VMS punctuation was found, it is already VMS format */
8368 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8369 if (utf8_flag != NULL)
8371 my_strlcpy(rslt, path, VMS_MAXRSS);
8372 if (vms_debug_fileify) {
8373 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8377 /* Now, what to do with trailing "." cases where there is no
8378 extension? If this is a UNIX specification, and EFS characters
8379 are enabled, then the trailing "." should be converted to a "^.".
8380 But if this was already a VMS specification, then it should be
8383 So in the case of ambiguity, leave the specification alone.
8387 /* If there is a possibility of UTF8, then if any UTF8 characters
8388 are present, then they must be converted to VTF-7
8390 if (utf8_flag != NULL)
8392 my_strlcpy(rslt, path, VMS_MAXRSS);
8393 if (vms_debug_fileify) {
8394 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8399 dirend = strrchr(path,'/');
8401 if (dirend == NULL) {
8402 /* If we get here with no Unix directory delimiters, then this is an
8403 * ambiguous file specification, such as a Unix glob specification, a
8404 * shell or make macro, or a filespec that would be valid except for
8405 * unescaped extended characters. The safest thing if it's a macro
8406 * is to pass it through as-is.
8408 if (strstr(path, "$(")) {
8409 my_strlcpy(rslt, path, VMS_MAXRSS);
8410 if (vms_debug_fileify) {
8411 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8417 else if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
8418 if (!*(dirend+2)) dirend +=2;
8419 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8420 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8425 lastdot = strrchr(cp2,'.');
8431 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8433 if (decc_disable_posix_root) {
8434 strcpy(rslt,"sys$disk:[000000]");
8437 strcpy(rslt,"sys$posix_root:[000000]");
8439 if (utf8_flag != NULL)
8441 if (vms_debug_fileify) {
8442 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8446 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8448 trndev = (char *)PerlMem_malloc(VMS_MAXRSS);
8449 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8450 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8452 /* DECC special handling */
8454 if (strcmp(rslt,"bin") == 0) {
8455 strcpy(rslt,"sys$system");
8458 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8460 else if (strcmp(rslt,"tmp") == 0) {
8461 strcpy(rslt,"sys$scratch");
8464 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8466 else if (!decc_disable_posix_root) {
8467 strcpy(rslt, "sys$posix_root");
8471 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8472 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8474 else if (strcmp(rslt,"dev") == 0) {
8475 if (strncmp(cp2,"/null", 5) == 0) {
8476 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8477 strcpy(rslt,"NLA0");
8481 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8487 trnend = islnm ? strlen(trndev) - 1 : 0;
8488 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8489 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8490 /* If the first element of the path is a logical name, determine
8491 * whether it has to be translated so we can add more directories. */
8492 if (!islnm || rooted) {
8495 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8499 if (cp2 != dirend) {
8500 my_strlcpy(rslt, trndev, VMS_MAXRSS);
8501 cp1 = rslt + trnend;
8508 if (decc_disable_posix_root) {
8514 PerlMem_free(trndev);
8519 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8520 cp2 += 2; /* skip over "./" - it's redundant */
8521 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8523 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8524 *(cp1++) = '-'; /* "../" --> "-" */
8527 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8528 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8529 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8530 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8533 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8534 /* Escape the extra dots in EFS file specifications */
8537 if (cp2 > dirend) cp2 = dirend;
8539 else *(cp1++) = '.';
8541 for (; cp2 < dirend; cp2++) {
8543 if (*(cp2-1) == '/') continue;
8544 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.';
8547 else if (!infront && *cp2 == '.') {
8548 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8549 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
8550 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8551 if (cp1 > rslt && (*(cp1-1) == '-' || *(cp1-1) == '[')) *(cp1++) = '-'; /* handle "../" */
8552 else if (cp1 > rslt + 1 && *(cp1-2) == '[') *(cp1-1) = '-';
8557 if (cp2 == dirend) break;
8559 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8560 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8561 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8562 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8564 *(cp1++) = '.'; /* Simulate trailing '/' */
8565 cp2 += 2; /* for loop will incr this to == dirend */
8567 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8570 if (decc_efs_charset == 0) {
8571 if (cp1 > rslt && *(cp1-1) == '^')
8572 cp1--; /* remove the escape, if any */
8573 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8576 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8581 if (!infront && cp1 > rslt && *(cp1-1) == '-') *(cp1++) = '.';
8583 if (decc_efs_charset == 0) {
8584 if (cp1 > rslt && *(cp1-1) == '^')
8585 cp1--; /* remove the escape, if any */
8589 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8594 cp2 += copy_expand_unix_filename_escape(cp1, cp2, &out_cnt, utf8_flag);
8595 cp2--; /* we're in a loop that will increment this */
8601 if (cp1 > rslt && *(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8602 if (hasdir) *(cp1++) = ']';
8603 if (*cp2 && *cp2 == '/') cp2++; /* check in case we ended with trailing '/' */
8610 if (decc_efs_charset == 0)
8616 if (cp2 >= path && (cp2 == path || *(cp2-1) != '^')) /* not previously escaped */
8622 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8623 decc_readdir_dropdotnotype) {
8624 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8627 /* trailing dot ==> '^..' on VMS */
8634 *(cp1++) = *(cp2++);
8639 /* This could be a macro to be passed through */
8640 *(cp1++) = *(cp2++);
8642 const char * save_cp2;
8646 /* paranoid check */
8652 *(cp1++) = *(cp2++);
8653 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8654 *(cp1++) = *(cp2++);
8655 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8656 *(cp1++) = *(cp2++);
8659 *(cp1++) = *(cp2++);
8663 if (is_macro == 0) {
8664 /* Not really a macro - never mind */
8677 /* Don't escape again if following character is
8678 * already something we escape.
8680 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8681 *(cp1++) = *(cp2++);
8684 /* But otherwise fall through and escape it. */
8701 if (cp2 >= path && *(cp2-1) != '^') /* not previously escaped */
8703 *(cp1++) = *(cp2++);
8706 /* If it doesn't look like the beginning of a version number,
8707 * or we've been promised there are no version numbers, then
8710 if (decc_filename_unix_no_version) {
8714 size_t all_nums = strspn(cp2+1, "0123456789");
8715 if (all_nums > 5 || *(cp2 + all_nums + 1) != '\0')
8718 *(cp1++) = *(cp2++);
8721 *(cp1++) = *(cp2++);
8724 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8728 /* Fix me for "^]", but that requires making sure that you do
8729 * not back up past the start of the filename
8731 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8736 if (utf8_flag != NULL)
8738 if (vms_debug_fileify) {
8739 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8743 } /* end of int_tovmsspec() */
8746 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8747 static char *mp_do_tovmsspec
8748 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
8749 static char __tovmsspec_retbuf[VMS_MAXRSS];
8750 char * vmsspec, *ret_spec, *ret_buf;
8754 if (ret_buf == NULL) {
8756 Newx(vmsspec, VMS_MAXRSS, char);
8757 if (vmsspec == NULL)
8758 _ckvmssts(SS$_INSFMEM);
8761 ret_buf = __tovmsspec_retbuf;
8765 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8767 if (ret_spec == NULL) {
8768 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8775 } /* end of mp_do_tovmsspec() */
8777 /* External entry points */
8778 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8779 { return do_tovmsspec(path,buf,0,NULL); }
8780 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8781 { return do_tovmsspec(path,buf,1,NULL); }
8782 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8783 { return do_tovmsspec(path,buf,0,utf8_fl); }
8784 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8785 { return do_tovmsspec(path,buf,1,utf8_fl); }
8787 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
8788 /* Internal routine for use with out an explicit context present */
8789 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
8791 char * ret_spec, *pathified;
8796 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8797 if (pathified == NULL)
8798 _ckvmssts_noperl(SS$_INSFMEM);
8800 ret_spec = int_pathify_dirspec(path, pathified);
8802 if (ret_spec == NULL) {
8803 PerlMem_free(pathified);
8807 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
8809 PerlMem_free(pathified);
8814 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8815 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8816 static char __tovmspath_retbuf[VMS_MAXRSS];
8818 char *pathified, *vmsified, *cp;
8820 if (path == NULL) return NULL;
8821 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8822 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8823 if (int_pathify_dirspec(path, pathified) == NULL) {
8824 PerlMem_free(pathified);
8830 Newx(vmsified, VMS_MAXRSS, char);
8831 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8832 PerlMem_free(pathified);
8833 if (vmsified) Safefree(vmsified);
8836 PerlMem_free(pathified);
8841 vmslen = strlen(vmsified);
8842 Newx(cp,vmslen+1,char);
8843 memcpy(cp,vmsified,vmslen);
8849 my_strlcpy(__tovmspath_retbuf, vmsified, sizeof(__tovmspath_retbuf));
8851 return __tovmspath_retbuf;
8854 } /* end of do_tovmspath() */
8856 /* External entry points */
8857 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
8858 { return do_tovmspath(path,buf,0, NULL); }
8859 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
8860 { return do_tovmspath(path,buf,1, NULL); }
8861 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
8862 { return do_tovmspath(path,buf,0,utf8_fl); }
8863 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
8864 { return do_tovmspath(path,buf,1,utf8_fl); }
8867 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
8868 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8869 static char __tounixpath_retbuf[VMS_MAXRSS];
8871 char *pathified, *unixified, *cp;
8873 if (path == NULL) return NULL;
8874 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8875 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8876 if (int_pathify_dirspec(path, pathified) == NULL) {
8877 PerlMem_free(pathified);
8883 Newx(unixified, VMS_MAXRSS, char);
8885 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
8886 PerlMem_free(pathified);
8887 if (unixified) Safefree(unixified);
8890 PerlMem_free(pathified);
8895 unixlen = strlen(unixified);
8896 Newx(cp,unixlen+1,char);
8897 memcpy(cp,unixified,unixlen);
8899 Safefree(unixified);
8903 my_strlcpy(__tounixpath_retbuf, unixified, sizeof(__tounixpath_retbuf));
8904 Safefree(unixified);
8905 return __tounixpath_retbuf;
8908 } /* end of do_tounixpath() */
8910 /* External entry points */
8911 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
8912 { return do_tounixpath(path,buf,0,NULL); }
8913 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
8914 { return do_tounixpath(path,buf,1,NULL); }
8915 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8916 { return do_tounixpath(path,buf,0,utf8_fl); }
8917 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8918 { return do_tounixpath(path,buf,1,utf8_fl); }
8921 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
8923 *****************************************************************************
8925 * Copyright (C) 1989-1994, 2007 by *
8926 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
8928 * Permission is hereby granted for the reproduction of this software *
8929 * on condition that this copyright notice is included in source *
8930 * distributions of the software. The code may be modified and *
8931 * distributed under the same terms as Perl itself. *
8933 * 27-Aug-1994 Modified for inclusion in perl5 *
8934 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
8935 *****************************************************************************
8939 * getredirection() is intended to aid in porting C programs
8940 * to VMS (Vax-11 C). The native VMS environment does not support
8941 * '>' and '<' I/O redirection, or command line wild card expansion,
8942 * or a command line pipe mechanism using the '|' AND background
8943 * command execution '&'. All of these capabilities are provided to any
8944 * C program which calls this procedure as the first thing in the
8946 * The piping mechanism will probably work with almost any 'filter' type
8947 * of program. With suitable modification, it may useful for other
8948 * portability problems as well.
8950 * Author: Mark Pizzolato (mark AT infocomm DOT com)
8954 struct list_item *next;
8958 static void add_item(struct list_item **head,
8959 struct list_item **tail,
8963 static void mp_expand_wild_cards(pTHX_ char *item,
8964 struct list_item **head,
8965 struct list_item **tail,
8968 static int background_process(pTHX_ int argc, char **argv);
8970 static void pipe_and_fork(pTHX_ char **cmargv);
8972 /*{{{ void getredirection(int *ac, char ***av)*/
8974 mp_getredirection(pTHX_ int *ac, char ***av)
8976 * Process vms redirection arg's. Exit if any error is seen.
8977 * If getredirection() processes an argument, it is erased
8978 * from the vector. getredirection() returns a new argc and argv value.
8979 * In the event that a background command is requested (by a trailing "&"),
8980 * this routine creates a background subprocess, and simply exits the program.
8982 * Warning: do not try to simplify the code for vms. The code
8983 * presupposes that getredirection() is called before any data is
8984 * read from stdin or written to stdout.
8986 * Normal usage is as follows:
8992 * getredirection(&argc, &argv);
8996 int argc = *ac; /* Argument Count */
8997 char **argv = *av; /* Argument Vector */
8998 char *ap; /* Argument pointer */
8999 int j; /* argv[] index */
9000 int item_count = 0; /* Count of Items in List */
9001 struct list_item *list_head = 0; /* First Item in List */
9002 struct list_item *list_tail; /* Last Item in List */
9003 char *in = NULL; /* Input File Name */
9004 char *out = NULL; /* Output File Name */
9005 char *outmode = "w"; /* Mode to Open Output File */
9006 char *err = NULL; /* Error File Name */
9007 char *errmode = "w"; /* Mode to Open Error File */
9008 int cmargc = 0; /* Piped Command Arg Count */
9009 char **cmargv = NULL;/* Piped Command Arg Vector */
9012 * First handle the case where the last thing on the line ends with
9013 * a '&'. This indicates the desire for the command to be run in a
9014 * subprocess, so we satisfy that desire.
9017 if (0 == strcmp("&", ap))
9018 exit(background_process(aTHX_ --argc, argv));
9019 if (*ap && '&' == ap[strlen(ap)-1])
9021 ap[strlen(ap)-1] = '\0';
9022 exit(background_process(aTHX_ argc, argv));
9025 * Now we handle the general redirection cases that involve '>', '>>',
9026 * '<', and pipes '|'.
9028 for (j = 0; j < argc; ++j)
9030 if (0 == strcmp("<", argv[j]))
9034 fprintf(stderr,"No input file after < on command line");
9035 exit(LIB$_WRONUMARG);
9040 if ('<' == *(ap = argv[j]))
9045 if (0 == strcmp(">", ap))
9049 fprintf(stderr,"No output file after > on command line");
9050 exit(LIB$_WRONUMARG);
9069 fprintf(stderr,"No output file after > or >> on command line");
9070 exit(LIB$_WRONUMARG);
9074 if (('2' == *ap) && ('>' == ap[1]))
9091 fprintf(stderr,"No output file after 2> or 2>> on command line");
9092 exit(LIB$_WRONUMARG);
9096 if (0 == strcmp("|", argv[j]))
9100 fprintf(stderr,"No command into which to pipe on command line");
9101 exit(LIB$_WRONUMARG);
9103 cmargc = argc-(j+1);
9104 cmargv = &argv[j+1];
9108 if ('|' == *(ap = argv[j]))
9116 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9119 * Allocate and fill in the new argument vector, Some Unix's terminate
9120 * the list with an extra null pointer.
9122 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9123 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9125 for (j = 0; j < item_count; ++j, list_head = list_head->next)
9126 argv[j] = list_head->value;
9132 fprintf(stderr,"'|' and '>' may not both be specified on command line");
9133 exit(LIB$_INVARGORD);
9135 pipe_and_fork(aTHX_ cmargv);
9138 /* Check for input from a pipe (mailbox) */
9140 if (in == NULL && 1 == isapipe(0))
9142 char mbxname[L_tmpnam];
9144 long int dvi_item = DVI$_DEVBUFSIZ;
9145 $DESCRIPTOR(mbxnam, "");
9146 $DESCRIPTOR(mbxdevnam, "");
9148 /* Input from a pipe, reopen it in binary mode to disable */
9149 /* carriage control processing. */
9151 fgetname(stdin, mbxname, 1);
9152 mbxnam.dsc$a_pointer = mbxname;
9153 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9154 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9155 mbxdevnam.dsc$a_pointer = mbxname;
9156 mbxdevnam.dsc$w_length = sizeof(mbxname);
9157 dvi_item = DVI$_DEVNAM;
9158 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9159 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9162 freopen(mbxname, "rb", stdin);
9165 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9169 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9171 fprintf(stderr,"Can't open input file %s as stdin",in);
9174 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9176 fprintf(stderr,"Can't open output file %s as stdout",out);
9179 if (out != NULL) vmssetuserlnm("SYS$OUTPUT", out);
9182 if (strcmp(err,"&1") == 0) {
9183 dup2(fileno(stdout), fileno(stderr));
9184 vmssetuserlnm("SYS$ERROR", "SYS$OUTPUT");
9187 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9189 fprintf(stderr,"Can't open error file %s as stderr",err);
9193 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9197 vmssetuserlnm("SYS$ERROR", err);
9200 #ifdef ARGPROC_DEBUG
9201 PerlIO_printf(Perl_debug_log, "Arglist:\n");
9202 for (j = 0; j < *ac; ++j)
9203 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9205 /* Clear errors we may have hit expanding wildcards, so they don't
9206 show up in Perl's $! later */
9207 set_errno(0); set_vaxc_errno(1);
9208 } /* end of getredirection() */
9211 static void add_item(struct list_item **head,
9212 struct list_item **tail,
9218 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9219 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9223 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9224 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9225 *tail = (*tail)->next;
9227 (*tail)->value = value;
9231 static void mp_expand_wild_cards(pTHX_ char *item,
9232 struct list_item **head,
9233 struct list_item **tail,
9237 unsigned long int context = 0;
9245 $DESCRIPTOR(filespec, "");
9246 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9247 $DESCRIPTOR(resultspec, "");
9248 unsigned long int lff_flags = 0;
9252 #ifdef VMS_LONGNAME_SUPPORT
9253 lff_flags = LIB$M_FIL_LONG_NAMES;
9256 for (cp = item; *cp; cp++) {
9257 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9258 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9260 if (!*cp || isspace(*cp))
9262 add_item(head, tail, item, count);
9267 /* "double quoted" wild card expressions pass as is */
9268 /* From DCL that means using e.g.: */
9269 /* perl program """perl.*""" */
9270 item_len = strlen(item);
9271 if ( '"' == *item && '"' == item[item_len-1] )
9274 item[item_len-2] = '\0';
9275 add_item(head, tail, item, count);
9279 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9280 resultspec.dsc$b_class = DSC$K_CLASS_D;
9281 resultspec.dsc$a_pointer = NULL;
9282 vmsspec = (char *)PerlMem_malloc(VMS_MAXRSS);
9283 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9284 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9285 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9286 if (!isunix || !filespec.dsc$a_pointer)
9287 filespec.dsc$a_pointer = item;
9288 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9290 * Only return version specs, if the caller specified a version
9292 had_version = strchr(item, ';');
9294 * Only return device and directory specs, if the caller specified either.
9296 had_device = strchr(item, ':');
9297 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9299 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9300 (&filespec, &resultspec, &context,
9301 &defaultspec, 0, &rms_sts, &lff_flags)))
9306 string = (char *)PerlMem_malloc(resultspec.dsc$w_length+1);
9307 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9308 my_strlcpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length+1);
9309 if (NULL == had_version)
9310 *(strrchr(string, ';')) = '\0';
9311 if ((!had_directory) && (had_device == NULL))
9313 if (NULL == (devdir = strrchr(string, ']')))
9314 devdir = strrchr(string, '>');
9315 my_strlcpy(string, devdir + 1, resultspec.dsc$w_length+1);
9318 * Be consistent with what the C RTL has already done to the rest of
9319 * the argv items and lowercase all of these names.
9321 if (!decc_efs_case_preserve) {
9322 for (c = string; *c; ++c)
9326 if (isunix) trim_unixpath(string,item,1);
9327 add_item(head, tail, string, count);
9330 PerlMem_free(vmsspec);
9331 if (sts != RMS$_NMF)
9333 set_vaxc_errno(sts);
9336 case RMS$_FNF: case RMS$_DNF:
9337 set_errno(ENOENT); break;
9339 set_errno(ENOTDIR); break;
9341 set_errno(ENODEV); break;
9342 case RMS$_FNM: case RMS$_SYN:
9343 set_errno(EINVAL); break;
9345 set_errno(EACCES); break;
9347 _ckvmssts_noperl(sts);
9351 add_item(head, tail, item, count);
9352 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9353 _ckvmssts_noperl(lib$find_file_end(&context));
9358 pipe_and_fork(pTHX_ char **cmargv)
9361 struct dsc$descriptor_s *vmscmd;
9362 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9363 int sts, j, l, ismcr, quote, tquote = 0;
9365 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
9366 vms_execfree(vmscmd);
9371 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
9372 && toupper(*(q+2)) == 'R' && !*(q+3);
9374 while (q && l < MAX_DCL_LINE_LENGTH) {
9376 if (j > 0 && quote) {
9382 if (ismcr && j > 1) quote = 1;
9383 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9386 if (quote || tquote) {
9392 if ((quote||tquote) && *q == '"') {
9402 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9404 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9408 static int background_process(pTHX_ int argc, char **argv)
9410 char command[MAX_DCL_SYMBOL + 1] = "$";
9411 $DESCRIPTOR(value, "");
9412 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9413 static $DESCRIPTOR(null, "NLA0:");
9414 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9416 $DESCRIPTOR(pidstr, "");
9418 unsigned long int flags = 17, one = 1, retsts;
9421 len = my_strlcat(command, argv[0], sizeof(command));
9422 while (--argc && (len < MAX_DCL_SYMBOL))
9424 my_strlcat(command, " \"", sizeof(command));
9425 my_strlcat(command, *(++argv), sizeof(command));
9426 len = my_strlcat(command, "\"", sizeof(command));
9428 value.dsc$a_pointer = command;
9429 value.dsc$w_length = strlen(value.dsc$a_pointer);
9430 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9431 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9432 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9433 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9436 _ckvmssts_noperl(retsts);
9438 #ifdef ARGPROC_DEBUG
9439 PerlIO_printf(Perl_debug_log, "%s\n", command);
9441 sprintf(pidstring, "%08X", pid);
9442 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9443 pidstr.dsc$a_pointer = pidstring;
9444 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9445 lib$set_symbol(&pidsymbol, &pidstr);
9449 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9452 /* OS-specific initialization at image activation (not thread startup) */
9453 /* Older VAXC header files lack these constants */
9454 #ifndef JPI$_RIGHTS_SIZE
9455 # define JPI$_RIGHTS_SIZE 817
9457 #ifndef KGB$M_SUBSYSTEM
9458 # define KGB$M_SUBSYSTEM 0x8
9461 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9463 /*{{{void vms_image_init(int *, char ***)*/
9465 vms_image_init(int *argcp, char ***argvp)
9468 char eqv[LNM$C_NAMLENGTH+1] = "";
9469 unsigned int len, tabct = 8, tabidx = 0;
9470 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9471 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9472 unsigned short int dummy, rlen;
9473 struct dsc$descriptor_s **tabvec;
9474 #if defined(PERL_IMPLICIT_CONTEXT)
9477 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9478 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9479 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9482 #ifdef KILL_BY_SIGPRC
9483 Perl_csighandler_init();
9486 #if __CRTL_VER >= 70300000 && !defined(__VAX)
9487 /* This was moved from the pre-image init handler because on threaded */
9488 /* Perl it was always returning 0 for the default value. */
9489 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9492 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9495 initial = decc$feature_get_value(s, 4);
9497 /* initial is: 0 if nothing has set the feature */
9498 /* -1 if initialized to default */
9499 /* 1 if set by logical name */
9500 /* 2 if set by decc$feature_set_value */
9501 decc_disable_posix_root = decc$feature_get_value(s, 1);
9503 /* If the value is not valid, force the feature off */
9504 if (decc_disable_posix_root < 0) {
9505 decc$feature_set_value(s, 1, 1);
9506 decc_disable_posix_root = 1;
9510 /* Nothing has asked for it explicitly, so use our own default. */
9511 decc_disable_posix_root = 1;
9512 decc$feature_set_value(s, 1, 1);
9518 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9519 _ckvmssts_noperl(iosb[0]);
9520 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9521 if (iprv[i]) { /* Running image installed with privs? */
9522 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
9527 /* Rights identifiers might trigger tainting as well. */
9528 if (!will_taint && (rlen || rsz)) {
9529 while (rlen < rsz) {
9530 /* We didn't get all the identifiers on the first pass. Allocate a
9531 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9532 * were needed to hold all identifiers at time of last call; we'll
9533 * allocate that many unsigned long ints), and go back and get 'em.
9534 * If it gave us less than it wanted to despite ample buffer space,
9535 * something's broken. Is your system missing a system identifier?
9537 if (rsz <= jpilist[1].buflen) {
9538 /* Perl_croak accvios when used this early in startup. */
9539 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9540 rsz, (unsigned long) jpilist[1].buflen,
9541 "Check your rights database for corruption.\n");
9544 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9545 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9546 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9547 jpilist[1].buflen = rsz * sizeof(unsigned long int);
9548 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9549 _ckvmssts_noperl(iosb[0]);
9551 mask = (unsigned long int *)jpilist[1].bufadr;
9552 /* Check attribute flags for each identifier (2nd longword); protected
9553 * subsystem identifiers trigger tainting.
9555 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9556 if (mask[i] & KGB$M_SUBSYSTEM) {
9561 if (mask != rlst) PerlMem_free(mask);
9564 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9565 * logical, some versions of the CRTL will add a phanthom /000000/
9566 * directory. This needs to be removed.
9568 if (decc_filename_unix_report) {
9571 ulen = strlen(argvp[0][0]);
9573 zeros = strstr(argvp[0][0], "/000000/");
9574 if (zeros != NULL) {
9576 mlen = ulen - (zeros - argvp[0][0]) - 7;
9577 memmove(zeros, &zeros[7], mlen);
9579 argvp[0][0][ulen] = '\0';
9582 /* It also may have a trailing dot that needs to be removed otherwise
9583 * it will be converted to VMS mode incorrectly.
9586 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9587 argvp[0][0][ulen] = '\0';
9590 /* We need to use this hack to tell Perl it should run with tainting,
9591 * since its tainting flag may be part of the PL_curinterp struct, which
9592 * hasn't been allocated when vms_image_init() is called.
9595 char **newargv, **oldargv;
9597 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9598 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9599 newargv[0] = oldargv[0];
9600 newargv[1] = (char *)PerlMem_malloc(3 * sizeof(char));
9601 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9602 strcpy(newargv[1], "-T");
9603 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9605 newargv[*argcp] = NULL;
9606 /* We orphan the old argv, since we don't know where it's come from,
9607 * so we don't know how to free it.
9611 else { /* Did user explicitly request tainting? */
9613 char *cp, **av = *argvp;
9614 for (i = 1; i < *argcp; i++) {
9615 if (*av[i] != '-') break;
9616 for (cp = av[i]+1; *cp; cp++) {
9617 if (*cp == 'T') { will_taint = 1; break; }
9618 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9619 strchr("DFIiMmx",*cp)) break;
9621 if (will_taint) break;
9626 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9629 tabvec = (struct dsc$descriptor_s **)
9630 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9631 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9633 else if (tabidx >= tabct) {
9635 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9636 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9638 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9639 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9640 tabvec[tabidx]->dsc$w_length = len;
9641 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9642 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_S;
9643 tabvec[tabidx]->dsc$a_pointer = (char *)PerlMem_malloc(len + 1);
9644 if (tabvec[tabidx]->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9645 my_strlcpy(tabvec[tabidx]->dsc$a_pointer, eqv, len + 1);
9647 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9649 getredirection(argcp,argvp);
9650 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9652 # include <reentrancy.h>
9653 decc$set_reentrancy(C$C_MULTITHREAD);
9662 * Trim Unix-style prefix off filespec, so it looks like what a shell
9663 * glob expansion would return (i.e. from specified prefix on, not
9664 * full path). Note that returned filespec is Unix-style, regardless
9665 * of whether input filespec was VMS-style or Unix-style.
9667 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9668 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9669 * vector of options; at present, only bit 0 is used, and if set tells
9670 * trim unixpath to try the current default directory as a prefix when
9671 * presented with a possibly ambiguous ... wildcard.
9673 * Returns !=0 on success, with trimmed filespec replacing contents of
9674 * fspec, and 0 on failure, with contents of fpsec unchanged.
9676 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9678 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9680 char *unixified, *unixwild, *tplate, *base, *end, *cp1, *cp2;
9681 int tmplen, reslen = 0, dirs = 0;
9683 if (!wildspec || !fspec) return 0;
9685 unixwild = (char *)PerlMem_malloc(VMS_MAXRSS);
9686 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9688 if (strpbrk(wildspec,"]>:") != NULL) {
9689 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
9690 PerlMem_free(unixwild);
9695 my_strlcpy(unixwild, wildspec, VMS_MAXRSS);
9697 unixified = (char *)PerlMem_malloc(VMS_MAXRSS);
9698 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9699 if (strpbrk(fspec,"]>:") != NULL) {
9700 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
9701 PerlMem_free(unixwild);
9702 PerlMem_free(unixified);
9705 else base = unixified;
9706 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9707 * check to see that final result fits into (isn't longer than) fspec */
9708 reslen = strlen(fspec);
9712 /* No prefix or absolute path on wildcard, so nothing to remove */
9713 if (!*tplate || *tplate == '/') {
9714 PerlMem_free(unixwild);
9715 if (base == fspec) {
9716 PerlMem_free(unixified);
9719 tmplen = strlen(unixified);
9720 if (tmplen > reslen) {
9721 PerlMem_free(unixified);
9722 return 0; /* not enough space */
9724 /* Copy unixified resultant, including trailing NUL */
9725 memmove(fspec,unixified,tmplen+1);
9726 PerlMem_free(unixified);
9730 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9731 if ((cp1 = strstr(tplate,".../")) == NULL) { /* No ...; just count elts */
9732 for (cp1 = tplate; *cp1; cp1++) if (*cp1 == '/') dirs++;
9733 for (cp1 = end ;cp1 >= base; cp1--)
9734 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9736 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9737 PerlMem_free(unixified);
9738 PerlMem_free(unixwild);
9743 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9744 int ells = 1, totells, segdirs, match;
9745 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9746 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9748 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9750 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9751 tpl = (char *)PerlMem_malloc(VMS_MAXRSS);
9752 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9753 if (ellipsis == tplate && opts & 1) {
9754 /* Template begins with an ellipsis. Since we can't tell how many
9755 * directory names at the front of the resultant to keep for an
9756 * arbitrary starting point, we arbitrarily choose the current
9757 * default directory as a starting point. If it's there as a prefix,
9758 * clip it off. If not, fall through and act as if the leading
9759 * ellipsis weren't there (i.e. return shortest possible path that
9760 * could match template).
9762 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9764 PerlMem_free(unixified);
9765 PerlMem_free(unixwild);
9768 if (!decc_efs_case_preserve) {
9769 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9770 if (_tolower(*cp1) != _tolower(*cp2)) break;
9772 segdirs = dirs - totells; /* Min # of dirs we must have left */
9773 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9774 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9775 memmove(fspec,cp2+1,end - cp2);
9777 PerlMem_free(unixified);
9778 PerlMem_free(unixwild);
9782 /* First off, back up over constant elements at end of path */
9784 for (front = end ; front >= base; front--)
9785 if (*front == '/' && !dirs--) { front++; break; }
9787 lcres = (char *)PerlMem_malloc(VMS_MAXRSS);
9788 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9789 for (cp1=tplate,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9791 if (!decc_efs_case_preserve) {
9792 *cp2 = _tolower(*cp1); /* Make lc copy for match */
9800 PerlMem_free(unixified);
9801 PerlMem_free(unixwild);
9802 PerlMem_free(lcres);
9803 return 0; /* Path too long. */
9806 *cp2 = '\0'; /* Pick up with memcpy later */
9807 lcfront = lcres + (front - base);
9808 /* Now skip over each ellipsis and try to match the path in front of it. */
9810 for (cp1 = ellipsis - 2; cp1 >= tplate; cp1--)
9811 if (*(cp1) == '.' && *(cp1+1) == '.' &&
9812 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
9813 if (cp1 < tplate) break; /* template started with an ellipsis */
9814 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9815 ellipsis = cp1; continue;
9817 wilddsc.dsc$a_pointer = tpl;
9818 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9820 for (segdirs = 0, cp2 = tpl;
9821 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
9823 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
9825 if (!decc_efs_case_preserve) {
9826 *cp2 = _tolower(*cp1); /* else lowercase for match */
9829 *cp2 = *cp1; /* else preserve case for match */
9832 if (*cp2 == '/') segdirs++;
9834 if (cp1 != ellipsis - 1) {
9836 PerlMem_free(unixified);
9837 PerlMem_free(unixwild);
9838 PerlMem_free(lcres);
9839 return 0; /* Path too long */
9841 /* Back up at least as many dirs as in template before matching */
9842 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
9843 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
9844 for (match = 0; cp1 > lcres;) {
9845 resdsc.dsc$a_pointer = cp1;
9846 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
9848 if (match == 1) lcfront = cp1;
9850 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
9854 PerlMem_free(unixified);
9855 PerlMem_free(unixwild);
9856 PerlMem_free(lcres);
9857 return 0; /* Can't find prefix ??? */
9859 if (match > 1 && opts & 1) {
9860 /* This ... wildcard could cover more than one set of dirs (i.e.
9861 * a set of similar dir names is repeated). If the template
9862 * contains more than 1 ..., upstream elements could resolve the
9863 * ambiguity, but it's not worth a full backtracking setup here.
9864 * As a quick heuristic, clip off the current default directory
9865 * if it's present to find the trimmed spec, else use the
9866 * shortest string that this ... could cover.
9868 char def[NAM$C_MAXRSS+1], *st;
9870 if (getcwd(def, sizeof def,0) == NULL) {
9871 PerlMem_free(unixified);
9872 PerlMem_free(unixwild);
9873 PerlMem_free(lcres);
9877 if (!decc_efs_case_preserve) {
9878 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9879 if (_tolower(*cp1) != _tolower(*cp2)) break;
9881 segdirs = dirs - totells; /* Min # of dirs we must have left */
9882 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
9883 if (*cp1 == '\0' && *cp2 == '/') {
9884 memmove(fspec,cp2+1,end - cp2);
9886 PerlMem_free(unixified);
9887 PerlMem_free(unixwild);
9888 PerlMem_free(lcres);
9891 /* Nope -- stick with lcfront from above and keep going. */
9894 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
9896 PerlMem_free(unixified);
9897 PerlMem_free(unixwild);
9898 PerlMem_free(lcres);
9902 } /* end of trim_unixpath() */
9907 * VMS readdir() routines.
9908 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
9910 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
9911 * Minor modifications to original routines.
9914 /* readdir may have been redefined by reentr.h, so make sure we get
9915 * the local version for what we do here.
9920 #if !defined(PERL_IMPLICIT_CONTEXT)
9921 # define readdir Perl_readdir
9923 # define readdir(a) Perl_readdir(aTHX_ a)
9926 /* Number of elements in vms_versions array */
9927 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
9930 * Open a directory, return a handle for later use.
9932 /*{{{ DIR *opendir(char*name) */
9934 Perl_opendir(pTHX_ const char *name)
9940 Newx(dir, VMS_MAXRSS, char);
9941 if (int_tovmspath(name, dir, NULL) == NULL) {
9945 /* Check access before stat; otherwise stat does not
9946 * accurately report whether it's a directory.
9948 if (!strstr(dir, "::") /* sys$check_access doesn't do remotes */
9949 && !cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
9950 /* cando_by_name has already set errno */
9954 if (flex_stat(dir,&sb) == -1) return NULL;
9955 if (!S_ISDIR(sb.st_mode)) {
9957 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
9960 /* Get memory for the handle, and the pattern. */
9962 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
9964 /* Fill in the fields; mainly playing with the descriptor. */
9965 sprintf(dd->pattern, "%s*.*",dir);
9970 /* By saying we want the result of readdir() in unix format, we are really
9971 * saying we want all the escapes removed, translating characters that
9972 * must be escaped in a VMS-format name to their unescaped form, which is
9973 * presumably allowed in a Unix-format name.
9975 dd->flags = decc_filename_unix_report ? PERL_VMSDIR_M_UNIXSPECS : 0;
9976 dd->pat.dsc$a_pointer = dd->pattern;
9977 dd->pat.dsc$w_length = strlen(dd->pattern);
9978 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
9979 dd->pat.dsc$b_class = DSC$K_CLASS_S;
9980 #if defined(USE_ITHREADS)
9981 Newx(dd->mutex,1,perl_mutex);
9982 MUTEX_INIT( (perl_mutex *) dd->mutex );
9988 } /* end of opendir() */
9992 * Set the flag to indicate we want versions or not.
9994 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
9996 vmsreaddirversions(DIR *dd, int flag)
9999 dd->flags |= PERL_VMSDIR_M_VERSIONS;
10001 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10006 * Free up an opened directory.
10008 /*{{{ void closedir(DIR *dd)*/
10010 Perl_closedir(DIR *dd)
10014 sts = lib$find_file_end(&dd->context);
10015 Safefree(dd->pattern);
10016 #if defined(USE_ITHREADS)
10017 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10018 Safefree(dd->mutex);
10025 * Collect all the version numbers for the current file.
10028 collectversions(pTHX_ DIR *dd)
10030 struct dsc$descriptor_s pat;
10031 struct dsc$descriptor_s res;
10033 char *p, *text, *buff;
10035 unsigned long context, tmpsts;
10037 /* Convenient shorthand. */
10040 /* Add the version wildcard, ignoring the "*.*" put on before */
10041 i = strlen(dd->pattern);
10042 Newx(text,i + e->d_namlen + 3,char);
10043 my_strlcpy(text, dd->pattern, i + 1);
10044 sprintf(&text[i - 3], "%s;*", e->d_name);
10046 /* Set up the pattern descriptor. */
10047 pat.dsc$a_pointer = text;
10048 pat.dsc$w_length = i + e->d_namlen - 1;
10049 pat.dsc$b_dtype = DSC$K_DTYPE_T;
10050 pat.dsc$b_class = DSC$K_CLASS_S;
10052 /* Set up result descriptor. */
10053 Newx(buff, VMS_MAXRSS, char);
10054 res.dsc$a_pointer = buff;
10055 res.dsc$w_length = VMS_MAXRSS - 1;
10056 res.dsc$b_dtype = DSC$K_DTYPE_T;
10057 res.dsc$b_class = DSC$K_CLASS_S;
10059 /* Read files, collecting versions. */
10060 for (context = 0, e->vms_verscount = 0;
10061 e->vms_verscount < VERSIZE(e);
10062 e->vms_verscount++) {
10063 unsigned long rsts;
10064 unsigned long flags = 0;
10066 #ifdef VMS_LONGNAME_SUPPORT
10067 flags = LIB$M_FIL_LONG_NAMES;
10069 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10070 if (tmpsts == RMS$_NMF || context == 0) break;
10072 buff[VMS_MAXRSS - 1] = '\0';
10073 if ((p = strchr(buff, ';')))
10074 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10076 e->vms_versions[e->vms_verscount] = -1;
10079 _ckvmssts(lib$find_file_end(&context));
10083 } /* end of collectversions() */
10086 * Read the next entry from the directory.
10088 /*{{{ struct dirent *readdir(DIR *dd)*/
10090 Perl_readdir(pTHX_ DIR *dd)
10092 struct dsc$descriptor_s res;
10094 unsigned long int tmpsts;
10095 unsigned long rsts;
10096 unsigned long flags = 0;
10097 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10098 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10100 /* Set up result descriptor, and get next file. */
10101 Newx(buff, VMS_MAXRSS, char);
10102 res.dsc$a_pointer = buff;
10103 res.dsc$w_length = VMS_MAXRSS - 1;
10104 res.dsc$b_dtype = DSC$K_DTYPE_T;
10105 res.dsc$b_class = DSC$K_CLASS_S;
10107 #ifdef VMS_LONGNAME_SUPPORT
10108 flags = LIB$M_FIL_LONG_NAMES;
10111 tmpsts = lib$find_file
10112 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10113 if (dd->context == 0)
10114 tmpsts = RMS$_NMF; /* None left. (should be set, but make sure) */
10116 if (!(tmpsts & 1)) {
10119 break; /* no more files considered success */
10121 SETERRNO(EACCES, tmpsts); break;
10123 SETERRNO(ENODEV, tmpsts); break;
10125 SETERRNO(ENOTDIR, tmpsts); break;
10126 case RMS$_FNF: case RMS$_DNF:
10127 SETERRNO(ENOENT, tmpsts); break;
10129 SETERRNO(EVMSERR, tmpsts);
10135 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10136 buff[res.dsc$w_length] = '\0';
10137 p = buff + res.dsc$w_length;
10138 while (--p >= buff) if (!isspace(*p)) break;
10140 if (!decc_efs_case_preserve) {
10141 for (p = buff; *p; p++) *p = _tolower(*p);
10144 /* Skip any directory component and just copy the name. */
10145 sts = vms_split_path
10160 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10162 /* In Unix report mode, remove the ".dir;1" from the name */
10163 /* if it is a real directory. */
10164 if (decc_filename_unix_report && decc_efs_charset) {
10165 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10169 ret_sts = flex_lstat(buff, &statbuf);
10170 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10177 /* Drop NULL extensions on UNIX file specification */
10178 if ((e_len == 1) && decc_readdir_dropdotnotype) {
10184 memcpy(dd->entry.d_name, n_spec, n_len + e_len);
10185 dd->entry.d_name[n_len + e_len] = '\0';
10186 dd->entry.d_namlen = n_len + e_len;
10188 /* Convert the filename to UNIX format if needed */
10189 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10191 /* Translate the encoded characters. */
10192 /* Fixme: Unicode handling could result in embedded 0 characters */
10193 if (strchr(dd->entry.d_name, '^') != NULL) {
10194 char new_name[256];
10196 p = dd->entry.d_name;
10199 int inchars_read, outchars_added;
10200 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10202 q += outchars_added;
10204 /* if outchars_added > 1, then this is a wide file specification */
10205 /* Wide file specifications need to be passed in Perl */
10206 /* counted strings apparently with a Unicode flag */
10209 dd->entry.d_namlen = my_strlcpy(dd->entry.d_name, new_name, sizeof(dd->entry.d_name));
10213 dd->entry.vms_verscount = 0;
10214 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10218 } /* end of readdir() */
10222 * Read the next entry from the directory -- thread-safe version.
10224 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10226 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10230 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10232 entry = readdir(dd);
10234 retval = ( *result == NULL ? errno : 0 );
10236 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10240 } /* end of readdir_r() */
10244 * Return something that can be used in a seekdir later.
10246 /*{{{ long telldir(DIR *dd)*/
10248 Perl_telldir(DIR *dd)
10255 * Return to a spot where we used to be. Brute force.
10257 /*{{{ void seekdir(DIR *dd,long count)*/
10259 Perl_seekdir(pTHX_ DIR *dd, long count)
10263 /* If we haven't done anything yet... */
10264 if (dd->count == 0)
10267 /* Remember some state, and clear it. */
10268 old_flags = dd->flags;
10269 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10270 _ckvmssts(lib$find_file_end(&dd->context));
10273 /* The increment is in readdir(). */
10274 for (dd->count = 0; dd->count < count; )
10277 dd->flags = old_flags;
10279 } /* end of seekdir() */
10282 /* VMS subprocess management
10284 * my_vfork() - just a vfork(), after setting a flag to record that
10285 * the current script is trying a Unix-style fork/exec.
10287 * vms_do_aexec() and vms_do_exec() are called in response to the
10288 * perl 'exec' function. If this follows a vfork call, then they
10289 * call out the regular perl routines in doio.c which do an
10290 * execvp (for those who really want to try this under VMS).
10291 * Otherwise, they do exactly what the perl docs say exec should
10292 * do - terminate the current script and invoke a new command
10293 * (See below for notes on command syntax.)
10295 * do_aspawn() and do_spawn() implement the VMS side of the perl
10296 * 'system' function.
10298 * Note on command arguments to perl 'exec' and 'system': When handled
10299 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10300 * are concatenated to form a DCL command string. If the first non-numeric
10301 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10302 * the command string is handed off to DCL directly. Otherwise,
10303 * the first token of the command is taken as the filespec of an image
10304 * to run. The filespec is expanded using a default type of '.EXE' and
10305 * the process defaults for device, directory, etc., and if found, the resultant
10306 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10307 * the command string as parameters. This is perhaps a bit complicated,
10308 * but I hope it will form a happy medium between what VMS folks expect
10309 * from lib$spawn and what Unix folks expect from exec.
10312 static int vfork_called;
10314 /*{{{int my_vfork(void)*/
10325 vms_execfree(struct dsc$descriptor_s *vmscmd)
10328 if (vmscmd->dsc$a_pointer) {
10329 PerlMem_free(vmscmd->dsc$a_pointer);
10331 PerlMem_free(vmscmd);
10336 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10338 char *junk, *tmps = NULL;
10346 tmps = SvPV(really,rlen);
10348 cmdlen += rlen + 1;
10353 for (idx++; idx <= sp; idx++) {
10355 junk = SvPVx(*idx,rlen);
10356 cmdlen += rlen ? rlen + 1 : 0;
10359 Newx(PL_Cmd, cmdlen+1, char);
10361 if (tmps && *tmps) {
10362 my_strlcpy(PL_Cmd, tmps, cmdlen + 1);
10365 else *PL_Cmd = '\0';
10366 while (++mark <= sp) {
10368 char *s = SvPVx(*mark,n_a);
10370 if (*PL_Cmd) my_strlcat(PL_Cmd, " ", cmdlen+1);
10371 my_strlcat(PL_Cmd, s, cmdlen+1);
10376 } /* end of setup_argstr() */
10379 static unsigned long int
10380 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10381 struct dsc$descriptor_s **pvmscmd)
10385 char image_name[NAM$C_MAXRSS+1];
10386 char image_argv[NAM$C_MAXRSS+1];
10387 $DESCRIPTOR(defdsc,".EXE");
10388 $DESCRIPTOR(defdsc2,".");
10389 struct dsc$descriptor_s resdsc;
10390 struct dsc$descriptor_s *vmscmd;
10391 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10392 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10393 char *s, *rest, *cp, *wordbreak;
10398 vmscmd = (struct dsc$descriptor_s *)PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10399 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10401 /* vmsspec is a DCL command buffer, not just a filename */
10402 vmsspec = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10403 if (vmsspec == NULL)
10404 _ckvmssts_noperl(SS$_INSFMEM);
10406 resspec = (char *)PerlMem_malloc(VMS_MAXRSS);
10407 if (resspec == NULL)
10408 _ckvmssts_noperl(SS$_INSFMEM);
10410 /* Make a copy for modification */
10411 cmdlen = strlen(incmd);
10412 cmd = (char *)PerlMem_malloc(cmdlen+1);
10413 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10414 my_strlcpy(cmd, incmd, cmdlen + 1);
10418 resdsc.dsc$a_pointer = resspec;
10419 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10420 resdsc.dsc$b_class = DSC$K_CLASS_S;
10421 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10423 vmscmd->dsc$a_pointer = NULL;
10424 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10425 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10426 vmscmd->dsc$w_length = 0;
10427 if (pvmscmd) *pvmscmd = vmscmd;
10429 if (suggest_quote) *suggest_quote = 0;
10431 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10433 PerlMem_free(vmsspec);
10434 PerlMem_free(resspec);
10435 return CLI$_BUFOVF; /* continuation lines currently unsupported */
10440 while (*s && isspace(*s)) s++;
10442 if (*s == '@' || *s == '$') {
10443 vmsspec[0] = *s; rest = s + 1;
10444 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10446 else { cp = vmsspec; rest = s; }
10448 /* If the first word is quoted, then we need to unquote it and
10449 * escape spaces within it. We'll expand into the resspec buffer,
10450 * then copy back into the cmd buffer, expanding the latter if
10453 if (*rest == '"') {
10458 int soff = s - cmd;
10460 for (cp2 = resspec;
10461 *rest && cp2 - resspec < (VMS_MAXRSS - 1);
10464 if (*rest == ' ') { /* Escape ' ' to '^_'. */
10470 else if (*rest == '"') {
10472 if (in_quote) { /* Must be closing quote. */
10485 /* Expand the command buffer if necessary. */
10486 if (clen > cmdlen) {
10487 cmd = (char *)PerlMem_realloc(cmd, clen);
10489 _ckvmssts_noperl(SS$_INSFMEM);
10490 /* Where we are may have changed, so recompute offsets */
10491 r = cmd + (r - s - soff);
10492 rest = cmd + (rest - s - soff);
10496 /* Shift the non-verb portion of the command (if any) up or
10497 * down as necessary.
10500 memmove(rest + clen - cmdlen, rest, s - soff + cmdlen - rest);
10502 /* Copy the unquoted and escaped command verb into place. */
10503 memcpy(r, resspec, cp2 - resspec);
10506 rest = r; /* Rewind for subsequent operations. */
10509 if (*rest == '.' || *rest == '/') {
10511 for (cp2 = resspec;
10512 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10513 rest++, cp2++) *cp2 = *rest;
10515 if (int_tovmsspec(resspec, cp, 0, NULL)) {
10518 /* When a UNIX spec with no file type is translated to VMS, */
10519 /* A trailing '.' is appended under ODS-5 rules. */
10520 /* Here we do not want that trailing "." as it prevents */
10521 /* Looking for a implied ".exe" type. */
10522 if (decc_efs_charset) {
10524 i = strlen(vmsspec);
10525 if (vmsspec[i-1] == '.') {
10526 vmsspec[i-1] = '\0';
10531 for (cp2 = vmsspec + strlen(vmsspec);
10532 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10533 rest++, cp2++) *cp2 = *rest;
10538 /* Intuit whether verb (first word of cmd) is a DCL command:
10539 * - if first nonspace char is '@', it's a DCL indirection
10541 * - if verb contains a filespec separator, it's not a DCL command
10542 * - if it doesn't, caller tells us whether to default to a DCL
10543 * command, or to a local image unless told it's DCL (by leading '$')
10547 if (suggest_quote) *suggest_quote = 1;
10549 char *filespec = strpbrk(s,":<[.;");
10550 rest = wordbreak = strpbrk(s," \"\t/");
10551 if (!wordbreak) wordbreak = s + strlen(s);
10552 if (*s == '$') check_img = 0;
10553 if (filespec && (filespec < wordbreak)) isdcl = 0;
10554 else isdcl = !check_img;
10559 imgdsc.dsc$a_pointer = s;
10560 imgdsc.dsc$w_length = wordbreak - s;
10561 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10563 _ckvmssts_noperl(lib$find_file_end(&cxt));
10564 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10565 if (!(retsts & 1) && *s == '$') {
10566 _ckvmssts_noperl(lib$find_file_end(&cxt));
10567 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10568 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10570 _ckvmssts_noperl(lib$find_file_end(&cxt));
10571 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10575 _ckvmssts_noperl(lib$find_file_end(&cxt));
10580 while (*s && !isspace(*s)) s++;
10583 /* check that it's really not DCL with no file extension */
10584 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10586 char b[256] = {0,0,0,0};
10587 read(fileno(fp), b, 256);
10588 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10592 /* Check for script */
10594 if ((b[0] == '#') && (b[1] == '!'))
10596 #ifdef ALTERNATE_SHEBANG
10598 shebang_len = strlen(ALTERNATE_SHEBANG);
10599 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10601 perlstr = strstr("perl",b);
10602 if (perlstr == NULL)
10610 if (shebang_len > 0) {
10613 char tmpspec[NAM$C_MAXRSS + 1];
10616 /* Image is following after white space */
10617 /*--------------------------------------*/
10618 while (isprint(b[i]) && isspace(b[i]))
10622 while (isprint(b[i]) && !isspace(b[i])) {
10623 tmpspec[j++] = b[i++];
10624 if (j >= NAM$C_MAXRSS)
10629 /* There may be some default parameters to the image */
10630 /*---------------------------------------------------*/
10632 while (isprint(b[i])) {
10633 image_argv[j++] = b[i++];
10634 if (j >= NAM$C_MAXRSS)
10637 while ((j > 0) && !isprint(image_argv[j-1]))
10641 /* It will need to be converted to VMS format and validated */
10642 if (tmpspec[0] != '\0') {
10645 /* Try to find the exact program requested to be run */
10646 /*---------------------------------------------------*/
10647 iname = int_rmsexpand
10648 (tmpspec, image_name, ".exe",
10649 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10650 if (iname != NULL) {
10651 if (cando_by_name_int
10652 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10653 /* MCR prefix needed */
10657 /* Try again with a null type */
10658 /*----------------------------*/
10659 iname = int_rmsexpand
10660 (tmpspec, image_name, ".",
10661 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10662 if (iname != NULL) {
10663 if (cando_by_name_int
10664 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10665 /* MCR prefix needed */
10671 /* Did we find the image to run the script? */
10672 /*------------------------------------------*/
10676 /* Assume DCL or foreign command exists */
10677 /*--------------------------------------*/
10678 tchr = strrchr(tmpspec, '/');
10679 if (tchr != NULL) {
10685 my_strlcpy(image_name, tchr, sizeof(image_name));
10693 if (check_img && isdcl) {
10695 PerlMem_free(resspec);
10696 PerlMem_free(vmsspec);
10700 if (cando_by_name(S_IXUSR,0,resspec)) {
10701 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10702 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10704 my_strlcpy(vmscmd->dsc$a_pointer,"$ MCR ", MAX_DCL_LINE_LENGTH);
10705 if (image_name[0] != 0) {
10706 my_strlcat(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10707 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10709 } else if (image_name[0] != 0) {
10710 my_strlcpy(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10711 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10713 my_strlcpy(vmscmd->dsc$a_pointer, "@", MAX_DCL_LINE_LENGTH);
10715 if (suggest_quote) *suggest_quote = 1;
10717 /* If there is an image name, use original command */
10718 if (image_name[0] == 0)
10719 my_strlcat(vmscmd->dsc$a_pointer, resspec, MAX_DCL_LINE_LENGTH);
10722 while (*rest && isspace(*rest)) rest++;
10725 if (image_argv[0] != 0) {
10726 my_strlcat(vmscmd->dsc$a_pointer, image_argv, MAX_DCL_LINE_LENGTH);
10727 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10733 rest_len = strlen(rest);
10734 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10735 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10736 my_strlcat(vmscmd->dsc$a_pointer, rest, MAX_DCL_LINE_LENGTH);
10738 retsts = CLI$_BUFOVF;
10740 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10742 PerlMem_free(vmsspec);
10743 PerlMem_free(resspec);
10744 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10750 /* It's either a DCL command or we couldn't find a suitable image */
10751 vmscmd->dsc$w_length = strlen(cmd);
10753 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(vmscmd->dsc$w_length + 1);
10754 my_strlcpy(vmscmd->dsc$a_pointer, cmd, vmscmd->dsc$w_length + 1);
10757 PerlMem_free(resspec);
10758 PerlMem_free(vmsspec);
10760 /* check if it's a symbol (for quoting purposes) */
10761 if (suggest_quote && !*suggest_quote) {
10763 char equiv[LNM$C_NAMLENGTH];
10764 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10765 eqvdsc.dsc$a_pointer = equiv;
10767 iss = lib$get_symbol(vmscmd,&eqvdsc);
10768 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10770 if (!(retsts & 1)) {
10771 /* just hand off status values likely to be due to user error */
10772 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10773 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10774 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10775 else { _ckvmssts_noperl(retsts); }
10778 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10780 } /* end of setup_cmddsc() */
10783 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10785 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10791 if (vfork_called) { /* this follows a vfork - act Unixish */
10793 if (vfork_called < 0) {
10794 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10797 else return do_aexec(really,mark,sp);
10799 /* no vfork - act VMSish */
10800 cmd = setup_argstr(aTHX_ really,mark,sp);
10801 exec_sts = vms_do_exec(cmd);
10802 Safefree(cmd); /* Clean up from setup_argstr() */
10807 } /* end of vms_do_aexec() */
10810 /* {{{bool vms_do_exec(char *cmd) */
10812 Perl_vms_do_exec(pTHX_ const char *cmd)
10814 struct dsc$descriptor_s *vmscmd;
10816 if (vfork_called) { /* this follows a vfork - act Unixish */
10818 if (vfork_called < 0) {
10819 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10822 else return do_exec(cmd);
10825 { /* no vfork - act VMSish */
10826 unsigned long int retsts;
10829 TAINT_PROPER("exec");
10830 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10831 retsts = lib$do_command(vmscmd);
10834 case RMS$_FNF: case RMS$_DNF:
10835 set_errno(ENOENT); break;
10837 set_errno(ENOTDIR); break;
10839 set_errno(ENODEV); break;
10841 set_errno(EACCES); break;
10843 set_errno(EINVAL); break;
10844 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10845 set_errno(E2BIG); break;
10846 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10847 _ckvmssts_noperl(retsts); /* fall through */
10848 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10849 set_errno(EVMSERR);
10851 set_vaxc_errno(retsts);
10852 if (ckWARN(WARN_EXEC)) {
10853 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
10854 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
10856 vms_execfree(vmscmd);
10861 } /* end of vms_do_exec() */
10864 int do_spawn2(pTHX_ const char *, int);
10867 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
10869 unsigned long int sts;
10875 /* We'll copy the (undocumented?) Win32 behavior and allow a
10876 * numeric first argument. But the only value we'll support
10877 * through do_aspawn is a value of 1, which means spawn without
10878 * waiting for completion -- other values are ignored.
10880 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
10882 flags = SvIVx(*mark);
10885 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
10886 flags = CLI$M_NOWAIT;
10890 cmd = setup_argstr(aTHX_ really, mark, sp);
10891 sts = do_spawn2(aTHX_ cmd, flags);
10892 /* pp_sys will clean up cmd */
10896 } /* end of do_aspawn() */
10900 /* {{{int do_spawn(char* cmd) */
10902 Perl_do_spawn(pTHX_ char* cmd)
10904 PERL_ARGS_ASSERT_DO_SPAWN;
10906 return do_spawn2(aTHX_ cmd, 0);
10910 /* {{{int do_spawn_nowait(char* cmd) */
10912 Perl_do_spawn_nowait(pTHX_ char* cmd)
10914 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
10916 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
10920 /* {{{int do_spawn2(char *cmd) */
10922 do_spawn2(pTHX_ const char *cmd, int flags)
10924 unsigned long int sts, substs;
10926 /* The caller of this routine expects to Safefree(PL_Cmd) */
10927 Newx(PL_Cmd,10,char);
10930 TAINT_PROPER("spawn");
10931 if (!cmd || !*cmd) {
10932 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
10935 case RMS$_FNF: case RMS$_DNF:
10936 set_errno(ENOENT); break;
10938 set_errno(ENOTDIR); break;
10940 set_errno(ENODEV); break;
10942 set_errno(EACCES); break;
10944 set_errno(EINVAL); break;
10945 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10946 set_errno(E2BIG); break;
10947 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10948 _ckvmssts_noperl(sts); /* fall through */
10949 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10950 set_errno(EVMSERR);
10952 set_vaxc_errno(sts);
10953 if (ckWARN(WARN_EXEC)) {
10954 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
10963 if (flags & CLI$M_NOWAIT)
10966 strcpy(mode, "nW");
10968 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
10971 /* sts will be the pid in the nowait case */
10974 } /* end of do_spawn2() */
10978 static unsigned int *sockflags, sockflagsize;
10981 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
10982 * routines found in some versions of the CRTL can't deal with sockets.
10983 * We don't shim the other file open routines since a socket isn't
10984 * likely to be opened by a name.
10986 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
10987 FILE *my_fdopen(int fd, const char *mode)
10989 FILE *fp = fdopen(fd, mode);
10992 unsigned int fdoff = fd / sizeof(unsigned int);
10993 Stat_t sbuf; /* native stat; we don't need flex_stat */
10994 if (!sockflagsize || fdoff > sockflagsize) {
10995 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
10996 else Newx (sockflags,fdoff+2,unsigned int);
10997 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
10998 sockflagsize = fdoff + 2;
11000 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11001 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11010 * Clear the corresponding bit when the (possibly) socket stream is closed.
11011 * There still a small hole: we miss an implicit close which might occur
11012 * via freopen(). >> Todo
11014 /*{{{ int my_fclose(FILE *fp)*/
11015 int my_fclose(FILE *fp) {
11017 unsigned int fd = fileno(fp);
11018 unsigned int fdoff = fd / sizeof(unsigned int);
11020 if (sockflagsize && fdoff < sockflagsize)
11021 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11029 * A simple fwrite replacement which outputs itmsz*nitm chars without
11030 * introducing record boundaries every itmsz chars.
11031 * We are using fputs, which depends on a terminating null. We may
11032 * well be writing binary data, so we need to accommodate not only
11033 * data with nulls sprinkled in the middle but also data with no null
11036 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11038 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11040 char *cp, *end, *cpd;
11042 unsigned int fd = fileno(dest);
11043 unsigned int fdoff = fd / sizeof(unsigned int);
11045 int bufsize = itmsz * nitm + 1;
11047 if (fdoff < sockflagsize &&
11048 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11049 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11053 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11054 memcpy( data, src, itmsz*nitm );
11055 data[itmsz*nitm] = '\0';
11057 end = data + itmsz * nitm;
11058 retval = (int) nitm; /* on success return # items written */
11061 while (cpd <= end) {
11062 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11063 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11065 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11069 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11072 } /* end of my_fwrite() */
11075 /*{{{ int my_flush(FILE *fp)*/
11077 Perl_my_flush(pTHX_ FILE *fp)
11080 if ((res = fflush(fp)) == 0 && fp) {
11081 #ifdef VMS_DO_SOCKETS
11083 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11085 res = fsync(fileno(fp));
11088 * If the flush succeeded but set end-of-file, we need to clear
11089 * the error because our caller may check ferror(). BTW, this
11090 * probably means we just flushed an empty file.
11092 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11098 /* fgetname() is not returning the correct file specifications when
11099 * decc_filename_unix_report mode is active. So we have to have it
11100 * aways return filenames in VMS mode and convert it ourselves.
11103 /*{{{ char * my_fgetname(FILE *fp, buf)*/
11105 Perl_my_fgetname(FILE *fp, char * buf) {
11109 retname = fgetname(fp, buf, 1);
11111 /* If we are in VMS mode, then we are done */
11112 if (!decc_filename_unix_report || (retname == NULL)) {
11116 /* Convert this to Unix format */
11117 vms_name = (char *)PerlMem_malloc(VMS_MAXRSS);
11118 my_strlcpy(vms_name, retname, VMS_MAXRSS);
11119 retname = int_tounixspec(vms_name, buf, NULL);
11120 PerlMem_free(vms_name);
11127 * Here are replacements for the following Unix routines in the VMS environment:
11128 * getpwuid Get information for a particular UIC or UID
11129 * getpwnam Get information for a named user
11130 * getpwent Get information for each user in the rights database
11131 * setpwent Reset search to the start of the rights database
11132 * endpwent Finish searching for users in the rights database
11134 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11135 * (defined in pwd.h), which contains the following fields:-
11137 * char *pw_name; Username (in lower case)
11138 * char *pw_passwd; Hashed password
11139 * unsigned int pw_uid; UIC
11140 * unsigned int pw_gid; UIC group number
11141 * char *pw_unixdir; Default device/directory (VMS-style)
11142 * char *pw_gecos; Owner name
11143 * char *pw_dir; Default device/directory (Unix-style)
11144 * char *pw_shell; Default CLI name (eg. DCL)
11146 * If the specified user does not exist, getpwuid and getpwnam return NULL.
11148 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11149 * not the UIC member number (eg. what's returned by getuid()),
11150 * getpwuid() can accept either as input (if uid is specified, the caller's
11151 * UIC group is used), though it won't recognise gid=0.
11153 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11154 * information about other users in your group or in other groups, respectively.
11155 * If the required privilege is not available, then these routines fill only
11156 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11159 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11162 /* sizes of various UAF record fields */
11163 #define UAI$S_USERNAME 12
11164 #define UAI$S_IDENT 31
11165 #define UAI$S_OWNER 31
11166 #define UAI$S_DEFDEV 31
11167 #define UAI$S_DEFDIR 63
11168 #define UAI$S_DEFCLI 31
11169 #define UAI$S_PWD 8
11171 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
11172 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11173 (uic).uic$v_group != UIC$K_WILD_GROUP)
11175 static char __empty[]= "";
11176 static struct passwd __passwd_empty=
11177 {(char *) __empty, (char *) __empty, 0, 0,
11178 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11179 static int contxt= 0;
11180 static struct passwd __pwdcache;
11181 static char __pw_namecache[UAI$S_IDENT+1];
11184 * This routine does most of the work extracting the user information.
11186 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11189 unsigned char length;
11190 char pw_gecos[UAI$S_OWNER+1];
11192 static union uicdef uic;
11194 unsigned char length;
11195 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11198 unsigned char length;
11199 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11202 unsigned char length;
11203 char pw_shell[UAI$S_DEFCLI+1];
11205 static char pw_passwd[UAI$S_PWD+1];
11207 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11208 struct dsc$descriptor_s name_desc;
11209 unsigned long int sts;
11211 static struct itmlst_3 itmlst[]= {
11212 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
11213 {sizeof(uic), UAI$_UIC, &uic, &luic},
11214 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
11215 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
11216 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
11217 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
11218 {0, 0, NULL, NULL}};
11220 name_desc.dsc$w_length= strlen(name);
11221 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11222 name_desc.dsc$b_class= DSC$K_CLASS_S;
11223 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11225 /* Note that sys$getuai returns many fields as counted strings. */
11226 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11227 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11228 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11230 else { _ckvmssts(sts); }
11231 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
11233 if ((int) owner.length < lowner) lowner= (int) owner.length;
11234 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11235 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11236 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11237 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11238 owner.pw_gecos[lowner]= '\0';
11239 defdev.pw_dir[ldefdev+ldefdir]= '\0';
11240 defcli.pw_shell[ldefcli]= '\0';
11241 if (valid_uic(uic)) {
11242 pwd->pw_uid= uic.uic$l_uic;
11243 pwd->pw_gid= uic.uic$v_group;
11246 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11247 pwd->pw_passwd= pw_passwd;
11248 pwd->pw_gecos= owner.pw_gecos;
11249 pwd->pw_dir= defdev.pw_dir;
11250 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11251 pwd->pw_shell= defcli.pw_shell;
11252 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11254 ldir= strlen(pwd->pw_unixdir) - 1;
11255 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11258 my_strlcpy(pwd->pw_unixdir, pwd->pw_dir, sizeof(pwd->pw_unixdir));
11259 if (!decc_efs_case_preserve)
11260 __mystrtolower(pwd->pw_unixdir);
11265 * Get information for a named user.
11267 /*{{{struct passwd *getpwnam(char *name)*/
11268 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
11270 struct dsc$descriptor_s name_desc;
11272 unsigned long int sts;
11274 __pwdcache = __passwd_empty;
11275 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11276 /* We still may be able to determine pw_uid and pw_gid */
11277 name_desc.dsc$w_length= strlen(name);
11278 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11279 name_desc.dsc$b_class= DSC$K_CLASS_S;
11280 name_desc.dsc$a_pointer= (char *) name;
11281 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11282 __pwdcache.pw_uid= uic.uic$l_uic;
11283 __pwdcache.pw_gid= uic.uic$v_group;
11286 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11287 set_vaxc_errno(sts);
11288 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11291 else { _ckvmssts(sts); }
11294 my_strlcpy(__pw_namecache, name, sizeof(__pw_namecache));
11295 __pwdcache.pw_name= __pw_namecache;
11296 return &__pwdcache;
11297 } /* end of my_getpwnam() */
11301 * Get information for a particular UIC or UID.
11302 * Called by my_getpwent with uid=-1 to list all users.
11304 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11305 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
11307 const $DESCRIPTOR(name_desc,__pw_namecache);
11308 unsigned short lname;
11310 unsigned long int status;
11312 if (uid == (unsigned int) -1) {
11314 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11315 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11316 set_vaxc_errno(status);
11317 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11321 else { _ckvmssts(status); }
11322 } while (!valid_uic (uic));
11325 uic.uic$l_uic= uid;
11326 if (!uic.uic$v_group)
11327 uic.uic$v_group= PerlProc_getgid();
11328 if (valid_uic(uic))
11329 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11330 else status = SS$_IVIDENT;
11331 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11332 status == RMS$_PRV) {
11333 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11336 else { _ckvmssts(status); }
11338 __pw_namecache[lname]= '\0';
11339 __mystrtolower(__pw_namecache);
11341 __pwdcache = __passwd_empty;
11342 __pwdcache.pw_name = __pw_namecache;
11344 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11345 The identifier's value is usually the UIC, but it doesn't have to be,
11346 so if we can, we let fillpasswd update this. */
11347 __pwdcache.pw_uid = uic.uic$l_uic;
11348 __pwdcache.pw_gid = uic.uic$v_group;
11350 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11351 return &__pwdcache;
11353 } /* end of my_getpwuid() */
11357 * Get information for next user.
11359 /*{{{struct passwd *my_getpwent()*/
11360 struct passwd *Perl_my_getpwent(pTHX)
11362 return (my_getpwuid((unsigned int) -1));
11367 * Finish searching rights database for users.
11369 /*{{{void my_endpwent()*/
11370 void Perl_my_endpwent(pTHX)
11373 _ckvmssts(sys$finish_rdb(&contxt));
11379 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11380 * my_utime(), and flex_stat(), all of which operate on UTC unless
11381 * VMSISH_TIMES is true.
11383 /* method used to handle UTC conversions:
11384 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
11386 static int gmtime_emulation_type;
11387 /* number of secs to add to UTC POSIX-style time to get local time */
11388 static long int utc_offset_secs;
11390 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11391 * in vmsish.h. #undef them here so we can call the CRTL routines
11399 static time_t toutc_dst(time_t loc) {
11402 if ((rsltmp = localtime(&loc)) == NULL) return -1u;
11403 loc -= utc_offset_secs;
11404 if (rsltmp->tm_isdst) loc -= 3600;
11407 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11408 ((gmtime_emulation_type || my_time(NULL)), \
11409 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11410 ((secs) - utc_offset_secs))))
11412 static time_t toloc_dst(time_t utc) {
11415 utc += utc_offset_secs;
11416 if ((rsltmp = localtime(&utc)) == NULL) return -1u;
11417 if (rsltmp->tm_isdst) utc += 3600;
11420 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11421 ((gmtime_emulation_type || my_time(NULL)), \
11422 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11423 ((secs) + utc_offset_secs))))
11425 /* my_time(), my_localtime(), my_gmtime()
11426 * By default traffic in UTC time values, using CRTL gmtime() or
11427 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11428 * Note: We need to use these functions even when the CRTL has working
11429 * UTC support, since they also handle C<use vmsish qw(times);>
11431 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
11432 * Modified by Charles Bailey <bailey@newman.upenn.edu>
11435 /*{{{time_t my_time(time_t *timep)*/
11436 time_t Perl_my_time(pTHX_ time_t *timep)
11441 if (gmtime_emulation_type == 0) {
11442 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
11443 /* results of calls to gmtime() and localtime() */
11444 /* for same &base */
11446 gmtime_emulation_type++;
11447 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11448 char off[LNM$C_NAMLENGTH+1];;
11450 gmtime_emulation_type++;
11451 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11452 gmtime_emulation_type++;
11453 utc_offset_secs = 0;
11454 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11456 else { utc_offset_secs = atol(off); }
11458 else { /* We've got a working gmtime() */
11459 struct tm gmt, local;
11462 tm_p = localtime(&base);
11464 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
11465 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11466 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
11467 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
11472 # ifdef VMSISH_TIME
11473 if (VMSISH_TIME) when = _toloc(when);
11475 if (timep != NULL) *timep = when;
11478 } /* end of my_time() */
11482 /*{{{struct tm *my_gmtime(const time_t *timep)*/
11484 Perl_my_gmtime(pTHX_ const time_t *timep)
11489 if (timep == NULL) {
11490 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11493 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11496 # ifdef VMSISH_TIME
11497 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11499 return gmtime(&when);
11500 } /* end of my_gmtime() */
11504 /*{{{struct tm *my_localtime(const time_t *timep)*/
11506 Perl_my_localtime(pTHX_ const time_t *timep)
11510 if (timep == NULL) {
11511 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11514 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11515 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
11518 # ifdef VMSISH_TIME
11519 if (VMSISH_TIME) when = _toutc(when);
11521 /* CRTL localtime() wants UTC as input, does tz correction itself */
11522 return localtime(&when);
11523 } /* end of my_localtime() */
11526 /* Reset definitions for later calls */
11527 #define gmtime(t) my_gmtime(t)
11528 #define localtime(t) my_localtime(t)
11529 #define time(t) my_time(t)
11532 /* my_utime - update modification/access time of a file
11534 * VMS 7.3 and later implementation
11535 * Only the UTC translation is home-grown. The rest is handled by the
11536 * CRTL utime(), which will take into account the relevant feature
11537 * logicals and ODS-5 volume characteristics for true access times.
11539 * pre VMS 7.3 implementation:
11540 * The calling sequence is identical to POSIX utime(), but under
11541 * VMS with ODS-2, only the modification time is changed; ODS-2 does
11542 * not maintain access times. Restrictions differ from the POSIX
11543 * definition in that the time can be changed as long as the
11544 * caller has permission to execute the necessary IO$_MODIFY $QIO;
11545 * no separate checks are made to insure that the caller is the
11546 * owner of the file or has special privs enabled.
11547 * Code here is based on Joe Meadows' FILE utility.
11551 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11552 * to VMS epoch (01-JAN-1858 00:00:00.00)
11553 * in 100 ns intervals.
11555 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11557 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11558 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
11560 #if __CRTL_VER >= 70300000
11561 struct utimbuf utc_utimes, *utc_utimesp;
11563 if (utimes != NULL) {
11564 utc_utimes.actime = utimes->actime;
11565 utc_utimes.modtime = utimes->modtime;
11566 # ifdef VMSISH_TIME
11567 /* If input was local; convert to UTC for sys svc */
11569 utc_utimes.actime = _toutc(utimes->actime);
11570 utc_utimes.modtime = _toutc(utimes->modtime);
11573 utc_utimesp = &utc_utimes;
11576 utc_utimesp = NULL;
11579 return utime(file, utc_utimesp);
11581 #else /* __CRTL_VER < 70300000 */
11585 long int bintime[2], len = 2, lowbit, unixtime,
11586 secscale = 10000000; /* seconds --> 100 ns intervals */
11587 unsigned long int chan, iosb[2], retsts;
11588 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
11589 struct FAB myfab = cc$rms_fab;
11590 struct NAM mynam = cc$rms_nam;
11591 #if defined (__DECC) && defined (__VAX)
11592 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
11593 * at least through VMS V6.1, which causes a type-conversion warning.
11595 # pragma message save
11596 # pragma message disable cvtdiftypes
11598 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
11599 struct fibdef myfib;
11600 #if defined (__DECC) && defined (__VAX)
11601 /* This should be right after the declaration of myatr, but due
11602 * to a bug in VAX DEC C, this takes effect a statement early.
11604 # pragma message restore
11606 /* cast ok for read only parameter */
11607 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
11608 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
11609 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
11611 if (file == NULL || *file == '\0') {
11612 SETERRNO(ENOENT, LIB$_INVARG);
11616 /* Convert to VMS format ensuring that it will fit in 255 characters */
11617 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
11618 SETERRNO(ENOENT, LIB$_INVARG);
11621 if (utimes != NULL) {
11622 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
11623 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
11624 * Since time_t is unsigned long int, and lib$emul takes a signed long int
11625 * as input, we force the sign bit to be clear by shifting unixtime right
11626 * one bit, then multiplying by an extra factor of 2 in lib$emul().
11628 lowbit = (utimes->modtime & 1) ? secscale : 0;
11629 unixtime = (long int) utimes->modtime;
11630 # ifdef VMSISH_TIME
11631 /* If input was UTC; convert to local for sys svc */
11632 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
11634 unixtime >>= 1; secscale <<= 1;
11635 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
11636 if (!(retsts & 1)) {
11637 SETERRNO(EVMSERR, retsts);
11640 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
11641 if (!(retsts & 1)) {
11642 SETERRNO(EVMSERR, retsts);
11647 /* Just get the current time in VMS format directly */
11648 retsts = sys$gettim(bintime);
11649 if (!(retsts & 1)) {
11650 SETERRNO(EVMSERR, retsts);
11655 myfab.fab$l_fna = vmsspec;
11656 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
11657 myfab.fab$l_nam = &mynam;
11658 mynam.nam$l_esa = esa;
11659 mynam.nam$b_ess = (unsigned char) sizeof esa;
11660 mynam.nam$l_rsa = rsa;
11661 mynam.nam$b_rss = (unsigned char) sizeof rsa;
11662 if (decc_efs_case_preserve)
11663 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
11665 /* Look for the file to be affected, letting RMS parse the file
11666 * specification for us as well. I have set errno using only
11667 * values documented in the utime() man page for VMS POSIX.
11669 retsts = sys$parse(&myfab,0,0);
11670 if (!(retsts & 1)) {
11671 set_vaxc_errno(retsts);
11672 if (retsts == RMS$_PRV) set_errno(EACCES);
11673 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
11674 else set_errno(EVMSERR);
11677 retsts = sys$search(&myfab,0,0);
11678 if (!(retsts & 1)) {
11679 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11680 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11681 set_vaxc_errno(retsts);
11682 if (retsts == RMS$_PRV) set_errno(EACCES);
11683 else if (retsts == RMS$_FNF) set_errno(ENOENT);
11684 else set_errno(EVMSERR);
11688 devdsc.dsc$w_length = mynam.nam$b_dev;
11689 /* cast ok for read only parameter */
11690 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
11692 retsts = sys$assign(&devdsc,&chan,0,0);
11693 if (!(retsts & 1)) {
11694 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11695 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11696 set_vaxc_errno(retsts);
11697 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
11698 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
11699 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
11700 else set_errno(EVMSERR);
11704 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
11705 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
11707 memset((void *) &myfib, 0, sizeof myfib);
11708 #if defined(__DECC) || defined(__DECCXX)
11709 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
11710 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
11711 /* This prevents the revision time of the file being reset to the current
11712 * time as a result of our IO$_MODIFY $QIO. */
11713 myfib.fib$l_acctl = FIB$M_NORECORD;
11715 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
11716 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
11717 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
11719 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
11720 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11721 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11722 _ckvmssts(sys$dassgn(chan));
11723 if (retsts & 1) retsts = iosb[0];
11724 if (!(retsts & 1)) {
11725 set_vaxc_errno(retsts);
11726 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11727 else set_errno(EVMSERR);
11733 #endif /* #if __CRTL_VER >= 70300000 */
11735 } /* end of my_utime() */
11739 * flex_stat, flex_lstat, flex_fstat
11740 * basic stat, but gets it right when asked to stat
11741 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
11744 #ifndef _USE_STD_STAT
11745 /* encode_dev packs a VMS device name string into an integer to allow
11746 * simple comparisons. This can be used, for example, to check whether two
11747 * files are located on the same device, by comparing their encoded device
11748 * names. Even a string comparison would not do, because stat() reuses the
11749 * device name buffer for each call; so without encode_dev, it would be
11750 * necessary to save the buffer and use strcmp (this would mean a number of
11751 * changes to the standard Perl code, to say nothing of what a Perl script
11752 * would have to do.
11754 * The device lock id, if it exists, should be unique (unless perhaps compared
11755 * with lock ids transferred from other nodes). We have a lock id if the disk is
11756 * mounted cluster-wide, which is when we tend to get long (host-qualified)
11757 * device names. Thus we use the lock id in preference, and only if that isn't
11758 * available, do we try to pack the device name into an integer (flagged by
11759 * the sign bit (LOCKID_MASK) being set).
11761 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
11762 * name and its encoded form, but it seems very unlikely that we will find
11763 * two files on different disks that share the same encoded device names,
11764 * and even more remote that they will share the same file id (if the test
11765 * is to check for the same file).
11767 * A better method might be to use sys$device_scan on the first call, and to
11768 * search for the device, returning an index into the cached array.
11769 * The number returned would be more intelligible.
11770 * This is probably not worth it, and anyway would take quite a bit longer
11771 * on the first call.
11773 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
11774 static mydev_t encode_dev (pTHX_ const char *dev)
11777 unsigned long int f;
11782 if (!dev || !dev[0]) return 0;
11786 struct dsc$descriptor_s dev_desc;
11787 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
11789 /* For cluster-mounted disks, the disk lock identifier is unique, so we
11790 can try that first. */
11791 dev_desc.dsc$w_length = strlen (dev);
11792 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
11793 dev_desc.dsc$b_class = DSC$K_CLASS_S;
11794 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
11795 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
11796 if (!$VMS_STATUS_SUCCESS(status)) {
11798 case SS$_NOSUCHDEV:
11799 SETERRNO(ENODEV, status);
11805 if (lockid) return (lockid & ~LOCKID_MASK);
11809 /* Otherwise we try to encode the device name */
11813 for (q = dev + strlen(dev); q--; q >= dev) {
11818 else if (isalpha (toupper (*q)))
11819 c= toupper (*q) - 'A' + (char)10;
11821 continue; /* Skip '$'s */
11823 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
11825 enc += f * (unsigned long int) c;
11827 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
11829 } /* end of encode_dev() */
11830 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11831 device_no = encode_dev(aTHX_ devname)
11833 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11834 device_no = new_dev_no
11838 is_null_device(const char *name)
11840 if (decc_bug_devnull != 0) {
11841 if (strncmp("/dev/null", name, 9) == 0)
11844 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
11845 The underscore prefix, controller letter, and unit number are
11846 independently optional; for our purposes, the colon punctuation
11847 is not. The colon can be trailed by optional directory and/or
11848 filename, but two consecutive colons indicates a nodename rather
11849 than a device. [pr] */
11850 if (*name == '_') ++name;
11851 if (tolower(*name++) != 'n') return 0;
11852 if (tolower(*name++) != 'l') return 0;
11853 if (tolower(*name) == 'a') ++name;
11854 if (*name == '0') ++name;
11855 return (*name++ == ':') && (*name != ':');
11859 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
11861 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
11864 Perl_cando_by_name_int
11865 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
11867 char usrname[L_cuserid];
11868 struct dsc$descriptor_s usrdsc =
11869 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
11870 char *vmsname = NULL, *fileified = NULL;
11871 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
11872 unsigned short int retlen, trnlnm_iter_count;
11873 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11874 union prvdef curprv;
11875 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
11876 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
11877 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
11878 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
11879 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
11881 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
11883 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11885 static int profile_context = -1;
11887 if (!fname || !*fname) return FALSE;
11889 /* Make sure we expand logical names, since sys$check_access doesn't */
11890 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
11891 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11892 if (!strpbrk(fname,"/]>:")) {
11893 my_strlcpy(fileified, fname, VMS_MAXRSS);
11894 trnlnm_iter_count = 0;
11895 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
11896 trnlnm_iter_count++;
11897 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
11902 vmsname = (char *)PerlMem_malloc(VMS_MAXRSS);
11903 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11904 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
11905 /* Don't know if already in VMS format, so make sure */
11906 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
11907 PerlMem_free(fileified);
11908 PerlMem_free(vmsname);
11913 my_strlcpy(vmsname, fname, VMS_MAXRSS);
11916 /* sys$check_access needs a file spec, not a directory spec.
11917 * flex_stat now will handle a null thread context during startup.
11920 retlen = namdsc.dsc$w_length = strlen(vmsname);
11921 if (vmsname[retlen-1] == ']'
11922 || vmsname[retlen-1] == '>'
11923 || vmsname[retlen-1] == ':'
11924 || (!flex_stat_int(vmsname, &st, 1) &&
11925 S_ISDIR(st.st_mode))) {
11927 if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
11928 PerlMem_free(fileified);
11929 PerlMem_free(vmsname);
11938 retlen = namdsc.dsc$w_length = strlen(fname);
11939 namdsc.dsc$a_pointer = (char *)fname;
11942 case S_IXUSR: case S_IXGRP: case S_IXOTH:
11943 access = ARM$M_EXECUTE;
11944 flags = CHP$M_READ;
11946 case S_IRUSR: case S_IRGRP: case S_IROTH:
11947 access = ARM$M_READ;
11948 flags = CHP$M_READ | CHP$M_USEREADALL;
11950 case S_IWUSR: case S_IWGRP: case S_IWOTH:
11951 access = ARM$M_WRITE;
11952 flags = CHP$M_READ | CHP$M_WRITE;
11954 case S_IDUSR: case S_IDGRP: case S_IDOTH:
11955 access = ARM$M_DELETE;
11956 flags = CHP$M_READ | CHP$M_WRITE;
11959 if (fileified != NULL)
11960 PerlMem_free(fileified);
11961 if (vmsname != NULL)
11962 PerlMem_free(vmsname);
11966 /* Before we call $check_access, create a user profile with the current
11967 * process privs since otherwise it just uses the default privs from the
11968 * UAF and might give false positives or negatives. This only works on
11969 * VMS versions v6.0 and later since that's when sys$create_user_profile
11970 * became available.
11973 /* get current process privs and username */
11974 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
11975 _ckvmssts_noperl(iosb[0]);
11977 /* find out the space required for the profile */
11978 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
11979 &usrprodsc.dsc$w_length,&profile_context));
11981 /* allocate space for the profile and get it filled in */
11982 usrprodsc.dsc$a_pointer = (char *)PerlMem_malloc(usrprodsc.dsc$w_length);
11983 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11984 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
11985 &usrprodsc.dsc$w_length,&profile_context));
11987 /* use the profile to check access to the file; free profile & analyze results */
11988 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
11989 PerlMem_free(usrprodsc.dsc$a_pointer);
11990 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
11992 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
11993 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
11994 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
11995 set_vaxc_errno(retsts);
11996 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11997 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
11998 else set_errno(ENOENT);
11999 if (fileified != NULL)
12000 PerlMem_free(fileified);
12001 if (vmsname != NULL)
12002 PerlMem_free(vmsname);
12005 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12006 if (fileified != NULL)
12007 PerlMem_free(fileified);
12008 if (vmsname != NULL)
12009 PerlMem_free(vmsname);
12012 _ckvmssts_noperl(retsts);
12014 if (fileified != NULL)
12015 PerlMem_free(fileified);
12016 if (vmsname != NULL)
12017 PerlMem_free(vmsname);
12018 return FALSE; /* Should never get here */
12022 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
12023 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12024 * subset of the applicable information.
12027 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12029 return cando_by_name_int
12030 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12031 } /* end of cando() */
12035 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12037 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12039 return cando_by_name_int(bit, effective, fname, 0);
12041 } /* end of cando_by_name() */
12045 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12047 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12049 dSAVE_ERRNO; /* fstat may set this even on success */
12050 if (!fstat(fd, &statbufp->crtl_stat)) {
12052 char *vms_filename;
12053 vms_filename = (char *)PerlMem_malloc(VMS_MAXRSS);
12054 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12056 /* Save name for cando by name in VMS format */
12057 cptr = getname(fd, vms_filename, 1);
12059 /* This should not happen, but just in case */
12060 if (cptr == NULL) {
12061 statbufp->st_devnam[0] = 0;
12064 /* Make sure that the saved name fits in 255 characters */
12065 cptr = int_rmsexpand_vms
12067 statbufp->st_devnam,
12070 statbufp->st_devnam[0] = 0;
12072 PerlMem_free(vms_filename);
12074 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12076 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12078 # ifdef VMSISH_TIME
12080 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12081 statbufp->st_atime = _toloc(statbufp->st_atime);
12082 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12090 } /* end of flex_fstat() */
12094 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12096 char *temp_fspec = NULL;
12097 char *fileified = NULL;
12098 const char *save_spec;
12102 char already_fileified = 0;
12110 if (decc_bug_devnull != 0) {
12111 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12112 memset(statbufp,0,sizeof *statbufp);
12113 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12114 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12115 statbufp->st_uid = 0x00010001;
12116 statbufp->st_gid = 0x0001;
12117 time((time_t *)&statbufp->st_mtime);
12118 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12125 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12127 * If we are in POSIX filespec mode, accept the filename as is.
12129 if (decc_posix_compliant_pathnames == 0) {
12132 /* Try for a simple stat first. If fspec contains a filename without
12133 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12134 * and sea:[wine.dark]water. exist, the CRTL prefers the directory here.
12135 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12136 * not sea:[wine.dark]., if the latter exists. If the intended target is
12137 * the file with null type, specify this by calling flex_stat() with
12138 * a '.' at the end of fspec.
12141 if (lstat_flag == 0)
12142 retval = stat(fspec, &statbufp->crtl_stat);
12144 retval = lstat(fspec, &statbufp->crtl_stat);
12150 /* In the odd case where we have write but not read access
12151 * to a directory, stat('foo.DIR') works but stat('foo') doesn't.
12153 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12154 if (fileified == NULL)
12155 _ckvmssts_noperl(SS$_INSFMEM);
12157 ret_spec = int_fileify_dirspec(fspec, fileified, NULL);
12158 if (ret_spec != NULL) {
12159 if (lstat_flag == 0)
12160 retval = stat(fileified, &statbufp->crtl_stat);
12162 retval = lstat(fileified, &statbufp->crtl_stat);
12163 save_spec = fileified;
12164 already_fileified = 1;
12168 if (retval && vms_bug_stat_filename) {
12170 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
12171 if (temp_fspec == NULL)
12172 _ckvmssts_noperl(SS$_INSFMEM);
12174 /* We should try again as a vmsified file specification. */
12176 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12177 if (ret_spec != NULL) {
12178 if (lstat_flag == 0)
12179 retval = stat(temp_fspec, &statbufp->crtl_stat);
12181 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12182 save_spec = temp_fspec;
12187 /* Last chance - allow multiple dots without EFS CHARSET */
12188 /* The CRTL stat() falls down hard on multi-dot filenames in unix
12189 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12190 * enable it if it isn't already.
12192 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12193 if (!decc_efs_charset && (decc_efs_charset_index > 0))
12194 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12196 if (lstat_flag == 0)
12197 retval = stat(fspec, &statbufp->crtl_stat);
12199 retval = lstat(fspec, &statbufp->crtl_stat);
12201 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12202 if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12203 decc$feature_set_value(decc_efs_charset_index, 1, 0);
12209 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12211 if (lstat_flag == 0)
12212 retval = stat(temp_fspec, &statbufp->crtl_stat);
12214 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12215 save_spec = temp_fspec;
12219 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12220 /* As you were... */
12221 if (!decc_efs_charset)
12222 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12227 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12229 /* If this is an lstat, do not follow the link */
12231 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12233 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12234 /* If we used the efs_hack above, we must also use it here for */
12235 /* perl_cando to work */
12236 if (efs_hack && (decc_efs_charset_index > 0)) {
12237 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12241 /* If we've got a directory, save a fileified, expanded version of it
12242 * in st_devnam. If not a directory, just an expanded version.
12244 if (S_ISDIR(statbufp->st_mode) && !already_fileified) {
12245 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12246 if (fileified == NULL)
12247 _ckvmssts_noperl(SS$_INSFMEM);
12249 cptr = do_fileify_dirspec(save_spec, fileified, 0, NULL);
12251 save_spec = fileified;
12254 cptr = int_rmsexpand(save_spec,
12255 statbufp->st_devnam,
12261 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12262 if (efs_hack && (decc_efs_charset_index > 0)) {
12263 decc$feature_set_value(decc_efs_charset, 1, 0);
12267 /* Fix me: If this is NULL then stat found a file, and we could */
12268 /* not convert the specification to VMS - Should never happen */
12270 statbufp->st_devnam[0] = 0;
12272 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12274 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12275 # ifdef VMSISH_TIME
12277 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12278 statbufp->st_atime = _toloc(statbufp->st_atime);
12279 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12283 /* If we were successful, leave errno where we found it */
12284 if (retval == 0) RESTORE_ERRNO;
12286 PerlMem_free(temp_fspec);
12288 PerlMem_free(fileified);
12291 } /* end of flex_stat_int() */
12294 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12296 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12298 return flex_stat_int(fspec, statbufp, 0);
12302 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12304 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12306 return flex_stat_int(fspec, statbufp, 1);
12311 /*{{{char *my_getlogin()*/
12312 /* VMS cuserid == Unix getlogin, except calling sequence */
12316 static char user[L_cuserid];
12317 return cuserid(user);
12322 /* rmscopy - copy a file using VMS RMS routines
12324 * Copies contents and attributes of spec_in to spec_out, except owner
12325 * and protection information. Name and type of spec_in are used as
12326 * defaults for spec_out. The third parameter specifies whether rmscopy()
12327 * should try to propagate timestamps from the input file to the output file.
12328 * If it is less than 0, no timestamps are preserved. If it is 0, then
12329 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12330 * propagated to the output file at creation iff the output file specification
12331 * did not contain an explicit name or type, and the revision date is always
12332 * updated at the end of the copy operation. If it is greater than 0, then
12333 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12334 * other than the revision date should be propagated, and bit 1 indicates
12335 * that the revision date should be propagated.
12337 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12339 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12340 * Incorporates, with permission, some code from EZCOPY by Tim Adye
12341 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12342 * as part of the Perl standard distribution under the terms of the
12343 * GNU General Public License or the Perl Artistic License. Copies
12344 * of each may be found in the Perl standard distribution.
12346 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12348 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12350 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12351 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12352 unsigned long int sts;
12354 struct FAB fab_in, fab_out;
12355 struct RAB rab_in, rab_out;
12356 rms_setup_nam(nam);
12357 rms_setup_nam(nam_out);
12358 struct XABDAT xabdat;
12359 struct XABFHC xabfhc;
12360 struct XABRDT xabrdt;
12361 struct XABSUM xabsum;
12363 vmsin = (char *)PerlMem_malloc(VMS_MAXRSS);
12364 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12365 vmsout = (char *)PerlMem_malloc(VMS_MAXRSS);
12366 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12367 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12368 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
12369 PerlMem_free(vmsin);
12370 PerlMem_free(vmsout);
12371 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12375 esa = (char *)PerlMem_malloc(VMS_MAXRSS);
12376 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12378 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12379 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
12380 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12382 fab_in = cc$rms_fab;
12383 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12384 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12385 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12386 fab_in.fab$l_fop = FAB$M_SQO;
12387 rms_bind_fab_nam(fab_in, nam);
12388 fab_in.fab$l_xab = (void *) &xabdat;
12390 rsa = (char *)PerlMem_malloc(VMS_MAXRSS);
12391 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12393 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12394 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
12395 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12397 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12398 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12399 rms_nam_esl(nam) = 0;
12400 rms_nam_rsl(nam) = 0;
12401 rms_nam_esll(nam) = 0;
12402 rms_nam_rsll(nam) = 0;
12403 #ifdef NAM$M_NO_SHORT_UPCASE
12404 if (decc_efs_case_preserve)
12405 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
12408 xabdat = cc$rms_xabdat; /* To get creation date */
12409 xabdat.xab$l_nxt = (void *) &xabfhc;
12411 xabfhc = cc$rms_xabfhc; /* To get record length */
12412 xabfhc.xab$l_nxt = (void *) &xabsum;
12414 xabsum = cc$rms_xabsum; /* To get key and area information */
12416 if (!((sts = sys$open(&fab_in)) & 1)) {
12417 PerlMem_free(vmsin);
12418 PerlMem_free(vmsout);
12421 PerlMem_free(esal);
12424 PerlMem_free(rsal);
12425 set_vaxc_errno(sts);
12427 case RMS$_FNF: case RMS$_DNF:
12428 set_errno(ENOENT); break;
12430 set_errno(ENOTDIR); break;
12432 set_errno(ENODEV); break;
12434 set_errno(EINVAL); break;
12436 set_errno(EACCES); break;
12438 set_errno(EVMSERR);
12445 fab_out.fab$w_ifi = 0;
12446 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12447 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12448 fab_out.fab$l_fop = FAB$M_SQO;
12449 rms_bind_fab_nam(fab_out, nam_out);
12450 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12451 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12452 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
12453 esa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12454 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12455 rsa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12456 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12459 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12460 esal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12461 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12462 rsal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12463 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12465 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
12466 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
12468 if (preserve_dates == 0) { /* Act like DCL COPY */
12469 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
12470 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
12471 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
12472 PerlMem_free(vmsin);
12473 PerlMem_free(vmsout);
12476 PerlMem_free(esal);
12479 PerlMem_free(rsal);
12480 PerlMem_free(esa_out);
12481 if (esal_out != NULL)
12482 PerlMem_free(esal_out);
12483 PerlMem_free(rsa_out);
12484 if (rsal_out != NULL)
12485 PerlMem_free(rsal_out);
12486 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12487 set_vaxc_errno(sts);
12490 fab_out.fab$l_xab = (void *) &xabdat;
12491 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12492 preserve_dates = 1;
12494 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
12495 preserve_dates =0; /* bitmask from this point forward */
12497 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
12498 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
12499 PerlMem_free(vmsin);
12500 PerlMem_free(vmsout);
12503 PerlMem_free(esal);
12506 PerlMem_free(rsal);
12507 PerlMem_free(esa_out);
12508 if (esal_out != NULL)
12509 PerlMem_free(esal_out);
12510 PerlMem_free(rsa_out);
12511 if (rsal_out != NULL)
12512 PerlMem_free(rsal_out);
12513 set_vaxc_errno(sts);
12516 set_errno(ENOENT); break;
12518 set_errno(ENOTDIR); break;
12520 set_errno(ENODEV); break;
12522 set_errno(EINVAL); break;
12524 set_errno(EACCES); break;
12526 set_errno(EVMSERR);
12530 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
12531 if (preserve_dates & 2) {
12532 /* sys$close() will process xabrdt, not xabdat */
12533 xabrdt = cc$rms_xabrdt;
12535 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12537 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
12538 * is unsigned long[2], while DECC & VAXC use a struct */
12539 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
12541 fab_out.fab$l_xab = (void *) &xabrdt;
12544 ubf = (char *)PerlMem_malloc(32256);
12545 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12546 rab_in = cc$rms_rab;
12547 rab_in.rab$l_fab = &fab_in;
12548 rab_in.rab$l_rop = RAB$M_BIO;
12549 rab_in.rab$l_ubf = ubf;
12550 rab_in.rab$w_usz = 32256;
12551 if (!((sts = sys$connect(&rab_in)) & 1)) {
12552 sys$close(&fab_in); sys$close(&fab_out);
12553 PerlMem_free(vmsin);
12554 PerlMem_free(vmsout);
12558 PerlMem_free(esal);
12561 PerlMem_free(rsal);
12562 PerlMem_free(esa_out);
12563 if (esal_out != NULL)
12564 PerlMem_free(esal_out);
12565 PerlMem_free(rsa_out);
12566 if (rsal_out != NULL)
12567 PerlMem_free(rsal_out);
12568 set_errno(EVMSERR); set_vaxc_errno(sts);
12572 rab_out = cc$rms_rab;
12573 rab_out.rab$l_fab = &fab_out;
12574 rab_out.rab$l_rbf = ubf;
12575 if (!((sts = sys$connect(&rab_out)) & 1)) {
12576 sys$close(&fab_in); sys$close(&fab_out);
12577 PerlMem_free(vmsin);
12578 PerlMem_free(vmsout);
12582 PerlMem_free(esal);
12585 PerlMem_free(rsal);
12586 PerlMem_free(esa_out);
12587 if (esal_out != NULL)
12588 PerlMem_free(esal_out);
12589 PerlMem_free(rsa_out);
12590 if (rsal_out != NULL)
12591 PerlMem_free(rsal_out);
12592 set_errno(EVMSERR); set_vaxc_errno(sts);
12596 while ((sts = sys$read(&rab_in))) { /* always true */
12597 if (sts == RMS$_EOF) break;
12598 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12599 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
12600 sys$close(&fab_in); sys$close(&fab_out);
12601 PerlMem_free(vmsin);
12602 PerlMem_free(vmsout);
12606 PerlMem_free(esal);
12609 PerlMem_free(rsal);
12610 PerlMem_free(esa_out);
12611 if (esal_out != NULL)
12612 PerlMem_free(esal_out);
12613 PerlMem_free(rsa_out);
12614 if (rsal_out != NULL)
12615 PerlMem_free(rsal_out);
12616 set_errno(EVMSERR); set_vaxc_errno(sts);
12622 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
12623 sys$close(&fab_in); sys$close(&fab_out);
12624 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
12626 PerlMem_free(vmsin);
12627 PerlMem_free(vmsout);
12631 PerlMem_free(esal);
12634 PerlMem_free(rsal);
12635 PerlMem_free(esa_out);
12636 if (esal_out != NULL)
12637 PerlMem_free(esal_out);
12638 PerlMem_free(rsa_out);
12639 if (rsal_out != NULL)
12640 PerlMem_free(rsal_out);
12643 set_errno(EVMSERR); set_vaxc_errno(sts);
12649 } /* end of rmscopy() */
12653 /*** The following glue provides 'hooks' to make some of the routines
12654 * from this file available from Perl. These routines are sufficiently
12655 * basic, and are required sufficiently early in the build process,
12656 * that's it's nice to have them available to miniperl as well as the
12657 * full Perl, so they're set up here instead of in an extension. The
12658 * Perl code which handles importation of these names into a given
12659 * package lives in [.VMS]Filespec.pm in @INC.
12663 rmsexpand_fromperl(pTHX_ CV *cv)
12666 char *fspec, *defspec = NULL, *rslt;
12668 int fs_utf8, dfs_utf8;
12672 if (!items || items > 2)
12673 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
12674 fspec = SvPV(ST(0),n_a);
12675 fs_utf8 = SvUTF8(ST(0));
12676 if (!fspec || !*fspec) XSRETURN_UNDEF;
12678 defspec = SvPV(ST(1),n_a);
12679 dfs_utf8 = SvUTF8(ST(1));
12681 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
12682 ST(0) = sv_newmortal();
12683 if (rslt != NULL) {
12684 sv_usepvn(ST(0),rslt,strlen(rslt));
12693 vmsify_fromperl(pTHX_ CV *cv)
12700 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
12701 utf8_fl = SvUTF8(ST(0));
12702 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12703 ST(0) = sv_newmortal();
12704 if (vmsified != NULL) {
12705 sv_usepvn(ST(0),vmsified,strlen(vmsified));
12714 unixify_fromperl(pTHX_ CV *cv)
12721 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
12722 utf8_fl = SvUTF8(ST(0));
12723 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12724 ST(0) = sv_newmortal();
12725 if (unixified != NULL) {
12726 sv_usepvn(ST(0),unixified,strlen(unixified));
12735 fileify_fromperl(pTHX_ CV *cv)
12742 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
12743 utf8_fl = SvUTF8(ST(0));
12744 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12745 ST(0) = sv_newmortal();
12746 if (fileified != NULL) {
12747 sv_usepvn(ST(0),fileified,strlen(fileified));
12756 pathify_fromperl(pTHX_ CV *cv)
12763 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
12764 utf8_fl = SvUTF8(ST(0));
12765 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12766 ST(0) = sv_newmortal();
12767 if (pathified != NULL) {
12768 sv_usepvn(ST(0),pathified,strlen(pathified));
12777 vmspath_fromperl(pTHX_ CV *cv)
12784 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
12785 utf8_fl = SvUTF8(ST(0));
12786 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12787 ST(0) = sv_newmortal();
12788 if (vmspath != NULL) {
12789 sv_usepvn(ST(0),vmspath,strlen(vmspath));
12798 unixpath_fromperl(pTHX_ CV *cv)
12805 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
12806 utf8_fl = SvUTF8(ST(0));
12807 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12808 ST(0) = sv_newmortal();
12809 if (unixpath != NULL) {
12810 sv_usepvn(ST(0),unixpath,strlen(unixpath));
12819 candelete_fromperl(pTHX_ CV *cv)
12827 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
12829 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12830 Newx(fspec, VMS_MAXRSS, char);
12831 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
12832 if (isGV_with_GP(mysv)) {
12833 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
12834 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12842 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
12843 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12850 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
12856 rmscopy_fromperl(pTHX_ CV *cv)
12859 char *inspec, *outspec, *inp, *outp;
12865 if (items < 2 || items > 3)
12866 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
12868 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12869 Newx(inspec, VMS_MAXRSS, char);
12870 if (isGV_with_GP(mysv)) {
12871 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
12872 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12873 ST(0) = sv_2mortal(newSViv(0));
12880 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
12881 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12882 ST(0) = sv_2mortal(newSViv(0));
12887 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
12888 Newx(outspec, VMS_MAXRSS, char);
12889 if (isGV_with_GP(mysv)) {
12890 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
12891 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12892 ST(0) = sv_2mortal(newSViv(0));
12900 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
12901 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12902 ST(0) = sv_2mortal(newSViv(0));
12908 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
12910 ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag)));
12916 /* The mod2fname is limited to shorter filenames by design, so it should
12917 * not be modified to support longer EFS pathnames
12920 mod2fname(pTHX_ CV *cv)
12923 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
12924 workbuff[NAM$C_MAXRSS*1 + 1];
12925 SSize_t counter, num_entries;
12926 /* ODS-5 ups this, but we want to be consistent, so... */
12927 int max_name_len = 39;
12928 AV *in_array = (AV *)SvRV(ST(0));
12930 num_entries = av_tindex(in_array);
12932 /* All the names start with PL_. */
12933 strcpy(ultimate_name, "PL_");
12935 /* Clean up our working buffer */
12936 Zero(work_name, sizeof(work_name), char);
12938 /* Run through the entries and build up a working name */
12939 for(counter = 0; counter <= num_entries; counter++) {
12940 /* If it's not the first name then tack on a __ */
12942 my_strlcat(work_name, "__", sizeof(work_name));
12944 my_strlcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)), sizeof(work_name));
12947 /* Check to see if we actually have to bother...*/
12948 if (strlen(work_name) + 3 <= max_name_len) {
12949 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
12951 /* It's too darned big, so we need to go strip. We use the same */
12952 /* algorithm as xsubpp does. First, strip out doubled __ */
12953 char *source, *dest, last;
12956 for (source = work_name; *source; source++) {
12957 if (last == *source && last == '_') {
12963 /* Go put it back */
12964 my_strlcpy(work_name, workbuff, sizeof(work_name));
12965 /* Is it still too big? */
12966 if (strlen(work_name) + 3 > max_name_len) {
12967 /* Strip duplicate letters */
12970 for (source = work_name; *source; source++) {
12971 if (last == toupper(*source)) {
12975 last = toupper(*source);
12977 my_strlcpy(work_name, workbuff, sizeof(work_name));
12980 /* Is it *still* too big? */
12981 if (strlen(work_name) + 3 > max_name_len) {
12982 /* Too bad, we truncate */
12983 work_name[max_name_len - 2] = 0;
12985 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
12988 /* Okay, return it */
12989 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
12994 hushexit_fromperl(pTHX_ CV *cv)
12999 VMSISH_HUSHED = SvTRUE(ST(0));
13001 ST(0) = boolSV(VMSISH_HUSHED);
13007 Perl_vms_start_glob
13008 (pTHX_ SV *tmpglob,
13012 struct vs_str_st *rslt;
13016 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13019 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13020 struct dsc$descriptor_vs rsdsc;
13021 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13022 unsigned long hasver = 0, isunix = 0;
13023 unsigned long int lff_flags = 0;
13025 int vms_old_glob = 1;
13027 if (!SvOK(tmpglob)) {
13028 SETERRNO(ENOENT,RMS$_FNF);
13032 vms_old_glob = !decc_filename_unix_report;
13034 #ifdef VMS_LONGNAME_SUPPORT
13035 lff_flags = LIB$M_FIL_LONG_NAMES;
13037 /* The Newx macro will not allow me to assign a smaller array
13038 * to the rslt pointer, so we will assign it to the begin char pointer
13039 * and then copy the value into the rslt pointer.
13041 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13042 rslt = (struct vs_str_st *)begin;
13044 rstr = &rslt->str[0];
13045 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13046 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13047 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13048 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13050 Newx(vmsspec, VMS_MAXRSS, char);
13052 /* We could find out if there's an explicit dev/dir or version
13053 by peeking into lib$find_file's internal context at
13054 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13055 but that's unsupported, so I don't want to do it now and
13056 have it bite someone in the future. */
13057 /* Fix-me: vms_split_path() is the only way to do this, the
13058 existing method will fail with many legal EFS or UNIX specifications
13061 cp = SvPV(tmpglob,i);
13064 if (cp[i] == ';') hasver = 1;
13065 if (cp[i] == '.') {
13066 if (sts) hasver = 1;
13069 if (cp[i] == '/') {
13070 hasdir = isunix = 1;
13073 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13079 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13080 if ((hasdir == 0) && decc_filename_unix_report) {
13084 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13085 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13086 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13092 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13093 if (!stat_sts && S_ISDIR(st.st_mode)) {
13095 const char * fname;
13098 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13099 /* path delimiter of ':>]', if so, then the old behavior has */
13100 /* obviously been specifically requested */
13102 fname = SvPVX_const(tmpglob);
13103 fname_len = strlen(fname);
13104 vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13105 if (vms_old_glob || (vms_dir != NULL)) {
13106 wilddsc.dsc$a_pointer = tovmspath_utf8(
13107 SvPVX(tmpglob),vmsspec,NULL);
13108 ok = (wilddsc.dsc$a_pointer != NULL);
13109 /* maybe passed 'foo' rather than '[.foo]', thus not
13113 /* Operate just on the directory, the special stat/fstat for */
13114 /* leaves the fileified specification in the st_devnam */
13116 wilddsc.dsc$a_pointer = st.st_devnam;
13121 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13122 ok = (wilddsc.dsc$a_pointer != NULL);
13125 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13127 /* If not extended character set, replace ? with % */
13128 /* With extended character set, ? is a wildcard single character */
13129 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13132 if (!decc_efs_charset)
13134 } else if (*cp == '%') {
13136 } else if (*cp == '*') {
13142 wv_sts = vms_split_path(
13143 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13144 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13145 &wvs_spec, &wvs_len);
13154 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13155 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13156 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13160 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13161 &dfltdsc,NULL,&rms_sts,&lff_flags);
13162 if (!$VMS_STATUS_SUCCESS(sts))
13165 /* with varying string, 1st word of buffer contains result length */
13166 rstr[rslt->length] = '\0';
13168 /* Find where all the components are */
13169 v_sts = vms_split_path
13184 /* If no version on input, truncate the version on output */
13185 if (!hasver && (vs_len > 0)) {
13192 /* In Unix report mode, remove the ".dir;1" from the name */
13193 /* if it is a real directory */
13194 if (decc_filename_unix_report && decc_efs_charset) {
13195 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13199 ret_sts = flex_lstat(rstr, &statbuf);
13200 if ((ret_sts == 0) &&
13201 S_ISDIR(statbuf.st_mode)) {
13208 /* No version & a null extension on UNIX handling */
13209 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13215 if (!decc_efs_case_preserve) {
13216 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13219 /* Find File treats a Null extension as return all extensions */
13220 /* This is contrary to Perl expectations */
13222 if (wildstar || wildquery || vms_old_glob) {
13223 /* really need to see if the returned file name matched */
13224 /* but for now will assume that it matches */
13227 /* Exact Match requested */
13228 /* How are directories handled? - like a file */
13229 if ((e_len == we_len) && (n_len == wn_len)) {
13233 t1 = strncmp(e_spec, we_spec, e_len);
13237 t1 = strncmp(n_spec, we_spec, n_len);
13248 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13252 /* Start with the name */
13255 strcat(begin,"\n");
13256 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13259 if (cxt) (void)lib$find_file_end(&cxt);
13262 /* Be POSIXish: return the input pattern when no matches */
13263 my_strlcpy(rstr, SvPVX(tmpglob), VMS_MAXRSS);
13265 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13268 if (ok && sts != RMS$_NMF &&
13269 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13272 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13274 PerlIO_close(tmpfp);
13278 PerlIO_rewind(tmpfp);
13279 IoTYPE(io) = IoTYPE_RDONLY;
13280 IoIFP(io) = fp = tmpfp;
13281 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13291 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13295 unixrealpath_fromperl(pTHX_ CV *cv)
13298 char *fspec, *rslt_spec, *rslt;
13301 if (!items || items != 1)
13302 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13304 fspec = SvPV(ST(0),n_a);
13305 if (!fspec || !*fspec) XSRETURN_UNDEF;
13307 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13308 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13310 ST(0) = sv_newmortal();
13312 sv_usepvn(ST(0),rslt,strlen(rslt));
13314 Safefree(rslt_spec);
13319 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13323 vmsrealpath_fromperl(pTHX_ CV *cv)
13326 char *fspec, *rslt_spec, *rslt;
13329 if (!items || items != 1)
13330 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13332 fspec = SvPV(ST(0),n_a);
13333 if (!fspec || !*fspec) XSRETURN_UNDEF;
13335 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13336 rslt = do_vms_realname(fspec, rslt_spec, NULL);
13338 ST(0) = sv_newmortal();
13340 sv_usepvn(ST(0),rslt,strlen(rslt));
13342 Safefree(rslt_spec);
13348 * A thin wrapper around decc$symlink to make sure we follow the
13349 * standard and do not create a symlink with a zero-length name,
13350 * and convert the target to Unix format, as the CRTL can't handle
13351 * targets in VMS format.
13353 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13355 Perl_my_symlink(pTHX_ const char *contents, const char *link_name)
13360 if (!link_name || !*link_name) {
13361 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13365 utarget = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
13366 /* An untranslatable filename should be passed through. */
13367 (void) int_tounixspec(contents, utarget, NULL);
13368 sts = symlink(utarget, link_name);
13369 PerlMem_free(utarget);
13374 #endif /* HAS_SYMLINK */
13376 int do_vms_case_tolerant(void);
13379 case_tolerant_process_fromperl(pTHX_ CV *cv)
13382 ST(0) = boolSV(do_vms_case_tolerant());
13386 #ifdef USE_ITHREADS
13389 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
13390 struct interp_intern *dst)
13392 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13394 memcpy(dst,src,sizeof(struct interp_intern));
13400 Perl_sys_intern_clear(pTHX)
13405 Perl_sys_intern_init(pTHX)
13407 unsigned int ix = RAND_MAX;
13412 MY_POSIX_EXIT = vms_posix_exit;
13415 MY_INV_RAND_MAX = 1./x;
13419 init_os_extras(void)
13422 char* file = __FILE__;
13423 if (decc_disable_to_vms_logname_translation) {
13424 no_translate_barewords = TRUE;
13426 no_translate_barewords = FALSE;
13429 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
13430 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
13431 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
13432 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
13433 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
13434 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
13435 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
13436 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
13437 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
13438 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
13439 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
13440 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
13441 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
13442 newXSproto("VMS::Filespec::case_tolerant_process",
13443 case_tolerant_process_fromperl,file,"");
13445 store_pipelocs(aTHX); /* will redo any earlier attempts */
13450 #if __CRTL_VER == 80200000
13451 /* This missed getting in to the DECC SDK for 8.2 */
13452 char *realpath(const char *file_name, char * resolved_name, ...);
13455 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
13456 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
13457 * The perl fallback routine to provide realpath() is not as efficient
13465 /* Hack, use old stat() as fastest way of getting ino_t and device */
13466 int decc$stat(const char *name, void * statbuf);
13467 #if !defined(__VAX) && __CRTL_VER >= 80200000
13468 int decc$lstat(const char *name, void * statbuf);
13470 #define decc$lstat decc$stat
13478 /* Realpath is fragile. In 8.3 it does not work if the feature
13479 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
13480 * links are implemented in RMS, not the CRTL. It also can fail if the
13481 * user does not have read/execute access to some of the directories.
13482 * So in order for Do What I Mean mode to work, if realpath() fails,
13483 * fall back to looking up the filename by the device name and FID.
13486 int vms_fid_to_name(char * outname, int outlen,
13487 const char * name, int lstat_flag, mode_t * mode)
13489 #pragma message save
13490 #pragma message disable MISALGNDSTRCT
13491 #pragma message disable MISALGNDMEM
13492 #pragma member_alignment save
13493 #pragma nomember_alignment
13496 unsigned short st_ino[3];
13497 unsigned short old_st_mode;
13498 unsigned long padl[30]; /* plenty of room */
13500 #pragma message restore
13501 #pragma member_alignment restore
13504 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13505 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13510 /* Need to follow the mostly the same rules as flex_stat_int, or we may get
13511 * unexpected answers
13514 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
13515 if (fileified == NULL)
13516 _ckvmssts_noperl(SS$_INSFMEM);
13518 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
13519 if (temp_fspec == NULL)
13520 _ckvmssts_noperl(SS$_INSFMEM);
13523 /* First need to try as a directory */
13524 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13525 if (ret_spec != NULL) {
13526 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
13527 if (ret_spec != NULL) {
13528 if (lstat_flag == 0)
13529 sts = decc$stat(fileified, &statbuf);
13531 sts = decc$lstat(fileified, &statbuf);
13535 /* Then as a VMS file spec */
13537 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
13538 if (ret_spec != NULL) {
13539 if (lstat_flag == 0) {
13540 sts = decc$stat(temp_fspec, &statbuf);
13542 sts = decc$lstat(temp_fspec, &statbuf);
13548 /* Next try - allow multiple dots with out EFS CHARSET */
13549 /* The CRTL stat() falls down hard on multi-dot filenames in unix
13550 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
13551 * enable it if it isn't already.
13553 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13554 if (!decc_efs_charset && (decc_efs_charset_index > 0))
13555 decc$feature_set_value(decc_efs_charset_index, 1, 1);
13557 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13558 if (lstat_flag == 0) {
13559 sts = decc$stat(name, &statbuf);
13561 sts = decc$lstat(name, &statbuf);
13563 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13564 if (!decc_efs_charset && (decc_efs_charset_index > 0))
13565 decc$feature_set_value(decc_efs_charset_index, 1, 0);
13570 /* and then because the Perl Unix to VMS conversion is not perfect */
13571 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
13572 /* characters from filenames so we need to try it as-is */
13574 if (lstat_flag == 0) {
13575 sts = decc$stat(name, &statbuf);
13577 sts = decc$lstat(name, &statbuf);
13584 dvidsc.dsc$a_pointer=statbuf.st_dev;
13585 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
13587 specdsc.dsc$a_pointer = outname;
13588 specdsc.dsc$w_length = outlen-1;
13590 vms_sts = lib$fid_to_name
13591 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
13592 if ($VMS_STATUS_SUCCESS(vms_sts)) {
13593 outname[specdsc.dsc$w_length] = 0;
13595 /* Return the mode */
13597 *mode = statbuf.old_st_mode;
13601 PerlMem_free(temp_fspec);
13602 PerlMem_free(fileified);
13609 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
13612 char * rslt = NULL;
13615 if (decc_posix_compliant_pathnames > 0 ) {
13616 /* realpath currently only works if posix compliant pathnames are
13617 * enabled. It may start working when they are not, but in that
13618 * case we still want the fallback behavior for backwards compatibility
13620 rslt = realpath(filespec, outbuf);
13624 if (rslt == NULL) {
13626 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13627 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13630 /* Fall back to fid_to_name */
13632 Newx(vms_spec, VMS_MAXRSS + 1, char);
13634 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
13638 /* Now need to trim the version off */
13639 sts = vms_split_path
13659 /* Trim off the version */
13660 int file_len = v_len + r_len + d_len + n_len + e_len;
13661 vms_spec[file_len] = 0;
13663 /* Trim off the .DIR if this is a directory */
13664 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13665 if (S_ISDIR(my_mode)) {
13671 /* Drop NULL extensions on UNIX file specification */
13672 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13677 /* The result is expected to be in UNIX format */
13678 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
13680 /* Downcase if input had any lower case letters and
13681 * case preservation is not in effect.
13683 if (!decc_efs_case_preserve) {
13684 for (cp = filespec; *cp; cp++)
13685 if (islower(*cp)) { haslower = 1; break; }
13687 if (haslower) __mystrtolower(rslt);
13692 /* Now for some hacks to deal with backwards and forward */
13693 /* compatibility */
13694 if (!decc_efs_charset) {
13696 /* 1. ODS-2 mode wants to do a syntax only translation */
13697 rslt = int_rmsexpand(filespec, outbuf,
13698 NULL, 0, NULL, utf8_fl);
13701 if (decc_filename_unix_report) {
13703 char * vms_dir_name;
13706 /* 2. ODS-5 / UNIX report mode should return a failure */
13707 /* if the parent directory also does not exist */
13708 /* Otherwise, get the real path for the parent */
13709 /* and add the child to it. */
13711 /* basename / dirname only available for VMS 7.0+ */
13712 /* So we may need to implement them as common routines */
13714 Newx(dir_name, VMS_MAXRSS + 1, char);
13715 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
13716 dir_name[0] = '\0';
13719 /* First try a VMS parse */
13720 sts = vms_split_path
13738 int dir_len = v_len + r_len + d_len + n_len;
13740 memcpy(dir_name, filespec, dir_len);
13741 dir_name[dir_len] = '\0';
13742 file_name = (char *)&filespec[dir_len + 1];
13745 /* This must be UNIX */
13748 tchar = strrchr(filespec, '/');
13750 if (tchar != NULL) {
13751 int dir_len = tchar - filespec;
13752 memcpy(dir_name, filespec, dir_len);
13753 dir_name[dir_len] = '\0';
13754 file_name = (char *) &filespec[dir_len + 1];
13758 /* Dir name is defaulted */
13759 if (dir_name[0] == 0) {
13761 dir_name[1] = '\0';
13764 /* Need realpath for the directory */
13765 sts = vms_fid_to_name(vms_dir_name,
13767 dir_name, 0, NULL);
13770 /* Now need to pathify it. */
13771 char *tdir = int_pathify_dirspec(vms_dir_name,
13774 /* And now add the original filespec to it */
13775 if (file_name != NULL) {
13776 my_strlcat(outbuf, file_name, VMS_MAXRSS);
13780 Safefree(vms_dir_name);
13781 Safefree(dir_name);
13785 Safefree(vms_spec);
13791 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
13794 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13795 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13797 /* Fall back to fid_to_name */
13799 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
13806 /* Now need to trim the version off */
13807 sts = vms_split_path
13827 /* Trim off the version */
13828 int file_len = v_len + r_len + d_len + n_len + e_len;
13829 outbuf[file_len] = 0;
13831 /* Downcase if input had any lower case letters and
13832 * case preservation is not in effect.
13834 if (!decc_efs_case_preserve) {
13835 for (cp = filespec; *cp; cp++)
13836 if (islower(*cp)) { haslower = 1; break; }
13838 if (haslower) __mystrtolower(outbuf);
13847 /* External entry points */
13848 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13849 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
13851 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13852 { return do_vms_realname(filespec, outbuf, utf8_fl); }
13854 /* case_tolerant */
13856 /*{{{int do_vms_case_tolerant(void)*/
13857 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
13858 * controlled by a process setting.
13860 int do_vms_case_tolerant(void)
13862 return vms_process_case_tolerant;
13865 /* External entry points */
13866 #if __CRTL_VER >= 70301000 && !defined(__VAX)
13867 int Perl_vms_case_tolerant(void)
13868 { return do_vms_case_tolerant(); }
13870 int Perl_vms_case_tolerant(void)
13871 { return vms_process_case_tolerant; }
13875 /* Start of DECC RTL Feature handling */
13877 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13880 set_feature_default(const char *name, int value)
13886 /* If the feature has been explicitly disabled in the environment,
13887 * then don't enable it here.
13890 status = simple_trnlnm(name, val_str, sizeof(val_str));
13892 val_str[0] = _toupper(val_str[0]);
13893 if (val_str[0] == 'D' || val_str[0] == '0' || val_str[0] == 'F')
13898 index = decc$feature_get_index(name);
13900 status = decc$feature_set_value(index, 1, value);
13901 if (index == -1 || (status == -1)) {
13905 status = decc$feature_get_value(index, 1);
13906 if (status != value) {
13910 /* Various things may check for an environment setting
13911 * rather than the feature directly, so set that too.
13913 vmssetuserlnm(name, value ? "ENABLE" : "DISABLE");
13920 /* C RTL Feature settings */
13922 #if defined(__DECC) || defined(__DECCXX)
13929 vmsperl_set_features(void)
13934 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
13935 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
13936 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
13937 unsigned long case_perm;
13938 unsigned long case_image;
13941 /* Allow an exception to bring Perl into the VMS debugger */
13942 vms_debug_on_exception = 0;
13943 status = simple_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
13945 val_str[0] = _toupper(val_str[0]);
13946 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13947 vms_debug_on_exception = 1;
13949 vms_debug_on_exception = 0;
13952 /* Debug unix/vms file translation routines */
13953 vms_debug_fileify = 0;
13954 status = simple_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
13956 val_str[0] = _toupper(val_str[0]);
13957 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13958 vms_debug_fileify = 1;
13960 vms_debug_fileify = 0;
13964 /* Historically PERL has been doing vmsify / stat differently than */
13965 /* the CRTL. In particular, under some conditions the CRTL will */
13966 /* remove some illegal characters like spaces from filenames */
13967 /* resulting in some differences. The stat()/lstat() wrapper has */
13968 /* been reporting such file names as invalid and fails to stat them */
13969 /* fixing this bug so that stat()/lstat() accept these like the */
13970 /* CRTL does will result in several tests failing. */
13971 /* This should really be fixed, but for now, set up a feature to */
13972 /* enable it so that the impact can be studied. */
13973 vms_bug_stat_filename = 0;
13974 status = simple_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
13976 val_str[0] = _toupper(val_str[0]);
13977 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13978 vms_bug_stat_filename = 1;
13980 vms_bug_stat_filename = 0;
13984 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
13985 vms_vtf7_filenames = 0;
13986 status = simple_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
13988 val_str[0] = _toupper(val_str[0]);
13989 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13990 vms_vtf7_filenames = 1;
13992 vms_vtf7_filenames = 0;
13995 /* unlink all versions on unlink() or rename() */
13996 vms_unlink_all_versions = 0;
13997 status = simple_trnlnm("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
13999 val_str[0] = _toupper(val_str[0]);
14000 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14001 vms_unlink_all_versions = 1;
14003 vms_unlink_all_versions = 0;
14006 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14007 /* Detect running under GNV Bash or other UNIX like shell */
14008 gnv_unix_shell = 0;
14009 status = simple_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14011 gnv_unix_shell = 1;
14012 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14013 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14014 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14015 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
14016 vms_unlink_all_versions = 1;
14017 vms_posix_exit = 1;
14018 /* Reverse default ordering of PERL_ENV_TABLES. */
14019 defenv[0] = &crtlenvdsc;
14020 defenv[1] = &fildevdsc;
14022 /* Some reasonable defaults that are not CRTL defaults */
14023 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14024 set_feature_default("DECC$ARGV_PARSE_STYLE", 1); /* Requires extended parse. */
14025 set_feature_default("DECC$EFS_CHARSET", 1);
14028 /* hacks to see if known bugs are still present for testing */
14030 /* PCP mode requires creating /dev/null special device file */
14031 decc_bug_devnull = 0;
14032 status = simple_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14034 val_str[0] = _toupper(val_str[0]);
14035 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14036 decc_bug_devnull = 1;
14038 decc_bug_devnull = 0;
14041 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14042 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14044 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14045 if (decc_disable_to_vms_logname_translation < 0)
14046 decc_disable_to_vms_logname_translation = 0;
14049 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14051 decc_efs_case_preserve = decc$feature_get_value(s, 1);
14052 if (decc_efs_case_preserve < 0)
14053 decc_efs_case_preserve = 0;
14056 s = decc$feature_get_index("DECC$EFS_CHARSET");
14057 decc_efs_charset_index = s;
14059 decc_efs_charset = decc$feature_get_value(s, 1);
14060 if (decc_efs_charset < 0)
14061 decc_efs_charset = 0;
14064 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14066 decc_filename_unix_report = decc$feature_get_value(s, 1);
14067 if (decc_filename_unix_report > 0) {
14068 decc_filename_unix_report = 1;
14069 vms_posix_exit = 1;
14072 decc_filename_unix_report = 0;
14075 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14077 decc_filename_unix_only = decc$feature_get_value(s, 1);
14078 if (decc_filename_unix_only > 0) {
14079 decc_filename_unix_only = 1;
14082 decc_filename_unix_only = 0;
14086 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14088 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14089 if (decc_filename_unix_no_version < 0)
14090 decc_filename_unix_no_version = 0;
14093 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14095 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14096 if (decc_readdir_dropdotnotype < 0)
14097 decc_readdir_dropdotnotype = 0;
14100 #if __CRTL_VER >= 80200000
14101 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14103 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14104 if (decc_posix_compliant_pathnames < 0)
14105 decc_posix_compliant_pathnames = 0;
14106 if (decc_posix_compliant_pathnames > 4)
14107 decc_posix_compliant_pathnames = 0;
14112 status = simple_trnlnm
14113 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14115 val_str[0] = _toupper(val_str[0]);
14116 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14117 decc_disable_to_vms_logname_translation = 1;
14122 status = simple_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14124 val_str[0] = _toupper(val_str[0]);
14125 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14126 decc_efs_case_preserve = 1;
14131 status = simple_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14133 val_str[0] = _toupper(val_str[0]);
14134 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14135 decc_filename_unix_report = 1;
14138 status = simple_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14140 val_str[0] = _toupper(val_str[0]);
14141 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14142 decc_filename_unix_only = 1;
14143 decc_filename_unix_report = 1;
14146 status = simple_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14148 val_str[0] = _toupper(val_str[0]);
14149 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14150 decc_filename_unix_no_version = 1;
14153 status = simple_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14155 val_str[0] = _toupper(val_str[0]);
14156 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14157 decc_readdir_dropdotnotype = 1;
14162 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14164 /* Report true case tolerance */
14165 /*----------------------------*/
14166 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14167 if (!$VMS_STATUS_SUCCESS(status))
14168 case_perm = PPROP$K_CASE_BLIND;
14169 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14170 if (!$VMS_STATUS_SUCCESS(status))
14171 case_image = PPROP$K_CASE_BLIND;
14172 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14173 (case_image == PPROP$K_CASE_SENSITIVE))
14174 vms_process_case_tolerant = 0;
14178 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
14179 /* for strict backward compatibility */
14180 status = simple_trnlnm("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14182 val_str[0] = _toupper(val_str[0]);
14183 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14184 vms_posix_exit = 1;
14186 vms_posix_exit = 0;
14190 /* Use 32-bit pointers because that's what the image activator
14191 * assumes for the LIB$INITIALZE psect.
14193 #if __INITIAL_POINTER_SIZE
14194 #pragma pointer_size save
14195 #pragma pointer_size 32
14198 /* Create a reference to the LIB$INITIALIZE function. */
14199 extern void LIB$INITIALIZE(void);
14200 extern void (*vmsperl_unused_global_1)(void) = LIB$INITIALIZE;
14202 /* Create an array of pointers to the init functions in the special
14203 * LIB$INITIALIZE section. In our case, the array only has one entry.
14205 #pragma extern_model save
14206 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic,gbl,nowrt,noshr,long
14207 extern void (* const vmsperl_unused_global_2[])() =
14209 vmsperl_set_features,
14211 #pragma extern_model restore
14213 #if __INITIAL_POINTER_SIZE
14214 #pragma pointer_size restore
14221 #endif /* defined(__DECC) || defined(__DECCXX) */