3 * VMS-specific routines for perl5
5 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
6 * 2002, 2003, 2004, 2005, 2006, 2007 by Charles Bailey and others.
8 * You may distribute under the terms of either the GNU General Public
9 * License or the Artistic License, as specified in the README file.
11 * Please see Changes*.* or the Perl Repository Browser for revision history.
15 * Yet small as was their hunted band
16 * still fell and fearless was each hand,
17 * and strong deeds they wrought yet oft,
18 * and loved the woods, whose ways more soft
19 * them seemed than thralls of that black throne
20 * to live and languish in halls of stone.
21 * "The Lay of Leithian", Canto II, lines 135-40
23 * [p.162 of _The Lays of Beleriand_]
32 #include <climsgdef.h>
43 #include <libclidef.h>
45 #include <lib$routines.h>
49 #if __CRTL_VER >= 70301000 && !defined(__VAX)
59 #include <str$routines.h>
65 #if __CRTL_VER >= 70000000 /* FIXME to earliest version */
67 #define NO_EFN EFN$C_ENF
72 #if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
73 int decc$feature_get_index(const char *name);
74 char* decc$feature_get_name(int index);
75 int decc$feature_get_value(int index, int mode);
76 int decc$feature_set_value(int index, int mode, int value);
81 #pragma member_alignment save
82 #pragma nomember_alignment longword
87 unsigned short * retadr;
89 #pragma member_alignment restore
91 #if __CRTL_VER >= 70300000 && !defined(__VAX)
93 static int set_feature_default(const char *name, int value)
98 index = decc$feature_get_index(name);
100 status = decc$feature_set_value(index, 1, value);
101 if (index == -1 || (status == -1)) {
105 status = decc$feature_get_value(index, 1);
106 if (status != value) {
114 /* Older versions of ssdef.h don't have these */
115 #ifndef SS$_INVFILFOROP
116 # define SS$_INVFILFOROP 3930
118 #ifndef SS$_NOSUCHOBJECT
119 # define SS$_NOSUCHOBJECT 2696
122 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
123 #define PERLIO_NOT_STDIO 0
125 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
126 * code below needs to get to the underlying CRTL routines. */
127 #define DONT_MASK_RTL_CALLS
131 /* Anticipating future expansion in lexical warnings . . . */
132 #ifndef WARN_INTERNAL
133 # define WARN_INTERNAL WARN_MISC
136 #ifdef VMS_LONGNAME_SUPPORT
137 #include <libfildef.h>
140 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
141 # define RTL_USES_UTC 1
144 #if !defined(__VAX) && __CRTL_VER >= 80200000
152 #define lstat(_x, _y) stat(_x, _y)
155 /* Routine to create a decterm for use with the Perl debugger */
156 /* No headers, this information was found in the Programming Concepts Manual */
158 static int (*decw_term_port)
159 (const struct dsc$descriptor_s * display,
160 const struct dsc$descriptor_s * setup_file,
161 const struct dsc$descriptor_s * customization,
162 struct dsc$descriptor_s * result_device_name,
163 unsigned short * result_device_name_length,
166 void * char_change_buffer) = 0;
168 /* gcc's header files don't #define direct access macros
169 * corresponding to VAXC's variant structs */
171 # define uic$v_format uic$r_uic_form.uic$v_format
172 # define uic$v_group uic$r_uic_form.uic$v_group
173 # define uic$v_member uic$r_uic_form.uic$v_member
174 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
175 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
176 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
177 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
180 #if defined(NEED_AN_H_ERRNO)
185 #pragma message disable pragma
186 #pragma member_alignment save
187 #pragma nomember_alignment longword
189 #pragma message disable misalgndmem
192 unsigned short int buflen;
193 unsigned short int itmcode;
195 unsigned short int *retlen;
198 struct filescan_itmlst_2 {
199 unsigned short length;
200 unsigned short itmcode;
205 unsigned short length;
210 #pragma message restore
211 #pragma member_alignment restore
214 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
215 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
216 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
217 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
218 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
219 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
220 #define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c)
221 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
222 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
223 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
224 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
225 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
227 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
228 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
229 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
230 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
232 static char * int_rmsexpand_vms(
233 const char * filespec, char * outbuf, unsigned opts);
234 static char * int_rmsexpand_tovms(
235 const char * filespec, char * outbuf, unsigned opts);
236 static char *int_tovmsspec
237 (const char *path, char *buf, int dir_flag, int * utf8_flag);
238 static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl);
239 static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
240 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl);
242 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
243 #define PERL_LNM_MAX_ALLOWED_INDEX 127
245 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
246 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
249 #define PERL_LNM_MAX_ITER 10
251 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
252 #if __CRTL_VER >= 70302000 && !defined(__VAX)
253 #define MAX_DCL_SYMBOL (8192)
254 #define MAX_DCL_LINE_LENGTH (4096 - 4)
256 #define MAX_DCL_SYMBOL (1024)
257 #define MAX_DCL_LINE_LENGTH (1024 - 4)
260 static char *__mystrtolower(char *str)
262 if (str) for (; *str; ++str) *str= tolower(*str);
266 static struct dsc$descriptor_s fildevdsc =
267 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
268 static struct dsc$descriptor_s crtlenvdsc =
269 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
270 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
271 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
272 static struct dsc$descriptor_s **env_tables = defenv;
273 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
275 /* True if we shouldn't treat barewords as logicals during directory */
277 static int no_translate_barewords;
280 static int tz_updated = 1;
283 /* DECC Features that may need to affect how Perl interprets
284 * displays filename information
286 static int decc_disable_to_vms_logname_translation = 1;
287 static int decc_disable_posix_root = 1;
288 int decc_efs_case_preserve = 0;
289 static int decc_efs_charset = 0;
290 static int decc_efs_charset_index = -1;
291 static int decc_filename_unix_no_version = 0;
292 static int decc_filename_unix_only = 0;
293 int decc_filename_unix_report = 0;
294 int decc_posix_compliant_pathnames = 0;
295 int decc_readdir_dropdotnotype = 0;
296 static int vms_process_case_tolerant = 1;
297 int vms_vtf7_filenames = 0;
298 int gnv_unix_shell = 0;
299 static int vms_unlink_all_versions = 0;
300 static int vms_posix_exit = 0;
302 /* bug workarounds if needed */
303 int decc_bug_devnull = 1;
304 int decc_dir_barename = 0;
305 int vms_bug_stat_filename = 0;
307 static int vms_debug_on_exception = 0;
308 static int vms_debug_fileify = 0;
310 /* Simple logical name translation */
311 static int simple_trnlnm
312 (const char * logname,
316 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
317 const unsigned long attr = LNM$M_CASE_BLIND;
318 struct dsc$descriptor_s name_dsc;
320 unsigned short result;
321 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
324 name_dsc.dsc$w_length = strlen(logname);
325 name_dsc.dsc$a_pointer = (char *)logname;
326 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
327 name_dsc.dsc$b_class = DSC$K_CLASS_S;
329 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
331 if ($VMS_STATUS_SUCCESS(status)) {
333 /* Null terminate and return the string */
334 /*--------------------------------------*/
343 /* Is this a UNIX file specification?
344 * No longer a simple check with EFS file specs
345 * For now, not a full check, but need to
346 * handle POSIX ^UP^ specifications
347 * Fixing to handle ^/ cases would require
348 * changes to many other conversion routines.
351 static int is_unix_filespec(const char *path)
357 if (strncmp(path,"\"^UP^",5) != 0) {
358 pch1 = strchr(path, '/');
363 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
364 if (decc_filename_unix_report || decc_filename_unix_only) {
365 if (strcmp(path,".") == 0)
373 /* This routine converts a UCS-2 character to be VTF-7 encoded.
376 static void ucs2_to_vtf7
378 unsigned long ucs2_char,
381 unsigned char * ucs_ptr;
384 ucs_ptr = (unsigned char *)&ucs2_char;
388 hex = (ucs_ptr[1] >> 4) & 0xf;
390 outspec[2] = hex + '0';
392 outspec[2] = (hex - 9) + 'A';
393 hex = ucs_ptr[1] & 0xF;
395 outspec[3] = hex + '0';
397 outspec[3] = (hex - 9) + 'A';
399 hex = (ucs_ptr[0] >> 4) & 0xf;
401 outspec[4] = hex + '0';
403 outspec[4] = (hex - 9) + 'A';
404 hex = ucs_ptr[1] & 0xF;
406 outspec[5] = hex + '0';
408 outspec[5] = (hex - 9) + 'A';
414 /* This handles the conversion of a UNIX extended character set to a ^
415 * escaped VMS character.
416 * in a UNIX file specification.
418 * The output count variable contains the number of characters added
419 * to the output string.
421 * The return value is the number of characters read from the input string
423 static int copy_expand_unix_filename_escape
424 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
431 utf8_flag = *utf8_fl;
435 if (*inspec >= 0x80) {
436 if (utf8_fl && vms_vtf7_filenames) {
437 unsigned long ucs_char;
441 if ((*inspec & 0xE0) == 0xC0) {
443 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
444 if (ucs_char >= 0x80) {
445 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
448 } else if ((*inspec & 0xF0) == 0xE0) {
450 ucs_char = ((inspec[0] & 0xF) << 12) +
451 ((inspec[1] & 0x3f) << 6) +
453 if (ucs_char >= 0x800) {
454 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
458 #if 0 /* I do not see longer sequences supported by OpenVMS */
459 /* Maybe some one can fix this later */
460 } else if ((*inspec & 0xF8) == 0xF0) {
463 } else if ((*inspec & 0xFC) == 0xF8) {
466 } else if ((*inspec & 0xFE) == 0xFC) {
473 /* High bit set, but not a Unicode character! */
475 /* Non printing DECMCS or ISO Latin-1 character? */
476 if (*inspec <= 0x9F) {
480 hex = (*inspec >> 4) & 0xF;
482 outspec[1] = hex + '0';
484 outspec[1] = (hex - 9) + 'A';
488 outspec[2] = hex + '0';
490 outspec[2] = (hex - 9) + 'A';
494 } else if (*inspec == 0xA0) {
500 } else if (*inspec == 0xFF) {
512 /* Is this a macro that needs to be passed through?
513 * Macros start with $( and an alpha character, followed
514 * by a string of alpha numeric characters ending with a )
515 * If this does not match, then encode it as ODS-5.
517 if ((inspec[0] == '$') && (inspec[1] == '(')) {
520 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
522 outspec[0] = inspec[0];
523 outspec[1] = inspec[1];
524 outspec[2] = inspec[2];
526 while(isalnum(inspec[tcnt]) ||
527 (inspec[2] == '.') || (inspec[2] == '_')) {
528 outspec[tcnt] = inspec[tcnt];
531 if (inspec[tcnt] == ')') {
532 outspec[tcnt] = inspec[tcnt];
549 if (decc_efs_charset == 0)
576 /* Don't escape again if following character is
577 * already something we escape.
579 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
585 /* But otherwise fall through and escape it. */
587 /* Assume that this is to be escaped */
589 outspec[1] = *inspec;
593 case ' ': /* space */
594 /* Assume that this is to be escaped */
609 /* This handles the expansion of a '^' prefix to the proper character
610 * in a UNIX file specification.
612 * The output count variable contains the number of characters added
613 * to the output string.
615 * The return value is the number of characters read from the input
618 static int copy_expand_vms_filename_escape
619 (char *outspec, const char *inspec, int *output_cnt)
626 if (*inspec == '^') {
629 /* Spaces and non-trailing dots should just be passed through,
630 * but eat the escape character.
637 case '_': /* space */
643 /* Hmm. Better leave the escape escaped. */
649 case 'U': /* Unicode - FIX-ME this is wrong. */
652 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
655 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
656 outspec[0] = c1 & 0xff;
657 outspec[1] = c2 & 0xff;
664 /* Error - do best we can to continue */
674 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
678 scnt = sscanf(inspec, "%2x", &c1);
679 outspec[0] = c1 & 0xff;
700 /* vms_split_path - Verify that the input file specification is a
701 * VMS format file specification, and provide pointers to the components of
702 * it. With EFS format filenames, this is virtually the only way to
703 * parse a VMS path specification into components.
705 * If the sum of the components do not add up to the length of the
706 * string, then the passed file specification is probably a UNIX style
709 static int vms_split_path
724 struct dsc$descriptor path_desc;
728 struct filescan_itmlst_2 item_list[9];
729 const int filespec = 0;
730 const int nodespec = 1;
731 const int devspec = 2;
732 const int rootspec = 3;
733 const int dirspec = 4;
734 const int namespec = 5;
735 const int typespec = 6;
736 const int verspec = 7;
738 /* Assume the worst for an easy exit */
752 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
753 path_desc.dsc$w_length = strlen(path);
754 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
755 path_desc.dsc$b_class = DSC$K_CLASS_S;
757 /* Get the total length, if it is shorter than the string passed
758 * then this was probably not a VMS formatted file specification
760 item_list[filespec].itmcode = FSCN$_FILESPEC;
761 item_list[filespec].length = 0;
762 item_list[filespec].component = NULL;
764 /* If the node is present, then it gets considered as part of the
765 * volume name to hopefully make things simple.
767 item_list[nodespec].itmcode = FSCN$_NODE;
768 item_list[nodespec].length = 0;
769 item_list[nodespec].component = NULL;
771 item_list[devspec].itmcode = FSCN$_DEVICE;
772 item_list[devspec].length = 0;
773 item_list[devspec].component = NULL;
775 /* root is a special case, adding it to either the directory or
776 * the device components will probably complicate things for the
777 * callers of this routine, so leave it separate.
779 item_list[rootspec].itmcode = FSCN$_ROOT;
780 item_list[rootspec].length = 0;
781 item_list[rootspec].component = NULL;
783 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
784 item_list[dirspec].length = 0;
785 item_list[dirspec].component = NULL;
787 item_list[namespec].itmcode = FSCN$_NAME;
788 item_list[namespec].length = 0;
789 item_list[namespec].component = NULL;
791 item_list[typespec].itmcode = FSCN$_TYPE;
792 item_list[typespec].length = 0;
793 item_list[typespec].component = NULL;
795 item_list[verspec].itmcode = FSCN$_VERSION;
796 item_list[verspec].length = 0;
797 item_list[verspec].component = NULL;
799 item_list[8].itmcode = 0;
800 item_list[8].length = 0;
801 item_list[8].component = NULL;
803 status = sys$filescan
804 ((const struct dsc$descriptor_s *)&path_desc, item_list,
806 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
808 /* If we parsed it successfully these two lengths should be the same */
809 if (path_desc.dsc$w_length != item_list[filespec].length)
812 /* If we got here, then it is a VMS file specification */
815 /* set the volume name */
816 if (item_list[nodespec].length > 0) {
817 *volume = item_list[nodespec].component;
818 *vol_len = item_list[nodespec].length + item_list[devspec].length;
821 *volume = item_list[devspec].component;
822 *vol_len = item_list[devspec].length;
825 *root = item_list[rootspec].component;
826 *root_len = item_list[rootspec].length;
828 *dir = item_list[dirspec].component;
829 *dir_len = item_list[dirspec].length;
831 /* Now fun with versions and EFS file specifications
832 * The parser can not tell the difference when a "." is a version
833 * delimiter or a part of the file specification.
835 if ((decc_efs_charset) &&
836 (item_list[verspec].length > 0) &&
837 (item_list[verspec].component[0] == '.')) {
838 *name = item_list[namespec].component;
839 *name_len = item_list[namespec].length + item_list[typespec].length;
840 *ext = item_list[verspec].component;
841 *ext_len = item_list[verspec].length;
846 *name = item_list[namespec].component;
847 *name_len = item_list[namespec].length;
848 *ext = item_list[typespec].component;
849 *ext_len = item_list[typespec].length;
850 *version = item_list[verspec].component;
851 *ver_len = item_list[verspec].length;
856 /* Routine to determine if the file specification ends with .dir */
857 static int is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len) {
859 /* e_len must be 4, and version must be <= 2 characters */
860 if (e_len != 4 || vs_len > 2)
863 /* If a version number is present, it needs to be one */
864 if ((vs_len == 2) && (vs_spec[1] != '1'))
867 /* Look for the DIR on the extension */
868 if (vms_process_case_tolerant) {
869 if ((toupper(e_spec[1]) == 'D') &&
870 (toupper(e_spec[2]) == 'I') &&
871 (toupper(e_spec[3]) == 'R')) {
875 /* Directory extensions are supposed to be in upper case only */
876 /* I would not be surprised if this rule can not be enforced */
877 /* if and when someone fully debugs the case sensitive mode */
878 if ((e_spec[1] == 'D') &&
879 (e_spec[2] == 'I') &&
880 (e_spec[3] == 'R')) {
889 * Routine to retrieve the maximum equivalence index for an input
890 * logical name. Some calls to this routine have no knowledge if
891 * the variable is a logical or not. So on error we return a max
894 /*{{{int my_maxidx(const char *lnm) */
896 my_maxidx(const char *lnm)
900 int attr = LNM$M_CASE_BLIND;
901 struct dsc$descriptor lnmdsc;
902 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
905 lnmdsc.dsc$w_length = strlen(lnm);
906 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
907 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
908 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
910 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
911 if ((status & 1) == 0)
918 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
920 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
921 struct dsc$descriptor_s **tabvec, unsigned long int flags)
924 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
925 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
926 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
928 unsigned char acmode;
929 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
930 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
931 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
932 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
934 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
935 #if defined(PERL_IMPLICIT_CONTEXT)
938 aTHX = PERL_GET_INTERP;
944 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
945 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
947 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
948 *cp2 = _toupper(*cp1);
949 if (cp1 - lnm > LNM$C_NAMLENGTH) {
950 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
954 lnmdsc.dsc$w_length = cp1 - lnm;
955 lnmdsc.dsc$a_pointer = uplnm;
956 uplnm[lnmdsc.dsc$w_length] = '\0';
957 secure = flags & PERL__TRNENV_SECURE;
958 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
959 if (!tabvec || !*tabvec) tabvec = env_tables;
961 for (curtab = 0; tabvec[curtab]; curtab++) {
962 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
963 if (!ivenv && !secure) {
968 #if defined(PERL_IMPLICIT_CONTEXT)
971 "Can't read CRTL environ\n");
974 Perl_warn(aTHX_ "Can't read CRTL environ\n");
977 retsts = SS$_NOLOGNAM;
978 for (i = 0; environ[i]; i++) {
979 if ((eq = strchr(environ[i],'=')) &&
980 lnmdsc.dsc$w_length == (eq - environ[i]) &&
981 !strncmp(environ[i],uplnm,eq - environ[i])) {
983 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
984 if (!eqvlen) continue;
989 if (retsts != SS$_NOLOGNAM) break;
992 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
993 !str$case_blind_compare(&tmpdsc,&clisym)) {
994 if (!ivsym && !secure) {
995 unsigned short int deflen = LNM$C_NAMLENGTH;
996 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
997 /* dynamic dsc to accommodate possible long value */
998 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
999 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
1001 if (eqvlen > MAX_DCL_SYMBOL) {
1002 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
1003 eqvlen = MAX_DCL_SYMBOL;
1004 /* Special hack--we might be called before the interpreter's */
1005 /* fully initialized, in which case either thr or PL_curcop */
1006 /* might be bogus. We have to check, since ckWARN needs them */
1007 /* both to be valid if running threaded */
1008 #if defined(PERL_IMPLICIT_CONTEXT)
1011 "Value of CLI symbol \"%s\" too long",lnm);
1014 if (ckWARN(WARN_MISC)) {
1015 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
1018 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
1020 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
1021 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1022 if (retsts == LIB$_NOSUCHSYM) continue;
1027 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
1028 midx = my_maxidx(lnm);
1029 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1030 lnmlst[1].bufadr = cp2;
1032 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1033 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1034 if (retsts == SS$_NOLOGNAM) break;
1035 /* PPFs have a prefix */
1038 *((int *)uplnm) == *((int *)"SYS$") &&
1040 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
1041 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
1042 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
1043 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
1044 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
1045 memmove(eqv,eqv+4,eqvlen-4);
1051 if ((retsts == SS$_IVLOGNAM) ||
1052 (retsts == SS$_NOLOGNAM)) { continue; }
1055 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1056 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1057 if (retsts == SS$_NOLOGNAM) continue;
1060 eqvlen = strlen(eqv);
1064 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1065 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1066 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
1067 retsts == SS$_NOLOGNAM) {
1068 set_errno(EINVAL); set_vaxc_errno(retsts);
1070 else _ckvmssts_noperl(retsts);
1072 } /* end of vmstrnenv */
1075 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1076 /* Define as a function so we can access statics. */
1077 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1081 #if defined(PERL_IMPLICIT_CONTEXT)
1084 #ifdef SECURE_INTERNAL_GETENV
1085 flags = (PL_curinterp ? PL_tainting : will_taint) ?
1086 PERL__TRNENV_SECURE : 0;
1089 return vmstrnenv(lnm, eqv, idx, fildev, flags);
1094 * Note: Uses Perl temp to store result so char * can be returned to
1095 * caller; this pointer will be invalidated at next Perl statement
1097 * We define this as a function rather than a macro in terms of my_getenv_len()
1098 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1101 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1103 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1106 static char *__my_getenv_eqv = NULL;
1107 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1108 unsigned long int idx = 0;
1109 int success, secure, saverr, savvmserr;
1113 midx = my_maxidx(lnm) + 1;
1115 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1116 /* Set up a temporary buffer for the return value; Perl will
1117 * clean it up at the next statement transition */
1118 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1119 if (!tmpsv) return NULL;
1123 /* Assume no interpreter ==> single thread */
1124 if (__my_getenv_eqv != NULL) {
1125 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1128 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1130 eqv = __my_getenv_eqv;
1133 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1134 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1136 getcwd(eqv,LNM$C_NAMLENGTH);
1140 /* Get rid of "000000/ in rooted filespecs */
1143 zeros = strstr(eqv, "/000000/");
1144 if (zeros != NULL) {
1146 mlen = len - (zeros - eqv) - 7;
1147 memmove(zeros, &zeros[7], mlen);
1155 /* Impose security constraints only if tainting */
1157 /* Impose security constraints only if tainting */
1158 secure = PL_curinterp ? PL_tainting : will_taint;
1159 saverr = errno; savvmserr = vaxc$errno;
1166 #ifdef SECURE_INTERNAL_GETENV
1167 secure ? PERL__TRNENV_SECURE : 0
1173 /* For the getenv interface we combine all the equivalence names
1174 * of a search list logical into one value to acquire a maximum
1175 * value length of 255*128 (assuming %ENV is using logicals).
1177 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1179 /* If the name contains a semicolon-delimited index, parse it
1180 * off and make sure we only retrieve the equivalence name for
1182 if ((cp2 = strchr(lnm,';')) != NULL) {
1184 uplnm[cp2-lnm] = '\0';
1185 idx = strtoul(cp2+1,NULL,0);
1187 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1190 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1192 /* Discard NOLOGNAM on internal calls since we're often looking
1193 * for an optional name, and this "error" often shows up as the
1194 * (bogus) exit status for a die() call later on. */
1195 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1196 return success ? eqv : NULL;
1199 } /* end of my_getenv() */
1203 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1205 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1209 unsigned long idx = 0;
1211 static char *__my_getenv_len_eqv = NULL;
1212 int secure, saverr, savvmserr;
1215 midx = my_maxidx(lnm) + 1;
1217 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1218 /* Set up a temporary buffer for the return value; Perl will
1219 * clean it up at the next statement transition */
1220 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1221 if (!tmpsv) return NULL;
1225 /* Assume no interpreter ==> single thread */
1226 if (__my_getenv_len_eqv != NULL) {
1227 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1230 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1232 buf = __my_getenv_len_eqv;
1235 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1236 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1239 getcwd(buf,LNM$C_NAMLENGTH);
1242 /* Get rid of "000000/ in rooted filespecs */
1244 zeros = strstr(buf, "/000000/");
1245 if (zeros != NULL) {
1247 mlen = *len - (zeros - buf) - 7;
1248 memmove(zeros, &zeros[7], mlen);
1257 /* Impose security constraints only if tainting */
1258 secure = PL_curinterp ? PL_tainting : will_taint;
1259 saverr = errno; savvmserr = vaxc$errno;
1266 #ifdef SECURE_INTERNAL_GETENV
1267 secure ? PERL__TRNENV_SECURE : 0
1273 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1275 if ((cp2 = strchr(lnm,';')) != NULL) {
1277 buf[cp2-lnm] = '\0';
1278 idx = strtoul(cp2+1,NULL,0);
1280 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1283 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1285 /* Get rid of "000000/ in rooted filespecs */
1288 zeros = strstr(buf, "/000000/");
1289 if (zeros != NULL) {
1291 mlen = *len - (zeros - buf) - 7;
1292 memmove(zeros, &zeros[7], mlen);
1298 /* Discard NOLOGNAM on internal calls since we're often looking
1299 * for an optional name, and this "error" often shows up as the
1300 * (bogus) exit status for a die() call later on. */
1301 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1302 return *len ? buf : NULL;
1305 } /* end of my_getenv_len() */
1308 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1310 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1312 /*{{{ void prime_env_iter() */
1314 prime_env_iter(void)
1315 /* Fill the %ENV associative array with all logical names we can
1316 * find, in preparation for iterating over it.
1319 static int primed = 0;
1320 HV *seenhv = NULL, *envhv;
1322 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1323 unsigned short int chan;
1324 #ifndef CLI$M_TRUSTED
1325 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1327 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1328 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0;
1330 bool have_sym = FALSE, have_lnm = FALSE;
1331 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1332 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1333 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1334 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1335 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1336 #if defined(PERL_IMPLICIT_CONTEXT)
1339 #if defined(USE_ITHREADS)
1340 static perl_mutex primenv_mutex;
1341 MUTEX_INIT(&primenv_mutex);
1344 #if defined(PERL_IMPLICIT_CONTEXT)
1345 /* We jump through these hoops because we can be called at */
1346 /* platform-specific initialization time, which is before anything is */
1347 /* set up--we can't even do a plain dTHX since that relies on the */
1348 /* interpreter structure to be initialized */
1350 aTHX = PERL_GET_INTERP;
1352 /* we never get here because the NULL pointer will cause the */
1353 /* several of the routines called by this routine to access violate */
1355 /* This routine is only called by hv.c/hv_iterinit which has a */
1356 /* context, so the real fix may be to pass it through instead of */
1357 /* the hoops above */
1362 if (primed || !PL_envgv) return;
1363 MUTEX_LOCK(&primenv_mutex);
1364 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1365 envhv = GvHVn(PL_envgv);
1366 /* Perform a dummy fetch as an lval to insure that the hash table is
1367 * set up. Otherwise, the hv_store() will turn into a nullop. */
1368 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1370 for (i = 0; env_tables[i]; i++) {
1371 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1372 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1373 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1375 if (have_sym || have_lnm) {
1376 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1377 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1378 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1379 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1382 for (i--; i >= 0; i--) {
1383 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1386 for (j = 0; environ[j]; j++) {
1387 if (!(start = strchr(environ[j],'='))) {
1388 if (ckWARN(WARN_INTERNAL))
1389 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1393 sv = newSVpv(start,0);
1395 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1400 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1401 !str$case_blind_compare(&tmpdsc,&clisym)) {
1402 strcpy(cmd,"Show Symbol/Global *");
1403 cmddsc.dsc$w_length = 20;
1404 if (env_tables[i]->dsc$w_length == 12 &&
1405 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1406 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
1407 flags = defflags | CLI$M_NOLOGNAM;
1410 strcpy(cmd,"Show Logical *");
1411 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1412 strcat(cmd," /Table=");
1413 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1414 cmddsc.dsc$w_length = strlen(cmd);
1416 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1417 flags = defflags | CLI$M_NOCLISYM;
1420 /* Create a new subprocess to execute each command, to exclude the
1421 * remote possibility that someone could subvert a mbx or file used
1422 * to write multiple commands to a single subprocess.
1425 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1426 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1427 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1428 defflags &= ~CLI$M_TRUSTED;
1429 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1431 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1432 if (seenhv) SvREFCNT_dec(seenhv);
1435 char *cp1, *cp2, *key;
1436 unsigned long int sts, iosb[2], retlen, keylen;
1439 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1440 if (sts & 1) sts = iosb[0] & 0xffff;
1441 if (sts == SS$_ENDOFFILE) {
1443 while (substs == 0) { sys$hiber(); wakect++;}
1444 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1449 retlen = iosb[0] >> 16;
1450 if (!retlen) continue; /* blank line */
1452 if (iosb[1] != subpid) {
1454 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1458 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1459 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1461 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1462 if (*cp1 == '(' || /* Logical name table name */
1463 *cp1 == '=' /* Next eqv of searchlist */) continue;
1464 if (*cp1 == '"') cp1++;
1465 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1466 key = cp1; keylen = cp2 - cp1;
1467 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1468 while (*cp2 && *cp2 != '=') cp2++;
1469 while (*cp2 && *cp2 == '=') cp2++;
1470 while (*cp2 && *cp2 == ' ') cp2++;
1471 if (*cp2 == '"') { /* String translation; may embed "" */
1472 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1473 cp2++; cp1--; /* Skip "" surrounding translation */
1475 else { /* Numeric translation */
1476 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1477 cp1--; /* stop on last non-space char */
1479 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1480 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1483 PERL_HASH(hash,key,keylen);
1485 if (cp1 == cp2 && *cp2 == '.') {
1486 /* A single dot usually means an unprintable character, such as a null
1487 * to indicate a zero-length value. Get the actual value to make sure.
1489 char lnm[LNM$C_NAMLENGTH+1];
1490 char eqv[MAX_DCL_SYMBOL+1];
1492 strncpy(lnm, key, keylen);
1493 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1494 sv = newSVpvn(eqv, strlen(eqv));
1497 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1501 hv_store(envhv,key,keylen,sv,hash);
1502 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1504 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1505 /* get the PPFs for this process, not the subprocess */
1506 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1507 char eqv[LNM$C_NAMLENGTH+1];
1509 for (i = 0; ppfs[i]; i++) {
1510 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1511 sv = newSVpv(eqv,trnlen);
1513 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1518 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1519 if (buf) Safefree(buf);
1520 if (seenhv) SvREFCNT_dec(seenhv);
1521 MUTEX_UNLOCK(&primenv_mutex);
1524 } /* end of prime_env_iter */
1528 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1529 /* Define or delete an element in the same "environment" as
1530 * vmstrnenv(). If an element is to be deleted, it's removed from
1531 * the first place it's found. If it's to be set, it's set in the
1532 * place designated by the first element of the table vector.
1533 * Like setenv() returns 0 for success, non-zero on error.
1536 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1539 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1540 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1542 unsigned long int retsts, usermode = PSL$C_USER;
1543 struct itmlst_3 *ile, *ilist;
1544 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1545 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1546 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1547 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1548 $DESCRIPTOR(local,"_LOCAL");
1551 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1552 return SS$_IVLOGNAM;
1555 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1556 *cp2 = _toupper(*cp1);
1557 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1558 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1559 return SS$_IVLOGNAM;
1562 lnmdsc.dsc$w_length = cp1 - lnm;
1563 if (!tabvec || !*tabvec) tabvec = env_tables;
1565 if (!eqv) { /* we're deleting n element */
1566 for (curtab = 0; tabvec[curtab]; curtab++) {
1567 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1569 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1570 if ((cp1 = strchr(environ[i],'=')) &&
1571 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1572 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1574 return setenv(lnm,"",1) ? vaxc$errno : 0;
1577 ivenv = 1; retsts = SS$_NOLOGNAM;
1579 if (ckWARN(WARN_INTERNAL))
1580 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1581 ivenv = 1; retsts = SS$_NOSUCHPGM;
1587 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1588 !str$case_blind_compare(&tmpdsc,&clisym)) {
1589 unsigned int symtype;
1590 if (tabvec[curtab]->dsc$w_length == 12 &&
1591 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1592 !str$case_blind_compare(&tmpdsc,&local))
1593 symtype = LIB$K_CLI_LOCAL_SYM;
1594 else symtype = LIB$K_CLI_GLOBAL_SYM;
1595 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1596 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1597 if (retsts == LIB$_NOSUCHSYM) continue;
1601 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1602 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1603 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1604 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1605 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1609 else { /* we're defining a value */
1610 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1612 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1614 if (ckWARN(WARN_INTERNAL))
1615 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1616 retsts = SS$_NOSUCHPGM;
1620 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1621 eqvdsc.dsc$w_length = strlen(eqv);
1622 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1623 !str$case_blind_compare(&tmpdsc,&clisym)) {
1624 unsigned int symtype;
1625 if (tabvec[0]->dsc$w_length == 12 &&
1626 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1627 !str$case_blind_compare(&tmpdsc,&local))
1628 symtype = LIB$K_CLI_LOCAL_SYM;
1629 else symtype = LIB$K_CLI_GLOBAL_SYM;
1630 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1633 if (!*eqv) eqvdsc.dsc$w_length = 1;
1634 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1636 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1637 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1638 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1639 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1640 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1641 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1644 Newx(ilist,nseg+1,struct itmlst_3);
1647 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1650 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1652 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1653 ile->itmcode = LNM$_STRING;
1655 if ((j+1) == nseg) {
1656 ile->buflen = strlen(c);
1657 /* in case we are truncating one that's too long */
1658 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1661 ile->buflen = LNM$C_NAMLENGTH;
1665 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1669 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1674 if (!(retsts & 1)) {
1676 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1677 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1678 set_errno(EVMSERR); break;
1679 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1680 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1681 set_errno(EINVAL); break;
1683 set_errno(EACCES); break;
1688 set_vaxc_errno(retsts);
1689 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1692 /* We reset error values on success because Perl does an hv_fetch()
1693 * before each hv_store(), and if the thing we're setting didn't
1694 * previously exist, we've got a leftover error message. (Of course,
1695 * this fails in the face of
1696 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1697 * in that the error reported in $! isn't spurious,
1698 * but it's right more often than not.)
1700 set_errno(0); set_vaxc_errno(retsts);
1704 } /* end of vmssetenv() */
1707 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1708 /* This has to be a function since there's a prototype for it in proto.h */
1710 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1713 int len = strlen(lnm);
1717 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1718 if (!strcmp(uplnm,"DEFAULT")) {
1719 if (eqv && *eqv) my_chdir(eqv);
1723 #ifndef RTL_USES_UTC
1724 if (len == 6 || len == 2) {
1727 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1729 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1730 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1734 (void) vmssetenv(lnm,eqv,NULL);
1738 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1740 * sets a user-mode logical in the process logical name table
1741 * used for redirection of sys$error
1743 * Fix-me: The pTHX is not needed for this routine, however doio.c
1744 * is calling it with one instead of using a macro.
1745 * A macro needs to be added to vmsish.h and doio.c updated to use it.
1749 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1751 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1752 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1753 unsigned long int iss, attr = LNM$M_CONFINE;
1754 unsigned char acmode = PSL$C_USER;
1755 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1757 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1758 d_name.dsc$w_length = strlen(name);
1760 lnmlst[0].buflen = strlen(eqv);
1761 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1763 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1764 if (!(iss&1)) lib$signal(iss);
1769 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1770 /* my_crypt - VMS password hashing
1771 * my_crypt() provides an interface compatible with the Unix crypt()
1772 * C library function, and uses sys$hash_password() to perform VMS
1773 * password hashing. The quadword hashed password value is returned
1774 * as a NUL-terminated 8 character string. my_crypt() does not change
1775 * the case of its string arguments; in order to match the behavior
1776 * of LOGINOUT et al., alphabetic characters in both arguments must
1777 * be upcased by the caller.
1779 * - fix me to call ACM services when available
1782 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1784 # ifndef UAI$C_PREFERRED_ALGORITHM
1785 # define UAI$C_PREFERRED_ALGORITHM 127
1787 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1788 unsigned short int salt = 0;
1789 unsigned long int sts;
1791 unsigned short int dsc$w_length;
1792 unsigned char dsc$b_type;
1793 unsigned char dsc$b_class;
1794 const char * dsc$a_pointer;
1795 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1796 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1797 struct itmlst_3 uailst[3] = {
1798 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1799 { sizeof salt, UAI$_SALT, &salt, 0},
1800 { 0, 0, NULL, NULL}};
1801 static char hash[9];
1803 usrdsc.dsc$w_length = strlen(usrname);
1804 usrdsc.dsc$a_pointer = usrname;
1805 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1807 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1811 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1816 set_vaxc_errno(sts);
1817 if (sts != RMS$_RNF) return NULL;
1820 txtdsc.dsc$w_length = strlen(textpasswd);
1821 txtdsc.dsc$a_pointer = textpasswd;
1822 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1823 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1826 return (char *) hash;
1828 } /* end of my_crypt() */
1832 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1833 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1834 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1836 /* fixup barenames that are directories for internal use.
1837 * There have been problems with the consistent handling of UNIX
1838 * style directory names when routines are presented with a name that
1839 * has no directory delimiters at all. So this routine will eventually
1842 static char * fixup_bare_dirnames(const char * name)
1844 if (decc_disable_to_vms_logname_translation) {
1850 /* 8.3, remove() is now broken on symbolic links */
1851 static int rms_erase(const char * vmsname);
1855 * A little hack to get around a bug in some implementation of remove()
1856 * that do not know how to delete a directory
1858 * Delete any file to which user has control access, regardless of whether
1859 * delete access is explicitly allowed.
1860 * Limitations: User must have write access to parent directory.
1861 * Does not block signals or ASTs; if interrupted in midstream
1862 * may leave file with an altered ACL.
1865 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1867 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1871 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1872 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1873 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1875 unsigned char myace$b_length;
1876 unsigned char myace$b_type;
1877 unsigned short int myace$w_flags;
1878 unsigned long int myace$l_access;
1879 unsigned long int myace$l_ident;
1880 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1881 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1882 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1884 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1885 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1886 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1887 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1888 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1889 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1891 /* Expand the input spec using RMS, since the CRTL remove() and
1892 * system services won't do this by themselves, so we may miss
1893 * a file "hiding" behind a logical name or search list. */
1894 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
1895 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1897 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
1899 PerlMem_free(vmsname);
1903 /* Erase the file */
1904 rmsts = rms_erase(vmsname);
1906 /* Did it succeed */
1907 if ($VMS_STATUS_SUCCESS(rmsts)) {
1908 PerlMem_free(vmsname);
1912 /* If not, can changing protections help? */
1913 if (rmsts != RMS$_PRV) {
1914 set_vaxc_errno(rmsts);
1915 PerlMem_free(vmsname);
1919 /* No, so we get our own UIC to use as a rights identifier,
1920 * and the insert an ACE at the head of the ACL which allows us
1921 * to delete the file.
1923 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1924 fildsc.dsc$w_length = strlen(vmsname);
1925 fildsc.dsc$a_pointer = vmsname;
1927 newace.myace$l_ident = oldace.myace$l_ident;
1929 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1931 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1932 set_errno(ENOENT); break;
1934 set_errno(ENOTDIR); break;
1936 set_errno(ENODEV); break;
1937 case RMS$_SYN: case SS$_INVFILFOROP:
1938 set_errno(EINVAL); break;
1940 set_errno(EACCES); break;
1942 _ckvmssts_noperl(aclsts);
1944 set_vaxc_errno(aclsts);
1945 PerlMem_free(vmsname);
1948 /* Grab any existing ACEs with this identifier in case we fail */
1949 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1950 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1951 || fndsts == SS$_NOMOREACE ) {
1952 /* Add the new ACE . . . */
1953 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1956 rmsts = rms_erase(vmsname);
1957 if ($VMS_STATUS_SUCCESS(rmsts)) {
1962 /* We blew it - dir with files in it, no write priv for
1963 * parent directory, etc. Put things back the way they were. */
1964 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1967 addlst[0].bufadr = &oldace;
1968 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1975 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1976 /* We just deleted it, so of course it's not there. Some versions of
1977 * VMS seem to return success on the unlock operation anyhow (after all
1978 * the unlock is successful), but others don't.
1980 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1981 if (aclsts & 1) aclsts = fndsts;
1982 if (!(aclsts & 1)) {
1984 set_vaxc_errno(aclsts);
1987 PerlMem_free(vmsname);
1990 } /* end of kill_file() */
1994 /*{{{int do_rmdir(char *name)*/
1996 Perl_do_rmdir(pTHX_ const char *name)
2002 /* lstat returns a VMS fileified specification of the name */
2003 /* that is looked up, and also lets verifies that this is a directory */
2005 retval = flex_lstat(name, &st);
2009 /* Due to a historical feature, flex_stat/lstat can not see some */
2010 /* Unix format file names that the rest of the CRTL can see */
2011 /* Fixing that feature will cause some perl tests to fail */
2012 /* So try this one more time. */
2014 retval = lstat(name, &st.crtl_stat);
2018 /* force it to a file spec for the kill file to work. */
2019 ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
2020 if (ret_spec == NULL) {
2026 if (!S_ISDIR(st.st_mode)) {
2031 dirfile = st.st_devnam;
2033 /* It may be possible for flex_stat to find a file and vmsify() to */
2034 /* fail with ODS-2 specifications. mp_do_kill_file can not deal */
2035 /* with that case, so fail it */
2036 if (dirfile[0] == 0) {
2041 retval = mp_do_kill_file(aTHX_ dirfile, 1);
2046 } /* end of do_rmdir */
2050 * Delete any file to which user has control access, regardless of whether
2051 * delete access is explicitly allowed.
2052 * Limitations: User must have write access to parent directory.
2053 * Does not block signals or ASTs; if interrupted in midstream
2054 * may leave file with an altered ACL.
2057 /*{{{int kill_file(char *name)*/
2059 Perl_kill_file(pTHX_ const char *name)
2065 /* Convert the filename to VMS format and see if it is a directory */
2066 /* flex_lstat returns a vmsified file specification */
2067 rmsts = flex_lstat(name, &st);
2070 /* Due to a historical feature, flex_stat/lstat can not see some */
2071 /* Unix format file names that the rest of the CRTL can see when */
2072 /* ODS-2 file specifications are in use. */
2073 /* Fixing that feature will cause some perl tests to fail */
2074 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2076 vmsfile = (char *) name; /* cast ok */
2079 vmsfile = st.st_devnam;
2080 if (vmsfile[0] == 0) {
2081 /* It may be possible for flex_stat to find a file and vmsify() */
2082 /* to fail with ODS-2 specifications. mp_do_kill_file can not */
2083 /* deal with that case, so fail it */
2089 /* Remove() is allowed to delete directories, according to the X/Open
2091 * This may need special handling to work with the ACL hacks.
2093 if (S_ISDIR(st.st_mode)) {
2094 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2098 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2100 /* Need to delete all versions ? */
2101 if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2104 /* Just use lstat() here as do not need st_dev */
2105 /* and we know that the file is in VMS format or that */
2106 /* because of a historical bug, flex_stat can not see the file */
2107 while (lstat(vmsfile, (stat_t *)&st) == 0) {
2108 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2113 /* Make sure that we do not loop forever */
2124 } /* end of kill_file() */
2128 /*{{{int my_mkdir(char *,Mode_t)*/
2130 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2132 STRLEN dirlen = strlen(dir);
2134 /* zero length string sometimes gives ACCVIO */
2135 if (dirlen == 0) return -1;
2137 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2138 * null file name/type. However, it's commonplace under Unix,
2139 * so we'll allow it for a gain in portability.
2141 if (dir[dirlen-1] == '/') {
2142 char *newdir = savepvn(dir,dirlen-1);
2143 int ret = mkdir(newdir,mode);
2147 else return mkdir(dir,mode);
2148 } /* end of my_mkdir */
2151 /*{{{int my_chdir(char *)*/
2153 Perl_my_chdir(pTHX_ const char *dir)
2155 STRLEN dirlen = strlen(dir);
2157 /* zero length string sometimes gives ACCVIO */
2158 if (dirlen == 0) return -1;
2161 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2162 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2163 * so that existing scripts do not need to be changed.
2166 while ((dirlen > 0) && (*dir1 == ' ')) {
2171 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2173 * null file name/type. However, it's commonplace under Unix,
2174 * so we'll allow it for a gain in portability.
2176 * '/' is valid when SYS$POSIX_ROOT or POSIX compliant pathnames are active.
2178 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2181 newdir = PerlMem_malloc(dirlen);
2183 _ckvmssts_noperl(SS$_INSFMEM);
2184 strncpy(newdir, dir1, dirlen-1);
2185 newdir[dirlen-1] = '\0';
2186 ret = chdir(newdir);
2187 PerlMem_free(newdir);
2190 else return chdir(dir1);
2191 } /* end of my_chdir */
2195 /*{{{int my_chmod(char *, mode_t)*/
2197 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2202 STRLEN speclen = strlen(file_spec);
2204 /* zero length string sometimes gives ACCVIO */
2205 if (speclen == 0) return -1;
2207 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2208 * that implies null file name/type. However, it's commonplace under Unix,
2209 * so we'll allow it for a gain in portability.
2211 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2212 * in VMS file.dir notation.
2214 changefile = (char *) file_spec; /* cast ok */
2215 ret = flex_lstat(file_spec, &st);
2218 /* Due to a historical feature, flex_stat/lstat can not see some */
2219 /* Unix format file names that the rest of the CRTL can see when */
2220 /* ODS-2 file specifications are in use. */
2221 /* Fixing that feature will cause some perl tests to fail */
2222 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2226 /* It may be possible to get here with nothing in st_devname */
2227 /* chmod still may work though */
2228 if (st.st_devnam[0] != 0) {
2229 changefile = st.st_devnam;
2232 ret = chmod(changefile, mode);
2234 } /* end of my_chmod */
2238 /*{{{FILE *my_tmpfile()*/
2245 if ((fp = tmpfile())) return fp;
2247 cp = PerlMem_malloc(L_tmpnam+24);
2248 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2250 if (decc_filename_unix_only == 0)
2251 strcpy(cp,"Sys$Scratch:");
2254 tmpnam(cp+strlen(cp));
2255 strcat(cp,".Perltmp");
2256 fp = fopen(cp,"w+","fop=dlt");
2263 #ifndef HOMEGROWN_POSIX_SIGNALS
2265 * The C RTL's sigaction fails to check for invalid signal numbers so we
2266 * help it out a bit. The docs are correct, but the actual routine doesn't
2267 * do what the docs say it will.
2269 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2271 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2272 struct sigaction* oact)
2274 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2275 SETERRNO(EINVAL, SS$_INVARG);
2278 return sigaction(sig, act, oact);
2283 #ifdef KILL_BY_SIGPRC
2284 #include <errnodef.h>
2286 /* We implement our own kill() using the undocumented system service
2287 sys$sigprc for one of two reasons:
2289 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2290 target process to do a sys$exit, which usually can't be handled
2291 gracefully...certainly not by Perl and the %SIG{} mechanism.
2293 2.) If the kill() in the CRTL can't be called from a signal
2294 handler without disappearing into the ether, i.e., the signal
2295 it purportedly sends is never trapped. Still true as of VMS 7.3.
2297 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2298 in the target process rather than calling sys$exit.
2300 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2301 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2302 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2303 with condition codes C$_SIG0+nsig*8, catching the exception on the
2304 target process and resignaling with appropriate arguments.
2306 But we don't have that VMS 7.0+ exception handler, so if you
2307 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2309 Also note that SIGTERM is listed in the docs as being "unimplemented",
2310 yet always seems to be signaled with a VMS condition code of 4 (and
2311 correctly handled for that code). So we hardwire it in.
2313 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2314 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2315 than signalling with an unrecognized (and unhandled by CRTL) code.
2318 #define _MY_SIG_MAX 28
2321 Perl_sig_to_vmscondition_int(int sig)
2323 static unsigned int sig_code[_MY_SIG_MAX+1] =
2326 SS$_HANGUP, /* 1 SIGHUP */
2327 SS$_CONTROLC, /* 2 SIGINT */
2328 SS$_CONTROLY, /* 3 SIGQUIT */
2329 SS$_RADRMOD, /* 4 SIGILL */
2330 SS$_BREAK, /* 5 SIGTRAP */
2331 SS$_OPCCUS, /* 6 SIGABRT */
2332 SS$_COMPAT, /* 7 SIGEMT */
2334 SS$_FLTOVF, /* 8 SIGFPE VAX */
2336 SS$_HPARITH, /* 8 SIGFPE AXP */
2338 SS$_ABORT, /* 9 SIGKILL */
2339 SS$_ACCVIO, /* 10 SIGBUS */
2340 SS$_ACCVIO, /* 11 SIGSEGV */
2341 SS$_BADPARAM, /* 12 SIGSYS */
2342 SS$_NOMBX, /* 13 SIGPIPE */
2343 SS$_ASTFLT, /* 14 SIGALRM */
2360 #if __VMS_VER >= 60200000
2361 static int initted = 0;
2364 sig_code[16] = C$_SIGUSR1;
2365 sig_code[17] = C$_SIGUSR2;
2366 #if __CRTL_VER >= 70000000
2367 sig_code[20] = C$_SIGCHLD;
2369 #if __CRTL_VER >= 70300000
2370 sig_code[28] = C$_SIGWINCH;
2375 if (sig < _SIG_MIN) return 0;
2376 if (sig > _MY_SIG_MAX) return 0;
2377 return sig_code[sig];
2381 Perl_sig_to_vmscondition(int sig)
2384 if (vms_debug_on_exception != 0)
2385 lib$signal(SS$_DEBUG);
2387 return Perl_sig_to_vmscondition_int(sig);
2392 Perl_my_kill(int pid, int sig)
2396 #define sys$sigprc SYS$SIGPRC
2397 int sys$sigprc(unsigned int *pidadr,
2398 struct dsc$descriptor_s *prcname,
2401 /* sig 0 means validate the PID */
2402 /*------------------------------*/
2404 const unsigned long int jpicode = JPI$_PID;
2407 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2408 if ($VMS_STATUS_SUCCESS(status))
2411 case SS$_NOSUCHNODE:
2412 case SS$_UNREACHABLE:
2426 code = Perl_sig_to_vmscondition_int(sig);
2429 SETERRNO(EINVAL, SS$_BADPARAM);
2433 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2434 * signals are to be sent to multiple processes.
2435 * pid = 0 - all processes in group except ones that the system exempts
2436 * pid = -1 - all processes except ones that the system exempts
2437 * pid = -n - all processes in group (abs(n)) except ...
2438 * For now, just report as not supported.
2442 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2446 iss = sys$sigprc((unsigned int *)&pid,0,code);
2447 if (iss&1) return 0;
2451 set_errno(EPERM); break;
2453 case SS$_NOSUCHNODE:
2454 case SS$_UNREACHABLE:
2455 set_errno(ESRCH); break;
2457 set_errno(ENOMEM); break;
2459 _ckvmssts_noperl(iss);
2462 set_vaxc_errno(iss);
2468 /* Routine to convert a VMS status code to a UNIX status code.
2469 ** More tricky than it appears because of conflicting conventions with
2472 ** VMS status codes are a bit mask, with the least significant bit set for
2475 ** Special UNIX status of EVMSERR indicates that no translation is currently
2476 ** available, and programs should check the VMS status code.
2478 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2482 #ifndef C_FACILITY_NO
2483 #define C_FACILITY_NO 0x350000
2486 #define DCL_IVVERB 0x38090
2489 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2497 /* Assume the best or the worst */
2498 if (vms_status & STS$M_SUCCESS)
2501 unix_status = EVMSERR;
2503 msg_status = vms_status & ~STS$M_CONTROL;
2505 facility = vms_status & STS$M_FAC_NO;
2506 fac_sp = vms_status & STS$M_FAC_SP;
2507 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2509 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2515 unix_status = EFAULT;
2517 case SS$_DEVOFFLINE:
2518 unix_status = EBUSY;
2521 unix_status = ENOTCONN;
2529 case SS$_INVFILFOROP:
2533 unix_status = EINVAL;
2535 case SS$_UNSUPPORTED:
2536 unix_status = ENOTSUP;
2541 unix_status = EACCES;
2543 case SS$_DEVICEFULL:
2544 unix_status = ENOSPC;
2547 unix_status = ENODEV;
2549 case SS$_NOSUCHFILE:
2550 case SS$_NOSUCHOBJECT:
2551 unix_status = ENOENT;
2553 case SS$_ABORT: /* Fatal case */
2554 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2555 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2556 unix_status = EINTR;
2559 unix_status = E2BIG;
2562 unix_status = ENOMEM;
2565 unix_status = EPERM;
2567 case SS$_NOSUCHNODE:
2568 case SS$_UNREACHABLE:
2569 unix_status = ESRCH;
2572 unix_status = ECHILD;
2575 if ((facility == 0) && (msg_no < 8)) {
2576 /* These are not real VMS status codes so assume that they are
2577 ** already UNIX status codes
2579 unix_status = msg_no;
2585 /* Translate a POSIX exit code to a UNIX exit code */
2586 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2587 unix_status = (msg_no & 0x07F8) >> 3;
2591 /* Documented traditional behavior for handling VMS child exits */
2592 /*--------------------------------------------------------------*/
2593 if (child_flag != 0) {
2595 /* Success / Informational return 0 */
2596 /*----------------------------------*/
2597 if (msg_no & STS$K_SUCCESS)
2600 /* Warning returns 1 */
2601 /*-------------------*/
2602 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2605 /* Everything else pass through the severity bits */
2606 /*------------------------------------------------*/
2607 return (msg_no & STS$M_SEVERITY);
2610 /* Normal VMS status to ERRNO mapping attempt */
2611 /*--------------------------------------------*/
2612 switch(msg_status) {
2613 /* case RMS$_EOF: */ /* End of File */
2614 case RMS$_FNF: /* File Not Found */
2615 case RMS$_DNF: /* Dir Not Found */
2616 unix_status = ENOENT;
2618 case RMS$_RNF: /* Record Not Found */
2619 unix_status = ESRCH;
2622 unix_status = ENOTDIR;
2625 unix_status = ENODEV;
2630 unix_status = EBADF;
2633 unix_status = EEXIST;
2637 case LIB$_INVSTRDES:
2639 case LIB$_NOSUCHSYM:
2640 case LIB$_INVSYMNAM:
2642 unix_status = EINVAL;
2648 unix_status = E2BIG;
2650 case RMS$_PRV: /* No privilege */
2651 case RMS$_ACC: /* ACP file access failed */
2652 case RMS$_WLK: /* Device write locked */
2653 unix_status = EACCES;
2655 case RMS$_MKD: /* Failed to mark for delete */
2656 unix_status = EPERM;
2658 /* case RMS$_NMF: */ /* No more files */
2666 /* Try to guess at what VMS error status should go with a UNIX errno
2667 * value. This is hard to do as there could be many possible VMS
2668 * error statuses that caused the errno value to be set.
2671 int Perl_unix_status_to_vms(int unix_status)
2673 int test_unix_status;
2675 /* Trivial cases first */
2676 /*---------------------*/
2677 if (unix_status == EVMSERR)
2680 /* Is vaxc$errno sane? */
2681 /*---------------------*/
2682 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2683 if (test_unix_status == unix_status)
2686 /* If way out of range, must be VMS code already */
2687 /*-----------------------------------------------*/
2688 if (unix_status > EVMSERR)
2691 /* If out of range, punt */
2692 /*-----------------------*/
2693 if (unix_status > __ERRNO_MAX)
2697 /* Ok, now we have to do it the hard way. */
2698 /*----------------------------------------*/
2699 switch(unix_status) {
2700 case 0: return SS$_NORMAL;
2701 case EPERM: return SS$_NOPRIV;
2702 case ENOENT: return SS$_NOSUCHOBJECT;
2703 case ESRCH: return SS$_UNREACHABLE;
2704 case EINTR: return SS$_ABORT;
2707 case E2BIG: return SS$_BUFFEROVF;
2709 case EBADF: return RMS$_IFI;
2710 case ECHILD: return SS$_NONEXPR;
2712 case ENOMEM: return SS$_INSFMEM;
2713 case EACCES: return SS$_FILACCERR;
2714 case EFAULT: return SS$_ACCVIO;
2716 case EBUSY: return SS$_DEVOFFLINE;
2717 case EEXIST: return RMS$_FEX;
2719 case ENODEV: return SS$_NOSUCHDEV;
2720 case ENOTDIR: return RMS$_DIR;
2722 case EINVAL: return SS$_INVARG;
2728 case ENOSPC: return SS$_DEVICEFULL;
2729 case ESPIPE: return LIB$_INVARG;
2734 case ERANGE: return LIB$_INVARG;
2735 /* case EWOULDBLOCK */
2736 /* case EINPROGRESS */
2739 /* case EDESTADDRREQ */
2741 /* case EPROTOTYPE */
2742 /* case ENOPROTOOPT */
2743 /* case EPROTONOSUPPORT */
2744 /* case ESOCKTNOSUPPORT */
2745 /* case EOPNOTSUPP */
2746 /* case EPFNOSUPPORT */
2747 /* case EAFNOSUPPORT */
2748 /* case EADDRINUSE */
2749 /* case EADDRNOTAVAIL */
2751 /* case ENETUNREACH */
2752 /* case ENETRESET */
2753 /* case ECONNABORTED */
2754 /* case ECONNRESET */
2757 case ENOTCONN: return SS$_CLEARED;
2758 /* case ESHUTDOWN */
2759 /* case ETOOMANYREFS */
2760 /* case ETIMEDOUT */
2761 /* case ECONNREFUSED */
2763 /* case ENAMETOOLONG */
2764 /* case EHOSTDOWN */
2765 /* case EHOSTUNREACH */
2766 /* case ENOTEMPTY */
2778 /* case ECANCELED */
2782 return SS$_UNSUPPORTED;
2788 /* case EABANDONED */
2790 return SS$_ABORT; /* punt */
2795 /* default piping mailbox size */
2797 # define PERL_BUFSIZ 512
2799 # define PERL_BUFSIZ 8192
2804 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2806 unsigned long int mbxbufsiz;
2807 static unsigned long int syssize = 0;
2808 unsigned long int dviitm = DVI$_DEVNAM;
2809 char csize[LNM$C_NAMLENGTH+1];
2813 unsigned long syiitm = SYI$_MAXBUF;
2815 * Get the SYSGEN parameter MAXBUF
2817 * If the logical 'PERL_MBX_SIZE' is defined
2818 * use the value of the logical instead of PERL_BUFSIZ, but
2819 * keep the size between 128 and MAXBUF.
2822 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2825 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2826 mbxbufsiz = atoi(csize);
2828 mbxbufsiz = PERL_BUFSIZ;
2830 if (mbxbufsiz < 128) mbxbufsiz = 128;
2831 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2833 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2835 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2836 _ckvmssts_noperl(sts);
2837 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2839 } /* end of create_mbx() */
2842 /*{{{ my_popen and my_pclose*/
2844 typedef struct _iosb IOSB;
2845 typedef struct _iosb* pIOSB;
2846 typedef struct _pipe Pipe;
2847 typedef struct _pipe* pPipe;
2848 typedef struct pipe_details Info;
2849 typedef struct pipe_details* pInfo;
2850 typedef struct _srqp RQE;
2851 typedef struct _srqp* pRQE;
2852 typedef struct _tochildbuf CBuf;
2853 typedef struct _tochildbuf* pCBuf;
2856 unsigned short status;
2857 unsigned short count;
2858 unsigned long dvispec;
2861 #pragma member_alignment save
2862 #pragma nomember_alignment quadword
2863 struct _srqp { /* VMS self-relative queue entry */
2864 unsigned long qptr[2];
2866 #pragma member_alignment restore
2867 static RQE RQE_ZERO = {0,0};
2869 struct _tochildbuf {
2872 unsigned short size;
2880 unsigned short chan_in;
2881 unsigned short chan_out;
2883 unsigned int bufsize;
2895 #if defined(PERL_IMPLICIT_CONTEXT)
2896 void *thx; /* Either a thread or an interpreter */
2897 /* pointer, depending on how we're built */
2905 PerlIO *fp; /* file pointer to pipe mailbox */
2906 int useFILE; /* using stdio, not perlio */
2907 int pid; /* PID of subprocess */
2908 int mode; /* == 'r' if pipe open for reading */
2909 int done; /* subprocess has completed */
2910 int waiting; /* waiting for completion/closure */
2911 int closing; /* my_pclose is closing this pipe */
2912 unsigned long completion; /* termination status of subprocess */
2913 pPipe in; /* pipe in to sub */
2914 pPipe out; /* pipe out of sub */
2915 pPipe err; /* pipe of sub's sys$error */
2916 int in_done; /* true when in pipe finished */
2919 unsigned short xchan; /* channel to debug xterm */
2920 unsigned short xchan_valid; /* channel is assigned */
2923 struct exit_control_block
2925 struct exit_control_block *flink;
2926 unsigned long int (*exit_routine)();
2927 unsigned long int arg_count;
2928 unsigned long int *status_address;
2929 unsigned long int exit_status;
2932 typedef struct _closed_pipes Xpipe;
2933 typedef struct _closed_pipes* pXpipe;
2935 struct _closed_pipes {
2936 int pid; /* PID of subprocess */
2937 unsigned long completion; /* termination status of subprocess */
2939 #define NKEEPCLOSED 50
2940 static Xpipe closed_list[NKEEPCLOSED];
2941 static int closed_index = 0;
2942 static int closed_num = 0;
2944 #define RETRY_DELAY "0 ::0.20"
2945 #define MAX_RETRY 50
2947 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2948 static unsigned long mypid;
2949 static unsigned long delaytime[2];
2951 static pInfo open_pipes = NULL;
2952 static $DESCRIPTOR(nl_desc, "NL:");
2954 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2958 static unsigned long int
2962 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2963 int sts, did_stuff, j;
2966 * Flush any pending i/o, but since we are in process run-down, be
2967 * careful about referencing PerlIO structures that may already have
2968 * been deallocated. We may not even have an interpreter anymore.
2973 #if defined(PERL_IMPLICIT_CONTEXT)
2974 /* We need to use the Perl context of the thread that created */
2978 aTHX = info->err->thx;
2980 aTHX = info->out->thx;
2982 aTHX = info->in->thx;
2985 #if defined(USE_ITHREADS)
2989 && PL_perlio_fd_refcnt
2992 PerlIO_flush(info->fp);
2994 fflush((FILE *)info->fp);
3000 next we try sending an EOF...ignore if doesn't work, make sure we
3007 _ckvmssts_noperl(sys$setast(0));
3008 if (info->in && !info->in->shut_on_empty) {
3009 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3014 _ckvmssts_noperl(sys$setast(1));
3018 /* wait for EOF to have effect, up to ~ 30 sec [default] */
3020 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3025 _ckvmssts_noperl(sys$setast(0));
3026 if (info->waiting && info->done)
3028 nwait += info->waiting;
3029 _ckvmssts_noperl(sys$setast(1));
3039 _ckvmssts_noperl(sys$setast(0));
3040 if (!info->done) { /* Tap them gently on the shoulder . . .*/
3041 sts = sys$forcex(&info->pid,0,&abort);
3042 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3045 _ckvmssts_noperl(sys$setast(1));
3049 /* again, wait for effect */
3051 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3056 _ckvmssts_noperl(sys$setast(0));
3057 if (info->waiting && info->done)
3059 nwait += info->waiting;
3060 _ckvmssts_noperl(sys$setast(1));
3069 _ckvmssts_noperl(sys$setast(0));
3070 if (!info->done) { /* We tried to be nice . . . */
3071 sts = sys$delprc(&info->pid,0);
3072 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3073 info->done = 1; /* sys$delprc is as done as we're going to get. */
3075 _ckvmssts_noperl(sys$setast(1));
3081 #if defined(PERL_IMPLICIT_CONTEXT)
3082 /* We need to use the Perl context of the thread that created */
3085 if (open_pipes->err)
3086 aTHX = open_pipes->err->thx;
3087 else if (open_pipes->out)
3088 aTHX = open_pipes->out->thx;
3089 else if (open_pipes->in)
3090 aTHX = open_pipes->in->thx;
3092 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3093 else if (!(sts & 1)) retsts = sts;
3098 static struct exit_control_block pipe_exitblock =
3099 {(struct exit_control_block *) 0,
3100 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3102 static void pipe_mbxtofd_ast(pPipe p);
3103 static void pipe_tochild1_ast(pPipe p);
3104 static void pipe_tochild2_ast(pPipe p);
3107 popen_completion_ast(pInfo info)
3109 pInfo i = open_pipes;
3112 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3113 closed_list[closed_index].pid = info->pid;
3114 closed_list[closed_index].completion = info->completion;
3116 if (closed_index == NKEEPCLOSED)
3121 if (i == info) break;
3124 if (!i) return; /* unlinked, probably freed too */
3129 Writing to subprocess ...
3130 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3132 chan_out may be waiting for "done" flag, or hung waiting
3133 for i/o completion to child...cancel the i/o. This will
3134 put it into "snarf mode" (done but no EOF yet) that discards
3137 Output from subprocess (stdout, stderr) needs to be flushed and
3138 shut down. We try sending an EOF, but if the mbx is full the pipe
3139 routine should still catch the "shut_on_empty" flag, telling it to
3140 use immediate-style reads so that "mbx empty" -> EOF.
3144 if (info->in && !info->in_done) { /* only for mode=w */
3145 if (info->in->shut_on_empty && info->in->need_wake) {
3146 info->in->need_wake = FALSE;
3147 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3149 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3153 if (info->out && !info->out_done) { /* were we also piping output? */
3154 info->out->shut_on_empty = TRUE;
3155 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3156 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3157 _ckvmssts_noperl(iss);
3160 if (info->err && !info->err_done) { /* we were piping stderr */
3161 info->err->shut_on_empty = TRUE;
3162 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3163 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3164 _ckvmssts_noperl(iss);
3166 _ckvmssts_noperl(sys$setef(pipe_ef));
3170 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3171 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3174 we actually differ from vmstrnenv since we use this to
3175 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
3176 are pointing to the same thing
3179 static unsigned short
3180 popen_translate(pTHX_ char *logical, char *result)
3183 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
3184 $DESCRIPTOR(d_log,"");
3186 unsigned short length;
3187 unsigned short code;
3189 unsigned short *retlenaddr;
3191 unsigned short l, ifi;
3193 d_log.dsc$a_pointer = logical;
3194 d_log.dsc$w_length = strlen(logical);
3196 itmlst[0].code = LNM$_STRING;
3197 itmlst[0].length = 255;
3198 itmlst[0].buffer_addr = result;
3199 itmlst[0].retlenaddr = &l;
3202 itmlst[1].length = 0;
3203 itmlst[1].buffer_addr = 0;
3204 itmlst[1].retlenaddr = 0;
3206 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3207 if (iss == SS$_NOLOGNAM) {
3211 if (!(iss&1)) lib$signal(iss);
3214 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
3215 strip it off and return the ifi, if any
3218 if (result[0] == 0x1b && result[1] == 0x00) {
3219 memmove(&ifi,result+2,2);
3220 strcpy(result,result+4);
3222 return ifi; /* this is the RMS internal file id */
3225 static void pipe_infromchild_ast(pPipe p);
3228 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3229 inside an AST routine without worrying about reentrancy and which Perl
3230 memory allocator is being used.
3232 We read data and queue up the buffers, then spit them out one at a
3233 time to the output mailbox when the output mailbox is ready for one.
3236 #define INITIAL_TOCHILDQUEUE 2
3239 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3243 char mbx1[64], mbx2[64];
3244 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3245 DSC$K_CLASS_S, mbx1},
3246 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3247 DSC$K_CLASS_S, mbx2};
3248 unsigned int dviitm = DVI$_DEVBUFSIZ;
3252 _ckvmssts_noperl(lib$get_vm(&n, &p));
3254 create_mbx(&p->chan_in , &d_mbx1);
3255 create_mbx(&p->chan_out, &d_mbx2);
3256 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3259 p->shut_on_empty = FALSE;
3260 p->need_wake = FALSE;
3263 p->iosb.status = SS$_NORMAL;
3264 p->iosb2.status = SS$_NORMAL;
3270 #ifdef PERL_IMPLICIT_CONTEXT
3274 n = sizeof(CBuf) + p->bufsize;
3276 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3277 _ckvmssts_noperl(lib$get_vm(&n, &b));
3278 b->buf = (char *) b + sizeof(CBuf);
3279 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3282 pipe_tochild2_ast(p);
3283 pipe_tochild1_ast(p);
3289 /* reads the MBX Perl is writing, and queues */
3292 pipe_tochild1_ast(pPipe p)
3295 int iss = p->iosb.status;
3296 int eof = (iss == SS$_ENDOFFILE);
3298 #ifdef PERL_IMPLICIT_CONTEXT
3304 p->shut_on_empty = TRUE;
3306 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3308 _ckvmssts_noperl(iss);
3312 b->size = p->iosb.count;
3313 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3315 p->need_wake = FALSE;
3316 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3319 p->retry = 1; /* initial call */
3322 if (eof) { /* flush the free queue, return when done */
3323 int n = sizeof(CBuf) + p->bufsize;
3325 iss = lib$remqti(&p->free, &b);
3326 if (iss == LIB$_QUEWASEMP) return;
3327 _ckvmssts_noperl(iss);
3328 _ckvmssts_noperl(lib$free_vm(&n, &b));
3332 iss = lib$remqti(&p->free, &b);
3333 if (iss == LIB$_QUEWASEMP) {
3334 int n = sizeof(CBuf) + p->bufsize;
3335 _ckvmssts_noperl(lib$get_vm(&n, &b));
3336 b->buf = (char *) b + sizeof(CBuf);
3338 _ckvmssts_noperl(iss);
3342 iss = sys$qio(0,p->chan_in,
3343 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3345 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3346 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3347 _ckvmssts_noperl(iss);
3351 /* writes queued buffers to output, waits for each to complete before
3355 pipe_tochild2_ast(pPipe p)
3358 int iss = p->iosb2.status;
3359 int n = sizeof(CBuf) + p->bufsize;
3360 int done = (p->info && p->info->done) ||
3361 iss == SS$_CANCEL || iss == SS$_ABORT;
3362 #if defined(PERL_IMPLICIT_CONTEXT)
3367 if (p->type) { /* type=1 has old buffer, dispose */
3368 if (p->shut_on_empty) {
3369 _ckvmssts_noperl(lib$free_vm(&n, &b));
3371 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3376 iss = lib$remqti(&p->wait, &b);
3377 if (iss == LIB$_QUEWASEMP) {
3378 if (p->shut_on_empty) {
3380 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3381 *p->pipe_done = TRUE;
3382 _ckvmssts_noperl(sys$setef(pipe_ef));
3384 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3385 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3389 p->need_wake = TRUE;
3392 _ckvmssts_noperl(iss);
3399 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3400 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3402 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3403 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3412 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3415 char mbx1[64], mbx2[64];
3416 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3417 DSC$K_CLASS_S, mbx1},
3418 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3419 DSC$K_CLASS_S, mbx2};
3420 unsigned int dviitm = DVI$_DEVBUFSIZ;
3422 int n = sizeof(Pipe);
3423 _ckvmssts_noperl(lib$get_vm(&n, &p));
3424 create_mbx(&p->chan_in , &d_mbx1);
3425 create_mbx(&p->chan_out, &d_mbx2);
3427 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3428 n = p->bufsize * sizeof(char);
3429 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3430 p->shut_on_empty = FALSE;
3433 p->iosb.status = SS$_NORMAL;
3434 #if defined(PERL_IMPLICIT_CONTEXT)
3437 pipe_infromchild_ast(p);
3445 pipe_infromchild_ast(pPipe p)
3447 int iss = p->iosb.status;
3448 int eof = (iss == SS$_ENDOFFILE);
3449 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3450 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3451 #if defined(PERL_IMPLICIT_CONTEXT)
3455 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3456 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3461 input shutdown if EOF from self (done or shut_on_empty)
3462 output shutdown if closing flag set (my_pclose)
3463 send data/eof from child or eof from self
3464 otherwise, re-read (snarf of data from child)
3469 if (myeof && p->chan_in) { /* input shutdown */
3470 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3475 if (myeof || kideof) { /* pass EOF to parent */
3476 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3477 pipe_infromchild_ast, p,
3480 } else if (eof) { /* eat EOF --- fall through to read*/
3482 } else { /* transmit data */
3483 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3484 pipe_infromchild_ast,p,
3485 p->buf, p->iosb.count, 0, 0, 0, 0));
3491 /* everything shut? flag as done */
3493 if (!p->chan_in && !p->chan_out) {
3494 *p->pipe_done = TRUE;
3495 _ckvmssts_noperl(sys$setef(pipe_ef));
3499 /* write completed (or read, if snarfing from child)
3500 if still have input active,
3501 queue read...immediate mode if shut_on_empty so we get EOF if empty
3503 check if Perl reading, generate EOFs as needed
3509 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3510 pipe_infromchild_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);
3514 } else { /* send EOFs for extra reads */
3515 p->iosb.status = SS$_ENDOFFILE;
3516 p->iosb.dvispec = 0;
3517 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3519 pipe_infromchild_ast, p, 0, 0, 0, 0));
3525 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3529 unsigned long dviitm = DVI$_DEVBUFSIZ;
3531 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3532 DSC$K_CLASS_S, mbx};
3533 int n = sizeof(Pipe);
3535 /* things like terminals and mbx's don't need this filter */
3536 if (fd && fstat(fd,&s) == 0) {
3537 unsigned long devchar;
3539 unsigned short dev_len;
3540 struct dsc$descriptor_s d_dev;
3542 struct item_list_3 items[3];
3544 unsigned short dvi_iosb[4];
3546 cptr = getname(fd, out, 1);
3547 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3548 d_dev.dsc$a_pointer = out;
3549 d_dev.dsc$w_length = strlen(out);
3550 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3551 d_dev.dsc$b_class = DSC$K_CLASS_S;
3554 items[0].code = DVI$_DEVCHAR;
3555 items[0].bufadr = &devchar;
3556 items[0].retadr = NULL;
3558 items[1].code = DVI$_FULLDEVNAM;
3559 items[1].bufadr = device;
3560 items[1].retadr = &dev_len;
3564 status = sys$getdviw
3565 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3566 _ckvmssts_noperl(status);
3567 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3568 device[dev_len] = 0;
3570 if (!(devchar & DEV$M_DIR)) {
3571 strcpy(out, device);
3577 _ckvmssts_noperl(lib$get_vm(&n, &p));
3578 p->fd_out = dup(fd);
3579 create_mbx(&p->chan_in, &d_mbx);
3580 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3581 n = (p->bufsize+1) * sizeof(char);
3582 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3583 p->shut_on_empty = FALSE;
3588 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3589 pipe_mbxtofd_ast, p,
3590 p->buf, p->bufsize, 0, 0, 0, 0));
3596 pipe_mbxtofd_ast(pPipe p)
3598 int iss = p->iosb.status;
3599 int done = p->info->done;
3601 int eof = (iss == SS$_ENDOFFILE);
3602 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3603 int err = !(iss&1) && !eof;
3604 #if defined(PERL_IMPLICIT_CONTEXT)
3608 if (done && myeof) { /* end piping */
3610 sys$dassgn(p->chan_in);
3611 *p->pipe_done = TRUE;
3612 _ckvmssts_noperl(sys$setef(pipe_ef));
3616 if (!err && !eof) { /* good data to send to file */
3617 p->buf[p->iosb.count] = '\n';
3618 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3621 if (p->retry < MAX_RETRY) {
3622 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3628 _ckvmssts_noperl(iss);
3632 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3633 pipe_mbxtofd_ast, p,
3634 p->buf, p->bufsize, 0, 0, 0, 0);
3635 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3636 _ckvmssts_noperl(iss);
3640 typedef struct _pipeloc PLOC;
3641 typedef struct _pipeloc* pPLOC;
3645 char dir[NAM$C_MAXRSS+1];
3647 static pPLOC head_PLOC = 0;
3650 free_pipelocs(pTHX_ void *head)
3653 pPLOC *pHead = (pPLOC *)head;
3665 store_pipelocs(pTHX)
3673 char temp[NAM$C_MAXRSS+1];
3677 free_pipelocs(aTHX_ &head_PLOC);
3679 /* the . directory from @INC comes last */
3681 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3682 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3683 p->next = head_PLOC;
3685 strcpy(p->dir,"./");
3687 /* get the directory from $^X */
3689 unixdir = PerlMem_malloc(VMS_MAXRSS);
3690 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3692 #ifdef PERL_IMPLICIT_CONTEXT
3693 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3695 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3697 strcpy(temp, PL_origargv[0]);
3698 x = strrchr(temp,']');
3700 x = strrchr(temp,'>');
3702 /* It could be a UNIX path */
3703 x = strrchr(temp,'/');
3709 /* Got a bare name, so use default directory */
3714 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3715 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3716 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3717 p->next = head_PLOC;
3719 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3720 p->dir[NAM$C_MAXRSS] = '\0';
3724 /* reverse order of @INC entries, skip "." since entered above */
3726 #ifdef PERL_IMPLICIT_CONTEXT
3729 if (PL_incgv) av = GvAVn(PL_incgv);
3731 for (i = 0; av && i <= AvFILL(av); i++) {
3732 dirsv = *av_fetch(av,i,TRUE);
3734 if (SvROK(dirsv)) continue;
3735 dir = SvPVx(dirsv,n_a);
3736 if (strcmp(dir,".") == 0) continue;
3737 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3740 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3741 p->next = head_PLOC;
3743 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3744 p->dir[NAM$C_MAXRSS] = '\0';
3747 /* most likely spot (ARCHLIB) put first in the list */
3750 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3751 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3752 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3753 p->next = head_PLOC;
3755 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3756 p->dir[NAM$C_MAXRSS] = '\0';
3759 PerlMem_free(unixdir);
3763 Perl_cando_by_name_int
3764 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3765 #if !defined(PERL_IMPLICIT_CONTEXT)
3766 #define cando_by_name_int Perl_cando_by_name_int
3768 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3774 static int vmspipe_file_status = 0;
3775 static char vmspipe_file[NAM$C_MAXRSS+1];
3777 /* already found? Check and use ... need read+execute permission */
3779 if (vmspipe_file_status == 1) {
3780 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3781 && cando_by_name_int
3782 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3783 return vmspipe_file;
3785 vmspipe_file_status = 0;
3788 /* scan through stored @INC, $^X */
3790 if (vmspipe_file_status == 0) {
3791 char file[NAM$C_MAXRSS+1];
3792 pPLOC p = head_PLOC;
3797 strcpy(file, p->dir);
3798 dirlen = strlen(file);
3799 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
3800 file[NAM$C_MAXRSS] = '\0';
3803 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3804 if (!exp_res) continue;
3806 if (cando_by_name_int
3807 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3808 && cando_by_name_int
3809 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3810 vmspipe_file_status = 1;
3811 return vmspipe_file;
3814 vmspipe_file_status = -1; /* failed, use tempfiles */
3821 vmspipe_tempfile(pTHX)
3823 char file[NAM$C_MAXRSS+1];
3825 static int index = 0;
3829 /* create a tempfile */
3831 /* we can't go from W, shr=get to R, shr=get without
3832 an intermediate vulnerable state, so don't bother trying...
3834 and lib$spawn doesn't shr=put, so have to close the write
3836 So... match up the creation date/time and the FID to
3837 make sure we're dealing with the same file
3842 if (!decc_filename_unix_only) {
3843 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3844 fp = fopen(file,"w");
3846 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3847 fp = fopen(file,"w");
3849 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3850 fp = fopen(file,"w");
3855 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3856 fp = fopen(file,"w");
3858 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3859 fp = fopen(file,"w");
3861 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3862 fp = fopen(file,"w");
3866 if (!fp) return 0; /* we're hosed */
3868 fprintf(fp,"$! 'f$verify(0)'\n");
3869 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3870 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3871 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3872 fprintf(fp,"$ perl_on = \"set noon\"\n");
3873 fprintf(fp,"$ perl_exit = \"exit\"\n");
3874 fprintf(fp,"$ perl_del = \"delete\"\n");
3875 fprintf(fp,"$ pif = \"if\"\n");
3876 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3877 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3878 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3879 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3880 fprintf(fp,"$! --- build command line to get max possible length\n");
3881 fprintf(fp,"$c=perl_popen_cmd0\n");
3882 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3883 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3884 fprintf(fp,"$x=perl_popen_cmd3\n");
3885 fprintf(fp,"$c=c+x\n");
3886 fprintf(fp,"$ perl_on\n");
3887 fprintf(fp,"$ 'c'\n");
3888 fprintf(fp,"$ perl_status = $STATUS\n");
3889 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3890 fprintf(fp,"$ perl_exit 'perl_status'\n");
3893 fgetname(fp, file, 1);
3894 fstat(fileno(fp), &s0.crtl_stat);
3897 if (decc_filename_unix_only)
3898 int_tounixspec(file, file, NULL);
3899 fp = fopen(file,"r","shr=get");
3901 fstat(fileno(fp), &s1.crtl_stat);
3903 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3904 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3913 static int vms_is_syscommand_xterm(void)
3915 const static struct dsc$descriptor_s syscommand_dsc =
3916 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3918 const static struct dsc$descriptor_s decwdisplay_dsc =
3919 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3921 struct item_list_3 items[2];
3922 unsigned short dvi_iosb[4];
3923 unsigned long devchar;
3924 unsigned long devclass;
3927 /* Very simple check to guess if sys$command is a decterm? */
3928 /* First see if the DECW$DISPLAY: device exists */
3930 items[0].code = DVI$_DEVCHAR;
3931 items[0].bufadr = &devchar;
3932 items[0].retadr = NULL;
3936 status = sys$getdviw
3937 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3939 if ($VMS_STATUS_SUCCESS(status)) {
3940 status = dvi_iosb[0];
3943 if (!$VMS_STATUS_SUCCESS(status)) {
3944 SETERRNO(EVMSERR, status);
3948 /* If it does, then for now assume that we are on a workstation */
3949 /* Now verify that SYS$COMMAND is a terminal */
3950 /* for creating the debugger DECTerm */
3953 items[0].code = DVI$_DEVCLASS;
3954 items[0].bufadr = &devclass;
3955 items[0].retadr = NULL;
3959 status = sys$getdviw
3960 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3962 if ($VMS_STATUS_SUCCESS(status)) {
3963 status = dvi_iosb[0];
3966 if (!$VMS_STATUS_SUCCESS(status)) {
3967 SETERRNO(EVMSERR, status);
3971 if (devclass == DC$_TERM) {
3978 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3979 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3984 char device_name[65];
3985 unsigned short device_name_len;
3986 struct dsc$descriptor_s customization_dsc;
3987 struct dsc$descriptor_s device_name_dsc;
3989 char customization[200];
3993 unsigned short p_chan;
3995 unsigned short iosb[4];
3996 const char * cust_str =
3997 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3998 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3999 DSC$K_CLASS_S, mbx1};
4001 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
4002 /*---------------------------------------*/
4003 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
4006 /* Make sure that this is from the Perl debugger */
4007 ret_char = strstr(cmd," xterm ");
4008 if (ret_char == NULL)
4010 cptr = ret_char + 7;
4011 ret_char = strstr(cmd,"tty");
4012 if (ret_char == NULL)
4014 ret_char = strstr(cmd,"sleep");
4015 if (ret_char == NULL)
4018 if (decw_term_port == 0) {
4019 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
4020 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
4021 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
4023 status = lib$find_image_symbol
4025 &decw_term_port_dsc,
4026 (void *)&decw_term_port,
4030 /* Try again with the other image name */
4031 if (!$VMS_STATUS_SUCCESS(status)) {
4033 status = lib$find_image_symbol
4035 &decw_term_port_dsc,
4036 (void *)&decw_term_port,
4045 /* No decw$term_port, give it up */
4046 if (!$VMS_STATUS_SUCCESS(status))
4049 /* Are we on a workstation? */
4050 /* to do: capture the rows / columns and pass their properties */
4051 ret_stat = vms_is_syscommand_xterm();
4055 /* Make the title: */
4056 ret_char = strstr(cptr,"-title");
4057 if (ret_char != NULL) {
4058 while ((*cptr != 0) && (*cptr != '\"')) {
4064 while ((*cptr != 0) && (*cptr != '\"')) {
4077 strcpy(title,"Perl Debug DECTerm");
4079 sprintf(customization, cust_str, title);
4081 customization_dsc.dsc$a_pointer = customization;
4082 customization_dsc.dsc$w_length = strlen(customization);
4083 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4084 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
4086 device_name_dsc.dsc$a_pointer = device_name;
4087 device_name_dsc.dsc$w_length = sizeof device_name -1;
4088 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4089 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
4091 device_name_len = 0;
4093 /* Try to create the window */
4094 status = (*decw_term_port)
4103 if (!$VMS_STATUS_SUCCESS(status)) {
4104 SETERRNO(EVMSERR, status);
4108 device_name[device_name_len] = '\0';
4110 /* Need to set this up to look like a pipe for cleanup */
4112 status = lib$get_vm(&n, &info);
4113 if (!$VMS_STATUS_SUCCESS(status)) {
4114 SETERRNO(ENOMEM, status);
4120 info->completion = 0;
4121 info->closing = FALSE;
4128 info->in_done = TRUE;
4129 info->out_done = TRUE;
4130 info->err_done = TRUE;
4132 /* Assign a channel on this so that it will persist, and not login */
4133 /* We stash this channel in the info structure for reference. */
4134 /* The created xterm self destructs when the last channel is removed */
4135 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4136 /* So leave this assigned. */
4137 device_name_dsc.dsc$w_length = device_name_len;
4138 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4139 if (!$VMS_STATUS_SUCCESS(status)) {
4140 SETERRNO(EVMSERR, status);
4143 info->xchan_valid = 1;
4145 /* Now create a mailbox to be read by the application */
4147 create_mbx(&p_chan, &d_mbx1);
4149 /* write the name of the created terminal to the mailbox */
4150 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4151 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4153 if (!$VMS_STATUS_SUCCESS(status)) {
4154 SETERRNO(EVMSERR, status);
4158 info->fp = PerlIO_open(mbx1, mode);
4160 /* Done with this channel */
4163 /* If any errors, then clean up */
4166 _ckvmssts_noperl(lib$free_vm(&n, &info));
4174 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4177 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4179 static int handler_set_up = FALSE;
4181 unsigned long int sts, flags = CLI$M_NOWAIT;
4182 /* The use of a GLOBAL table (as was done previously) rendered
4183 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4184 * environment. Hence we've switched to LOCAL symbol table.
4186 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4188 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4189 char *in, *out, *err, mbx[512];
4191 char tfilebuf[NAM$C_MAXRSS+1];
4193 char cmd_sym_name[20];
4194 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4195 DSC$K_CLASS_S, symbol};
4196 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4198 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4199 DSC$K_CLASS_S, cmd_sym_name};
4200 struct dsc$descriptor_s *vmscmd;
4201 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4202 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4203 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4205 /* Check here for Xterm create request. This means looking for
4206 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4207 * is possible to create an xterm.
4209 if (*in_mode == 'r') {
4212 #if defined(PERL_IMPLICIT_CONTEXT)
4213 /* Can not fork an xterm with a NULL context */
4214 /* This probably could never happen */
4218 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4219 if (xterm_fd != NULL)
4223 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4225 /* once-per-program initialization...
4226 note that the SETAST calls and the dual test of pipe_ef
4227 makes sure that only the FIRST thread through here does
4228 the initialization...all other threads wait until it's
4231 Yeah, uglier than a pthread call, it's got all the stuff inline
4232 rather than in a separate routine.
4236 _ckvmssts_noperl(sys$setast(0));
4238 unsigned long int pidcode = JPI$_PID;
4239 $DESCRIPTOR(d_delay, RETRY_DELAY);
4240 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4241 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4242 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4244 if (!handler_set_up) {
4245 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4246 handler_set_up = TRUE;
4248 _ckvmssts_noperl(sys$setast(1));
4251 /* see if we can find a VMSPIPE.COM */
4254 vmspipe = find_vmspipe(aTHX);
4256 strcpy(tfilebuf+1,vmspipe);
4257 } else { /* uh, oh...we're in tempfile hell */
4258 tpipe = vmspipe_tempfile(aTHX);
4259 if (!tpipe) { /* a fish popular in Boston */
4260 if (ckWARN(WARN_PIPE)) {
4261 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4265 fgetname(tpipe,tfilebuf+1,1);
4267 vmspipedsc.dsc$a_pointer = tfilebuf;
4268 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4270 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4273 case RMS$_FNF: case RMS$_DNF:
4274 set_errno(ENOENT); break;
4276 set_errno(ENOTDIR); break;
4278 set_errno(ENODEV); break;
4280 set_errno(EACCES); break;
4282 set_errno(EINVAL); break;
4283 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4284 set_errno(E2BIG); break;
4285 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4286 _ckvmssts_noperl(sts); /* fall through */
4287 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4290 set_vaxc_errno(sts);
4291 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4292 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4298 _ckvmssts_noperl(lib$get_vm(&n, &info));
4300 strcpy(mode,in_mode);
4303 info->completion = 0;
4304 info->closing = FALSE;
4311 info->in_done = TRUE;
4312 info->out_done = TRUE;
4313 info->err_done = TRUE;
4315 info->xchan_valid = 0;
4317 in = PerlMem_malloc(VMS_MAXRSS);
4318 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4319 out = PerlMem_malloc(VMS_MAXRSS);
4320 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4321 err = PerlMem_malloc(VMS_MAXRSS);
4322 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4324 in[0] = out[0] = err[0] = '\0';
4326 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4330 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4335 if (*mode == 'r') { /* piping from subroutine */
4337 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4339 info->out->pipe_done = &info->out_done;
4340 info->out_done = FALSE;
4341 info->out->info = info;
4343 if (!info->useFILE) {
4344 info->fp = PerlIO_open(mbx, mode);
4346 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4347 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4350 if (!info->fp && info->out) {
4351 sys$cancel(info->out->chan_out);
4353 while (!info->out_done) {
4355 _ckvmssts_noperl(sys$setast(0));
4356 done = info->out_done;
4357 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4358 _ckvmssts_noperl(sys$setast(1));
4359 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4362 if (info->out->buf) {
4363 n = info->out->bufsize * sizeof(char);
4364 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4367 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4369 _ckvmssts_noperl(lib$free_vm(&n, &info));
4374 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4376 info->err->pipe_done = &info->err_done;
4377 info->err_done = FALSE;
4378 info->err->info = info;
4381 } else if (*mode == 'w') { /* piping to subroutine */
4383 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4385 info->out->pipe_done = &info->out_done;
4386 info->out_done = FALSE;
4387 info->out->info = info;
4390 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4392 info->err->pipe_done = &info->err_done;
4393 info->err_done = FALSE;
4394 info->err->info = info;
4397 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4398 if (!info->useFILE) {
4399 info->fp = PerlIO_open(mbx, mode);
4401 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4402 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4406 info->in->pipe_done = &info->in_done;
4407 info->in_done = FALSE;
4408 info->in->info = info;
4412 if (!info->fp && info->in) {
4414 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4415 0, 0, 0, 0, 0, 0, 0, 0));
4417 while (!info->in_done) {
4419 _ckvmssts_noperl(sys$setast(0));
4420 done = info->in_done;
4421 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4422 _ckvmssts_noperl(sys$setast(1));
4423 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4426 if (info->in->buf) {
4427 n = info->in->bufsize * sizeof(char);
4428 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4431 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4433 _ckvmssts_noperl(lib$free_vm(&n, &info));
4439 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4440 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4442 info->out->pipe_done = &info->out_done;
4443 info->out_done = FALSE;
4444 info->out->info = info;
4447 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4449 info->err->pipe_done = &info->err_done;
4450 info->err_done = FALSE;
4451 info->err->info = info;
4455 symbol[MAX_DCL_SYMBOL] = '\0';
4457 strncpy(symbol, in, MAX_DCL_SYMBOL);
4458 d_symbol.dsc$w_length = strlen(symbol);
4459 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4461 strncpy(symbol, err, MAX_DCL_SYMBOL);
4462 d_symbol.dsc$w_length = strlen(symbol);
4463 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4465 strncpy(symbol, out, MAX_DCL_SYMBOL);
4466 d_symbol.dsc$w_length = strlen(symbol);
4467 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4469 /* Done with the names for the pipes */
4474 p = vmscmd->dsc$a_pointer;
4475 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4476 if (*p == '$') p++; /* remove leading $ */
4477 while (*p == ' ' || *p == '\t') p++;
4479 for (j = 0; j < 4; j++) {
4480 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4481 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4483 strncpy(symbol, p, MAX_DCL_SYMBOL);
4484 d_symbol.dsc$w_length = strlen(symbol);
4485 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4487 if (strlen(p) > MAX_DCL_SYMBOL) {
4488 p += MAX_DCL_SYMBOL;
4493 _ckvmssts_noperl(sys$setast(0));
4494 info->next=open_pipes; /* prepend to list */
4496 _ckvmssts_noperl(sys$setast(1));
4497 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4498 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4499 * have SYS$COMMAND if we need it.
4501 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4502 0, &info->pid, &info->completion,
4503 0, popen_completion_ast,info,0,0,0));
4505 /* if we were using a tempfile, close it now */
4507 if (tpipe) fclose(tpipe);
4509 /* once the subprocess is spawned, it has copied the symbols and
4510 we can get rid of ours */
4512 for (j = 0; j < 4; j++) {
4513 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4514 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4515 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4517 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4518 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4519 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4520 vms_execfree(vmscmd);
4522 #ifdef PERL_IMPLICIT_CONTEXT
4525 PL_forkprocess = info->pid;
4532 _ckvmssts_noperl(sys$setast(0));
4534 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4535 _ckvmssts_noperl(sys$setast(1));
4536 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4538 *psts = info->completion;
4539 /* Caller thinks it is open and tries to close it. */
4540 /* This causes some problems, as it changes the error status */
4541 /* my_pclose(info->fp); */
4543 /* If we did not have a file pointer open, then we have to */
4544 /* clean up here or eventually we will run out of something */
4546 if (info->fp == NULL) {
4547 my_pclose_pinfo(aTHX_ info);
4555 } /* end of safe_popen */
4558 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4560 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4564 TAINT_PROPER("popen");
4565 PERL_FLUSHALL_FOR_CHILD;
4566 return safe_popen(aTHX_ cmd,mode,&sts);
4572 /* Routine to close and cleanup a pipe info structure */
4574 static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4576 unsigned long int retsts;
4580 /* If we were writing to a subprocess, insure that someone reading from
4581 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4582 * produce an EOF record in the mailbox.
4584 * well, at least sometimes it *does*, so we have to watch out for
4585 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4589 #if defined(USE_ITHREADS)
4593 && PL_perlio_fd_refcnt
4596 PerlIO_flush(info->fp);
4598 fflush((FILE *)info->fp);
4601 _ckvmssts(sys$setast(0));
4602 info->closing = TRUE;
4603 done = info->done && info->in_done && info->out_done && info->err_done;
4604 /* hanging on write to Perl's input? cancel it */
4605 if (info->mode == 'r' && info->out && !info->out_done) {
4606 if (info->out->chan_out) {
4607 _ckvmssts(sys$cancel(info->out->chan_out));
4608 if (!info->out->chan_in) { /* EOF generation, need AST */
4609 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4613 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4614 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4616 _ckvmssts(sys$setast(1));
4619 #if defined(USE_ITHREADS)
4623 && PL_perlio_fd_refcnt
4626 PerlIO_close(info->fp);
4628 fclose((FILE *)info->fp);
4631 we have to wait until subprocess completes, but ALSO wait until all
4632 the i/o completes...otherwise we'll be freeing the "info" structure
4633 that the i/o ASTs could still be using...
4637 _ckvmssts(sys$setast(0));
4638 done = info->done && info->in_done && info->out_done && info->err_done;
4639 if (!done) _ckvmssts(sys$clref(pipe_ef));
4640 _ckvmssts(sys$setast(1));
4641 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4643 retsts = info->completion;
4645 /* remove from list of open pipes */
4646 _ckvmssts(sys$setast(0));
4648 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4654 last->next = info->next;
4656 open_pipes = info->next;
4657 _ckvmssts(sys$setast(1));
4659 /* free buffers and structures */
4662 if (info->in->buf) {
4663 n = info->in->bufsize * sizeof(char);
4664 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4667 _ckvmssts(lib$free_vm(&n, &info->in));
4670 if (info->out->buf) {
4671 n = info->out->bufsize * sizeof(char);
4672 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4675 _ckvmssts(lib$free_vm(&n, &info->out));
4678 if (info->err->buf) {
4679 n = info->err->bufsize * sizeof(char);
4680 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4683 _ckvmssts(lib$free_vm(&n, &info->err));
4686 _ckvmssts(lib$free_vm(&n, &info));
4692 /*{{{ I32 my_pclose(PerlIO *fp)*/
4693 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4695 pInfo info, last = NULL;
4698 /* Fixme - need ast and mutex protection here */
4699 for (info = open_pipes; info != NULL; last = info, info = info->next)
4700 if (info->fp == fp) break;
4702 if (info == NULL) { /* no such pipe open */
4703 set_errno(ECHILD); /* quoth POSIX */
4704 set_vaxc_errno(SS$_NONEXPR);
4708 ret_status = my_pclose_pinfo(aTHX_ info);
4712 } /* end of my_pclose() */
4714 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4715 /* Roll our own prototype because we want this regardless of whether
4716 * _VMS_WAIT is defined.
4718 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4720 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4721 created with popen(); otherwise partially emulate waitpid() unless
4722 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4723 Also check processes not considered by the CRTL waitpid().
4725 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4727 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4734 if (statusp) *statusp = 0;
4736 for (info = open_pipes; info != NULL; info = info->next)
4737 if (info->pid == pid) break;
4739 if (info != NULL) { /* we know about this child */
4740 while (!info->done) {
4741 _ckvmssts(sys$setast(0));
4743 if (!done) _ckvmssts(sys$clref(pipe_ef));
4744 _ckvmssts(sys$setast(1));
4745 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4748 if (statusp) *statusp = info->completion;
4752 /* child that already terminated? */
4754 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4755 if (closed_list[j].pid == pid) {
4756 if (statusp) *statusp = closed_list[j].completion;
4761 /* fall through if this child is not one of our own pipe children */
4763 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4765 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4766 * in 7.2 did we get a version that fills in the VMS completion
4767 * status as Perl has always tried to do.
4770 sts = __vms_waitpid( pid, statusp, flags );
4772 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4775 /* If the real waitpid tells us the child does not exist, we
4776 * fall through here to implement waiting for a child that
4777 * was created by some means other than exec() (say, spawned
4778 * from DCL) or to wait for a process that is not a subprocess
4779 * of the current process.
4782 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4785 $DESCRIPTOR(intdsc,"0 00:00:01");
4786 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4787 unsigned long int pidcode = JPI$_PID, mypid;
4788 unsigned long int interval[2];
4789 unsigned int jpi_iosb[2];
4790 struct itmlst_3 jpilist[2] = {
4791 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4796 /* Sorry folks, we don't presently implement rooting around for
4797 the first child we can find, and we definitely don't want to
4798 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4804 /* Get the owner of the child so I can warn if it's not mine. If the
4805 * process doesn't exist or I don't have the privs to look at it,
4806 * I can go home early.
4808 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4809 if (sts & 1) sts = jpi_iosb[0];
4821 set_vaxc_errno(sts);
4825 if (ckWARN(WARN_EXEC)) {
4826 /* remind folks they are asking for non-standard waitpid behavior */
4827 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4828 if (ownerpid != mypid)
4829 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4830 "waitpid: process %x is not a child of process %x",
4834 /* simply check on it once a second until it's not there anymore. */
4836 _ckvmssts(sys$bintim(&intdsc,interval));
4837 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4838 _ckvmssts(sys$schdwk(0,0,interval,0));
4839 _ckvmssts(sys$hiber());
4841 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4846 } /* end of waitpid() */
4851 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4853 my_gconvert(double val, int ndig, int trail, char *buf)
4855 static char __gcvtbuf[DBL_DIG+1];
4858 loc = buf ? buf : __gcvtbuf;
4860 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
4862 sprintf(loc,"%.*g",ndig,val);
4868 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4869 return gcvt(val,ndig,loc);
4872 loc[0] = '0'; loc[1] = '\0';
4879 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4880 static int rms_free_search_context(struct FAB * fab)
4884 nam = fab->fab$l_nam;
4885 nam->nam$b_nop |= NAM$M_SYNCHK;
4886 nam->nam$l_rlf = NULL;
4888 return sys$parse(fab, NULL, NULL);
4891 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4892 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4893 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4894 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4895 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4896 #define rms_nam_esll(nam) nam.nam$b_esl
4897 #define rms_nam_esl(nam) nam.nam$b_esl
4898 #define rms_nam_name(nam) nam.nam$l_name
4899 #define rms_nam_namel(nam) nam.nam$l_name
4900 #define rms_nam_type(nam) nam.nam$l_type
4901 #define rms_nam_typel(nam) nam.nam$l_type
4902 #define rms_nam_ver(nam) nam.nam$l_ver
4903 #define rms_nam_verl(nam) nam.nam$l_ver
4904 #define rms_nam_rsll(nam) nam.nam$b_rsl
4905 #define rms_nam_rsl(nam) nam.nam$b_rsl
4906 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4907 #define rms_set_fna(fab, nam, name, size) \
4908 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4909 #define rms_get_fna(fab, nam) fab.fab$l_fna
4910 #define rms_set_dna(fab, nam, name, size) \
4911 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4912 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4913 #define rms_set_esa(nam, name, size) \
4914 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4915 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4916 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4917 #define rms_set_rsa(nam, name, size) \
4918 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4919 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4920 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4921 #define rms_nam_name_type_l_size(nam) \
4922 (nam.nam$b_name + nam.nam$b_type)
4924 static int rms_free_search_context(struct FAB * fab)
4928 nam = fab->fab$l_naml;
4929 nam->naml$b_nop |= NAM$M_SYNCHK;
4930 nam->naml$l_rlf = NULL;
4931 nam->naml$l_long_defname_size = 0;
4934 return sys$parse(fab, NULL, NULL);
4937 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4938 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4939 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4940 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4941 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4942 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4943 #define rms_nam_esl(nam) nam.naml$b_esl
4944 #define rms_nam_name(nam) nam.naml$l_name
4945 #define rms_nam_namel(nam) nam.naml$l_long_name
4946 #define rms_nam_type(nam) nam.naml$l_type
4947 #define rms_nam_typel(nam) nam.naml$l_long_type
4948 #define rms_nam_ver(nam) nam.naml$l_ver
4949 #define rms_nam_verl(nam) nam.naml$l_long_ver
4950 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4951 #define rms_nam_rsl(nam) nam.naml$b_rsl
4952 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4953 #define rms_set_fna(fab, nam, name, size) \
4954 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4955 nam.naml$l_long_filename_size = size; \
4956 nam.naml$l_long_filename = name;}
4957 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4958 #define rms_set_dna(fab, nam, name, size) \
4959 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4960 nam.naml$l_long_defname_size = size; \
4961 nam.naml$l_long_defname = name; }
4962 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4963 #define rms_set_esa(nam, name, size) \
4964 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4965 nam.naml$l_long_expand_alloc = size; \
4966 nam.naml$l_long_expand = name; }
4967 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4968 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4969 nam.naml$l_long_expand = l_name; \
4970 nam.naml$l_long_expand_alloc = l_size; }
4971 #define rms_set_rsa(nam, name, size) \
4972 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4973 nam.naml$l_long_result = name; \
4974 nam.naml$l_long_result_alloc = size; }
4975 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4976 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4977 nam.naml$l_long_result = l_name; \
4978 nam.naml$l_long_result_alloc = l_size; }
4979 #define rms_nam_name_type_l_size(nam) \
4980 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4985 * The CRTL for 8.3 and later can create symbolic links in any mode,
4986 * however in 8.3 the unlink/remove/delete routines will only properly handle
4987 * them if one of the PCP modes is active.
4989 static int rms_erase(const char * vmsname)
4992 struct FAB myfab = cc$rms_fab;
4993 rms_setup_nam(mynam);
4995 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4996 rms_bind_fab_nam(myfab, mynam);
4998 #ifdef NAML$M_OPEN_SPECIAL
4999 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5002 status = sys$erase(&myfab, 0, 0);
5009 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
5010 const struct dsc$descriptor_s * vms_dst_dsc,
5011 unsigned long flags)
5013 /* VMS and UNIX handle file permissions differently and the
5014 * the same ACL trick may be needed for renaming files,
5015 * especially if they are directories.
5018 /* todo: get kill_file and rename to share common code */
5019 /* I can not find online documentation for $change_acl
5020 * it appears to be replaced by $set_security some time ago */
5022 const unsigned int access_mode = 0;
5023 $DESCRIPTOR(obj_file_dsc,"FILE");
5026 unsigned long int jpicode = JPI$_UIC;
5027 int aclsts, fndsts, rnsts = -1;
5028 unsigned int ctx = 0;
5029 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5030 struct dsc$descriptor_s * clean_dsc;
5033 unsigned char myace$b_length;
5034 unsigned char myace$b_type;
5035 unsigned short int myace$w_flags;
5036 unsigned long int myace$l_access;
5037 unsigned long int myace$l_ident;
5038 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
5039 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
5041 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
5044 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
5045 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
5047 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
5048 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
5052 /* Expand the input spec using RMS, since we do not want to put
5053 * ACLs on the target of a symbolic link */
5054 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
5055 if (vmsname == NULL)
5058 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
5060 PERL_RMSEXPAND_M_SYMLINK);
5062 PerlMem_free(vmsname);
5066 /* So we get our own UIC to use as a rights identifier,
5067 * and the insert an ACE at the head of the ACL which allows us
5068 * to delete the file.
5070 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
5072 fildsc.dsc$w_length = strlen(vmsname);
5073 fildsc.dsc$a_pointer = vmsname;
5075 newace.myace$l_ident = oldace.myace$l_ident;
5078 /* Grab any existing ACEs with this identifier in case we fail */
5079 clean_dsc = &fildsc;
5080 aclsts = fndsts = sys$get_security(&obj_file_dsc,
5088 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
5089 /* Add the new ACE . . . */
5091 /* if the sys$get_security succeeded, then ctx is valid, and the
5092 * object/file descriptors will be ignored. But otherwise they
5095 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
5096 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5097 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5099 set_vaxc_errno(aclsts);
5100 PerlMem_free(vmsname);
5104 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5107 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5109 if ($VMS_STATUS_SUCCESS(rnsts)) {
5110 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5113 /* Put things back the way they were. */
5115 aclsts = sys$get_security(&obj_file_dsc,
5123 if ($VMS_STATUS_SUCCESS(aclsts)) {
5127 if (!$VMS_STATUS_SUCCESS(fndsts))
5128 sec_flags = OSS$M_RELCTX;
5130 /* Get rid of the new ACE */
5131 aclsts = sys$set_security(NULL, NULL, NULL,
5132 sec_flags, dellst, &ctx, &access_mode);
5134 /* If there was an old ACE, put it back */
5135 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5136 addlst[0].bufadr = &oldace;
5137 aclsts = sys$set_security(NULL, NULL, NULL,
5138 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5139 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5141 set_vaxc_errno(aclsts);
5147 /* Try to clear the lock on the ACL list */
5148 aclsts2 = sys$set_security(NULL, NULL, NULL,
5149 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5151 /* Rename errors are most important */
5152 if (!$VMS_STATUS_SUCCESS(rnsts))
5155 set_vaxc_errno(aclsts);
5160 if (aclsts != SS$_ACLEMPTY)
5167 PerlMem_free(vmsname);
5172 /*{{{int rename(const char *, const char * */
5173 /* Not exactly what X/Open says to do, but doing it absolutely right
5174 * and efficiently would require a lot more work. This should be close
5175 * enough to pass all but the most strict X/Open compliance test.
5178 Perl_rename(pTHX_ const char *src, const char * dst)
5187 /* Validate the source file */
5188 src_sts = flex_lstat(src, &src_st);
5191 /* No source file or other problem */
5194 if (src_st.st_devnam[0] == 0) {
5195 /* This may be possible so fail if it is seen. */
5200 dst_sts = flex_lstat(dst, &dst_st);
5203 if (dst_st.st_dev != src_st.st_dev) {
5204 /* Must be on the same device */
5209 /* VMS_INO_T_COMPARE is true if the inodes are different
5210 * to match the output of memcmp
5213 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5214 /* That was easy, the files are the same! */
5218 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5219 /* If source is a directory, so must be dest */
5227 if ((dst_sts == 0) &&
5228 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5230 /* We have issues here if vms_unlink_all_versions is set
5231 * If the destination exists, and is not a directory, then
5232 * we must delete in advance.
5234 * If the src is a directory, then we must always pre-delete
5237 * If we successfully delete the dst in advance, and the rename fails
5238 * X/Open requires that errno be EIO.
5242 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5244 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam,
5245 S_ISDIR(dst_st.st_mode));
5247 /* Need to delete all versions ? */
5248 if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5251 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
5252 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 0);
5257 /* Make sure that we do not loop forever */
5269 /* We killed the destination, so only errno now is EIO */
5274 /* Originally the idea was to call the CRTL rename() and only
5275 * try the lib$rename_file if it failed.
5276 * It turns out that there are too many variants in what the
5277 * the CRTL rename might do, so only use lib$rename_file
5282 /* Is the source and dest both in VMS format */
5283 /* if the source is a directory, then need to fileify */
5284 /* and dest must be a directory or non-existent. */
5289 unsigned long flags;
5290 struct dsc$descriptor_s old_file_dsc;
5291 struct dsc$descriptor_s new_file_dsc;
5293 /* We need to modify the src and dst depending
5294 * on if one or more of them are directories.
5297 vms_dst = PerlMem_malloc(VMS_MAXRSS);
5298 if (vms_dst == NULL)
5299 _ckvmssts_noperl(SS$_INSFMEM);
5301 if (S_ISDIR(src_st.st_mode)) {
5303 char * vms_dir_file;
5305 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5306 if (vms_dir_file == NULL)
5307 _ckvmssts_noperl(SS$_INSFMEM);
5309 /* If the dest is a directory, we must remove it
5312 d_sts = mp_do_kill_file(aTHX_ dst_st.st_devnam, 1);
5314 PerlMem_free(vms_dst);
5322 /* The dest must be a VMS file specification */
5323 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5324 if (ret_str == NULL) {
5325 PerlMem_free(vms_dst);
5330 /* The source must be a file specification */
5331 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5332 if (ret_str == NULL) {
5333 PerlMem_free(vms_dst);
5334 PerlMem_free(vms_dir_file);
5338 PerlMem_free(vms_dst);
5339 vms_dst = vms_dir_file;
5342 /* File to file or file to new dir */
5344 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5345 /* VMS pathify a dir target */
5346 ret_str = int_tovmspath(dst, vms_dst, NULL);
5347 if (ret_str == NULL) {
5348 PerlMem_free(vms_dst);
5353 char * v_spec, * r_spec, * d_spec, * n_spec;
5354 char * e_spec, * vs_spec;
5355 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5357 /* fileify a target VMS file specification */
5358 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5359 if (ret_str == NULL) {
5360 PerlMem_free(vms_dst);
5365 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5366 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5367 &e_len, &vs_spec, &vs_len);
5370 /* Get rid of the version */
5374 /* Need to specify a '.' so that the extension */
5375 /* is not inherited */
5376 strcat(vms_dst,".");
5382 old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5383 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
5384 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5385 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5387 new_file_dsc.dsc$a_pointer = vms_dst;
5388 new_file_dsc.dsc$w_length = strlen(vms_dst);
5389 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5390 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5393 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5394 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5397 sts = lib$rename_file(&old_file_dsc,
5401 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5402 if (!$VMS_STATUS_SUCCESS(sts)) {
5404 /* We could have failed because VMS style permissions do not
5405 * permit renames that UNIX will allow. Just like the hack
5408 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5411 PerlMem_free(vms_dst);
5412 if (!$VMS_STATUS_SUCCESS(sts)) {
5419 if (vms_unlink_all_versions) {
5420 /* Now get rid of any previous versions of the source file that
5426 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5427 S_ISDIR(src_st.st_mode));
5428 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
5429 src_sts = mp_do_kill_file(aTHX_ src_st.st_devnam,
5430 S_ISDIR(src_st.st_mode));
5435 /* Make sure that we do not loop forever */
5444 /* We deleted the destination, so must force the error to be EIO */
5445 if ((retval != 0) && (pre_delete != 0))
5453 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5454 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5455 * to expand file specification. Allows for a single default file
5456 * specification and a simple mask of options. If outbuf is non-NULL,
5457 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5458 * the resultant file specification is placed. If outbuf is NULL, the
5459 * resultant file specification is placed into a static buffer.
5460 * The third argument, if non-NULL, is taken to be a default file
5461 * specification string. The fourth argument is unused at present.
5462 * rmesexpand() returns the address of the resultant string if
5463 * successful, and NULL on error.
5465 * New functionality for previously unused opts value:
5466 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5467 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5468 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5469 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5471 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5475 (const char *filespec,
5477 const char *defspec,
5483 const char * in_spec;
5485 const char * def_spec;
5486 char * vmsfspec, *vmsdefspec;
5490 struct FAB myfab = cc$rms_fab;
5491 rms_setup_nam(mynam);
5493 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5496 /* temp hack until UTF8 is actually implemented */
5497 if (fs_utf8 != NULL)
5500 if (!filespec || !*filespec) {
5501 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5511 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5512 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5513 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5515 /* If this is a UNIX file spec, convert it to VMS */
5516 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5517 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5518 &e_len, &vs_spec, &vs_len);
5523 vmsfspec = PerlMem_malloc(VMS_MAXRSS);
5524 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5525 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5526 if (ret_spec == NULL) {
5527 PerlMem_free(vmsfspec);
5530 in_spec = (const char *)vmsfspec;
5532 /* Unless we are forcing to VMS format, a UNIX input means
5533 * UNIX output, and that requires long names to be used
5535 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5536 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5537 opts |= PERL_RMSEXPAND_M_LONG;
5547 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5548 rms_bind_fab_nam(myfab, mynam);
5550 /* Process the default file specification if present */
5552 if (defspec && *defspec) {
5554 t_isunix = is_unix_filespec(defspec);
5556 vmsdefspec = PerlMem_malloc(VMS_MAXRSS);
5557 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5558 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5560 if (ret_spec == NULL) {
5561 /* Clean up and bail */
5562 PerlMem_free(vmsdefspec);
5563 if (vmsfspec != NULL)
5564 PerlMem_free(vmsfspec);
5567 def_spec = (const char *)vmsdefspec;
5569 rms_set_dna(myfab, mynam,
5570 (char *)def_spec, strlen(def_spec)); /* cast ok */
5573 /* Now we need the expansion buffers */
5574 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
5575 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5576 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5577 esal = PerlMem_malloc(VMS_MAXRSS);
5578 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5580 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5582 /* If a NAML block is used RMS always writes to the long and short
5583 * addresses unless you suppress the short name.
5585 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5586 outbufl = PerlMem_malloc(VMS_MAXRSS);
5587 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5589 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5591 #ifdef NAM$M_NO_SHORT_UPCASE
5592 if (decc_efs_case_preserve)
5593 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5596 /* We may not want to follow symbolic links */
5597 #ifdef NAML$M_OPEN_SPECIAL
5598 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5599 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5602 /* First attempt to parse as an existing file */
5603 retsts = sys$parse(&myfab,0,0);
5604 if (!(retsts & STS$K_SUCCESS)) {
5606 /* Could not find the file, try as syntax only if error is not fatal */
5607 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5608 if (retsts == RMS$_DNF ||
5609 retsts == RMS$_DIR ||
5610 retsts == RMS$_DEV ||
5611 retsts == RMS$_PRV) {
5612 retsts = sys$parse(&myfab,0,0);
5613 if (retsts & STS$K_SUCCESS) goto int_expanded;
5616 /* Still could not parse the file specification */
5617 /*----------------------------------------------*/
5618 sts = rms_free_search_context(&myfab); /* Free search context */
5619 if (vmsdefspec != NULL)
5620 PerlMem_free(vmsdefspec);
5621 if (vmsfspec != NULL)
5622 PerlMem_free(vmsfspec);
5623 if (outbufl != NULL)
5624 PerlMem_free(outbufl);
5628 set_vaxc_errno(retsts);
5629 if (retsts == RMS$_PRV) set_errno(EACCES);
5630 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5631 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5632 else set_errno(EVMSERR);
5635 retsts = sys$search(&myfab,0,0);
5636 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5637 sts = rms_free_search_context(&myfab); /* Free search context */
5638 if (vmsdefspec != NULL)
5639 PerlMem_free(vmsdefspec);
5640 if (vmsfspec != NULL)
5641 PerlMem_free(vmsfspec);
5642 if (outbufl != NULL)
5643 PerlMem_free(outbufl);
5647 set_vaxc_errno(retsts);
5648 if (retsts == RMS$_PRV) set_errno(EACCES);
5649 else set_errno(EVMSERR);
5653 /* If the input filespec contained any lowercase characters,
5654 * downcase the result for compatibility with Unix-minded code. */
5656 if (!decc_efs_case_preserve) {
5658 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5659 if (islower(*tbuf)) { haslower = 1; break; }
5662 /* Is a long or a short name expected */
5663 /*------------------------------------*/
5665 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5666 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5667 if (rms_nam_rsll(mynam)) {
5669 speclen = rms_nam_rsll(mynam);
5672 spec_buf = esal; /* Not esa */
5673 speclen = rms_nam_esll(mynam);
5678 if (rms_nam_rsl(mynam)) {
5680 speclen = rms_nam_rsl(mynam);
5683 spec_buf = esa; /* Not esal */
5684 speclen = rms_nam_esl(mynam);
5686 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5689 spec_buf[speclen] = '\0';
5691 /* Trim off null fields added by $PARSE
5692 * If type > 1 char, must have been specified in original or default spec
5693 * (not true for version; $SEARCH may have added version of existing file).
5695 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5696 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5697 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5698 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5701 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5702 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5704 if (trimver || trimtype) {
5705 if (defspec && *defspec) {
5706 char *defesal = NULL;
5707 char *defesa = NULL;
5708 defesa = PerlMem_malloc(VMS_MAXRSS + 1);
5709 if (defesa != NULL) {
5710 struct FAB deffab = cc$rms_fab;
5711 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5712 defesal = PerlMem_malloc(VMS_MAXRSS + 1);
5713 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5715 rms_setup_nam(defnam);
5717 rms_bind_fab_nam(deffab, defnam);
5721 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5723 /* RMS needs the esa/esal as a work area if wildcards are involved */
5724 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5726 rms_clear_nam_nop(defnam);
5727 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5728 #ifdef NAM$M_NO_SHORT_UPCASE
5729 if (decc_efs_case_preserve)
5730 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5732 #ifdef NAML$M_OPEN_SPECIAL
5733 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5734 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5736 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5738 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5741 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5744 if (defesal != NULL)
5745 PerlMem_free(defesal);
5746 PerlMem_free(defesa);
5748 _ckvmssts_noperl(SS$_INSFMEM);
5752 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5753 if (*(rms_nam_verl(mynam)) != '\"')
5754 speclen = rms_nam_verl(mynam) - spec_buf;
5757 if (*(rms_nam_ver(mynam)) != '\"')
5758 speclen = rms_nam_ver(mynam) - spec_buf;
5762 /* If we didn't already trim version, copy down */
5763 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5764 if (speclen > rms_nam_verl(mynam) - spec_buf)
5766 (rms_nam_typel(mynam),
5767 rms_nam_verl(mynam),
5768 speclen - (rms_nam_verl(mynam) - spec_buf));
5769 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5772 if (speclen > rms_nam_ver(mynam) - spec_buf)
5774 (rms_nam_type(mynam),
5776 speclen - (rms_nam_ver(mynam) - spec_buf));
5777 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5782 /* Done with these copies of the input files */
5783 /*-------------------------------------------*/
5784 if (vmsfspec != NULL)
5785 PerlMem_free(vmsfspec);
5786 if (vmsdefspec != NULL)
5787 PerlMem_free(vmsdefspec);
5789 /* If we just had a directory spec on input, $PARSE "helpfully"
5790 * adds an empty name and type for us */
5791 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5792 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5793 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5794 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5795 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5796 speclen = rms_nam_namel(mynam) - spec_buf;
5801 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5802 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5803 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5804 speclen = rms_nam_name(mynam) - spec_buf;
5807 /* Posix format specifications must have matching quotes */
5808 if (speclen < (VMS_MAXRSS - 1)) {
5809 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5810 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5811 spec_buf[speclen] = '\"';
5816 spec_buf[speclen] = '\0';
5817 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5819 /* Have we been working with an expanded, but not resultant, spec? */
5820 /* Also, convert back to Unix syntax if necessary. */
5824 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5825 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5826 rsl = rms_nam_rsll(mynam);
5830 rsl = rms_nam_rsl(mynam);
5833 /* rsl is not present, it means that spec_buf is either */
5834 /* esa or esal, and needs to be copied to outbuf */
5835 /* convert to Unix if desired */
5837 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5839 /* VMS file specs are not in UTF-8 */
5840 if (fs_utf8 != NULL)
5842 strcpy(outbuf, spec_buf);
5847 /* Now spec_buf is either outbuf or outbufl */
5848 /* We need the result into outbuf */
5850 /* If we need this in UNIX, then we need another buffer */
5851 /* to keep things in order */
5853 char * new_src = NULL;
5854 if (spec_buf == outbuf) {
5855 new_src = PerlMem_malloc(VMS_MAXRSS);
5856 strcpy(new_src, spec_buf);
5860 ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5862 PerlMem_free(new_src);
5865 /* VMS file specs are not in UTF-8 */
5866 if (fs_utf8 != NULL)
5869 /* Copy the buffer if needed */
5870 if (outbuf != spec_buf)
5871 strcpy(outbuf, spec_buf);
5877 /* Need to clean up the search context */
5878 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5879 sts = rms_free_search_context(&myfab); /* Free search context */
5881 /* Clean up the extra buffers */
5885 if (outbufl != NULL)
5886 PerlMem_free(outbufl);
5888 /* Return the result */
5892 /* Common simple case - Expand an already VMS spec */
5894 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5895 opts |= PERL_RMSEXPAND_M_VMS_IN;
5896 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5899 /* Common simple case - Expand to a VMS spec */
5901 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5902 opts |= PERL_RMSEXPAND_M_VMS;
5903 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5907 /* Entry point used by perl routines */
5910 (pTHX_ const char *filespec,
5913 const char *defspec,
5918 static char __rmsexpand_retbuf[VMS_MAXRSS];
5919 char * expanded, *ret_spec, *ret_buf;
5923 if (ret_buf == NULL) {
5925 Newx(expanded, VMS_MAXRSS, char);
5926 if (expanded == NULL)
5927 _ckvmssts(SS$_INSFMEM);
5930 ret_buf = __rmsexpand_retbuf;
5935 ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5936 opts, fs_utf8, dfs_utf8);
5938 if (ret_spec == NULL) {
5939 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5947 /* External entry points */
5948 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5949 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5950 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5951 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5952 char *Perl_rmsexpand_utf8
5953 (pTHX_ const char *spec, char *buf, const char *def,
5954 unsigned opt, int * fs_utf8, int * dfs_utf8)
5955 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5956 char *Perl_rmsexpand_utf8_ts
5957 (pTHX_ const char *spec, char *buf, const char *def,
5958 unsigned opt, int * fs_utf8, int * dfs_utf8)
5959 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
5963 ** The following routines are provided to make life easier when
5964 ** converting among VMS-style and Unix-style directory specifications.
5965 ** All will take input specifications in either VMS or Unix syntax. On
5966 ** failure, all return NULL. If successful, the routines listed below
5967 ** return a pointer to a buffer containing the appropriately
5968 ** reformatted spec (and, therefore, subsequent calls to that routine
5969 ** will clobber the result), while the routines of the same names with
5970 ** a _ts suffix appended will return a pointer to a mallocd string
5971 ** containing the appropriately reformatted spec.
5972 ** In all cases, only explicit syntax is altered; no check is made that
5973 ** the resulting string is valid or that the directory in question
5976 ** fileify_dirspec() - convert a directory spec into the name of the
5977 ** directory file (i.e. what you can stat() to see if it's a dir).
5978 ** The style (VMS or Unix) of the result is the same as the style
5979 ** of the parameter passed in.
5980 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5981 ** what you prepend to a filename to indicate what directory it's in).
5982 ** The style (VMS or Unix) of the result is the same as the style
5983 ** of the parameter passed in.
5984 ** tounixpath() - convert a directory spec into a Unix-style path.
5985 ** tovmspath() - convert a directory spec into a VMS-style path.
5986 ** tounixspec() - convert any file spec into a Unix-style file spec.
5987 ** tovmsspec() - convert any file spec into a VMS-style spec.
5988 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5990 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
5991 ** Permission is given to distribute this code as part of the Perl
5992 ** standard distribution under the terms of the GNU General Public
5993 ** License or the Perl Artistic License. Copies of each may be
5994 ** found in the Perl standard distribution.
5997 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5999 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
6001 unsigned long int dirlen, retlen, hasfilename = 0;
6002 char *cp1, *cp2, *lastdir;
6003 char *trndir, *vmsdir;
6004 unsigned short int trnlnm_iter_count;
6008 if (utf8_fl != NULL)
6011 if (!dir || !*dir) {
6012 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6014 dirlen = strlen(dir);
6015 while (dirlen && dir[dirlen-1] == '/') --dirlen;
6016 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
6017 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
6024 if (dirlen > (VMS_MAXRSS - 1)) {
6025 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
6028 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
6029 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6030 if (!strpbrk(dir+1,"/]>:") &&
6031 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
6032 strcpy(trndir,*dir == '/' ? dir + 1: dir);
6033 trnlnm_iter_count = 0;
6034 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
6035 trnlnm_iter_count++;
6036 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6038 dirlen = strlen(trndir);
6041 strncpy(trndir,dir,dirlen);
6042 trndir[dirlen] = '\0';
6045 /* At this point we are done with *dir and use *trndir which is a
6046 * copy that can be modified. *dir must not be modified.
6049 /* If we were handed a rooted logical name or spec, treat it like a
6050 * simple directory, so that
6051 * $ Define myroot dev:[dir.]
6052 * ... do_fileify_dirspec("myroot",buf,1) ...
6053 * does something useful.
6055 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
6056 trndir[--dirlen] = '\0';
6057 trndir[dirlen-1] = ']';
6059 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
6060 trndir[--dirlen] = '\0';
6061 trndir[dirlen-1] = '>';
6064 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
6065 /* If we've got an explicit filename, we can just shuffle the string. */
6066 if (*(cp1+1)) hasfilename = 1;
6067 /* Similarly, we can just back up a level if we've got multiple levels
6068 of explicit directories in a VMS spec which ends with directories. */
6070 for (cp2 = cp1; cp2 > trndir; cp2--) {
6072 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
6073 /* fix-me, can not scan EFS file specs backward like this */
6074 *cp2 = *cp1; *cp1 = '\0';
6079 if (*cp2 == '[' || *cp2 == '<') break;
6084 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
6085 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6086 cp1 = strpbrk(trndir,"]:>");
6087 if (hasfilename || !cp1) { /* filename present or not VMS */
6089 if (decc_efs_charset && !cp1) {
6091 /* EFS handling for UNIX mode */
6093 /* Just remove the trailing '/' and we should be done */
6095 trndir_len = strlen(trndir);
6097 if (trndir_len > 1) {
6099 if (trndir[trndir_len] == '/') {
6100 trndir[trndir_len] = '\0';
6103 strcpy(buf, trndir);
6104 PerlMem_free(trndir);
6105 PerlMem_free(vmsdir);
6109 /* For non-EFS mode, this is left for backwards compatibility */
6110 /* For EFS mode, this is only done for VMS format filespecs as */
6111 /* Perl programs generally have problems when a UNIX format spec */
6112 /* returns a VMS format spec */
6113 if (trndir[0] == '.') {
6114 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
6115 PerlMem_free(trndir);
6116 PerlMem_free(vmsdir);
6117 return int_fileify_dirspec("[]", buf, NULL);
6119 else if (trndir[1] == '.' &&
6120 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
6121 PerlMem_free(trndir);
6122 PerlMem_free(vmsdir);
6123 return int_fileify_dirspec("[-]", buf, NULL);
6126 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
6127 dirlen -= 1; /* to last element */
6128 lastdir = strrchr(trndir,'/');
6130 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
6131 /* If we have "/." or "/..", VMSify it and let the VMS code
6132 * below expand it, rather than repeating the code to handle
6133 * relative components of a filespec here */
6135 if (*(cp1+2) == '.') cp1++;
6136 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6138 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6139 PerlMem_free(trndir);
6140 PerlMem_free(vmsdir);
6143 if (strchr(vmsdir,'/') != NULL) {
6144 /* If int_tovmsspec() returned it, it must have VMS syntax
6145 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6146 * the time to check this here only so we avoid a recursion
6147 * loop; otherwise, gigo.
6149 PerlMem_free(trndir);
6150 PerlMem_free(vmsdir);
6151 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6154 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6155 PerlMem_free(trndir);
6156 PerlMem_free(vmsdir);
6159 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6160 PerlMem_free(trndir);
6161 PerlMem_free(vmsdir);
6165 } while ((cp1 = strstr(cp1,"/.")) != NULL);
6166 lastdir = strrchr(trndir,'/');
6168 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6170 /* Ditto for specs that end in an MFD -- let the VMS code
6171 * figure out whether it's a real device or a rooted logical. */
6173 /* This should not happen any more. Allowing the fake /000000
6174 * in a UNIX pathname causes all sorts of problems when trying
6175 * to run in UNIX emulation. So the VMS to UNIX conversions
6176 * now remove the fake /000000 directories.
6179 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6180 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6181 PerlMem_free(trndir);
6182 PerlMem_free(vmsdir);
6185 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6186 PerlMem_free(trndir);
6187 PerlMem_free(vmsdir);
6190 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6191 PerlMem_free(trndir);
6192 PerlMem_free(vmsdir);
6197 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6198 !(lastdir = cp1 = strrchr(trndir,']')) &&
6199 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6201 cp2 = strrchr(cp1,'.');
6203 int e_len, vs_len = 0;
6206 cp3 = strchr(cp2,';');
6207 e_len = strlen(cp2);
6209 vs_len = strlen(cp3);
6210 e_len = e_len - vs_len;
6212 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6214 if (!decc_efs_charset) {
6215 /* If this is not EFS, then not a directory */
6216 PerlMem_free(trndir);
6217 PerlMem_free(vmsdir);
6219 set_vaxc_errno(RMS$_DIR);
6223 /* Ok, here we have an issue, technically if a .dir shows */
6224 /* from inside a directory, then we should treat it as */
6225 /* xxx^.dir.dir. But we do not have that context at this */
6226 /* point unless this is totally restructured, so we remove */
6227 /* The .dir for now, and fix this better later */
6228 dirlen = cp2 - trndir;
6234 retlen = dirlen + 6;
6235 memcpy(buf, trndir, dirlen);
6238 /* We've picked up everything up to the directory file name.
6239 Now just add the type and version, and we're set. */
6241 /* We should only add type for VMS syntax, but historically Perl
6242 has added it for UNIX style also */
6244 /* Fix me - we should not be using the same routine for VMS and
6245 UNIX format files. Things are too tangled so we need to lookup
6246 what syntax the output is */
6250 lastdir = strrchr(trndir,'/');
6254 lastdir = strpbrk(trndir,"]:>");
6260 if ((is_vms == 0) && (is_unix == 0)) {
6261 /* We still do not know? */
6262 is_unix = decc_filename_unix_report;
6267 if ((is_unix && !decc_efs_charset) || is_vms) {
6269 /* It is a bug to add a .dir to a UNIX format directory spec */
6270 /* However Perl on VMS may have programs that expect this so */
6271 /* If not using EFS character specifications allow it. */
6273 if ((!decc_efs_case_preserve) && vms_process_case_tolerant) {
6274 /* Traditionally Perl expects filenames in lower case */
6275 strcat(buf, ".dir");
6277 /* VMS expects the .DIR to be in upper case */
6278 strcat(buf, ".DIR");
6281 /* It is also a bug to put a VMS format version on a UNIX file */
6282 /* specification. Perl self tests are looking for this */
6283 if (is_vms || !(decc_efs_charset || decc_filename_unix_report))
6286 PerlMem_free(trndir);
6287 PerlMem_free(vmsdir);
6290 else { /* VMS-style directory spec */
6292 char *esa, *esal, term, *cp;
6295 unsigned long int cmplen, haslower = 0;
6296 struct FAB dirfab = cc$rms_fab;
6297 rms_setup_nam(savnam);
6298 rms_setup_nam(dirnam);
6300 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
6301 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6303 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6304 esal = PerlMem_malloc(VMS_MAXRSS);
6305 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6307 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6308 rms_bind_fab_nam(dirfab, dirnam);
6309 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6310 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6311 #ifdef NAM$M_NO_SHORT_UPCASE
6312 if (decc_efs_case_preserve)
6313 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6316 for (cp = trndir; *cp; cp++)
6317 if (islower(*cp)) { haslower = 1; break; }
6318 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6319 if ((dirfab.fab$l_sts == RMS$_DIR) ||
6320 (dirfab.fab$l_sts == RMS$_DNF) ||
6321 (dirfab.fab$l_sts == RMS$_PRV)) {
6322 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6323 sts = sys$parse(&dirfab);
6329 PerlMem_free(trndir);
6330 PerlMem_free(vmsdir);
6332 set_vaxc_errno(dirfab.fab$l_sts);
6338 /* Does the file really exist? */
6339 if (sys$search(&dirfab)& STS$K_SUCCESS) {
6340 /* Yes; fake the fnb bits so we'll check type below */
6341 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6343 else { /* No; just work with potential name */
6344 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6347 fab_sts = dirfab.fab$l_sts;
6348 sts = rms_free_search_context(&dirfab);
6352 PerlMem_free(trndir);
6353 PerlMem_free(vmsdir);
6354 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
6360 /* Make sure we are using the right buffer */
6361 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6364 my_esa_len = rms_nam_esll(dirnam);
6368 my_esa_len = rms_nam_esl(dirnam);
6369 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6372 my_esa[my_esa_len] = '\0';
6373 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6374 cp1 = strchr(my_esa,']');
6375 if (!cp1) cp1 = strchr(my_esa,'>');
6376 if (cp1) { /* Should always be true */
6377 my_esa_len -= cp1 - my_esa - 1;
6378 memmove(my_esa, cp1 + 1, my_esa_len);
6381 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6382 /* Yep; check version while we're at it, if it's there. */
6383 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6384 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6385 /* Something other than .DIR[;1]. Bzzt. */
6386 sts = rms_free_search_context(&dirfab);
6390 PerlMem_free(trndir);
6391 PerlMem_free(vmsdir);
6393 set_vaxc_errno(RMS$_DIR);
6398 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6399 /* They provided at least the name; we added the type, if necessary, */
6400 strcpy(buf, my_esa);
6401 sts = rms_free_search_context(&dirfab);
6402 PerlMem_free(trndir);
6406 PerlMem_free(vmsdir);
6409 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6410 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6414 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6415 if (cp1 == NULL) { /* should never happen */
6416 sts = rms_free_search_context(&dirfab);
6417 PerlMem_free(trndir);
6421 PerlMem_free(vmsdir);
6426 retlen = strlen(my_esa);
6427 cp1 = strrchr(my_esa,'.');
6428 /* ODS-5 directory specifications can have extra "." in them. */
6429 /* Fix-me, can not scan EFS file specifications backwards */
6430 while (cp1 != NULL) {
6431 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6435 while ((cp1 > my_esa) && (*cp1 != '.'))
6442 if ((cp1) != NULL) {
6443 /* There's more than one directory in the path. Just roll back. */
6445 strcpy(buf, my_esa);
6448 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6449 /* Go back and expand rooted logical name */
6450 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6451 #ifdef NAM$M_NO_SHORT_UPCASE
6452 if (decc_efs_case_preserve)
6453 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6455 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6456 sts = rms_free_search_context(&dirfab);
6460 PerlMem_free(trndir);
6461 PerlMem_free(vmsdir);
6463 set_vaxc_errno(dirfab.fab$l_sts);
6467 /* This changes the length of the string of course */
6469 my_esa_len = rms_nam_esll(dirnam);
6471 my_esa_len = rms_nam_esl(dirnam);
6474 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6475 cp1 = strstr(my_esa,"][");
6476 if (!cp1) cp1 = strstr(my_esa,"]<");
6477 dirlen = cp1 - my_esa;
6478 memcpy(buf, my_esa, dirlen);
6479 if (!strncmp(cp1+2,"000000]",7)) {
6480 buf[dirlen-1] = '\0';
6481 /* fix-me Not full ODS-5, just extra dots in directories for now */
6482 cp1 = buf + dirlen - 1;
6488 if (*(cp1-1) != '^')
6493 if (*cp1 == '.') *cp1 = ']';
6495 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6496 memmove(cp1+1,"000000]",7);
6500 memmove(buf+dirlen, cp1+2, retlen-dirlen);
6502 /* Convert last '.' to ']' */
6504 while (*cp != '[') {
6507 /* Do not trip on extra dots in ODS-5 directories */
6508 if ((cp1 == buf) || (*(cp1-1) != '^'))
6512 if (*cp1 == '.') *cp1 = ']';
6514 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6515 memmove(cp1+1,"000000]",7);
6519 else { /* This is a top-level dir. Add the MFD to the path. */
6522 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
6523 strcpy(cp2,":[000000]");
6528 sts = rms_free_search_context(&dirfab);
6529 /* We've set up the string up through the filename. Add the
6530 type and version, and we're done. */
6531 strcat(buf,".DIR;1");
6533 /* $PARSE may have upcased filespec, so convert output to lower
6534 * case if input contained any lowercase characters. */
6535 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
6536 PerlMem_free(trndir);
6540 PerlMem_free(vmsdir);
6543 } /* end of int_fileify_dirspec() */
6546 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6547 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6549 static char __fileify_retbuf[VMS_MAXRSS];
6550 char * fileified, *ret_spec, *ret_buf;
6554 if (ret_buf == NULL) {
6556 Newx(fileified, VMS_MAXRSS, char);
6557 if (fileified == NULL)
6558 _ckvmssts(SS$_INSFMEM);
6559 ret_buf = fileified;
6561 ret_buf = __fileify_retbuf;
6565 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6567 if (ret_spec == NULL) {
6568 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6570 Safefree(fileified);
6574 } /* end of do_fileify_dirspec() */
6577 /* External entry points */
6578 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6579 { return do_fileify_dirspec(dir,buf,0,NULL); }
6580 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6581 { return do_fileify_dirspec(dir,buf,1,NULL); }
6582 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6583 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6584 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6585 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6587 static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6588 char * v_spec, int v_len, char * r_spec, int r_len,
6589 char * d_spec, int d_len, char * n_spec, int n_len,
6590 char * e_spec, int e_len, char * vs_spec, int vs_len) {
6592 /* VMS specification - Try to do this the simple way */
6593 if ((v_len + r_len > 0) || (d_len > 0)) {
6596 /* No name or extension component, already a directory */
6597 if ((n_len + e_len + vs_len) == 0) {
6602 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6603 /* This results from catfile() being used instead of catdir() */
6604 /* So even though it should not work, we need to allow it */
6606 /* If this is .DIR;1 then do a simple conversion */
6607 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6608 if (is_dir || (e_len == 0) && (d_len > 0)) {
6610 len = v_len + r_len + d_len - 1;
6611 char dclose = d_spec[d_len - 1];
6612 strncpy(buf, dir, len);
6615 strncpy(&buf[len], n_spec, n_len);
6618 buf[len + 1] = '\0';
6623 else if (d_len > 0) {
6624 /* In the olden days, a directory needed to have a .DIR */
6625 /* extension to be a valid directory, but now it could */
6626 /* be a symbolic link */
6628 len = v_len + r_len + d_len - 1;
6629 char dclose = d_spec[d_len - 1];
6630 strncpy(buf, dir, len);
6633 strncpy(&buf[len], n_spec, n_len);
6636 if (decc_efs_charset) {
6639 strncpy(&buf[len], e_spec, e_len);
6642 set_vaxc_errno(RMS$_DIR);
6648 buf[len + 1] = '\0';
6653 set_vaxc_errno(RMS$_DIR);
6659 set_vaxc_errno(RMS$_DIR);
6665 /* Internal routine to make sure or convert a directory to be in a */
6666 /* path specification. No utf8 flag because it is not changed or used */
6667 static char *int_pathify_dirspec(const char *dir, char *buf)
6669 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6670 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6671 char * exp_spec, *ret_spec;
6673 unsigned short int trnlnm_iter_count;
6677 if (vms_debug_fileify) {
6679 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6681 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6684 /* We may need to lower case the result if we translated */
6685 /* a logical name or got the current working directory */
6688 if (!dir || !*dir) {
6690 set_vaxc_errno(SS$_BADPARAM);
6694 trndir = PerlMem_malloc(VMS_MAXRSS);
6696 _ckvmssts_noperl(SS$_INSFMEM);
6698 /* If no directory specified use the current default */
6700 strcpy(trndir, dir);
6702 getcwd(trndir, VMS_MAXRSS - 1);
6706 /* now deal with bare names that could be logical names */
6707 trnlnm_iter_count = 0;
6708 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6709 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6710 trnlnm_iter_count++;
6712 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6714 trnlen = strlen(trndir);
6716 /* Trap simple rooted lnms, and return lnm:[000000] */
6717 if (!strcmp(trndir+trnlen-2,".]")) {
6719 strcat(buf, ":[000000]");
6720 PerlMem_free(trndir);
6722 if (vms_debug_fileify) {
6723 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6729 /* At this point we do not work with *dir, but the copy in *trndir */
6731 if (need_to_lower && !decc_efs_case_preserve) {
6732 /* Legacy mode, lower case the returned value */
6733 __mystrtolower(trndir);
6737 /* Some special cases, '..', '.' */
6739 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6740 /* Force UNIX filespec */
6744 /* Is this Unix or VMS format? */
6745 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6746 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6747 &e_len, &vs_spec, &vs_len);
6750 /* Just a filename? */
6751 if ((v_len + r_len + d_len) == 0) {
6753 /* Now we have a problem, this could be Unix or VMS */
6754 /* We have to guess. .DIR usually means VMS */
6756 /* In UNIX report mode, the .DIR extension is removed */
6757 /* if one shows up, it is for a non-directory or a directory */
6758 /* in EFS charset mode */
6760 /* So if we are in Unix report mode, assume that this */
6761 /* is a relative Unix directory specification */
6764 if (!decc_filename_unix_report && decc_efs_charset) {
6766 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6769 /* Traditional mode, assume .DIR is directory */
6772 strncpy(&buf[2], n_spec, n_len);
6773 buf[n_len + 2] = ']';
6774 buf[n_len + 3] = '\0';
6775 PerlMem_free(trndir);
6776 if (vms_debug_fileify) {
6778 "int_pathify_dirspec: buf = %s\n",
6788 ret_spec = int_pathify_dirspec_simple(trndir, buf,
6789 v_spec, v_len, r_spec, r_len,
6790 d_spec, d_len, n_spec, n_len,
6791 e_spec, e_len, vs_spec, vs_len);
6793 if (ret_spec != NULL) {
6794 PerlMem_free(trndir);
6795 if (vms_debug_fileify) {
6797 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6802 /* Simple way did not work, which means that a logical name */
6803 /* was present for the directory specification. */
6804 /* Need to use an rmsexpand variant to decode it completely */
6805 exp_spec = PerlMem_malloc(VMS_MAXRSS);
6806 if (exp_spec == NULL)
6807 _ckvmssts_noperl(SS$_INSFMEM);
6809 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6810 if (ret_spec != NULL) {
6811 sts = vms_split_path(exp_spec, &v_spec, &v_len,
6812 &r_spec, &r_len, &d_spec, &d_len,
6813 &n_spec, &n_len, &e_spec,
6814 &e_len, &vs_spec, &vs_len);
6816 ret_spec = int_pathify_dirspec_simple(
6817 exp_spec, buf, v_spec, v_len, r_spec, r_len,
6818 d_spec, d_len, n_spec, n_len,
6819 e_spec, e_len, vs_spec, vs_len);
6821 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6822 /* Legacy mode, lower case the returned value */
6823 __mystrtolower(ret_spec);
6826 set_vaxc_errno(RMS$_DIR);
6831 PerlMem_free(exp_spec);
6832 PerlMem_free(trndir);
6833 if (vms_debug_fileify) {
6834 if (ret_spec == NULL)
6835 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6838 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6843 /* Unix specification, Could be trivial conversion */
6845 dir_len = strlen(trndir);
6847 /* If the extended file character set is in effect */
6848 /* then pathify is simple */
6850 if (!decc_efs_charset) {
6851 /* Have to deal with trailing '.dir' or extra '.' */
6852 /* that should not be there in legacy mode, but is */
6858 lastslash = strrchr(trndir, '/');
6859 if (lastslash == NULL)
6866 /* '..' or '.' are valid directory components */
6868 if (lastslash[0] == '.') {
6869 if (lastslash[1] == '\0') {
6871 } else if (lastslash[1] == '.') {
6872 if (lastslash[2] == '\0') {
6875 /* And finally allow '...' */
6876 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6884 lastdot = strrchr(lastslash, '.');
6886 if (lastdot != NULL) {
6889 /* '.dir' is discarded, and any other '.' is invalid */
6890 e_len = strlen(lastdot);
6892 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6895 dir_len = dir_len - 4;
6901 strcpy(buf, trndir);
6902 if (buf[dir_len - 1] != '/') {
6904 buf[dir_len + 1] = '\0';
6907 /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6908 if (!decc_efs_charset) {
6911 if (str[0] == '.') {
6914 while ((dots[cnt] == '.') && (cnt < 3))
6917 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6923 for (; *str; ++str) {
6924 while (*str == '/') {
6930 /* Have to skip up to three dots which could be */
6931 /* directories, 3 dots being a VMS extension for Perl */
6934 while ((dots[cnt] == '.') && (cnt < 3)) {
6937 if (dots[cnt] == '\0')
6939 if ((cnt > 1) && (dots[cnt] != '/')) {
6945 /* too many dots? */
6946 if ((cnt == 0) || (cnt > 3)) {
6950 if (!dir_start && (*str == '.')) {
6955 PerlMem_free(trndir);
6957 if (vms_debug_fileify) {
6958 if (ret_spec == NULL)
6959 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6962 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6968 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6969 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6971 static char __pathify_retbuf[VMS_MAXRSS];
6972 char * pathified, *ret_spec, *ret_buf;
6976 if (ret_buf == NULL) {
6978 Newx(pathified, VMS_MAXRSS, char);
6979 if (pathified == NULL)
6980 _ckvmssts(SS$_INSFMEM);
6981 ret_buf = pathified;
6983 ret_buf = __pathify_retbuf;
6987 ret_spec = int_pathify_dirspec(dir, ret_buf);
6989 if (ret_spec == NULL) {
6990 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6992 Safefree(pathified);
6997 } /* end of do_pathify_dirspec() */
7000 /* External entry points */
7001 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
7002 { return do_pathify_dirspec(dir,buf,0,NULL); }
7003 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
7004 { return do_pathify_dirspec(dir,buf,1,NULL); }
7005 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
7006 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
7007 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
7008 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
7010 /* Internal tounixspec routine that does not use a thread context */
7011 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
7012 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
7014 char *dirend, *cp1, *cp3, *tmp;
7017 unsigned short int trnlnm_iter_count;
7019 if (utf8_fl != NULL)
7022 if (vms_debug_fileify) {
7024 fprintf(stderr, "int_tounixspec: spec = NULL\n");
7026 fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
7032 set_vaxc_errno(SS$_BADPARAM);
7035 if (strlen(spec) > (VMS_MAXRSS-1)) {
7037 set_vaxc_errno(SS$_BUFFEROVF);
7041 /* New VMS specific format needs translation
7042 * glob passes filenames with trailing '\n' and expects this preserved.
7044 if (decc_posix_compliant_pathnames) {
7045 if (strncmp(spec, "\"^UP^", 5) == 0) {
7051 tunix = PerlMem_malloc(VMS_MAXRSS);
7052 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7053 strcpy(tunix, spec);
7054 tunix_len = strlen(tunix);
7056 if (tunix[tunix_len - 1] == '\n') {
7057 tunix[tunix_len - 1] = '\"';
7058 tunix[tunix_len] = '\0';
7062 uspec = decc$translate_vms(tunix);
7063 PerlMem_free(tunix);
7064 if ((int)uspec > 0) {
7070 /* If we can not translate it, makemaker wants as-is */
7078 cmp_rslt = 0; /* Presume VMS */
7079 cp1 = strchr(spec, '/');
7083 /* Look for EFS ^/ */
7084 if (decc_efs_charset) {
7085 while (cp1 != NULL) {
7088 /* Found illegal VMS, assume UNIX */
7093 cp1 = strchr(cp1, '/');
7097 /* Look for "." and ".." */
7098 if (decc_filename_unix_report) {
7099 if (spec[0] == '.') {
7100 if ((spec[1] == '\0') || (spec[1] == '\n')) {
7104 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
7110 /* This is already UNIX or at least nothing VMS understands */
7113 if (vms_debug_fileify) {
7114 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7121 dirend = strrchr(spec,']');
7122 if (dirend == NULL) dirend = strrchr(spec,'>');
7123 if (dirend == NULL) dirend = strchr(spec,':');
7124 if (dirend == NULL) {
7126 if (vms_debug_fileify) {
7127 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7132 /* Special case 1 - sys$posix_root = / */
7133 #if __CRTL_VER >= 70000000
7134 if (!decc_disable_posix_root) {
7135 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7143 /* Special case 2 - Convert NLA0: to /dev/null */
7144 #if __CRTL_VER < 70000000
7145 cmp_rslt = strncmp(spec,"NLA0:", 5);
7147 cmp_rslt = strncmp(spec,"nla0:", 5);
7149 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7151 if (cmp_rslt == 0) {
7152 strcpy(rslt, "/dev/null");
7155 if (spec[6] != '\0') {
7162 /* Also handle special case "SYS$SCRATCH:" */
7163 #if __CRTL_VER < 70000000
7164 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
7166 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
7168 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7170 tmp = PerlMem_malloc(VMS_MAXRSS);
7171 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7172 if (cmp_rslt == 0) {
7175 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7177 strcpy(rslt, "/tmp");
7180 if (spec[12] != '\0') {
7188 if (*cp2 != '[' && *cp2 != '<') {
7191 else { /* the VMS spec begins with directories */
7193 if (*cp2 == ']' || *cp2 == '>') {
7194 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
7198 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7199 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7201 if (vms_debug_fileify) {
7202 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7206 trnlnm_iter_count = 0;
7209 while (*cp3 != ':' && *cp3) cp3++;
7211 if (strchr(cp3,']') != NULL) break;
7212 trnlnm_iter_count++;
7213 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7214 } while (vmstrnenv(tmp,tmp,0,fildev,0));
7219 *(cp1++) = *(cp3++);
7220 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7222 set_errno(ENAMETOOLONG);
7223 set_vaxc_errno(SS$_BUFFEROVF);
7224 if (vms_debug_fileify) {
7225 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7227 return NULL; /* No room */
7232 if ((*cp2 == '^')) {
7233 /* EFS file escape, pass the next character as is */
7234 /* Fix me: HEX encoding for Unicode not implemented */
7237 else if ( *cp2 == '.') {
7238 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7239 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7246 for (; cp2 <= dirend; cp2++) {
7247 if ((*cp2 == '^')) {
7248 /* EFS file escape, pass the next character as is */
7249 /* Fix me: HEX encoding for Unicode not implemented */
7250 *(cp1++) = *(++cp2);
7251 /* An escaped dot stays as is -- don't convert to slash */
7252 if (*cp2 == '.') cp2++;
7256 if (*(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7258 else if (*cp2 == ']' || *cp2 == '>') {
7259 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7261 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7263 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7264 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7265 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7266 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7267 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7269 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7270 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7274 else if (*cp2 == '-') {
7275 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7276 while (*cp2 == '-') {
7278 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7280 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7281 /* filespecs like */
7282 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
7283 if (vms_debug_fileify) {
7284 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7289 else *(cp1++) = *cp2;
7291 else *(cp1++) = *cp2;
7293 /* Translate the rest of the filename. */
7298 /* Fixme - for compatibility with the CRTL we should be removing */
7299 /* spaces from the file specifications, but this may show that */
7300 /* some tests that were appearing to pass are not really passing */
7306 /* Fix me hex expansions not implemented */
7307 cp2++; /* '^.' --> '.' and other. */
7313 *(cp1++) = *(cp2++);
7318 if (decc_filename_unix_no_version) {
7319 /* Easy, drop the version */
7324 /* Punt - passing the version as a dot will probably */
7325 /* break perl in weird ways, but so did passing */
7326 /* through the ; as a version. Follow the CRTL and */
7327 /* hope for the best. */
7334 /* We will need to fix this properly later */
7335 /* As Perl may be installed on an ODS-5 volume, but not */
7336 /* have the EFS_CHARSET enabled, it still may encounter */
7337 /* filenames with extra dots in them, and a precedent got */
7338 /* set which allowed them to work, that we will uphold here */
7339 /* If extra dots are present in a name and no ^ is on them */
7340 /* VMS assumes that the first one is the extension delimiter */
7341 /* the rest have an implied ^. */
7343 /* this is also a conflict as the . is also a version */
7344 /* delimiter in VMS, */
7346 *(cp1++) = *(cp2++);
7350 /* This is an extension */
7351 if (decc_readdir_dropdotnotype) {
7353 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7354 /* Drop the dot for the extension */
7362 *(cp1++) = *(cp2++);
7367 /* This still leaves /000000/ when working with a
7368 * VMS device root or concealed root.
7374 ulen = strlen(rslt);
7376 /* Get rid of "000000/ in rooted filespecs */
7378 zeros = strstr(rslt, "/000000/");
7379 if (zeros != NULL) {
7381 mlen = ulen - (zeros - rslt) - 7;
7382 memmove(zeros, &zeros[7], mlen);
7389 if (vms_debug_fileify) {
7390 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7394 } /* end of int_tounixspec() */
7397 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7398 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7400 static char __tounixspec_retbuf[VMS_MAXRSS];
7401 char * unixspec, *ret_spec, *ret_buf;
7405 if (ret_buf == NULL) {
7407 Newx(unixspec, VMS_MAXRSS, char);
7408 if (unixspec == NULL)
7409 _ckvmssts(SS$_INSFMEM);
7412 ret_buf = __tounixspec_retbuf;
7416 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7418 if (ret_spec == NULL) {
7419 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7426 } /* end of do_tounixspec() */
7428 /* External entry points */
7429 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7430 { return do_tounixspec(spec,buf,0, NULL); }
7431 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7432 { return do_tounixspec(spec,buf,1, NULL); }
7433 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7434 { return do_tounixspec(spec,buf,0, utf8_fl); }
7435 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7436 { return do_tounixspec(spec,buf,1, utf8_fl); }
7438 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7441 This procedure is used to identify if a path is based in either
7442 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7443 it returns the OpenVMS format directory for it.
7445 It is expecting specifications of only '/' or '/xxxx/'
7447 If a posix root does not exist, or 'xxxx' is not a directory
7448 in the posix root, it returns a failure.
7450 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7452 It is used only internally by posix_to_vmsspec_hardway().
7455 static int posix_root_to_vms
7456 (char *vmspath, int vmspath_len,
7457 const char *unixpath,
7458 const int * utf8_fl)
7461 struct FAB myfab = cc$rms_fab;
7462 rms_setup_nam(mynam);
7463 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7464 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7465 char * esa, * esal, * rsa, * rsal;
7471 unixlen = strlen(unixpath);
7476 #if __CRTL_VER >= 80200000
7477 /* If not a posix spec already, convert it */
7478 if (decc_posix_compliant_pathnames) {
7479 if (strncmp(unixpath,"\"^UP^",5) != 0) {
7480 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7483 /* This is already a VMS specification, no conversion */
7485 strncpy(vmspath,unixpath, vmspath_len);
7494 /* Check to see if this is under the POSIX root */
7495 if (decc_disable_posix_root) {
7499 /* Skip leading / */
7500 if (unixpath[0] == '/') {
7506 strcpy(vmspath,"SYS$POSIX_ROOT:");
7508 /* If this is only the / , or blank, then... */
7509 if (unixpath[0] == '\0') {
7510 /* by definition, this is the answer */
7514 /* Need to look up a directory */
7518 /* Copy and add '^' escape characters as needed */
7521 while (unixpath[i] != 0) {
7524 j += copy_expand_unix_filename_escape
7525 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7529 path_len = strlen(vmspath);
7530 if (vmspath[path_len - 1] == '/')
7532 vmspath[path_len] = ']';
7534 vmspath[path_len] = '\0';
7537 vmspath[vmspath_len] = 0;
7538 if (unixpath[unixlen - 1] == '/')
7540 esal = PerlMem_malloc(VMS_MAXRSS);
7541 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7542 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7543 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7544 rsal = PerlMem_malloc(VMS_MAXRSS);
7545 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7546 rsa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7547 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7548 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7549 rms_bind_fab_nam(myfab, mynam);
7550 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7551 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7552 if (decc_efs_case_preserve)
7553 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7554 #ifdef NAML$M_OPEN_SPECIAL
7555 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7558 /* Set up the remaining naml fields */
7559 sts = sys$parse(&myfab);
7561 /* It failed! Try again as a UNIX filespec */
7570 /* get the Device ID and the FID */
7571 sts = sys$search(&myfab);
7573 /* These are no longer needed */
7578 /* on any failure, returned the POSIX ^UP^ filespec */
7583 specdsc.dsc$a_pointer = vmspath;
7584 specdsc.dsc$w_length = vmspath_len;
7586 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7587 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7588 sts = lib$fid_to_name
7589 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7591 /* on any failure, returned the POSIX ^UP^ filespec */
7593 /* This can happen if user does not have permission to read directories */
7594 if (strncmp(unixpath,"\"^UP^",5) != 0)
7595 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7597 strcpy(vmspath, unixpath);
7600 vmspath[specdsc.dsc$w_length] = 0;
7602 /* Are we expecting a directory? */
7603 if (dir_flag != 0) {
7609 i = specdsc.dsc$w_length - 1;
7613 /* Version must be '1' */
7614 if (vmspath[i--] != '1')
7616 /* Version delimiter is one of ".;" */
7617 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7620 if (vmspath[i--] != 'R')
7622 if (vmspath[i--] != 'I')
7624 if (vmspath[i--] != 'D')
7626 if (vmspath[i--] != '.')
7628 eptr = &vmspath[i+1];
7630 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7631 if (vmspath[i-1] != '^') {
7639 /* Get rid of 6 imaginary zero directory filename */
7640 vmspath[i+1] = '\0';
7644 if (vmspath[i] == '0')
7658 /* /dev/mumble needs to be handled special.
7659 /dev/null becomes NLA0:, And there is the potential for other stuff
7660 like /dev/tty which may need to be mapped to something.
7664 slash_dev_special_to_vms
7665 (const char * unixptr,
7674 nextslash = strchr(unixptr, '/');
7675 len = strlen(unixptr);
7676 if (nextslash != NULL)
7677 len = nextslash - unixptr;
7678 cmp = strncmp("null", unixptr, 5);
7680 if (vmspath_len >= 6) {
7681 strcpy(vmspath, "_NLA0:");
7689 /* The built in routines do not understand perl's special needs, so
7690 doing a manual conversion from UNIX to VMS
7692 If the utf8_fl is not null and points to a non-zero value, then
7693 treat 8 bit characters as UTF-8.
7695 The sequence starting with '$(' and ending with ')' will be passed
7696 through with out interpretation instead of being escaped.
7699 static int posix_to_vmsspec_hardway
7700 (char *vmspath, int vmspath_len,
7701 const char *unixpath,
7706 const char *unixptr;
7707 const char *unixend;
7709 const char *lastslash;
7710 const char *lastdot;
7716 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7717 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7719 if (utf8_fl != NULL)
7725 /* Ignore leading "/" characters */
7726 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7729 unixlen = strlen(unixptr);
7731 /* Do nothing with blank paths */
7738 /* This could have a "^UP^ on the front */
7739 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7745 lastslash = strrchr(unixptr,'/');
7746 lastdot = strrchr(unixptr,'.');
7747 unixend = strrchr(unixptr,'\"');
7748 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7749 unixend = unixptr + unixlen;
7752 /* last dot is last dot or past end of string */
7753 if (lastdot == NULL)
7754 lastdot = unixptr + unixlen;
7756 /* if no directories, set last slash to beginning of string */
7757 if (lastslash == NULL) {
7758 lastslash = unixptr;
7761 /* Watch out for trailing "." after last slash, still a directory */
7762 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7763 lastslash = unixptr + unixlen;
7766 /* Watch out for trailing ".." after last slash, still a directory */
7767 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7768 lastslash = unixptr + unixlen;
7771 /* dots in directories are aways escaped */
7772 if (lastdot < lastslash)
7773 lastdot = unixptr + unixlen;
7776 /* if (unixptr < lastslash) then we are in a directory */
7783 /* Start with the UNIX path */
7784 if (*unixptr != '/') {
7785 /* relative paths */
7787 /* If allowing logical names on relative pathnames, then handle here */
7788 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7789 !decc_posix_compliant_pathnames) {
7795 /* Find the next slash */
7796 nextslash = strchr(unixptr,'/');
7798 esa = PerlMem_malloc(vmspath_len);
7799 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7801 trn = PerlMem_malloc(VMS_MAXRSS);
7802 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7804 if (nextslash != NULL) {
7806 seg_len = nextslash - unixptr;
7807 strncpy(esa, unixptr, seg_len);
7811 strcpy(esa, unixptr);
7812 seg_len = strlen(unixptr);
7814 /* trnlnm(section) */
7815 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7818 /* Now fix up the directory */
7820 /* Split up the path to find the components */
7821 sts = vms_split_path
7839 /* A logical name must be a directory or the full
7840 specification. It is only a full specification if
7841 it is the only component */
7842 if ((unixptr[seg_len] == '\0') ||
7843 (unixptr[seg_len+1] == '\0')) {
7845 /* Is a directory being required? */
7846 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7847 /* Not a logical name */
7852 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7853 /* This must be a directory */
7854 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7855 strcpy(vmsptr, esa);
7856 vmslen=strlen(vmsptr);
7857 vmsptr[vmslen] = ':';
7859 vmsptr[vmslen] = '\0';
7867 /* must be dev/directory - ignore version */
7868 if ((n_len + e_len) != 0)
7871 /* transfer the volume */
7872 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7873 strncpy(vmsptr, v_spec, v_len);
7879 /* unroot the rooted directory */
7880 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7882 r_spec[r_len - 1] = ']';
7884 /* This should not be there, but nothing is perfect */
7886 cmp = strcmp(&r_spec[1], "000000.");
7896 strncpy(vmsptr, r_spec, r_len);
7902 /* Bring over the directory. */
7904 ((d_len + vmslen) < vmspath_len)) {
7906 d_spec[d_len - 1] = ']';
7908 cmp = strcmp(&d_spec[1], "000000.");
7919 /* Remove the redundant root */
7927 strncpy(vmsptr, d_spec, d_len);
7941 if (lastslash > unixptr) {
7944 /* skip leading ./ */
7946 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7952 /* Are we still in a directory? */
7953 if (unixptr <= lastslash) {
7958 /* if not backing up, then it is relative forward. */
7959 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7960 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7968 /* Perl wants an empty directory here to tell the difference
7969 * between a DCL command and a filename
7978 /* Handle two special files . and .. */
7979 if (unixptr[0] == '.') {
7980 if (&unixptr[1] == unixend) {
7987 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7998 else { /* Absolute PATH handling */
8002 /* Need to find out where root is */
8004 /* In theory, this procedure should never get an absolute POSIX pathname
8005 * that can not be found on the POSIX root.
8006 * In practice, that can not be relied on, and things will show up
8007 * here that are a VMS device name or concealed logical name instead.
8008 * So to make things work, this procedure must be tolerant.
8010 esa = PerlMem_malloc(vmspath_len);
8011 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8014 nextslash = strchr(&unixptr[1],'/');
8016 if (nextslash != NULL) {
8018 seg_len = nextslash - &unixptr[1];
8019 strncpy(vmspath, unixptr, seg_len + 1);
8020 vmspath[seg_len+1] = 0;
8023 cmp = strncmp(vmspath, "dev", 4);
8025 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
8026 if (sts == SS$_NORMAL)
8030 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
8033 if ($VMS_STATUS_SUCCESS(sts)) {
8034 /* This is verified to be a real path */
8036 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
8037 if ($VMS_STATUS_SUCCESS(sts)) {
8038 strcpy(vmspath, esa);
8039 vmslen = strlen(vmspath);
8040 vmsptr = vmspath + vmslen;
8042 if (unixptr < lastslash) {
8051 cmp = strcmp(rptr,"000000.");
8056 } /* removing 6 zeros */
8057 } /* vmslen < 7, no 6 zeros possible */
8058 } /* Not in a directory */
8059 } /* Posix root found */
8061 /* No posix root, fall back to default directory */
8062 strcpy(vmspath, "SYS$DISK:[");
8063 vmsptr = &vmspath[10];
8065 if (unixptr > lastslash) {
8074 } /* end of verified real path handling */
8079 /* Ok, we have a device or a concealed root that is not in POSIX
8080 * or we have garbage. Make the best of it.
8083 /* Posix to VMS destroyed this, so copy it again */
8084 strncpy(vmspath, &unixptr[1], seg_len);
8085 vmspath[seg_len] = 0;
8087 vmsptr = &vmsptr[vmslen];
8090 /* Now do we need to add the fake 6 zero directory to it? */
8092 if ((*lastslash == '/') && (nextslash < lastslash)) {
8093 /* No there is another directory */
8100 /* now we have foo:bar or foo:[000000]bar to decide from */
8101 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
8103 if (!islnm && !decc_posix_compliant_pathnames) {
8105 cmp = strncmp("bin", vmspath, 4);
8107 /* bin => SYS$SYSTEM: */
8108 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
8111 /* tmp => SYS$SCRATCH: */
8112 cmp = strncmp("tmp", vmspath, 4);
8114 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
8119 trnend = islnm ? islnm - 1 : 0;
8121 /* if this was a logical name, ']' or '>' must be present */
8122 /* if not a logical name, then assume a device and hope. */
8123 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
8125 /* if log name and trailing '.' then rooted - treat as device */
8126 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
8128 /* Fix me, if not a logical name, a device lookup should be
8129 * done to see if the device is file structured. If the device
8130 * is not file structured, the 6 zeros should not be put on.
8132 * As it is, perl is occasionally looking for dev:[000000]tty.
8133 * which looks a little strange.
8135 * Not that easy to detect as "/dev" may be file structured with
8136 * special device files.
8139 if (!islnm && (add_6zero == 0) && (*nextslash == '/') &&
8140 (&nextslash[1] == unixend)) {
8141 /* No real directory present */
8146 /* Put the device delimiter on */
8149 unixptr = nextslash;
8152 /* Start directory if needed */
8153 if (!islnm || add_6zero) {
8159 /* add fake 000000] if needed */
8172 } /* non-POSIX translation */
8174 } /* End of relative/absolute path handling */
8176 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
8183 if (dir_start != 0) {
8185 /* First characters in a directory are handled special */
8186 while ((*unixptr == '/') ||
8187 ((*unixptr == '.') &&
8188 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8189 (&unixptr[1]==unixend)))) {
8194 /* Skip redundant / in specification */
8195 while ((*unixptr == '/') && (dir_start != 0)) {
8198 if (unixptr == lastslash)
8201 if (unixptr == lastslash)
8204 /* Skip redundant ./ characters */
8205 while ((*unixptr == '.') &&
8206 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8209 if (unixptr == lastslash)
8211 if (*unixptr == '/')
8214 if (unixptr == lastslash)
8217 /* Skip redundant ../ characters */
8218 while ((*unixptr == '.') && (unixptr[1] == '.') &&
8219 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8220 /* Set the backing up flag */
8226 unixptr++; /* first . */
8227 unixptr++; /* second . */
8228 if (unixptr == lastslash)
8230 if (*unixptr == '/') /* The slash */
8233 if (unixptr == lastslash)
8236 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8237 /* Not needed when VMS is pretending to be UNIX. */
8239 /* Is this loop stuck because of too many dots? */
8240 if (loop_flag == 0) {
8241 /* Exit the loop and pass the rest through */
8246 /* Are we done with directories yet? */
8247 if (unixptr >= lastslash) {
8249 /* Watch out for trailing dots */
8258 if (*unixptr == '/')
8262 /* Have we stopped backing up? */
8267 /* dir_start continues to be = 1 */
8269 if (*unixptr == '-') {
8271 *vmsptr++ = *unixptr++;
8275 /* Now are we done with directories yet? */
8276 if (unixptr >= lastslash) {
8278 /* Watch out for trailing dots */
8294 if (unixptr >= unixend)
8297 /* Normal characters - More EFS work probably needed */
8303 /* remove multiple / */
8304 while (unixptr[1] == '/') {
8307 if (unixptr == lastslash) {
8308 /* Watch out for trailing dots */
8320 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8321 /* Not needed when VMS is pretending to be UNIX. */
8325 if (unixptr != unixend)
8330 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8331 (&unixptr[1] == unixend)) {
8337 /* trailing dot ==> '^..' on VMS */
8338 if (unixptr == unixend) {
8346 *vmsptr++ = *unixptr++;
8350 if (quoted && (&unixptr[1] == unixend)) {
8354 in_cnt = copy_expand_unix_filename_escape
8355 (vmsptr, unixptr, &out_cnt, utf8_fl);
8365 in_cnt = copy_expand_unix_filename_escape
8366 (vmsptr, unixptr, &out_cnt, utf8_fl);
8373 /* Make sure directory is closed */
8374 if (unixptr == lastslash) {
8376 vmsptr2 = vmsptr - 1;
8378 if (*vmsptr2 != ']') {
8381 /* directories do not end in a dot bracket */
8382 if (*vmsptr2 == '.') {
8386 if (*vmsptr2 != '^') {
8387 vmsptr--; /* back up over the dot */
8395 /* Add a trailing dot if a file with no extension */
8396 vmsptr2 = vmsptr - 1;
8398 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8399 (*vmsptr2 != ')') && (*lastdot != '.') && (*vmsptr2 != ':')) {
8410 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8411 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8416 /* If a UTF8 flag is being passed, honor it */
8418 if (utf8_fl != NULL) {
8419 utf8_flag = *utf8_fl;
8424 /* If there is a possibility of UTF8, then if any UTF8 characters
8425 are present, then they must be converted to VTF-7
8427 result = strcpy(rslt, path); /* FIX-ME */
8430 result = strcpy(rslt, path);
8437 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8438 static char *int_tovmsspec
8439 (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8444 unsigned long int infront = 0, hasdir = 1;
8447 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8448 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8450 if (vms_debug_fileify) {
8452 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8454 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8458 /* If we fail, we should be setting errno */
8460 set_vaxc_errno(SS$_BADPARAM);
8463 rslt_len = VMS_MAXRSS-1;
8465 /* '.' and '..' are "[]" and "[-]" for a quick check */
8466 if (path[0] == '.') {
8467 if (path[1] == '\0') {
8469 if (utf8_flag != NULL)
8474 if (path[1] == '.' && path[2] == '\0') {
8476 if (utf8_flag != NULL)
8483 /* Posix specifications are now a native VMS format */
8484 /*--------------------------------------------------*/
8485 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8486 if (decc_posix_compliant_pathnames) {
8487 if (strncmp(path,"\"^UP^",5) == 0) {
8488 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8494 /* This is really the only way to see if this is already in VMS format */
8495 sts = vms_split_path
8510 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8511 replacement, because the above parse just took care of most of
8512 what is needed to do vmspath when the specification is already
8515 And if it is not already, it is easier to do the conversion as
8516 part of this routine than to call this routine and then work on
8520 /* If VMS punctuation was found, it is already VMS format */
8521 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8522 if (utf8_flag != NULL)
8525 if (vms_debug_fileify) {
8526 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8530 /* Now, what to do with trailing "." cases where there is no
8531 extension? If this is a UNIX specification, and EFS characters
8532 are enabled, then the trailing "." should be converted to a "^.".
8533 But if this was already a VMS specification, then it should be
8536 So in the case of ambiguity, leave the specification alone.
8540 /* If there is a possibility of UTF8, then if any UTF8 characters
8541 are present, then they must be converted to VTF-7
8543 if (utf8_flag != NULL)
8546 if (vms_debug_fileify) {
8547 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8552 dirend = strrchr(path,'/');
8554 if (dirend == NULL) {
8558 /* If we get here with no UNIX directory delimiters, then this is
8559 not a complete file specification, either garbage a UNIX glob
8560 specification that can not be converted to a VMS wildcard, or
8561 it a UNIX shell macro. MakeMaker wants shell macros passed
8564 utf8 flag setting needs to be preserved.
8569 macro_start = strchr(path,'$');
8570 if (macro_start != NULL) {
8571 if (macro_start[1] == '(') {
8575 if ((decc_efs_charset == 0) || (has_macro)) {
8577 if (vms_debug_fileify) {
8578 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8584 /* If EFS charset mode active, handle the conversion */
8585 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8586 if (decc_efs_charset) {
8587 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8588 if (vms_debug_fileify) {
8589 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8595 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
8596 if (!*(dirend+2)) dirend +=2;
8597 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8598 if (decc_efs_charset == 0) {
8599 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8605 lastdot = strrchr(cp2,'.');
8611 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8613 if (decc_disable_posix_root) {
8614 strcpy(rslt,"sys$disk:[000000]");
8617 strcpy(rslt,"sys$posix_root:[000000]");
8619 if (utf8_flag != NULL)
8621 if (vms_debug_fileify) {
8622 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8626 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8628 trndev = PerlMem_malloc(VMS_MAXRSS);
8629 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8630 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8632 /* DECC special handling */
8634 if (strcmp(rslt,"bin") == 0) {
8635 strcpy(rslt,"sys$system");
8638 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8640 else if (strcmp(rslt,"tmp") == 0) {
8641 strcpy(rslt,"sys$scratch");
8644 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8646 else if (!decc_disable_posix_root) {
8647 strcpy(rslt, "sys$posix_root");
8651 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8652 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8654 else if (strcmp(rslt,"dev") == 0) {
8655 if (strncmp(cp2,"/null", 5) == 0) {
8656 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8657 strcpy(rslt,"NLA0");
8661 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8667 trnend = islnm ? strlen(trndev) - 1 : 0;
8668 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8669 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8670 /* If the first element of the path is a logical name, determine
8671 * whether it has to be translated so we can add more directories. */
8672 if (!islnm || rooted) {
8675 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8679 if (cp2 != dirend) {
8680 strcpy(rslt,trndev);
8681 cp1 = rslt + trnend;
8688 if (decc_disable_posix_root) {
8694 PerlMem_free(trndev);
8699 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8700 cp2 += 2; /* skip over "./" - it's redundant */
8701 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8703 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8704 *(cp1++) = '-'; /* "../" --> "-" */
8707 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8708 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8709 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8710 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8713 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8714 /* Escape the extra dots in EFS file specifications */
8717 if (cp2 > dirend) cp2 = dirend;
8719 else *(cp1++) = '.';
8721 for (; cp2 < dirend; cp2++) {
8723 if (*(cp2-1) == '/') continue;
8724 if (*(cp1-1) != '.') *(cp1++) = '.';
8727 else if (!infront && *cp2 == '.') {
8728 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8729 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
8730 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8731 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
8732 else if (*(cp1-2) == '[') *(cp1-1) = '-';
8733 else { /* back up over previous directory name */
8735 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
8736 if (*(cp1-1) == '[') {
8737 memcpy(cp1,"000000.",7);
8742 if (cp2 == dirend) break;
8744 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8745 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8746 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8747 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8749 *(cp1++) = '.'; /* Simulate trailing '/' */
8750 cp2 += 2; /* for loop will incr this to == dirend */
8752 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8755 if (decc_efs_charset == 0)
8756 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8758 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
8764 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
8766 if (decc_efs_charset == 0)
8773 else *(cp1++) = *cp2;
8777 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8778 if (hasdir) *(cp1++) = ']';
8779 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
8780 /* fixme for ODS5 */
8787 if (decc_efs_charset == 0)
8798 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8799 decc_readdir_dropdotnotype) {
8804 /* trailing dot ==> '^..' on VMS */
8811 *(cp1++) = *(cp2++);
8816 /* This could be a macro to be passed through */
8817 *(cp1++) = *(cp2++);
8819 const char * save_cp2;
8823 /* paranoid check */
8829 *(cp1++) = *(cp2++);
8830 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8831 *(cp1++) = *(cp2++);
8832 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8833 *(cp1++) = *(cp2++);
8836 *(cp1++) = *(cp2++);
8840 if (is_macro == 0) {
8841 /* Not really a macro - never mind */
8854 /* Don't escape again if following character is
8855 * already something we escape.
8857 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8858 *(cp1++) = *(cp2++);
8861 /* But otherwise fall through and escape it. */
8879 *(cp1++) = *(cp2++);
8882 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
8883 * which is wrong. UNIX notation should be ".dir." unless
8884 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8885 * changing this behavior could break more things at this time.
8886 * efs character set effectively does not allow "." to be a version
8887 * delimiter as a further complication about changing this.
8889 if (decc_filename_unix_report != 0) {
8892 *(cp1++) = *(cp2++);
8895 *(cp1++) = *(cp2++);
8898 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8902 /* Fix me for "^]", but that requires making sure that you do
8903 * not back up past the start of the filename
8905 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8910 if (utf8_flag != NULL)
8912 if (vms_debug_fileify) {
8913 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8917 } /* end of int_tovmsspec() */
8920 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8921 static char *mp_do_tovmsspec
8922 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
8923 static char __tovmsspec_retbuf[VMS_MAXRSS];
8924 char * vmsspec, *ret_spec, *ret_buf;
8928 if (ret_buf == NULL) {
8930 Newx(vmsspec, VMS_MAXRSS, char);
8931 if (vmsspec == NULL)
8932 _ckvmssts(SS$_INSFMEM);
8935 ret_buf = __tovmsspec_retbuf;
8939 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8941 if (ret_spec == NULL) {
8942 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8949 } /* end of mp_do_tovmsspec() */
8951 /* External entry points */
8952 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8953 { return do_tovmsspec(path,buf,0,NULL); }
8954 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8955 { return do_tovmsspec(path,buf,1,NULL); }
8956 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8957 { return do_tovmsspec(path,buf,0,utf8_fl); }
8958 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8959 { return do_tovmsspec(path,buf,1,utf8_fl); }
8961 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
8962 /* Internal routine for use with out an explicit context present */
8963 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
8965 char * ret_spec, *pathified;
8970 pathified = PerlMem_malloc(VMS_MAXRSS);
8971 if (pathified == NULL)
8972 _ckvmssts_noperl(SS$_INSFMEM);
8974 ret_spec = int_pathify_dirspec(path, pathified);
8976 if (ret_spec == NULL) {
8977 PerlMem_free(pathified);
8981 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
8983 PerlMem_free(pathified);
8988 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8989 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8990 static char __tovmspath_retbuf[VMS_MAXRSS];
8992 char *pathified, *vmsified, *cp;
8994 if (path == NULL) return NULL;
8995 pathified = PerlMem_malloc(VMS_MAXRSS);
8996 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8997 if (int_pathify_dirspec(path, pathified) == NULL) {
8998 PerlMem_free(pathified);
9004 Newx(vmsified, VMS_MAXRSS, char);
9005 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
9006 PerlMem_free(pathified);
9007 if (vmsified) Safefree(vmsified);
9010 PerlMem_free(pathified);
9015 vmslen = strlen(vmsified);
9016 Newx(cp,vmslen+1,char);
9017 memcpy(cp,vmsified,vmslen);
9023 strcpy(__tovmspath_retbuf,vmsified);
9025 return __tovmspath_retbuf;
9028 } /* end of do_tovmspath() */
9030 /* External entry points */
9031 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
9032 { return do_tovmspath(path,buf,0, NULL); }
9033 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
9034 { return do_tovmspath(path,buf,1, NULL); }
9035 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
9036 { return do_tovmspath(path,buf,0,utf8_fl); }
9037 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
9038 { return do_tovmspath(path,buf,1,utf8_fl); }
9041 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
9042 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
9043 static char __tounixpath_retbuf[VMS_MAXRSS];
9045 char *pathified, *unixified, *cp;
9047 if (path == NULL) return NULL;
9048 pathified = PerlMem_malloc(VMS_MAXRSS);
9049 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
9050 if (int_pathify_dirspec(path, pathified) == NULL) {
9051 PerlMem_free(pathified);
9057 Newx(unixified, VMS_MAXRSS, char);
9059 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
9060 PerlMem_free(pathified);
9061 if (unixified) Safefree(unixified);
9064 PerlMem_free(pathified);
9069 unixlen = strlen(unixified);
9070 Newx(cp,unixlen+1,char);
9071 memcpy(cp,unixified,unixlen);
9073 Safefree(unixified);
9077 strcpy(__tounixpath_retbuf,unixified);
9078 Safefree(unixified);
9079 return __tounixpath_retbuf;
9082 } /* end of do_tounixpath() */
9084 /* External entry points */
9085 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
9086 { return do_tounixpath(path,buf,0,NULL); }
9087 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
9088 { return do_tounixpath(path,buf,1,NULL); }
9089 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9090 { return do_tounixpath(path,buf,0,utf8_fl); }
9091 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9092 { return do_tounixpath(path,buf,1,utf8_fl); }
9095 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
9097 *****************************************************************************
9099 * Copyright (C) 1989-1994, 2007 by *
9100 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
9102 * Permission is hereby granted for the reproduction of this software *
9103 * on condition that this copyright notice is included in source *
9104 * distributions of the software. The code may be modified and *
9105 * distributed under the same terms as Perl itself. *
9107 * 27-Aug-1994 Modified for inclusion in perl5 *
9108 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
9109 *****************************************************************************
9113 * getredirection() is intended to aid in porting C programs
9114 * to VMS (Vax-11 C). The native VMS environment does not support
9115 * '>' and '<' I/O redirection, or command line wild card expansion,
9116 * or a command line pipe mechanism using the '|' AND background
9117 * command execution '&'. All of these capabilities are provided to any
9118 * C program which calls this procedure as the first thing in the
9120 * The piping mechanism will probably work with almost any 'filter' type
9121 * of program. With suitable modification, it may useful for other
9122 * portability problems as well.
9124 * Author: Mark Pizzolato (mark AT infocomm DOT com)
9128 struct list_item *next;
9132 static void add_item(struct list_item **head,
9133 struct list_item **tail,
9137 static void mp_expand_wild_cards(pTHX_ char *item,
9138 struct list_item **head,
9139 struct list_item **tail,
9142 static int background_process(pTHX_ int argc, char **argv);
9144 static void pipe_and_fork(pTHX_ char **cmargv);
9146 /*{{{ void getredirection(int *ac, char ***av)*/
9148 mp_getredirection(pTHX_ int *ac, char ***av)
9150 * Process vms redirection arg's. Exit if any error is seen.
9151 * If getredirection() processes an argument, it is erased
9152 * from the vector. getredirection() returns a new argc and argv value.
9153 * In the event that a background command is requested (by a trailing "&"),
9154 * this routine creates a background subprocess, and simply exits the program.
9156 * Warning: do not try to simplify the code for vms. The code
9157 * presupposes that getredirection() is called before any data is
9158 * read from stdin or written to stdout.
9160 * Normal usage is as follows:
9166 * getredirection(&argc, &argv);
9170 int argc = *ac; /* Argument Count */
9171 char **argv = *av; /* Argument Vector */
9172 char *ap; /* Argument pointer */
9173 int j; /* argv[] index */
9174 int item_count = 0; /* Count of Items in List */
9175 struct list_item *list_head = 0; /* First Item in List */
9176 struct list_item *list_tail; /* Last Item in List */
9177 char *in = NULL; /* Input File Name */
9178 char *out = NULL; /* Output File Name */
9179 char *outmode = "w"; /* Mode to Open Output File */
9180 char *err = NULL; /* Error File Name */
9181 char *errmode = "w"; /* Mode to Open Error File */
9182 int cmargc = 0; /* Piped Command Arg Count */
9183 char **cmargv = NULL;/* Piped Command Arg Vector */
9186 * First handle the case where the last thing on the line ends with
9187 * a '&'. This indicates the desire for the command to be run in a
9188 * subprocess, so we satisfy that desire.
9191 if (0 == strcmp("&", ap))
9192 exit(background_process(aTHX_ --argc, argv));
9193 if (*ap && '&' == ap[strlen(ap)-1])
9195 ap[strlen(ap)-1] = '\0';
9196 exit(background_process(aTHX_ argc, argv));
9199 * Now we handle the general redirection cases that involve '>', '>>',
9200 * '<', and pipes '|'.
9202 for (j = 0; j < argc; ++j)
9204 if (0 == strcmp("<", argv[j]))
9208 fprintf(stderr,"No input file after < on command line");
9209 exit(LIB$_WRONUMARG);
9214 if ('<' == *(ap = argv[j]))
9219 if (0 == strcmp(">", ap))
9223 fprintf(stderr,"No output file after > on command line");
9224 exit(LIB$_WRONUMARG);
9243 fprintf(stderr,"No output file after > or >> on command line");
9244 exit(LIB$_WRONUMARG);
9248 if (('2' == *ap) && ('>' == ap[1]))
9265 fprintf(stderr,"No output file after 2> or 2>> on command line");
9266 exit(LIB$_WRONUMARG);
9270 if (0 == strcmp("|", argv[j]))
9274 fprintf(stderr,"No command into which to pipe on command line");
9275 exit(LIB$_WRONUMARG);
9277 cmargc = argc-(j+1);
9278 cmargv = &argv[j+1];
9282 if ('|' == *(ap = argv[j]))
9290 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9293 * Allocate and fill in the new argument vector, Some Unix's terminate
9294 * the list with an extra null pointer.
9296 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9297 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9299 for (j = 0; j < item_count; ++j, list_head = list_head->next)
9300 argv[j] = list_head->value;
9306 fprintf(stderr,"'|' and '>' may not both be specified on command line");
9307 exit(LIB$_INVARGORD);
9309 pipe_and_fork(aTHX_ cmargv);
9312 /* Check for input from a pipe (mailbox) */
9314 if (in == NULL && 1 == isapipe(0))
9316 char mbxname[L_tmpnam];
9318 long int dvi_item = DVI$_DEVBUFSIZ;
9319 $DESCRIPTOR(mbxnam, "");
9320 $DESCRIPTOR(mbxdevnam, "");
9322 /* Input from a pipe, reopen it in binary mode to disable */
9323 /* carriage control processing. */
9325 fgetname(stdin, mbxname, 1);
9326 mbxnam.dsc$a_pointer = mbxname;
9327 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9328 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9329 mbxdevnam.dsc$a_pointer = mbxname;
9330 mbxdevnam.dsc$w_length = sizeof(mbxname);
9331 dvi_item = DVI$_DEVNAM;
9332 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9333 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9336 freopen(mbxname, "rb", stdin);
9339 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9343 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9345 fprintf(stderr,"Can't open input file %s as stdin",in);
9348 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9350 fprintf(stderr,"Can't open output file %s as stdout",out);
9353 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
9356 if (strcmp(err,"&1") == 0) {
9357 dup2(fileno(stdout), fileno(stderr));
9358 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
9361 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9363 fprintf(stderr,"Can't open error file %s as stderr",err);
9367 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9371 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
9374 #ifdef ARGPROC_DEBUG
9375 PerlIO_printf(Perl_debug_log, "Arglist:\n");
9376 for (j = 0; j < *ac; ++j)
9377 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9379 /* Clear errors we may have hit expanding wildcards, so they don't
9380 show up in Perl's $! later */
9381 set_errno(0); set_vaxc_errno(1);
9382 } /* end of getredirection() */
9385 static void add_item(struct list_item **head,
9386 struct list_item **tail,
9392 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9393 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9397 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9398 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9399 *tail = (*tail)->next;
9401 (*tail)->value = value;
9405 static void mp_expand_wild_cards(pTHX_ char *item,
9406 struct list_item **head,
9407 struct list_item **tail,
9411 unsigned long int context = 0;
9419 $DESCRIPTOR(filespec, "");
9420 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9421 $DESCRIPTOR(resultspec, "");
9422 unsigned long int lff_flags = 0;
9426 #ifdef VMS_LONGNAME_SUPPORT
9427 lff_flags = LIB$M_FIL_LONG_NAMES;
9430 for (cp = item; *cp; cp++) {
9431 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9432 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9434 if (!*cp || isspace(*cp))
9436 add_item(head, tail, item, count);
9441 /* "double quoted" wild card expressions pass as is */
9442 /* From DCL that means using e.g.: */
9443 /* perl program """perl.*""" */
9444 item_len = strlen(item);
9445 if ( '"' == *item && '"' == item[item_len-1] )
9448 item[item_len-2] = '\0';
9449 add_item(head, tail, item, count);
9453 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9454 resultspec.dsc$b_class = DSC$K_CLASS_D;
9455 resultspec.dsc$a_pointer = NULL;
9456 vmsspec = PerlMem_malloc(VMS_MAXRSS);
9457 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9458 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9459 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9460 if (!isunix || !filespec.dsc$a_pointer)
9461 filespec.dsc$a_pointer = item;
9462 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9464 * Only return version specs, if the caller specified a version
9466 had_version = strchr(item, ';');
9468 * Only return device and directory specs, if the caller specified either.
9470 had_device = strchr(item, ':');
9471 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9473 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9474 (&filespec, &resultspec, &context,
9475 &defaultspec, 0, &rms_sts, &lff_flags)))
9480 string = PerlMem_malloc(resultspec.dsc$w_length+1);
9481 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9482 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
9483 string[resultspec.dsc$w_length] = '\0';
9484 if (NULL == had_version)
9485 *(strrchr(string, ';')) = '\0';
9486 if ((!had_directory) && (had_device == NULL))
9488 if (NULL == (devdir = strrchr(string, ']')))
9489 devdir = strrchr(string, '>');
9490 strcpy(string, devdir + 1);
9493 * Be consistent with what the C RTL has already done to the rest of
9494 * the argv items and lowercase all of these names.
9496 if (!decc_efs_case_preserve) {
9497 for (c = string; *c; ++c)
9501 if (isunix) trim_unixpath(string,item,1);
9502 add_item(head, tail, string, count);
9505 PerlMem_free(vmsspec);
9506 if (sts != RMS$_NMF)
9508 set_vaxc_errno(sts);
9511 case RMS$_FNF: case RMS$_DNF:
9512 set_errno(ENOENT); break;
9514 set_errno(ENOTDIR); break;
9516 set_errno(ENODEV); break;
9517 case RMS$_FNM: case RMS$_SYN:
9518 set_errno(EINVAL); break;
9520 set_errno(EACCES); break;
9522 _ckvmssts_noperl(sts);
9526 add_item(head, tail, item, count);
9527 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9528 _ckvmssts_noperl(lib$find_file_end(&context));
9531 static int child_st[2];/* Event Flag set when child process completes */
9533 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
9535 static unsigned long int exit_handler(int *status)
9539 if (0 == child_st[0])
9541 #ifdef ARGPROC_DEBUG
9542 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
9544 fflush(stdout); /* Have to flush pipe for binary data to */
9545 /* terminate properly -- <tp@mccall.com> */
9546 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9547 sys$dassgn(child_chan);
9549 sys$synch(0, child_st);
9554 static void sig_child(int chan)
9556 #ifdef ARGPROC_DEBUG
9557 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
9559 if (child_st[0] == 0)
9563 static struct exit_control_block exit_block =
9568 &exit_block.exit_status,
9573 pipe_and_fork(pTHX_ char **cmargv)
9576 struct dsc$descriptor_s *vmscmd;
9577 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9578 int sts, j, l, ismcr, quote, tquote = 0;
9580 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
9581 vms_execfree(vmscmd);
9586 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
9587 && toupper(*(q+2)) == 'R' && !*(q+3);
9589 while (q && l < MAX_DCL_LINE_LENGTH) {
9591 if (j > 0 && quote) {
9597 if (ismcr && j > 1) quote = 1;
9598 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9601 if (quote || tquote) {
9607 if ((quote||tquote) && *q == '"') {
9617 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9619 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9623 static int background_process(pTHX_ int argc, char **argv)
9625 char command[MAX_DCL_SYMBOL + 1] = "$";
9626 $DESCRIPTOR(value, "");
9627 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9628 static $DESCRIPTOR(null, "NLA0:");
9629 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9631 $DESCRIPTOR(pidstr, "");
9633 unsigned long int flags = 17, one = 1, retsts;
9636 strcat(command, argv[0]);
9637 len = strlen(command);
9638 while (--argc && (len < MAX_DCL_SYMBOL))
9640 strcat(command, " \"");
9641 strcat(command, *(++argv));
9642 strcat(command, "\"");
9643 len = strlen(command);
9645 value.dsc$a_pointer = command;
9646 value.dsc$w_length = strlen(value.dsc$a_pointer);
9647 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9648 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9649 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9650 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9653 _ckvmssts_noperl(retsts);
9655 #ifdef ARGPROC_DEBUG
9656 PerlIO_printf(Perl_debug_log, "%s\n", command);
9658 sprintf(pidstring, "%08X", pid);
9659 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9660 pidstr.dsc$a_pointer = pidstring;
9661 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9662 lib$set_symbol(&pidsymbol, &pidstr);
9666 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9669 /* OS-specific initialization at image activation (not thread startup) */
9670 /* Older VAXC header files lack these constants */
9671 #ifndef JPI$_RIGHTS_SIZE
9672 # define JPI$_RIGHTS_SIZE 817
9674 #ifndef KGB$M_SUBSYSTEM
9675 # define KGB$M_SUBSYSTEM 0x8
9678 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9680 /*{{{void vms_image_init(int *, char ***)*/
9682 vms_image_init(int *argcp, char ***argvp)
9685 char eqv[LNM$C_NAMLENGTH+1] = "";
9686 unsigned int len, tabct = 8, tabidx = 0;
9687 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9688 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9689 unsigned short int dummy, rlen;
9690 struct dsc$descriptor_s **tabvec;
9691 #if defined(PERL_IMPLICIT_CONTEXT)
9694 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9695 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9696 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9699 #ifdef KILL_BY_SIGPRC
9700 Perl_csighandler_init();
9703 #if __CRTL_VER >= 70300000 && !defined(__VAX)
9704 /* This was moved from the pre-image init handler because on threaded */
9705 /* Perl it was always returning 0 for the default value. */
9706 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9709 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9712 initial = decc$feature_get_value(s, 4);
9714 /* initial is: 0 if nothing has set the feature */
9715 /* -1 if initialized to default */
9716 /* 1 if set by logical name */
9717 /* 2 if set by decc$feature_set_value */
9718 decc_disable_posix_root = decc$feature_get_value(s, 1);
9720 /* If the value is not valid, force the feature off */
9721 if (decc_disable_posix_root < 0) {
9722 decc$feature_set_value(s, 1, 1);
9723 decc_disable_posix_root = 1;
9727 /* Nothing has asked for it explicitly, so use our own default. */
9728 decc_disable_posix_root = 1;
9729 decc$feature_set_value(s, 1, 1);
9735 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9736 _ckvmssts_noperl(iosb[0]);
9737 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9738 if (iprv[i]) { /* Running image installed with privs? */
9739 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
9744 /* Rights identifiers might trigger tainting as well. */
9745 if (!will_taint && (rlen || rsz)) {
9746 while (rlen < rsz) {
9747 /* We didn't get all the identifiers on the first pass. Allocate a
9748 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9749 * were needed to hold all identifiers at time of last call; we'll
9750 * allocate that many unsigned long ints), and go back and get 'em.
9751 * If it gave us less than it wanted to despite ample buffer space,
9752 * something's broken. Is your system missing a system identifier?
9754 if (rsz <= jpilist[1].buflen) {
9755 /* Perl_croak accvios when used this early in startup. */
9756 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9757 rsz, (unsigned long) jpilist[1].buflen,
9758 "Check your rights database for corruption.\n");
9761 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9762 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9763 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9764 jpilist[1].buflen = rsz * sizeof(unsigned long int);
9765 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9766 _ckvmssts_noperl(iosb[0]);
9768 mask = jpilist[1].bufadr;
9769 /* Check attribute flags for each identifier (2nd longword); protected
9770 * subsystem identifiers trigger tainting.
9772 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9773 if (mask[i] & KGB$M_SUBSYSTEM) {
9778 if (mask != rlst) PerlMem_free(mask);
9781 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9782 * logical, some versions of the CRTL will add a phanthom /000000/
9783 * directory. This needs to be removed.
9785 if (decc_filename_unix_report) {
9788 ulen = strlen(argvp[0][0]);
9790 zeros = strstr(argvp[0][0], "/000000/");
9791 if (zeros != NULL) {
9793 mlen = ulen - (zeros - argvp[0][0]) - 7;
9794 memmove(zeros, &zeros[7], mlen);
9796 argvp[0][0][ulen] = '\0';
9799 /* It also may have a trailing dot that needs to be removed otherwise
9800 * it will be converted to VMS mode incorrectly.
9803 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9804 argvp[0][0][ulen] = '\0';
9807 /* We need to use this hack to tell Perl it should run with tainting,
9808 * since its tainting flag may be part of the PL_curinterp struct, which
9809 * hasn't been allocated when vms_image_init() is called.
9812 char **newargv, **oldargv;
9814 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9815 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9816 newargv[0] = oldargv[0];
9817 newargv[1] = PerlMem_malloc(3 * sizeof(char));
9818 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9819 strcpy(newargv[1], "-T");
9820 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9822 newargv[*argcp] = NULL;
9823 /* We orphan the old argv, since we don't know where it's come from,
9824 * so we don't know how to free it.
9828 else { /* Did user explicitly request tainting? */
9830 char *cp, **av = *argvp;
9831 for (i = 1; i < *argcp; i++) {
9832 if (*av[i] != '-') break;
9833 for (cp = av[i]+1; *cp; cp++) {
9834 if (*cp == 'T') { will_taint = 1; break; }
9835 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9836 strchr("DFIiMmx",*cp)) break;
9838 if (will_taint) break;
9843 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9846 tabvec = (struct dsc$descriptor_s **)
9847 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9848 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9850 else if (tabidx >= tabct) {
9852 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9853 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9855 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9856 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9857 tabvec[tabidx]->dsc$w_length = 0;
9858 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9859 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
9860 tabvec[tabidx]->dsc$a_pointer = NULL;
9861 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
9863 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9865 getredirection(argcp,argvp);
9866 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9868 # include <reentrancy.h>
9869 decc$set_reentrancy(C$C_MULTITHREAD);
9878 * Trim Unix-style prefix off filespec, so it looks like what a shell
9879 * glob expansion would return (i.e. from specified prefix on, not
9880 * full path). Note that returned filespec is Unix-style, regardless
9881 * of whether input filespec was VMS-style or Unix-style.
9883 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9884 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9885 * vector of options; at present, only bit 0 is used, and if set tells
9886 * trim unixpath to try the current default directory as a prefix when
9887 * presented with a possibly ambiguous ... wildcard.
9889 * Returns !=0 on success, with trimmed filespec replacing contents of
9890 * fspec, and 0 on failure, with contents of fpsec unchanged.
9892 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9894 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9896 char *unixified, *unixwild,
9897 *template, *base, *end, *cp1, *cp2;
9898 register int tmplen, reslen = 0, dirs = 0;
9900 if (!wildspec || !fspec) return 0;
9902 unixwild = PerlMem_malloc(VMS_MAXRSS);
9903 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9904 template = unixwild;
9905 if (strpbrk(wildspec,"]>:") != NULL) {
9906 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
9907 PerlMem_free(unixwild);
9912 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
9913 unixwild[VMS_MAXRSS-1] = 0;
9915 unixified = PerlMem_malloc(VMS_MAXRSS);
9916 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9917 if (strpbrk(fspec,"]>:") != NULL) {
9918 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
9919 PerlMem_free(unixwild);
9920 PerlMem_free(unixified);
9923 else base = unixified;
9924 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9925 * check to see that final result fits into (isn't longer than) fspec */
9926 reslen = strlen(fspec);
9930 /* No prefix or absolute path on wildcard, so nothing to remove */
9931 if (!*template || *template == '/') {
9932 PerlMem_free(unixwild);
9933 if (base == fspec) {
9934 PerlMem_free(unixified);
9937 tmplen = strlen(unixified);
9938 if (tmplen > reslen) {
9939 PerlMem_free(unixified);
9940 return 0; /* not enough space */
9942 /* Copy unixified resultant, including trailing NUL */
9943 memmove(fspec,unixified,tmplen+1);
9944 PerlMem_free(unixified);
9948 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9949 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
9950 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
9951 for (cp1 = end ;cp1 >= base; cp1--)
9952 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9954 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9955 PerlMem_free(unixified);
9956 PerlMem_free(unixwild);
9961 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9962 int ells = 1, totells, segdirs, match;
9963 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9964 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9966 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9968 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9969 tpl = PerlMem_malloc(VMS_MAXRSS);
9970 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9971 if (ellipsis == template && opts & 1) {
9972 /* Template begins with an ellipsis. Since we can't tell how many
9973 * directory names at the front of the resultant to keep for an
9974 * arbitrary starting point, we arbitrarily choose the current
9975 * default directory as a starting point. If it's there as a prefix,
9976 * clip it off. If not, fall through and act as if the leading
9977 * ellipsis weren't there (i.e. return shortest possible path that
9978 * could match template).
9980 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9982 PerlMem_free(unixified);
9983 PerlMem_free(unixwild);
9986 if (!decc_efs_case_preserve) {
9987 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9988 if (_tolower(*cp1) != _tolower(*cp2)) break;
9990 segdirs = dirs - totells; /* Min # of dirs we must have left */
9991 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9992 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9993 memmove(fspec,cp2+1,end - cp2);
9995 PerlMem_free(unixified);
9996 PerlMem_free(unixwild);
10000 /* First off, back up over constant elements at end of path */
10002 for (front = end ; front >= base; front--)
10003 if (*front == '/' && !dirs--) { front++; break; }
10005 lcres = PerlMem_malloc(VMS_MAXRSS);
10006 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10007 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
10009 if (!decc_efs_case_preserve) {
10010 *cp2 = _tolower(*cp1); /* Make lc copy for match */
10018 PerlMem_free(unixified);
10019 PerlMem_free(unixwild);
10020 PerlMem_free(lcres);
10021 return 0; /* Path too long. */
10024 *cp2 = '\0'; /* Pick up with memcpy later */
10025 lcfront = lcres + (front - base);
10026 /* Now skip over each ellipsis and try to match the path in front of it. */
10028 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
10029 if (*(cp1) == '.' && *(cp1+1) == '.' &&
10030 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
10031 if (cp1 < template) break; /* template started with an ellipsis */
10032 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
10033 ellipsis = cp1; continue;
10035 wilddsc.dsc$a_pointer = tpl;
10036 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
10038 for (segdirs = 0, cp2 = tpl;
10039 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
10041 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
10043 if (!decc_efs_case_preserve) {
10044 *cp2 = _tolower(*cp1); /* else lowercase for match */
10047 *cp2 = *cp1; /* else preserve case for match */
10050 if (*cp2 == '/') segdirs++;
10052 if (cp1 != ellipsis - 1) {
10054 PerlMem_free(unixified);
10055 PerlMem_free(unixwild);
10056 PerlMem_free(lcres);
10057 return 0; /* Path too long */
10059 /* Back up at least as many dirs as in template before matching */
10060 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
10061 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
10062 for (match = 0; cp1 > lcres;) {
10063 resdsc.dsc$a_pointer = cp1;
10064 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
10066 if (match == 1) lcfront = cp1;
10068 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
10072 PerlMem_free(unixified);
10073 PerlMem_free(unixwild);
10074 PerlMem_free(lcres);
10075 return 0; /* Can't find prefix ??? */
10077 if (match > 1 && opts & 1) {
10078 /* This ... wildcard could cover more than one set of dirs (i.e.
10079 * a set of similar dir names is repeated). If the template
10080 * contains more than 1 ..., upstream elements could resolve the
10081 * ambiguity, but it's not worth a full backtracking setup here.
10082 * As a quick heuristic, clip off the current default directory
10083 * if it's present to find the trimmed spec, else use the
10084 * shortest string that this ... could cover.
10086 char def[NAM$C_MAXRSS+1], *st;
10088 if (getcwd(def, sizeof def,0) == NULL) {
10089 PerlMem_free(unixified);
10090 PerlMem_free(unixwild);
10091 PerlMem_free(lcres);
10095 if (!decc_efs_case_preserve) {
10096 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10097 if (_tolower(*cp1) != _tolower(*cp2)) break;
10099 segdirs = dirs - totells; /* Min # of dirs we must have left */
10100 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
10101 if (*cp1 == '\0' && *cp2 == '/') {
10102 memmove(fspec,cp2+1,end - cp2);
10104 PerlMem_free(unixified);
10105 PerlMem_free(unixwild);
10106 PerlMem_free(lcres);
10109 /* Nope -- stick with lcfront from above and keep going. */
10112 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
10114 PerlMem_free(unixified);
10115 PerlMem_free(unixwild);
10116 PerlMem_free(lcres);
10120 } /* end of trim_unixpath() */
10125 * VMS readdir() routines.
10126 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
10128 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
10129 * Minor modifications to original routines.
10132 /* readdir may have been redefined by reentr.h, so make sure we get
10133 * the local version for what we do here.
10138 #if !defined(PERL_IMPLICIT_CONTEXT)
10139 # define readdir Perl_readdir
10141 # define readdir(a) Perl_readdir(aTHX_ a)
10144 /* Number of elements in vms_versions array */
10145 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
10148 * Open a directory, return a handle for later use.
10150 /*{{{ DIR *opendir(char*name) */
10152 Perl_opendir(pTHX_ const char *name)
10158 Newx(dir, VMS_MAXRSS, char);
10159 if (int_tovmspath(name, dir, NULL) == NULL) {
10163 /* Check access before stat; otherwise stat does not
10164 * accurately report whether it's a directory.
10166 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
10167 /* cando_by_name has already set errno */
10171 if (flex_stat(dir,&sb) == -1) return NULL;
10172 if (!S_ISDIR(sb.st_mode)) {
10174 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
10177 /* Get memory for the handle, and the pattern. */
10179 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
10181 /* Fill in the fields; mainly playing with the descriptor. */
10182 sprintf(dd->pattern, "%s*.*",dir);
10187 /* By saying we always want the result of readdir() in unix format, we
10188 * are really saying we want all the escapes removed. Otherwise the caller,
10189 * having no way to know whether it's already in VMS format, might send it
10190 * through tovmsspec again, thus double escaping.
10192 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
10193 dd->pat.dsc$a_pointer = dd->pattern;
10194 dd->pat.dsc$w_length = strlen(dd->pattern);
10195 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10196 dd->pat.dsc$b_class = DSC$K_CLASS_S;
10197 #if defined(USE_ITHREADS)
10198 Newx(dd->mutex,1,perl_mutex);
10199 MUTEX_INIT( (perl_mutex *) dd->mutex );
10205 } /* end of opendir() */
10209 * Set the flag to indicate we want versions or not.
10211 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10213 vmsreaddirversions(DIR *dd, int flag)
10216 dd->flags |= PERL_VMSDIR_M_VERSIONS;
10218 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10223 * Free up an opened directory.
10225 /*{{{ void closedir(DIR *dd)*/
10227 Perl_closedir(DIR *dd)
10231 sts = lib$find_file_end(&dd->context);
10232 Safefree(dd->pattern);
10233 #if defined(USE_ITHREADS)
10234 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10235 Safefree(dd->mutex);
10242 * Collect all the version numbers for the current file.
10245 collectversions(pTHX_ DIR *dd)
10247 struct dsc$descriptor_s pat;
10248 struct dsc$descriptor_s res;
10250 char *p, *text, *buff;
10252 unsigned long context, tmpsts;
10254 /* Convenient shorthand. */
10257 /* Add the version wildcard, ignoring the "*.*" put on before */
10258 i = strlen(dd->pattern);
10259 Newx(text,i + e->d_namlen + 3,char);
10260 strcpy(text, dd->pattern);
10261 sprintf(&text[i - 3], "%s;*", e->d_name);
10263 /* Set up the pattern descriptor. */
10264 pat.dsc$a_pointer = text;
10265 pat.dsc$w_length = i + e->d_namlen - 1;
10266 pat.dsc$b_dtype = DSC$K_DTYPE_T;
10267 pat.dsc$b_class = DSC$K_CLASS_S;
10269 /* Set up result descriptor. */
10270 Newx(buff, VMS_MAXRSS, char);
10271 res.dsc$a_pointer = buff;
10272 res.dsc$w_length = VMS_MAXRSS - 1;
10273 res.dsc$b_dtype = DSC$K_DTYPE_T;
10274 res.dsc$b_class = DSC$K_CLASS_S;
10276 /* Read files, collecting versions. */
10277 for (context = 0, e->vms_verscount = 0;
10278 e->vms_verscount < VERSIZE(e);
10279 e->vms_verscount++) {
10280 unsigned long rsts;
10281 unsigned long flags = 0;
10283 #ifdef VMS_LONGNAME_SUPPORT
10284 flags = LIB$M_FIL_LONG_NAMES;
10286 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10287 if (tmpsts == RMS$_NMF || context == 0) break;
10289 buff[VMS_MAXRSS - 1] = '\0';
10290 if ((p = strchr(buff, ';')))
10291 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10293 e->vms_versions[e->vms_verscount] = -1;
10296 _ckvmssts(lib$find_file_end(&context));
10300 } /* end of collectversions() */
10303 * Read the next entry from the directory.
10305 /*{{{ struct dirent *readdir(DIR *dd)*/
10307 Perl_readdir(pTHX_ DIR *dd)
10309 struct dsc$descriptor_s res;
10311 unsigned long int tmpsts;
10312 unsigned long rsts;
10313 unsigned long flags = 0;
10314 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10315 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10317 /* Set up result descriptor, and get next file. */
10318 Newx(buff, VMS_MAXRSS, char);
10319 res.dsc$a_pointer = buff;
10320 res.dsc$w_length = VMS_MAXRSS - 1;
10321 res.dsc$b_dtype = DSC$K_DTYPE_T;
10322 res.dsc$b_class = DSC$K_CLASS_S;
10324 #ifdef VMS_LONGNAME_SUPPORT
10325 flags = LIB$M_FIL_LONG_NAMES;
10328 tmpsts = lib$find_file
10329 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10330 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
10331 if (!(tmpsts & 1)) {
10332 set_vaxc_errno(tmpsts);
10335 set_errno(EACCES); break;
10337 set_errno(ENODEV); break;
10339 set_errno(ENOTDIR); break;
10340 case RMS$_FNF: case RMS$_DNF:
10341 set_errno(ENOENT); break;
10343 set_errno(EVMSERR);
10349 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10350 buff[res.dsc$w_length] = '\0';
10351 p = buff + res.dsc$w_length;
10352 while (--p >= buff) if (!isspace(*p)) break;
10354 if (!decc_efs_case_preserve) {
10355 for (p = buff; *p; p++) *p = _tolower(*p);
10358 /* Skip any directory component and just copy the name. */
10359 sts = vms_split_path
10374 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10376 /* In Unix report mode, remove the ".dir;1" from the name */
10377 /* if it is a real directory. */
10378 if (decc_filename_unix_report || decc_efs_charset) {
10379 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
10383 ret_sts = flex_lstat(buff, &statbuf);
10384 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10391 /* Drop NULL extensions on UNIX file specification */
10392 if ((e_len == 1) && decc_readdir_dropdotnotype) {
10398 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
10399 dd->entry.d_name[n_len + e_len] = '\0';
10400 dd->entry.d_namlen = strlen(dd->entry.d_name);
10402 /* Convert the filename to UNIX format if needed */
10403 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10405 /* Translate the encoded characters. */
10406 /* Fixme: Unicode handling could result in embedded 0 characters */
10407 if (strchr(dd->entry.d_name, '^') != NULL) {
10408 char new_name[256];
10410 p = dd->entry.d_name;
10413 int inchars_read, outchars_added;
10414 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10416 q += outchars_added;
10418 /* if outchars_added > 1, then this is a wide file specification */
10419 /* Wide file specifications need to be passed in Perl */
10420 /* counted strings apparently with a Unicode flag */
10423 strcpy(dd->entry.d_name, new_name);
10424 dd->entry.d_namlen = strlen(dd->entry.d_name);
10428 dd->entry.vms_verscount = 0;
10429 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10433 } /* end of readdir() */
10437 * Read the next entry from the directory -- thread-safe version.
10439 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10441 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10445 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10447 entry = readdir(dd);
10449 retval = ( *result == NULL ? errno : 0 );
10451 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10455 } /* end of readdir_r() */
10459 * Return something that can be used in a seekdir later.
10461 /*{{{ long telldir(DIR *dd)*/
10463 Perl_telldir(DIR *dd)
10470 * Return to a spot where we used to be. Brute force.
10472 /*{{{ void seekdir(DIR *dd,long count)*/
10474 Perl_seekdir(pTHX_ DIR *dd, long count)
10478 /* If we haven't done anything yet... */
10479 if (dd->count == 0)
10482 /* Remember some state, and clear it. */
10483 old_flags = dd->flags;
10484 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10485 _ckvmssts(lib$find_file_end(&dd->context));
10488 /* The increment is in readdir(). */
10489 for (dd->count = 0; dd->count < count; )
10492 dd->flags = old_flags;
10494 } /* end of seekdir() */
10497 /* VMS subprocess management
10499 * my_vfork() - just a vfork(), after setting a flag to record that
10500 * the current script is trying a Unix-style fork/exec.
10502 * vms_do_aexec() and vms_do_exec() are called in response to the
10503 * perl 'exec' function. If this follows a vfork call, then they
10504 * call out the regular perl routines in doio.c which do an
10505 * execvp (for those who really want to try this under VMS).
10506 * Otherwise, they do exactly what the perl docs say exec should
10507 * do - terminate the current script and invoke a new command
10508 * (See below for notes on command syntax.)
10510 * do_aspawn() and do_spawn() implement the VMS side of the perl
10511 * 'system' function.
10513 * Note on command arguments to perl 'exec' and 'system': When handled
10514 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10515 * are concatenated to form a DCL command string. If the first non-numeric
10516 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10517 * the command string is handed off to DCL directly. Otherwise,
10518 * the first token of the command is taken as the filespec of an image
10519 * to run. The filespec is expanded using a default type of '.EXE' and
10520 * the process defaults for device, directory, etc., and if found, the resultant
10521 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10522 * the command string as parameters. This is perhaps a bit complicated,
10523 * but I hope it will form a happy medium between what VMS folks expect
10524 * from lib$spawn and what Unix folks expect from exec.
10527 static int vfork_called;
10529 /*{{{int my_vfork()*/
10540 vms_execfree(struct dsc$descriptor_s *vmscmd)
10543 if (vmscmd->dsc$a_pointer) {
10544 PerlMem_free(vmscmd->dsc$a_pointer);
10546 PerlMem_free(vmscmd);
10551 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10553 char *junk, *tmps = NULL;
10554 register size_t cmdlen = 0;
10561 tmps = SvPV(really,rlen);
10563 cmdlen += rlen + 1;
10568 for (idx++; idx <= sp; idx++) {
10570 junk = SvPVx(*idx,rlen);
10571 cmdlen += rlen ? rlen + 1 : 0;
10574 Newx(PL_Cmd, cmdlen+1, char);
10576 if (tmps && *tmps) {
10577 strcpy(PL_Cmd,tmps);
10580 else *PL_Cmd = '\0';
10581 while (++mark <= sp) {
10583 char *s = SvPVx(*mark,n_a);
10585 if (*PL_Cmd) strcat(PL_Cmd," ");
10591 } /* end of setup_argstr() */
10594 static unsigned long int
10595 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10596 struct dsc$descriptor_s **pvmscmd)
10600 char image_name[NAM$C_MAXRSS+1];
10601 char image_argv[NAM$C_MAXRSS+1];
10602 $DESCRIPTOR(defdsc,".EXE");
10603 $DESCRIPTOR(defdsc2,".");
10604 struct dsc$descriptor_s resdsc;
10605 struct dsc$descriptor_s *vmscmd;
10606 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10607 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10608 register char *s, *rest, *cp, *wordbreak;
10611 register int isdcl;
10613 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10614 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10616 /* vmsspec is a DCL command buffer, not just a filename */
10617 vmsspec = PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10618 if (vmsspec == NULL)
10619 _ckvmssts_noperl(SS$_INSFMEM);
10621 resspec = PerlMem_malloc(VMS_MAXRSS);
10622 if (resspec == NULL)
10623 _ckvmssts_noperl(SS$_INSFMEM);
10625 /* Make a copy for modification */
10626 cmdlen = strlen(incmd);
10627 cmd = PerlMem_malloc(cmdlen+1);
10628 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10629 strncpy(cmd, incmd, cmdlen);
10634 resdsc.dsc$a_pointer = resspec;
10635 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10636 resdsc.dsc$b_class = DSC$K_CLASS_S;
10637 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10639 vmscmd->dsc$a_pointer = NULL;
10640 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10641 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10642 vmscmd->dsc$w_length = 0;
10643 if (pvmscmd) *pvmscmd = vmscmd;
10645 if (suggest_quote) *suggest_quote = 0;
10647 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10649 PerlMem_free(vmsspec);
10650 PerlMem_free(resspec);
10651 return CLI$_BUFOVF; /* continuation lines currently unsupported */
10656 while (*s && isspace(*s)) s++;
10658 if (*s == '@' || *s == '$') {
10659 vmsspec[0] = *s; rest = s + 1;
10660 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10662 else { cp = vmsspec; rest = s; }
10663 if (*rest == '.' || *rest == '/') {
10665 for (cp2 = resspec;
10666 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10667 rest++, cp2++) *cp2 = *rest;
10669 if (int_tovmsspec(resspec, cp, 0, NULL)) {
10672 /* When a UNIX spec with no file type is translated to VMS, */
10673 /* A trailing '.' is appended under ODS-5 rules. */
10674 /* Here we do not want that trailing "." as it prevents */
10675 /* Looking for a implied ".exe" type. */
10676 if (decc_efs_charset) {
10678 i = strlen(vmsspec);
10679 if (vmsspec[i-1] == '.') {
10680 vmsspec[i-1] = '\0';
10685 for (cp2 = vmsspec + strlen(vmsspec);
10686 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10687 rest++, cp2++) *cp2 = *rest;
10692 /* Intuit whether verb (first word of cmd) is a DCL command:
10693 * - if first nonspace char is '@', it's a DCL indirection
10695 * - if verb contains a filespec separator, it's not a DCL command
10696 * - if it doesn't, caller tells us whether to default to a DCL
10697 * command, or to a local image unless told it's DCL (by leading '$')
10701 if (suggest_quote) *suggest_quote = 1;
10703 register char *filespec = strpbrk(s,":<[.;");
10704 rest = wordbreak = strpbrk(s," \"\t/");
10705 if (!wordbreak) wordbreak = s + strlen(s);
10706 if (*s == '$') check_img = 0;
10707 if (filespec && (filespec < wordbreak)) isdcl = 0;
10708 else isdcl = !check_img;
10713 imgdsc.dsc$a_pointer = s;
10714 imgdsc.dsc$w_length = wordbreak - s;
10715 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10717 _ckvmssts_noperl(lib$find_file_end(&cxt));
10718 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10719 if (!(retsts & 1) && *s == '$') {
10720 _ckvmssts_noperl(lib$find_file_end(&cxt));
10721 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10722 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10724 _ckvmssts_noperl(lib$find_file_end(&cxt));
10725 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10729 _ckvmssts_noperl(lib$find_file_end(&cxt));
10734 while (*s && !isspace(*s)) s++;
10737 /* check that it's really not DCL with no file extension */
10738 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10740 char b[256] = {0,0,0,0};
10741 read(fileno(fp), b, 256);
10742 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10746 /* Check for script */
10748 if ((b[0] == '#') && (b[1] == '!'))
10750 #ifdef ALTERNATE_SHEBANG
10752 shebang_len = strlen(ALTERNATE_SHEBANG);
10753 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10755 perlstr = strstr("perl",b);
10756 if (perlstr == NULL)
10764 if (shebang_len > 0) {
10767 char tmpspec[NAM$C_MAXRSS + 1];
10770 /* Image is following after white space */
10771 /*--------------------------------------*/
10772 while (isprint(b[i]) && isspace(b[i]))
10776 while (isprint(b[i]) && !isspace(b[i])) {
10777 tmpspec[j++] = b[i++];
10778 if (j >= NAM$C_MAXRSS)
10783 /* There may be some default parameters to the image */
10784 /*---------------------------------------------------*/
10786 while (isprint(b[i])) {
10787 image_argv[j++] = b[i++];
10788 if (j >= NAM$C_MAXRSS)
10791 while ((j > 0) && !isprint(image_argv[j-1]))
10795 /* It will need to be converted to VMS format and validated */
10796 if (tmpspec[0] != '\0') {
10799 /* Try to find the exact program requested to be run */
10800 /*---------------------------------------------------*/
10801 iname = int_rmsexpand
10802 (tmpspec, image_name, ".exe",
10803 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10804 if (iname != NULL) {
10805 if (cando_by_name_int
10806 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10807 /* MCR prefix needed */
10811 /* Try again with a null type */
10812 /*----------------------------*/
10813 iname = int_rmsexpand
10814 (tmpspec, image_name, ".",
10815 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10816 if (iname != NULL) {
10817 if (cando_by_name_int
10818 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10819 /* MCR prefix needed */
10825 /* Did we find the image to run the script? */
10826 /*------------------------------------------*/
10830 /* Assume DCL or foreign command exists */
10831 /*--------------------------------------*/
10832 tchr = strrchr(tmpspec, '/');
10833 if (tchr != NULL) {
10839 strcpy(image_name, tchr);
10847 if (check_img && isdcl) {
10849 PerlMem_free(resspec);
10850 PerlMem_free(vmsspec);
10854 if (cando_by_name(S_IXUSR,0,resspec)) {
10855 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10856 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10858 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
10859 if (image_name[0] != 0) {
10860 strcat(vmscmd->dsc$a_pointer, image_name);
10861 strcat(vmscmd->dsc$a_pointer, " ");
10863 } else if (image_name[0] != 0) {
10864 strcpy(vmscmd->dsc$a_pointer, image_name);
10865 strcat(vmscmd->dsc$a_pointer, " ");
10867 strcpy(vmscmd->dsc$a_pointer,"@");
10869 if (suggest_quote) *suggest_quote = 1;
10871 /* If there is an image name, use original command */
10872 if (image_name[0] == 0)
10873 strcat(vmscmd->dsc$a_pointer,resspec);
10876 while (*rest && isspace(*rest)) rest++;
10879 if (image_argv[0] != 0) {
10880 strcat(vmscmd->dsc$a_pointer,image_argv);
10881 strcat(vmscmd->dsc$a_pointer, " ");
10887 rest_len = strlen(rest);
10888 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10889 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10890 strcat(vmscmd->dsc$a_pointer,rest);
10892 retsts = CLI$_BUFOVF;
10894 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10896 PerlMem_free(vmsspec);
10897 PerlMem_free(resspec);
10898 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10904 /* It's either a DCL command or we couldn't find a suitable image */
10905 vmscmd->dsc$w_length = strlen(cmd);
10907 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
10908 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
10909 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
10912 PerlMem_free(resspec);
10913 PerlMem_free(vmsspec);
10915 /* check if it's a symbol (for quoting purposes) */
10916 if (suggest_quote && !*suggest_quote) {
10918 char equiv[LNM$C_NAMLENGTH];
10919 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10920 eqvdsc.dsc$a_pointer = equiv;
10922 iss = lib$get_symbol(vmscmd,&eqvdsc);
10923 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10925 if (!(retsts & 1)) {
10926 /* just hand off status values likely to be due to user error */
10927 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10928 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10929 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10930 else { _ckvmssts_noperl(retsts); }
10933 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10935 } /* end of setup_cmddsc() */
10938 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10940 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10946 if (vfork_called) { /* this follows a vfork - act Unixish */
10948 if (vfork_called < 0) {
10949 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10952 else return do_aexec(really,mark,sp);
10954 /* no vfork - act VMSish */
10955 cmd = setup_argstr(aTHX_ really,mark,sp);
10956 exec_sts = vms_do_exec(cmd);
10957 Safefree(cmd); /* Clean up from setup_argstr() */
10962 } /* end of vms_do_aexec() */
10965 /* {{{bool vms_do_exec(char *cmd) */
10967 Perl_vms_do_exec(pTHX_ const char *cmd)
10969 struct dsc$descriptor_s *vmscmd;
10971 if (vfork_called) { /* this follows a vfork - act Unixish */
10973 if (vfork_called < 0) {
10974 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10977 else return do_exec(cmd);
10980 { /* no vfork - act VMSish */
10981 unsigned long int retsts;
10984 TAINT_PROPER("exec");
10985 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10986 retsts = lib$do_command(vmscmd);
10989 case RMS$_FNF: case RMS$_DNF:
10990 set_errno(ENOENT); break;
10992 set_errno(ENOTDIR); break;
10994 set_errno(ENODEV); break;
10996 set_errno(EACCES); break;
10998 set_errno(EINVAL); break;
10999 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11000 set_errno(E2BIG); break;
11001 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11002 _ckvmssts_noperl(retsts); /* fall through */
11003 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11004 set_errno(EVMSERR);
11006 set_vaxc_errno(retsts);
11007 if (ckWARN(WARN_EXEC)) {
11008 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
11009 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
11011 vms_execfree(vmscmd);
11016 } /* end of vms_do_exec() */
11019 int do_spawn2(pTHX_ const char *, int);
11022 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
11024 unsigned long int sts;
11030 /* We'll copy the (undocumented?) Win32 behavior and allow a
11031 * numeric first argument. But the only value we'll support
11032 * through do_aspawn is a value of 1, which means spawn without
11033 * waiting for completion -- other values are ignored.
11035 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
11037 flags = SvIVx(*mark);
11040 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
11041 flags = CLI$M_NOWAIT;
11045 cmd = setup_argstr(aTHX_ really, mark, sp);
11046 sts = do_spawn2(aTHX_ cmd, flags);
11047 /* pp_sys will clean up cmd */
11051 } /* end of do_aspawn() */
11055 /* {{{int do_spawn(char* cmd) */
11057 Perl_do_spawn(pTHX_ char* cmd)
11059 PERL_ARGS_ASSERT_DO_SPAWN;
11061 return do_spawn2(aTHX_ cmd, 0);
11065 /* {{{int do_spawn_nowait(char* cmd) */
11067 Perl_do_spawn_nowait(pTHX_ char* cmd)
11069 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
11071 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
11075 /* {{{int do_spawn2(char *cmd) */
11077 do_spawn2(pTHX_ const char *cmd, int flags)
11079 unsigned long int sts, substs;
11081 /* The caller of this routine expects to Safefree(PL_Cmd) */
11082 Newx(PL_Cmd,10,char);
11085 TAINT_PROPER("spawn");
11086 if (!cmd || !*cmd) {
11087 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
11090 case RMS$_FNF: case RMS$_DNF:
11091 set_errno(ENOENT); break;
11093 set_errno(ENOTDIR); break;
11095 set_errno(ENODEV); break;
11097 set_errno(EACCES); break;
11099 set_errno(EINVAL); break;
11100 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11101 set_errno(E2BIG); break;
11102 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11103 _ckvmssts_noperl(sts); /* fall through */
11104 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11105 set_errno(EVMSERR);
11107 set_vaxc_errno(sts);
11108 if (ckWARN(WARN_EXEC)) {
11109 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
11118 if (flags & CLI$M_NOWAIT)
11121 strcpy(mode, "nW");
11123 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
11126 /* sts will be the pid in the nowait case */
11129 } /* end of do_spawn2() */
11133 static unsigned int *sockflags, sockflagsize;
11136 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11137 * routines found in some versions of the CRTL can't deal with sockets.
11138 * We don't shim the other file open routines since a socket isn't
11139 * likely to be opened by a name.
11141 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11142 FILE *my_fdopen(int fd, const char *mode)
11144 FILE *fp = fdopen(fd, mode);
11147 unsigned int fdoff = fd / sizeof(unsigned int);
11148 Stat_t sbuf; /* native stat; we don't need flex_stat */
11149 if (!sockflagsize || fdoff > sockflagsize) {
11150 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
11151 else Newx (sockflags,fdoff+2,unsigned int);
11152 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11153 sockflagsize = fdoff + 2;
11155 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11156 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11165 * Clear the corresponding bit when the (possibly) socket stream is closed.
11166 * There still a small hole: we miss an implicit close which might occur
11167 * via freopen(). >> Todo
11169 /*{{{ int my_fclose(FILE *fp)*/
11170 int my_fclose(FILE *fp) {
11172 unsigned int fd = fileno(fp);
11173 unsigned int fdoff = fd / sizeof(unsigned int);
11175 if (sockflagsize && fdoff < sockflagsize)
11176 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11184 * A simple fwrite replacement which outputs itmsz*nitm chars without
11185 * introducing record boundaries every itmsz chars.
11186 * We are using fputs, which depends on a terminating null. We may
11187 * well be writing binary data, so we need to accommodate not only
11188 * data with nulls sprinkled in the middle but also data with no null
11191 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11193 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11195 register char *cp, *end, *cpd;
11197 register unsigned int fd = fileno(dest);
11198 register unsigned int fdoff = fd / sizeof(unsigned int);
11200 int bufsize = itmsz * nitm + 1;
11202 if (fdoff < sockflagsize &&
11203 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11204 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11208 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11209 memcpy( data, src, itmsz*nitm );
11210 data[itmsz*nitm] = '\0';
11212 end = data + itmsz * nitm;
11213 retval = (int) nitm; /* on success return # items written */
11216 while (cpd <= end) {
11217 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11218 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11220 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11224 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11227 } /* end of my_fwrite() */
11230 /*{{{ int my_flush(FILE *fp)*/
11232 Perl_my_flush(pTHX_ FILE *fp)
11235 if ((res = fflush(fp)) == 0 && fp) {
11236 #ifdef VMS_DO_SOCKETS
11238 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11240 res = fsync(fileno(fp));
11243 * If the flush succeeded but set end-of-file, we need to clear
11244 * the error because our caller may check ferror(). BTW, this
11245 * probably means we just flushed an empty file.
11247 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11253 /* fgetname() is not returning the correct file specifications when
11254 * decc_filename_unix_report mode is active. So we have to have it
11255 * aways return filenames in VMS mode and convert it ourselves.
11258 /*{{{ char * my_fgetname(FILE *fp, buf)*/
11260 Perl_my_fgetname(FILE *fp, char * buf) {
11264 retname = fgetname(fp, buf, 1);
11266 /* If we are in VMS mode, then we are done */
11267 if (!decc_filename_unix_report || (retname == NULL)) {
11271 /* Convert this to Unix format */
11272 vms_name = PerlMem_malloc(VMS_MAXRSS + 1);
11273 strcpy(vms_name, retname);
11274 retname = int_tounixspec(vms_name, buf, NULL);
11275 PerlMem_free(vms_name);
11282 * Here are replacements for the following Unix routines in the VMS environment:
11283 * getpwuid Get information for a particular UIC or UID
11284 * getpwnam Get information for a named user
11285 * getpwent Get information for each user in the rights database
11286 * setpwent Reset search to the start of the rights database
11287 * endpwent Finish searching for users in the rights database
11289 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11290 * (defined in pwd.h), which contains the following fields:-
11292 * char *pw_name; Username (in lower case)
11293 * char *pw_passwd; Hashed password
11294 * unsigned int pw_uid; UIC
11295 * unsigned int pw_gid; UIC group number
11296 * char *pw_unixdir; Default device/directory (VMS-style)
11297 * char *pw_gecos; Owner name
11298 * char *pw_dir; Default device/directory (Unix-style)
11299 * char *pw_shell; Default CLI name (eg. DCL)
11301 * If the specified user does not exist, getpwuid and getpwnam return NULL.
11303 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11304 * not the UIC member number (eg. what's returned by getuid()),
11305 * getpwuid() can accept either as input (if uid is specified, the caller's
11306 * UIC group is used), though it won't recognise gid=0.
11308 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11309 * information about other users in your group or in other groups, respectively.
11310 * If the required privilege is not available, then these routines fill only
11311 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11314 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11317 /* sizes of various UAF record fields */
11318 #define UAI$S_USERNAME 12
11319 #define UAI$S_IDENT 31
11320 #define UAI$S_OWNER 31
11321 #define UAI$S_DEFDEV 31
11322 #define UAI$S_DEFDIR 63
11323 #define UAI$S_DEFCLI 31
11324 #define UAI$S_PWD 8
11326 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
11327 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11328 (uic).uic$v_group != UIC$K_WILD_GROUP)
11330 static char __empty[]= "";
11331 static struct passwd __passwd_empty=
11332 {(char *) __empty, (char *) __empty, 0, 0,
11333 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11334 static int contxt= 0;
11335 static struct passwd __pwdcache;
11336 static char __pw_namecache[UAI$S_IDENT+1];
11339 * This routine does most of the work extracting the user information.
11341 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11344 unsigned char length;
11345 char pw_gecos[UAI$S_OWNER+1];
11347 static union uicdef uic;
11349 unsigned char length;
11350 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11353 unsigned char length;
11354 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11357 unsigned char length;
11358 char pw_shell[UAI$S_DEFCLI+1];
11360 static char pw_passwd[UAI$S_PWD+1];
11362 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11363 struct dsc$descriptor_s name_desc;
11364 unsigned long int sts;
11366 static struct itmlst_3 itmlst[]= {
11367 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
11368 {sizeof(uic), UAI$_UIC, &uic, &luic},
11369 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
11370 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
11371 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
11372 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
11373 {0, 0, NULL, NULL}};
11375 name_desc.dsc$w_length= strlen(name);
11376 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11377 name_desc.dsc$b_class= DSC$K_CLASS_S;
11378 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11380 /* Note that sys$getuai returns many fields as counted strings. */
11381 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11382 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11383 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11385 else { _ckvmssts(sts); }
11386 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
11388 if ((int) owner.length < lowner) lowner= (int) owner.length;
11389 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11390 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11391 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11392 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11393 owner.pw_gecos[lowner]= '\0';
11394 defdev.pw_dir[ldefdev+ldefdir]= '\0';
11395 defcli.pw_shell[ldefcli]= '\0';
11396 if (valid_uic(uic)) {
11397 pwd->pw_uid= uic.uic$l_uic;
11398 pwd->pw_gid= uic.uic$v_group;
11401 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11402 pwd->pw_passwd= pw_passwd;
11403 pwd->pw_gecos= owner.pw_gecos;
11404 pwd->pw_dir= defdev.pw_dir;
11405 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11406 pwd->pw_shell= defcli.pw_shell;
11407 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11409 ldir= strlen(pwd->pw_unixdir) - 1;
11410 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11413 strcpy(pwd->pw_unixdir, pwd->pw_dir);
11414 if (!decc_efs_case_preserve)
11415 __mystrtolower(pwd->pw_unixdir);
11420 * Get information for a named user.
11422 /*{{{struct passwd *getpwnam(char *name)*/
11423 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
11425 struct dsc$descriptor_s name_desc;
11427 unsigned long int sts;
11429 __pwdcache = __passwd_empty;
11430 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11431 /* We still may be able to determine pw_uid and pw_gid */
11432 name_desc.dsc$w_length= strlen(name);
11433 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11434 name_desc.dsc$b_class= DSC$K_CLASS_S;
11435 name_desc.dsc$a_pointer= (char *) name;
11436 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11437 __pwdcache.pw_uid= uic.uic$l_uic;
11438 __pwdcache.pw_gid= uic.uic$v_group;
11441 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11442 set_vaxc_errno(sts);
11443 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11446 else { _ckvmssts(sts); }
11449 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
11450 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
11451 __pwdcache.pw_name= __pw_namecache;
11452 return &__pwdcache;
11453 } /* end of my_getpwnam() */
11457 * Get information for a particular UIC or UID.
11458 * Called by my_getpwent with uid=-1 to list all users.
11460 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11461 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
11463 const $DESCRIPTOR(name_desc,__pw_namecache);
11464 unsigned short lname;
11466 unsigned long int status;
11468 if (uid == (unsigned int) -1) {
11470 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11471 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11472 set_vaxc_errno(status);
11473 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11477 else { _ckvmssts(status); }
11478 } while (!valid_uic (uic));
11481 uic.uic$l_uic= uid;
11482 if (!uic.uic$v_group)
11483 uic.uic$v_group= PerlProc_getgid();
11484 if (valid_uic(uic))
11485 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11486 else status = SS$_IVIDENT;
11487 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11488 status == RMS$_PRV) {
11489 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11492 else { _ckvmssts(status); }
11494 __pw_namecache[lname]= '\0';
11495 __mystrtolower(__pw_namecache);
11497 __pwdcache = __passwd_empty;
11498 __pwdcache.pw_name = __pw_namecache;
11500 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11501 The identifier's value is usually the UIC, but it doesn't have to be,
11502 so if we can, we let fillpasswd update this. */
11503 __pwdcache.pw_uid = uic.uic$l_uic;
11504 __pwdcache.pw_gid = uic.uic$v_group;
11506 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11507 return &__pwdcache;
11509 } /* end of my_getpwuid() */
11513 * Get information for next user.
11515 /*{{{struct passwd *my_getpwent()*/
11516 struct passwd *Perl_my_getpwent(pTHX)
11518 return (my_getpwuid((unsigned int) -1));
11523 * Finish searching rights database for users.
11525 /*{{{void my_endpwent()*/
11526 void Perl_my_endpwent(pTHX)
11529 _ckvmssts(sys$finish_rdb(&contxt));
11535 #ifdef HOMEGROWN_POSIX_SIGNALS
11536 /* Signal handling routines, pulled into the core from POSIX.xs.
11538 * We need these for threads, so they've been rolled into the core,
11539 * rather than left in POSIX.xs.
11541 * (DRS, Oct 23, 1997)
11544 /* sigset_t is atomic under VMS, so these routines are easy */
11545 /*{{{int my_sigemptyset(sigset_t *) */
11546 int my_sigemptyset(sigset_t *set) {
11547 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11548 *set = 0; return 0;
11553 /*{{{int my_sigfillset(sigset_t *)*/
11554 int my_sigfillset(sigset_t *set) {
11556 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11557 for (i = 0; i < NSIG; i++) *set |= (1 << i);
11563 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
11564 int my_sigaddset(sigset_t *set, int sig) {
11565 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11566 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11567 *set |= (1 << (sig - 1));
11573 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
11574 int my_sigdelset(sigset_t *set, int sig) {
11575 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11576 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11577 *set &= ~(1 << (sig - 1));
11583 /*{{{int my_sigismember(sigset_t *set, int sig)*/
11584 int my_sigismember(sigset_t *set, int sig) {
11585 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11586 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11587 return *set & (1 << (sig - 1));
11592 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
11593 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
11596 /* If set and oset are both null, then things are badly wrong. Bail out. */
11597 if ((oset == NULL) && (set == NULL)) {
11598 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
11602 /* If set's null, then we're just handling a fetch. */
11604 tempmask = sigblock(0);
11609 tempmask = sigsetmask(*set);
11612 tempmask = sigblock(*set);
11615 tempmask = sigblock(0);
11616 sigsetmask(*oset & ~tempmask);
11619 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11624 /* Did they pass us an oset? If so, stick our holding mask into it */
11631 #endif /* HOMEGROWN_POSIX_SIGNALS */
11634 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11635 * my_utime(), and flex_stat(), all of which operate on UTC unless
11636 * VMSISH_TIMES is true.
11638 /* method used to handle UTC conversions:
11639 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
11641 static int gmtime_emulation_type;
11642 /* number of secs to add to UTC POSIX-style time to get local time */
11643 static long int utc_offset_secs;
11645 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11646 * in vmsish.h. #undef them here so we can call the CRTL routines
11655 * DEC C previous to 6.0 corrupts the behavior of the /prefix
11656 * qualifier with the extern prefix pragma. This provisional
11657 * hack circumvents this prefix pragma problem in previous
11660 #if defined(__VMS_VER) && __VMS_VER >= 70000000
11661 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
11662 # pragma __extern_prefix save
11663 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
11664 # define gmtime decc$__utctz_gmtime
11665 # define localtime decc$__utctz_localtime
11666 # define time decc$__utc_time
11667 # pragma __extern_prefix restore
11669 struct tm *gmtime(), *localtime();
11675 static time_t toutc_dst(time_t loc) {
11678 if ((rsltmp = localtime(&loc)) == NULL) return -1;
11679 loc -= utc_offset_secs;
11680 if (rsltmp->tm_isdst) loc -= 3600;
11683 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11684 ((gmtime_emulation_type || my_time(NULL)), \
11685 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11686 ((secs) - utc_offset_secs))))
11688 static time_t toloc_dst(time_t utc) {
11691 utc += utc_offset_secs;
11692 if ((rsltmp = localtime(&utc)) == NULL) return -1;
11693 if (rsltmp->tm_isdst) utc += 3600;
11696 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11697 ((gmtime_emulation_type || my_time(NULL)), \
11698 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11699 ((secs) + utc_offset_secs))))
11701 #ifndef RTL_USES_UTC
11704 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
11705 DST starts on 1st sun of april at 02:00 std time
11706 ends on last sun of october at 02:00 dst time
11707 see the UCX management command reference, SET CONFIG TIMEZONE
11708 for formatting info.
11710 No, it's not as general as it should be, but then again, NOTHING
11711 will handle UK times in a sensible way.
11716 parse the DST start/end info:
11717 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
11721 tz_parse_startend(char *s, struct tm *w, int *past)
11723 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
11724 int ly, dozjd, d, m, n, hour, min, sec, j, k;
11729 if (!past) return 0;
11732 if (w->tm_year % 4 == 0) ly = 1;
11733 if (w->tm_year % 100 == 0) ly = 0;
11734 if (w->tm_year+1900 % 400 == 0) ly = 1;
11737 dozjd = isdigit(*s);
11738 if (*s == 'J' || *s == 'j' || dozjd) {
11739 if (!dozjd && !isdigit(*++s)) return 0;
11742 d = d*10 + *s++ - '0';
11744 d = d*10 + *s++ - '0';
11747 if (d == 0) return 0;
11748 if (d > 366) return 0;
11750 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
11753 } else if (*s == 'M' || *s == 'm') {
11754 if (!isdigit(*++s)) return 0;
11756 if (isdigit(*s)) m = 10*m + *s++ - '0';
11757 if (*s != '.') return 0;
11758 if (!isdigit(*++s)) return 0;
11760 if (n < 1 || n > 5) return 0;
11761 if (*s != '.') return 0;
11762 if (!isdigit(*++s)) return 0;
11764 if (d > 6) return 0;
11768 if (!isdigit(*++s)) return 0;
11770 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
11772 if (!isdigit(*++s)) return 0;
11774 if (isdigit(*s)) min = 10*min + *s++ - '0';
11776 if (!isdigit(*++s)) return 0;
11778 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
11788 if (w->tm_yday < d) goto before;
11789 if (w->tm_yday > d) goto after;
11791 if (w->tm_mon+1 < m) goto before;
11792 if (w->tm_mon+1 > m) goto after;
11794 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
11795 k = d - j; /* mday of first d */
11796 if (k <= 0) k += 7;
11797 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
11798 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
11799 if (w->tm_mday < k) goto before;
11800 if (w->tm_mday > k) goto after;
11803 if (w->tm_hour < hour) goto before;
11804 if (w->tm_hour > hour) goto after;
11805 if (w->tm_min < min) goto before;
11806 if (w->tm_min > min) goto after;
11807 if (w->tm_sec < sec) goto before;
11821 /* parse the offset: (+|-)hh[:mm[:ss]] */
11824 tz_parse_offset(char *s, int *offset)
11826 int hour = 0, min = 0, sec = 0;
11829 if (!offset) return 0;
11831 if (*s == '-') {neg++; s++;}
11832 if (*s == '+') s++;
11833 if (!isdigit(*s)) return 0;
11835 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
11836 if (hour > 24) return 0;
11838 if (!isdigit(*++s)) return 0;
11840 if (isdigit(*s)) min = min*10 + (*s++ - '0');
11841 if (min > 59) return 0;
11843 if (!isdigit(*++s)) return 0;
11845 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
11846 if (sec > 59) return 0;
11850 *offset = (hour*60+min)*60 + sec;
11851 if (neg) *offset = -*offset;
11856 input time is w, whatever type of time the CRTL localtime() uses.
11857 sets dst, the zone, and the gmtoff (seconds)
11859 caches the value of TZ and UCX$TZ env variables; note that
11860 my_setenv looks for these and sets a flag if they're changed
11863 We have to watch out for the "australian" case (dst starts in
11864 october, ends in april)...flagged by "reverse" and checked by
11865 scanning through the months of the previous year.
11870 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
11875 char *dstzone, *tz, *s_start, *s_end;
11876 int std_off, dst_off, isdst;
11877 int y, dststart, dstend;
11878 static char envtz[1025]; /* longer than any logical, symbol, ... */
11879 static char ucxtz[1025];
11880 static char reversed = 0;
11886 reversed = -1; /* flag need to check */
11887 envtz[0] = ucxtz[0] = '\0';
11888 tz = my_getenv("TZ",0);
11889 if (tz) strcpy(envtz, tz);
11890 tz = my_getenv("UCX$TZ",0);
11891 if (tz) strcpy(ucxtz, tz);
11892 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
11895 if (!*tz) tz = ucxtz;
11898 while (isalpha(*s)) s++;
11899 s = tz_parse_offset(s, &std_off);
11901 if (!*s) { /* no DST, hurray we're done! */
11907 while (isalpha(*s)) s++;
11908 s2 = tz_parse_offset(s, &dst_off);
11912 dst_off = std_off - 3600;
11915 if (!*s) { /* default dst start/end?? */
11916 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
11917 s = strchr(ucxtz,',');
11919 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
11921 if (*s != ',') return 0;
11924 when = _toutc(when); /* convert to utc */
11925 when = when - std_off; /* convert to pseudolocal time*/
11927 w2 = localtime(&when);
11930 s = tz_parse_startend(s_start,w2,&dststart);
11932 if (*s != ',') return 0;
11935 when = _toutc(when); /* convert to utc */
11936 when = when - dst_off; /* convert to pseudolocal time*/
11937 w2 = localtime(&when);
11938 if (w2->tm_year != y) { /* spans a year, just check one time */
11939 when += dst_off - std_off;
11940 w2 = localtime(&when);
11943 s = tz_parse_startend(s_end,w2,&dstend);
11946 if (reversed == -1) { /* need to check if start later than end */
11950 if (when < 2*365*86400) {
11951 when += 2*365*86400;
11955 w2 =localtime(&when);
11956 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
11958 for (j = 0; j < 12; j++) {
11959 w2 =localtime(&when);
11960 tz_parse_startend(s_start,w2,&ds);
11961 tz_parse_startend(s_end,w2,&de);
11962 if (ds != de) break;
11966 if (de && !ds) reversed = 1;
11969 isdst = dststart && !dstend;
11970 if (reversed) isdst = dststart || !dstend;
11973 if (dst) *dst = isdst;
11974 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
11975 if (isdst) tz = dstzone;
11977 while(isalpha(*tz)) *zone++ = *tz++;
11983 #endif /* !RTL_USES_UTC */
11985 /* my_time(), my_localtime(), my_gmtime()
11986 * By default traffic in UTC time values, using CRTL gmtime() or
11987 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11988 * Note: We need to use these functions even when the CRTL has working
11989 * UTC support, since they also handle C<use vmsish qw(times);>
11991 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
11992 * Modified by Charles Bailey <bailey@newman.upenn.edu>
11995 /*{{{time_t my_time(time_t *timep)*/
11996 time_t Perl_my_time(pTHX_ time_t *timep)
12001 if (gmtime_emulation_type == 0) {
12002 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
12003 /* results of calls to gmtime() and localtime() */
12004 /* for same &base */
12006 gmtime_emulation_type++;
12007 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
12008 char off[LNM$C_NAMLENGTH+1];;
12010 gmtime_emulation_type++;
12011 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
12012 gmtime_emulation_type++;
12013 utc_offset_secs = 0;
12014 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
12016 else { utc_offset_secs = atol(off); }
12018 else { /* We've got a working gmtime() */
12019 struct tm gmt, local;
12022 tm_p = localtime(&base);
12024 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
12025 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
12026 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
12027 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
12032 # ifdef VMSISH_TIME
12033 # ifdef RTL_USES_UTC
12034 if (VMSISH_TIME) when = _toloc(when);
12036 if (!VMSISH_TIME) when = _toutc(when);
12039 if (timep != NULL) *timep = when;
12042 } /* end of my_time() */
12046 /*{{{struct tm *my_gmtime(const time_t *timep)*/
12048 Perl_my_gmtime(pTHX_ const time_t *timep)
12053 if (timep == NULL) {
12054 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12057 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
12060 # ifdef VMSISH_TIME
12061 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
12063 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
12064 return gmtime(&when);
12066 /* CRTL localtime() wants local time as input, so does no tz correction */
12067 rsltmp = localtime(&when);
12068 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
12071 } /* end of my_gmtime() */
12075 /*{{{struct tm *my_localtime(const time_t *timep)*/
12077 Perl_my_localtime(pTHX_ const time_t *timep)
12079 time_t when, whenutc;
12083 if (timep == NULL) {
12084 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12087 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
12088 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
12091 # ifdef RTL_USES_UTC
12092 # ifdef VMSISH_TIME
12093 if (VMSISH_TIME) when = _toutc(when);
12095 /* CRTL localtime() wants UTC as input, does tz correction itself */
12096 return localtime(&when);
12098 # else /* !RTL_USES_UTC */
12100 # ifdef VMSISH_TIME
12101 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
12102 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
12105 #ifndef RTL_USES_UTC
12106 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
12107 when = whenutc - offset; /* pseudolocal time*/
12110 /* CRTL localtime() wants local time as input, so does no tz correction */
12111 rsltmp = localtime(&when);
12112 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
12116 } /* end of my_localtime() */
12119 /* Reset definitions for later calls */
12120 #define gmtime(t) my_gmtime(t)
12121 #define localtime(t) my_localtime(t)
12122 #define time(t) my_time(t)
12125 /* my_utime - update modification/access time of a file
12127 * VMS 7.3 and later implementation
12128 * Only the UTC translation is home-grown. The rest is handled by the
12129 * CRTL utime(), which will take into account the relevant feature
12130 * logicals and ODS-5 volume characteristics for true access times.
12132 * pre VMS 7.3 implementation:
12133 * The calling sequence is identical to POSIX utime(), but under
12134 * VMS with ODS-2, only the modification time is changed; ODS-2 does
12135 * not maintain access times. Restrictions differ from the POSIX
12136 * definition in that the time can be changed as long as the
12137 * caller has permission to execute the necessary IO$_MODIFY $QIO;
12138 * no separate checks are made to insure that the caller is the
12139 * owner of the file or has special privs enabled.
12140 * Code here is based on Joe Meadows' FILE utility.
12144 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
12145 * to VMS epoch (01-JAN-1858 00:00:00.00)
12146 * in 100 ns intervals.
12148 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
12150 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
12151 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
12153 #if __CRTL_VER >= 70300000
12154 struct utimbuf utc_utimes, *utc_utimesp;
12156 if (utimes != NULL) {
12157 utc_utimes.actime = utimes->actime;
12158 utc_utimes.modtime = utimes->modtime;
12159 # ifdef VMSISH_TIME
12160 /* If input was local; convert to UTC for sys svc */
12162 utc_utimes.actime = _toutc(utimes->actime);
12163 utc_utimes.modtime = _toutc(utimes->modtime);
12166 utc_utimesp = &utc_utimes;
12169 utc_utimesp = NULL;
12172 return utime(file, utc_utimesp);
12174 #else /* __CRTL_VER < 70300000 */
12178 long int bintime[2], len = 2, lowbit, unixtime,
12179 secscale = 10000000; /* seconds --> 100 ns intervals */
12180 unsigned long int chan, iosb[2], retsts;
12181 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
12182 struct FAB myfab = cc$rms_fab;
12183 struct NAM mynam = cc$rms_nam;
12184 #if defined (__DECC) && defined (__VAX)
12185 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
12186 * at least through VMS V6.1, which causes a type-conversion warning.
12188 # pragma message save
12189 # pragma message disable cvtdiftypes
12191 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
12192 struct fibdef myfib;
12193 #if defined (__DECC) && defined (__VAX)
12194 /* This should be right after the declaration of myatr, but due
12195 * to a bug in VAX DEC C, this takes effect a statement early.
12197 # pragma message restore
12199 /* cast ok for read only parameter */
12200 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
12201 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
12202 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
12204 if (file == NULL || *file == '\0') {
12205 SETERRNO(ENOENT, LIB$_INVARG);
12209 /* Convert to VMS format ensuring that it will fit in 255 characters */
12210 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
12211 SETERRNO(ENOENT, LIB$_INVARG);
12214 if (utimes != NULL) {
12215 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
12216 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
12217 * Since time_t is unsigned long int, and lib$emul takes a signed long int
12218 * as input, we force the sign bit to be clear by shifting unixtime right
12219 * one bit, then multiplying by an extra factor of 2 in lib$emul().
12221 lowbit = (utimes->modtime & 1) ? secscale : 0;
12222 unixtime = (long int) utimes->modtime;
12223 # ifdef VMSISH_TIME
12224 /* If input was UTC; convert to local for sys svc */
12225 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
12227 unixtime >>= 1; secscale <<= 1;
12228 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
12229 if (!(retsts & 1)) {
12230 SETERRNO(EVMSERR, retsts);
12233 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
12234 if (!(retsts & 1)) {
12235 SETERRNO(EVMSERR, retsts);
12240 /* Just get the current time in VMS format directly */
12241 retsts = sys$gettim(bintime);
12242 if (!(retsts & 1)) {
12243 SETERRNO(EVMSERR, retsts);
12248 myfab.fab$l_fna = vmsspec;
12249 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
12250 myfab.fab$l_nam = &mynam;
12251 mynam.nam$l_esa = esa;
12252 mynam.nam$b_ess = (unsigned char) sizeof esa;
12253 mynam.nam$l_rsa = rsa;
12254 mynam.nam$b_rss = (unsigned char) sizeof rsa;
12255 if (decc_efs_case_preserve)
12256 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
12258 /* Look for the file to be affected, letting RMS parse the file
12259 * specification for us as well. I have set errno using only
12260 * values documented in the utime() man page for VMS POSIX.
12262 retsts = sys$parse(&myfab,0,0);
12263 if (!(retsts & 1)) {
12264 set_vaxc_errno(retsts);
12265 if (retsts == RMS$_PRV) set_errno(EACCES);
12266 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
12267 else set_errno(EVMSERR);
12270 retsts = sys$search(&myfab,0,0);
12271 if (!(retsts & 1)) {
12272 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
12273 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
12274 set_vaxc_errno(retsts);
12275 if (retsts == RMS$_PRV) set_errno(EACCES);
12276 else if (retsts == RMS$_FNF) set_errno(ENOENT);
12277 else set_errno(EVMSERR);
12281 devdsc.dsc$w_length = mynam.nam$b_dev;
12282 /* cast ok for read only parameter */
12283 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
12285 retsts = sys$assign(&devdsc,&chan,0,0);
12286 if (!(retsts & 1)) {
12287 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
12288 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
12289 set_vaxc_errno(retsts);
12290 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
12291 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
12292 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
12293 else set_errno(EVMSERR);
12297 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
12298 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
12300 memset((void *) &myfib, 0, sizeof myfib);
12301 #if defined(__DECC) || defined(__DECCXX)
12302 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
12303 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
12304 /* This prevents the revision time of the file being reset to the current
12305 * time as a result of our IO$_MODIFY $QIO. */
12306 myfib.fib$l_acctl = FIB$M_NORECORD;
12308 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
12309 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
12310 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
12312 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
12313 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
12314 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
12315 _ckvmssts(sys$dassgn(chan));
12316 if (retsts & 1) retsts = iosb[0];
12317 if (!(retsts & 1)) {
12318 set_vaxc_errno(retsts);
12319 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12320 else set_errno(EVMSERR);
12326 #endif /* #if __CRTL_VER >= 70300000 */
12328 } /* end of my_utime() */
12332 * flex_stat, flex_lstat, flex_fstat
12333 * basic stat, but gets it right when asked to stat
12334 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
12337 #ifndef _USE_STD_STAT
12338 /* encode_dev packs a VMS device name string into an integer to allow
12339 * simple comparisons. This can be used, for example, to check whether two
12340 * files are located on the same device, by comparing their encoded device
12341 * names. Even a string comparison would not do, because stat() reuses the
12342 * device name buffer for each call; so without encode_dev, it would be
12343 * necessary to save the buffer and use strcmp (this would mean a number of
12344 * changes to the standard Perl code, to say nothing of what a Perl script
12345 * would have to do.
12347 * The device lock id, if it exists, should be unique (unless perhaps compared
12348 * with lock ids transferred from other nodes). We have a lock id if the disk is
12349 * mounted cluster-wide, which is when we tend to get long (host-qualified)
12350 * device names. Thus we use the lock id in preference, and only if that isn't
12351 * available, do we try to pack the device name into an integer (flagged by
12352 * the sign bit (LOCKID_MASK) being set).
12354 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
12355 * name and its encoded form, but it seems very unlikely that we will find
12356 * two files on different disks that share the same encoded device names,
12357 * and even more remote that they will share the same file id (if the test
12358 * is to check for the same file).
12360 * A better method might be to use sys$device_scan on the first call, and to
12361 * search for the device, returning an index into the cached array.
12362 * The number returned would be more intelligible.
12363 * This is probably not worth it, and anyway would take quite a bit longer
12364 * on the first call.
12366 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
12367 static mydev_t encode_dev (pTHX_ const char *dev)
12370 unsigned long int f;
12375 if (!dev || !dev[0]) return 0;
12379 struct dsc$descriptor_s dev_desc;
12380 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
12382 /* For cluster-mounted disks, the disk lock identifier is unique, so we
12383 can try that first. */
12384 dev_desc.dsc$w_length = strlen (dev);
12385 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
12386 dev_desc.dsc$b_class = DSC$K_CLASS_S;
12387 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
12388 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
12389 if (!$VMS_STATUS_SUCCESS(status)) {
12391 case SS$_NOSUCHDEV:
12392 SETERRNO(ENODEV, status);
12398 if (lockid) return (lockid & ~LOCKID_MASK);
12402 /* Otherwise we try to encode the device name */
12406 for (q = dev + strlen(dev); q--; q >= dev) {
12411 else if (isalpha (toupper (*q)))
12412 c= toupper (*q) - 'A' + (char)10;
12414 continue; /* Skip '$'s */
12416 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
12418 enc += f * (unsigned long int) c;
12420 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
12422 } /* end of encode_dev() */
12423 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12424 device_no = encode_dev(aTHX_ devname)
12426 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12427 device_no = new_dev_no
12431 is_null_device(const char *name)
12433 if (decc_bug_devnull != 0) {
12434 if (strncmp("/dev/null", name, 9) == 0)
12437 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
12438 The underscore prefix, controller letter, and unit number are
12439 independently optional; for our purposes, the colon punctuation
12440 is not. The colon can be trailed by optional directory and/or
12441 filename, but two consecutive colons indicates a nodename rather
12442 than a device. [pr] */
12443 if (*name == '_') ++name;
12444 if (tolower(*name++) != 'n') return 0;
12445 if (tolower(*name++) != 'l') return 0;
12446 if (tolower(*name) == 'a') ++name;
12447 if (*name == '0') ++name;
12448 return (*name++ == ':') && (*name != ':');
12452 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
12454 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
12457 Perl_cando_by_name_int
12458 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
12460 char usrname[L_cuserid];
12461 struct dsc$descriptor_s usrdsc =
12462 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
12463 char *vmsname = NULL, *fileified = NULL;
12464 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
12465 unsigned short int retlen, trnlnm_iter_count;
12466 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12467 union prvdef curprv;
12468 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
12469 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
12470 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
12471 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
12472 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
12474 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
12476 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12478 static int profile_context = -1;
12480 if (!fname || !*fname) return FALSE;
12482 /* Make sure we expand logical names, since sys$check_access doesn't */
12483 fileified = PerlMem_malloc(VMS_MAXRSS);
12484 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12485 if (!strpbrk(fname,"/]>:")) {
12486 strcpy(fileified,fname);
12487 trnlnm_iter_count = 0;
12488 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
12489 trnlnm_iter_count++;
12490 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
12495 vmsname = PerlMem_malloc(VMS_MAXRSS);
12496 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12497 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
12498 /* Don't know if already in VMS format, so make sure */
12499 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
12500 PerlMem_free(fileified);
12501 PerlMem_free(vmsname);
12506 strcpy(vmsname,fname);
12509 /* sys$check_access needs a file spec, not a directory spec.
12510 * flex_stat now will handle a null thread context during startup.
12513 retlen = namdsc.dsc$w_length = strlen(vmsname);
12514 if (vmsname[retlen-1] == ']'
12515 || vmsname[retlen-1] == '>'
12516 || vmsname[retlen-1] == ':'
12517 || (!flex_stat_int(vmsname, &st, 1) &&
12518 S_ISDIR(st.st_mode))) {
12520 if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
12521 PerlMem_free(fileified);
12522 PerlMem_free(vmsname);
12531 retlen = namdsc.dsc$w_length = strlen(fname);
12532 namdsc.dsc$a_pointer = (char *)fname;
12535 case S_IXUSR: case S_IXGRP: case S_IXOTH:
12536 access = ARM$M_EXECUTE;
12537 flags = CHP$M_READ;
12539 case S_IRUSR: case S_IRGRP: case S_IROTH:
12540 access = ARM$M_READ;
12541 flags = CHP$M_READ | CHP$M_USEREADALL;
12543 case S_IWUSR: case S_IWGRP: case S_IWOTH:
12544 access = ARM$M_WRITE;
12545 flags = CHP$M_READ | CHP$M_WRITE;
12547 case S_IDUSR: case S_IDGRP: case S_IDOTH:
12548 access = ARM$M_DELETE;
12549 flags = CHP$M_READ | CHP$M_WRITE;
12552 if (fileified != NULL)
12553 PerlMem_free(fileified);
12554 if (vmsname != NULL)
12555 PerlMem_free(vmsname);
12559 /* Before we call $check_access, create a user profile with the current
12560 * process privs since otherwise it just uses the default privs from the
12561 * UAF and might give false positives or negatives. This only works on
12562 * VMS versions v6.0 and later since that's when sys$create_user_profile
12563 * became available.
12566 /* get current process privs and username */
12567 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
12568 _ckvmssts_noperl(iosb[0]);
12570 #if defined(__VMS_VER) && __VMS_VER >= 60000000
12572 /* find out the space required for the profile */
12573 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
12574 &usrprodsc.dsc$w_length,&profile_context));
12576 /* allocate space for the profile and get it filled in */
12577 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
12578 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12579 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
12580 &usrprodsc.dsc$w_length,&profile_context));
12582 /* use the profile to check access to the file; free profile & analyze results */
12583 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
12584 PerlMem_free(usrprodsc.dsc$a_pointer);
12585 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
12589 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
12593 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
12594 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
12595 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
12596 set_vaxc_errno(retsts);
12597 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12598 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12599 else set_errno(ENOENT);
12600 if (fileified != NULL)
12601 PerlMem_free(fileified);
12602 if (vmsname != NULL)
12603 PerlMem_free(vmsname);
12606 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12607 if (fileified != NULL)
12608 PerlMem_free(fileified);
12609 if (vmsname != NULL)
12610 PerlMem_free(vmsname);
12613 _ckvmssts_noperl(retsts);
12615 if (fileified != NULL)
12616 PerlMem_free(fileified);
12617 if (vmsname != NULL)
12618 PerlMem_free(vmsname);
12619 return FALSE; /* Should never get here */
12623 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
12624 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12625 * subset of the applicable information.
12628 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12630 return cando_by_name_int
12631 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12632 } /* end of cando() */
12636 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12638 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12640 return cando_by_name_int(bit, effective, fname, 0);
12642 } /* end of cando_by_name() */
12646 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12648 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12650 if (!fstat(fd, &statbufp->crtl_stat)) {
12652 char *vms_filename;
12653 vms_filename = PerlMem_malloc(VMS_MAXRSS);
12654 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12656 /* Save name for cando by name in VMS format */
12657 cptr = getname(fd, vms_filename, 1);
12659 /* This should not happen, but just in case */
12660 if (cptr == NULL) {
12661 statbufp->st_devnam[0] = 0;
12664 /* Make sure that the saved name fits in 255 characters */
12665 cptr = int_rmsexpand_vms
12667 statbufp->st_devnam,
12670 statbufp->st_devnam[0] = 0;
12672 PerlMem_free(vms_filename);
12674 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12676 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12678 # ifdef RTL_USES_UTC
12679 # ifdef VMSISH_TIME
12681 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12682 statbufp->st_atime = _toloc(statbufp->st_atime);
12683 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12687 # ifdef VMSISH_TIME
12688 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12692 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12693 statbufp->st_atime = _toutc(statbufp->st_atime);
12694 statbufp->st_ctime = _toutc(statbufp->st_ctime);
12701 } /* end of flex_fstat() */
12705 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12709 const char *save_spec;
12720 if (decc_bug_devnull != 0) {
12721 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12722 memset(statbufp,0,sizeof *statbufp);
12723 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12724 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12725 statbufp->st_uid = 0x00010001;
12726 statbufp->st_gid = 0x0001;
12727 time((time_t *)&statbufp->st_mtime);
12728 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12733 /* Try for a directory name first. If fspec contains a filename without
12734 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12735 * and sea:[wine.dark]water. exist, we prefer the directory here.
12736 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12737 * not sea:[wine.dark]., if the latter exists. If the intended target is
12738 * the file with null type, specify this by calling flex_stat() with
12739 * a '.' at the end of fspec.
12741 * If we are in Posix filespec mode, accept the filename as is.
12745 fileified = PerlMem_malloc(VMS_MAXRSS);
12746 if (fileified == NULL)
12747 _ckvmssts_noperl(SS$_INSFMEM);
12749 temp_fspec = PerlMem_malloc(VMS_MAXRSS);
12750 if (temp_fspec == NULL)
12751 _ckvmssts_noperl(SS$_INSFMEM);
12753 strcpy(temp_fspec, fspec);
12757 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12758 if (decc_posix_compliant_pathnames == 0) {
12761 /* We may be able to optimize this, but in order for fileify_dirspec to
12762 * always return a usuable answer, we have to call vmspath first to
12763 * make sure that it is in VMS directory format, as stat/lstat on 8.3
12764 * can not handle directories in unix format that it does not have read
12765 * access to. Vmspath handles the case where a bare name which could be
12766 * a logical name gets passed.
12768 ret_spec = int_tovmspath(fspec, temp_fspec, NULL);
12769 if (ret_spec != NULL) {
12770 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
12771 if (ret_spec != NULL) {
12772 if (lstat_flag == 0)
12773 retval = stat(fileified, &statbufp->crtl_stat);
12775 retval = lstat(fileified, &statbufp->crtl_stat);
12776 save_spec = fileified;
12780 if (retval && vms_bug_stat_filename) {
12782 /* We should try again as a vmsified file specification */
12783 /* However Perl traditionally has not done this, which */
12784 /* causes problems with existing tests */
12786 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12787 if (ret_spec != NULL) {
12788 if (lstat_flag == 0)
12789 retval = stat(temp_fspec, &statbufp->crtl_stat);
12791 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12792 save_spec = temp_fspec;
12797 /* Last chance - allow multiple dots with out EFS CHARSET */
12798 /* The CRTL stat() falls down hard on multi-dot filenames in unix
12799 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12800 * enable it if it isn't already.
12802 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12803 if (!decc_efs_charset && (decc_efs_charset_index > 0))
12804 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12806 if (lstat_flag == 0)
12807 retval = stat(fspec, &statbufp->crtl_stat);
12809 retval = lstat(fspec, &statbufp->crtl_stat);
12811 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12812 if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12813 decc$feature_set_value(decc_efs_charset_index, 1, 0);
12819 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12821 if (lstat_flag == 0)
12822 retval = stat(temp_fspec, &statbufp->crtl_stat);
12824 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12825 save_spec = temp_fspec;
12829 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12830 /* As you were... */
12831 if (!decc_efs_charset)
12832 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12837 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12839 /* If this is an lstat, do not follow the link */
12841 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12843 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12844 /* If we used the efs_hack above, we must also use it here for */
12845 /* perl_cando to work */
12846 if (efs_hack && (decc_efs_charset_index > 0)) {
12847 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12850 cptr = int_rmsexpand_tovms(save_spec, statbufp->st_devnam, rmsex_flags);
12851 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12852 if (efs_hack && (decc_efs_charset_index > 0)) {
12853 decc$feature_set_value(decc_efs_charset, 1, 0);
12857 /* Fix me: If this is NULL then stat found a file, and we could */
12858 /* not convert the specification to VMS - Should never happen */
12860 statbufp->st_devnam[0] = 0;
12862 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12864 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12865 # ifdef RTL_USES_UTC
12866 # ifdef VMSISH_TIME
12868 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12869 statbufp->st_atime = _toloc(statbufp->st_atime);
12870 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12874 # ifdef VMSISH_TIME
12875 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12879 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12880 statbufp->st_atime = _toutc(statbufp->st_atime);
12881 statbufp->st_ctime = _toutc(statbufp->st_ctime);
12885 /* If we were successful, leave errno where we found it */
12886 if (retval == 0) RESTORE_ERRNO;
12887 PerlMem_free(temp_fspec);
12888 PerlMem_free(fileified);
12891 } /* end of flex_stat_int() */
12894 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12896 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12898 return flex_stat_int(fspec, statbufp, 0);
12902 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12904 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12906 return flex_stat_int(fspec, statbufp, 1);
12911 /*{{{char *my_getlogin()*/
12912 /* VMS cuserid == Unix getlogin, except calling sequence */
12916 static char user[L_cuserid];
12917 return cuserid(user);
12922 /* rmscopy - copy a file using VMS RMS routines
12924 * Copies contents and attributes of spec_in to spec_out, except owner
12925 * and protection information. Name and type of spec_in are used as
12926 * defaults for spec_out. The third parameter specifies whether rmscopy()
12927 * should try to propagate timestamps from the input file to the output file.
12928 * If it is less than 0, no timestamps are preserved. If it is 0, then
12929 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12930 * propagated to the output file at creation iff the output file specification
12931 * did not contain an explicit name or type, and the revision date is always
12932 * updated at the end of the copy operation. If it is greater than 0, then
12933 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12934 * other than the revision date should be propagated, and bit 1 indicates
12935 * that the revision date should be propagated.
12937 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12939 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12940 * Incorporates, with permission, some code from EZCOPY by Tim Adye
12941 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12942 * as part of the Perl standard distribution under the terms of the
12943 * GNU General Public License or the Perl Artistic License. Copies
12944 * of each may be found in the Perl standard distribution.
12946 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12948 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12950 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12951 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12952 unsigned long int sts;
12954 struct FAB fab_in, fab_out;
12955 struct RAB rab_in, rab_out;
12956 rms_setup_nam(nam);
12957 rms_setup_nam(nam_out);
12958 struct XABDAT xabdat;
12959 struct XABFHC xabfhc;
12960 struct XABRDT xabrdt;
12961 struct XABSUM xabsum;
12963 vmsin = PerlMem_malloc(VMS_MAXRSS);
12964 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12965 vmsout = PerlMem_malloc(VMS_MAXRSS);
12966 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12967 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12968 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
12969 PerlMem_free(vmsin);
12970 PerlMem_free(vmsout);
12971 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12975 esa = PerlMem_malloc(VMS_MAXRSS);
12976 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12978 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12979 esal = PerlMem_malloc(VMS_MAXRSS);
12980 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12982 fab_in = cc$rms_fab;
12983 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12984 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12985 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12986 fab_in.fab$l_fop = FAB$M_SQO;
12987 rms_bind_fab_nam(fab_in, nam);
12988 fab_in.fab$l_xab = (void *) &xabdat;
12990 rsa = PerlMem_malloc(VMS_MAXRSS);
12991 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12993 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12994 rsal = PerlMem_malloc(VMS_MAXRSS);
12995 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12997 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12998 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12999 rms_nam_esl(nam) = 0;
13000 rms_nam_rsl(nam) = 0;
13001 rms_nam_esll(nam) = 0;
13002 rms_nam_rsll(nam) = 0;
13003 #ifdef NAM$M_NO_SHORT_UPCASE
13004 if (decc_efs_case_preserve)
13005 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
13008 xabdat = cc$rms_xabdat; /* To get creation date */
13009 xabdat.xab$l_nxt = (void *) &xabfhc;
13011 xabfhc = cc$rms_xabfhc; /* To get record length */
13012 xabfhc.xab$l_nxt = (void *) &xabsum;
13014 xabsum = cc$rms_xabsum; /* To get key and area information */
13016 if (!((sts = sys$open(&fab_in)) & 1)) {
13017 PerlMem_free(vmsin);
13018 PerlMem_free(vmsout);
13021 PerlMem_free(esal);
13024 PerlMem_free(rsal);
13025 set_vaxc_errno(sts);
13027 case RMS$_FNF: case RMS$_DNF:
13028 set_errno(ENOENT); break;
13030 set_errno(ENOTDIR); break;
13032 set_errno(ENODEV); break;
13034 set_errno(EINVAL); break;
13036 set_errno(EACCES); break;
13038 set_errno(EVMSERR);
13045 fab_out.fab$w_ifi = 0;
13046 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
13047 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
13048 fab_out.fab$l_fop = FAB$M_SQO;
13049 rms_bind_fab_nam(fab_out, nam_out);
13050 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
13051 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
13052 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
13053 esa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
13054 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13055 rsa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
13056 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13059 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13060 esal_out = PerlMem_malloc(VMS_MAXRSS);
13061 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13062 rsal_out = PerlMem_malloc(VMS_MAXRSS);
13063 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13065 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
13066 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
13068 if (preserve_dates == 0) { /* Act like DCL COPY */
13069 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
13070 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
13071 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
13072 PerlMem_free(vmsin);
13073 PerlMem_free(vmsout);
13076 PerlMem_free(esal);
13079 PerlMem_free(rsal);
13080 PerlMem_free(esa_out);
13081 if (esal_out != NULL)
13082 PerlMem_free(esal_out);
13083 PerlMem_free(rsa_out);
13084 if (rsal_out != NULL)
13085 PerlMem_free(rsal_out);
13086 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
13087 set_vaxc_errno(sts);
13090 fab_out.fab$l_xab = (void *) &xabdat;
13091 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
13092 preserve_dates = 1;
13094 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
13095 preserve_dates =0; /* bitmask from this point forward */
13097 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
13098 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
13099 PerlMem_free(vmsin);
13100 PerlMem_free(vmsout);
13103 PerlMem_free(esal);
13106 PerlMem_free(rsal);
13107 PerlMem_free(esa_out);
13108 if (esal_out != NULL)
13109 PerlMem_free(esal_out);
13110 PerlMem_free(rsa_out);
13111 if (rsal_out != NULL)
13112 PerlMem_free(rsal_out);
13113 set_vaxc_errno(sts);
13116 set_errno(ENOENT); break;
13118 set_errno(ENOTDIR); break;
13120 set_errno(ENODEV); break;
13122 set_errno(EINVAL); break;
13124 set_errno(EACCES); break;
13126 set_errno(EVMSERR);
13130 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
13131 if (preserve_dates & 2) {
13132 /* sys$close() will process xabrdt, not xabdat */
13133 xabrdt = cc$rms_xabrdt;
13135 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
13137 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
13138 * is unsigned long[2], while DECC & VAXC use a struct */
13139 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
13141 fab_out.fab$l_xab = (void *) &xabrdt;
13144 ubf = PerlMem_malloc(32256);
13145 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13146 rab_in = cc$rms_rab;
13147 rab_in.rab$l_fab = &fab_in;
13148 rab_in.rab$l_rop = RAB$M_BIO;
13149 rab_in.rab$l_ubf = ubf;
13150 rab_in.rab$w_usz = 32256;
13151 if (!((sts = sys$connect(&rab_in)) & 1)) {
13152 sys$close(&fab_in); sys$close(&fab_out);
13153 PerlMem_free(vmsin);
13154 PerlMem_free(vmsout);
13158 PerlMem_free(esal);
13161 PerlMem_free(rsal);
13162 PerlMem_free(esa_out);
13163 if (esal_out != NULL)
13164 PerlMem_free(esal_out);
13165 PerlMem_free(rsa_out);
13166 if (rsal_out != NULL)
13167 PerlMem_free(rsal_out);
13168 set_errno(EVMSERR); set_vaxc_errno(sts);
13172 rab_out = cc$rms_rab;
13173 rab_out.rab$l_fab = &fab_out;
13174 rab_out.rab$l_rbf = ubf;
13175 if (!((sts = sys$connect(&rab_out)) & 1)) {
13176 sys$close(&fab_in); sys$close(&fab_out);
13177 PerlMem_free(vmsin);
13178 PerlMem_free(vmsout);
13182 PerlMem_free(esal);
13185 PerlMem_free(rsal);
13186 PerlMem_free(esa_out);
13187 if (esal_out != NULL)
13188 PerlMem_free(esal_out);
13189 PerlMem_free(rsa_out);
13190 if (rsal_out != NULL)
13191 PerlMem_free(rsal_out);
13192 set_errno(EVMSERR); set_vaxc_errno(sts);
13196 while ((sts = sys$read(&rab_in))) { /* always true */
13197 if (sts == RMS$_EOF) break;
13198 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
13199 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
13200 sys$close(&fab_in); sys$close(&fab_out);
13201 PerlMem_free(vmsin);
13202 PerlMem_free(vmsout);
13206 PerlMem_free(esal);
13209 PerlMem_free(rsal);
13210 PerlMem_free(esa_out);
13211 if (esal_out != NULL)
13212 PerlMem_free(esal_out);
13213 PerlMem_free(rsa_out);
13214 if (rsal_out != NULL)
13215 PerlMem_free(rsal_out);
13216 set_errno(EVMSERR); set_vaxc_errno(sts);
13222 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
13223 sys$close(&fab_in); sys$close(&fab_out);
13224 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
13226 PerlMem_free(vmsin);
13227 PerlMem_free(vmsout);
13231 PerlMem_free(esal);
13234 PerlMem_free(rsal);
13235 PerlMem_free(esa_out);
13236 if (esal_out != NULL)
13237 PerlMem_free(esal_out);
13238 PerlMem_free(rsa_out);
13239 if (rsal_out != NULL)
13240 PerlMem_free(rsal_out);
13243 set_errno(EVMSERR); set_vaxc_errno(sts);
13249 } /* end of rmscopy() */
13253 /*** The following glue provides 'hooks' to make some of the routines
13254 * from this file available from Perl. These routines are sufficiently
13255 * basic, and are required sufficiently early in the build process,
13256 * that's it's nice to have them available to miniperl as well as the
13257 * full Perl, so they're set up here instead of in an extension. The
13258 * Perl code which handles importation of these names into a given
13259 * package lives in [.VMS]Filespec.pm in @INC.
13263 rmsexpand_fromperl(pTHX_ CV *cv)
13266 char *fspec, *defspec = NULL, *rslt;
13268 int fs_utf8, dfs_utf8;
13272 if (!items || items > 2)
13273 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
13274 fspec = SvPV(ST(0),n_a);
13275 fs_utf8 = SvUTF8(ST(0));
13276 if (!fspec || !*fspec) XSRETURN_UNDEF;
13278 defspec = SvPV(ST(1),n_a);
13279 dfs_utf8 = SvUTF8(ST(1));
13281 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
13282 ST(0) = sv_newmortal();
13283 if (rslt != NULL) {
13284 sv_usepvn(ST(0),rslt,strlen(rslt));
13293 vmsify_fromperl(pTHX_ CV *cv)
13300 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
13301 utf8_fl = SvUTF8(ST(0));
13302 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13303 ST(0) = sv_newmortal();
13304 if (vmsified != NULL) {
13305 sv_usepvn(ST(0),vmsified,strlen(vmsified));
13314 unixify_fromperl(pTHX_ CV *cv)
13321 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
13322 utf8_fl = SvUTF8(ST(0));
13323 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13324 ST(0) = sv_newmortal();
13325 if (unixified != NULL) {
13326 sv_usepvn(ST(0),unixified,strlen(unixified));
13335 fileify_fromperl(pTHX_ CV *cv)
13342 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
13343 utf8_fl = SvUTF8(ST(0));
13344 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13345 ST(0) = sv_newmortal();
13346 if (fileified != NULL) {
13347 sv_usepvn(ST(0),fileified,strlen(fileified));
13356 pathify_fromperl(pTHX_ CV *cv)
13363 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
13364 utf8_fl = SvUTF8(ST(0));
13365 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13366 ST(0) = sv_newmortal();
13367 if (pathified != NULL) {
13368 sv_usepvn(ST(0),pathified,strlen(pathified));
13377 vmspath_fromperl(pTHX_ CV *cv)
13384 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
13385 utf8_fl = SvUTF8(ST(0));
13386 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13387 ST(0) = sv_newmortal();
13388 if (vmspath != NULL) {
13389 sv_usepvn(ST(0),vmspath,strlen(vmspath));
13398 unixpath_fromperl(pTHX_ CV *cv)
13405 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
13406 utf8_fl = SvUTF8(ST(0));
13407 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13408 ST(0) = sv_newmortal();
13409 if (unixpath != NULL) {
13410 sv_usepvn(ST(0),unixpath,strlen(unixpath));
13419 candelete_fromperl(pTHX_ CV *cv)
13427 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
13429 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13430 Newx(fspec, VMS_MAXRSS, char);
13431 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
13432 if (isGV_with_GP(mysv)) {
13433 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
13434 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13442 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
13443 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13450 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
13456 rmscopy_fromperl(pTHX_ CV *cv)
13459 char *inspec, *outspec, *inp, *outp;
13465 if (items < 2 || items > 3)
13466 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
13468 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13469 Newx(inspec, VMS_MAXRSS, char);
13470 if (isGV_with_GP(mysv)) {
13471 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
13472 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13473 ST(0) = sv_2mortal(newSViv(0));
13480 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
13481 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13482 ST(0) = sv_2mortal(newSViv(0));
13487 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
13488 Newx(outspec, VMS_MAXRSS, char);
13489 if (isGV_with_GP(mysv)) {
13490 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
13491 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13492 ST(0) = sv_2mortal(newSViv(0));
13500 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
13501 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13502 ST(0) = sv_2mortal(newSViv(0));
13508 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
13510 ST(0) = sv_2mortal(newSViv(rmscopy(inp,outp,date_flag)));
13516 /* The mod2fname is limited to shorter filenames by design, so it should
13517 * not be modified to support longer EFS pathnames
13520 mod2fname(pTHX_ CV *cv)
13523 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
13524 workbuff[NAM$C_MAXRSS*1 + 1];
13525 int counter, num_entries;
13526 /* ODS-5 ups this, but we want to be consistent, so... */
13527 int max_name_len = 39;
13528 AV *in_array = (AV *)SvRV(ST(0));
13530 num_entries = av_len(in_array);
13532 /* All the names start with PL_. */
13533 strcpy(ultimate_name, "PL_");
13535 /* Clean up our working buffer */
13536 Zero(work_name, sizeof(work_name), char);
13538 /* Run through the entries and build up a working name */
13539 for(counter = 0; counter <= num_entries; counter++) {
13540 /* If it's not the first name then tack on a __ */
13542 strcat(work_name, "__");
13544 strcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)));
13547 /* Check to see if we actually have to bother...*/
13548 if (strlen(work_name) + 3 <= max_name_len) {
13549 strcat(ultimate_name, work_name);
13551 /* It's too darned big, so we need to go strip. We use the same */
13552 /* algorithm as xsubpp does. First, strip out doubled __ */
13553 char *source, *dest, last;
13556 for (source = work_name; *source; source++) {
13557 if (last == *source && last == '_') {
13563 /* Go put it back */
13564 strcpy(work_name, workbuff);
13565 /* Is it still too big? */
13566 if (strlen(work_name) + 3 > max_name_len) {
13567 /* Strip duplicate letters */
13570 for (source = work_name; *source; source++) {
13571 if (last == toupper(*source)) {
13575 last = toupper(*source);
13577 strcpy(work_name, workbuff);
13580 /* Is it *still* too big? */
13581 if (strlen(work_name) + 3 > max_name_len) {
13582 /* Too bad, we truncate */
13583 work_name[max_name_len - 2] = 0;
13585 strcat(ultimate_name, work_name);
13588 /* Okay, return it */
13589 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13594 hushexit_fromperl(pTHX_ CV *cv)
13599 VMSISH_HUSHED = SvTRUE(ST(0));
13601 ST(0) = boolSV(VMSISH_HUSHED);
13607 Perl_vms_start_glob
13608 (pTHX_ SV *tmpglob,
13612 struct vs_str_st *rslt;
13616 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13619 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13620 struct dsc$descriptor_vs rsdsc;
13621 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13622 unsigned long hasver = 0, isunix = 0;
13623 unsigned long int lff_flags = 0;
13625 int vms_old_glob = 1;
13627 if (!SvOK(tmpglob)) {
13628 SETERRNO(ENOENT,RMS$_FNF);
13632 vms_old_glob = !decc_filename_unix_report;
13634 #ifdef VMS_LONGNAME_SUPPORT
13635 lff_flags = LIB$M_FIL_LONG_NAMES;
13637 /* The Newx macro will not allow me to assign a smaller array
13638 * to the rslt pointer, so we will assign it to the begin char pointer
13639 * and then copy the value into the rslt pointer.
13641 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13642 rslt = (struct vs_str_st *)begin;
13644 rstr = &rslt->str[0];
13645 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13646 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13647 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13648 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13650 Newx(vmsspec, VMS_MAXRSS, char);
13652 /* We could find out if there's an explicit dev/dir or version
13653 by peeking into lib$find_file's internal context at
13654 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13655 but that's unsupported, so I don't want to do it now and
13656 have it bite someone in the future. */
13657 /* Fix-me: vms_split_path() is the only way to do this, the
13658 existing method will fail with many legal EFS or UNIX specifications
13661 cp = SvPV(tmpglob,i);
13664 if (cp[i] == ';') hasver = 1;
13665 if (cp[i] == '.') {
13666 if (sts) hasver = 1;
13669 if (cp[i] == '/') {
13670 hasdir = isunix = 1;
13673 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13679 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13680 if ((hasdir == 0) && decc_filename_unix_report) {
13684 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13685 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13686 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13692 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13693 if (!stat_sts && S_ISDIR(st.st_mode)) {
13695 const char * fname;
13698 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13699 /* path delimiter of ':>]', if so, then the old behavior has */
13700 /* obviously been specifically requested */
13702 fname = SvPVX_const(tmpglob);
13703 fname_len = strlen(fname);
13704 vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13705 if (vms_old_glob || (vms_dir != NULL)) {
13706 wilddsc.dsc$a_pointer = tovmspath_utf8(
13707 SvPVX(tmpglob),vmsspec,NULL);
13708 ok = (wilddsc.dsc$a_pointer != NULL);
13709 /* maybe passed 'foo' rather than '[.foo]', thus not
13713 /* Operate just on the directory, the special stat/fstat for */
13714 /* leaves the fileified specification in the st_devnam */
13716 wilddsc.dsc$a_pointer = st.st_devnam;
13721 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13722 ok = (wilddsc.dsc$a_pointer != NULL);
13725 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13727 /* If not extended character set, replace ? with % */
13728 /* With extended character set, ? is a wildcard single character */
13729 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13732 if (!decc_efs_case_preserve)
13734 } else if (*cp == '%') {
13736 } else if (*cp == '*') {
13742 wv_sts = vms_split_path(
13743 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13744 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13745 &wvs_spec, &wvs_len);
13754 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13755 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13756 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13760 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13761 &dfltdsc,NULL,&rms_sts,&lff_flags);
13762 if (!$VMS_STATUS_SUCCESS(sts))
13765 /* with varying string, 1st word of buffer contains result length */
13766 rstr[rslt->length] = '\0';
13768 /* Find where all the components are */
13769 v_sts = vms_split_path
13784 /* If no version on input, truncate the version on output */
13785 if (!hasver && (vs_len > 0)) {
13792 /* In Unix report mode, remove the ".dir;1" from the name */
13793 /* if it is a real directory */
13794 if (decc_filename_unix_report || decc_efs_charset) {
13795 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13799 ret_sts = flex_lstat(rstr, &statbuf);
13800 if ((ret_sts == 0) &&
13801 S_ISDIR(statbuf.st_mode)) {
13808 /* No version & a null extension on UNIX handling */
13809 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13815 if (!decc_efs_case_preserve) {
13816 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13819 /* Find File treats a Null extension as return all extensions */
13820 /* This is contrary to Perl expectations */
13822 if (wildstar || wildquery || vms_old_glob) {
13823 /* really need to see if the returned file name matched */
13824 /* but for now will assume that it matches */
13827 /* Exact Match requested */
13828 /* How are directories handled? - like a file */
13829 if ((e_len == we_len) && (n_len == wn_len)) {
13833 t1 = strncmp(e_spec, we_spec, e_len);
13837 t1 = strncmp(n_spec, we_spec, n_len);
13848 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13852 /* Start with the name */
13855 strcat(begin,"\n");
13856 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13859 if (cxt) (void)lib$find_file_end(&cxt);
13862 /* Be POSIXish: return the input pattern when no matches */
13863 strcpy(rstr,SvPVX(tmpglob));
13865 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13868 if (ok && sts != RMS$_NMF &&
13869 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13872 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13874 PerlIO_close(tmpfp);
13878 PerlIO_rewind(tmpfp);
13879 IoTYPE(io) = IoTYPE_RDONLY;
13880 IoIFP(io) = fp = tmpfp;
13881 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13891 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13895 unixrealpath_fromperl(pTHX_ CV *cv)
13898 char *fspec, *rslt_spec, *rslt;
13901 if (!items || items != 1)
13902 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13904 fspec = SvPV(ST(0),n_a);
13905 if (!fspec || !*fspec) XSRETURN_UNDEF;
13907 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13908 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13910 ST(0) = sv_newmortal();
13912 sv_usepvn(ST(0),rslt,strlen(rslt));
13914 Safefree(rslt_spec);
13919 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13923 vmsrealpath_fromperl(pTHX_ CV *cv)
13926 char *fspec, *rslt_spec, *rslt;
13929 if (!items || items != 1)
13930 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13932 fspec = SvPV(ST(0),n_a);
13933 if (!fspec || !*fspec) XSRETURN_UNDEF;
13935 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13936 rslt = do_vms_realname(fspec, rslt_spec, NULL);
13938 ST(0) = sv_newmortal();
13940 sv_usepvn(ST(0),rslt,strlen(rslt));
13942 Safefree(rslt_spec);
13948 * A thin wrapper around decc$symlink to make sure we follow the
13949 * standard and do not create a symlink with a zero-length name.
13951 * Also in ODS-2 mode, existing tests assume that the link target
13952 * will be converted to UNIX format.
13954 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13955 int Perl_my_symlink(pTHX_ const char *contents, const char *link_name) {
13956 if (!link_name || !*link_name) {
13957 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13961 if (decc_efs_charset) {
13962 return symlink(contents, link_name);
13967 /* Unless we are in ODS-5 mode, convert the symlink target to UNIX */
13968 /* because in order to work, the symlink target must be in UNIX format */
13970 /* As symbolic links can hold things other than files, we will only do */
13971 /* the conversion in in ODS-2 mode */
13973 utarget = PerlMem_malloc(VMS_MAXRSS + 1);
13974 if (int_tounixspec(contents, utarget, NULL) == NULL) {
13976 /* This should not fail, as an untranslatable filename */
13977 /* should be passed through */
13978 utarget = (char *)contents;
13980 sts = symlink(utarget, link_name);
13981 PerlMem_free(utarget);
13988 #endif /* HAS_SYMLINK */
13990 int do_vms_case_tolerant(void);
13993 case_tolerant_process_fromperl(pTHX_ CV *cv)
13996 ST(0) = boolSV(do_vms_case_tolerant());
14000 #ifdef USE_ITHREADS
14003 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
14004 struct interp_intern *dst)
14006 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
14008 memcpy(dst,src,sizeof(struct interp_intern));
14014 Perl_sys_intern_clear(pTHX)
14019 Perl_sys_intern_init(pTHX)
14021 unsigned int ix = RAND_MAX;
14026 MY_POSIX_EXIT = vms_posix_exit;
14029 MY_INV_RAND_MAX = 1./x;
14033 init_os_extras(void)
14036 char* file = __FILE__;
14037 if (decc_disable_to_vms_logname_translation) {
14038 no_translate_barewords = TRUE;
14040 no_translate_barewords = FALSE;
14043 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
14044 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
14045 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
14046 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
14047 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
14048 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
14049 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
14050 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
14051 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
14052 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
14053 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
14054 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
14055 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
14056 newXSproto("VMS::Filespec::case_tolerant_process",
14057 case_tolerant_process_fromperl,file,"");
14059 store_pipelocs(aTHX); /* will redo any earlier attempts */
14064 #if __CRTL_VER == 80200000
14065 /* This missed getting in to the DECC SDK for 8.2 */
14066 char *realpath(const char *file_name, char * resolved_name, ...);
14069 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
14070 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
14071 * The perl fallback routine to provide realpath() is not as efficient
14075 /* Hack, use old stat() as fastest way of getting ino_t and device */
14076 int decc$stat(const char *name, void * statbuf);
14077 #if !defined(__VAX) && __CRTL_VER >= 80200000
14078 int decc$lstat(const char *name, void * statbuf);
14080 #define decc$lstat decc$stat
14084 /* Realpath is fragile. In 8.3 it does not work if the feature
14085 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
14086 * links are implemented in RMS, not the CRTL. It also can fail if the
14087 * user does not have read/execute access to some of the directories.
14088 * So in order for Do What I Mean mode to work, if realpath() fails,
14089 * fall back to looking up the filename by the device name and FID.
14092 int vms_fid_to_name(char * outname, int outlen,
14093 const char * name, int lstat_flag, mode_t * mode)
14095 #pragma message save
14096 #pragma message disable MISALGNDSTRCT
14097 #pragma message disable MISALGNDMEM
14098 #pragma member_alignment save
14099 #pragma nomember_alignment
14102 unsigned short st_ino[3];
14103 unsigned short old_st_mode;
14104 unsigned long padl[30]; /* plenty of room */
14106 #pragma message restore
14107 #pragma member_alignment restore
14110 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14111 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14116 /* Need to follow the mostly the same rules as flex_stat_int, or we may get
14117 * unexpected answers
14120 fileified = PerlMem_malloc(VMS_MAXRSS);
14121 if (fileified == NULL)
14122 _ckvmssts_noperl(SS$_INSFMEM);
14124 temp_fspec = PerlMem_malloc(VMS_MAXRSS);
14125 if (temp_fspec == NULL)
14126 _ckvmssts_noperl(SS$_INSFMEM);
14129 /* First need to try as a directory */
14130 ret_spec = int_tovmspath(name, temp_fspec, NULL);
14131 if (ret_spec != NULL) {
14132 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
14133 if (ret_spec != NULL) {
14134 if (lstat_flag == 0)
14135 sts = decc$stat(fileified, &statbuf);
14137 sts = decc$lstat(fileified, &statbuf);
14141 /* Then as a VMS file spec */
14143 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
14144 if (ret_spec != NULL) {
14145 if (lstat_flag == 0) {
14146 sts = decc$stat(temp_fspec, &statbuf);
14148 sts = decc$lstat(temp_fspec, &statbuf);
14154 /* Next try - allow multiple dots with out EFS CHARSET */
14155 /* The CRTL stat() falls down hard on multi-dot filenames in unix
14156 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
14157 * enable it if it isn't already.
14159 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14160 if (!decc_efs_charset && (decc_efs_charset_index > 0))
14161 decc$feature_set_value(decc_efs_charset_index, 1, 1);
14163 ret_spec = int_tovmspath(name, temp_fspec, NULL);
14164 if (lstat_flag == 0) {
14165 sts = decc$stat(name, &statbuf);
14167 sts = decc$lstat(name, &statbuf);
14169 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14170 if (!decc_efs_charset && (decc_efs_charset_index > 0))
14171 decc$feature_set_value(decc_efs_charset_index, 1, 0);
14176 /* and then because the Perl Unix to VMS conversion is not perfect */
14177 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
14178 /* characters from filenames so we need to try it as-is */
14180 if (lstat_flag == 0) {
14181 sts = decc$stat(name, &statbuf);
14183 sts = decc$lstat(name, &statbuf);
14190 dvidsc.dsc$a_pointer=statbuf.st_dev;
14191 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
14193 specdsc.dsc$a_pointer = outname;
14194 specdsc.dsc$w_length = outlen-1;
14196 vms_sts = lib$fid_to_name
14197 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
14198 if ($VMS_STATUS_SUCCESS(vms_sts)) {
14199 outname[specdsc.dsc$w_length] = 0;
14201 /* Return the mode */
14203 *mode = statbuf.old_st_mode;
14207 PerlMem_free(temp_fspec);
14208 PerlMem_free(fileified);
14215 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
14218 char * rslt = NULL;
14221 if (decc_posix_compliant_pathnames > 0 ) {
14222 /* realpath currently only works if posix compliant pathnames are
14223 * enabled. It may start working when they are not, but in that
14224 * case we still want the fallback behavior for backwards compatibility
14226 rslt = realpath(filespec, outbuf);
14230 if (rslt == NULL) {
14232 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14233 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14236 /* Fall back to fid_to_name */
14238 Newx(vms_spec, VMS_MAXRSS + 1, char);
14240 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
14244 /* Now need to trim the version off */
14245 sts = vms_split_path
14265 /* Trim off the version */
14266 int file_len = v_len + r_len + d_len + n_len + e_len;
14267 vms_spec[file_len] = 0;
14269 /* Trim off the .DIR if this is a directory */
14270 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
14271 if (S_ISDIR(my_mode)) {
14277 /* Drop NULL extensions on UNIX file specification */
14278 if ((e_len == 1) && decc_readdir_dropdotnotype) {
14283 /* The result is expected to be in UNIX format */
14284 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
14286 /* Downcase if input had any lower case letters and
14287 * case preservation is not in effect.
14289 if (!decc_efs_case_preserve) {
14290 for (cp = filespec; *cp; cp++)
14291 if (islower(*cp)) { haslower = 1; break; }
14293 if (haslower) __mystrtolower(rslt);
14298 /* Now for some hacks to deal with backwards and forward */
14299 /* compatibility */
14300 if (!decc_efs_charset) {
14302 /* 1. ODS-2 mode wants to do a syntax only translation */
14303 rslt = int_rmsexpand(filespec, outbuf,
14304 NULL, 0, NULL, utf8_fl);
14307 if (decc_filename_unix_report) {
14309 char * vms_dir_name;
14312 /* 2. ODS-5 / UNIX report mode should return a failure */
14313 /* if the parent directory also does not exist */
14314 /* Otherwise, get the real path for the parent */
14315 /* and add the child to it.
14317 /* basename / dirname only available for VMS 7.0+ */
14318 /* So we may need to implement them as common routines */
14320 Newx(dir_name, VMS_MAXRSS + 1, char);
14321 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
14322 dir_name[0] = '\0';
14325 /* First try a VMS parse */
14326 sts = vms_split_path
14344 int dir_len = v_len + r_len + d_len + n_len;
14346 strncpy(dir_name, filespec, dir_len);
14347 dir_name[dir_len] = '\0';
14348 file_name = (char *)&filespec[dir_len + 1];
14351 /* This must be UNIX */
14354 tchar = strrchr(filespec, '/');
14356 if (tchar != NULL) {
14357 int dir_len = tchar - filespec;
14358 strncpy(dir_name, filespec, dir_len);
14359 dir_name[dir_len] = '\0';
14360 file_name = (char *) &filespec[dir_len + 1];
14364 /* Dir name is defaulted */
14365 if (dir_name[0] == 0) {
14367 dir_name[1] = '\0';
14370 /* Need realpath for the directory */
14371 sts = vms_fid_to_name(vms_dir_name,
14373 dir_name, 0, NULL);
14376 /* Now need to pathify it.
14377 char *tdir = int_pathify_dirspec(vms_dir_name,
14380 /* And now add the original filespec to it */
14381 if (file_name != NULL) {
14382 strcat(outbuf, file_name);
14386 Safefree(vms_dir_name);
14387 Safefree(dir_name);
14391 Safefree(vms_spec);
14397 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
14400 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14401 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14403 /* Fall back to fid_to_name */
14405 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
14412 /* Now need to trim the version off */
14413 sts = vms_split_path
14433 /* Trim off the version */
14434 int file_len = v_len + r_len + d_len + n_len + e_len;
14435 outbuf[file_len] = 0;
14437 /* Downcase if input had any lower case letters and
14438 * case preservation is not in effect.
14440 if (!decc_efs_case_preserve) {
14441 for (cp = filespec; *cp; cp++)
14442 if (islower(*cp)) { haslower = 1; break; }
14444 if (haslower) __mystrtolower(outbuf);
14453 /* External entry points */
14454 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14455 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
14457 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14458 { return do_vms_realname(filespec, outbuf, utf8_fl); }
14460 /* case_tolerant */
14462 /*{{{int do_vms_case_tolerant(void)*/
14463 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
14464 * controlled by a process setting.
14466 int do_vms_case_tolerant(void)
14468 return vms_process_case_tolerant;
14471 /* External entry points */
14472 #if __CRTL_VER >= 70301000 && !defined(__VAX)
14473 int Perl_vms_case_tolerant(void)
14474 { return do_vms_case_tolerant(); }
14476 int Perl_vms_case_tolerant(void)
14477 { return vms_process_case_tolerant; }
14481 /* Start of DECC RTL Feature handling */
14483 static int sys_trnlnm
14484 (const char * logname,
14488 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
14489 const unsigned long attr = LNM$M_CASE_BLIND;
14490 struct dsc$descriptor_s name_dsc;
14492 unsigned short result;
14493 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
14496 name_dsc.dsc$w_length = strlen(logname);
14497 name_dsc.dsc$a_pointer = (char *)logname;
14498 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14499 name_dsc.dsc$b_class = DSC$K_CLASS_S;
14501 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
14503 if ($VMS_STATUS_SUCCESS(status)) {
14505 /* Null terminate and return the string */
14506 /*--------------------------------------*/
14513 static int sys_crelnm
14514 (const char * logname,
14515 const char * value)
14518 const char * proc_table = "LNM$PROCESS_TABLE";
14519 struct dsc$descriptor_s proc_table_dsc;
14520 struct dsc$descriptor_s logname_dsc;
14521 struct itmlst_3 item_list[2];
14523 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
14524 proc_table_dsc.dsc$w_length = strlen(proc_table);
14525 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14526 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
14528 logname_dsc.dsc$a_pointer = (char *) logname;
14529 logname_dsc.dsc$w_length = strlen(logname);
14530 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14531 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
14533 item_list[0].buflen = strlen(value);
14534 item_list[0].itmcode = LNM$_STRING;
14535 item_list[0].bufadr = (char *)value;
14536 item_list[0].retlen = NULL;
14538 item_list[1].buflen = 0;
14539 item_list[1].itmcode = 0;
14541 ret_val = sys$crelnm
14543 (const struct dsc$descriptor_s *)&proc_table_dsc,
14544 (const struct dsc$descriptor_s *)&logname_dsc,
14546 (const struct item_list_3 *) item_list);
14551 /* C RTL Feature settings */
14553 static int set_features
14554 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
14555 int (* cli_routine)(void), /* Not documented */
14556 void *image_info) /* Not documented */
14561 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
14562 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
14563 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
14564 unsigned long case_perm;
14565 unsigned long case_image;
14568 /* Allow an exception to bring Perl into the VMS debugger */
14569 vms_debug_on_exception = 0;
14570 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
14571 if ($VMS_STATUS_SUCCESS(status)) {
14572 val_str[0] = _toupper(val_str[0]);
14573 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14574 vms_debug_on_exception = 1;
14576 vms_debug_on_exception = 0;
14579 /* Debug unix/vms file translation routines */
14580 vms_debug_fileify = 0;
14581 status = sys_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
14582 if ($VMS_STATUS_SUCCESS(status)) {
14583 val_str[0] = _toupper(val_str[0]);
14584 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14585 vms_debug_fileify = 1;
14587 vms_debug_fileify = 0;
14591 /* Historically PERL has been doing vmsify / stat differently than */
14592 /* the CRTL. In particular, under some conditions the CRTL will */
14593 /* remove some illegal characters like spaces from filenames */
14594 /* resulting in some differences. The stat()/lstat() wrapper has */
14595 /* been reporting such file names as invalid and fails to stat them */
14596 /* fixing this bug so that stat()/lstat() accept these like the */
14597 /* CRTL does will result in several tests failing. */
14598 /* This should really be fixed, but for now, set up a feature to */
14599 /* enable it so that the impact can be studied. */
14600 vms_bug_stat_filename = 0;
14601 status = sys_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
14602 if ($VMS_STATUS_SUCCESS(status)) {
14603 val_str[0] = _toupper(val_str[0]);
14604 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14605 vms_bug_stat_filename = 1;
14607 vms_bug_stat_filename = 0;
14611 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
14612 vms_vtf7_filenames = 0;
14613 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14614 if ($VMS_STATUS_SUCCESS(status)) {
14615 val_str[0] = _toupper(val_str[0]);
14616 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14617 vms_vtf7_filenames = 1;
14619 vms_vtf7_filenames = 0;
14622 /* unlink all versions on unlink() or rename() */
14623 vms_unlink_all_versions = 0;
14624 status = sys_trnlnm
14625 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14626 if ($VMS_STATUS_SUCCESS(status)) {
14627 val_str[0] = _toupper(val_str[0]);
14628 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14629 vms_unlink_all_versions = 1;
14631 vms_unlink_all_versions = 0;
14634 /* Dectect running under GNV Bash or other UNIX like shell */
14635 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14636 gnv_unix_shell = 0;
14637 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14638 if ($VMS_STATUS_SUCCESS(status)) {
14639 gnv_unix_shell = 1;
14640 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14641 set_feature_default("DECC$EFS_CHARSET", 1);
14642 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14643 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14644 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14645 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
14646 vms_unlink_all_versions = 1;
14647 vms_posix_exit = 1;
14651 /* hacks to see if known bugs are still present for testing */
14653 /* PCP mode requires creating /dev/null special device file */
14654 decc_bug_devnull = 0;
14655 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14656 if ($VMS_STATUS_SUCCESS(status)) {
14657 val_str[0] = _toupper(val_str[0]);
14658 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14659 decc_bug_devnull = 1;
14661 decc_bug_devnull = 0;
14664 /* UNIX directory names with no paths are broken in a lot of places */
14665 decc_dir_barename = 1;
14666 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
14667 if ($VMS_STATUS_SUCCESS(status)) {
14668 val_str[0] = _toupper(val_str[0]);
14669 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14670 decc_dir_barename = 1;
14672 decc_dir_barename = 0;
14675 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14676 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14678 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14679 if (decc_disable_to_vms_logname_translation < 0)
14680 decc_disable_to_vms_logname_translation = 0;
14683 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14685 decc_efs_case_preserve = decc$feature_get_value(s, 1);
14686 if (decc_efs_case_preserve < 0)
14687 decc_efs_case_preserve = 0;
14690 s = decc$feature_get_index("DECC$EFS_CHARSET");
14691 decc_efs_charset_index = s;
14693 decc_efs_charset = decc$feature_get_value(s, 1);
14694 if (decc_efs_charset < 0)
14695 decc_efs_charset = 0;
14698 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14700 decc_filename_unix_report = decc$feature_get_value(s, 1);
14701 if (decc_filename_unix_report > 0) {
14702 decc_filename_unix_report = 1;
14703 vms_posix_exit = 1;
14706 decc_filename_unix_report = 0;
14709 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14711 decc_filename_unix_only = decc$feature_get_value(s, 1);
14712 if (decc_filename_unix_only > 0) {
14713 decc_filename_unix_only = 1;
14716 decc_filename_unix_only = 0;
14720 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14722 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14723 if (decc_filename_unix_no_version < 0)
14724 decc_filename_unix_no_version = 0;
14727 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14729 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14730 if (decc_readdir_dropdotnotype < 0)
14731 decc_readdir_dropdotnotype = 0;
14734 #if __CRTL_VER >= 80200000
14735 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14737 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14738 if (decc_posix_compliant_pathnames < 0)
14739 decc_posix_compliant_pathnames = 0;
14740 if (decc_posix_compliant_pathnames > 4)
14741 decc_posix_compliant_pathnames = 0;
14746 status = sys_trnlnm
14747 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14748 if ($VMS_STATUS_SUCCESS(status)) {
14749 val_str[0] = _toupper(val_str[0]);
14750 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14751 decc_disable_to_vms_logname_translation = 1;
14756 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14757 if ($VMS_STATUS_SUCCESS(status)) {
14758 val_str[0] = _toupper(val_str[0]);
14759 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14760 decc_efs_case_preserve = 1;
14765 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14766 if ($VMS_STATUS_SUCCESS(status)) {
14767 val_str[0] = _toupper(val_str[0]);
14768 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14769 decc_filename_unix_report = 1;
14772 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14773 if ($VMS_STATUS_SUCCESS(status)) {
14774 val_str[0] = _toupper(val_str[0]);
14775 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14776 decc_filename_unix_only = 1;
14777 decc_filename_unix_report = 1;
14780 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14781 if ($VMS_STATUS_SUCCESS(status)) {
14782 val_str[0] = _toupper(val_str[0]);
14783 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14784 decc_filename_unix_no_version = 1;
14787 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14788 if ($VMS_STATUS_SUCCESS(status)) {
14789 val_str[0] = _toupper(val_str[0]);
14790 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14791 decc_readdir_dropdotnotype = 1;
14796 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14798 /* Report true case tolerance */
14799 /*----------------------------*/
14800 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14801 if (!$VMS_STATUS_SUCCESS(status))
14802 case_perm = PPROP$K_CASE_BLIND;
14803 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14804 if (!$VMS_STATUS_SUCCESS(status))
14805 case_image = PPROP$K_CASE_BLIND;
14806 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14807 (case_image == PPROP$K_CASE_SENSITIVE))
14808 vms_process_case_tolerant = 0;
14812 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
14813 /* for strict backward compatibility */
14814 status = sys_trnlnm
14815 ("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14816 if ($VMS_STATUS_SUCCESS(status)) {
14817 val_str[0] = _toupper(val_str[0]);
14818 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14819 vms_posix_exit = 1;
14821 vms_posix_exit = 0;
14825 /* CRTL can be initialized past this point, but not before. */
14826 /* DECC$CRTL_INIT(); */
14833 #pragma extern_model save
14834 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
14835 const __align (LONGWORD) int spare[8] = {0};
14837 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
14838 #if __DECC_VER >= 60560002
14839 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
14841 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
14843 #endif /* __DECC */
14845 const long vms_cc_features = (const long)set_features;
14848 ** Force a reference to LIB$INITIALIZE to ensure it
14849 ** exists in the image.
14851 #define lib$initialize LIB$INITIALIZE
14852 int lib$initialize(void);
14854 #pragma extern_model strict_refdef
14856 int lib_init_ref = (int) lib$initialize;
14859 #pragma extern_model restore