3 * VMS-specific routines for perl5
5 * Copyright (C) 1993-2015 by Charles Bailey and others.
7 * You may distribute under the terms of either the GNU General Public
8 * License or the Artistic License, as specified in the README file.
12 * Yet small as was their hunted band
13 * still fell and fearless was each hand,
14 * and strong deeds they wrought yet oft,
15 * and loved the woods, whose ways more soft
16 * them seemed than thralls of that black throne
17 * to live and languish in halls of stone.
18 * "The Lay of Leithian", Canto II, lines 135-40
20 * [p.162 of _The Lays of Beleriand_]
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 */
274 simple_trnlnm(const char * logname, char * value, int value_len)
276 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
277 const unsigned long attr = LNM$M_CASE_BLIND;
278 struct dsc$descriptor_s name_dsc;
280 unsigned short result;
281 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
284 name_dsc.dsc$w_length = strlen(logname);
285 name_dsc.dsc$a_pointer = (char *)logname;
286 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
287 name_dsc.dsc$b_class = DSC$K_CLASS_S;
289 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
291 if ($VMS_STATUS_SUCCESS(status)) {
293 /* Null terminate and return the string */
294 /*--------------------------------------*/
303 /* Is this a UNIX file specification?
304 * No longer a simple check with EFS file specs
305 * For now, not a full check, but need to
306 * handle POSIX ^UP^ specifications
307 * Fixing to handle ^/ cases would require
308 * changes to many other conversion routines.
312 is_unix_filespec(const char *path)
318 if (strncmp(path,"\"^UP^",5) != 0) {
319 pch1 = strchr(path, '/');
324 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
325 if (decc_filename_unix_report || decc_filename_unix_only) {
326 if (strcmp(path,".") == 0)
334 /* This routine converts a UCS-2 character to be VTF-7 encoded.
338 ucs2_to_vtf7(char *outspec, unsigned long ucs2_char, int * output_cnt)
340 unsigned char * ucs_ptr;
343 ucs_ptr = (unsigned char *)&ucs2_char;
347 hex = (ucs_ptr[1] >> 4) & 0xf;
349 outspec[2] = hex + '0';
351 outspec[2] = (hex - 9) + 'A';
352 hex = ucs_ptr[1] & 0xF;
354 outspec[3] = hex + '0';
356 outspec[3] = (hex - 9) + 'A';
358 hex = (ucs_ptr[0] >> 4) & 0xf;
360 outspec[4] = hex + '0';
362 outspec[4] = (hex - 9) + 'A';
363 hex = ucs_ptr[1] & 0xF;
365 outspec[5] = hex + '0';
367 outspec[5] = (hex - 9) + 'A';
373 /* This handles the conversion of a UNIX extended character set to a ^
374 * escaped VMS character.
375 * in a UNIX file specification.
377 * The output count variable contains the number of characters added
378 * to the output string.
380 * The return value is the number of characters read from the input string
383 copy_expand_unix_filename_escape(char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
390 utf8_flag = *utf8_fl;
394 if (*inspec >= 0x80) {
395 if (utf8_fl && vms_vtf7_filenames) {
396 unsigned long ucs_char;
400 if ((*inspec & 0xE0) == 0xC0) {
402 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
403 if (ucs_char >= 0x80) {
404 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
407 } else if ((*inspec & 0xF0) == 0xE0) {
409 ucs_char = ((inspec[0] & 0xF) << 12) +
410 ((inspec[1] & 0x3f) << 6) +
412 if (ucs_char >= 0x800) {
413 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
417 #if 0 /* I do not see longer sequences supported by OpenVMS */
418 /* Maybe some one can fix this later */
419 } else if ((*inspec & 0xF8) == 0xF0) {
422 } else if ((*inspec & 0xFC) == 0xF8) {
425 } else if ((*inspec & 0xFE) == 0xFC) {
432 /* High bit set, but not a Unicode character! */
434 /* Non printing DECMCS or ISO Latin-1 character? */
435 if ((unsigned char)*inspec <= 0x9F) {
439 hex = (*inspec >> 4) & 0xF;
441 outspec[1] = hex + '0';
443 outspec[1] = (hex - 9) + 'A';
447 outspec[2] = hex + '0';
449 outspec[2] = (hex - 9) + 'A';
453 } else if ((unsigned char)*inspec == 0xA0) {
459 } else if ((unsigned char)*inspec == 0xFF) {
471 /* Is this a macro that needs to be passed through?
472 * Macros start with $( and an alpha character, followed
473 * by a string of alpha numeric characters ending with a )
474 * If this does not match, then encode it as ODS-5.
476 if ((inspec[0] == '$') && (inspec[1] == '(')) {
479 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
481 outspec[0] = inspec[0];
482 outspec[1] = inspec[1];
483 outspec[2] = inspec[2];
485 while(isalnum(inspec[tcnt]) ||
486 (inspec[2] == '.') || (inspec[2] == '_')) {
487 outspec[tcnt] = inspec[tcnt];
490 if (inspec[tcnt] == ')') {
491 outspec[tcnt] = inspec[tcnt];
508 if (decc_efs_charset == 0)
535 /* Don't escape again if following character is
536 * already something we escape.
538 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
544 /* But otherwise fall through and escape it. */
546 /* Assume that this is to be escaped */
548 outspec[1] = *inspec;
552 case ' ': /* space */
553 /* Assume that this is to be escaped */
569 /* This handles the expansion of a '^' prefix to the proper character
570 * in a UNIX file specification.
572 * The output count variable contains the number of characters added
573 * to the output string.
575 * The return value is the number of characters read from the input
579 copy_expand_vms_filename_escape(char *outspec, const char *inspec, int *output_cnt)
586 if (*inspec == '^') {
589 /* Spaces and non-trailing dots should just be passed through,
590 * but eat the escape character.
597 case '_': /* space */
603 /* Hmm. Better leave the escape escaped. */
609 case 'U': /* Unicode - FIX-ME this is wrong. */
612 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
615 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
616 outspec[0] = c1 & 0xff;
617 outspec[1] = c2 & 0xff;
624 /* Error - do best we can to continue */
634 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
638 scnt = sscanf(inspec, "%2x", &c1);
639 outspec[0] = c1 & 0xff;
660 /* vms_split_path - Verify that the input file specification is a
661 * VMS format file specification, and provide pointers to the components of
662 * it. With EFS format filenames, this is virtually the only way to
663 * parse a VMS path specification into components.
665 * If the sum of the components do not add up to the length of the
666 * string, then the passed file specification is probably a UNIX style
670 vms_split_path(const char * path, char * * volume, int * vol_len, char * * root, int * root_len,
671 char * * dir, int * dir_len, char * * name, int * name_len,
672 char * * ext, int * ext_len, char * * version, int * ver_len)
674 struct dsc$descriptor path_desc;
678 struct filescan_itmlst_2 item_list[9];
679 const int filespec = 0;
680 const int nodespec = 1;
681 const int devspec = 2;
682 const int rootspec = 3;
683 const int dirspec = 4;
684 const int namespec = 5;
685 const int typespec = 6;
686 const int verspec = 7;
688 /* Assume the worst for an easy exit */
702 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
703 path_desc.dsc$w_length = strlen(path);
704 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
705 path_desc.dsc$b_class = DSC$K_CLASS_S;
707 /* Get the total length, if it is shorter than the string passed
708 * then this was probably not a VMS formatted file specification
710 item_list[filespec].itmcode = FSCN$_FILESPEC;
711 item_list[filespec].length = 0;
712 item_list[filespec].component = NULL;
714 /* If the node is present, then it gets considered as part of the
715 * volume name to hopefully make things simple.
717 item_list[nodespec].itmcode = FSCN$_NODE;
718 item_list[nodespec].length = 0;
719 item_list[nodespec].component = NULL;
721 item_list[devspec].itmcode = FSCN$_DEVICE;
722 item_list[devspec].length = 0;
723 item_list[devspec].component = NULL;
725 /* root is a special case, adding it to either the directory or
726 * the device components will probably complicate things for the
727 * callers of this routine, so leave it separate.
729 item_list[rootspec].itmcode = FSCN$_ROOT;
730 item_list[rootspec].length = 0;
731 item_list[rootspec].component = NULL;
733 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
734 item_list[dirspec].length = 0;
735 item_list[dirspec].component = NULL;
737 item_list[namespec].itmcode = FSCN$_NAME;
738 item_list[namespec].length = 0;
739 item_list[namespec].component = NULL;
741 item_list[typespec].itmcode = FSCN$_TYPE;
742 item_list[typespec].length = 0;
743 item_list[typespec].component = NULL;
745 item_list[verspec].itmcode = FSCN$_VERSION;
746 item_list[verspec].length = 0;
747 item_list[verspec].component = NULL;
749 item_list[8].itmcode = 0;
750 item_list[8].length = 0;
751 item_list[8].component = NULL;
753 status = sys$filescan
754 ((const struct dsc$descriptor_s *)&path_desc, item_list,
756 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
758 /* If we parsed it successfully these two lengths should be the same */
759 if (path_desc.dsc$w_length != item_list[filespec].length)
762 /* If we got here, then it is a VMS file specification */
765 /* set the volume name */
766 if (item_list[nodespec].length > 0) {
767 *volume = item_list[nodespec].component;
768 *vol_len = item_list[nodespec].length + item_list[devspec].length;
771 *volume = item_list[devspec].component;
772 *vol_len = item_list[devspec].length;
775 *root = item_list[rootspec].component;
776 *root_len = item_list[rootspec].length;
778 *dir = item_list[dirspec].component;
779 *dir_len = item_list[dirspec].length;
781 /* Now fun with versions and EFS file specifications
782 * The parser can not tell the difference when a "." is a version
783 * delimiter or a part of the file specification.
785 if ((decc_efs_charset) &&
786 (item_list[verspec].length > 0) &&
787 (item_list[verspec].component[0] == '.')) {
788 *name = item_list[namespec].component;
789 *name_len = item_list[namespec].length + item_list[typespec].length;
790 *ext = item_list[verspec].component;
791 *ext_len = item_list[verspec].length;
796 *name = item_list[namespec].component;
797 *name_len = item_list[namespec].length;
798 *ext = item_list[typespec].component;
799 *ext_len = item_list[typespec].length;
800 *version = item_list[verspec].component;
801 *ver_len = item_list[verspec].length;
806 /* Routine to determine if the file specification ends with .dir */
808 is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len)
811 /* e_len must be 4, and version must be <= 2 characters */
812 if (e_len != 4 || vs_len > 2)
815 /* If a version number is present, it needs to be one */
816 if ((vs_len == 2) && (vs_spec[1] != '1'))
819 /* Look for the DIR on the extension */
820 if (vms_process_case_tolerant) {
821 if ((toupper(e_spec[1]) == 'D') &&
822 (toupper(e_spec[2]) == 'I') &&
823 (toupper(e_spec[3]) == 'R')) {
827 /* Directory extensions are supposed to be in upper case only */
828 /* I would not be surprised if this rule can not be enforced */
829 /* if and when someone fully debugs the case sensitive mode */
830 if ((e_spec[1] == 'D') &&
831 (e_spec[2] == 'I') &&
832 (e_spec[3] == 'R')) {
841 * Routine to retrieve the maximum equivalence index for an input
842 * logical name. Some calls to this routine have no knowledge if
843 * the variable is a logical or not. So on error we return a max
846 /*{{{int my_maxidx(const char *lnm) */
848 my_maxidx(const char *lnm)
852 int attr = LNM$M_CASE_BLIND;
853 struct dsc$descriptor lnmdsc;
854 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
857 lnmdsc.dsc$w_length = strlen(lnm);
858 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
859 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
860 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
862 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
863 if ((status & 1) == 0)
870 /* Routine to remove the 2-byte prefix from the translation of a
871 * process-permanent file (PPF).
873 static inline unsigned short int
874 S_remove_ppf_prefix(const char *lnm, char *eqv, unsigned short int eqvlen)
876 if (*((int *)lnm) == *((int *)"SYS$") &&
877 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
878 ( (lnm[4] == 'O' && !strcmp(lnm,"SYS$OUTPUT")) ||
879 (lnm[4] == 'I' && !strcmp(lnm,"SYS$INPUT")) ||
880 (lnm[4] == 'E' && !strcmp(lnm,"SYS$ERROR")) ||
881 (lnm[4] == 'C' && !strcmp(lnm,"SYS$COMMAND")) ) ) {
883 memmove(eqv, eqv+4, eqvlen-4);
889 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
891 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
892 struct dsc$descriptor_s **tabvec, unsigned long int flags)
895 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
896 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
897 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
899 unsigned char acmode;
900 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
901 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
902 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
903 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
905 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
906 #if defined(PERL_IMPLICIT_CONTEXT)
909 aTHX = PERL_GET_INTERP;
915 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
916 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
918 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
919 *cp2 = _toupper(*cp1);
920 if (cp1 - lnm > LNM$C_NAMLENGTH) {
921 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
925 lnmdsc.dsc$w_length = cp1 - lnm;
926 lnmdsc.dsc$a_pointer = uplnm;
927 uplnm[lnmdsc.dsc$w_length] = '\0';
928 secure = flags & PERL__TRNENV_SECURE;
929 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
930 if (!tabvec || !*tabvec) tabvec = env_tables;
932 for (curtab = 0; tabvec[curtab]; curtab++) {
933 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
934 if (!ivenv && !secure) {
939 #if defined(PERL_IMPLICIT_CONTEXT)
942 "Can't read CRTL environ\n");
945 Perl_warn(aTHX_ "Can't read CRTL environ\n");
948 retsts = SS$_NOLOGNAM;
949 for (i = 0; environ[i]; i++) {
950 if ((eq = strchr(environ[i],'=')) &&
951 lnmdsc.dsc$w_length == (eq - environ[i]) &&
952 !strncmp(environ[i],uplnm,eq - environ[i])) {
954 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
955 if (!eqvlen) continue;
960 if (retsts != SS$_NOLOGNAM) break;
963 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
964 !str$case_blind_compare(&tmpdsc,&clisym)) {
965 if (!ivsym && !secure) {
966 unsigned short int deflen = LNM$C_NAMLENGTH;
967 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
968 /* dynamic dsc to accommodate possible long value */
969 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
970 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
972 if (eqvlen > MAX_DCL_SYMBOL) {
973 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
974 eqvlen = MAX_DCL_SYMBOL;
975 /* Special hack--we might be called before the interpreter's */
976 /* fully initialized, in which case either thr or PL_curcop */
977 /* might be bogus. We have to check, since ckWARN needs them */
978 /* both to be valid if running threaded */
979 #if defined(PERL_IMPLICIT_CONTEXT)
982 "Value of CLI symbol \"%s\" too long",lnm);
985 if (ckWARN(WARN_MISC)) {
986 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
989 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
991 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
992 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
993 if (retsts == LIB$_NOSUCHSYM) continue;
998 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
999 midx = my_maxidx(lnm);
1000 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1001 lnmlst[1].bufadr = cp2;
1003 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1004 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1005 if (retsts == SS$_NOLOGNAM) break;
1006 eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen);
1010 if ((retsts == SS$_IVLOGNAM) ||
1011 (retsts == SS$_NOLOGNAM)) { continue; }
1012 eqvlen = strlen(eqv);
1015 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1016 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1017 if (retsts == SS$_NOLOGNAM) continue;
1018 eqvlen = S_remove_ppf_prefix(uplnm, eqv, eqvlen);
1024 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1025 else if (retsts == LIB$_NOSUCHSYM ||
1026 retsts == SS$_NOLOGNAM) {
1027 /* Unsuccessful lookup is normal -- no need to set errno */
1030 else if (retsts == LIB$_INVSYMNAM ||
1031 retsts == SS$_IVLOGNAM ||
1032 retsts == SS$_IVLOGTAB) {
1033 set_errno(EINVAL); set_vaxc_errno(retsts);
1035 else _ckvmssts_noperl(retsts);
1037 } /* end of vmstrnenv */
1040 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1041 /* Define as a function so we can access statics. */
1043 Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1047 #if defined(PERL_IMPLICIT_CONTEXT)
1050 #ifdef SECURE_INTERNAL_GETENV
1051 flags = (PL_curinterp ? TAINTING_get : will_taint) ?
1052 PERL__TRNENV_SECURE : 0;
1055 return vmstrnenv(lnm, eqv, idx, fildev, flags);
1060 * Note: Uses Perl temp to store result so char * can be returned to
1061 * caller; this pointer will be invalidated at next Perl statement
1063 * We define this as a function rather than a macro in terms of my_getenv_len()
1064 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1067 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1069 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1072 static char *__my_getenv_eqv = NULL;
1073 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1074 unsigned long int idx = 0;
1075 int success, secure;
1079 midx = my_maxidx(lnm) + 1;
1081 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1082 /* Set up a temporary buffer for the return value; Perl will
1083 * clean it up at the next statement transition */
1084 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1085 if (!tmpsv) return NULL;
1089 /* Assume no interpreter ==> single thread */
1090 if (__my_getenv_eqv != NULL) {
1091 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1094 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1096 eqv = __my_getenv_eqv;
1099 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1100 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1102 getcwd(eqv,LNM$C_NAMLENGTH);
1106 /* Get rid of "000000/ in rooted filespecs */
1109 zeros = strstr(eqv, "/000000/");
1110 if (zeros != NULL) {
1112 mlen = len - (zeros - eqv) - 7;
1113 memmove(zeros, &zeros[7], mlen);
1121 /* Impose security constraints only if tainting */
1123 /* Impose security constraints only if tainting */
1124 secure = PL_curinterp ? TAINTING_get : will_taint;
1131 #ifdef SECURE_INTERNAL_GETENV
1132 secure ? PERL__TRNENV_SECURE : 0
1138 /* For the getenv interface we combine all the equivalence names
1139 * of a search list logical into one value to acquire a maximum
1140 * value length of 255*128 (assuming %ENV is using logicals).
1142 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1144 /* If the name contains a semicolon-delimited index, parse it
1145 * off and make sure we only retrieve the equivalence name for
1147 if ((cp2 = strchr(lnm,';')) != NULL) {
1148 my_strlcpy(uplnm, lnm, cp2 - lnm + 1);
1149 idx = strtoul(cp2+1,NULL,0);
1151 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1154 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1156 return success ? eqv : NULL;
1159 } /* end of my_getenv() */
1163 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1165 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1169 unsigned long idx = 0;
1171 static char *__my_getenv_len_eqv = NULL;
1175 midx = my_maxidx(lnm) + 1;
1177 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1178 /* Set up a temporary buffer for the return value; Perl will
1179 * clean it up at the next statement transition */
1180 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1181 if (!tmpsv) return NULL;
1185 /* Assume no interpreter ==> single thread */
1186 if (__my_getenv_len_eqv != NULL) {
1187 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1190 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1192 buf = __my_getenv_len_eqv;
1195 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1196 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1199 getcwd(buf,LNM$C_NAMLENGTH);
1202 /* Get rid of "000000/ in rooted filespecs */
1204 zeros = strstr(buf, "/000000/");
1205 if (zeros != NULL) {
1207 mlen = *len - (zeros - buf) - 7;
1208 memmove(zeros, &zeros[7], mlen);
1217 /* Impose security constraints only if tainting */
1218 secure = PL_curinterp ? TAINTING_get : will_taint;
1225 #ifdef SECURE_INTERNAL_GETENV
1226 secure ? PERL__TRNENV_SECURE : 0
1232 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1234 if ((cp2 = strchr(lnm,';')) != NULL) {
1235 my_strlcpy(buf, lnm, cp2 - lnm + 1);
1236 idx = strtoul(cp2+1,NULL,0);
1238 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1241 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1243 /* Get rid of "000000/ in rooted filespecs */
1246 zeros = strstr(buf, "/000000/");
1247 if (zeros != NULL) {
1249 mlen = *len - (zeros - buf) - 7;
1250 memmove(zeros, &zeros[7], mlen);
1256 return *len ? buf : NULL;
1259 } /* end of my_getenv_len() */
1262 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1264 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1266 /*{{{ void prime_env_iter() */
1268 prime_env_iter(void)
1269 /* Fill the %ENV associative array with all logical names we can
1270 * find, in preparation for iterating over it.
1273 static int primed = 0;
1274 HV *seenhv = NULL, *envhv;
1276 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1277 unsigned short int chan;
1278 #ifndef CLI$M_TRUSTED
1279 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1281 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1282 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0;
1284 bool have_sym = FALSE, have_lnm = FALSE;
1285 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1286 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1287 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1288 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1289 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1290 #if defined(PERL_IMPLICIT_CONTEXT)
1293 #if defined(USE_ITHREADS)
1294 static perl_mutex primenv_mutex;
1295 MUTEX_INIT(&primenv_mutex);
1298 #if defined(PERL_IMPLICIT_CONTEXT)
1299 /* We jump through these hoops because we can be called at */
1300 /* platform-specific initialization time, which is before anything is */
1301 /* set up--we can't even do a plain dTHX since that relies on the */
1302 /* interpreter structure to be initialized */
1304 aTHX = PERL_GET_INTERP;
1306 /* we never get here because the NULL pointer will cause the */
1307 /* several of the routines called by this routine to access violate */
1309 /* This routine is only called by hv.c/hv_iterinit which has a */
1310 /* context, so the real fix may be to pass it through instead of */
1311 /* the hoops above */
1316 if (primed || !PL_envgv) return;
1317 MUTEX_LOCK(&primenv_mutex);
1318 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1319 envhv = GvHVn(PL_envgv);
1320 /* Perform a dummy fetch as an lval to insure that the hash table is
1321 * set up. Otherwise, the hv_store() will turn into a nullop. */
1322 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1324 for (i = 0; env_tables[i]; i++) {
1325 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1326 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1327 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1329 if (have_sym || have_lnm) {
1330 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1331 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1332 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1333 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1336 for (i--; i >= 0; i--) {
1337 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1340 for (j = 0; environ[j]; j++) {
1341 if (!(start = strchr(environ[j],'='))) {
1342 if (ckWARN(WARN_INTERNAL))
1343 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1347 sv = newSVpv(start,0);
1349 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1354 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1355 !str$case_blind_compare(&tmpdsc,&clisym)) {
1356 my_strlcpy(cmd, "Show Symbol/Global *", sizeof(cmd));
1357 cmddsc.dsc$w_length = 20;
1358 if (env_tables[i]->dsc$w_length == 12 &&
1359 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1360 !str$case_blind_compare(&tmpdsc,&local)) my_strlcpy(cmd+12, "Local *", sizeof(cmd)-12);
1361 flags = defflags | CLI$M_NOLOGNAM;
1364 my_strlcpy(cmd, "Show Logical *", sizeof(cmd));
1365 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1366 my_strlcat(cmd," /Table=", sizeof(cmd));
1367 cmddsc.dsc$w_length = my_strlcat(cmd, env_tables[i]->dsc$a_pointer, sizeof(cmd));
1369 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1370 flags = defflags | CLI$M_NOCLISYM;
1373 /* Create a new subprocess to execute each command, to exclude the
1374 * remote possibility that someone could subvert a mbx or file used
1375 * to write multiple commands to a single subprocess.
1378 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1379 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1380 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1381 defflags &= ~CLI$M_TRUSTED;
1382 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1384 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1385 if (seenhv) SvREFCNT_dec(seenhv);
1388 char *cp1, *cp2, *key;
1389 unsigned long int sts, iosb[2], retlen, keylen;
1392 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1393 if (sts & 1) sts = iosb[0] & 0xffff;
1394 if (sts == SS$_ENDOFFILE) {
1396 while (substs == 0) { sys$hiber(); wakect++;}
1397 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1402 retlen = iosb[0] >> 16;
1403 if (!retlen) continue; /* blank line */
1405 if (iosb[1] != subpid) {
1407 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1411 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1412 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1414 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1415 if (*cp1 == '(' || /* Logical name table name */
1416 *cp1 == '=' /* Next eqv of searchlist */) continue;
1417 if (*cp1 == '"') cp1++;
1418 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1419 key = cp1; keylen = cp2 - cp1;
1420 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1421 while (*cp2 && *cp2 != '=') cp2++;
1422 while (*cp2 && *cp2 == '=') cp2++;
1423 while (*cp2 && *cp2 == ' ') cp2++;
1424 if (*cp2 == '"') { /* String translation; may embed "" */
1425 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1426 cp2++; cp1--; /* Skip "" surrounding translation */
1428 else { /* Numeric translation */
1429 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1430 cp1--; /* stop on last non-space char */
1432 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1433 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1436 PERL_HASH(hash,key,keylen);
1438 if (cp1 == cp2 && *cp2 == '.') {
1439 /* A single dot usually means an unprintable character, such as a null
1440 * to indicate a zero-length value. Get the actual value to make sure.
1442 char lnm[LNM$C_NAMLENGTH+1];
1443 char eqv[MAX_DCL_SYMBOL+1];
1445 strncpy(lnm, key, keylen);
1446 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1447 sv = newSVpvn(eqv, strlen(eqv));
1450 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1454 hv_store(envhv,key,keylen,sv,hash);
1455 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1457 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1458 /* get the PPFs for this process, not the subprocess */
1459 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1460 char eqv[LNM$C_NAMLENGTH+1];
1462 for (i = 0; ppfs[i]; i++) {
1463 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1464 sv = newSVpv(eqv,trnlen);
1466 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1471 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1472 if (buf) Safefree(buf);
1473 if (seenhv) SvREFCNT_dec(seenhv);
1474 MUTEX_UNLOCK(&primenv_mutex);
1477 } /* end of prime_env_iter */
1481 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1482 /* Define or delete an element in the same "environment" as
1483 * vmstrnenv(). If an element is to be deleted, it's removed from
1484 * the first place it's found. If it's to be set, it's set in the
1485 * place designated by the first element of the table vector.
1486 * Like setenv() returns 0 for success, non-zero on error.
1489 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1492 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1493 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1495 unsigned long int retsts, usermode = PSL$C_USER;
1496 struct itmlst_3 *ile, *ilist;
1497 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1498 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1499 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1500 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1501 $DESCRIPTOR(local,"_LOCAL");
1504 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1505 return SS$_IVLOGNAM;
1508 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1509 *cp2 = _toupper(*cp1);
1510 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1511 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1512 return SS$_IVLOGNAM;
1515 lnmdsc.dsc$w_length = cp1 - lnm;
1516 if (!tabvec || !*tabvec) tabvec = env_tables;
1518 if (!eqv) { /* we're deleting n element */
1519 for (curtab = 0; tabvec[curtab]; curtab++) {
1520 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1522 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1523 if ((cp1 = strchr(environ[i],'=')) &&
1524 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1525 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1527 return setenv(lnm,"",1) ? vaxc$errno : 0;
1530 ivenv = 1; retsts = SS$_NOLOGNAM;
1532 if (ckWARN(WARN_INTERNAL))
1533 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1534 ivenv = 1; retsts = SS$_NOSUCHPGM;
1540 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1541 !str$case_blind_compare(&tmpdsc,&clisym)) {
1542 unsigned int symtype;
1543 if (tabvec[curtab]->dsc$w_length == 12 &&
1544 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1545 !str$case_blind_compare(&tmpdsc,&local))
1546 symtype = LIB$K_CLI_LOCAL_SYM;
1547 else symtype = LIB$K_CLI_GLOBAL_SYM;
1548 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1549 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1550 if (retsts == LIB$_NOSUCHSYM) continue;
1554 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1555 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1556 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1557 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1558 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1562 else { /* we're defining a value */
1563 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1565 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1567 if (ckWARN(WARN_INTERNAL))
1568 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1569 retsts = SS$_NOSUCHPGM;
1573 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1574 eqvdsc.dsc$w_length = strlen(eqv);
1575 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1576 !str$case_blind_compare(&tmpdsc,&clisym)) {
1577 unsigned int symtype;
1578 if (tabvec[0]->dsc$w_length == 12 &&
1579 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1580 !str$case_blind_compare(&tmpdsc,&local))
1581 symtype = LIB$K_CLI_LOCAL_SYM;
1582 else symtype = LIB$K_CLI_GLOBAL_SYM;
1583 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1586 if (!*eqv) eqvdsc.dsc$w_length = 1;
1587 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1589 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1590 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1591 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1592 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1593 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1594 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1597 Newx(ilist,nseg+1,struct itmlst_3);
1600 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1603 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1605 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1606 ile->itmcode = LNM$_STRING;
1608 if ((j+1) == nseg) {
1609 ile->buflen = strlen(c);
1610 /* in case we are truncating one that's too long */
1611 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1614 ile->buflen = LNM$C_NAMLENGTH;
1618 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1622 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1627 if (!(retsts & 1)) {
1629 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1630 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1631 set_errno(EVMSERR); break;
1632 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1633 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1634 set_errno(EINVAL); break;
1636 set_errno(EACCES); break;
1641 set_vaxc_errno(retsts);
1642 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1645 /* We reset error values on success because Perl does an hv_fetch()
1646 * before each hv_store(), and if the thing we're setting didn't
1647 * previously exist, we've got a leftover error message. (Of course,
1648 * this fails in the face of
1649 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1650 * in that the error reported in $! isn't spurious,
1651 * but it's right more often than not.)
1653 set_errno(0); set_vaxc_errno(retsts);
1657 } /* end of vmssetenv() */
1660 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1661 /* This has to be a function since there's a prototype for it in proto.h */
1663 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1666 int len = strlen(lnm);
1670 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1671 if (!strcmp(uplnm,"DEFAULT")) {
1672 if (eqv && *eqv) my_chdir(eqv);
1677 (void) vmssetenv(lnm,eqv,NULL);
1681 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1683 * sets a user-mode logical in the process logical name table
1684 * used for redirection of sys$error
1687 Perl_vmssetuserlnm(const char *name, const char *eqv)
1689 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1690 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1691 unsigned long int iss, attr = LNM$M_CONFINE;
1692 unsigned char acmode = PSL$C_USER;
1693 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1695 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1696 d_name.dsc$w_length = strlen(name);
1698 lnmlst[0].buflen = strlen(eqv);
1699 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1701 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1702 if (!(iss&1)) lib$signal(iss);
1707 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1708 /* my_crypt - VMS password hashing
1709 * my_crypt() provides an interface compatible with the Unix crypt()
1710 * C library function, and uses sys$hash_password() to perform VMS
1711 * password hashing. The quadword hashed password value is returned
1712 * as a NUL-terminated 8 character string. my_crypt() does not change
1713 * the case of its string arguments; in order to match the behavior
1714 * of LOGINOUT et al., alphabetic characters in both arguments must
1715 * be upcased by the caller.
1717 * - fix me to call ACM services when available
1720 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1722 # ifndef UAI$C_PREFERRED_ALGORITHM
1723 # define UAI$C_PREFERRED_ALGORITHM 127
1725 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1726 unsigned short int salt = 0;
1727 unsigned long int sts;
1729 unsigned short int dsc$w_length;
1730 unsigned char dsc$b_type;
1731 unsigned char dsc$b_class;
1732 const char * dsc$a_pointer;
1733 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1734 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1735 struct itmlst_3 uailst[3] = {
1736 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1737 { sizeof salt, UAI$_SALT, &salt, 0},
1738 { 0, 0, NULL, NULL}};
1739 static char hash[9];
1741 usrdsc.dsc$w_length = strlen(usrname);
1742 usrdsc.dsc$a_pointer = usrname;
1743 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1745 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1749 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1754 set_vaxc_errno(sts);
1755 if (sts != RMS$_RNF) return NULL;
1758 txtdsc.dsc$w_length = strlen(textpasswd);
1759 txtdsc.dsc$a_pointer = textpasswd;
1760 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1761 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1764 return (char *) hash;
1766 } /* end of my_crypt() */
1770 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1771 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1772 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1774 /* 8.3, remove() is now broken on symbolic links */
1775 static int rms_erase(const char * vmsname);
1779 * A little hack to get around a bug in some implementation of remove()
1780 * that do not know how to delete a directory
1782 * Delete any file to which user has control access, regardless of whether
1783 * delete access is explicitly allowed.
1784 * Limitations: User must have write access to parent directory.
1785 * Does not block signals or ASTs; if interrupted in midstream
1786 * may leave file with an altered ACL.
1789 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1791 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1795 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1796 unsigned long int cxt = 0, aclsts, fndsts;
1798 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1800 unsigned char myace$b_length;
1801 unsigned char myace$b_type;
1802 unsigned short int myace$w_flags;
1803 unsigned long int myace$l_access;
1804 unsigned long int myace$l_ident;
1805 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1806 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1807 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1809 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1810 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1811 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1812 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1813 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1814 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1816 /* Expand the input spec using RMS, since the CRTL remove() and
1817 * system services won't do this by themselves, so we may miss
1818 * a file "hiding" behind a logical name or search list. */
1819 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
1820 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1822 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
1824 PerlMem_free(vmsname);
1828 /* Erase the file */
1829 rmsts = rms_erase(vmsname);
1831 /* Did it succeed */
1832 if ($VMS_STATUS_SUCCESS(rmsts)) {
1833 PerlMem_free(vmsname);
1837 /* If not, can changing protections help? */
1838 if (rmsts != RMS$_PRV) {
1839 set_vaxc_errno(rmsts);
1840 PerlMem_free(vmsname);
1844 /* No, so we get our own UIC to use as a rights identifier,
1845 * and the insert an ACE at the head of the ACL which allows us
1846 * to delete the file.
1848 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1849 fildsc.dsc$w_length = strlen(vmsname);
1850 fildsc.dsc$a_pointer = vmsname;
1852 newace.myace$l_ident = oldace.myace$l_ident;
1854 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1856 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1857 set_errno(ENOENT); break;
1859 set_errno(ENOTDIR); break;
1861 set_errno(ENODEV); break;
1862 case RMS$_SYN: case SS$_INVFILFOROP:
1863 set_errno(EINVAL); break;
1865 set_errno(EACCES); break;
1867 _ckvmssts_noperl(aclsts);
1869 set_vaxc_errno(aclsts);
1870 PerlMem_free(vmsname);
1873 /* Grab any existing ACEs with this identifier in case we fail */
1874 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1875 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1876 || fndsts == SS$_NOMOREACE ) {
1877 /* Add the new ACE . . . */
1878 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1881 rmsts = rms_erase(vmsname);
1882 if ($VMS_STATUS_SUCCESS(rmsts)) {
1887 /* We blew it - dir with files in it, no write priv for
1888 * parent directory, etc. Put things back the way they were. */
1889 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1892 addlst[0].bufadr = &oldace;
1893 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1900 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1901 /* We just deleted it, so of course it's not there. Some versions of
1902 * VMS seem to return success on the unlock operation anyhow (after all
1903 * the unlock is successful), but others don't.
1905 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1906 if (aclsts & 1) aclsts = fndsts;
1907 if (!(aclsts & 1)) {
1909 set_vaxc_errno(aclsts);
1912 PerlMem_free(vmsname);
1915 } /* end of kill_file() */
1919 /*{{{int do_rmdir(char *name)*/
1921 Perl_do_rmdir(pTHX_ const char *name)
1927 /* lstat returns a VMS fileified specification of the name */
1928 /* that is looked up, and also lets verifies that this is a directory */
1930 retval = flex_lstat(name, &st);
1934 /* Due to a historical feature, flex_stat/lstat can not see some */
1935 /* Unix format file names that the rest of the CRTL can see */
1936 /* Fixing that feature will cause some perl tests to fail */
1937 /* So try this one more time. */
1939 retval = lstat(name, &st.crtl_stat);
1943 /* force it to a file spec for the kill file to work. */
1944 ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
1945 if (ret_spec == NULL) {
1951 if (!S_ISDIR(st.st_mode)) {
1956 dirfile = st.st_devnam;
1958 /* It may be possible for flex_stat to find a file and vmsify() to */
1959 /* fail with ODS-2 specifications. mp_do_kill_file can not deal */
1960 /* with that case, so fail it */
1961 if (dirfile[0] == 0) {
1966 retval = mp_do_kill_file(aTHX_ dirfile, 1);
1971 } /* end of do_rmdir */
1975 * Delete any file to which user has control access, regardless of whether
1976 * delete access is explicitly allowed.
1977 * Limitations: User must have write access to parent directory.
1978 * Does not block signals or ASTs; if interrupted in midstream
1979 * may leave file with an altered ACL.
1982 /*{{{int kill_file(char *name)*/
1984 Perl_kill_file(pTHX_ const char *name)
1990 /* Convert the filename to VMS format and see if it is a directory */
1991 /* flex_lstat returns a vmsified file specification */
1992 rmsts = flex_lstat(name, &st);
1995 /* Due to a historical feature, flex_stat/lstat can not see some */
1996 /* Unix format file names that the rest of the CRTL can see when */
1997 /* ODS-2 file specifications are in use. */
1998 /* Fixing that feature will cause some perl tests to fail */
1999 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2001 vmsfile = (char *) name; /* cast ok */
2004 vmsfile = st.st_devnam;
2005 if (vmsfile[0] == 0) {
2006 /* It may be possible for flex_stat to find a file and vmsify() */
2007 /* to fail with ODS-2 specifications. mp_do_kill_file can not */
2008 /* deal with that case, so fail it */
2014 /* Remove() is allowed to delete directories, according to the X/Open
2016 * This may need special handling to work with the ACL hacks.
2018 if (S_ISDIR(st.st_mode)) {
2019 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2023 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2025 /* Need to delete all versions ? */
2026 if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2029 /* Just use lstat() here as do not need st_dev */
2030 /* and we know that the file is in VMS format or that */
2031 /* because of a historical bug, flex_stat can not see the file */
2032 while (lstat(vmsfile, (stat_t *)&st) == 0) {
2033 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2038 /* Make sure that we do not loop forever */
2049 } /* end of kill_file() */
2053 /*{{{int my_mkdir(char *,Mode_t)*/
2055 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2057 STRLEN dirlen = strlen(dir);
2059 /* zero length string sometimes gives ACCVIO */
2060 if (dirlen == 0) return -1;
2062 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2063 * null file name/type. However, it's commonplace under Unix,
2064 * so we'll allow it for a gain in portability.
2066 if (dir[dirlen-1] == '/') {
2067 char *newdir = savepvn(dir,dirlen-1);
2068 int ret = mkdir(newdir,mode);
2072 else return mkdir(dir,mode);
2073 } /* end of my_mkdir */
2076 /*{{{int my_chdir(char *)*/
2078 Perl_my_chdir(pTHX_ const char *dir)
2080 STRLEN dirlen = strlen(dir);
2081 const char *dir1 = dir;
2083 /* zero length string sometimes gives ACCVIO */
2085 SETERRNO(EINVAL, SS$_BADPARAM);
2089 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2090 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2091 * so that existing scripts do not need to be changed.
2093 while ((dirlen > 0) && (*dir1 == ' ')) {
2098 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2100 * null file name/type. However, it's commonplace under Unix,
2101 * so we'll allow it for a gain in portability.
2103 * '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
2105 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2108 newdir = (char *)PerlMem_malloc(dirlen);
2110 _ckvmssts_noperl(SS$_INSFMEM);
2111 memcpy(newdir, dir1, dirlen-1);
2112 newdir[dirlen-1] = '\0';
2113 ret = chdir(newdir);
2114 PerlMem_free(newdir);
2117 else return chdir(dir1);
2118 } /* end of my_chdir */
2122 /*{{{int my_chmod(char *, mode_t)*/
2124 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2129 STRLEN speclen = strlen(file_spec);
2131 /* zero length string sometimes gives ACCVIO */
2132 if (speclen == 0) return -1;
2134 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2135 * that implies null file name/type. However, it's commonplace under Unix,
2136 * so we'll allow it for a gain in portability.
2138 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2139 * in VMS file.dir notation.
2141 changefile = (char *) file_spec; /* cast ok */
2142 ret = flex_lstat(file_spec, &st);
2145 /* Due to a historical feature, flex_stat/lstat can not see some */
2146 /* Unix format file names that the rest of the CRTL can see when */
2147 /* ODS-2 file specifications are in use. */
2148 /* Fixing that feature will cause some perl tests to fail */
2149 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2153 /* It may be possible to get here with nothing in st_devname */
2154 /* chmod still may work though */
2155 if (st.st_devnam[0] != 0) {
2156 changefile = st.st_devnam;
2159 ret = chmod(changefile, mode);
2161 } /* end of my_chmod */
2165 /*{{{FILE *my_tmpfile()*/
2172 if ((fp = tmpfile())) return fp;
2174 cp = (char *)PerlMem_malloc(L_tmpnam+24);
2175 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2177 if (decc_filename_unix_only == 0)
2178 strcpy(cp,"Sys$Scratch:");
2181 tmpnam(cp+strlen(cp));
2182 strcat(cp,".Perltmp");
2183 fp = fopen(cp,"w+","fop=dlt");
2191 * The C RTL's sigaction fails to check for invalid signal numbers so we
2192 * help it out a bit. The docs are correct, but the actual routine doesn't
2193 * do what the docs say it will.
2195 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2197 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2198 struct sigaction* oact)
2200 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2201 SETERRNO(EINVAL, SS$_INVARG);
2204 return sigaction(sig, act, oact);
2208 #ifdef KILL_BY_SIGPRC
2209 #include <errnodef.h>
2211 /* We implement our own kill() using the undocumented system service
2212 sys$sigprc for one of two reasons:
2214 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2215 target process to do a sys$exit, which usually can't be handled
2216 gracefully...certainly not by Perl and the %SIG{} mechanism.
2218 2.) If the kill() in the CRTL can't be called from a signal
2219 handler without disappearing into the ether, i.e., the signal
2220 it purportedly sends is never trapped. Still true as of VMS 7.3.
2222 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2223 in the target process rather than calling sys$exit.
2225 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2226 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2227 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2228 with condition codes C$_SIG0+nsig*8, catching the exception on the
2229 target process and resignaling with appropriate arguments.
2231 But we don't have that VMS 7.0+ exception handler, so if you
2232 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2234 Also note that SIGTERM is listed in the docs as being "unimplemented",
2235 yet always seems to be signaled with a VMS condition code of 4 (and
2236 correctly handled for that code). So we hardwire it in.
2238 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2239 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2240 than signalling with an unrecognized (and unhandled by CRTL) code.
2243 #define _MY_SIG_MAX 28
2246 Perl_sig_to_vmscondition_int(int sig)
2248 static unsigned int sig_code[_MY_SIG_MAX+1] =
2251 SS$_HANGUP, /* 1 SIGHUP */
2252 SS$_CONTROLC, /* 2 SIGINT */
2253 SS$_CONTROLY, /* 3 SIGQUIT */
2254 SS$_RADRMOD, /* 4 SIGILL */
2255 SS$_BREAK, /* 5 SIGTRAP */
2256 SS$_OPCCUS, /* 6 SIGABRT */
2257 SS$_COMPAT, /* 7 SIGEMT */
2259 SS$_FLTOVF, /* 8 SIGFPE VAX */
2261 SS$_HPARITH, /* 8 SIGFPE AXP */
2263 SS$_ABORT, /* 9 SIGKILL */
2264 SS$_ACCVIO, /* 10 SIGBUS */
2265 SS$_ACCVIO, /* 11 SIGSEGV */
2266 SS$_BADPARAM, /* 12 SIGSYS */
2267 SS$_NOMBX, /* 13 SIGPIPE */
2268 SS$_ASTFLT, /* 14 SIGALRM */
2285 static int initted = 0;
2288 sig_code[16] = C$_SIGUSR1;
2289 sig_code[17] = C$_SIGUSR2;
2290 sig_code[20] = C$_SIGCHLD;
2291 #if __CRTL_VER >= 70300000
2292 sig_code[28] = C$_SIGWINCH;
2296 if (sig < _SIG_MIN) return 0;
2297 if (sig > _MY_SIG_MAX) return 0;
2298 return sig_code[sig];
2302 Perl_sig_to_vmscondition(int sig)
2305 if (vms_debug_on_exception != 0)
2306 lib$signal(SS$_DEBUG);
2308 return Perl_sig_to_vmscondition_int(sig);
2312 #define sys$sigprc SYS$SIGPRC
2316 int sys$sigprc(unsigned int *pidadr,
2317 struct dsc$descriptor_s *prcname,
2324 Perl_my_kill(int pid, int sig)
2329 /* sig 0 means validate the PID */
2330 /*------------------------------*/
2332 const unsigned long int jpicode = JPI$_PID;
2335 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2336 if ($VMS_STATUS_SUCCESS(status))
2339 case SS$_NOSUCHNODE:
2340 case SS$_UNREACHABLE:
2354 code = Perl_sig_to_vmscondition_int(sig);
2357 SETERRNO(EINVAL, SS$_BADPARAM);
2361 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2362 * signals are to be sent to multiple processes.
2363 * pid = 0 - all processes in group except ones that the system exempts
2364 * pid = -1 - all processes except ones that the system exempts
2365 * pid = -n - all processes in group (abs(n)) except ...
2366 * For now, just report as not supported.
2370 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2374 iss = sys$sigprc((unsigned int *)&pid,0,code);
2375 if (iss&1) return 0;
2379 set_errno(EPERM); break;
2381 case SS$_NOSUCHNODE:
2382 case SS$_UNREACHABLE:
2383 set_errno(ESRCH); break;
2385 set_errno(ENOMEM); break;
2387 _ckvmssts_noperl(iss);
2390 set_vaxc_errno(iss);
2396 /* Routine to convert a VMS status code to a UNIX status code.
2397 ** More tricky than it appears because of conflicting conventions with
2400 ** VMS status codes are a bit mask, with the least significant bit set for
2403 ** Special UNIX status of EVMSERR indicates that no translation is currently
2404 ** available, and programs should check the VMS status code.
2406 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2410 #ifndef C_FACILITY_NO
2411 #define C_FACILITY_NO 0x350000
2414 #define DCL_IVVERB 0x38090
2418 Perl_vms_status_to_unix(int vms_status, int child_flag)
2426 /* Assume the best or the worst */
2427 if (vms_status & STS$M_SUCCESS)
2430 unix_status = EVMSERR;
2432 msg_status = vms_status & ~STS$M_CONTROL;
2434 facility = vms_status & STS$M_FAC_NO;
2435 fac_sp = vms_status & STS$M_FAC_SP;
2436 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2438 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2444 unix_status = EFAULT;
2446 case SS$_DEVOFFLINE:
2447 unix_status = EBUSY;
2450 unix_status = ENOTCONN;
2458 case SS$_INVFILFOROP:
2462 unix_status = EINVAL;
2464 case SS$_UNSUPPORTED:
2465 unix_status = ENOTSUP;
2470 unix_status = EACCES;
2472 case SS$_DEVICEFULL:
2473 unix_status = ENOSPC;
2476 unix_status = ENODEV;
2478 case SS$_NOSUCHFILE:
2479 case SS$_NOSUCHOBJECT:
2480 unix_status = ENOENT;
2482 case SS$_ABORT: /* Fatal case */
2483 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2484 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2485 unix_status = EINTR;
2488 unix_status = E2BIG;
2491 unix_status = ENOMEM;
2494 unix_status = EPERM;
2496 case SS$_NOSUCHNODE:
2497 case SS$_UNREACHABLE:
2498 unix_status = ESRCH;
2501 unix_status = ECHILD;
2504 if ((facility == 0) && (msg_no < 8)) {
2505 /* These are not real VMS status codes so assume that they are
2506 ** already UNIX status codes
2508 unix_status = msg_no;
2514 /* Translate a POSIX exit code to a UNIX exit code */
2515 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2516 unix_status = (msg_no & 0x07F8) >> 3;
2520 /* Documented traditional behavior for handling VMS child exits */
2521 /*--------------------------------------------------------------*/
2522 if (child_flag != 0) {
2524 /* Success / Informational return 0 */
2525 /*----------------------------------*/
2526 if (msg_no & STS$K_SUCCESS)
2529 /* Warning returns 1 */
2530 /*-------------------*/
2531 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2534 /* Everything else pass through the severity bits */
2535 /*------------------------------------------------*/
2536 return (msg_no & STS$M_SEVERITY);
2539 /* Normal VMS status to ERRNO mapping attempt */
2540 /*--------------------------------------------*/
2541 switch(msg_status) {
2542 /* case RMS$_EOF: */ /* End of File */
2543 case RMS$_FNF: /* File Not Found */
2544 case RMS$_DNF: /* Dir Not Found */
2545 unix_status = ENOENT;
2547 case RMS$_RNF: /* Record Not Found */
2548 unix_status = ESRCH;
2551 unix_status = ENOTDIR;
2554 unix_status = ENODEV;
2559 unix_status = EBADF;
2562 unix_status = EEXIST;
2566 case LIB$_INVSTRDES:
2568 case LIB$_NOSUCHSYM:
2569 case LIB$_INVSYMNAM:
2571 unix_status = EINVAL;
2577 unix_status = E2BIG;
2579 case RMS$_PRV: /* No privilege */
2580 case RMS$_ACC: /* ACP file access failed */
2581 case RMS$_WLK: /* Device write locked */
2582 unix_status = EACCES;
2584 case RMS$_MKD: /* Failed to mark for delete */
2585 unix_status = EPERM;
2587 /* case RMS$_NMF: */ /* No more files */
2595 /* Try to guess at what VMS error status should go with a UNIX errno
2596 * value. This is hard to do as there could be many possible VMS
2597 * error statuses that caused the errno value to be set.
2601 Perl_unix_status_to_vms(int unix_status)
2603 int test_unix_status;
2605 /* Trivial cases first */
2606 /*---------------------*/
2607 if (unix_status == EVMSERR)
2610 /* Is vaxc$errno sane? */
2611 /*---------------------*/
2612 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2613 if (test_unix_status == unix_status)
2616 /* If way out of range, must be VMS code already */
2617 /*-----------------------------------------------*/
2618 if (unix_status > EVMSERR)
2621 /* If out of range, punt */
2622 /*-----------------------*/
2623 if (unix_status > __ERRNO_MAX)
2627 /* Ok, now we have to do it the hard way. */
2628 /*----------------------------------------*/
2629 switch(unix_status) {
2630 case 0: return SS$_NORMAL;
2631 case EPERM: return SS$_NOPRIV;
2632 case ENOENT: return SS$_NOSUCHOBJECT;
2633 case ESRCH: return SS$_UNREACHABLE;
2634 case EINTR: return SS$_ABORT;
2637 case E2BIG: return SS$_BUFFEROVF;
2639 case EBADF: return RMS$_IFI;
2640 case ECHILD: return SS$_NONEXPR;
2642 case ENOMEM: return SS$_INSFMEM;
2643 case EACCES: return SS$_FILACCERR;
2644 case EFAULT: return SS$_ACCVIO;
2646 case EBUSY: return SS$_DEVOFFLINE;
2647 case EEXIST: return RMS$_FEX;
2649 case ENODEV: return SS$_NOSUCHDEV;
2650 case ENOTDIR: return RMS$_DIR;
2652 case EINVAL: return SS$_INVARG;
2658 case ENOSPC: return SS$_DEVICEFULL;
2659 case ESPIPE: return LIB$_INVARG;
2664 case ERANGE: return LIB$_INVARG;
2665 /* case EWOULDBLOCK */
2666 /* case EINPROGRESS */
2669 /* case EDESTADDRREQ */
2671 /* case EPROTOTYPE */
2672 /* case ENOPROTOOPT */
2673 /* case EPROTONOSUPPORT */
2674 /* case ESOCKTNOSUPPORT */
2675 /* case EOPNOTSUPP */
2676 /* case EPFNOSUPPORT */
2677 /* case EAFNOSUPPORT */
2678 /* case EADDRINUSE */
2679 /* case EADDRNOTAVAIL */
2681 /* case ENETUNREACH */
2682 /* case ENETRESET */
2683 /* case ECONNABORTED */
2684 /* case ECONNRESET */
2687 case ENOTCONN: return SS$_CLEARED;
2688 /* case ESHUTDOWN */
2689 /* case ETOOMANYREFS */
2690 /* case ETIMEDOUT */
2691 /* case ECONNREFUSED */
2693 /* case ENAMETOOLONG */
2694 /* case EHOSTDOWN */
2695 /* case EHOSTUNREACH */
2696 /* case ENOTEMPTY */
2708 /* case ECANCELED */
2712 return SS$_UNSUPPORTED;
2718 /* case EABANDONED */
2720 return SS$_ABORT; /* punt */
2725 /* default piping mailbox size */
2727 # define PERL_BUFSIZ 512
2729 # define PERL_BUFSIZ 8192
2734 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2736 unsigned long int mbxbufsiz;
2737 static unsigned long int syssize = 0;
2738 unsigned long int dviitm = DVI$_DEVNAM;
2739 char csize[LNM$C_NAMLENGTH+1];
2743 unsigned long syiitm = SYI$_MAXBUF;
2745 * Get the SYSGEN parameter MAXBUF
2747 * If the logical 'PERL_MBX_SIZE' is defined
2748 * use the value of the logical instead of PERL_BUFSIZ, but
2749 * keep the size between 128 and MAXBUF.
2752 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2755 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2756 mbxbufsiz = atoi(csize);
2758 mbxbufsiz = PERL_BUFSIZ;
2760 if (mbxbufsiz < 128) mbxbufsiz = 128;
2761 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2763 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2765 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2766 _ckvmssts_noperl(sts);
2767 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2769 } /* end of create_mbx() */
2772 /*{{{ my_popen and my_pclose*/
2774 typedef struct _iosb IOSB;
2775 typedef struct _iosb* pIOSB;
2776 typedef struct _pipe Pipe;
2777 typedef struct _pipe* pPipe;
2778 typedef struct pipe_details Info;
2779 typedef struct pipe_details* pInfo;
2780 typedef struct _srqp RQE;
2781 typedef struct _srqp* pRQE;
2782 typedef struct _tochildbuf CBuf;
2783 typedef struct _tochildbuf* pCBuf;
2786 unsigned short status;
2787 unsigned short count;
2788 unsigned long dvispec;
2791 #pragma member_alignment save
2792 #pragma nomember_alignment quadword
2793 struct _srqp { /* VMS self-relative queue entry */
2794 unsigned long qptr[2];
2796 #pragma member_alignment restore
2797 static RQE RQE_ZERO = {0,0};
2799 struct _tochildbuf {
2802 unsigned short size;
2810 unsigned short chan_in;
2811 unsigned short chan_out;
2813 unsigned int bufsize;
2825 #if defined(PERL_IMPLICIT_CONTEXT)
2826 void *thx; /* Either a thread or an interpreter */
2827 /* pointer, depending on how we're built */
2835 PerlIO *fp; /* file pointer to pipe mailbox */
2836 int useFILE; /* using stdio, not perlio */
2837 int pid; /* PID of subprocess */
2838 int mode; /* == 'r' if pipe open for reading */
2839 int done; /* subprocess has completed */
2840 int waiting; /* waiting for completion/closure */
2841 int closing; /* my_pclose is closing this pipe */
2842 unsigned long completion; /* termination status of subprocess */
2843 pPipe in; /* pipe in to sub */
2844 pPipe out; /* pipe out of sub */
2845 pPipe err; /* pipe of sub's sys$error */
2846 int in_done; /* true when in pipe finished */
2849 unsigned short xchan; /* channel to debug xterm */
2850 unsigned short xchan_valid; /* channel is assigned */
2853 struct exit_control_block
2855 struct exit_control_block *flink;
2856 unsigned long int (*exit_routine)(void);
2857 unsigned long int arg_count;
2858 unsigned long int *status_address;
2859 unsigned long int exit_status;
2862 typedef struct _closed_pipes Xpipe;
2863 typedef struct _closed_pipes* pXpipe;
2865 struct _closed_pipes {
2866 int pid; /* PID of subprocess */
2867 unsigned long completion; /* termination status of subprocess */
2869 #define NKEEPCLOSED 50
2870 static Xpipe closed_list[NKEEPCLOSED];
2871 static int closed_index = 0;
2872 static int closed_num = 0;
2874 #define RETRY_DELAY "0 ::0.20"
2875 #define MAX_RETRY 50
2877 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2878 static unsigned long mypid;
2879 static unsigned long delaytime[2];
2881 static pInfo open_pipes = NULL;
2882 static $DESCRIPTOR(nl_desc, "NL:");
2884 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2888 static unsigned long int
2889 pipe_exit_routine(void)
2892 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2893 int sts, did_stuff, j;
2896 * Flush any pending i/o, but since we are in process run-down, be
2897 * careful about referencing PerlIO structures that may already have
2898 * been deallocated. We may not even have an interpreter anymore.
2903 #if defined(PERL_IMPLICIT_CONTEXT)
2904 /* We need to use the Perl context of the thread that created */
2908 aTHX = info->err->thx;
2910 aTHX = info->out->thx;
2912 aTHX = info->in->thx;
2915 #if defined(USE_ITHREADS)
2919 && PL_perlio_fd_refcnt
2922 PerlIO_flush(info->fp);
2924 fflush((FILE *)info->fp);
2930 next we try sending an EOF...ignore if doesn't work, make sure we
2937 _ckvmssts_noperl(sys$setast(0));
2938 if (info->in && !info->in->shut_on_empty) {
2939 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2944 _ckvmssts_noperl(sys$setast(1));
2948 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2950 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2955 _ckvmssts_noperl(sys$setast(0));
2956 if (info->waiting && info->done)
2958 nwait += info->waiting;
2959 _ckvmssts_noperl(sys$setast(1));
2969 _ckvmssts_noperl(sys$setast(0));
2970 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2971 sts = sys$forcex(&info->pid,0,&abort);
2972 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2975 _ckvmssts_noperl(sys$setast(1));
2979 /* again, wait for effect */
2981 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2986 _ckvmssts_noperl(sys$setast(0));
2987 if (info->waiting && info->done)
2989 nwait += info->waiting;
2990 _ckvmssts_noperl(sys$setast(1));
2999 _ckvmssts_noperl(sys$setast(0));
3000 if (!info->done) { /* We tried to be nice . . . */
3001 sts = sys$delprc(&info->pid,0);
3002 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3003 info->done = 1; /* sys$delprc is as done as we're going to get. */
3005 _ckvmssts_noperl(sys$setast(1));
3011 #if defined(PERL_IMPLICIT_CONTEXT)
3012 /* We need to use the Perl context of the thread that created */
3015 if (open_pipes->err)
3016 aTHX = open_pipes->err->thx;
3017 else if (open_pipes->out)
3018 aTHX = open_pipes->out->thx;
3019 else if (open_pipes->in)
3020 aTHX = open_pipes->in->thx;
3022 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3023 else if (!(sts & 1)) retsts = sts;
3028 static struct exit_control_block pipe_exitblock =
3029 {(struct exit_control_block *) 0,
3030 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3032 static void pipe_mbxtofd_ast(pPipe p);
3033 static void pipe_tochild1_ast(pPipe p);
3034 static void pipe_tochild2_ast(pPipe p);
3037 popen_completion_ast(pInfo info)
3039 pInfo i = open_pipes;
3042 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3043 closed_list[closed_index].pid = info->pid;
3044 closed_list[closed_index].completion = info->completion;
3046 if (closed_index == NKEEPCLOSED)
3051 if (i == info) break;
3054 if (!i) return; /* unlinked, probably freed too */
3059 Writing to subprocess ...
3060 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3062 chan_out may be waiting for "done" flag, or hung waiting
3063 for i/o completion to child...cancel the i/o. This will
3064 put it into "snarf mode" (done but no EOF yet) that discards
3067 Output from subprocess (stdout, stderr) needs to be flushed and
3068 shut down. We try sending an EOF, but if the mbx is full the pipe
3069 routine should still catch the "shut_on_empty" flag, telling it to
3070 use immediate-style reads so that "mbx empty" -> EOF.
3074 if (info->in && !info->in_done) { /* only for mode=w */
3075 if (info->in->shut_on_empty && info->in->need_wake) {
3076 info->in->need_wake = FALSE;
3077 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3079 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3083 if (info->out && !info->out_done) { /* were we also piping output? */
3084 info->out->shut_on_empty = TRUE;
3085 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3086 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3087 _ckvmssts_noperl(iss);
3090 if (info->err && !info->err_done) { /* we were piping stderr */
3091 info->err->shut_on_empty = TRUE;
3092 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3093 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3094 _ckvmssts_noperl(iss);
3096 _ckvmssts_noperl(sys$setef(pipe_ef));
3100 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3101 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3102 static void pipe_infromchild_ast(pPipe p);
3105 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3106 inside an AST routine without worrying about reentrancy and which Perl
3107 memory allocator is being used.
3109 We read data and queue up the buffers, then spit them out one at a
3110 time to the output mailbox when the output mailbox is ready for one.
3113 #define INITIAL_TOCHILDQUEUE 2
3116 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3120 char mbx1[64], mbx2[64];
3121 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3122 DSC$K_CLASS_S, mbx1},
3123 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3124 DSC$K_CLASS_S, mbx2};
3125 unsigned int dviitm = DVI$_DEVBUFSIZ;
3129 _ckvmssts_noperl(lib$get_vm(&n, &p));
3131 create_mbx(&p->chan_in , &d_mbx1);
3132 create_mbx(&p->chan_out, &d_mbx2);
3133 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3136 p->shut_on_empty = FALSE;
3137 p->need_wake = FALSE;
3140 p->iosb.status = SS$_NORMAL;
3141 p->iosb2.status = SS$_NORMAL;
3147 #ifdef PERL_IMPLICIT_CONTEXT
3151 n = sizeof(CBuf) + p->bufsize;
3153 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3154 _ckvmssts_noperl(lib$get_vm(&n, &b));
3155 b->buf = (char *) b + sizeof(CBuf);
3156 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3159 pipe_tochild2_ast(p);
3160 pipe_tochild1_ast(p);
3166 /* reads the MBX Perl is writing, and queues */
3169 pipe_tochild1_ast(pPipe p)
3172 int iss = p->iosb.status;
3173 int eof = (iss == SS$_ENDOFFILE);
3175 #ifdef PERL_IMPLICIT_CONTEXT
3181 p->shut_on_empty = TRUE;
3183 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3185 _ckvmssts_noperl(iss);
3189 b->size = p->iosb.count;
3190 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3192 p->need_wake = FALSE;
3193 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3196 p->retry = 1; /* initial call */
3199 if (eof) { /* flush the free queue, return when done */
3200 int n = sizeof(CBuf) + p->bufsize;
3202 iss = lib$remqti(&p->free, &b);
3203 if (iss == LIB$_QUEWASEMP) return;
3204 _ckvmssts_noperl(iss);
3205 _ckvmssts_noperl(lib$free_vm(&n, &b));
3209 iss = lib$remqti(&p->free, &b);
3210 if (iss == LIB$_QUEWASEMP) {
3211 int n = sizeof(CBuf) + p->bufsize;
3212 _ckvmssts_noperl(lib$get_vm(&n, &b));
3213 b->buf = (char *) b + sizeof(CBuf);
3215 _ckvmssts_noperl(iss);
3219 iss = sys$qio(0,p->chan_in,
3220 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3222 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3223 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3224 _ckvmssts_noperl(iss);
3228 /* writes queued buffers to output, waits for each to complete before
3232 pipe_tochild2_ast(pPipe p)
3235 int iss = p->iosb2.status;
3236 int n = sizeof(CBuf) + p->bufsize;
3237 int done = (p->info && p->info->done) ||
3238 iss == SS$_CANCEL || iss == SS$_ABORT;
3239 #if defined(PERL_IMPLICIT_CONTEXT)
3244 if (p->type) { /* type=1 has old buffer, dispose */
3245 if (p->shut_on_empty) {
3246 _ckvmssts_noperl(lib$free_vm(&n, &b));
3248 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3253 iss = lib$remqti(&p->wait, &b);
3254 if (iss == LIB$_QUEWASEMP) {
3255 if (p->shut_on_empty) {
3257 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3258 *p->pipe_done = TRUE;
3259 _ckvmssts_noperl(sys$setef(pipe_ef));
3261 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3262 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3266 p->need_wake = TRUE;
3269 _ckvmssts_noperl(iss);
3276 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3277 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3279 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3280 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3289 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3292 char mbx1[64], mbx2[64];
3293 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3294 DSC$K_CLASS_S, mbx1},
3295 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3296 DSC$K_CLASS_S, mbx2};
3297 unsigned int dviitm = DVI$_DEVBUFSIZ;
3299 int n = sizeof(Pipe);
3300 _ckvmssts_noperl(lib$get_vm(&n, &p));
3301 create_mbx(&p->chan_in , &d_mbx1);
3302 create_mbx(&p->chan_out, &d_mbx2);
3304 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3305 n = p->bufsize * sizeof(char);
3306 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3307 p->shut_on_empty = FALSE;
3310 p->iosb.status = SS$_NORMAL;
3311 #if defined(PERL_IMPLICIT_CONTEXT)
3314 pipe_infromchild_ast(p);
3322 pipe_infromchild_ast(pPipe p)
3324 int iss = p->iosb.status;
3325 int eof = (iss == SS$_ENDOFFILE);
3326 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3327 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3328 #if defined(PERL_IMPLICIT_CONTEXT)
3332 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3333 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3338 input shutdown if EOF from self (done or shut_on_empty)
3339 output shutdown if closing flag set (my_pclose)
3340 send data/eof from child or eof from self
3341 otherwise, re-read (snarf of data from child)
3346 if (myeof && p->chan_in) { /* input shutdown */
3347 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3352 if (myeof || kideof) { /* pass EOF to parent */
3353 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3354 pipe_infromchild_ast, p,
3357 } else if (eof) { /* eat EOF --- fall through to read*/
3359 } else { /* transmit data */
3360 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3361 pipe_infromchild_ast,p,
3362 p->buf, p->iosb.count, 0, 0, 0, 0));
3368 /* everything shut? flag as done */
3370 if (!p->chan_in && !p->chan_out) {
3371 *p->pipe_done = TRUE;
3372 _ckvmssts_noperl(sys$setef(pipe_ef));
3376 /* write completed (or read, if snarfing from child)
3377 if still have input active,
3378 queue read...immediate mode if shut_on_empty so we get EOF if empty
3380 check if Perl reading, generate EOFs as needed
3386 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3387 pipe_infromchild_ast,p,
3388 p->buf, p->bufsize, 0, 0, 0, 0);
3389 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3390 _ckvmssts_noperl(iss);
3391 } else { /* send EOFs for extra reads */
3392 p->iosb.status = SS$_ENDOFFILE;
3393 p->iosb.dvispec = 0;
3394 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3396 pipe_infromchild_ast, p, 0, 0, 0, 0));
3402 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3406 unsigned long dviitm = DVI$_DEVBUFSIZ;
3408 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3409 DSC$K_CLASS_S, mbx};
3410 int n = sizeof(Pipe);
3412 /* things like terminals and mbx's don't need this filter */
3413 if (fd && fstat(fd,&s) == 0) {
3414 unsigned long devchar;
3416 unsigned short dev_len;
3417 struct dsc$descriptor_s d_dev;
3419 struct item_list_3 items[3];
3421 unsigned short dvi_iosb[4];
3423 cptr = getname(fd, out, 1);
3424 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3425 d_dev.dsc$a_pointer = out;
3426 d_dev.dsc$w_length = strlen(out);
3427 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3428 d_dev.dsc$b_class = DSC$K_CLASS_S;
3431 items[0].code = DVI$_DEVCHAR;
3432 items[0].bufadr = &devchar;
3433 items[0].retadr = NULL;
3435 items[1].code = DVI$_FULLDEVNAM;
3436 items[1].bufadr = device;
3437 items[1].retadr = &dev_len;
3441 status = sys$getdviw
3442 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3443 _ckvmssts_noperl(status);
3444 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3445 device[dev_len] = 0;
3447 if (!(devchar & DEV$M_DIR)) {
3448 strcpy(out, device);
3454 _ckvmssts_noperl(lib$get_vm(&n, &p));
3455 p->fd_out = dup(fd);
3456 create_mbx(&p->chan_in, &d_mbx);
3457 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3458 n = (p->bufsize+1) * sizeof(char);
3459 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3460 p->shut_on_empty = FALSE;
3465 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3466 pipe_mbxtofd_ast, p,
3467 p->buf, p->bufsize, 0, 0, 0, 0));
3473 pipe_mbxtofd_ast(pPipe p)
3475 int iss = p->iosb.status;
3476 int done = p->info->done;
3478 int eof = (iss == SS$_ENDOFFILE);
3479 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3480 int err = !(iss&1) && !eof;
3481 #if defined(PERL_IMPLICIT_CONTEXT)
3485 if (done && myeof) { /* end piping */
3487 sys$dassgn(p->chan_in);
3488 *p->pipe_done = TRUE;
3489 _ckvmssts_noperl(sys$setef(pipe_ef));
3493 if (!err && !eof) { /* good data to send to file */
3494 p->buf[p->iosb.count] = '\n';
3495 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3498 if (p->retry < MAX_RETRY) {
3499 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3505 _ckvmssts_noperl(iss);
3509 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3510 pipe_mbxtofd_ast, p,
3511 p->buf, p->bufsize, 0, 0, 0, 0);
3512 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3513 _ckvmssts_noperl(iss);
3517 typedef struct _pipeloc PLOC;
3518 typedef struct _pipeloc* pPLOC;
3522 char dir[NAM$C_MAXRSS+1];
3524 static pPLOC head_PLOC = 0;
3527 free_pipelocs(pTHX_ void *head)
3530 pPLOC *pHead = (pPLOC *)head;
3542 store_pipelocs(pTHX)
3550 char temp[NAM$C_MAXRSS+1];
3554 free_pipelocs(aTHX_ &head_PLOC);
3556 /* the . directory from @INC comes last */
3558 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3559 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3560 p->next = head_PLOC;
3562 strcpy(p->dir,"./");
3564 /* get the directory from $^X */
3566 unixdir = (char *)PerlMem_malloc(VMS_MAXRSS);
3567 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3569 #ifdef PERL_IMPLICIT_CONTEXT
3570 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3572 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3574 my_strlcpy(temp, PL_origargv[0], sizeof(temp));
3575 x = strrchr(temp,']');
3577 x = strrchr(temp,'>');
3579 /* It could be a UNIX path */
3580 x = strrchr(temp,'/');
3586 /* Got a bare name, so use default directory */
3591 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3592 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3593 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3594 p->next = head_PLOC;
3596 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3600 /* reverse order of @INC entries, skip "." since entered above */
3602 #ifdef PERL_IMPLICIT_CONTEXT
3605 if (PL_incgv) av = GvAVn(PL_incgv);
3607 for (i = 0; av && i <= AvFILL(av); i++) {
3608 dirsv = *av_fetch(av,i,TRUE);
3610 if (SvROK(dirsv)) continue;
3611 dir = SvPVx(dirsv,n_a);
3612 if (strcmp(dir,".") == 0) continue;
3613 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3616 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3617 p->next = head_PLOC;
3619 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3622 /* most likely spot (ARCHLIB) put first in the list */
3625 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3626 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3627 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3628 p->next = head_PLOC;
3630 my_strlcpy(p->dir, unixdir, sizeof(p->dir));
3633 PerlMem_free(unixdir);
3636 static I32 Perl_cando_by_name_int(pTHX_ I32 bit, bool effective,
3637 const char *fname, int opts);
3638 #if !defined(PERL_IMPLICIT_CONTEXT)
3639 #define cando_by_name_int Perl_cando_by_name_int
3641 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3647 static int vmspipe_file_status = 0;
3648 static char vmspipe_file[NAM$C_MAXRSS+1];
3650 /* already found? Check and use ... need read+execute permission */
3652 if (vmspipe_file_status == 1) {
3653 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3654 && cando_by_name_int
3655 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3656 return vmspipe_file;
3658 vmspipe_file_status = 0;
3661 /* scan through stored @INC, $^X */
3663 if (vmspipe_file_status == 0) {
3664 char file[NAM$C_MAXRSS+1];
3665 pPLOC p = head_PLOC;
3670 dirlen = my_strlcpy(file, p->dir, sizeof(file));
3671 my_strlcat(file, "vmspipe.com", sizeof(file));
3674 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3675 if (!exp_res) continue;
3677 if (cando_by_name_int
3678 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3679 && cando_by_name_int
3680 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3681 vmspipe_file_status = 1;
3682 return vmspipe_file;
3685 vmspipe_file_status = -1; /* failed, use tempfiles */
3692 vmspipe_tempfile(pTHX)
3694 char file[NAM$C_MAXRSS+1];
3696 static int index = 0;
3700 /* create a tempfile */
3702 /* we can't go from W, shr=get to R, shr=get without
3703 an intermediate vulnerable state, so don't bother trying...
3705 and lib$spawn doesn't shr=put, so have to close the write
3707 So... match up the creation date/time and the FID to
3708 make sure we're dealing with the same file
3713 if (!decc_filename_unix_only) {
3714 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3715 fp = fopen(file,"w");
3717 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3718 fp = fopen(file,"w");
3720 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3721 fp = fopen(file,"w");
3726 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3727 fp = fopen(file,"w");
3729 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3730 fp = fopen(file,"w");
3732 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3733 fp = fopen(file,"w");
3737 if (!fp) return 0; /* we're hosed */
3739 fprintf(fp,"$! 'f$verify(0)'\n");
3740 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3741 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3742 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3743 fprintf(fp,"$ perl_on = \"set noon\"\n");
3744 fprintf(fp,"$ perl_exit = \"exit\"\n");
3745 fprintf(fp,"$ perl_del = \"delete\"\n");
3746 fprintf(fp,"$ pif = \"if\"\n");
3747 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3748 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3749 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3750 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3751 fprintf(fp,"$! --- build command line to get max possible length\n");
3752 fprintf(fp,"$c=perl_popen_cmd0\n");
3753 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3754 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3755 fprintf(fp,"$x=perl_popen_cmd3\n");
3756 fprintf(fp,"$c=c+x\n");
3757 fprintf(fp,"$ perl_on\n");
3758 fprintf(fp,"$ 'c'\n");
3759 fprintf(fp,"$ perl_status = $STATUS\n");
3760 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3761 fprintf(fp,"$ perl_exit 'perl_status'\n");
3764 fgetname(fp, file, 1);
3765 fstat(fileno(fp), &s0.crtl_stat);
3768 if (decc_filename_unix_only)
3769 int_tounixspec(file, file, NULL);
3770 fp = fopen(file,"r","shr=get");
3772 fstat(fileno(fp), &s1.crtl_stat);
3774 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3775 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3785 vms_is_syscommand_xterm(void)
3787 const static struct dsc$descriptor_s syscommand_dsc =
3788 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3790 const static struct dsc$descriptor_s decwdisplay_dsc =
3791 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3793 struct item_list_3 items[2];
3794 unsigned short dvi_iosb[4];
3795 unsigned long devchar;
3796 unsigned long devclass;
3799 /* Very simple check to guess if sys$command is a decterm? */
3800 /* First see if the DECW$DISPLAY: device exists */
3802 items[0].code = DVI$_DEVCHAR;
3803 items[0].bufadr = &devchar;
3804 items[0].retadr = NULL;
3808 status = sys$getdviw
3809 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3811 if ($VMS_STATUS_SUCCESS(status)) {
3812 status = dvi_iosb[0];
3815 if (!$VMS_STATUS_SUCCESS(status)) {
3816 SETERRNO(EVMSERR, status);
3820 /* If it does, then for now assume that we are on a workstation */
3821 /* Now verify that SYS$COMMAND is a terminal */
3822 /* for creating the debugger DECTerm */
3825 items[0].code = DVI$_DEVCLASS;
3826 items[0].bufadr = &devclass;
3827 items[0].retadr = NULL;
3831 status = sys$getdviw
3832 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3834 if ($VMS_STATUS_SUCCESS(status)) {
3835 status = dvi_iosb[0];
3838 if (!$VMS_STATUS_SUCCESS(status)) {
3839 SETERRNO(EVMSERR, status);
3843 if (devclass == DC$_TERM) {
3850 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3852 create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3857 char device_name[65];
3858 unsigned short device_name_len;
3859 struct dsc$descriptor_s customization_dsc;
3860 struct dsc$descriptor_s device_name_dsc;
3862 char customization[200];
3866 unsigned short p_chan;
3868 unsigned short iosb[4];
3869 const char * cust_str =
3870 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3871 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3872 DSC$K_CLASS_S, mbx1};
3874 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
3875 /*---------------------------------------*/
3876 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
3879 /* Make sure that this is from the Perl debugger */
3880 ret_char = strstr(cmd," xterm ");
3881 if (ret_char == NULL)
3883 cptr = ret_char + 7;
3884 ret_char = strstr(cmd,"tty");
3885 if (ret_char == NULL)
3887 ret_char = strstr(cmd,"sleep");
3888 if (ret_char == NULL)
3891 if (decw_term_port == 0) {
3892 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
3893 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
3894 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
3896 status = lib$find_image_symbol
3898 &decw_term_port_dsc,
3899 (void *)&decw_term_port,
3903 /* Try again with the other image name */
3904 if (!$VMS_STATUS_SUCCESS(status)) {
3906 status = lib$find_image_symbol
3908 &decw_term_port_dsc,
3909 (void *)&decw_term_port,
3918 /* No decw$term_port, give it up */
3919 if (!$VMS_STATUS_SUCCESS(status))
3922 /* Are we on a workstation? */
3923 /* to do: capture the rows / columns and pass their properties */
3924 ret_stat = vms_is_syscommand_xterm();
3928 /* Make the title: */
3929 ret_char = strstr(cptr,"-title");
3930 if (ret_char != NULL) {
3931 while ((*cptr != 0) && (*cptr != '\"')) {
3937 while ((*cptr != 0) && (*cptr != '\"')) {
3950 strcpy(title,"Perl Debug DECTerm");
3952 sprintf(customization, cust_str, title);
3954 customization_dsc.dsc$a_pointer = customization;
3955 customization_dsc.dsc$w_length = strlen(customization);
3956 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3957 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
3959 device_name_dsc.dsc$a_pointer = device_name;
3960 device_name_dsc.dsc$w_length = sizeof device_name -1;
3961 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3962 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
3964 device_name_len = 0;
3966 /* Try to create the window */
3967 status = (*decw_term_port)
3976 if (!$VMS_STATUS_SUCCESS(status)) {
3977 SETERRNO(EVMSERR, status);
3981 device_name[device_name_len] = '\0';
3983 /* Need to set this up to look like a pipe for cleanup */
3985 status = lib$get_vm(&n, &info);
3986 if (!$VMS_STATUS_SUCCESS(status)) {
3987 SETERRNO(ENOMEM, status);
3993 info->completion = 0;
3994 info->closing = FALSE;
4001 info->in_done = TRUE;
4002 info->out_done = TRUE;
4003 info->err_done = TRUE;
4005 /* Assign a channel on this so that it will persist, and not login */
4006 /* We stash this channel in the info structure for reference. */
4007 /* The created xterm self destructs when the last channel is removed */
4008 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4009 /* So leave this assigned. */
4010 device_name_dsc.dsc$w_length = device_name_len;
4011 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4012 if (!$VMS_STATUS_SUCCESS(status)) {
4013 SETERRNO(EVMSERR, status);
4016 info->xchan_valid = 1;
4018 /* Now create a mailbox to be read by the application */
4020 create_mbx(&p_chan, &d_mbx1);
4022 /* write the name of the created terminal to the mailbox */
4023 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4024 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4026 if (!$VMS_STATUS_SUCCESS(status)) {
4027 SETERRNO(EVMSERR, status);
4031 info->fp = PerlIO_open(mbx1, mode);
4033 /* Done with this channel */
4036 /* If any errors, then clean up */
4039 _ckvmssts_noperl(lib$free_vm(&n, &info));
4047 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4050 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4052 static int handler_set_up = FALSE;
4054 unsigned long int sts, flags = CLI$M_NOWAIT;
4055 /* The use of a GLOBAL table (as was done previously) rendered
4056 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4057 * environment. Hence we've switched to LOCAL symbol table.
4059 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4061 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4062 char *in, *out, *err, mbx[512];
4064 char tfilebuf[NAM$C_MAXRSS+1];
4066 char cmd_sym_name[20];
4067 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4068 DSC$K_CLASS_S, symbol};
4069 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4071 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4072 DSC$K_CLASS_S, cmd_sym_name};
4073 struct dsc$descriptor_s *vmscmd;
4074 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4075 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4076 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4078 /* Check here for Xterm create request. This means looking for
4079 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4080 * is possible to create an xterm.
4082 if (*in_mode == 'r') {
4085 #if defined(PERL_IMPLICIT_CONTEXT)
4086 /* Can not fork an xterm with a NULL context */
4087 /* This probably could never happen */
4091 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4092 if (xterm_fd != NULL)
4096 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4098 /* once-per-program initialization...
4099 note that the SETAST calls and the dual test of pipe_ef
4100 makes sure that only the FIRST thread through here does
4101 the initialization...all other threads wait until it's
4104 Yeah, uglier than a pthread call, it's got all the stuff inline
4105 rather than in a separate routine.
4109 _ckvmssts_noperl(sys$setast(0));
4111 unsigned long int pidcode = JPI$_PID;
4112 $DESCRIPTOR(d_delay, RETRY_DELAY);
4113 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4114 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4115 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4117 if (!handler_set_up) {
4118 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4119 handler_set_up = TRUE;
4121 _ckvmssts_noperl(sys$setast(1));
4124 /* see if we can find a VMSPIPE.COM */
4127 vmspipe = find_vmspipe(aTHX);
4129 vmspipedsc.dsc$w_length = my_strlcpy(tfilebuf+1, vmspipe, sizeof(tfilebuf)-1) + 1;
4130 } else { /* uh, oh...we're in tempfile hell */
4131 tpipe = vmspipe_tempfile(aTHX);
4132 if (!tpipe) { /* a fish popular in Boston */
4133 if (ckWARN(WARN_PIPE)) {
4134 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4138 fgetname(tpipe,tfilebuf+1,1);
4139 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4141 vmspipedsc.dsc$a_pointer = tfilebuf;
4143 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4146 case RMS$_FNF: case RMS$_DNF:
4147 set_errno(ENOENT); break;
4149 set_errno(ENOTDIR); break;
4151 set_errno(ENODEV); break;
4153 set_errno(EACCES); break;
4155 set_errno(EINVAL); break;
4156 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4157 set_errno(E2BIG); break;
4158 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4159 _ckvmssts_noperl(sts); /* fall through */
4160 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4163 set_vaxc_errno(sts);
4164 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4165 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4171 _ckvmssts_noperl(lib$get_vm(&n, &info));
4173 my_strlcpy(mode, in_mode, sizeof(mode));
4176 info->completion = 0;
4177 info->closing = FALSE;
4184 info->in_done = TRUE;
4185 info->out_done = TRUE;
4186 info->err_done = TRUE;
4188 info->xchan_valid = 0;
4190 in = (char *)PerlMem_malloc(VMS_MAXRSS);
4191 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4192 out = (char *)PerlMem_malloc(VMS_MAXRSS);
4193 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4194 err = (char *)PerlMem_malloc(VMS_MAXRSS);
4195 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4197 in[0] = out[0] = err[0] = '\0';
4199 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4203 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4208 if (*mode == 'r') { /* piping from subroutine */
4210 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4212 info->out->pipe_done = &info->out_done;
4213 info->out_done = FALSE;
4214 info->out->info = info;
4216 if (!info->useFILE) {
4217 info->fp = PerlIO_open(mbx, mode);
4219 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4220 vmssetuserlnm("SYS$INPUT", mbx);
4223 if (!info->fp && info->out) {
4224 sys$cancel(info->out->chan_out);
4226 while (!info->out_done) {
4228 _ckvmssts_noperl(sys$setast(0));
4229 done = info->out_done;
4230 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4231 _ckvmssts_noperl(sys$setast(1));
4232 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4235 if (info->out->buf) {
4236 n = info->out->bufsize * sizeof(char);
4237 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4240 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4242 _ckvmssts_noperl(lib$free_vm(&n, &info));
4247 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4249 info->err->pipe_done = &info->err_done;
4250 info->err_done = FALSE;
4251 info->err->info = info;
4254 } else if (*mode == 'w') { /* piping to subroutine */
4256 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4258 info->out->pipe_done = &info->out_done;
4259 info->out_done = FALSE;
4260 info->out->info = info;
4263 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4265 info->err->pipe_done = &info->err_done;
4266 info->err_done = FALSE;
4267 info->err->info = info;
4270 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4271 if (!info->useFILE) {
4272 info->fp = PerlIO_open(mbx, mode);
4274 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4275 vmssetuserlnm("SYS$OUTPUT", mbx);
4279 info->in->pipe_done = &info->in_done;
4280 info->in_done = FALSE;
4281 info->in->info = info;
4285 if (!info->fp && info->in) {
4287 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4288 0, 0, 0, 0, 0, 0, 0, 0));
4290 while (!info->in_done) {
4292 _ckvmssts_noperl(sys$setast(0));
4293 done = info->in_done;
4294 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4295 _ckvmssts_noperl(sys$setast(1));
4296 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4299 if (info->in->buf) {
4300 n = info->in->bufsize * sizeof(char);
4301 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4304 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4306 _ckvmssts_noperl(lib$free_vm(&n, &info));
4312 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4313 /* Let the child inherit standard input, unless it's a directory. */
4315 if (my_trnlnm("SYS$INPUT", in, 0)) {
4316 if (flex_stat(in, &st) != 0 || S_ISDIR(st.st_mode))
4320 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4322 info->out->pipe_done = &info->out_done;
4323 info->out_done = FALSE;
4324 info->out->info = info;
4327 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4329 info->err->pipe_done = &info->err_done;
4330 info->err_done = FALSE;
4331 info->err->info = info;
4335 d_symbol.dsc$w_length = my_strlcpy(symbol, in, sizeof(symbol));
4336 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4338 d_symbol.dsc$w_length = my_strlcpy(symbol, err, sizeof(symbol));
4339 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4341 d_symbol.dsc$w_length = my_strlcpy(symbol, out, sizeof(symbol));
4342 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4344 /* Done with the names for the pipes */
4349 p = vmscmd->dsc$a_pointer;
4350 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4351 if (*p == '$') p++; /* remove leading $ */
4352 while (*p == ' ' || *p == '\t') p++;
4354 for (j = 0; j < 4; j++) {
4355 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4356 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4358 d_symbol.dsc$w_length = my_strlcpy(symbol, p, sizeof(symbol));
4359 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4361 if (strlen(p) > MAX_DCL_SYMBOL) {
4362 p += MAX_DCL_SYMBOL;
4367 _ckvmssts_noperl(sys$setast(0));
4368 info->next=open_pipes; /* prepend to list */
4370 _ckvmssts_noperl(sys$setast(1));
4371 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4372 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4373 * have SYS$COMMAND if we need it.
4375 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4376 0, &info->pid, &info->completion,
4377 0, popen_completion_ast,info,0,0,0));
4379 /* if we were using a tempfile, close it now */
4381 if (tpipe) fclose(tpipe);
4383 /* once the subprocess is spawned, it has copied the symbols and
4384 we can get rid of ours */
4386 for (j = 0; j < 4; j++) {
4387 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4388 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4389 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4391 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4392 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4393 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4394 vms_execfree(vmscmd);
4396 #ifdef PERL_IMPLICIT_CONTEXT
4399 PL_forkprocess = info->pid;
4406 _ckvmssts_noperl(sys$setast(0));
4408 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4409 _ckvmssts_noperl(sys$setast(1));
4410 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4412 *psts = info->completion;
4413 /* Caller thinks it is open and tries to close it. */
4414 /* This causes some problems, as it changes the error status */
4415 /* my_pclose(info->fp); */
4417 /* If we did not have a file pointer open, then we have to */
4418 /* clean up here or eventually we will run out of something */
4420 if (info->fp == NULL) {
4421 my_pclose_pinfo(aTHX_ info);
4429 } /* end of safe_popen */
4432 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4434 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4438 TAINT_PROPER("popen");
4439 PERL_FLUSHALL_FOR_CHILD;
4440 return safe_popen(aTHX_ cmd,mode,&sts);
4446 /* Routine to close and cleanup a pipe info structure */
4449 my_pclose_pinfo(pTHX_ pInfo info) {
4451 unsigned long int retsts;
4455 /* If we were writing to a subprocess, insure that someone reading from
4456 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4457 * produce an EOF record in the mailbox.
4459 * well, at least sometimes it *does*, so we have to watch out for
4460 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4464 #if defined(USE_ITHREADS)
4468 && PL_perlio_fd_refcnt
4471 PerlIO_flush(info->fp);
4473 fflush((FILE *)info->fp);
4476 _ckvmssts(sys$setast(0));
4477 info->closing = TRUE;
4478 done = info->done && info->in_done && info->out_done && info->err_done;
4479 /* hanging on write to Perl's input? cancel it */
4480 if (info->mode == 'r' && info->out && !info->out_done) {
4481 if (info->out->chan_out) {
4482 _ckvmssts(sys$cancel(info->out->chan_out));
4483 if (!info->out->chan_in) { /* EOF generation, need AST */
4484 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4488 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4489 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4491 _ckvmssts(sys$setast(1));
4494 #if defined(USE_ITHREADS)
4498 && PL_perlio_fd_refcnt
4501 PerlIO_close(info->fp);
4503 fclose((FILE *)info->fp);
4506 we have to wait until subprocess completes, but ALSO wait until all
4507 the i/o completes...otherwise we'll be freeing the "info" structure
4508 that the i/o ASTs could still be using...
4512 _ckvmssts(sys$setast(0));
4513 done = info->done && info->in_done && info->out_done && info->err_done;
4514 if (!done) _ckvmssts(sys$clref(pipe_ef));
4515 _ckvmssts(sys$setast(1));
4516 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4518 retsts = info->completion;
4520 /* remove from list of open pipes */
4521 _ckvmssts(sys$setast(0));
4523 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4529 last->next = info->next;
4531 open_pipes = info->next;
4532 _ckvmssts(sys$setast(1));
4534 /* free buffers and structures */
4537 if (info->in->buf) {
4538 n = info->in->bufsize * sizeof(char);
4539 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4542 _ckvmssts(lib$free_vm(&n, &info->in));
4545 if (info->out->buf) {
4546 n = info->out->bufsize * sizeof(char);
4547 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4550 _ckvmssts(lib$free_vm(&n, &info->out));
4553 if (info->err->buf) {
4554 n = info->err->bufsize * sizeof(char);
4555 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4558 _ckvmssts(lib$free_vm(&n, &info->err));
4561 _ckvmssts(lib$free_vm(&n, &info));
4567 /*{{{ I32 my_pclose(PerlIO *fp)*/
4568 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4570 pInfo info, last = NULL;
4573 /* Fixme - need ast and mutex protection here */
4574 for (info = open_pipes; info != NULL; last = info, info = info->next)
4575 if (info->fp == fp) break;
4577 if (info == NULL) { /* no such pipe open */
4578 set_errno(ECHILD); /* quoth POSIX */
4579 set_vaxc_errno(SS$_NONEXPR);
4583 ret_status = my_pclose_pinfo(aTHX_ info);
4587 } /* end of my_pclose() */
4589 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4590 /* Roll our own prototype because we want this regardless of whether
4591 * _VMS_WAIT is defined.
4597 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4603 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4604 created with popen(); otherwise partially emulate waitpid() unless
4605 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4606 Also check processes not considered by the CRTL waitpid().
4608 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4610 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4617 if (statusp) *statusp = 0;
4619 for (info = open_pipes; info != NULL; info = info->next)
4620 if (info->pid == pid) break;
4622 if (info != NULL) { /* we know about this child */
4623 while (!info->done) {
4624 _ckvmssts(sys$setast(0));
4626 if (!done) _ckvmssts(sys$clref(pipe_ef));
4627 _ckvmssts(sys$setast(1));
4628 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4631 if (statusp) *statusp = info->completion;
4635 /* child that already terminated? */
4637 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4638 if (closed_list[j].pid == pid) {
4639 if (statusp) *statusp = closed_list[j].completion;
4644 /* fall through if this child is not one of our own pipe children */
4646 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4648 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4649 * in 7.2 did we get a version that fills in the VMS completion
4650 * status as Perl has always tried to do.
4653 sts = __vms_waitpid( pid, statusp, flags );
4655 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4658 /* If the real waitpid tells us the child does not exist, we
4659 * fall through here to implement waiting for a child that
4660 * was created by some means other than exec() (say, spawned
4661 * from DCL) or to wait for a process that is not a subprocess
4662 * of the current process.
4665 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4668 $DESCRIPTOR(intdsc,"0 00:00:01");
4669 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4670 unsigned long int pidcode = JPI$_PID, mypid;
4671 unsigned long int interval[2];
4672 unsigned int jpi_iosb[2];
4673 struct itmlst_3 jpilist[2] = {
4674 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4679 /* Sorry folks, we don't presently implement rooting around for
4680 the first child we can find, and we definitely don't want to
4681 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4687 /* Get the owner of the child so I can warn if it's not mine. If the
4688 * process doesn't exist or I don't have the privs to look at it,
4689 * I can go home early.
4691 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4692 if (sts & 1) sts = jpi_iosb[0];
4704 set_vaxc_errno(sts);
4708 if (ckWARN(WARN_EXEC)) {
4709 /* remind folks they are asking for non-standard waitpid behavior */
4710 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4711 if (ownerpid != mypid)
4712 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4713 "waitpid: process %x is not a child of process %x",
4717 /* simply check on it once a second until it's not there anymore. */
4719 _ckvmssts(sys$bintim(&intdsc,interval));
4720 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4721 _ckvmssts(sys$schdwk(0,0,interval,0));
4722 _ckvmssts(sys$hiber());
4724 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4729 } /* end of waitpid() */
4734 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4736 my_gconvert(double val, int ndig, int trail, char *buf)
4738 static char __gcvtbuf[DBL_DIG+1];
4741 loc = buf ? buf : __gcvtbuf;
4744 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4745 return gcvt(val,ndig,loc);
4748 loc[0] = '0'; loc[1] = '\0';
4755 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4757 rms_free_search_context(struct FAB * fab)
4761 nam = fab->fab$l_nam;
4762 nam->nam$b_nop |= NAM$M_SYNCHK;
4763 nam->nam$l_rlf = NULL;
4765 return sys$parse(fab, NULL, NULL);
4768 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4769 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4770 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4771 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4772 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4773 #define rms_nam_esll(nam) nam.nam$b_esl
4774 #define rms_nam_esl(nam) nam.nam$b_esl
4775 #define rms_nam_name(nam) nam.nam$l_name
4776 #define rms_nam_namel(nam) nam.nam$l_name
4777 #define rms_nam_type(nam) nam.nam$l_type
4778 #define rms_nam_typel(nam) nam.nam$l_type
4779 #define rms_nam_ver(nam) nam.nam$l_ver
4780 #define rms_nam_verl(nam) nam.nam$l_ver
4781 #define rms_nam_rsll(nam) nam.nam$b_rsl
4782 #define rms_nam_rsl(nam) nam.nam$b_rsl
4783 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4784 #define rms_set_fna(fab, nam, name, size) \
4785 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4786 #define rms_get_fna(fab, nam) fab.fab$l_fna
4787 #define rms_set_dna(fab, nam, name, size) \
4788 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4789 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4790 #define rms_set_esa(nam, name, size) \
4791 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4792 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4793 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4794 #define rms_set_rsa(nam, name, size) \
4795 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4796 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4797 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4798 #define rms_nam_name_type_l_size(nam) \
4799 (nam.nam$b_name + nam.nam$b_type)
4802 rms_free_search_context(struct FAB * fab)
4806 nam = fab->fab$l_naml;
4807 nam->naml$b_nop |= NAM$M_SYNCHK;
4808 nam->naml$l_rlf = NULL;
4809 nam->naml$l_long_defname_size = 0;
4812 return sys$parse(fab, NULL, NULL);
4815 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4816 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4817 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4818 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4819 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4820 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4821 #define rms_nam_esl(nam) nam.naml$b_esl
4822 #define rms_nam_name(nam) nam.naml$l_name
4823 #define rms_nam_namel(nam) nam.naml$l_long_name
4824 #define rms_nam_type(nam) nam.naml$l_type
4825 #define rms_nam_typel(nam) nam.naml$l_long_type
4826 #define rms_nam_ver(nam) nam.naml$l_ver
4827 #define rms_nam_verl(nam) nam.naml$l_long_ver
4828 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4829 #define rms_nam_rsl(nam) nam.naml$b_rsl
4830 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4831 #define rms_set_fna(fab, nam, name, size) \
4832 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4833 nam.naml$l_long_filename_size = size; \
4834 nam.naml$l_long_filename = name;}
4835 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4836 #define rms_set_dna(fab, nam, name, size) \
4837 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4838 nam.naml$l_long_defname_size = size; \
4839 nam.naml$l_long_defname = name; }
4840 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4841 #define rms_set_esa(nam, name, size) \
4842 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4843 nam.naml$l_long_expand_alloc = size; \
4844 nam.naml$l_long_expand = name; }
4845 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4846 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4847 nam.naml$l_long_expand = l_name; \
4848 nam.naml$l_long_expand_alloc = l_size; }
4849 #define rms_set_rsa(nam, name, size) \
4850 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4851 nam.naml$l_long_result = name; \
4852 nam.naml$l_long_result_alloc = size; }
4853 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4854 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4855 nam.naml$l_long_result = l_name; \
4856 nam.naml$l_long_result_alloc = l_size; }
4857 #define rms_nam_name_type_l_size(nam) \
4858 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4863 * The CRTL for 8.3 and later can create symbolic links in any mode,
4864 * however in 8.3 the unlink/remove/delete routines will only properly handle
4865 * them if one of the PCP modes is active.
4868 rms_erase(const char * vmsname)
4871 struct FAB myfab = cc$rms_fab;
4872 rms_setup_nam(mynam);
4874 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4875 rms_bind_fab_nam(myfab, mynam);
4877 #ifdef NAML$M_OPEN_SPECIAL
4878 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
4881 status = sys$erase(&myfab, 0, 0);
4888 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
4889 const struct dsc$descriptor_s * vms_dst_dsc,
4890 unsigned long flags)
4892 /* VMS and UNIX handle file permissions differently and the
4893 * the same ACL trick may be needed for renaming files,
4894 * especially if they are directories.
4897 /* todo: get kill_file and rename to share common code */
4898 /* I can not find online documentation for $change_acl
4899 * it appears to be replaced by $set_security some time ago */
4901 const unsigned int access_mode = 0;
4902 $DESCRIPTOR(obj_file_dsc,"FILE");
4905 unsigned long int jpicode = JPI$_UIC;
4906 int aclsts, fndsts, rnsts = -1;
4907 unsigned int ctx = 0;
4908 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4909 struct dsc$descriptor_s * clean_dsc;
4912 unsigned char myace$b_length;
4913 unsigned char myace$b_type;
4914 unsigned short int myace$w_flags;
4915 unsigned long int myace$l_access;
4916 unsigned long int myace$l_ident;
4917 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
4918 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
4920 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
4923 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
4924 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
4926 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
4927 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
4931 /* Expand the input spec using RMS, since we do not want to put
4932 * ACLs on the target of a symbolic link */
4933 vmsname = (char *)PerlMem_malloc(NAM$C_MAXRSS+1);
4934 if (vmsname == NULL)
4937 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
4939 PERL_RMSEXPAND_M_SYMLINK);
4941 PerlMem_free(vmsname);
4945 /* So we get our own UIC to use as a rights identifier,
4946 * and the insert an ACE at the head of the ACL which allows us
4947 * to delete the file.
4949 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
4951 fildsc.dsc$w_length = strlen(vmsname);
4952 fildsc.dsc$a_pointer = vmsname;
4954 newace.myace$l_ident = oldace.myace$l_ident;
4957 /* Grab any existing ACEs with this identifier in case we fail */
4958 clean_dsc = &fildsc;
4959 aclsts = fndsts = sys$get_security(&obj_file_dsc,
4967 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
4968 /* Add the new ACE . . . */
4970 /* if the sys$get_security succeeded, then ctx is valid, and the
4971 * object/file descriptors will be ignored. But otherwise they
4974 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
4975 OSS$M_RELCTX, addlst, &ctx, &access_mode);
4976 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
4978 set_vaxc_errno(aclsts);
4979 PerlMem_free(vmsname);
4983 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
4986 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
4988 if ($VMS_STATUS_SUCCESS(rnsts)) {
4989 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
4992 /* Put things back the way they were. */
4994 aclsts = sys$get_security(&obj_file_dsc,
5002 if ($VMS_STATUS_SUCCESS(aclsts)) {
5006 if (!$VMS_STATUS_SUCCESS(fndsts))
5007 sec_flags = OSS$M_RELCTX;
5009 /* Get rid of the new ACE */
5010 aclsts = sys$set_security(NULL, NULL, NULL,
5011 sec_flags, dellst, &ctx, &access_mode);
5013 /* If there was an old ACE, put it back */
5014 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5015 addlst[0].bufadr = &oldace;
5016 aclsts = sys$set_security(NULL, NULL, NULL,
5017 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5018 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5020 set_vaxc_errno(aclsts);
5026 /* Try to clear the lock on the ACL list */
5027 aclsts2 = sys$set_security(NULL, NULL, NULL,
5028 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5030 /* Rename errors are most important */
5031 if (!$VMS_STATUS_SUCCESS(rnsts))
5034 set_vaxc_errno(aclsts);
5039 if (aclsts != SS$_ACLEMPTY)
5046 PerlMem_free(vmsname);
5051 /*{{{int rename(const char *, const char * */
5052 /* Not exactly what X/Open says to do, but doing it absolutely right
5053 * and efficiently would require a lot more work. This should be close
5054 * enough to pass all but the most strict X/Open compliance test.
5057 Perl_rename(pTHX_ const char *src, const char * dst)
5066 /* Validate the source file */
5067 src_sts = flex_lstat(src, &src_st);
5070 /* No source file or other problem */
5073 if (src_st.st_devnam[0] == 0) {
5074 /* This may be possible so fail if it is seen. */
5079 dst_sts = flex_lstat(dst, &dst_st);
5082 if (dst_st.st_dev != src_st.st_dev) {
5083 /* Must be on the same device */
5088 /* VMS_INO_T_COMPARE is true if the inodes are different
5089 * to match the output of memcmp
5092 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5093 /* That was easy, the files are the same! */
5097 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5098 /* If source is a directory, so must be dest */
5106 if ((dst_sts == 0) &&
5107 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5109 /* We have issues here if vms_unlink_all_versions is set
5110 * If the destination exists, and is not a directory, then
5111 * we must delete in advance.
5113 * If the src is a directory, then we must always pre-delete
5116 * If we successfully delete the dst in advance, and the rename fails
5117 * X/Open requires that errno be EIO.
5121 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5123 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
5124 S_ISDIR(dst_st.st_mode));
5126 /* Need to delete all versions ? */
5127 if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5130 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
5131 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
5136 /* Make sure that we do not loop forever */
5148 /* We killed the destination, so only errno now is EIO */
5153 /* Originally the idea was to call the CRTL rename() and only
5154 * try the lib$rename_file if it failed.
5155 * It turns out that there are too many variants in what the
5156 * the CRTL rename might do, so only use lib$rename_file
5161 /* Is the source and dest both in VMS format */
5162 /* if the source is a directory, then need to fileify */
5163 /* and dest must be a directory or non-existent. */
5168 unsigned long flags;
5169 struct dsc$descriptor_s old_file_dsc;
5170 struct dsc$descriptor_s new_file_dsc;
5172 /* We need to modify the src and dst depending
5173 * on if one or more of them are directories.
5176 vms_dst = (char *)PerlMem_malloc(VMS_MAXRSS);
5177 if (vms_dst == NULL)
5178 _ckvmssts_noperl(SS$_INSFMEM);
5180 if (S_ISDIR(src_st.st_mode)) {
5182 char * vms_dir_file;
5184 vms_dir_file = (char *)PerlMem_malloc(VMS_MAXRSS);
5185 if (vms_dir_file == NULL)
5186 _ckvmssts_noperl(SS$_INSFMEM);
5188 /* If the dest is a directory, we must remove it */
5191 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
5193 PerlMem_free(vms_dst);
5201 /* The dest must be a VMS file specification */
5202 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5203 if (ret_str == NULL) {
5204 PerlMem_free(vms_dst);
5209 /* The source must be a file specification */
5210 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5211 if (ret_str == NULL) {
5212 PerlMem_free(vms_dst);
5213 PerlMem_free(vms_dir_file);
5217 PerlMem_free(vms_dst);
5218 vms_dst = vms_dir_file;
5221 /* File to file or file to new dir */
5223 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5224 /* VMS pathify a dir target */
5225 ret_str = int_tovmspath(dst, vms_dst, NULL);
5226 if (ret_str == NULL) {
5227 PerlMem_free(vms_dst);
5232 char * v_spec, * r_spec, * d_spec, * n_spec;
5233 char * e_spec, * vs_spec;
5234 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5236 /* fileify a target VMS file specification */
5237 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5238 if (ret_str == NULL) {
5239 PerlMem_free(vms_dst);
5244 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5245 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5246 &e_len, &vs_spec, &vs_len);
5249 /* Get rid of the version */
5253 /* Need to specify a '.' so that the extension */
5254 /* is not inherited */
5255 strcat(vms_dst,".");
5261 old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5262 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
5263 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5264 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5266 new_file_dsc.dsc$a_pointer = vms_dst;
5267 new_file_dsc.dsc$w_length = strlen(vms_dst);
5268 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5269 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5272 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5273 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5276 sts = lib$rename_file(&old_file_dsc,
5280 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5281 if (!$VMS_STATUS_SUCCESS(sts)) {
5283 /* We could have failed because VMS style permissions do not
5284 * permit renames that UNIX will allow. Just like the hack
5287 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5290 PerlMem_free(vms_dst);
5291 if (!$VMS_STATUS_SUCCESS(sts)) {
5298 if (vms_unlink_all_versions) {
5299 /* Now get rid of any previous versions of the source file that
5305 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5306 S_ISDIR(src_st.st_mode));
5307 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
5308 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5309 S_ISDIR(src_st.st_mode));
5314 /* Make sure that we do not loop forever */
5323 /* We deleted the destination, so must force the error to be EIO */
5324 if ((retval != 0) && (pre_delete != 0))
5332 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5333 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5334 * to expand file specification. Allows for a single default file
5335 * specification and a simple mask of options. If outbuf is non-NULL,
5336 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5337 * the resultant file specification is placed. If outbuf is NULL, the
5338 * resultant file specification is placed into a static buffer.
5339 * The third argument, if non-NULL, is taken to be a default file
5340 * specification string. The fourth argument is unused at present.
5341 * rmesexpand() returns the address of the resultant string if
5342 * successful, and NULL on error.
5344 * New functionality for previously unused opts value:
5345 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5346 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5347 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5348 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5350 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5354 (const char *filespec,
5356 const char *defspec,
5362 const char * in_spec;
5364 const char * def_spec;
5365 char * vmsfspec, *vmsdefspec;
5369 struct FAB myfab = cc$rms_fab;
5370 rms_setup_nam(mynam);
5372 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5375 /* temp hack until UTF8 is actually implemented */
5376 if (fs_utf8 != NULL)
5379 if (!filespec || !*filespec) {
5380 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5390 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5391 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5392 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5394 /* If this is a UNIX file spec, convert it to VMS */
5395 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5396 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5397 &e_len, &vs_spec, &vs_len);
5402 vmsfspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5403 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5404 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5405 if (ret_spec == NULL) {
5406 PerlMem_free(vmsfspec);
5409 in_spec = (const char *)vmsfspec;
5411 /* Unless we are forcing to VMS format, a UNIX input means
5412 * UNIX output, and that requires long names to be used
5414 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5415 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5416 opts |= PERL_RMSEXPAND_M_LONG;
5426 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5427 rms_bind_fab_nam(myfab, mynam);
5429 /* Process the default file specification if present */
5431 if (defspec && *defspec) {
5433 t_isunix = is_unix_filespec(defspec);
5435 vmsdefspec = (char *)PerlMem_malloc(VMS_MAXRSS);
5436 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5437 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5439 if (ret_spec == NULL) {
5440 /* Clean up and bail */
5441 PerlMem_free(vmsdefspec);
5442 if (vmsfspec != NULL)
5443 PerlMem_free(vmsfspec);
5446 def_spec = (const char *)vmsdefspec;
5448 rms_set_dna(myfab, mynam,
5449 (char *)def_spec, strlen(def_spec)); /* cast ok */
5452 /* Now we need the expansion buffers */
5453 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
5454 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5455 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5456 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
5457 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5459 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5461 /* If a NAML block is used RMS always writes to the long and short
5462 * addresses unless you suppress the short name.
5464 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5465 outbufl = (char *)PerlMem_malloc(VMS_MAXRSS);
5466 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5468 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5470 #ifdef NAM$M_NO_SHORT_UPCASE
5471 if (decc_efs_case_preserve)
5472 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5475 /* We may not want to follow symbolic links */
5476 #ifdef NAML$M_OPEN_SPECIAL
5477 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5478 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5481 /* First attempt to parse as an existing file */
5482 retsts = sys$parse(&myfab,0,0);
5483 if (!(retsts & STS$K_SUCCESS)) {
5485 /* Could not find the file, try as syntax only if error is not fatal */
5486 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5487 if (retsts == RMS$_DNF ||
5488 retsts == RMS$_DIR ||
5489 retsts == RMS$_DEV ||
5490 retsts == RMS$_PRV) {
5491 retsts = sys$parse(&myfab,0,0);
5492 if (retsts & STS$K_SUCCESS) goto int_expanded;
5495 /* Still could not parse the file specification */
5496 /*----------------------------------------------*/
5497 sts = rms_free_search_context(&myfab); /* Free search context */
5498 if (vmsdefspec != NULL)
5499 PerlMem_free(vmsdefspec);
5500 if (vmsfspec != NULL)
5501 PerlMem_free(vmsfspec);
5502 if (outbufl != NULL)
5503 PerlMem_free(outbufl);
5507 set_vaxc_errno(retsts);
5508 if (retsts == RMS$_PRV) set_errno(EACCES);
5509 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5510 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5511 else set_errno(EVMSERR);
5514 retsts = sys$search(&myfab,0,0);
5515 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5516 sts = rms_free_search_context(&myfab); /* Free search context */
5517 if (vmsdefspec != NULL)
5518 PerlMem_free(vmsdefspec);
5519 if (vmsfspec != NULL)
5520 PerlMem_free(vmsfspec);
5521 if (outbufl != NULL)
5522 PerlMem_free(outbufl);
5526 set_vaxc_errno(retsts);
5527 if (retsts == RMS$_PRV) set_errno(EACCES);
5528 else set_errno(EVMSERR);
5532 /* If the input filespec contained any lowercase characters,
5533 * downcase the result for compatibility with Unix-minded code. */
5535 if (!decc_efs_case_preserve) {
5537 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5538 if (islower(*tbuf)) { haslower = 1; break; }
5541 /* Is a long or a short name expected */
5542 /*------------------------------------*/
5544 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5545 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5546 if (rms_nam_rsll(mynam)) {
5548 speclen = rms_nam_rsll(mynam);
5551 spec_buf = esal; /* Not esa */
5552 speclen = rms_nam_esll(mynam);
5557 if (rms_nam_rsl(mynam)) {
5559 speclen = rms_nam_rsl(mynam);
5562 spec_buf = esa; /* Not esal */
5563 speclen = rms_nam_esl(mynam);
5565 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5568 spec_buf[speclen] = '\0';
5570 /* Trim off null fields added by $PARSE
5571 * If type > 1 char, must have been specified in original or default spec
5572 * (not true for version; $SEARCH may have added version of existing file).
5574 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5575 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5576 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5577 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5580 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5581 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5583 if (trimver || trimtype) {
5584 if (defspec && *defspec) {
5585 char *defesal = NULL;
5586 char *defesa = NULL;
5587 defesa = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5588 if (defesa != NULL) {
5589 struct FAB deffab = cc$rms_fab;
5590 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5591 defesal = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5592 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5594 rms_setup_nam(defnam);
5596 rms_bind_fab_nam(deffab, defnam);
5600 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5602 /* RMS needs the esa/esal as a work area if wildcards are involved */
5603 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5605 rms_clear_nam_nop(defnam);
5606 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5607 #ifdef NAM$M_NO_SHORT_UPCASE
5608 if (decc_efs_case_preserve)
5609 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5611 #ifdef NAML$M_OPEN_SPECIAL
5612 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5613 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5615 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5617 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5620 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5623 if (defesal != NULL)
5624 PerlMem_free(defesal);
5625 PerlMem_free(defesa);
5627 _ckvmssts_noperl(SS$_INSFMEM);
5631 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5632 if (*(rms_nam_verl(mynam)) != '\"')
5633 speclen = rms_nam_verl(mynam) - spec_buf;
5636 if (*(rms_nam_ver(mynam)) != '\"')
5637 speclen = rms_nam_ver(mynam) - spec_buf;
5641 /* If we didn't already trim version, copy down */
5642 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5643 if (speclen > rms_nam_verl(mynam) - spec_buf)
5645 (rms_nam_typel(mynam),
5646 rms_nam_verl(mynam),
5647 speclen - (rms_nam_verl(mynam) - spec_buf));
5648 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5651 if (speclen > rms_nam_ver(mynam) - spec_buf)
5653 (rms_nam_type(mynam),
5655 speclen - (rms_nam_ver(mynam) - spec_buf));
5656 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5661 /* Done with these copies of the input files */
5662 /*-------------------------------------------*/
5663 if (vmsfspec != NULL)
5664 PerlMem_free(vmsfspec);
5665 if (vmsdefspec != NULL)
5666 PerlMem_free(vmsdefspec);
5668 /* If we just had a directory spec on input, $PARSE "helpfully"
5669 * adds an empty name and type for us */
5670 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5671 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5672 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5673 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5674 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5675 speclen = rms_nam_namel(mynam) - spec_buf;
5680 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5681 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5682 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5683 speclen = rms_nam_name(mynam) - spec_buf;
5686 /* Posix format specifications must have matching quotes */
5687 if (speclen < (VMS_MAXRSS - 1)) {
5688 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5689 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5690 spec_buf[speclen] = '\"';
5695 spec_buf[speclen] = '\0';
5696 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5698 /* Have we been working with an expanded, but not resultant, spec? */
5699 /* Also, convert back to Unix syntax if necessary. */
5703 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5704 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5705 rsl = rms_nam_rsll(mynam);
5709 rsl = rms_nam_rsl(mynam);
5712 /* rsl is not present, it means that spec_buf is either */
5713 /* esa or esal, and needs to be copied to outbuf */
5714 /* convert to Unix if desired */
5716 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5718 /* VMS file specs are not in UTF-8 */
5719 if (fs_utf8 != NULL)
5721 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5726 /* Now spec_buf is either outbuf or outbufl */
5727 /* We need the result into outbuf */
5729 /* If we need this in UNIX, then we need another buffer */
5730 /* to keep things in order */
5732 char * new_src = NULL;
5733 if (spec_buf == outbuf) {
5734 new_src = (char *)PerlMem_malloc(VMS_MAXRSS);
5735 my_strlcpy(new_src, spec_buf, VMS_MAXRSS);
5739 ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5741 PerlMem_free(new_src);
5744 /* VMS file specs are not in UTF-8 */
5745 if (fs_utf8 != NULL)
5748 /* Copy the buffer if needed */
5749 if (outbuf != spec_buf)
5750 my_strlcpy(outbuf, spec_buf, VMS_MAXRSS);
5756 /* Need to clean up the search context */
5757 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5758 sts = rms_free_search_context(&myfab); /* Free search context */
5760 /* Clean up the extra buffers */
5764 if (outbufl != NULL)
5765 PerlMem_free(outbufl);
5767 /* Return the result */
5771 /* Common simple case - Expand an already VMS spec */
5773 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5774 opts |= PERL_RMSEXPAND_M_VMS_IN;
5775 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5778 /* Common simple case - Expand to a VMS spec */
5780 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5781 opts |= PERL_RMSEXPAND_M_VMS;
5782 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5786 /* Entry point used by perl routines */
5789 (pTHX_ const char *filespec,
5792 const char *defspec,
5797 static char __rmsexpand_retbuf[VMS_MAXRSS];
5798 char * expanded, *ret_spec, *ret_buf;
5802 if (ret_buf == NULL) {
5804 Newx(expanded, VMS_MAXRSS, char);
5805 if (expanded == NULL)
5806 _ckvmssts(SS$_INSFMEM);
5809 ret_buf = __rmsexpand_retbuf;
5814 ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5815 opts, fs_utf8, dfs_utf8);
5817 if (ret_spec == NULL) {
5818 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5826 /* External entry points */
5828 Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5830 return do_rmsexpand(spec, buf, 0, def, opt, NULL, NULL);
5834 Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5836 return do_rmsexpand(spec, buf, 1, def, opt, NULL, NULL);
5840 Perl_rmsexpand_utf8(pTHX_ const char *spec, char *buf, const char *def,
5841 unsigned opt, int * fs_utf8, int * dfs_utf8)
5843 return do_rmsexpand(spec, buf, 0, def, opt, fs_utf8, dfs_utf8);
5847 Perl_rmsexpand_utf8_ts(pTHX_ const char *spec, char *buf, const char *def,
5848 unsigned opt, int * fs_utf8, int * dfs_utf8)
5850 return do_rmsexpand(spec, buf, 1, def, opt, fs_utf8, dfs_utf8);
5855 ** The following routines are provided to make life easier when
5856 ** converting among VMS-style and Unix-style directory specifications.
5857 ** All will take input specifications in either VMS or Unix syntax. On
5858 ** failure, all return NULL. If successful, the routines listed below
5859 ** return a pointer to a buffer containing the appropriately
5860 ** reformatted spec (and, therefore, subsequent calls to that routine
5861 ** will clobber the result), while the routines of the same names with
5862 ** a _ts suffix appended will return a pointer to a mallocd string
5863 ** containing the appropriately reformatted spec.
5864 ** In all cases, only explicit syntax is altered; no check is made that
5865 ** the resulting string is valid or that the directory in question
5868 ** fileify_dirspec() - convert a directory spec into the name of the
5869 ** directory file (i.e. what you can stat() to see if it's a dir).
5870 ** The style (VMS or Unix) of the result is the same as the style
5871 ** of the parameter passed in.
5872 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5873 ** what you prepend to a filename to indicate what directory it's in).
5874 ** The style (VMS or Unix) of the result is the same as the style
5875 ** of the parameter passed in.
5876 ** tounixpath() - convert a directory spec into a Unix-style path.
5877 ** tovmspath() - convert a directory spec into a VMS-style path.
5878 ** tounixspec() - convert any file spec into a Unix-style file spec.
5879 ** tovmsspec() - convert any file spec into a VMS-style spec.
5880 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5882 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
5883 ** Permission is given to distribute this code as part of the Perl
5884 ** standard distribution under the terms of the GNU General Public
5885 ** License or the Perl Artistic License. Copies of each may be
5886 ** found in the Perl standard distribution.
5889 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5891 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
5893 unsigned long int dirlen, retlen, hasfilename = 0;
5894 char *cp1, *cp2, *lastdir;
5895 char *trndir, *vmsdir;
5896 unsigned short int trnlnm_iter_count;
5898 if (utf8_fl != NULL)
5901 if (!dir || !*dir) {
5902 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5904 dirlen = strlen(dir);
5905 while (dirlen && dir[dirlen-1] == '/') --dirlen;
5906 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
5907 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
5914 if (dirlen > (VMS_MAXRSS - 1)) {
5915 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
5918 trndir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5919 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5920 if (!strpbrk(dir+1,"/]>:") &&
5921 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
5922 strcpy(trndir,*dir == '/' ? dir + 1: dir);
5923 trnlnm_iter_count = 0;
5924 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
5925 trnlnm_iter_count++;
5926 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5928 dirlen = strlen(trndir);
5931 memcpy(trndir, dir, dirlen);
5932 trndir[dirlen] = '\0';
5935 /* At this point we are done with *dir and use *trndir which is a
5936 * copy that can be modified. *dir must not be modified.
5939 /* If we were handed a rooted logical name or spec, treat it like a
5940 * simple directory, so that
5941 * $ Define myroot dev:[dir.]
5942 * ... do_fileify_dirspec("myroot",buf,1) ...
5943 * does something useful.
5945 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
5946 trndir[--dirlen] = '\0';
5947 trndir[dirlen-1] = ']';
5949 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
5950 trndir[--dirlen] = '\0';
5951 trndir[dirlen-1] = '>';
5954 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
5955 /* If we've got an explicit filename, we can just shuffle the string. */
5956 if (*(cp1+1)) hasfilename = 1;
5957 /* Similarly, we can just back up a level if we've got multiple levels
5958 of explicit directories in a VMS spec which ends with directories. */
5960 for (cp2 = cp1; cp2 > trndir; cp2--) {
5962 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
5963 /* fix-me, can not scan EFS file specs backward like this */
5964 *cp2 = *cp1; *cp1 = '\0';
5969 if (*cp2 == '[' || *cp2 == '<') break;
5974 vmsdir = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
5975 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5976 cp1 = strpbrk(trndir,"]:>");
5977 if (cp1 && *(cp1+1) == ':') /* DECNet node spec with :: */
5978 cp1 = strpbrk(cp1+2,"]:>");
5980 if (hasfilename || !cp1) { /* filename present or not VMS */
5982 if (trndir[0] == '.') {
5983 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
5984 PerlMem_free(trndir);
5985 PerlMem_free(vmsdir);
5986 return int_fileify_dirspec("[]", buf, NULL);
5988 else if (trndir[1] == '.' &&
5989 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
5990 PerlMem_free(trndir);
5991 PerlMem_free(vmsdir);
5992 return int_fileify_dirspec("[-]", buf, NULL);
5995 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
5996 dirlen -= 1; /* to last element */
5997 lastdir = strrchr(trndir,'/');
5999 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
6000 /* If we have "/." or "/..", VMSify it and let the VMS code
6001 * below expand it, rather than repeating the code to handle
6002 * relative components of a filespec here */
6004 if (*(cp1+2) == '.') cp1++;
6005 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6007 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6008 PerlMem_free(trndir);
6009 PerlMem_free(vmsdir);
6012 if (strchr(vmsdir,'/') != NULL) {
6013 /* If int_tovmsspec() returned it, it must have VMS syntax
6014 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6015 * the time to check this here only so we avoid a recursion
6016 * loop; otherwise, gigo.
6018 PerlMem_free(trndir);
6019 PerlMem_free(vmsdir);
6020 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6023 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6024 PerlMem_free(trndir);
6025 PerlMem_free(vmsdir);
6028 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6029 PerlMem_free(trndir);
6030 PerlMem_free(vmsdir);
6034 } while ((cp1 = strstr(cp1,"/.")) != NULL);
6035 lastdir = strrchr(trndir,'/');
6037 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6039 /* Ditto for specs that end in an MFD -- let the VMS code
6040 * figure out whether it's a real device or a rooted logical. */
6042 /* This should not happen any more. Allowing the fake /000000
6043 * in a UNIX pathname causes all sorts of problems when trying
6044 * to run in UNIX emulation. So the VMS to UNIX conversions
6045 * now remove the fake /000000 directories.
6048 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6049 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6050 PerlMem_free(trndir);
6051 PerlMem_free(vmsdir);
6054 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6055 PerlMem_free(trndir);
6056 PerlMem_free(vmsdir);
6059 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6060 PerlMem_free(trndir);
6061 PerlMem_free(vmsdir);
6066 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6067 !(lastdir = cp1 = strrchr(trndir,']')) &&
6068 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6070 cp2 = strrchr(cp1,'.');
6072 int e_len, vs_len = 0;
6075 cp3 = strchr(cp2,';');
6076 e_len = strlen(cp2);
6078 vs_len = strlen(cp3);
6079 e_len = e_len - vs_len;
6081 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6083 if (!decc_efs_charset) {
6084 /* If this is not EFS, then not a directory */
6085 PerlMem_free(trndir);
6086 PerlMem_free(vmsdir);
6088 set_vaxc_errno(RMS$_DIR);
6092 /* Ok, here we have an issue, technically if a .dir shows */
6093 /* from inside a directory, then we should treat it as */
6094 /* xxx^.dir.dir. But we do not have that context at this */
6095 /* point unless this is totally restructured, so we remove */
6096 /* The .dir for now, and fix this better later */
6097 dirlen = cp2 - trndir;
6099 if (decc_efs_charset && !strchr(trndir,'/')) {
6100 /* Dots are allowed in dir names, so escape them if input not in Unix syntax. */
6101 char *cp4 = is_dir ? (cp2 - 1) : cp2;
6103 for (; cp4 > cp1; cp4--) {
6105 if ((cp4 - 1 > trndir) && (*(cp4 - 1) != '^')) {
6106 memmove(cp4 + 1, cp4, trndir + dirlen - cp4 + 1);
6117 retlen = dirlen + 6;
6118 memcpy(buf, trndir, dirlen);
6121 /* We've picked up everything up to the directory file name.
6122 Now just add the type and version, and we're set. */
6123 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
6127 if (!decc_filename_unix_no_version)
6129 PerlMem_free(trndir);
6130 PerlMem_free(vmsdir);
6133 else { /* VMS-style directory spec */
6135 char *esa, *esal, term, *cp;
6138 unsigned long int cmplen, haslower = 0;
6139 struct FAB dirfab = cc$rms_fab;
6140 rms_setup_nam(savnam);
6141 rms_setup_nam(dirnam);
6143 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
6144 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6146 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6147 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
6148 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6150 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6151 rms_bind_fab_nam(dirfab, dirnam);
6152 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6153 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6154 #ifdef NAM$M_NO_SHORT_UPCASE
6155 if (decc_efs_case_preserve)
6156 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6159 for (cp = trndir; *cp; cp++)
6160 if (islower(*cp)) { haslower = 1; break; }
6161 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6162 if ((dirfab.fab$l_sts == RMS$_DIR) ||
6163 (dirfab.fab$l_sts == RMS$_DNF) ||
6164 (dirfab.fab$l_sts == RMS$_PRV)) {
6165 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6166 sts = sys$parse(&dirfab);
6172 PerlMem_free(trndir);
6173 PerlMem_free(vmsdir);
6175 set_vaxc_errno(dirfab.fab$l_sts);
6181 /* Does the file really exist? */
6182 if (sys$search(&dirfab)& STS$K_SUCCESS) {
6183 /* Yes; fake the fnb bits so we'll check type below */
6184 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6186 else { /* No; just work with potential name */
6187 if (dirfab.fab$l_sts == RMS$_FNF
6188 || dirfab.fab$l_sts == RMS$_DNF
6189 || dirfab.fab$l_sts == RMS$_FND)
6193 fab_sts = dirfab.fab$l_sts;
6194 sts = rms_free_search_context(&dirfab);
6198 PerlMem_free(trndir);
6199 PerlMem_free(vmsdir);
6200 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
6206 /* Make sure we are using the right buffer */
6207 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6210 my_esa_len = rms_nam_esll(dirnam);
6214 my_esa_len = rms_nam_esl(dirnam);
6215 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6218 my_esa[my_esa_len] = '\0';
6219 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6220 cp1 = strchr(my_esa,']');
6221 if (!cp1) cp1 = strchr(my_esa,'>');
6222 if (cp1) { /* Should always be true */
6223 my_esa_len -= cp1 - my_esa - 1;
6224 memmove(my_esa, cp1 + 1, my_esa_len);
6227 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6228 /* Yep; check version while we're at it, if it's there. */
6229 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6230 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6231 /* Something other than .DIR[;1]. Bzzt. */
6232 sts = rms_free_search_context(&dirfab);
6236 PerlMem_free(trndir);
6237 PerlMem_free(vmsdir);
6239 set_vaxc_errno(RMS$_DIR);
6244 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6245 /* They provided at least the name; we added the type, if necessary, */
6246 my_strlcpy(buf, my_esa, VMS_MAXRSS);
6247 sts = rms_free_search_context(&dirfab);
6248 PerlMem_free(trndir);
6252 PerlMem_free(vmsdir);
6255 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6256 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6260 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6261 if (cp1 == NULL) { /* should never happen */
6262 sts = rms_free_search_context(&dirfab);
6263 PerlMem_free(trndir);
6267 PerlMem_free(vmsdir);
6272 retlen = strlen(my_esa);
6273 cp1 = strrchr(my_esa,'.');
6274 /* ODS-5 directory specifications can have extra "." in them. */
6275 /* Fix-me, can not scan EFS file specifications backwards */
6276 while (cp1 != NULL) {
6277 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6281 while ((cp1 > my_esa) && (*cp1 != '.'))
6288 if ((cp1) != NULL) {
6289 /* There's more than one directory in the path. Just roll back. */
6291 my_strlcpy(buf, my_esa, VMS_MAXRSS);
6294 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6295 /* Go back and expand rooted logical name */
6296 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6297 #ifdef NAM$M_NO_SHORT_UPCASE
6298 if (decc_efs_case_preserve)
6299 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6301 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6302 sts = rms_free_search_context(&dirfab);
6306 PerlMem_free(trndir);
6307 PerlMem_free(vmsdir);
6309 set_vaxc_errno(dirfab.fab$l_sts);
6313 /* This changes the length of the string of course */
6315 my_esa_len = rms_nam_esll(dirnam);
6317 my_esa_len = rms_nam_esl(dirnam);
6320 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6321 cp1 = strstr(my_esa,"][");
6322 if (!cp1) cp1 = strstr(my_esa,"]<");
6323 dirlen = cp1 - my_esa;
6324 memcpy(buf, my_esa, dirlen);
6325 if (!strncmp(cp1+2,"000000]",7)) {
6326 buf[dirlen-1] = '\0';
6327 /* fix-me Not full ODS-5, just extra dots in directories for now */
6328 cp1 = buf + dirlen - 1;
6334 if (*(cp1-1) != '^')
6339 if (*cp1 == '.') *cp1 = ']';
6341 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6342 memmove(cp1+1,"000000]",7);
6346 memmove(buf+dirlen, cp1+2, retlen-dirlen);
6348 /* Convert last '.' to ']' */
6350 while (*cp != '[') {
6353 /* Do not trip on extra dots in ODS-5 directories */
6354 if ((cp1 == buf) || (*(cp1-1) != '^'))
6358 if (*cp1 == '.') *cp1 = ']';
6360 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6361 memmove(cp1+1,"000000]",7);
6365 else { /* This is a top-level dir. Add the MFD to the path. */
6366 cp1 = strrchr(my_esa, ':');
6368 memmove(buf, my_esa, cp1 - my_esa + 1);
6369 memmove(buf + (cp1 - my_esa) + 1, "[000000]", 8);
6370 memmove(buf + (cp1 - my_esa) + 9, cp1 + 2, retlen - (cp1 - my_esa + 2));
6371 buf[retlen + 7] = '\0'; /* We've inserted '000000]' */
6374 sts = rms_free_search_context(&dirfab);
6375 /* We've set up the string up through the filename. Add the
6376 type and version, and we're done. */
6377 strcat(buf,".DIR;1");
6379 /* $PARSE may have upcased filespec, so convert output to lower
6380 * case if input contained any lowercase characters. */
6381 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
6382 PerlMem_free(trndir);
6386 PerlMem_free(vmsdir);
6389 } /* end of int_fileify_dirspec() */
6392 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6394 mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6396 static char __fileify_retbuf[VMS_MAXRSS];
6397 char * fileified, *ret_spec, *ret_buf;
6401 if (ret_buf == NULL) {
6403 Newx(fileified, VMS_MAXRSS, char);
6404 if (fileified == NULL)
6405 _ckvmssts(SS$_INSFMEM);
6406 ret_buf = fileified;
6408 ret_buf = __fileify_retbuf;
6412 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6414 if (ret_spec == NULL) {
6415 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6417 Safefree(fileified);
6421 } /* end of do_fileify_dirspec() */
6424 /* External entry points */
6426 Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6428 return do_fileify_dirspec(dir, buf, 0, NULL);
6432 Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6434 return do_fileify_dirspec(dir, buf, 1, NULL);
6438 Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6440 return do_fileify_dirspec(dir, buf, 0, utf8_fl);
6444 Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6446 return do_fileify_dirspec(dir, buf, 1, utf8_fl);
6450 int_pathify_dirspec_simple(const char * dir, char * buf,
6451 char * v_spec, int v_len, char * r_spec, int r_len,
6452 char * d_spec, int d_len, char * n_spec, int n_len,
6453 char * e_spec, int e_len, char * vs_spec, int vs_len)
6456 /* VMS specification - Try to do this the simple way */
6457 if ((v_len + r_len > 0) || (d_len > 0)) {
6460 /* No name or extension component, already a directory */
6461 if ((n_len + e_len + vs_len) == 0) {
6466 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6467 /* This results from catfile() being used instead of catdir() */
6468 /* So even though it should not work, we need to allow it */
6470 /* If this is .DIR;1 then do a simple conversion */
6471 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6472 if (is_dir || (e_len == 0) && (d_len > 0)) {
6474 len = v_len + r_len + d_len - 1;
6475 char dclose = d_spec[d_len - 1];
6476 memcpy(buf, dir, len);
6479 memcpy(&buf[len], n_spec, n_len);
6482 buf[len + 1] = '\0';
6487 else if (d_len > 0) {
6488 /* In the olden days, a directory needed to have a .DIR */
6489 /* extension to be a valid directory, but now it could */
6490 /* be a symbolic link */
6492 len = v_len + r_len + d_len - 1;
6493 char dclose = d_spec[d_len - 1];
6494 memcpy(buf, dir, len);
6497 memcpy(&buf[len], n_spec, n_len);
6500 if (decc_efs_charset) {
6502 && (toupper(e_spec[1]) == 'D')
6503 && (toupper(e_spec[2]) == 'I')
6504 && (toupper(e_spec[3]) == 'R')) {
6506 /* Corner case: directory spec with invalid version.
6507 * Valid would have followed is_dir path above.
6509 SETERRNO(ENOTDIR, RMS$_DIR);
6515 memcpy(&buf[len], e_spec, e_len);
6520 SETERRNO(ENOTDIR, RMS$_DIR);
6525 buf[len + 1] = '\0';
6530 set_vaxc_errno(RMS$_DIR);
6536 set_vaxc_errno(RMS$_DIR);
6542 /* Internal routine to make sure or convert a directory to be in a */
6543 /* path specification. No utf8 flag because it is not changed or used */
6545 int_pathify_dirspec(const char *dir, char *buf)
6547 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6548 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6549 char * exp_spec, *ret_spec;
6551 unsigned short int trnlnm_iter_count;
6555 if (vms_debug_fileify) {
6557 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6559 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6562 /* We may need to lower case the result if we translated */
6563 /* a logical name or got the current working directory */
6566 if (!dir || !*dir) {
6568 set_vaxc_errno(SS$_BADPARAM);
6572 trndir = (char *)PerlMem_malloc(VMS_MAXRSS);
6574 _ckvmssts_noperl(SS$_INSFMEM);
6576 /* If no directory specified use the current default */
6578 my_strlcpy(trndir, dir, VMS_MAXRSS);
6580 getcwd(trndir, VMS_MAXRSS - 1);
6584 /* now deal with bare names that could be logical names */
6585 trnlnm_iter_count = 0;
6586 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6587 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6588 trnlnm_iter_count++;
6590 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6592 trnlen = strlen(trndir);
6594 /* Trap simple rooted lnms, and return lnm:[000000] */
6595 if (!strcmp(trndir+trnlen-2,".]")) {
6596 my_strlcpy(buf, dir, VMS_MAXRSS);
6597 strcat(buf, ":[000000]");
6598 PerlMem_free(trndir);
6600 if (vms_debug_fileify) {
6601 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6607 /* At this point we do not work with *dir, but the copy in *trndir */
6609 if (need_to_lower && !decc_efs_case_preserve) {
6610 /* Legacy mode, lower case the returned value */
6611 __mystrtolower(trndir);
6615 /* Some special cases, '..', '.' */
6617 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6618 /* Force UNIX filespec */
6622 /* Is this Unix or VMS format? */
6623 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6624 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6625 &e_len, &vs_spec, &vs_len);
6628 /* Just a filename? */
6629 if ((v_len + r_len + d_len) == 0) {
6631 /* Now we have a problem, this could be Unix or VMS */
6632 /* We have to guess. .DIR usually means VMS */
6634 /* In UNIX report mode, the .DIR extension is removed */
6635 /* if one shows up, it is for a non-directory or a directory */
6636 /* in EFS charset mode */
6638 /* So if we are in Unix report mode, assume that this */
6639 /* is a relative Unix directory specification */
6642 if (!decc_filename_unix_report && decc_efs_charset) {
6644 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6647 /* Traditional mode, assume .DIR is directory */
6650 memcpy(&buf[2], n_spec, n_len);
6651 buf[n_len + 2] = ']';
6652 buf[n_len + 3] = '\0';
6653 PerlMem_free(trndir);
6654 if (vms_debug_fileify) {
6656 "int_pathify_dirspec: buf = %s\n",
6666 ret_spec = int_pathify_dirspec_simple(trndir, buf,
6667 v_spec, v_len, r_spec, r_len,
6668 d_spec, d_len, n_spec, n_len,
6669 e_spec, e_len, vs_spec, vs_len);
6671 if (ret_spec != NULL) {
6672 PerlMem_free(trndir);
6673 if (vms_debug_fileify) {
6675 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6680 /* Simple way did not work, which means that a logical name */
6681 /* was present for the directory specification. */
6682 /* Need to use an rmsexpand variant to decode it completely */
6683 exp_spec = (char *)PerlMem_malloc(VMS_MAXRSS);
6684 if (exp_spec == NULL)
6685 _ckvmssts_noperl(SS$_INSFMEM);
6687 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6688 if (ret_spec != NULL) {
6689 sts = vms_split_path(exp_spec, &v_spec, &v_len,
6690 &r_spec, &r_len, &d_spec, &d_len,
6691 &n_spec, &n_len, &e_spec,
6692 &e_len, &vs_spec, &vs_len);
6694 ret_spec = int_pathify_dirspec_simple(
6695 exp_spec, buf, v_spec, v_len, r_spec, r_len,
6696 d_spec, d_len, n_spec, n_len,
6697 e_spec, e_len, vs_spec, vs_len);
6699 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6700 /* Legacy mode, lower case the returned value */
6701 __mystrtolower(ret_spec);
6704 set_vaxc_errno(RMS$_DIR);
6709 PerlMem_free(exp_spec);
6710 PerlMem_free(trndir);
6711 if (vms_debug_fileify) {
6712 if (ret_spec == NULL)
6713 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6716 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6721 /* Unix specification, Could be trivial conversion, */
6722 /* but have to deal with trailing '.dir' or extra '.' */
6727 STRLEN dir_len = strlen(trndir);
6729 lastslash = strrchr(trndir, '/');
6730 if (lastslash == NULL)
6737 /* '..' or '.' are valid directory components */
6739 if (lastslash[0] == '.') {
6740 if (lastslash[1] == '\0') {
6742 } else if (lastslash[1] == '.') {
6743 if (lastslash[2] == '\0') {
6746 /* And finally allow '...' */
6747 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6755 lastdot = strrchr(lastslash, '.');
6757 if (lastdot != NULL) {
6759 /* '.dir' is discarded, and any other '.' is invalid */
6760 e_len = strlen(lastdot);
6762 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6765 dir_len = dir_len - 4;
6769 my_strlcpy(buf, trndir, VMS_MAXRSS);
6770 if (buf[dir_len - 1] != '/') {
6772 buf[dir_len + 1] = '\0';
6775 /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6776 if (!decc_efs_charset) {
6779 if (str[0] == '.') {
6782 while ((dots[cnt] == '.') && (cnt < 3))
6785 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6791 for (; *str; ++str) {
6792 while (*str == '/') {
6798 /* Have to skip up to three dots which could be */
6799 /* directories, 3 dots being a VMS extension for Perl */
6802 while ((dots[cnt] == '.') && (cnt < 3)) {
6805 if (dots[cnt] == '\0')
6807 if ((cnt > 1) && (dots[cnt] != '/')) {
6813 /* too many dots? */
6814 if ((cnt == 0) || (cnt > 3)) {
6818 if (!dir_start && (*str == '.')) {
6823 PerlMem_free(trndir);
6825 if (vms_debug_fileify) {
6826 if (ret_spec == NULL)
6827 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6830 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6836 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6838 mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6840 static char __pathify_retbuf[VMS_MAXRSS];
6841 char * pathified, *ret_spec, *ret_buf;
6845 if (ret_buf == NULL) {
6847 Newx(pathified, VMS_MAXRSS, char);
6848 if (pathified == NULL)
6849 _ckvmssts(SS$_INSFMEM);
6850 ret_buf = pathified;
6852 ret_buf = __pathify_retbuf;
6856 ret_spec = int_pathify_dirspec(dir, ret_buf);
6858 if (ret_spec == NULL) {
6859 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6861 Safefree(pathified);
6866 } /* end of do_pathify_dirspec() */
6869 /* External entry points */
6871 Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6873 return do_pathify_dirspec(dir, buf, 0, NULL);
6877 Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
6879 return do_pathify_dirspec(dir, buf, 1, NULL);
6883 Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
6885 return do_pathify_dirspec(dir, buf, 0, utf8_fl);
6889 Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
6891 return do_pathify_dirspec(dir, buf, 1, utf8_fl);
6894 /* Internal tounixspec routine that does not use a thread context */
6895 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
6897 int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
6899 char *dirend, *cp1, *cp3, *tmp;
6902 unsigned short int trnlnm_iter_count;
6903 int cmp_rslt, outchars_added;
6904 if (utf8_fl != NULL)
6907 if (vms_debug_fileify) {
6909 fprintf(stderr, "int_tounixspec: spec = NULL\n");
6911 fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
6917 set_vaxc_errno(SS$_BADPARAM);
6920 if (strlen(spec) > (VMS_MAXRSS-1)) {
6922 set_vaxc_errno(SS$_BUFFEROVF);
6926 /* New VMS specific format needs translation
6927 * glob passes filenames with trailing '\n' and expects this preserved.
6929 if (decc_posix_compliant_pathnames) {
6930 if (strncmp(spec, "\"^UP^", 5) == 0) {
6936 tunix = (char *)PerlMem_malloc(VMS_MAXRSS);
6937 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6938 tunix_len = my_strlcpy(tunix, spec, VMS_MAXRSS);
6940 if (tunix[tunix_len - 1] == '\n') {
6941 tunix[tunix_len - 1] = '\"';
6942 tunix[tunix_len] = '\0';
6946 uspec = decc$translate_vms(tunix);
6947 PerlMem_free(tunix);
6948 if ((int)uspec > 0) {
6949 my_strlcpy(rslt, uspec, VMS_MAXRSS);
6954 /* If we can not translate it, makemaker wants as-is */
6955 my_strlcpy(rslt, spec, VMS_MAXRSS);
6962 cmp_rslt = 0; /* Presume VMS */
6963 cp1 = strchr(spec, '/');
6967 /* Look for EFS ^/ */
6968 if (decc_efs_charset) {
6969 while (cp1 != NULL) {
6972 /* Found illegal VMS, assume UNIX */
6977 cp1 = strchr(cp1, '/');
6981 /* Look for "." and ".." */
6982 if (decc_filename_unix_report) {
6983 if (spec[0] == '.') {
6984 if ((spec[1] == '\0') || (spec[1] == '\n')) {
6988 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
6998 /* This is already UNIX or at least nothing VMS understands,
6999 * so all we can reasonably do is unescape extended chars.
7003 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7004 cp1 += outchars_added;
7007 if (vms_debug_fileify) {
7008 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7013 dirend = strrchr(spec,']');
7014 if (dirend == NULL) dirend = strrchr(spec,'>');
7015 if (dirend == NULL) dirend = strchr(spec,':');
7016 if (dirend == NULL) {
7018 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7019 cp1 += outchars_added;
7022 if (vms_debug_fileify) {
7023 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7028 /* Special case 1 - sys$posix_root = / */
7029 if (!decc_disable_posix_root) {
7030 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7037 /* Special case 2 - Convert NLA0: to /dev/null */
7038 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7039 if (cmp_rslt == 0) {
7040 strcpy(rslt, "/dev/null");
7043 if (spec[6] != '\0') {
7050 /* Also handle special case "SYS$SCRATCH:" */
7051 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7052 tmp = (char *)PerlMem_malloc(VMS_MAXRSS);
7053 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7054 if (cmp_rslt == 0) {
7057 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7059 strcpy(rslt, "/tmp");
7062 if (spec[12] != '\0') {
7070 if (*cp2 != '[' && *cp2 != '<') {
7073 else { /* the VMS spec begins with directories */
7075 if (*cp2 == ']' || *cp2 == '>') {
7076 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
7080 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7081 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7083 if (vms_debug_fileify) {
7084 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7088 trnlnm_iter_count = 0;
7091 while (*cp3 != ':' && *cp3) cp3++;
7093 if (strchr(cp3,']') != NULL) break;
7094 trnlnm_iter_count++;
7095 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7096 } while (vmstrnenv(tmp,tmp,0,fildev,0));
7101 *(cp1++) = *(cp3++);
7102 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7104 set_errno(ENAMETOOLONG);
7105 set_vaxc_errno(SS$_BUFFEROVF);
7106 if (vms_debug_fileify) {
7107 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7109 return NULL; /* No room */
7114 if ((*cp2 == '^')) {
7115 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7116 cp1 += outchars_added;
7118 else if ( *cp2 == '.') {
7119 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7120 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7127 for (; cp2 <= dirend; cp2++) {
7128 if ((*cp2 == '^')) {
7129 /* EFS file escape -- unescape it. */
7130 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added) - 1;
7131 cp1 += outchars_added;
7133 else if (*cp2 == ':') {
7135 if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7137 else if (*cp2 == ']' || *cp2 == '>') {
7138 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7140 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7142 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7143 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7144 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7145 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7146 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7148 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7149 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7153 else if (*cp2 == '-') {
7154 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7155 while (*cp2 == '-') {
7157 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7159 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7160 /* filespecs like */
7161 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
7162 if (vms_debug_fileify) {
7163 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7168 else *(cp1++) = *cp2;
7170 else *(cp1++) = *cp2;
7172 /* Translate the rest of the filename. */
7176 /* Fixme - for compatibility with the CRTL we should be removing */
7177 /* spaces from the file specifications, but this may show that */
7178 /* some tests that were appearing to pass are not really passing */
7184 cp2 += copy_expand_vms_filename_escape(cp1, cp2, &outchars_added);
7185 cp1 += outchars_added;
7188 if (decc_filename_unix_no_version) {
7189 /* Easy, drop the version */
7194 /* Punt - passing the version as a dot will probably */
7195 /* break perl in weird ways, but so did passing */
7196 /* through the ; as a version. Follow the CRTL and */
7197 /* hope for the best. */
7204 /* We will need to fix this properly later */
7205 /* As Perl may be installed on an ODS-5 volume, but not */
7206 /* have the EFS_CHARSET enabled, it still may encounter */
7207 /* filenames with extra dots in them, and a precedent got */
7208 /* set which allowed them to work, that we will uphold here */
7209 /* If extra dots are present in a name and no ^ is on them */
7210 /* VMS assumes that the first one is the extension delimiter */
7211 /* the rest have an implied ^. */
7213 /* this is also a conflict as the . is also a version */
7214 /* delimiter in VMS, */
7216 *(cp1++) = *(cp2++);
7220 /* This is an extension */
7221 if (decc_readdir_dropdotnotype) {
7223 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7224 /* Drop the dot for the extension */
7232 *(cp1++) = *(cp2++);
7237 /* This still leaves /000000/ when working with a
7238 * VMS device root or concealed root.
7244 ulen = strlen(rslt);
7246 /* Get rid of "000000/ in rooted filespecs */
7248 zeros = strstr(rslt, "/000000/");
7249 if (zeros != NULL) {
7251 mlen = ulen - (zeros - rslt) - 7;
7252 memmove(zeros, &zeros[7], mlen);
7259 if (vms_debug_fileify) {
7260 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7264 } /* end of int_tounixspec() */
7267 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7269 mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7271 static char __tounixspec_retbuf[VMS_MAXRSS];
7272 char * unixspec, *ret_spec, *ret_buf;
7276 if (ret_buf == NULL) {
7278 Newx(unixspec, VMS_MAXRSS, char);
7279 if (unixspec == NULL)
7280 _ckvmssts(SS$_INSFMEM);
7283 ret_buf = __tounixspec_retbuf;
7287 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7289 if (ret_spec == NULL) {
7290 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7297 } /* end of do_tounixspec() */
7299 /* External entry points */
7301 Perl_tounixspec(pTHX_ const char *spec, char *buf)
7303 return do_tounixspec(spec, buf, 0, NULL);
7307 Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7309 return do_tounixspec(spec,buf,1, NULL);
7313 Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7315 return do_tounixspec(spec,buf,0, utf8_fl);
7319 Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7321 return do_tounixspec(spec,buf,1, utf8_fl);
7324 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7327 This procedure is used to identify if a path is based in either
7328 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7329 it returns the OpenVMS format directory for it.
7331 It is expecting specifications of only '/' or '/xxxx/'
7333 If a posix root does not exist, or 'xxxx' is not a directory
7334 in the posix root, it returns a failure.
7336 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7338 It is used only internally by posix_to_vmsspec_hardway().
7342 posix_root_to_vms(char *vmspath, int vmspath_len,
7343 const char *unixpath, const int * utf8_fl)
7346 struct FAB myfab = cc$rms_fab;
7347 rms_setup_nam(mynam);
7348 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7349 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7350 char * esa, * esal, * rsa, * rsal;
7356 unixlen = strlen(unixpath);
7361 #if __CRTL_VER >= 80200000
7362 /* If not a posix spec already, convert it */
7363 if (decc_posix_compliant_pathnames) {
7364 if (strncmp(unixpath,"\"^UP^",5) != 0) {
7365 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7368 /* This is already a VMS specification, no conversion */
7370 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7379 /* Check to see if this is under the POSIX root */
7380 if (decc_disable_posix_root) {
7384 /* Skip leading / */
7385 if (unixpath[0] == '/') {
7391 strcpy(vmspath,"SYS$POSIX_ROOT:");
7393 /* If this is only the / , or blank, then... */
7394 if (unixpath[0] == '\0') {
7395 /* by definition, this is the answer */
7399 /* Need to look up a directory */
7403 /* Copy and add '^' escape characters as needed */
7406 while (unixpath[i] != 0) {
7409 j += copy_expand_unix_filename_escape
7410 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7414 path_len = strlen(vmspath);
7415 if (vmspath[path_len - 1] == '/')
7417 vmspath[path_len] = ']';
7419 vmspath[path_len] = '\0';
7422 vmspath[vmspath_len] = 0;
7423 if (unixpath[unixlen - 1] == '/')
7425 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
7426 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7427 esa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7428 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7429 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
7430 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7431 rsa = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
7432 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7433 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7434 rms_bind_fab_nam(myfab, mynam);
7435 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7436 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7437 if (decc_efs_case_preserve)
7438 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7439 #ifdef NAML$M_OPEN_SPECIAL
7440 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7443 /* Set up the remaining naml fields */
7444 sts = sys$parse(&myfab);
7446 /* It failed! Try again as a UNIX filespec */
7455 /* get the Device ID and the FID */
7456 sts = sys$search(&myfab);
7458 /* These are no longer needed */
7463 /* on any failure, returned the POSIX ^UP^ filespec */
7468 specdsc.dsc$a_pointer = vmspath;
7469 specdsc.dsc$w_length = vmspath_len;
7471 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7472 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7473 sts = lib$fid_to_name
7474 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7476 /* on any failure, returned the POSIX ^UP^ filespec */
7478 /* This can happen if user does not have permission to read directories */
7479 if (strncmp(unixpath,"\"^UP^",5) != 0)
7480 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7482 my_strlcpy(vmspath, unixpath, vmspath_len + 1);
7485 vmspath[specdsc.dsc$w_length] = 0;
7487 /* Are we expecting a directory? */
7488 if (dir_flag != 0) {
7494 i = specdsc.dsc$w_length - 1;
7498 /* Version must be '1' */
7499 if (vmspath[i--] != '1')
7501 /* Version delimiter is one of ".;" */
7502 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7505 if (vmspath[i--] != 'R')
7507 if (vmspath[i--] != 'I')
7509 if (vmspath[i--] != 'D')
7511 if (vmspath[i--] != '.')
7513 eptr = &vmspath[i+1];
7515 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7516 if (vmspath[i-1] != '^') {
7524 /* Get rid of 6 imaginary zero directory filename */
7525 vmspath[i+1] = '\0';
7529 if (vmspath[i] == '0')
7543 /* /dev/mumble needs to be handled special.
7544 /dev/null becomes NLA0:, And there is the potential for other stuff
7545 like /dev/tty which may need to be mapped to something.
7549 slash_dev_special_to_vms(const char *unixptr, char *vmspath, int vmspath_len)
7556 nextslash = strchr(unixptr, '/');
7557 len = strlen(unixptr);
7558 if (nextslash != NULL)
7559 len = nextslash - unixptr;
7560 cmp = strncmp("null", unixptr, 5);
7562 if (vmspath_len >= 6) {
7563 strcpy(vmspath, "_NLA0:");
7571 /* The built in routines do not understand perl's special needs, so
7572 doing a manual conversion from UNIX to VMS
7574 If the utf8_fl is not null and points to a non-zero value, then
7575 treat 8 bit characters as UTF-8.
7577 The sequence starting with '$(' and ending with ')' will be passed
7578 through with out interpretation instead of being escaped.
7582 posix_to_vmsspec_hardway(char *vmspath, int vmspath_len, const char *unixpath,
7583 int dir_flag, int * utf8_fl)
7587 const char *unixptr;
7588 const char *unixend;
7590 const char *lastslash;
7591 const char *lastdot;
7597 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7598 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7600 if (utf8_fl != NULL)
7606 /* Ignore leading "/" characters */
7607 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7610 unixlen = strlen(unixptr);
7612 /* Do nothing with blank paths */
7619 /* This could have a "^UP^ on the front */
7620 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7626 lastslash = strrchr(unixptr,'/');
7627 lastdot = strrchr(unixptr,'.');
7628 unixend = strrchr(unixptr,'\"');
7629 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7630 unixend = unixptr + unixlen;
7633 /* last dot is last dot or past end of string */
7634 if (lastdot == NULL)
7635 lastdot = unixptr + unixlen;
7637 /* if no directories, set last slash to beginning of string */
7638 if (lastslash == NULL) {
7639 lastslash = unixptr;
7642 /* Watch out for trailing "." after last slash, still a directory */
7643 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7644 lastslash = unixptr + unixlen;
7647 /* Watch out for trailing ".." after last slash, still a directory */
7648 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7649 lastslash = unixptr + unixlen;
7652 /* dots in directories are aways escaped */
7653 if (lastdot < lastslash)
7654 lastdot = unixptr + unixlen;
7657 /* if (unixptr < lastslash) then we are in a directory */
7664 /* Start with the UNIX path */
7665 if (*unixptr != '/') {
7666 /* relative paths */
7668 /* If allowing logical names on relative pathnames, then handle here */
7669 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7670 !decc_posix_compliant_pathnames) {
7676 /* Find the next slash */
7677 nextslash = strchr(unixptr,'/');
7679 esa = (char *)PerlMem_malloc(vmspath_len);
7680 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7682 trn = (char *)PerlMem_malloc(VMS_MAXRSS);
7683 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7685 if (nextslash != NULL) {
7687 seg_len = nextslash - unixptr;
7688 memcpy(esa, unixptr, seg_len);
7692 seg_len = my_strlcpy(esa, unixptr, sizeof(esa));
7694 /* trnlnm(section) */
7695 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7698 /* Now fix up the directory */
7700 /* Split up the path to find the components */
7701 sts = vms_split_path
7719 /* A logical name must be a directory or the full
7720 specification. It is only a full specification if
7721 it is the only component */
7722 if ((unixptr[seg_len] == '\0') ||
7723 (unixptr[seg_len+1] == '\0')) {
7725 /* Is a directory being required? */
7726 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7727 /* Not a logical name */
7732 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7733 /* This must be a directory */
7734 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7735 vmslen = my_strlcpy(vmsptr, esa, vmspath_len - 1);
7736 vmsptr[vmslen] = ':';
7738 vmsptr[vmslen] = '\0';
7746 /* must be dev/directory - ignore version */
7747 if ((n_len + e_len) != 0)
7750 /* transfer the volume */
7751 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7752 memcpy(vmsptr, v_spec, v_len);
7758 /* unroot the rooted directory */
7759 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7761 r_spec[r_len - 1] = ']';
7763 /* This should not be there, but nothing is perfect */
7765 cmp = strcmp(&r_spec[1], "000000.");
7775 memcpy(vmsptr, r_spec, r_len);
7781 /* Bring over the directory. */
7783 ((d_len + vmslen) < vmspath_len)) {
7785 d_spec[d_len - 1] = ']';
7787 cmp = strcmp(&d_spec[1], "000000.");
7798 /* Remove the redundant root */
7806 memcpy(vmsptr, d_spec, d_len);
7820 if (lastslash > unixptr) {
7823 /* skip leading ./ */
7825 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7831 /* Are we still in a directory? */
7832 if (unixptr <= lastslash) {
7837 /* if not backing up, then it is relative forward. */
7838 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7839 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7847 /* Perl wants an empty directory here to tell the difference
7848 * between a DCL command and a filename
7857 /* Handle two special files . and .. */
7858 if (unixptr[0] == '.') {
7859 if (&unixptr[1] == unixend) {
7866 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7877 else { /* Absolute PATH handling */
7881 /* Need to find out where root is */
7883 /* In theory, this procedure should never get an absolute POSIX pathname
7884 * that can not be found on the POSIX root.
7885 * In practice, that can not be relied on, and things will show up
7886 * here that are a VMS device name or concealed logical name instead.
7887 * So to make things work, this procedure must be tolerant.
7889 esa = (char *)PerlMem_malloc(vmspath_len);
7890 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7893 nextslash = strchr(&unixptr[1],'/');
7895 if (nextslash != NULL) {
7897 seg_len = nextslash - &unixptr[1];
7898 my_strlcpy(vmspath, unixptr, seg_len + 2);
7901 cmp = strncmp(vmspath, "dev", 4);
7903 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
7904 if (sts == SS$_NORMAL)
7908 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
7911 if ($VMS_STATUS_SUCCESS(sts)) {
7912 /* This is verified to be a real path */
7914 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
7915 if ($VMS_STATUS_SUCCESS(sts)) {
7916 vmslen = my_strlcpy(vmspath, esa, vmspath_len + 1);
7917 vmsptr = vmspath + vmslen;
7919 if (unixptr < lastslash) {
7928 cmp = strcmp(rptr,"000000.");
7933 } /* removing 6 zeros */
7934 } /* vmslen < 7, no 6 zeros possible */
7935 } /* Not in a directory */
7936 } /* Posix root found */
7938 /* No posix root, fall back to default directory */
7939 strcpy(vmspath, "SYS$DISK:[");
7940 vmsptr = &vmspath[10];
7942 if (unixptr > lastslash) {
7951 } /* end of verified real path handling */
7956 /* Ok, we have a device or a concealed root that is not in POSIX
7957 * or we have garbage. Make the best of it.
7960 /* Posix to VMS destroyed this, so copy it again */
7961 my_strlcpy(vmspath, &unixptr[1], seg_len + 1);
7962 vmslen = strlen(vmspath); /* We know we're truncating. */
7963 vmsptr = &vmsptr[vmslen];
7966 /* Now do we need to add the fake 6 zero directory to it? */
7968 if ((*lastslash == '/') && (nextslash < lastslash)) {
7969 /* No there is another directory */
7976 /* now we have foo:bar or foo:[000000]bar to decide from */
7977 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
7979 if (!islnm && !decc_posix_compliant_pathnames) {
7981 cmp = strncmp("bin", vmspath, 4);
7983 /* bin => SYS$SYSTEM: */
7984 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
7987 /* tmp => SYS$SCRATCH: */
7988 cmp = strncmp("tmp", vmspath, 4);
7990 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
7995 trnend = islnm ? islnm - 1 : 0;
7997 /* if this was a logical name, ']' or '>' must be present */
7998 /* if not a logical name, then assume a device and hope. */
7999 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
8001 /* if log name and trailing '.' then rooted - treat as device */
8002 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
8004 /* Fix me, if not a logical name, a device lookup should be
8005 * done to see if the device is file structured. If the device
8006 * is not file structured, the 6 zeros should not be put on.
8008 * As it is, perl is occasionally looking for dev:[000000]tty.
8009 * which looks a little strange.
8011 * Not that easy to detect as "/dev" may be file structured with
8012 * special device files.
8015 if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
8016 (&nextslash[1] == unixend)) {
8017 /* No real directory present */
8022 /* Put the device delimiter on */
8025 unixptr = nextslash;
8028 /* Start directory if needed */
8029 if (!islnm || add_6zero) {
8035 /* add fake 000000] if needed */
8048 } /* non-POSIX translation */
8050 } /* End of relative/absolute path handling */
8052 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
8059 if (dir_start != 0) {
8061 /* First characters in a directory are handled special */
8062 while ((*unixptr == '/') ||
8063 ((*unixptr == '.') &&
8064 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8065 (&unixptr[1]==unixend)))) {
8070 /* Skip redundant / in specification */
8071 while ((*unixptr == '/') && (dir_start != 0)) {
8074 if (unixptr == lastslash)
8077 if (unixptr == lastslash)
8080 /* Skip redundant ./ characters */
8081 while ((*unixptr == '.') &&
8082 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8085 if (unixptr == lastslash)
8087 if (*unixptr == '/')
8090 if (unixptr == lastslash)
8093 /* Skip redundant ../ characters */
8094 while ((*unixptr == '.') && (unixptr[1] == '.') &&
8095 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8096 /* Set the backing up flag */
8102 unixptr++; /* first . */
8103 unixptr++; /* second . */
8104 if (unixptr == lastslash)
8106 if (*unixptr == '/') /* The slash */
8109 if (unixptr == lastslash)
8112 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8113 /* Not needed when VMS is pretending to be UNIX. */
8115 /* Is this loop stuck because of too many dots? */
8116 if (loop_flag == 0) {
8117 /* Exit the loop and pass the rest through */
8122 /* Are we done with directories yet? */
8123 if (unixptr >= lastslash) {
8125 /* Watch out for trailing dots */
8134 if (*unixptr == '/')
8138 /* Have we stopped backing up? */
8143 /* dir_start continues to be = 1 */
8145 if (*unixptr == '-') {
8147 *vmsptr++ = *unixptr++;
8151 /* Now are we done with directories yet? */
8152 if (unixptr >= lastslash) {
8154 /* Watch out for trailing dots */
8170 if (unixptr >= unixend)
8173 /* Normal characters - More EFS work probably needed */
8179 /* remove multiple / */
8180 while (unixptr[1] == '/') {
8183 if (unixptr == lastslash) {
8184 /* Watch out for trailing dots */
8196 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8197 /* Not needed when VMS is pretending to be UNIX. */
8201 if (unixptr != unixend)
8206 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8207 (&unixptr[1] == unixend)) {
8213 /* trailing dot ==> '^..' on VMS */
8214 if (unixptr == unixend) {
8222 *vmsptr++ = *unixptr++;
8226 if (quoted && (&unixptr[1] == unixend)) {
8230 in_cnt = copy_expand_unix_filename_escape
8231 (vmsptr, unixptr, &out_cnt, utf8_fl);
8241 in_cnt = copy_expand_unix_filename_escape
8242 (vmsptr, unixptr, &out_cnt, utf8_fl);
8249 /* Make sure directory is closed */
8250 if (unixptr == lastslash) {
8252 vmsptr2 = vmsptr - 1;
8254 if (*vmsptr2 != ']') {
8257 /* directories do not end in a dot bracket */
8258 if (*vmsptr2 == '.') {
8262 if (*vmsptr2 != '^') {
8263 vmsptr--; /* back up over the dot */
8271 /* Add a trailing dot if a file with no extension */
8272 vmsptr2 = vmsptr - 1;
8274 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8275 (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
8286 /* A convenience macro for copying dots in filenames and escaping
8287 * them when they haven't already been escaped, with guards to
8288 * avoid checking before the start of the buffer or advancing
8289 * beyond the end of it (allowing room for the NUL terminator).
8291 #define VMSEFS_DOT_WITH_ESCAPE(vmsefsdot,vmsefsbuf,vmsefsbufsiz) STMT_START { \
8292 if ( ((vmsefsdot) > (vmsefsbuf) && *((vmsefsdot) - 1) != '^' \
8293 || ((vmsefsdot) == (vmsefsbuf))) \
8294 && (vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 3 \
8296 *((vmsefsdot)++) = '^'; \
8298 if ((vmsefsdot) < (vmsefsbuf) + (vmsefsbufsiz) - 2) \
8299 *((vmsefsdot)++) = '.'; \
8302 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8304 int_tovmsspec(const char *path, char *rslt, int dir_flag, int * utf8_flag)
8310 unsigned long int infront = 0, hasdir = 1;
8313 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8314 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8316 if (vms_debug_fileify) {
8318 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8320 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8324 /* If we fail, we should be setting errno */
8326 set_vaxc_errno(SS$_BADPARAM);
8329 rslt_len = VMS_MAXRSS-1;
8331 /* '.' and '..' are "[]" and "[-]" for a quick check */
8332 if (path[0] == '.') {
8333 if (path[1] == '\0') {
8335 if (utf8_flag != NULL)
8340 if (path[1] == '.' && path[2] == '\0') {
8342 if (utf8_flag != NULL)
8349 /* Posix specifications are now a native VMS format */
8350 /*--------------------------------------------------*/
8351 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8352 if (decc_posix_compliant_pathnames) {
8353 if (strncmp(path,"\"^UP^",5) == 0) {
8354 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8360 /* This is really the only way to see if this is already in VMS format */
8361 sts = vms_split_path
8376 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8377 replacement, because the above parse just took care of most of
8378 what is needed to do vmspath when the specification is already
8381 And if it is not already, it is easier to do the conversion as
8382 part of this routine than to call this routine and then work on
8386 /* If VMS punctuation was found, it is already VMS format */
8387 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8388 if (utf8_flag != NULL)
8390 my_strlcpy(rslt, path, VMS_MAXRSS);
8391 if (vms_debug_fileify) {
8392 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8396 /* Now, what to do with trailing "." cases where there is no
8397 extension? If this is a UNIX specification, and EFS characters
8398 are enabled, then the trailing "." should be converted to a "^.".
8399 But if this was already a VMS specification, then it should be
8402 So in the case of ambiguity, leave the specification alone.
8406 /* If there is a possibility of UTF8, then if any UTF8 characters
8407 are present, then they must be converted to VTF-7
8409 if (utf8_flag != NULL)
8411 my_strlcpy(rslt, path, VMS_MAXRSS);
8412 if (vms_debug_fileify) {
8413 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8418 dirend = strrchr(path,'/');
8420 if (dirend == NULL) {
8421 /* If we get here with no Unix directory delimiters, then this is an
8422 * ambiguous file specification, such as a Unix glob specification, a
8423 * shell or make macro, or a filespec that would be valid except for
8424 * unescaped extended characters. The safest thing if it's a macro
8425 * is to pass it through as-is.
8427 if (strstr(path, "$(")) {
8428 my_strlcpy(rslt, path, VMS_MAXRSS);
8429 if (vms_debug_fileify) {
8430 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8436 else if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
8437 if (!*(dirend+2)) dirend +=2;
8438 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8439 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8444 lastdot = strrchr(cp2,'.');
8450 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8452 if (decc_disable_posix_root) {
8453 strcpy(rslt,"sys$disk:[000000]");
8456 strcpy(rslt,"sys$posix_root:[000000]");
8458 if (utf8_flag != NULL)
8460 if (vms_debug_fileify) {
8461 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8465 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8467 trndev = (char *)PerlMem_malloc(VMS_MAXRSS);
8468 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8469 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8471 /* DECC special handling */
8473 if (strcmp(rslt,"bin") == 0) {
8474 strcpy(rslt,"sys$system");
8477 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8479 else if (strcmp(rslt,"tmp") == 0) {
8480 strcpy(rslt,"sys$scratch");
8483 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8485 else if (!decc_disable_posix_root) {
8486 strcpy(rslt, "sys$posix_root");
8490 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8491 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8493 else if (strcmp(rslt,"dev") == 0) {
8494 if (strncmp(cp2,"/null", 5) == 0) {
8495 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8496 strcpy(rslt,"NLA0");
8500 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8506 trnend = islnm ? strlen(trndev) - 1 : 0;
8507 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8508 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8509 /* If the first element of the path is a logical name, determine
8510 * whether it has to be translated so we can add more directories. */
8511 if (!islnm || rooted) {
8514 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8518 if (cp2 != dirend) {
8519 my_strlcpy(rslt, trndev, VMS_MAXRSS);
8520 cp1 = rslt + trnend;
8527 if (decc_disable_posix_root) {
8533 PerlMem_free(trndev);
8538 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8539 cp2 += 2; /* skip over "./" - it's redundant */
8540 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8542 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8543 *(cp1++) = '-'; /* "../" --> "-" */
8546 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8547 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8548 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8549 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8552 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8553 /* Escape the extra dots in EFS file specifications */
8556 if (cp2 > dirend) cp2 = dirend;
8558 else *(cp1++) = '.';
8560 for (; cp2 < dirend; cp2++) {
8562 if (*(cp2-1) == '/') continue;
8563 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.';
8566 else if (!infront && *cp2 == '.') {
8567 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8568 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
8569 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8570 if (cp1 > rslt && (*(cp1-1) == '-' || *(cp1-1) == '[')) *(cp1++) = '-'; /* handle "../" */
8571 else if (cp1 > rslt + 1 && *(cp1-2) == '[') *(cp1-1) = '-';
8576 if (cp2 == dirend) break;
8578 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8579 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8580 if (cp1 > rslt && *(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8581 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8583 *(cp1++) = '.'; /* Simulate trailing '/' */
8584 cp2 += 2; /* for loop will incr this to == dirend */
8586 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8589 if (decc_efs_charset == 0) {
8590 if (cp1 > rslt && *(cp1-1) == '^')
8591 cp1--; /* remove the escape, if any */
8592 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8595 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8600 if (!infront && cp1 > rslt && *(cp1-1) == '-') *(cp1++) = '.';
8602 if (decc_efs_charset == 0) {
8603 if (cp1 > rslt && *(cp1-1) == '^')
8604 cp1--; /* remove the escape, if any */
8608 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8613 cp2 += copy_expand_unix_filename_escape(cp1, cp2, &out_cnt, utf8_flag);
8614 cp2--; /* we're in a loop that will increment this */
8620 if (cp1 > rslt && *(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8621 if (hasdir) *(cp1++) = ']';
8622 if (*cp2 && *cp2 == '/') cp2++; /* check in case we ended with trailing '/' */
8629 if (decc_efs_charset == 0)
8635 if (cp2 >= path && (cp2 == path || *(cp2-1) != '^')) /* not previously escaped */
8641 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8642 decc_readdir_dropdotnotype) {
8643 VMSEFS_DOT_WITH_ESCAPE(cp1, rslt, VMS_MAXRSS);
8646 /* trailing dot ==> '^..' on VMS */
8653 *(cp1++) = *(cp2++);
8658 /* This could be a macro to be passed through */
8659 *(cp1++) = *(cp2++);
8661 const char * save_cp2;
8665 /* paranoid check */
8671 *(cp1++) = *(cp2++);
8672 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8673 *(cp1++) = *(cp2++);
8674 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8675 *(cp1++) = *(cp2++);
8678 *(cp1++) = *(cp2++);
8682 if (is_macro == 0) {
8683 /* Not really a macro - never mind */
8696 /* Don't escape again if following character is
8697 * already something we escape.
8699 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8700 *(cp1++) = *(cp2++);
8703 /* But otherwise fall through and escape it. */
8720 if (cp2 >= path && *(cp2-1) != '^') /* not previously escaped */
8722 *(cp1++) = *(cp2++);
8725 /* If it doesn't look like the beginning of a version number,
8726 * or we've been promised there are no version numbers, then
8729 if (decc_filename_unix_no_version) {
8733 size_t all_nums = strspn(cp2+1, "0123456789");
8734 if (all_nums > 5 || *(cp2 + all_nums + 1) != '\0')
8737 *(cp1++) = *(cp2++);
8740 *(cp1++) = *(cp2++);
8743 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8747 /* Fix me for "^]", but that requires making sure that you do
8748 * not back up past the start of the filename
8750 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8755 if (utf8_flag != NULL)
8757 if (vms_debug_fileify) {
8758 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8762 } /* end of int_tovmsspec() */
8765 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8767 mp_do_tovmsspec(pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag)
8769 static char __tovmsspec_retbuf[VMS_MAXRSS];
8770 char * vmsspec, *ret_spec, *ret_buf;
8774 if (ret_buf == NULL) {
8776 Newx(vmsspec, VMS_MAXRSS, char);
8777 if (vmsspec == NULL)
8778 _ckvmssts(SS$_INSFMEM);
8781 ret_buf = __tovmsspec_retbuf;
8785 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8787 if (ret_spec == NULL) {
8788 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8795 } /* end of mp_do_tovmsspec() */
8797 /* External entry points */
8799 Perl_tovmsspec(pTHX_ const char *path, char *buf)
8801 return do_tovmsspec(path, buf, 0, NULL);
8805 Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8807 return do_tovmsspec(path, buf, 1, NULL);
8811 Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8813 return do_tovmsspec(path, buf, 0, utf8_fl);
8817 Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8819 return do_tovmsspec(path, buf, 1, utf8_fl);
8822 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
8823 /* Internal routine for use with out an explicit context present */
8825 int_tovmspath(const char *path, char *buf, int * utf8_fl)
8827 char * ret_spec, *pathified;
8832 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8833 if (pathified == NULL)
8834 _ckvmssts_noperl(SS$_INSFMEM);
8836 ret_spec = int_pathify_dirspec(path, pathified);
8838 if (ret_spec == NULL) {
8839 PerlMem_free(pathified);
8843 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
8845 PerlMem_free(pathified);
8850 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8852 mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl)
8854 static char __tovmspath_retbuf[VMS_MAXRSS];
8856 char *pathified, *vmsified, *cp;
8858 if (path == NULL) return NULL;
8859 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8860 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8861 if (int_pathify_dirspec(path, pathified) == NULL) {
8862 PerlMem_free(pathified);
8868 Newx(vmsified, VMS_MAXRSS, char);
8869 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8870 PerlMem_free(pathified);
8871 if (vmsified) Safefree(vmsified);
8874 PerlMem_free(pathified);
8879 vmslen = strlen(vmsified);
8880 Newx(cp,vmslen+1,char);
8881 memcpy(cp,vmsified,vmslen);
8887 my_strlcpy(__tovmspath_retbuf, vmsified, sizeof(__tovmspath_retbuf));
8889 return __tovmspath_retbuf;
8892 } /* end of do_tovmspath() */
8894 /* External entry points */
8896 Perl_tovmspath(pTHX_ const char *path, char *buf)
8898 return do_tovmspath(path, buf, 0, NULL);
8902 Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
8904 return do_tovmspath(path, buf, 1, NULL);
8908 Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
8910 return do_tovmspath(path, buf, 0, utf8_fl);
8914 Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
8916 return do_tovmspath(path, buf, 1, utf8_fl);
8920 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
8922 mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl)
8924 static char __tounixpath_retbuf[VMS_MAXRSS];
8926 char *pathified, *unixified, *cp;
8928 if (path == NULL) return NULL;
8929 pathified = (char *)PerlMem_malloc(VMS_MAXRSS);
8930 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8931 if (int_pathify_dirspec(path, pathified) == NULL) {
8932 PerlMem_free(pathified);
8938 Newx(unixified, VMS_MAXRSS, char);
8940 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
8941 PerlMem_free(pathified);
8942 if (unixified) Safefree(unixified);
8945 PerlMem_free(pathified);
8950 unixlen = strlen(unixified);
8951 Newx(cp,unixlen+1,char);
8952 memcpy(cp,unixified,unixlen);
8954 Safefree(unixified);
8958 my_strlcpy(__tounixpath_retbuf, unixified, sizeof(__tounixpath_retbuf));
8959 Safefree(unixified);
8960 return __tounixpath_retbuf;
8963 } /* end of do_tounixpath() */
8965 /* External entry points */
8967 Perl_tounixpath(pTHX_ const char *path, char *buf)
8969 return do_tounixpath(path, buf, 0, NULL);
8973 Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
8975 return do_tounixpath(path, buf, 1, NULL);
8979 Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8981 return do_tounixpath(path, buf, 0, utf8_fl);
8985 Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8987 return do_tounixpath(path, buf, 1, utf8_fl);
8991 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
8993 *****************************************************************************
8995 * Copyright (C) 1989-1994, 2007 by *
8996 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
8998 * Permission is hereby granted for the reproduction of this software *
8999 * on condition that this copyright notice is included in source *
9000 * distributions of the software. The code may be modified and *
9001 * distributed under the same terms as Perl itself. *
9003 * 27-Aug-1994 Modified for inclusion in perl5 *
9004 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
9005 *****************************************************************************
9009 * getredirection() is intended to aid in porting C programs
9010 * to VMS (Vax-11 C). The native VMS environment does not support
9011 * '>' and '<' I/O redirection, or command line wild card expansion,
9012 * or a command line pipe mechanism using the '|' AND background
9013 * command execution '&'. All of these capabilities are provided to any
9014 * C program which calls this procedure as the first thing in the
9016 * The piping mechanism will probably work with almost any 'filter' type
9017 * of program. With suitable modification, it may useful for other
9018 * portability problems as well.
9020 * Author: Mark Pizzolato (mark AT infocomm DOT com)
9024 struct list_item *next;
9028 static void add_item(struct list_item **head,
9029 struct list_item **tail,
9033 static void mp_expand_wild_cards(pTHX_ char *item,
9034 struct list_item **head,
9035 struct list_item **tail,
9038 static int background_process(pTHX_ int argc, char **argv);
9040 static void pipe_and_fork(pTHX_ char **cmargv);
9042 /*{{{ void getredirection(int *ac, char ***av)*/
9044 mp_getredirection(pTHX_ int *ac, char ***av)
9046 * Process vms redirection arg's. Exit if any error is seen.
9047 * If getredirection() processes an argument, it is erased
9048 * from the vector. getredirection() returns a new argc and argv value.
9049 * In the event that a background command is requested (by a trailing "&"),
9050 * this routine creates a background subprocess, and simply exits the program.
9052 * Warning: do not try to simplify the code for vms. The code
9053 * presupposes that getredirection() is called before any data is
9054 * read from stdin or written to stdout.
9056 * Normal usage is as follows:
9062 * getredirection(&argc, &argv);
9066 int argc = *ac; /* Argument Count */
9067 char **argv = *av; /* Argument Vector */
9068 char *ap; /* Argument pointer */
9069 int j; /* argv[] index */
9070 int item_count = 0; /* Count of Items in List */
9071 struct list_item *list_head = 0; /* First Item in List */
9072 struct list_item *list_tail; /* Last Item in List */
9073 char *in = NULL; /* Input File Name */
9074 char *out = NULL; /* Output File Name */
9075 char *outmode = "w"; /* Mode to Open Output File */
9076 char *err = NULL; /* Error File Name */
9077 char *errmode = "w"; /* Mode to Open Error File */
9078 int cmargc = 0; /* Piped Command Arg Count */
9079 char **cmargv = NULL;/* Piped Command Arg Vector */
9082 * First handle the case where the last thing on the line ends with
9083 * a '&'. This indicates the desire for the command to be run in a
9084 * subprocess, so we satisfy that desire.
9087 if (0 == strcmp("&", ap))
9088 exit(background_process(aTHX_ --argc, argv));
9089 if (*ap && '&' == ap[strlen(ap)-1])
9091 ap[strlen(ap)-1] = '\0';
9092 exit(background_process(aTHX_ argc, argv));
9095 * Now we handle the general redirection cases that involve '>', '>>',
9096 * '<', and pipes '|'.
9098 for (j = 0; j < argc; ++j)
9100 if (0 == strcmp("<", argv[j]))
9104 fprintf(stderr,"No input file after < on command line");
9105 exit(LIB$_WRONUMARG);
9110 if ('<' == *(ap = argv[j]))
9115 if (0 == strcmp(">", ap))
9119 fprintf(stderr,"No output file after > on command line");
9120 exit(LIB$_WRONUMARG);
9139 fprintf(stderr,"No output file after > or >> on command line");
9140 exit(LIB$_WRONUMARG);
9144 if (('2' == *ap) && ('>' == ap[1]))
9161 fprintf(stderr,"No output file after 2> or 2>> on command line");
9162 exit(LIB$_WRONUMARG);
9166 if (0 == strcmp("|", argv[j]))
9170 fprintf(stderr,"No command into which to pipe on command line");
9171 exit(LIB$_WRONUMARG);
9173 cmargc = argc-(j+1);
9174 cmargv = &argv[j+1];
9178 if ('|' == *(ap = argv[j]))
9186 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9189 * Allocate and fill in the new argument vector, Some Unix's terminate
9190 * the list with an extra null pointer.
9192 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9193 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9195 for (j = 0; j < item_count; ++j, list_head = list_head->next)
9196 argv[j] = list_head->value;
9202 fprintf(stderr,"'|' and '>' may not both be specified on command line");
9203 exit(LIB$_INVARGORD);
9205 pipe_and_fork(aTHX_ cmargv);
9208 /* Check for input from a pipe (mailbox) */
9210 if (in == NULL && 1 == isapipe(0))
9212 char mbxname[L_tmpnam];
9214 long int dvi_item = DVI$_DEVBUFSIZ;
9215 $DESCRIPTOR(mbxnam, "");
9216 $DESCRIPTOR(mbxdevnam, "");
9218 /* Input from a pipe, reopen it in binary mode to disable */
9219 /* carriage control processing. */
9221 fgetname(stdin, mbxname, 1);
9222 mbxnam.dsc$a_pointer = mbxname;
9223 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9224 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9225 mbxdevnam.dsc$a_pointer = mbxname;
9226 mbxdevnam.dsc$w_length = sizeof(mbxname);
9227 dvi_item = DVI$_DEVNAM;
9228 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9229 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9232 freopen(mbxname, "rb", stdin);
9235 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9239 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9241 fprintf(stderr,"Can't open input file %s as stdin",in);
9244 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9246 fprintf(stderr,"Can't open output file %s as stdout",out);
9249 if (out != NULL) vmssetuserlnm("SYS$OUTPUT", out);
9252 if (strcmp(err,"&1") == 0) {
9253 dup2(fileno(stdout), fileno(stderr));
9254 vmssetuserlnm("SYS$ERROR", "SYS$OUTPUT");
9257 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9259 fprintf(stderr,"Can't open error file %s as stderr",err);
9263 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9267 vmssetuserlnm("SYS$ERROR", err);
9270 #ifdef ARGPROC_DEBUG
9271 PerlIO_printf(Perl_debug_log, "Arglist:\n");
9272 for (j = 0; j < *ac; ++j)
9273 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9275 /* Clear errors we may have hit expanding wildcards, so they don't
9276 show up in Perl's $! later */
9277 set_errno(0); set_vaxc_errno(1);
9278 } /* end of getredirection() */
9282 add_item(struct list_item **head, struct list_item **tail, char *value, int *count)
9286 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9287 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9291 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9292 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9293 *tail = (*tail)->next;
9295 (*tail)->value = value;
9300 mp_expand_wild_cards(pTHX_ char *item, struct list_item **head,
9301 struct list_item **tail, int *count)
9304 unsigned long int context = 0;
9312 $DESCRIPTOR(filespec, "");
9313 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9314 $DESCRIPTOR(resultspec, "");
9315 unsigned long int lff_flags = 0;
9319 #ifdef VMS_LONGNAME_SUPPORT
9320 lff_flags = LIB$M_FIL_LONG_NAMES;
9323 for (cp = item; *cp; cp++) {
9324 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9325 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9327 if (!*cp || isspace(*cp))
9329 add_item(head, tail, item, count);
9334 /* "double quoted" wild card expressions pass as is */
9335 /* From DCL that means using e.g.: */
9336 /* perl program """perl.*""" */
9337 item_len = strlen(item);
9338 if ( '"' == *item && '"' == item[item_len-1] )
9341 item[item_len-2] = '\0';
9342 add_item(head, tail, item, count);
9346 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9347 resultspec.dsc$b_class = DSC$K_CLASS_D;
9348 resultspec.dsc$a_pointer = NULL;
9349 vmsspec = (char *)PerlMem_malloc(VMS_MAXRSS);
9350 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9351 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9352 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9353 if (!isunix || !filespec.dsc$a_pointer)
9354 filespec.dsc$a_pointer = item;
9355 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9357 * Only return version specs, if the caller specified a version
9359 had_version = strchr(item, ';');
9361 * Only return device and directory specs, if the caller specified either.
9363 had_device = strchr(item, ':');
9364 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9366 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9367 (&filespec, &resultspec, &context,
9368 &defaultspec, 0, &rms_sts, &lff_flags)))
9373 string = (char *)PerlMem_malloc(resultspec.dsc$w_length+1);
9374 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9375 my_strlcpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length+1);
9376 if (NULL == had_version)
9377 *(strrchr(string, ';')) = '\0';
9378 if ((!had_directory) && (had_device == NULL))
9380 if (NULL == (devdir = strrchr(string, ']')))
9381 devdir = strrchr(string, '>');
9382 my_strlcpy(string, devdir + 1, resultspec.dsc$w_length+1);
9385 * Be consistent with what the C RTL has already done to the rest of
9386 * the argv items and lowercase all of these names.
9388 if (!decc_efs_case_preserve) {
9389 for (c = string; *c; ++c)
9393 if (isunix) trim_unixpath(string,item,1);
9394 add_item(head, tail, string, count);
9397 PerlMem_free(vmsspec);
9398 if (sts != RMS$_NMF)
9400 set_vaxc_errno(sts);
9403 case RMS$_FNF: case RMS$_DNF:
9404 set_errno(ENOENT); break;
9406 set_errno(ENOTDIR); break;
9408 set_errno(ENODEV); break;
9409 case RMS$_FNM: case RMS$_SYN:
9410 set_errno(EINVAL); break;
9412 set_errno(EACCES); break;
9414 _ckvmssts_noperl(sts);
9418 add_item(head, tail, item, count);
9419 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9420 _ckvmssts_noperl(lib$find_file_end(&context));
9425 pipe_and_fork(pTHX_ char **cmargv)
9428 struct dsc$descriptor_s *vmscmd;
9429 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9430 int sts, j, l, ismcr, quote, tquote = 0;
9432 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
9433 vms_execfree(vmscmd);
9438 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
9439 && toupper(*(q+2)) == 'R' && !*(q+3);
9441 while (q && l < MAX_DCL_LINE_LENGTH) {
9443 if (j > 0 && quote) {
9449 if (ismcr && j > 1) quote = 1;
9450 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9453 if (quote || tquote) {
9459 if ((quote||tquote) && *q == '"') {
9469 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9471 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9476 background_process(pTHX_ int argc, char **argv)
9478 char command[MAX_DCL_SYMBOL + 1] = "$";
9479 $DESCRIPTOR(value, "");
9480 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9481 static $DESCRIPTOR(null, "NLA0:");
9482 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9484 $DESCRIPTOR(pidstr, "");
9486 unsigned long int flags = 17, one = 1, retsts;
9489 len = my_strlcat(command, argv[0], sizeof(command));
9490 while (--argc && (len < MAX_DCL_SYMBOL))
9492 my_strlcat(command, " \"", sizeof(command));
9493 my_strlcat(command, *(++argv), sizeof(command));
9494 len = my_strlcat(command, "\"", sizeof(command));
9496 value.dsc$a_pointer = command;
9497 value.dsc$w_length = strlen(value.dsc$a_pointer);
9498 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9499 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9500 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9501 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9504 _ckvmssts_noperl(retsts);
9506 #ifdef ARGPROC_DEBUG
9507 PerlIO_printf(Perl_debug_log, "%s\n", command);
9509 sprintf(pidstring, "%08X", pid);
9510 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9511 pidstr.dsc$a_pointer = pidstring;
9512 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9513 lib$set_symbol(&pidsymbol, &pidstr);
9517 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9520 /* OS-specific initialization at image activation (not thread startup) */
9521 /* Older VAXC header files lack these constants */
9522 #ifndef JPI$_RIGHTS_SIZE
9523 # define JPI$_RIGHTS_SIZE 817
9525 #ifndef KGB$M_SUBSYSTEM
9526 # define KGB$M_SUBSYSTEM 0x8
9529 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9531 /*{{{void vms_image_init(int *, char ***)*/
9533 vms_image_init(int *argcp, char ***argvp)
9536 char eqv[LNM$C_NAMLENGTH+1] = "";
9537 unsigned int len, tabct = 8, tabidx = 0;
9538 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9539 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9540 unsigned short int dummy, rlen;
9541 struct dsc$descriptor_s **tabvec;
9542 #if defined(PERL_IMPLICIT_CONTEXT)
9545 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9546 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9547 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9550 #ifdef KILL_BY_SIGPRC
9551 Perl_csighandler_init();
9554 #if __CRTL_VER >= 70300000 && !defined(__VAX)
9555 /* This was moved from the pre-image init handler because on threaded */
9556 /* Perl it was always returning 0 for the default value. */
9557 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9560 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9563 initial = decc$feature_get_value(s, 4);
9565 /* initial is: 0 if nothing has set the feature */
9566 /* -1 if initialized to default */
9567 /* 1 if set by logical name */
9568 /* 2 if set by decc$feature_set_value */
9569 decc_disable_posix_root = decc$feature_get_value(s, 1);
9571 /* If the value is not valid, force the feature off */
9572 if (decc_disable_posix_root < 0) {
9573 decc$feature_set_value(s, 1, 1);
9574 decc_disable_posix_root = 1;
9578 /* Nothing has asked for it explicitly, so use our own default. */
9579 decc_disable_posix_root = 1;
9580 decc$feature_set_value(s, 1, 1);
9586 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9587 _ckvmssts_noperl(iosb[0]);
9588 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9589 if (iprv[i]) { /* Running image installed with privs? */
9590 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
9595 /* Rights identifiers might trigger tainting as well. */
9596 if (!will_taint && (rlen || rsz)) {
9597 while (rlen < rsz) {
9598 /* We didn't get all the identifiers on the first pass. Allocate a
9599 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9600 * were needed to hold all identifiers at time of last call; we'll
9601 * allocate that many unsigned long ints), and go back and get 'em.
9602 * If it gave us less than it wanted to despite ample buffer space,
9603 * something's broken. Is your system missing a system identifier?
9605 if (rsz <= jpilist[1].buflen) {
9606 /* Perl_croak accvios when used this early in startup. */
9607 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9608 rsz, (unsigned long) jpilist[1].buflen,
9609 "Check your rights database for corruption.\n");
9612 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9613 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9614 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9615 jpilist[1].buflen = rsz * sizeof(unsigned long int);
9616 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9617 _ckvmssts_noperl(iosb[0]);
9619 mask = (unsigned long int *)jpilist[1].bufadr;
9620 /* Check attribute flags for each identifier (2nd longword); protected
9621 * subsystem identifiers trigger tainting.
9623 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9624 if (mask[i] & KGB$M_SUBSYSTEM) {
9629 if (mask != rlst) PerlMem_free(mask);
9632 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9633 * logical, some versions of the CRTL will add a phanthom /000000/
9634 * directory. This needs to be removed.
9636 if (decc_filename_unix_report) {
9639 ulen = strlen(argvp[0][0]);
9641 zeros = strstr(argvp[0][0], "/000000/");
9642 if (zeros != NULL) {
9644 mlen = ulen - (zeros - argvp[0][0]) - 7;
9645 memmove(zeros, &zeros[7], mlen);
9647 argvp[0][0][ulen] = '\0';
9650 /* It also may have a trailing dot that needs to be removed otherwise
9651 * it will be converted to VMS mode incorrectly.
9654 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9655 argvp[0][0][ulen] = '\0';
9658 /* We need to use this hack to tell Perl it should run with tainting,
9659 * since its tainting flag may be part of the PL_curinterp struct, which
9660 * hasn't been allocated when vms_image_init() is called.
9663 char **newargv, **oldargv;
9665 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9666 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9667 newargv[0] = oldargv[0];
9668 newargv[1] = (char *)PerlMem_malloc(3 * sizeof(char));
9669 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9670 strcpy(newargv[1], "-T");
9671 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9673 newargv[*argcp] = NULL;
9674 /* We orphan the old argv, since we don't know where it's come from,
9675 * so we don't know how to free it.
9679 else { /* Did user explicitly request tainting? */
9681 char *cp, **av = *argvp;
9682 for (i = 1; i < *argcp; i++) {
9683 if (*av[i] != '-') break;
9684 for (cp = av[i]+1; *cp; cp++) {
9685 if (*cp == 'T') { will_taint = 1; break; }
9686 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9687 strchr("DFIiMmx",*cp)) break;
9689 if (will_taint) break;
9694 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9697 tabvec = (struct dsc$descriptor_s **)
9698 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9699 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9701 else if (tabidx >= tabct) {
9703 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9704 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9706 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9707 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9708 tabvec[tabidx]->dsc$w_length = len;
9709 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9710 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_S;
9711 tabvec[tabidx]->dsc$a_pointer = (char *)PerlMem_malloc(len + 1);
9712 if (tabvec[tabidx]->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9713 my_strlcpy(tabvec[tabidx]->dsc$a_pointer, eqv, len + 1);
9715 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9717 getredirection(argcp,argvp);
9718 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9720 # include <reentrancy.h>
9721 decc$set_reentrancy(C$C_MULTITHREAD);
9730 * Trim Unix-style prefix off filespec, so it looks like what a shell
9731 * glob expansion would return (i.e. from specified prefix on, not
9732 * full path). Note that returned filespec is Unix-style, regardless
9733 * of whether input filespec was VMS-style or Unix-style.
9735 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9736 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9737 * vector of options; at present, only bit 0 is used, and if set tells
9738 * trim unixpath to try the current default directory as a prefix when
9739 * presented with a possibly ambiguous ... wildcard.
9741 * Returns !=0 on success, with trimmed filespec replacing contents of
9742 * fspec, and 0 on failure, with contents of fpsec unchanged.
9744 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9746 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9748 char *unixified, *unixwild, *tplate, *base, *end, *cp1, *cp2;
9749 int tmplen, reslen = 0, dirs = 0;
9751 if (!wildspec || !fspec) return 0;
9753 unixwild = (char *)PerlMem_malloc(VMS_MAXRSS);
9754 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9756 if (strpbrk(wildspec,"]>:") != NULL) {
9757 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
9758 PerlMem_free(unixwild);
9763 my_strlcpy(unixwild, wildspec, VMS_MAXRSS);
9765 unixified = (char *)PerlMem_malloc(VMS_MAXRSS);
9766 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9767 if (strpbrk(fspec,"]>:") != NULL) {
9768 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
9769 PerlMem_free(unixwild);
9770 PerlMem_free(unixified);
9773 else base = unixified;
9774 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9775 * check to see that final result fits into (isn't longer than) fspec */
9776 reslen = strlen(fspec);
9780 /* No prefix or absolute path on wildcard, so nothing to remove */
9781 if (!*tplate || *tplate == '/') {
9782 PerlMem_free(unixwild);
9783 if (base == fspec) {
9784 PerlMem_free(unixified);
9787 tmplen = strlen(unixified);
9788 if (tmplen > reslen) {
9789 PerlMem_free(unixified);
9790 return 0; /* not enough space */
9792 /* Copy unixified resultant, including trailing NUL */
9793 memmove(fspec,unixified,tmplen+1);
9794 PerlMem_free(unixified);
9798 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9799 if ((cp1 = strstr(tplate,".../")) == NULL) { /* No ...; just count elts */
9800 for (cp1 = tplate; *cp1; cp1++) if (*cp1 == '/') dirs++;
9801 for (cp1 = end ;cp1 >= base; cp1--)
9802 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9804 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9805 PerlMem_free(unixified);
9806 PerlMem_free(unixwild);
9811 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9812 int ells = 1, totells, segdirs, match;
9813 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9814 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9816 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9818 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9819 tpl = (char *)PerlMem_malloc(VMS_MAXRSS);
9820 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9821 if (ellipsis == tplate && opts & 1) {
9822 /* Template begins with an ellipsis. Since we can't tell how many
9823 * directory names at the front of the resultant to keep for an
9824 * arbitrary starting point, we arbitrarily choose the current
9825 * default directory as a starting point. If it's there as a prefix,
9826 * clip it off. If not, fall through and act as if the leading
9827 * ellipsis weren't there (i.e. return shortest possible path that
9828 * could match template).
9830 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9832 PerlMem_free(unixified);
9833 PerlMem_free(unixwild);
9836 if (!decc_efs_case_preserve) {
9837 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9838 if (_tolower(*cp1) != _tolower(*cp2)) break;
9840 segdirs = dirs - totells; /* Min # of dirs we must have left */
9841 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9842 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9843 memmove(fspec,cp2+1,end - cp2);
9845 PerlMem_free(unixified);
9846 PerlMem_free(unixwild);
9850 /* First off, back up over constant elements at end of path */
9852 for (front = end ; front >= base; front--)
9853 if (*front == '/' && !dirs--) { front++; break; }
9855 lcres = (char *)PerlMem_malloc(VMS_MAXRSS);
9856 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9857 for (cp1=tplate,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9859 if (!decc_efs_case_preserve) {
9860 *cp2 = _tolower(*cp1); /* Make lc copy for match */
9868 PerlMem_free(unixified);
9869 PerlMem_free(unixwild);
9870 PerlMem_free(lcres);
9871 return 0; /* Path too long. */
9874 *cp2 = '\0'; /* Pick up with memcpy later */
9875 lcfront = lcres + (front - base);
9876 /* Now skip over each ellipsis and try to match the path in front of it. */
9878 for (cp1 = ellipsis - 2; cp1 >= tplate; cp1--)
9879 if (*(cp1) == '.' && *(cp1+1) == '.' &&
9880 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
9881 if (cp1 < tplate) break; /* template started with an ellipsis */
9882 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9883 ellipsis = cp1; continue;
9885 wilddsc.dsc$a_pointer = tpl;
9886 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9888 for (segdirs = 0, cp2 = tpl;
9889 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
9891 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
9893 if (!decc_efs_case_preserve) {
9894 *cp2 = _tolower(*cp1); /* else lowercase for match */
9897 *cp2 = *cp1; /* else preserve case for match */
9900 if (*cp2 == '/') segdirs++;
9902 if (cp1 != ellipsis - 1) {
9904 PerlMem_free(unixified);
9905 PerlMem_free(unixwild);
9906 PerlMem_free(lcres);
9907 return 0; /* Path too long */
9909 /* Back up at least as many dirs as in template before matching */
9910 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
9911 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
9912 for (match = 0; cp1 > lcres;) {
9913 resdsc.dsc$a_pointer = cp1;
9914 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
9916 if (match == 1) lcfront = cp1;
9918 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
9922 PerlMem_free(unixified);
9923 PerlMem_free(unixwild);
9924 PerlMem_free(lcres);
9925 return 0; /* Can't find prefix ??? */
9927 if (match > 1 && opts & 1) {
9928 /* This ... wildcard could cover more than one set of dirs (i.e.
9929 * a set of similar dir names is repeated). If the template
9930 * contains more than 1 ..., upstream elements could resolve the
9931 * ambiguity, but it's not worth a full backtracking setup here.
9932 * As a quick heuristic, clip off the current default directory
9933 * if it's present to find the trimmed spec, else use the
9934 * shortest string that this ... could cover.
9936 char def[NAM$C_MAXRSS+1], *st;
9938 if (getcwd(def, sizeof def,0) == NULL) {
9939 PerlMem_free(unixified);
9940 PerlMem_free(unixwild);
9941 PerlMem_free(lcres);
9945 if (!decc_efs_case_preserve) {
9946 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9947 if (_tolower(*cp1) != _tolower(*cp2)) break;
9949 segdirs = dirs - totells; /* Min # of dirs we must have left */
9950 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
9951 if (*cp1 == '\0' && *cp2 == '/') {
9952 memmove(fspec,cp2+1,end - cp2);
9954 PerlMem_free(unixified);
9955 PerlMem_free(unixwild);
9956 PerlMem_free(lcres);
9959 /* Nope -- stick with lcfront from above and keep going. */
9962 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
9964 PerlMem_free(unixified);
9965 PerlMem_free(unixwild);
9966 PerlMem_free(lcres);
9970 } /* end of trim_unixpath() */
9975 * VMS readdir() routines.
9976 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
9978 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
9979 * Minor modifications to original routines.
9982 /* readdir may have been redefined by reentr.h, so make sure we get
9983 * the local version for what we do here.
9988 #if !defined(PERL_IMPLICIT_CONTEXT)
9989 # define readdir Perl_readdir
9991 # define readdir(a) Perl_readdir(aTHX_ a)
9994 /* Number of elements in vms_versions array */
9995 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
9998 * Open a directory, return a handle for later use.
10000 /*{{{ DIR *opendir(char*name) */
10002 Perl_opendir(pTHX_ const char *name)
10008 Newx(dir, VMS_MAXRSS, char);
10009 if (int_tovmspath(name, dir, NULL) == NULL) {
10013 /* Check access before stat; otherwise stat does not
10014 * accurately report whether it's a directory.
10016 if (!strstr(dir, "::") /* sys$check_access doesn't do remotes */
10017 && !cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
10018 /* cando_by_name has already set errno */
10022 if (flex_stat(dir,&sb) == -1) return NULL;
10023 if (!S_ISDIR(sb.st_mode)) {
10025 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
10028 /* Get memory for the handle, and the pattern. */
10030 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
10032 /* Fill in the fields; mainly playing with the descriptor. */
10033 sprintf(dd->pattern, "%s*.*",dir);
10038 /* By saying we want the result of readdir() in unix format, we are really
10039 * saying we want all the escapes removed, translating characters that
10040 * must be escaped in a VMS-format name to their unescaped form, which is
10041 * presumably allowed in a Unix-format name.
10043 dd->flags = decc_filename_unix_report ? PERL_VMSDIR_M_UNIXSPECS : 0;
10044 dd->pat.dsc$a_pointer = dd->pattern;
10045 dd->pat.dsc$w_length = strlen(dd->pattern);
10046 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10047 dd->pat.dsc$b_class = DSC$K_CLASS_S;
10048 #if defined(USE_ITHREADS)
10049 Newx(dd->mutex,1,perl_mutex);
10050 MUTEX_INIT( (perl_mutex *) dd->mutex );
10056 } /* end of opendir() */
10060 * Set the flag to indicate we want versions or not.
10062 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10064 vmsreaddirversions(DIR *dd, int flag)
10067 dd->flags |= PERL_VMSDIR_M_VERSIONS;
10069 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10074 * Free up an opened directory.
10076 /*{{{ void closedir(DIR *dd)*/
10078 Perl_closedir(DIR *dd)
10082 sts = lib$find_file_end(&dd->context);
10083 Safefree(dd->pattern);
10084 #if defined(USE_ITHREADS)
10085 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10086 Safefree(dd->mutex);
10093 * Collect all the version numbers for the current file.
10096 collectversions(pTHX_ DIR *dd)
10098 struct dsc$descriptor_s pat;
10099 struct dsc$descriptor_s res;
10101 char *p, *text, *buff;
10103 unsigned long context, tmpsts;
10105 /* Convenient shorthand. */
10108 /* Add the version wildcard, ignoring the "*.*" put on before */
10109 i = strlen(dd->pattern);
10110 Newx(text,i + e->d_namlen + 3,char);
10111 my_strlcpy(text, dd->pattern, i + 1);
10112 sprintf(&text[i - 3], "%s;*", e->d_name);
10114 /* Set up the pattern descriptor. */
10115 pat.dsc$a_pointer = text;
10116 pat.dsc$w_length = i + e->d_namlen - 1;
10117 pat.dsc$b_dtype = DSC$K_DTYPE_T;
10118 pat.dsc$b_class = DSC$K_CLASS_S;
10120 /* Set up result descriptor. */
10121 Newx(buff, VMS_MAXRSS, char);
10122 res.dsc$a_pointer = buff;
10123 res.dsc$w_length = VMS_MAXRSS - 1;
10124 res.dsc$b_dtype = DSC$K_DTYPE_T;
10125 res.dsc$b_class = DSC$K_CLASS_S;
10127 /* Read files, collecting versions. */
10128 for (context = 0, e->vms_verscount = 0;
10129 e->vms_verscount < VERSIZE(e);
10130 e->vms_verscount++) {
10131 unsigned long rsts;
10132 unsigned long flags = 0;
10134 #ifdef VMS_LONGNAME_SUPPORT
10135 flags = LIB$M_FIL_LONG_NAMES;
10137 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10138 if (tmpsts == RMS$_NMF || context == 0) break;
10140 buff[VMS_MAXRSS - 1] = '\0';
10141 if ((p = strchr(buff, ';')))
10142 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10144 e->vms_versions[e->vms_verscount] = -1;
10147 _ckvmssts(lib$find_file_end(&context));
10151 } /* end of collectversions() */
10154 * Read the next entry from the directory.
10156 /*{{{ struct dirent *readdir(DIR *dd)*/
10158 Perl_readdir(pTHX_ DIR *dd)
10160 struct dsc$descriptor_s res;
10162 unsigned long int tmpsts;
10163 unsigned long rsts;
10164 unsigned long flags = 0;
10165 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10166 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10168 /* Set up result descriptor, and get next file. */
10169 Newx(buff, VMS_MAXRSS, char);
10170 res.dsc$a_pointer = buff;
10171 res.dsc$w_length = VMS_MAXRSS - 1;
10172 res.dsc$b_dtype = DSC$K_DTYPE_T;
10173 res.dsc$b_class = DSC$K_CLASS_S;
10175 #ifdef VMS_LONGNAME_SUPPORT
10176 flags = LIB$M_FIL_LONG_NAMES;
10179 tmpsts = lib$find_file
10180 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10181 if (dd->context == 0)
10182 tmpsts = RMS$_NMF; /* None left. (should be set, but make sure) */
10184 if (!(tmpsts & 1)) {
10187 break; /* no more files considered success */
10189 SETERRNO(EACCES, tmpsts); break;
10191 SETERRNO(ENODEV, tmpsts); break;
10193 SETERRNO(ENOTDIR, tmpsts); break;
10194 case RMS$_FNF: case RMS$_DNF:
10195 SETERRNO(ENOENT, tmpsts); break;
10197 SETERRNO(EVMSERR, tmpsts);
10203 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10204 buff[res.dsc$w_length] = '\0';
10205 p = buff + res.dsc$w_length;
10206 while (--p >= buff) if (!isspace(*p)) break;
10208 if (!decc_efs_case_preserve) {
10209 for (p = buff; *p; p++) *p = _tolower(*p);
10212 /* Skip any directory component and just copy the name. */
10213 sts = vms_split_path
10228 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10230 /* In Unix report mode, remove the ".dir;1" from the name */
10231 /* if it is a real directory. */
10232 if (decc_filename_unix_report && decc_efs_charset) {
10233 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10237 ret_sts = flex_lstat(buff, &statbuf);
10238 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10245 /* Drop NULL extensions on UNIX file specification */
10246 if ((e_len == 1) && decc_readdir_dropdotnotype) {
10252 memcpy(dd->entry.d_name, n_spec, n_len + e_len);
10253 dd->entry.d_name[n_len + e_len] = '\0';
10254 dd->entry.d_namlen = n_len + e_len;
10256 /* Convert the filename to UNIX format if needed */
10257 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10259 /* Translate the encoded characters. */
10260 /* Fixme: Unicode handling could result in embedded 0 characters */
10261 if (strchr(dd->entry.d_name, '^') != NULL) {
10262 char new_name[256];
10264 p = dd->entry.d_name;
10267 int inchars_read, outchars_added;
10268 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10270 q += outchars_added;
10272 /* if outchars_added > 1, then this is a wide file specification */
10273 /* Wide file specifications need to be passed in Perl */
10274 /* counted strings apparently with a Unicode flag */
10277 dd->entry.d_namlen = my_strlcpy(dd->entry.d_name, new_name, sizeof(dd->entry.d_name));
10281 dd->entry.vms_verscount = 0;
10282 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10286 } /* end of readdir() */
10290 * Read the next entry from the directory -- thread-safe version.
10292 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10294 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10298 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10300 entry = readdir(dd);
10302 retval = ( *result == NULL ? errno : 0 );
10304 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10308 } /* end of readdir_r() */
10312 * Return something that can be used in a seekdir later.
10314 /*{{{ long telldir(DIR *dd)*/
10316 Perl_telldir(DIR *dd)
10323 * Return to a spot where we used to be. Brute force.
10325 /*{{{ void seekdir(DIR *dd,long count)*/
10327 Perl_seekdir(pTHX_ DIR *dd, long count)
10331 /* If we haven't done anything yet... */
10332 if (dd->count == 0)
10335 /* Remember some state, and clear it. */
10336 old_flags = dd->flags;
10337 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10338 _ckvmssts(lib$find_file_end(&dd->context));
10341 /* The increment is in readdir(). */
10342 for (dd->count = 0; dd->count < count; )
10345 dd->flags = old_flags;
10347 } /* end of seekdir() */
10350 /* VMS subprocess management
10352 * my_vfork() - just a vfork(), after setting a flag to record that
10353 * the current script is trying a Unix-style fork/exec.
10355 * vms_do_aexec() and vms_do_exec() are called in response to the
10356 * perl 'exec' function. If this follows a vfork call, then they
10357 * call out the regular perl routines in doio.c which do an
10358 * execvp (for those who really want to try this under VMS).
10359 * Otherwise, they do exactly what the perl docs say exec should
10360 * do - terminate the current script and invoke a new command
10361 * (See below for notes on command syntax.)
10363 * do_aspawn() and do_spawn() implement the VMS side of the perl
10364 * 'system' function.
10366 * Note on command arguments to perl 'exec' and 'system': When handled
10367 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10368 * are concatenated to form a DCL command string. If the first non-numeric
10369 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10370 * the command string is handed off to DCL directly. Otherwise,
10371 * the first token of the command is taken as the filespec of an image
10372 * to run. The filespec is expanded using a default type of '.EXE' and
10373 * the process defaults for device, directory, etc., and if found, the resultant
10374 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10375 * the command string as parameters. This is perhaps a bit complicated,
10376 * but I hope it will form a happy medium between what VMS folks expect
10377 * from lib$spawn and what Unix folks expect from exec.
10380 static int vfork_called;
10382 /*{{{int my_vfork(void)*/
10393 vms_execfree(struct dsc$descriptor_s *vmscmd)
10396 if (vmscmd->dsc$a_pointer) {
10397 PerlMem_free(vmscmd->dsc$a_pointer);
10399 PerlMem_free(vmscmd);
10404 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10406 char *junk, *tmps = NULL;
10414 tmps = SvPV(really,rlen);
10416 cmdlen += rlen + 1;
10421 for (idx++; idx <= sp; idx++) {
10423 junk = SvPVx(*idx,rlen);
10424 cmdlen += rlen ? rlen + 1 : 0;
10427 Newx(PL_Cmd, cmdlen+1, char);
10429 if (tmps && *tmps) {
10430 my_strlcpy(PL_Cmd, tmps, cmdlen + 1);
10433 else *PL_Cmd = '\0';
10434 while (++mark <= sp) {
10436 char *s = SvPVx(*mark,n_a);
10438 if (*PL_Cmd) my_strlcat(PL_Cmd, " ", cmdlen+1);
10439 my_strlcat(PL_Cmd, s, cmdlen+1);
10444 } /* end of setup_argstr() */
10447 static unsigned long int
10448 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10449 struct dsc$descriptor_s **pvmscmd)
10453 char image_name[NAM$C_MAXRSS+1];
10454 char image_argv[NAM$C_MAXRSS+1];
10455 $DESCRIPTOR(defdsc,".EXE");
10456 $DESCRIPTOR(defdsc2,".");
10457 struct dsc$descriptor_s resdsc;
10458 struct dsc$descriptor_s *vmscmd;
10459 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10460 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10461 char *s, *rest, *cp, *wordbreak;
10466 vmscmd = (struct dsc$descriptor_s *)PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10467 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10469 /* vmsspec is a DCL command buffer, not just a filename */
10470 vmsspec = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10471 if (vmsspec == NULL)
10472 _ckvmssts_noperl(SS$_INSFMEM);
10474 resspec = (char *)PerlMem_malloc(VMS_MAXRSS);
10475 if (resspec == NULL)
10476 _ckvmssts_noperl(SS$_INSFMEM);
10478 /* Make a copy for modification */
10479 cmdlen = strlen(incmd);
10480 cmd = (char *)PerlMem_malloc(cmdlen+1);
10481 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10482 my_strlcpy(cmd, incmd, cmdlen + 1);
10486 resdsc.dsc$a_pointer = resspec;
10487 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10488 resdsc.dsc$b_class = DSC$K_CLASS_S;
10489 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10491 vmscmd->dsc$a_pointer = NULL;
10492 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10493 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10494 vmscmd->dsc$w_length = 0;
10495 if (pvmscmd) *pvmscmd = vmscmd;
10497 if (suggest_quote) *suggest_quote = 0;
10499 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10501 PerlMem_free(vmsspec);
10502 PerlMem_free(resspec);
10503 return CLI$_BUFOVF; /* continuation lines currently unsupported */
10508 while (*s && isspace(*s)) s++;
10510 if (*s == '@' || *s == '$') {
10511 vmsspec[0] = *s; rest = s + 1;
10512 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10514 else { cp = vmsspec; rest = s; }
10516 /* If the first word is quoted, then we need to unquote it and
10517 * escape spaces within it. We'll expand into the resspec buffer,
10518 * then copy back into the cmd buffer, expanding the latter if
10521 if (*rest == '"') {
10526 int soff = s - cmd;
10528 for (cp2 = resspec;
10529 *rest && cp2 - resspec < (VMS_MAXRSS - 1);
10532 if (*rest == ' ') { /* Escape ' ' to '^_'. */
10538 else if (*rest == '"') {
10540 if (in_quote) { /* Must be closing quote. */
10553 /* Expand the command buffer if necessary. */
10554 if (clen > cmdlen) {
10555 cmd = (char *)PerlMem_realloc(cmd, clen);
10557 _ckvmssts_noperl(SS$_INSFMEM);
10558 /* Where we are may have changed, so recompute offsets */
10559 r = cmd + (r - s - soff);
10560 rest = cmd + (rest - s - soff);
10564 /* Shift the non-verb portion of the command (if any) up or
10565 * down as necessary.
10568 memmove(rest + clen - cmdlen, rest, s - soff + cmdlen - rest);
10570 /* Copy the unquoted and escaped command verb into place. */
10571 memcpy(r, resspec, cp2 - resspec);
10574 rest = r; /* Rewind for subsequent operations. */
10577 if (*rest == '.' || *rest == '/') {
10579 for (cp2 = resspec;
10580 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10581 rest++, cp2++) *cp2 = *rest;
10583 if (int_tovmsspec(resspec, cp, 0, NULL)) {
10586 /* When a UNIX spec with no file type is translated to VMS, */
10587 /* A trailing '.' is appended under ODS-5 rules. */
10588 /* Here we do not want that trailing "." as it prevents */
10589 /* Looking for a implied ".exe" type. */
10590 if (decc_efs_charset) {
10592 i = strlen(vmsspec);
10593 if (vmsspec[i-1] == '.') {
10594 vmsspec[i-1] = '\0';
10599 for (cp2 = vmsspec + strlen(vmsspec);
10600 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10601 rest++, cp2++) *cp2 = *rest;
10606 /* Intuit whether verb (first word of cmd) is a DCL command:
10607 * - if first nonspace char is '@', it's a DCL indirection
10609 * - if verb contains a filespec separator, it's not a DCL command
10610 * - if it doesn't, caller tells us whether to default to a DCL
10611 * command, or to a local image unless told it's DCL (by leading '$')
10615 if (suggest_quote) *suggest_quote = 1;
10617 char *filespec = strpbrk(s,":<[.;");
10618 rest = wordbreak = strpbrk(s," \"\t/");
10619 if (!wordbreak) wordbreak = s + strlen(s);
10620 if (*s == '$') check_img = 0;
10621 if (filespec && (filespec < wordbreak)) isdcl = 0;
10622 else isdcl = !check_img;
10627 imgdsc.dsc$a_pointer = s;
10628 imgdsc.dsc$w_length = wordbreak - s;
10629 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10631 _ckvmssts_noperl(lib$find_file_end(&cxt));
10632 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10633 if (!(retsts & 1) && *s == '$') {
10634 _ckvmssts_noperl(lib$find_file_end(&cxt));
10635 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10636 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10638 _ckvmssts_noperl(lib$find_file_end(&cxt));
10639 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10643 _ckvmssts_noperl(lib$find_file_end(&cxt));
10648 while (*s && !isspace(*s)) s++;
10651 /* check that it's really not DCL with no file extension */
10652 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10654 char b[256] = {0,0,0,0};
10655 read(fileno(fp), b, 256);
10656 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10660 /* Check for script */
10662 if ((b[0] == '#') && (b[1] == '!'))
10664 #ifdef ALTERNATE_SHEBANG
10666 shebang_len = strlen(ALTERNATE_SHEBANG);
10667 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10669 perlstr = strstr("perl",b);
10670 if (perlstr == NULL)
10678 if (shebang_len > 0) {
10681 char tmpspec[NAM$C_MAXRSS + 1];
10684 /* Image is following after white space */
10685 /*--------------------------------------*/
10686 while (isprint(b[i]) && isspace(b[i]))
10690 while (isprint(b[i]) && !isspace(b[i])) {
10691 tmpspec[j++] = b[i++];
10692 if (j >= NAM$C_MAXRSS)
10697 /* There may be some default parameters to the image */
10698 /*---------------------------------------------------*/
10700 while (isprint(b[i])) {
10701 image_argv[j++] = b[i++];
10702 if (j >= NAM$C_MAXRSS)
10705 while ((j > 0) && !isprint(image_argv[j-1]))
10709 /* It will need to be converted to VMS format and validated */
10710 if (tmpspec[0] != '\0') {
10713 /* Try to find the exact program requested to be run */
10714 /*---------------------------------------------------*/
10715 iname = int_rmsexpand
10716 (tmpspec, image_name, ".exe",
10717 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10718 if (iname != NULL) {
10719 if (cando_by_name_int
10720 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10721 /* MCR prefix needed */
10725 /* Try again with a null type */
10726 /*----------------------------*/
10727 iname = int_rmsexpand
10728 (tmpspec, image_name, ".",
10729 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10730 if (iname != NULL) {
10731 if (cando_by_name_int
10732 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10733 /* MCR prefix needed */
10739 /* Did we find the image to run the script? */
10740 /*------------------------------------------*/
10744 /* Assume DCL or foreign command exists */
10745 /*--------------------------------------*/
10746 tchr = strrchr(tmpspec, '/');
10747 if (tchr != NULL) {
10753 my_strlcpy(image_name, tchr, sizeof(image_name));
10761 if (check_img && isdcl) {
10763 PerlMem_free(resspec);
10764 PerlMem_free(vmsspec);
10768 if (cando_by_name(S_IXUSR,0,resspec)) {
10769 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10770 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10772 my_strlcpy(vmscmd->dsc$a_pointer,"$ MCR ", MAX_DCL_LINE_LENGTH);
10773 if (image_name[0] != 0) {
10774 my_strlcat(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10775 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10777 } else if (image_name[0] != 0) {
10778 my_strlcpy(vmscmd->dsc$a_pointer, image_name, MAX_DCL_LINE_LENGTH);
10779 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10781 my_strlcpy(vmscmd->dsc$a_pointer, "@", MAX_DCL_LINE_LENGTH);
10783 if (suggest_quote) *suggest_quote = 1;
10785 /* If there is an image name, use original command */
10786 if (image_name[0] == 0)
10787 my_strlcat(vmscmd->dsc$a_pointer, resspec, MAX_DCL_LINE_LENGTH);
10790 while (*rest && isspace(*rest)) rest++;
10793 if (image_argv[0] != 0) {
10794 my_strlcat(vmscmd->dsc$a_pointer, image_argv, MAX_DCL_LINE_LENGTH);
10795 my_strlcat(vmscmd->dsc$a_pointer, " ", MAX_DCL_LINE_LENGTH);
10801 rest_len = strlen(rest);
10802 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10803 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10804 my_strlcat(vmscmd->dsc$a_pointer, rest, MAX_DCL_LINE_LENGTH);
10806 retsts = CLI$_BUFOVF;
10808 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10810 PerlMem_free(vmsspec);
10811 PerlMem_free(resspec);
10812 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10818 /* It's either a DCL command or we couldn't find a suitable image */
10819 vmscmd->dsc$w_length = strlen(cmd);
10821 vmscmd->dsc$a_pointer = (char *)PerlMem_malloc(vmscmd->dsc$w_length + 1);
10822 my_strlcpy(vmscmd->dsc$a_pointer, cmd, vmscmd->dsc$w_length + 1);
10825 PerlMem_free(resspec);
10826 PerlMem_free(vmsspec);
10828 /* check if it's a symbol (for quoting purposes) */
10829 if (suggest_quote && !*suggest_quote) {
10831 char equiv[LNM$C_NAMLENGTH];
10832 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10833 eqvdsc.dsc$a_pointer = equiv;
10835 iss = lib$get_symbol(vmscmd,&eqvdsc);
10836 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10838 if (!(retsts & 1)) {
10839 /* just hand off status values likely to be due to user error */
10840 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10841 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10842 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10843 else { _ckvmssts_noperl(retsts); }
10846 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10848 } /* end of setup_cmddsc() */
10851 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10853 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10859 if (vfork_called) { /* this follows a vfork - act Unixish */
10861 if (vfork_called < 0) {
10862 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10865 else return do_aexec(really,mark,sp);
10867 /* no vfork - act VMSish */
10868 cmd = setup_argstr(aTHX_ really,mark,sp);
10869 exec_sts = vms_do_exec(cmd);
10870 Safefree(cmd); /* Clean up from setup_argstr() */
10875 } /* end of vms_do_aexec() */
10878 /* {{{bool vms_do_exec(char *cmd) */
10880 Perl_vms_do_exec(pTHX_ const char *cmd)
10882 struct dsc$descriptor_s *vmscmd;
10884 if (vfork_called) { /* this follows a vfork - act Unixish */
10886 if (vfork_called < 0) {
10887 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10890 else return do_exec(cmd);
10893 { /* no vfork - act VMSish */
10894 unsigned long int retsts;
10897 TAINT_PROPER("exec");
10898 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10899 retsts = lib$do_command(vmscmd);
10902 case RMS$_FNF: case RMS$_DNF:
10903 set_errno(ENOENT); break;
10905 set_errno(ENOTDIR); break;
10907 set_errno(ENODEV); break;
10909 set_errno(EACCES); break;
10911 set_errno(EINVAL); break;
10912 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10913 set_errno(E2BIG); break;
10914 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10915 _ckvmssts_noperl(retsts); /* fall through */
10916 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10917 set_errno(EVMSERR);
10919 set_vaxc_errno(retsts);
10920 if (ckWARN(WARN_EXEC)) {
10921 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
10922 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
10924 vms_execfree(vmscmd);
10929 } /* end of vms_do_exec() */
10932 int do_spawn2(pTHX_ const char *, int);
10935 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
10937 unsigned long int sts;
10943 /* We'll copy the (undocumented?) Win32 behavior and allow a
10944 * numeric first argument. But the only value we'll support
10945 * through do_aspawn is a value of 1, which means spawn without
10946 * waiting for completion -- other values are ignored.
10948 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
10950 flags = SvIVx(*mark);
10953 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
10954 flags = CLI$M_NOWAIT;
10958 cmd = setup_argstr(aTHX_ really, mark, sp);
10959 sts = do_spawn2(aTHX_ cmd, flags);
10960 /* pp_sys will clean up cmd */
10964 } /* end of do_aspawn() */
10968 /* {{{int do_spawn(char* cmd) */
10970 Perl_do_spawn(pTHX_ char* cmd)
10972 PERL_ARGS_ASSERT_DO_SPAWN;
10974 return do_spawn2(aTHX_ cmd, 0);
10978 /* {{{int do_spawn_nowait(char* cmd) */
10980 Perl_do_spawn_nowait(pTHX_ char* cmd)
10982 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
10984 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
10988 /* {{{int do_spawn2(char *cmd) */
10990 do_spawn2(pTHX_ const char *cmd, int flags)
10992 unsigned long int sts, substs;
10994 /* The caller of this routine expects to Safefree(PL_Cmd) */
10995 Newx(PL_Cmd,10,char);
10998 TAINT_PROPER("spawn");
10999 if (!cmd || !*cmd) {
11000 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
11003 case RMS$_FNF: case RMS$_DNF:
11004 set_errno(ENOENT); break;
11006 set_errno(ENOTDIR); break;
11008 set_errno(ENODEV); break;
11010 set_errno(EACCES); break;
11012 set_errno(EINVAL); break;
11013 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11014 set_errno(E2BIG); break;
11015 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11016 _ckvmssts_noperl(sts); /* fall through */
11017 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11018 set_errno(EVMSERR);
11020 set_vaxc_errno(sts);
11021 if (ckWARN(WARN_EXEC)) {
11022 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
11031 if (flags & CLI$M_NOWAIT)
11034 strcpy(mode, "nW");
11036 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
11039 /* sts will be the pid in the nowait case, so leave a
11040 * hint saying not to do any bit shifting to it.
11042 if (flags & CLI$M_NOWAIT)
11043 PL_statusvalue = -1;
11046 } /* end of do_spawn2() */
11050 static unsigned int *sockflags, sockflagsize;
11053 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11054 * routines found in some versions of the CRTL can't deal with sockets.
11055 * We don't shim the other file open routines since a socket isn't
11056 * likely to be opened by a name.
11058 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11060 my_fdopen(int fd, const char *mode)
11062 FILE *fp = fdopen(fd, mode);
11065 unsigned int fdoff = fd / sizeof(unsigned int);
11066 Stat_t sbuf; /* native stat; we don't need flex_stat */
11067 if (!sockflagsize || fdoff > sockflagsize) {
11068 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
11069 else Newx (sockflags,fdoff+2,unsigned int);
11070 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11071 sockflagsize = fdoff + 2;
11073 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11074 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11083 * Clear the corresponding bit when the (possibly) socket stream is closed.
11084 * There still a small hole: we miss an implicit close which might occur
11085 * via freopen(). >> Todo
11087 /*{{{ int my_fclose(FILE *fp)*/
11089 my_fclose(FILE *fp) {
11091 unsigned int fd = fileno(fp);
11092 unsigned int fdoff = fd / sizeof(unsigned int);
11094 if (sockflagsize && fdoff < sockflagsize)
11095 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11103 * A simple fwrite replacement which outputs itmsz*nitm chars without
11104 * introducing record boundaries every itmsz chars.
11105 * We are using fputs, which depends on a terminating null. We may
11106 * well be writing binary data, so we need to accommodate not only
11107 * data with nulls sprinkled in the middle but also data with no null
11110 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11112 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11114 char *cp, *end, *cpd;
11116 unsigned int fd = fileno(dest);
11117 unsigned int fdoff = fd / sizeof(unsigned int);
11119 int bufsize = itmsz * nitm + 1;
11121 if (fdoff < sockflagsize &&
11122 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11123 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11127 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11128 memcpy( data, src, itmsz*nitm );
11129 data[itmsz*nitm] = '\0';
11131 end = data + itmsz * nitm;
11132 retval = (int) nitm; /* on success return # items written */
11135 while (cpd <= end) {
11136 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11137 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11139 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11143 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11146 } /* end of my_fwrite() */
11149 /*{{{ int my_flush(FILE *fp)*/
11151 Perl_my_flush(pTHX_ FILE *fp)
11154 if ((res = fflush(fp)) == 0 && fp) {
11155 #ifdef VMS_DO_SOCKETS
11157 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11159 res = fsync(fileno(fp));
11162 * If the flush succeeded but set end-of-file, we need to clear
11163 * the error because our caller may check ferror(). BTW, this
11164 * probably means we just flushed an empty file.
11166 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11172 /* fgetname() is not returning the correct file specifications when
11173 * decc_filename_unix_report mode is active. So we have to have it
11174 * aways return filenames in VMS mode and convert it ourselves.
11177 /*{{{ char * my_fgetname(FILE *fp, buf)*/
11179 Perl_my_fgetname(FILE *fp, char * buf) {
11183 retname = fgetname(fp, buf, 1);
11185 /* If we are in VMS mode, then we are done */
11186 if (!decc_filename_unix_report || (retname == NULL)) {
11190 /* Convert this to Unix format */
11191 vms_name = (char *)PerlMem_malloc(VMS_MAXRSS);
11192 my_strlcpy(vms_name, retname, VMS_MAXRSS);
11193 retname = int_tounixspec(vms_name, buf, NULL);
11194 PerlMem_free(vms_name);
11201 * Here are replacements for the following Unix routines in the VMS environment:
11202 * getpwuid Get information for a particular UIC or UID
11203 * getpwnam Get information for a named user
11204 * getpwent Get information for each user in the rights database
11205 * setpwent Reset search to the start of the rights database
11206 * endpwent Finish searching for users in the rights database
11208 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11209 * (defined in pwd.h), which contains the following fields:-
11211 * char *pw_name; Username (in lower case)
11212 * char *pw_passwd; Hashed password
11213 * unsigned int pw_uid; UIC
11214 * unsigned int pw_gid; UIC group number
11215 * char *pw_unixdir; Default device/directory (VMS-style)
11216 * char *pw_gecos; Owner name
11217 * char *pw_dir; Default device/directory (Unix-style)
11218 * char *pw_shell; Default CLI name (eg. DCL)
11220 * If the specified user does not exist, getpwuid and getpwnam return NULL.
11222 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11223 * not the UIC member number (eg. what's returned by getuid()),
11224 * getpwuid() can accept either as input (if uid is specified, the caller's
11225 * UIC group is used), though it won't recognise gid=0.
11227 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11228 * information about other users in your group or in other groups, respectively.
11229 * If the required privilege is not available, then these routines fill only
11230 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11233 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11236 /* sizes of various UAF record fields */
11237 #define UAI$S_USERNAME 12
11238 #define UAI$S_IDENT 31
11239 #define UAI$S_OWNER 31
11240 #define UAI$S_DEFDEV 31
11241 #define UAI$S_DEFDIR 63
11242 #define UAI$S_DEFCLI 31
11243 #define UAI$S_PWD 8
11245 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
11246 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11247 (uic).uic$v_group != UIC$K_WILD_GROUP)
11249 static char __empty[]= "";
11250 static struct passwd __passwd_empty=
11251 {(char *) __empty, (char *) __empty, 0, 0,
11252 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11253 static int contxt= 0;
11254 static struct passwd __pwdcache;
11255 static char __pw_namecache[UAI$S_IDENT+1];
11258 * This routine does most of the work extracting the user information.
11261 fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11264 unsigned char length;
11265 char pw_gecos[UAI$S_OWNER+1];
11267 static union uicdef uic;
11269 unsigned char length;
11270 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11273 unsigned char length;
11274 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11277 unsigned char length;
11278 char pw_shell[UAI$S_DEFCLI+1];
11280 static char pw_passwd[UAI$S_PWD+1];
11282 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11283 struct dsc$descriptor_s name_desc;
11284 unsigned long int sts;
11286 static struct itmlst_3 itmlst[]= {
11287 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
11288 {sizeof(uic), UAI$_UIC, &uic, &luic},
11289 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
11290 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
11291 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
11292 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
11293 {0, 0, NULL, NULL}};
11295 name_desc.dsc$w_length= strlen(name);
11296 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11297 name_desc.dsc$b_class= DSC$K_CLASS_S;
11298 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11300 /* Note that sys$getuai returns many fields as counted strings. */
11301 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11302 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11303 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11305 else { _ckvmssts(sts); }
11306 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
11308 if ((int) owner.length < lowner) lowner= (int) owner.length;
11309 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11310 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11311 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11312 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11313 owner.pw_gecos[lowner]= '\0';
11314 defdev.pw_dir[ldefdev+ldefdir]= '\0';
11315 defcli.pw_shell[ldefcli]= '\0';
11316 if (valid_uic(uic)) {
11317 pwd->pw_uid= uic.uic$l_uic;
11318 pwd->pw_gid= uic.uic$v_group;
11321 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11322 pwd->pw_passwd= pw_passwd;
11323 pwd->pw_gecos= owner.pw_gecos;
11324 pwd->pw_dir= defdev.pw_dir;
11325 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11326 pwd->pw_shell= defcli.pw_shell;
11327 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11329 ldir= strlen(pwd->pw_unixdir) - 1;
11330 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11333 my_strlcpy(pwd->pw_unixdir, pwd->pw_dir, sizeof(pwd->pw_unixdir));
11334 if (!decc_efs_case_preserve)
11335 __mystrtolower(pwd->pw_unixdir);
11340 * Get information for a named user.
11342 /*{{{struct passwd *getpwnam(char *name)*/
11344 Perl_my_getpwnam(pTHX_ const char *name)
11346 struct dsc$descriptor_s name_desc;
11348 unsigned long int sts;
11350 __pwdcache = __passwd_empty;
11351 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11352 /* We still may be able to determine pw_uid and pw_gid */
11353 name_desc.dsc$w_length= strlen(name);
11354 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11355 name_desc.dsc$b_class= DSC$K_CLASS_S;
11356 name_desc.dsc$a_pointer= (char *) name;
11357 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11358 __pwdcache.pw_uid= uic.uic$l_uic;
11359 __pwdcache.pw_gid= uic.uic$v_group;
11362 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11363 set_vaxc_errno(sts);
11364 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11367 else { _ckvmssts(sts); }
11370 my_strlcpy(__pw_namecache, name, sizeof(__pw_namecache));
11371 __pwdcache.pw_name= __pw_namecache;
11372 return &__pwdcache;
11373 } /* end of my_getpwnam() */
11377 * Get information for a particular UIC or UID.
11378 * Called by my_getpwent with uid=-1 to list all users.
11380 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11382 Perl_my_getpwuid(pTHX_ Uid_t uid)
11384 const $DESCRIPTOR(name_desc,__pw_namecache);
11385 unsigned short lname;
11387 unsigned long int status;
11389 if (uid == (unsigned int) -1) {
11391 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11392 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11393 set_vaxc_errno(status);
11394 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11398 else { _ckvmssts(status); }
11399 } while (!valid_uic (uic));
11402 uic.uic$l_uic= uid;
11403 if (!uic.uic$v_group)
11404 uic.uic$v_group= PerlProc_getgid();
11405 if (valid_uic(uic))
11406 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11407 else status = SS$_IVIDENT;
11408 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11409 status == RMS$_PRV) {
11410 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11413 else { _ckvmssts(status); }
11415 __pw_namecache[lname]= '\0';
11416 __mystrtolower(__pw_namecache);
11418 __pwdcache = __passwd_empty;
11419 __pwdcache.pw_name = __pw_namecache;
11421 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11422 The identifier's value is usually the UIC, but it doesn't have to be,
11423 so if we can, we let fillpasswd update this. */
11424 __pwdcache.pw_uid = uic.uic$l_uic;
11425 __pwdcache.pw_gid = uic.uic$v_group;
11427 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11428 return &__pwdcache;
11430 } /* end of my_getpwuid() */
11434 * Get information for next user.
11436 /*{{{struct passwd *my_getpwent()*/
11438 Perl_my_getpwent(pTHX)
11440 return (my_getpwuid((unsigned int) -1));
11445 * Finish searching rights database for users.
11447 /*{{{void my_endpwent()*/
11449 Perl_my_endpwent(pTHX)
11452 _ckvmssts(sys$finish_rdb(&contxt));
11458 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11459 * my_utime(), and flex_stat(), all of which operate on UTC unless
11460 * VMSISH_TIMES is true.
11462 /* method used to handle UTC conversions:
11463 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
11465 static int gmtime_emulation_type;
11466 /* number of secs to add to UTC POSIX-style time to get local time */
11467 static long int utc_offset_secs;
11469 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11470 * in vmsish.h. #undef them here so we can call the CRTL routines
11478 static time_t toutc_dst(time_t loc) {
11481 if ((rsltmp = localtime(&loc)) == NULL) return -1u;
11482 loc -= utc_offset_secs;
11483 if (rsltmp->tm_isdst) loc -= 3600;
11486 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11487 ((gmtime_emulation_type || my_time(NULL)), \
11488 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11489 ((secs) - utc_offset_secs))))
11491 static time_t toloc_dst(time_t utc) {
11494 utc += utc_offset_secs;
11495 if ((rsltmp = localtime(&utc)) == NULL) return -1u;
11496 if (rsltmp->tm_isdst) utc += 3600;
11499 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11500 ((gmtime_emulation_type || my_time(NULL)), \
11501 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11502 ((secs) + utc_offset_secs))))
11504 /* my_time(), my_localtime(), my_gmtime()
11505 * By default traffic in UTC time values, using CRTL gmtime() or
11506 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11507 * Note: We need to use these functions even when the CRTL has working
11508 * UTC support, since they also handle C<use vmsish qw(times);>
11510 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
11511 * Modified by Charles Bailey <bailey@newman.upenn.edu>
11514 /*{{{time_t my_time(time_t *timep)*/
11516 Perl_my_time(pTHX_ time_t *timep)
11521 if (gmtime_emulation_type == 0) {
11522 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
11523 /* results of calls to gmtime() and localtime() */
11524 /* for same &base */
11526 gmtime_emulation_type++;
11527 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11528 char off[LNM$C_NAMLENGTH+1];;
11530 gmtime_emulation_type++;
11531 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11532 gmtime_emulation_type++;
11533 utc_offset_secs = 0;
11534 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11536 else { utc_offset_secs = atol(off); }
11538 else { /* We've got a working gmtime() */
11539 struct tm gmt, local;
11542 tm_p = localtime(&base);
11544 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
11545 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11546 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
11547 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
11552 # ifdef VMSISH_TIME
11553 if (VMSISH_TIME) when = _toloc(when);
11555 if (timep != NULL) *timep = when;
11558 } /* end of my_time() */
11562 /*{{{struct tm *my_gmtime(const time_t *timep)*/
11564 Perl_my_gmtime(pTHX_ const time_t *timep)
11569 if (timep == NULL) {
11570 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11573 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11576 # ifdef VMSISH_TIME
11577 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11579 return gmtime(&when);
11580 } /* end of my_gmtime() */
11584 /*{{{struct tm *my_localtime(const time_t *timep)*/
11586 Perl_my_localtime(pTHX_ const time_t *timep)
11590 if (timep == NULL) {
11591 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11594 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11595 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
11598 # ifdef VMSISH_TIME
11599 if (VMSISH_TIME) when = _toutc(when);
11601 /* CRTL localtime() wants UTC as input, does tz correction itself */
11602 return localtime(&when);
11603 } /* end of my_localtime() */
11606 /* Reset definitions for later calls */
11607 #define gmtime(t) my_gmtime(t)
11608 #define localtime(t) my_localtime(t)
11609 #define time(t) my_time(t)
11612 /* my_utime - update modification/access time of a file
11614 * VMS 7.3 and later implementation
11615 * Only the UTC translation is home-grown. The rest is handled by the
11616 * CRTL utime(), which will take into account the relevant feature
11617 * logicals and ODS-5 volume characteristics for true access times.
11619 * pre VMS 7.3 implementation:
11620 * The calling sequence is identical to POSIX utime(), but under
11621 * VMS with ODS-2, only the modification time is changed; ODS-2 does
11622 * not maintain access times. Restrictions differ from the POSIX
11623 * definition in that the time can be changed as long as the
11624 * caller has permission to execute the necessary IO$_MODIFY $QIO;
11625 * no separate checks are made to insure that the caller is the
11626 * owner of the file or has special privs enabled.
11627 * Code here is based on Joe Meadows' FILE utility.
11631 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11632 * to VMS epoch (01-JAN-1858 00:00:00.00)
11633 * in 100 ns intervals.
11635 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11637 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11639 Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
11641 #if __CRTL_VER >= 70300000
11642 struct utimbuf utc_utimes, *utc_utimesp;
11644 if (utimes != NULL) {
11645 utc_utimes.actime = utimes->actime;
11646 utc_utimes.modtime = utimes->modtime;
11647 # ifdef VMSISH_TIME
11648 /* If input was local; convert to UTC for sys svc */
11650 utc_utimes.actime = _toutc(utimes->actime);
11651 utc_utimes.modtime = _toutc(utimes->modtime);
11654 utc_utimesp = &utc_utimes;
11657 utc_utimesp = NULL;
11660 return utime(file, utc_utimesp);
11662 #else /* __CRTL_VER < 70300000 */
11666 long int bintime[2], len = 2, lowbit, unixtime,
11667 secscale = 10000000; /* seconds --> 100 ns intervals */
11668 unsigned long int chan, iosb[2], retsts;
11669 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
11670 struct FAB myfab = cc$rms_fab;
11671 struct NAM mynam = cc$rms_nam;
11672 #if defined (__DECC) && defined (__VAX)
11673 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
11674 * at least through VMS V6.1, which causes a type-conversion warning.
11676 # pragma message save
11677 # pragma message disable cvtdiftypes
11679 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
11680 struct fibdef myfib;
11681 #if defined (__DECC) && defined (__VAX)
11682 /* This should be right after the declaration of myatr, but due
11683 * to a bug in VAX DEC C, this takes effect a statement early.
11685 # pragma message restore
11687 /* cast ok for read only parameter */
11688 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
11689 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
11690 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
11692 if (file == NULL || *file == '\0') {
11693 SETERRNO(ENOENT, LIB$_INVARG);
11697 /* Convert to VMS format ensuring that it will fit in 255 characters */
11698 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
11699 SETERRNO(ENOENT, LIB$_INVARG);
11702 if (utimes != NULL) {
11703 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
11704 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
11705 * Since time_t is unsigned long int, and lib$emul takes a signed long int
11706 * as input, we force the sign bit to be clear by shifting unixtime right
11707 * one bit, then multiplying by an extra factor of 2 in lib$emul().
11709 lowbit = (utimes->modtime & 1) ? secscale : 0;
11710 unixtime = (long int) utimes->modtime;
11711 # ifdef VMSISH_TIME
11712 /* If input was UTC; convert to local for sys svc */
11713 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
11715 unixtime >>= 1; secscale <<= 1;
11716 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
11717 if (!(retsts & 1)) {
11718 SETERRNO(EVMSERR, retsts);
11721 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
11722 if (!(retsts & 1)) {
11723 SETERRNO(EVMSERR, retsts);
11728 /* Just get the current time in VMS format directly */
11729 retsts = sys$gettim(bintime);
11730 if (!(retsts & 1)) {
11731 SETERRNO(EVMSERR, retsts);
11736 myfab.fab$l_fna = vmsspec;
11737 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
11738 myfab.fab$l_nam = &mynam;
11739 mynam.nam$l_esa = esa;
11740 mynam.nam$b_ess = (unsigned char) sizeof esa;
11741 mynam.nam$l_rsa = rsa;
11742 mynam.nam$b_rss = (unsigned char) sizeof rsa;
11743 if (decc_efs_case_preserve)
11744 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
11746 /* Look for the file to be affected, letting RMS parse the file
11747 * specification for us as well. I have set errno using only
11748 * values documented in the utime() man page for VMS POSIX.
11750 retsts = sys$parse(&myfab,0,0);
11751 if (!(retsts & 1)) {
11752 set_vaxc_errno(retsts);
11753 if (retsts == RMS$_PRV) set_errno(EACCES);
11754 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
11755 else set_errno(EVMSERR);
11758 retsts = sys$search(&myfab,0,0);
11759 if (!(retsts & 1)) {
11760 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11761 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11762 set_vaxc_errno(retsts);
11763 if (retsts == RMS$_PRV) set_errno(EACCES);
11764 else if (retsts == RMS$_FNF) set_errno(ENOENT);
11765 else set_errno(EVMSERR);
11769 devdsc.dsc$w_length = mynam.nam$b_dev;
11770 /* cast ok for read only parameter */
11771 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
11773 retsts = sys$assign(&devdsc,&chan,0,0);
11774 if (!(retsts & 1)) {
11775 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11776 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11777 set_vaxc_errno(retsts);
11778 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
11779 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
11780 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
11781 else set_errno(EVMSERR);
11785 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
11786 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
11788 memset((void *) &myfib, 0, sizeof myfib);
11789 #if defined(__DECC) || defined(__DECCXX)
11790 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
11791 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
11792 /* This prevents the revision time of the file being reset to the current
11793 * time as a result of our IO$_MODIFY $QIO. */
11794 myfib.fib$l_acctl = FIB$M_NORECORD;
11796 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
11797 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
11798 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
11800 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
11801 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11802 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11803 _ckvmssts(sys$dassgn(chan));
11804 if (retsts & 1) retsts = iosb[0];
11805 if (!(retsts & 1)) {
11806 set_vaxc_errno(retsts);
11807 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11808 else set_errno(EVMSERR);
11814 #endif /* #if __CRTL_VER >= 70300000 */
11816 } /* end of my_utime() */
11820 * flex_stat, flex_lstat, flex_fstat
11821 * basic stat, but gets it right when asked to stat
11822 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
11825 #ifndef _USE_STD_STAT
11826 /* encode_dev packs a VMS device name string into an integer to allow
11827 * simple comparisons. This can be used, for example, to check whether two
11828 * files are located on the same device, by comparing their encoded device
11829 * names. Even a string comparison would not do, because stat() reuses the
11830 * device name buffer for each call; so without encode_dev, it would be
11831 * necessary to save the buffer and use strcmp (this would mean a number of
11832 * changes to the standard Perl code, to say nothing of what a Perl script
11833 * would have to do.
11835 * The device lock id, if it exists, should be unique (unless perhaps compared
11836 * with lock ids transferred from other nodes). We have a lock id if the disk is
11837 * mounted cluster-wide, which is when we tend to get long (host-qualified)
11838 * device names. Thus we use the lock id in preference, and only if that isn't
11839 * available, do we try to pack the device name into an integer (flagged by
11840 * the sign bit (LOCKID_MASK) being set).
11842 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
11843 * name and its encoded form, but it seems very unlikely that we will find
11844 * two files on different disks that share the same encoded device names,
11845 * and even more remote that they will share the same file id (if the test
11846 * is to check for the same file).
11848 * A better method might be to use sys$device_scan on the first call, and to
11849 * search for the device, returning an index into the cached array.
11850 * The number returned would be more intelligible.
11851 * This is probably not worth it, and anyway would take quite a bit longer
11852 * on the first call.
11854 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
11856 encode_dev (pTHX_ const char *dev)
11859 unsigned long int f;
11864 if (!dev || !dev[0]) return 0;
11868 struct dsc$descriptor_s dev_desc;
11869 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
11871 /* For cluster-mounted disks, the disk lock identifier is unique, so we
11872 can try that first. */
11873 dev_desc.dsc$w_length = strlen (dev);
11874 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
11875 dev_desc.dsc$b_class = DSC$K_CLASS_S;
11876 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
11877 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
11878 if (!$VMS_STATUS_SUCCESS(status)) {
11880 case SS$_NOSUCHDEV:
11881 SETERRNO(ENODEV, status);
11887 if (lockid) return (lockid & ~LOCKID_MASK);
11891 /* Otherwise we try to encode the device name */
11895 for (q = dev + strlen(dev); q--; q >= dev) {
11900 else if (isalpha (toupper (*q)))
11901 c= toupper (*q) - 'A' + (char)10;
11903 continue; /* Skip '$'s */
11905 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
11907 enc += f * (unsigned long int) c;
11909 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
11911 } /* end of encode_dev() */
11912 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11913 device_no = encode_dev(aTHX_ devname)
11915 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11916 device_no = new_dev_no
11920 is_null_device(const char *name)
11922 if (decc_bug_devnull != 0) {
11923 if (strncmp("/dev/null", name, 9) == 0)
11926 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
11927 The underscore prefix, controller letter, and unit number are
11928 independently optional; for our purposes, the colon punctuation
11929 is not. The colon can be trailed by optional directory and/or
11930 filename, but two consecutive colons indicates a nodename rather
11931 than a device. [pr] */
11932 if (*name == '_') ++name;
11933 if (tolower(*name++) != 'n') return 0;
11934 if (tolower(*name++) != 'l') return 0;
11935 if (tolower(*name) == 'a') ++name;
11936 if (*name == '0') ++name;
11937 return (*name++ == ':') && (*name != ':');
11941 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
11943 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
11946 Perl_cando_by_name_int(pTHX_ I32 bit, bool effective, const char *fname, int opts)
11948 char usrname[L_cuserid];
11949 struct dsc$descriptor_s usrdsc =
11950 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
11951 char *vmsname = NULL, *fileified = NULL;
11952 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
11953 unsigned short int retlen, trnlnm_iter_count;
11954 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11955 union prvdef curprv;
11956 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
11957 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
11958 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
11959 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
11960 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
11962 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
11964 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11966 static int profile_context = -1;
11968 if (!fname || !*fname) return FALSE;
11970 /* Make sure we expand logical names, since sys$check_access doesn't */
11971 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
11972 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11973 if (!strpbrk(fname,"/]>:")) {
11974 my_strlcpy(fileified, fname, VMS_MAXRSS);
11975 trnlnm_iter_count = 0;
11976 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
11977 trnlnm_iter_count++;
11978 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
11983 vmsname = (char *)PerlMem_malloc(VMS_MAXRSS);
11984 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11985 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
11986 /* Don't know if already in VMS format, so make sure */
11987 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
11988 PerlMem_free(fileified);
11989 PerlMem_free(vmsname);
11994 my_strlcpy(vmsname, fname, VMS_MAXRSS);
11997 /* sys$check_access needs a file spec, not a directory spec.
11998 * flex_stat now will handle a null thread context during startup.
12001 retlen = namdsc.dsc$w_length = strlen(vmsname);
12002 if (vmsname[retlen-1] == ']'
12003 || vmsname[retlen-1] == '>'
12004 || vmsname[retlen-1] == ':'
12005 || (!flex_stat_int(vmsname, &st, 1) &&
12006 S_ISDIR(st.st_mode))) {
12008 if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
12009 PerlMem_free(fileified);
12010 PerlMem_free(vmsname);
12019 retlen = namdsc.dsc$w_length = strlen(fname);
12020 namdsc.dsc$a_pointer = (char *)fname;
12023 case S_IXUSR: case S_IXGRP: case S_IXOTH:
12024 access = ARM$M_EXECUTE;
12025 flags = CHP$M_READ;
12027 case S_IRUSR: case S_IRGRP: case S_IROTH:
12028 access = ARM$M_READ;
12029 flags = CHP$M_READ | CHP$M_USEREADALL;
12031 case S_IWUSR: case S_IWGRP: case S_IWOTH:
12032 access = ARM$M_WRITE;
12033 flags = CHP$M_READ | CHP$M_WRITE;
12035 case S_IDUSR: case S_IDGRP: case S_IDOTH:
12036 access = ARM$M_DELETE;
12037 flags = CHP$M_READ | CHP$M_WRITE;
12040 if (fileified != NULL)
12041 PerlMem_free(fileified);
12042 if (vmsname != NULL)
12043 PerlMem_free(vmsname);
12047 /* Before we call $check_access, create a user profile with the current
12048 * process privs since otherwise it just uses the default privs from the
12049 * UAF and might give false positives or negatives. This only works on
12050 * VMS versions v6.0 and later since that's when sys$create_user_profile
12051 * became available.
12054 /* get current process privs and username */
12055 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
12056 _ckvmssts_noperl(iosb[0]);
12058 /* find out the space required for the profile */
12059 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
12060 &usrprodsc.dsc$w_length,&profile_context));
12062 /* allocate space for the profile and get it filled in */
12063 usrprodsc.dsc$a_pointer = (char *)PerlMem_malloc(usrprodsc.dsc$w_length);
12064 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12065 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
12066 &usrprodsc.dsc$w_length,&profile_context));
12068 /* use the profile to check access to the file; free profile & analyze results */
12069 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
12070 PerlMem_free(usrprodsc.dsc$a_pointer);
12071 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
12073 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
12074 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
12075 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
12076 set_vaxc_errno(retsts);
12077 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12078 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12079 else set_errno(ENOENT);
12080 if (fileified != NULL)
12081 PerlMem_free(fileified);
12082 if (vmsname != NULL)
12083 PerlMem_free(vmsname);
12086 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12087 if (fileified != NULL)
12088 PerlMem_free(fileified);
12089 if (vmsname != NULL)
12090 PerlMem_free(vmsname);
12093 _ckvmssts_noperl(retsts);
12095 if (fileified != NULL)
12096 PerlMem_free(fileified);
12097 if (vmsname != NULL)
12098 PerlMem_free(vmsname);
12099 return FALSE; /* Should never get here */
12103 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
12104 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12105 * subset of the applicable information.
12108 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12110 return cando_by_name_int
12111 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12112 } /* end of cando() */
12116 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12118 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12120 return cando_by_name_int(bit, effective, fname, 0);
12122 } /* end of cando_by_name() */
12126 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12128 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12130 dSAVE_ERRNO; /* fstat may set this even on success */
12131 if (!fstat(fd, &statbufp->crtl_stat)) {
12133 char *vms_filename;
12134 vms_filename = (char *)PerlMem_malloc(VMS_MAXRSS);
12135 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12137 /* Save name for cando by name in VMS format */
12138 cptr = getname(fd, vms_filename, 1);
12140 /* This should not happen, but just in case */
12141 if (cptr == NULL) {
12142 statbufp->st_devnam[0] = 0;
12145 /* Make sure that the saved name fits in 255 characters */
12146 cptr = int_rmsexpand_vms
12148 statbufp->st_devnam,
12151 statbufp->st_devnam[0] = 0;
12153 PerlMem_free(vms_filename);
12155 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12157 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12159 # ifdef VMSISH_TIME
12161 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12162 statbufp->st_atime = _toloc(statbufp->st_atime);
12163 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12171 } /* end of flex_fstat() */
12175 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12177 char *temp_fspec = NULL;
12178 char *fileified = NULL;
12179 const char *save_spec;
12183 char already_fileified = 0;
12191 if (decc_bug_devnull != 0) {
12192 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12193 memset(statbufp,0,sizeof *statbufp);
12194 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12195 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12196 statbufp->st_uid = 0x00010001;
12197 statbufp->st_gid = 0x0001;
12198 time((time_t *)&statbufp->st_mtime);
12199 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12206 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12208 * If we are in POSIX filespec mode, accept the filename as is.
12210 if (decc_posix_compliant_pathnames == 0) {
12213 /* Try for a simple stat first. If fspec contains a filename without
12214 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12215 * and sea:[wine.dark]water. exist, the CRTL prefers the directory here.
12216 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12217 * not sea:[wine.dark]., if the latter exists. If the intended target is
12218 * the file with null type, specify this by calling flex_stat() with
12219 * a '.' at the end of fspec.
12222 if (lstat_flag == 0)
12223 retval = stat(fspec, &statbufp->crtl_stat);
12225 retval = lstat(fspec, &statbufp->crtl_stat);
12231 /* In the odd case where we have write but not read access
12232 * to a directory, stat('foo.DIR') works but stat('foo') doesn't.
12234 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12235 if (fileified == NULL)
12236 _ckvmssts_noperl(SS$_INSFMEM);
12238 ret_spec = int_fileify_dirspec(fspec, fileified, NULL);
12239 if (ret_spec != NULL) {
12240 if (lstat_flag == 0)
12241 retval = stat(fileified, &statbufp->crtl_stat);
12243 retval = lstat(fileified, &statbufp->crtl_stat);
12244 save_spec = fileified;
12245 already_fileified = 1;
12249 if (retval && vms_bug_stat_filename) {
12251 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
12252 if (temp_fspec == NULL)
12253 _ckvmssts_noperl(SS$_INSFMEM);
12255 /* We should try again as a vmsified file specification. */
12257 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12258 if (ret_spec != NULL) {
12259 if (lstat_flag == 0)
12260 retval = stat(temp_fspec, &statbufp->crtl_stat);
12262 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12263 save_spec = temp_fspec;
12268 /* Last chance - allow multiple dots without EFS CHARSET */
12269 /* The CRTL stat() falls down hard on multi-dot filenames in unix
12270 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12271 * enable it if it isn't already.
12273 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12274 if (!decc_efs_charset && (decc_efs_charset_index > 0))
12275 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12277 if (lstat_flag == 0)
12278 retval = stat(fspec, &statbufp->crtl_stat);
12280 retval = lstat(fspec, &statbufp->crtl_stat);
12282 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12283 if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12284 decc$feature_set_value(decc_efs_charset_index, 1, 0);
12290 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12292 if (lstat_flag == 0)
12293 retval = stat(temp_fspec, &statbufp->crtl_stat);
12295 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12296 save_spec = temp_fspec;
12300 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12301 /* As you were... */
12302 if (!decc_efs_charset)
12303 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12308 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12310 /* If this is an lstat, do not follow the link */
12312 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12314 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12315 /* If we used the efs_hack above, we must also use it here for */
12316 /* perl_cando to work */
12317 if (efs_hack && (decc_efs_charset_index > 0)) {
12318 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12322 /* If we've got a directory, save a fileified, expanded version of it
12323 * in st_devnam. If not a directory, just an expanded version.
12325 if (S_ISDIR(statbufp->st_mode) && !already_fileified) {
12326 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
12327 if (fileified == NULL)
12328 _ckvmssts_noperl(SS$_INSFMEM);
12330 cptr = do_fileify_dirspec(save_spec, fileified, 0, NULL);
12332 save_spec = fileified;
12335 cptr = int_rmsexpand(save_spec,
12336 statbufp->st_devnam,
12342 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12343 if (efs_hack && (decc_efs_charset_index > 0)) {
12344 decc$feature_set_value(decc_efs_charset, 1, 0);
12348 /* Fix me: If this is NULL then stat found a file, and we could */
12349 /* not convert the specification to VMS - Should never happen */
12351 statbufp->st_devnam[0] = 0;
12353 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12355 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12356 # ifdef VMSISH_TIME
12358 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12359 statbufp->st_atime = _toloc(statbufp->st_atime);
12360 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12364 /* If we were successful, leave errno where we found it */
12365 if (retval == 0) RESTORE_ERRNO;
12367 PerlMem_free(temp_fspec);
12369 PerlMem_free(fileified);
12372 } /* end of flex_stat_int() */
12375 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12377 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12379 return flex_stat_int(fspec, statbufp, 0);
12383 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12385 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12387 return flex_stat_int(fspec, statbufp, 1);
12392 /*{{{char *my_getlogin()*/
12393 /* VMS cuserid == Unix getlogin, except calling sequence */
12397 static char user[L_cuserid];
12398 return cuserid(user);
12403 /* rmscopy - copy a file using VMS RMS routines
12405 * Copies contents and attributes of spec_in to spec_out, except owner
12406 * and protection information. Name and type of spec_in are used as
12407 * defaults for spec_out. The third parameter specifies whether rmscopy()
12408 * should try to propagate timestamps from the input file to the output file.
12409 * If it is less than 0, no timestamps are preserved. If it is 0, then
12410 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12411 * propagated to the output file at creation iff the output file specification
12412 * did not contain an explicit name or type, and the revision date is always
12413 * updated at the end of the copy operation. If it is greater than 0, then
12414 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12415 * other than the revision date should be propagated, and bit 1 indicates
12416 * that the revision date should be propagated.
12418 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12420 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12421 * Incorporates, with permission, some code from EZCOPY by Tim Adye
12422 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12423 * as part of the Perl standard distribution under the terms of the
12424 * GNU General Public License or the Perl Artistic License. Copies
12425 * of each may be found in the Perl standard distribution.
12427 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12429 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12431 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12432 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12433 unsigned long int sts;
12435 struct FAB fab_in, fab_out;
12436 struct RAB rab_in, rab_out;
12437 rms_setup_nam(nam);
12438 rms_setup_nam(nam_out);
12439 struct XABDAT xabdat;
12440 struct XABFHC xabfhc;
12441 struct XABRDT xabrdt;
12442 struct XABSUM xabsum;
12444 vmsin = (char *)PerlMem_malloc(VMS_MAXRSS);
12445 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12446 vmsout = (char *)PerlMem_malloc(VMS_MAXRSS);
12447 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12448 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12449 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
12450 PerlMem_free(vmsin);
12451 PerlMem_free(vmsout);
12452 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12456 esa = (char *)PerlMem_malloc(VMS_MAXRSS);
12457 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12459 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12460 esal = (char *)PerlMem_malloc(VMS_MAXRSS);
12461 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12463 fab_in = cc$rms_fab;
12464 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12465 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12466 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12467 fab_in.fab$l_fop = FAB$M_SQO;
12468 rms_bind_fab_nam(fab_in, nam);
12469 fab_in.fab$l_xab = (void *) &xabdat;
12471 rsa = (char *)PerlMem_malloc(VMS_MAXRSS);
12472 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12474 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12475 rsal = (char *)PerlMem_malloc(VMS_MAXRSS);
12476 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12478 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12479 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12480 rms_nam_esl(nam) = 0;
12481 rms_nam_rsl(nam) = 0;
12482 rms_nam_esll(nam) = 0;
12483 rms_nam_rsll(nam) = 0;
12484 #ifdef NAM$M_NO_SHORT_UPCASE
12485 if (decc_efs_case_preserve)
12486 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
12489 xabdat = cc$rms_xabdat; /* To get creation date */
12490 xabdat.xab$l_nxt = (void *) &xabfhc;
12492 xabfhc = cc$rms_xabfhc; /* To get record length */
12493 xabfhc.xab$l_nxt = (void *) &xabsum;
12495 xabsum = cc$rms_xabsum; /* To get key and area information */
12497 if (!((sts = sys$open(&fab_in)) & 1)) {
12498 PerlMem_free(vmsin);
12499 PerlMem_free(vmsout);
12502 PerlMem_free(esal);
12505 PerlMem_free(rsal);
12506 set_vaxc_errno(sts);
12508 case RMS$_FNF: case RMS$_DNF:
12509 set_errno(ENOENT); break;
12511 set_errno(ENOTDIR); break;
12513 set_errno(ENODEV); break;
12515 set_errno(EINVAL); break;
12517 set_errno(EACCES); break;
12519 set_errno(EVMSERR);
12526 fab_out.fab$w_ifi = 0;
12527 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12528 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12529 fab_out.fab$l_fop = FAB$M_SQO;
12530 rms_bind_fab_nam(fab_out, nam_out);
12531 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12532 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12533 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
12534 esa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12535 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12536 rsa_out = (char *)PerlMem_malloc(NAM$C_MAXRSS + 1);
12537 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12540 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12541 esal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12542 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12543 rsal_out = (char *)PerlMem_malloc(VMS_MAXRSS);
12544 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12546 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
12547 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
12549 if (preserve_dates == 0) { /* Act like DCL COPY */
12550 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
12551 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
12552 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
12553 PerlMem_free(vmsin);
12554 PerlMem_free(vmsout);
12557 PerlMem_free(esal);
12560 PerlMem_free(rsal);
12561 PerlMem_free(esa_out);
12562 if (esal_out != NULL)
12563 PerlMem_free(esal_out);
12564 PerlMem_free(rsa_out);
12565 if (rsal_out != NULL)
12566 PerlMem_free(rsal_out);
12567 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12568 set_vaxc_errno(sts);
12571 fab_out.fab$l_xab = (void *) &xabdat;
12572 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12573 preserve_dates = 1;
12575 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
12576 preserve_dates =0; /* bitmask from this point forward */
12578 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
12579 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
12580 PerlMem_free(vmsin);
12581 PerlMem_free(vmsout);
12584 PerlMem_free(esal);
12587 PerlMem_free(rsal);
12588 PerlMem_free(esa_out);
12589 if (esal_out != NULL)
12590 PerlMem_free(esal_out);
12591 PerlMem_free(rsa_out);
12592 if (rsal_out != NULL)
12593 PerlMem_free(rsal_out);
12594 set_vaxc_errno(sts);
12597 set_errno(ENOENT); break;
12599 set_errno(ENOTDIR); break;
12601 set_errno(ENODEV); break;
12603 set_errno(EINVAL); break;
12605 set_errno(EACCES); break;
12607 set_errno(EVMSERR);
12611 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
12612 if (preserve_dates & 2) {
12613 /* sys$close() will process xabrdt, not xabdat */
12614 xabrdt = cc$rms_xabrdt;
12616 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12618 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
12619 * is unsigned long[2], while DECC & VAXC use a struct */
12620 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
12622 fab_out.fab$l_xab = (void *) &xabrdt;
12625 ubf = (char *)PerlMem_malloc(32256);
12626 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12627 rab_in = cc$rms_rab;
12628 rab_in.rab$l_fab = &fab_in;
12629 rab_in.rab$l_rop = RAB$M_BIO;
12630 rab_in.rab$l_ubf = ubf;
12631 rab_in.rab$w_usz = 32256;
12632 if (!((sts = sys$connect(&rab_in)) & 1)) {
12633 sys$close(&fab_in); sys$close(&fab_out);
12634 PerlMem_free(vmsin);
12635 PerlMem_free(vmsout);
12639 PerlMem_free(esal);
12642 PerlMem_free(rsal);
12643 PerlMem_free(esa_out);
12644 if (esal_out != NULL)
12645 PerlMem_free(esal_out);
12646 PerlMem_free(rsa_out);
12647 if (rsal_out != NULL)
12648 PerlMem_free(rsal_out);
12649 set_errno(EVMSERR); set_vaxc_errno(sts);
12653 rab_out = cc$rms_rab;
12654 rab_out.rab$l_fab = &fab_out;
12655 rab_out.rab$l_rbf = ubf;
12656 if (!((sts = sys$connect(&rab_out)) & 1)) {
12657 sys$close(&fab_in); sys$close(&fab_out);
12658 PerlMem_free(vmsin);
12659 PerlMem_free(vmsout);
12663 PerlMem_free(esal);
12666 PerlMem_free(rsal);
12667 PerlMem_free(esa_out);
12668 if (esal_out != NULL)
12669 PerlMem_free(esal_out);
12670 PerlMem_free(rsa_out);
12671 if (rsal_out != NULL)
12672 PerlMem_free(rsal_out);
12673 set_errno(EVMSERR); set_vaxc_errno(sts);
12677 while ((sts = sys$read(&rab_in))) { /* always true */
12678 if (sts == RMS$_EOF) break;
12679 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12680 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
12681 sys$close(&fab_in); sys$close(&fab_out);
12682 PerlMem_free(vmsin);
12683 PerlMem_free(vmsout);
12687 PerlMem_free(esal);
12690 PerlMem_free(rsal);
12691 PerlMem_free(esa_out);
12692 if (esal_out != NULL)
12693 PerlMem_free(esal_out);
12694 PerlMem_free(rsa_out);
12695 if (rsal_out != NULL)
12696 PerlMem_free(rsal_out);
12697 set_errno(EVMSERR); set_vaxc_errno(sts);
12703 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
12704 sys$close(&fab_in); sys$close(&fab_out);
12705 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
12707 PerlMem_free(vmsin);
12708 PerlMem_free(vmsout);
12712 PerlMem_free(esal);
12715 PerlMem_free(rsal);
12716 PerlMem_free(esa_out);
12717 if (esal_out != NULL)
12718 PerlMem_free(esal_out);
12719 PerlMem_free(rsa_out);
12720 if (rsal_out != NULL)
12721 PerlMem_free(rsal_out);
12724 set_errno(EVMSERR); set_vaxc_errno(sts);
12730 } /* end of rmscopy() */
12734 /*** The following glue provides 'hooks' to make some of the routines
12735 * from this file available from Perl. These routines are sufficiently
12736 * basic, and are required sufficiently early in the build process,
12737 * that's it's nice to have them available to miniperl as well as the
12738 * full Perl, so they're set up here instead of in an extension. The
12739 * Perl code which handles importation of these names into a given
12740 * package lives in [.VMS]Filespec.pm in @INC.
12744 rmsexpand_fromperl(pTHX_ CV *cv)
12747 char *fspec, *defspec = NULL, *rslt;
12749 int fs_utf8, dfs_utf8;
12753 if (!items || items > 2)
12754 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
12755 fspec = SvPV(ST(0),n_a);
12756 fs_utf8 = SvUTF8(ST(0));
12757 if (!fspec || !*fspec) XSRETURN_UNDEF;
12759 defspec = SvPV(ST(1),n_a);
12760 dfs_utf8 = SvUTF8(ST(1));
12762 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
12763 ST(0) = sv_newmortal();
12764 if (rslt != NULL) {
12765 sv_usepvn(ST(0),rslt,strlen(rslt));
12774 vmsify_fromperl(pTHX_ CV *cv)
12781 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
12782 utf8_fl = SvUTF8(ST(0));
12783 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12784 ST(0) = sv_newmortal();
12785 if (vmsified != NULL) {
12786 sv_usepvn(ST(0),vmsified,strlen(vmsified));
12795 unixify_fromperl(pTHX_ CV *cv)
12802 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
12803 utf8_fl = SvUTF8(ST(0));
12804 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12805 ST(0) = sv_newmortal();
12806 if (unixified != NULL) {
12807 sv_usepvn(ST(0),unixified,strlen(unixified));
12816 fileify_fromperl(pTHX_ CV *cv)
12823 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
12824 utf8_fl = SvUTF8(ST(0));
12825 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12826 ST(0) = sv_newmortal();
12827 if (fileified != NULL) {
12828 sv_usepvn(ST(0),fileified,strlen(fileified));
12837 pathify_fromperl(pTHX_ CV *cv)
12844 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
12845 utf8_fl = SvUTF8(ST(0));
12846 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12847 ST(0) = sv_newmortal();
12848 if (pathified != NULL) {
12849 sv_usepvn(ST(0),pathified,strlen(pathified));
12858 vmspath_fromperl(pTHX_ CV *cv)
12865 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
12866 utf8_fl = SvUTF8(ST(0));
12867 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12868 ST(0) = sv_newmortal();
12869 if (vmspath != NULL) {
12870 sv_usepvn(ST(0),vmspath,strlen(vmspath));
12879 unixpath_fromperl(pTHX_ CV *cv)
12886 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
12887 utf8_fl = SvUTF8(ST(0));
12888 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12889 ST(0) = sv_newmortal();
12890 if (unixpath != NULL) {
12891 sv_usepvn(ST(0),unixpath,strlen(unixpath));
12900 candelete_fromperl(pTHX_ CV *cv)
12908 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
12910 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12911 Newx(fspec, VMS_MAXRSS, char);
12912 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
12913 if (isGV_with_GP(mysv)) {
12914 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
12915 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12923 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
12924 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12931 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
12937 rmscopy_fromperl(pTHX_ CV *cv)
12940 char *inspec, *outspec, *inp, *outp;
12946 if (items < 2 || items > 3)
12947 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
12949 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12950 Newx(inspec, VMS_MAXRSS, char);
12951 if (isGV_with_GP(mysv)) {
12952 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
12953 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12954 ST(0) = sv_2mortal(newSViv(0));
12961 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
12962 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12963 ST(0) = sv_2mortal(newSViv(0));
12968 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
12969 Newx(outspec, VMS_MAXRSS, char);
12970 if (isGV_with_GP(mysv)) {
12971 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
12972 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12973 ST(0) = sv_2mortal(newSViv(0));
12981 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
12982 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12983 ST(0) = sv_2mortal(newSViv(0));
12989 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
12991 ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag)));
12997 /* The mod2fname is limited to shorter filenames by design, so it should
12998 * not be modified to support longer EFS pathnames
13001 mod2fname(pTHX_ CV *cv)
13004 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
13005 workbuff[NAM$C_MAXRSS*1 + 1];
13006 SSize_t counter, num_entries;
13007 /* ODS-5 ups this, but we want to be consistent, so... */
13008 int max_name_len = 39;
13009 AV *in_array = (AV *)SvRV(ST(0));
13011 num_entries = av_tindex(in_array);
13013 /* All the names start with PL_. */
13014 strcpy(ultimate_name, "PL_");
13016 /* Clean up our working buffer */
13017 Zero(work_name, sizeof(work_name), char);
13019 /* Run through the entries and build up a working name */
13020 for(counter = 0; counter <= num_entries; counter++) {
13021 /* If it's not the first name then tack on a __ */
13023 my_strlcat(work_name, "__", sizeof(work_name));
13025 my_strlcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)), sizeof(work_name));
13028 /* Check to see if we actually have to bother...*/
13029 if (strlen(work_name) + 3 <= max_name_len) {
13030 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
13032 /* It's too darned big, so we need to go strip. We use the same */
13033 /* algorithm as xsubpp does. First, strip out doubled __ */
13034 char *source, *dest, last;
13037 for (source = work_name; *source; source++) {
13038 if (last == *source && last == '_') {
13044 /* Go put it back */
13045 my_strlcpy(work_name, workbuff, sizeof(work_name));
13046 /* Is it still too big? */
13047 if (strlen(work_name) + 3 > max_name_len) {
13048 /* Strip duplicate letters */
13051 for (source = work_name; *source; source++) {
13052 if (last == toupper(*source)) {
13056 last = toupper(*source);
13058 my_strlcpy(work_name, workbuff, sizeof(work_name));
13061 /* Is it *still* too big? */
13062 if (strlen(work_name) + 3 > max_name_len) {
13063 /* Too bad, we truncate */
13064 work_name[max_name_len - 2] = 0;
13066 my_strlcat(ultimate_name, work_name, sizeof(ultimate_name));
13069 /* Okay, return it */
13070 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13075 hushexit_fromperl(pTHX_ CV *cv)
13080 VMSISH_HUSHED = SvTRUE(ST(0));
13082 ST(0) = boolSV(VMSISH_HUSHED);
13088 Perl_vms_start_glob(pTHX_ SV *tmpglob, IO *io)
13091 struct vs_str_st *rslt;
13095 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13098 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13099 struct dsc$descriptor_vs rsdsc;
13100 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13101 unsigned long hasver = 0, isunix = 0;
13102 unsigned long int lff_flags = 0;
13104 int vms_old_glob = 1;
13106 if (!SvOK(tmpglob)) {
13107 SETERRNO(ENOENT,RMS$_FNF);
13111 vms_old_glob = !decc_filename_unix_report;
13113 #ifdef VMS_LONGNAME_SUPPORT
13114 lff_flags = LIB$M_FIL_LONG_NAMES;
13116 /* The Newx macro will not allow me to assign a smaller array
13117 * to the rslt pointer, so we will assign it to the begin char pointer
13118 * and then copy the value into the rslt pointer.
13120 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13121 rslt = (struct vs_str_st *)begin;
13123 rstr = &rslt->str[0];
13124 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13125 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13126 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13127 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13129 Newx(vmsspec, VMS_MAXRSS, char);
13131 /* We could find out if there's an explicit dev/dir or version
13132 by peeking into lib$find_file's internal context at
13133 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13134 but that's unsupported, so I don't want to do it now and
13135 have it bite someone in the future. */
13136 /* Fix-me: vms_split_path() is the only way to do this, the
13137 existing method will fail with many legal EFS or UNIX specifications
13140 cp = SvPV(tmpglob,i);
13143 if (cp[i] == ';') hasver = 1;
13144 if (cp[i] == '.') {
13145 if (sts) hasver = 1;
13148 if (cp[i] == '/') {
13149 hasdir = isunix = 1;
13152 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13158 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13159 if ((hasdir == 0) && decc_filename_unix_report) {
13163 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13164 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13165 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13171 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13172 if (!stat_sts && S_ISDIR(st.st_mode)) {
13174 const char * fname;
13177 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13178 /* path delimiter of ':>]', if so, then the old behavior has */
13179 /* obviously been specifically requested */
13181 fname = SvPVX_const(tmpglob);
13182 fname_len = strlen(fname);
13183 vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13184 if (vms_old_glob || (vms_dir != NULL)) {
13185 wilddsc.dsc$a_pointer = tovmspath_utf8(
13186 SvPVX(tmpglob),vmsspec,NULL);
13187 ok = (wilddsc.dsc$a_pointer != NULL);
13188 /* maybe passed 'foo' rather than '[.foo]', thus not
13192 /* Operate just on the directory, the special stat/fstat for */
13193 /* leaves the fileified specification in the st_devnam */
13195 wilddsc.dsc$a_pointer = st.st_devnam;
13200 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13201 ok = (wilddsc.dsc$a_pointer != NULL);
13204 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13206 /* If not extended character set, replace ? with % */
13207 /* With extended character set, ? is a wildcard single character */
13208 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13211 if (!decc_efs_charset)
13213 } else if (*cp == '%') {
13215 } else if (*cp == '*') {
13221 wv_sts = vms_split_path(
13222 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13223 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13224 &wvs_spec, &wvs_len);
13233 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13234 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13235 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13239 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13240 &dfltdsc,NULL,&rms_sts,&lff_flags);
13241 if (!$VMS_STATUS_SUCCESS(sts))
13244 /* with varying string, 1st word of buffer contains result length */
13245 rstr[rslt->length] = '\0';
13247 /* Find where all the components are */
13248 v_sts = vms_split_path
13263 /* If no version on input, truncate the version on output */
13264 if (!hasver && (vs_len > 0)) {
13271 /* In Unix report mode, remove the ".dir;1" from the name */
13272 /* if it is a real directory */
13273 if (decc_filename_unix_report && decc_efs_charset) {
13274 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13278 ret_sts = flex_lstat(rstr, &statbuf);
13279 if ((ret_sts == 0) &&
13280 S_ISDIR(statbuf.st_mode)) {
13287 /* No version & a null extension on UNIX handling */
13288 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13294 if (!decc_efs_case_preserve) {
13295 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13298 /* Find File treats a Null extension as return all extensions */
13299 /* This is contrary to Perl expectations */
13301 if (wildstar || wildquery || vms_old_glob) {
13302 /* really need to see if the returned file name matched */
13303 /* but for now will assume that it matches */
13306 /* Exact Match requested */
13307 /* How are directories handled? - like a file */
13308 if ((e_len == we_len) && (n_len == wn_len)) {
13312 t1 = strncmp(e_spec, we_spec, e_len);
13316 t1 = strncmp(n_spec, we_spec, n_len);
13327 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13331 /* Start with the name */
13334 strcat(begin,"\n");
13335 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13338 if (cxt) (void)lib$find_file_end(&cxt);
13341 /* Be POSIXish: return the input pattern when no matches */
13342 my_strlcpy(rstr, SvPVX(tmpglob), VMS_MAXRSS);
13344 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13347 if (ok && sts != RMS$_NMF &&
13348 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13351 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13353 PerlIO_close(tmpfp);
13357 PerlIO_rewind(tmpfp);
13358 IoTYPE(io) = IoTYPE_RDONLY;
13359 IoIFP(io) = fp = tmpfp;
13360 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13370 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13374 unixrealpath_fromperl(pTHX_ CV *cv)
13377 char *fspec, *rslt_spec, *rslt;
13380 if (!items || items != 1)
13381 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13383 fspec = SvPV(ST(0),n_a);
13384 if (!fspec || !*fspec) XSRETURN_UNDEF;
13386 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13387 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13389 ST(0) = sv_newmortal();
13391 sv_usepvn(ST(0),rslt,strlen(rslt));
13393 Safefree(rslt_spec);
13398 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13402 vmsrealpath_fromperl(pTHX_ CV *cv)
13405 char *fspec, *rslt_spec, *rslt;
13408 if (!items || items != 1)
13409 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13411 fspec = SvPV(ST(0),n_a);
13412 if (!fspec || !*fspec) XSRETURN_UNDEF;
13414 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13415 rslt = do_vms_realname(fspec, rslt_spec, NULL);
13417 ST(0) = sv_newmortal();
13419 sv_usepvn(ST(0),rslt,strlen(rslt));
13421 Safefree(rslt_spec);
13427 * A thin wrapper around decc$symlink to make sure we follow the
13428 * standard and do not create a symlink with a zero-length name,
13429 * and convert the target to Unix format, as the CRTL can't handle
13430 * targets in VMS format.
13432 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13434 Perl_my_symlink(pTHX_ const char *contents, const char *link_name)
13439 if (!link_name || !*link_name) {
13440 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13444 utarget = (char *)PerlMem_malloc(VMS_MAXRSS + 1);
13445 /* An untranslatable filename should be passed through. */
13446 (void) int_tounixspec(contents, utarget, NULL);
13447 sts = symlink(utarget, link_name);
13448 PerlMem_free(utarget);
13453 #endif /* HAS_SYMLINK */
13455 int do_vms_case_tolerant(void);
13458 case_tolerant_process_fromperl(pTHX_ CV *cv)
13461 ST(0) = boolSV(do_vms_case_tolerant());
13465 #ifdef USE_ITHREADS
13468 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
13469 struct interp_intern *dst)
13471 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13473 memcpy(dst,src,sizeof(struct interp_intern));
13479 Perl_sys_intern_clear(pTHX)
13484 Perl_sys_intern_init(pTHX)
13486 unsigned int ix = RAND_MAX;
13491 MY_POSIX_EXIT = vms_posix_exit;
13494 MY_INV_RAND_MAX = 1./x;
13498 init_os_extras(void)
13501 char* file = __FILE__;
13502 if (decc_disable_to_vms_logname_translation) {
13503 no_translate_barewords = TRUE;
13505 no_translate_barewords = FALSE;
13508 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
13509 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
13510 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
13511 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
13512 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
13513 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
13514 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
13515 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
13516 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
13517 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
13518 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
13519 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
13520 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
13521 newXSproto("VMS::Filespec::case_tolerant_process",
13522 case_tolerant_process_fromperl,file,"");
13524 store_pipelocs(aTHX); /* will redo any earlier attempts */
13529 #if __CRTL_VER == 80200000
13530 /* This missed getting in to the DECC SDK for 8.2 */
13531 char *realpath(const char *file_name, char * resolved_name, ...);
13534 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
13535 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
13536 * The perl fallback routine to provide realpath() is not as efficient
13544 /* Hack, use old stat() as fastest way of getting ino_t and device */
13545 int decc$stat(const char *name, void * statbuf);
13546 #if !defined(__VAX) && __CRTL_VER >= 80200000
13547 int decc$lstat(const char *name, void * statbuf);
13549 #define decc$lstat decc$stat
13557 /* Realpath is fragile. In 8.3 it does not work if the feature
13558 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
13559 * links are implemented in RMS, not the CRTL. It also can fail if the
13560 * user does not have read/execute access to some of the directories.
13561 * So in order for Do What I Mean mode to work, if realpath() fails,
13562 * fall back to looking up the filename by the device name and FID.
13565 int vms_fid_to_name(char * outname, int outlen,
13566 const char * name, int lstat_flag, mode_t * mode)
13568 #pragma message save
13569 #pragma message disable MISALGNDSTRCT
13570 #pragma message disable MISALGNDMEM
13571 #pragma member_alignment save
13572 #pragma nomember_alignment
13575 unsigned short st_ino[3];
13576 unsigned short old_st_mode;
13577 unsigned long padl[30]; /* plenty of room */
13579 #pragma message restore
13580 #pragma member_alignment restore
13583 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13584 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13589 /* Need to follow the mostly the same rules as flex_stat_int, or we may get
13590 * unexpected answers
13593 fileified = (char *)PerlMem_malloc(VMS_MAXRSS);
13594 if (fileified == NULL)
13595 _ckvmssts_noperl(SS$_INSFMEM);
13597 temp_fspec = (char *)PerlMem_malloc(VMS_MAXRSS);
13598 if (temp_fspec == NULL)
13599 _ckvmssts_noperl(SS$_INSFMEM);
13602 /* First need to try as a directory */
13603 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13604 if (ret_spec != NULL) {
13605 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
13606 if (ret_spec != NULL) {
13607 if (lstat_flag == 0)
13608 sts = decc$stat(fileified, &statbuf);
13610 sts = decc$lstat(fileified, &statbuf);
13614 /* Then as a VMS file spec */
13616 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
13617 if (ret_spec != NULL) {
13618 if (lstat_flag == 0) {
13619 sts = decc$stat(temp_fspec, &statbuf);
13621 sts = decc$lstat(temp_fspec, &statbuf);
13627 /* Next try - allow multiple dots with out EFS CHARSET */
13628 /* The CRTL stat() falls down hard on multi-dot filenames in unix
13629 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
13630 * enable it if it isn't already.
13632 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13633 if (!decc_efs_charset && (decc_efs_charset_index > 0))
13634 decc$feature_set_value(decc_efs_charset_index, 1, 1);
13636 ret_spec = int_tovmspath(name, temp_fspec, NULL);
13637 if (lstat_flag == 0) {
13638 sts = decc$stat(name, &statbuf);
13640 sts = decc$lstat(name, &statbuf);
13642 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13643 if (!decc_efs_charset && (decc_efs_charset_index > 0))
13644 decc$feature_set_value(decc_efs_charset_index, 1, 0);
13649 /* and then because the Perl Unix to VMS conversion is not perfect */
13650 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
13651 /* characters from filenames so we need to try it as-is */
13653 if (lstat_flag == 0) {
13654 sts = decc$stat(name, &statbuf);
13656 sts = decc$lstat(name, &statbuf);
13663 dvidsc.dsc$a_pointer=statbuf.st_dev;
13664 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
13666 specdsc.dsc$a_pointer = outname;
13667 specdsc.dsc$w_length = outlen-1;
13669 vms_sts = lib$fid_to_name
13670 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
13671 if ($VMS_STATUS_SUCCESS(vms_sts)) {
13672 outname[specdsc.dsc$w_length] = 0;
13674 /* Return the mode */
13676 *mode = statbuf.old_st_mode;
13680 PerlMem_free(temp_fspec);
13681 PerlMem_free(fileified);
13688 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
13691 char * rslt = NULL;
13694 if (decc_posix_compliant_pathnames > 0 ) {
13695 /* realpath currently only works if posix compliant pathnames are
13696 * enabled. It may start working when they are not, but in that
13697 * case we still want the fallback behavior for backwards compatibility
13699 rslt = realpath(filespec, outbuf);
13703 if (rslt == NULL) {
13705 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13706 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13709 /* Fall back to fid_to_name */
13711 Newx(vms_spec, VMS_MAXRSS + 1, char);
13713 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
13717 /* Now need to trim the version off */
13718 sts = vms_split_path
13738 /* Trim off the version */
13739 int file_len = v_len + r_len + d_len + n_len + e_len;
13740 vms_spec[file_len] = 0;
13742 /* Trim off the .DIR if this is a directory */
13743 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13744 if (S_ISDIR(my_mode)) {
13750 /* Drop NULL extensions on UNIX file specification */
13751 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13756 /* The result is expected to be in UNIX format */
13757 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
13759 /* Downcase if input had any lower case letters and
13760 * case preservation is not in effect.
13762 if (!decc_efs_case_preserve) {
13763 for (cp = filespec; *cp; cp++)
13764 if (islower(*cp)) { haslower = 1; break; }
13766 if (haslower) __mystrtolower(rslt);
13771 /* Now for some hacks to deal with backwards and forward */
13772 /* compatibility */
13773 if (!decc_efs_charset) {
13775 /* 1. ODS-2 mode wants to do a syntax only translation */
13776 rslt = int_rmsexpand(filespec, outbuf,
13777 NULL, 0, NULL, utf8_fl);
13780 if (decc_filename_unix_report) {
13782 char * vms_dir_name;
13785 /* 2. ODS-5 / UNIX report mode should return a failure */
13786 /* if the parent directory also does not exist */
13787 /* Otherwise, get the real path for the parent */
13788 /* and add the child to it. */
13790 /* basename / dirname only available for VMS 7.0+ */
13791 /* So we may need to implement them as common routines */
13793 Newx(dir_name, VMS_MAXRSS + 1, char);
13794 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
13795 dir_name[0] = '\0';
13798 /* First try a VMS parse */
13799 sts = vms_split_path
13817 int dir_len = v_len + r_len + d_len + n_len;
13819 memcpy(dir_name, filespec, dir_len);
13820 dir_name[dir_len] = '\0';
13821 file_name = (char *)&filespec[dir_len + 1];
13824 /* This must be UNIX */
13827 tchar = strrchr(filespec, '/');
13829 if (tchar != NULL) {
13830 int dir_len = tchar - filespec;
13831 memcpy(dir_name, filespec, dir_len);
13832 dir_name[dir_len] = '\0';
13833 file_name = (char *) &filespec[dir_len + 1];
13837 /* Dir name is defaulted */
13838 if (dir_name[0] == 0) {
13840 dir_name[1] = '\0';
13843 /* Need realpath for the directory */
13844 sts = vms_fid_to_name(vms_dir_name,
13846 dir_name, 0, NULL);
13849 /* Now need to pathify it. */
13850 char *tdir = int_pathify_dirspec(vms_dir_name,
13853 /* And now add the original filespec to it */
13854 if (file_name != NULL) {
13855 my_strlcat(outbuf, file_name, VMS_MAXRSS);
13859 Safefree(vms_dir_name);
13860 Safefree(dir_name);
13864 Safefree(vms_spec);
13870 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
13873 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13874 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13876 /* Fall back to fid_to_name */
13878 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
13885 /* Now need to trim the version off */
13886 sts = vms_split_path
13906 /* Trim off the version */
13907 int file_len = v_len + r_len + d_len + n_len + e_len;
13908 outbuf[file_len] = 0;
13910 /* Downcase if input had any lower case letters and
13911 * case preservation is not in effect.
13913 if (!decc_efs_case_preserve) {
13914 for (cp = filespec; *cp; cp++)
13915 if (islower(*cp)) { haslower = 1; break; }
13917 if (haslower) __mystrtolower(outbuf);
13926 /* External entry points */
13928 Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13930 return do_vms_realpath(filespec, outbuf, utf8_fl);
13934 Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13936 return do_vms_realname(filespec, outbuf, utf8_fl);
13939 /* case_tolerant */
13941 /*{{{int do_vms_case_tolerant(void)*/
13942 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
13943 * controlled by a process setting.
13946 do_vms_case_tolerant(void)
13948 return vms_process_case_tolerant;
13951 /* External entry points */
13953 Perl_vms_case_tolerant(void)
13955 #if __CRTL_VER >= 70301000 && !defined(__VAX)
13956 return do_vms_case_tolerant();
13958 return vms_process_case_tolerant;
13962 /* Start of DECC RTL Feature handling */
13964 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13967 set_feature_default(const char *name, int value)
13973 /* If the feature has been explicitly disabled in the environment,
13974 * then don't enable it here.
13977 status = simple_trnlnm(name, val_str, sizeof(val_str));
13979 val_str[0] = _toupper(val_str[0]);
13980 if (val_str[0] == 'D' || val_str[0] == '0' || val_str[0] == 'F')
13985 index = decc$feature_get_index(name);
13987 status = decc$feature_set_value(index, 1, value);
13988 if (index == -1 || (status == -1)) {
13992 status = decc$feature_get_value(index, 1);
13993 if (status != value) {
13997 /* Various things may check for an environment setting
13998 * rather than the feature directly, so set that too.
14000 vmssetuserlnm(name, value ? "ENABLE" : "DISABLE");
14007 /* C RTL Feature settings */
14009 #if defined(__DECC) || defined(__DECCXX)
14016 vmsperl_set_features(void)
14021 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
14022 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
14023 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
14024 unsigned long case_perm;
14025 unsigned long case_image;
14028 /* Allow an exception to bring Perl into the VMS debugger */
14029 vms_debug_on_exception = 0;
14030 status = simple_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
14032 val_str[0] = _toupper(val_str[0]);
14033 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14034 vms_debug_on_exception = 1;
14036 vms_debug_on_exception = 0;
14039 /* Debug unix/vms file translation routines */
14040 vms_debug_fileify = 0;
14041 status = simple_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
14043 val_str[0] = _toupper(val_str[0]);
14044 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14045 vms_debug_fileify = 1;
14047 vms_debug_fileify = 0;
14051 /* Historically PERL has been doing vmsify / stat differently than */
14052 /* the CRTL. In particular, under some conditions the CRTL will */
14053 /* remove some illegal characters like spaces from filenames */
14054 /* resulting in some differences. The stat()/lstat() wrapper has */
14055 /* been reporting such file names as invalid and fails to stat them */
14056 /* fixing this bug so that stat()/lstat() accept these like the */
14057 /* CRTL does will result in several tests failing. */
14058 /* This should really be fixed, but for now, set up a feature to */
14059 /* enable it so that the impact can be studied. */
14060 vms_bug_stat_filename = 0;
14061 status = simple_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
14063 val_str[0] = _toupper(val_str[0]);
14064 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14065 vms_bug_stat_filename = 1;
14067 vms_bug_stat_filename = 0;
14071 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
14072 vms_vtf7_filenames = 0;
14073 status = simple_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14075 val_str[0] = _toupper(val_str[0]);
14076 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14077 vms_vtf7_filenames = 1;
14079 vms_vtf7_filenames = 0;
14082 /* unlink all versions on unlink() or rename() */
14083 vms_unlink_all_versions = 0;
14084 status = simple_trnlnm("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14086 val_str[0] = _toupper(val_str[0]);
14087 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14088 vms_unlink_all_versions = 1;
14090 vms_unlink_all_versions = 0;
14093 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14094 /* Detect running under GNV Bash or other UNIX like shell */
14095 gnv_unix_shell = 0;
14096 status = simple_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14098 gnv_unix_shell = 1;
14099 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14100 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14101 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14102 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
14103 vms_unlink_all_versions = 1;
14104 vms_posix_exit = 1;
14105 /* Reverse default ordering of PERL_ENV_TABLES. */
14106 defenv[0] = &crtlenvdsc;
14107 defenv[1] = &fildevdsc;
14109 /* Some reasonable defaults that are not CRTL defaults */
14110 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14111 set_feature_default("DECC$ARGV_PARSE_STYLE", 1); /* Requires extended parse. */
14112 set_feature_default("DECC$EFS_CHARSET", 1);
14115 /* hacks to see if known bugs are still present for testing */
14117 /* PCP mode requires creating /dev/null special device file */
14118 decc_bug_devnull = 0;
14119 status = simple_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14121 val_str[0] = _toupper(val_str[0]);
14122 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14123 decc_bug_devnull = 1;
14125 decc_bug_devnull = 0;
14128 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14129 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14131 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14132 if (decc_disable_to_vms_logname_translation < 0)
14133 decc_disable_to_vms_logname_translation = 0;
14136 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14138 decc_efs_case_preserve = decc$feature_get_value(s, 1);
14139 if (decc_efs_case_preserve < 0)
14140 decc_efs_case_preserve = 0;
14143 s = decc$feature_get_index("DECC$EFS_CHARSET");
14144 decc_efs_charset_index = s;
14146 decc_efs_charset = decc$feature_get_value(s, 1);
14147 if (decc_efs_charset < 0)
14148 decc_efs_charset = 0;
14151 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14153 decc_filename_unix_report = decc$feature_get_value(s, 1);
14154 if (decc_filename_unix_report > 0) {
14155 decc_filename_unix_report = 1;
14156 vms_posix_exit = 1;
14159 decc_filename_unix_report = 0;
14162 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14164 decc_filename_unix_only = decc$feature_get_value(s, 1);
14165 if (decc_filename_unix_only > 0) {
14166 decc_filename_unix_only = 1;
14169 decc_filename_unix_only = 0;
14173 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14175 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14176 if (decc_filename_unix_no_version < 0)
14177 decc_filename_unix_no_version = 0;
14180 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14182 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14183 if (decc_readdir_dropdotnotype < 0)
14184 decc_readdir_dropdotnotype = 0;
14187 #if __CRTL_VER >= 80200000
14188 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14190 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14191 if (decc_posix_compliant_pathnames < 0)
14192 decc_posix_compliant_pathnames = 0;
14193 if (decc_posix_compliant_pathnames > 4)
14194 decc_posix_compliant_pathnames = 0;
14199 status = simple_trnlnm
14200 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14202 val_str[0] = _toupper(val_str[0]);
14203 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14204 decc_disable_to_vms_logname_translation = 1;
14209 status = simple_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14211 val_str[0] = _toupper(val_str[0]);
14212 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14213 decc_efs_case_preserve = 1;
14218 status = simple_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14220 val_str[0] = _toupper(val_str[0]);
14221 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14222 decc_filename_unix_report = 1;
14225 status = simple_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14227 val_str[0] = _toupper(val_str[0]);
14228 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14229 decc_filename_unix_only = 1;
14230 decc_filename_unix_report = 1;
14233 status = simple_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14235 val_str[0] = _toupper(val_str[0]);
14236 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14237 decc_filename_unix_no_version = 1;
14240 status = simple_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14242 val_str[0] = _toupper(val_str[0]);
14243 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14244 decc_readdir_dropdotnotype = 1;
14249 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14251 /* Report true case tolerance */
14252 /*----------------------------*/
14253 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14254 if (!$VMS_STATUS_SUCCESS(status))
14255 case_perm = PPROP$K_CASE_BLIND;
14256 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14257 if (!$VMS_STATUS_SUCCESS(status))
14258 case_image = PPROP$K_CASE_BLIND;
14259 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14260 (case_image == PPROP$K_CASE_SENSITIVE))
14261 vms_process_case_tolerant = 0;
14265 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
14266 /* for strict backward compatibility */
14267 status = simple_trnlnm("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14269 val_str[0] = _toupper(val_str[0]);
14270 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14271 vms_posix_exit = 1;
14273 vms_posix_exit = 0;
14277 /* Use 32-bit pointers because that's what the image activator
14278 * assumes for the LIB$INITIALZE psect.
14280 #if __INITIAL_POINTER_SIZE
14281 #pragma pointer_size save
14282 #pragma pointer_size 32
14285 /* Create a reference to the LIB$INITIALIZE function. */
14286 extern void LIB$INITIALIZE(void);
14287 extern void (*vmsperl_unused_global_1)(void) = LIB$INITIALIZE;
14289 /* Create an array of pointers to the init functions in the special
14290 * LIB$INITIALIZE section. In our case, the array only has one entry.
14292 #pragma extern_model save
14293 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic,gbl,nowrt,noshr,long
14294 extern void (* const vmsperl_unused_global_2[])() =
14296 vmsperl_set_features,
14298 #pragma extern_model restore
14300 #if __INITIAL_POINTER_SIZE
14301 #pragma pointer_size restore
14308 #endif /* defined(__DECC) || defined(__DECCXX) */