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.
22 * The Lay of Leithian, 135-40
31 #include <climsgdef.h>
42 #include <libclidef.h>
44 #include <lib$routines.h>
48 #if __CRTL_VER >= 70301000 && !defined(__VAX)
58 #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 /* More specific prototype than in starlet_c.h makes programming errors
99 const struct dsc$descriptor_s * devnam,
100 const struct item_list_3 * itmlst,
102 void * (astadr)(unsigned long),
107 #ifdef sys$get_security
108 #undef sys$get_security
110 (const struct dsc$descriptor_s * clsnam,
111 const struct dsc$descriptor_s * objnam,
112 const unsigned int *objhan,
114 const struct item_list_3 * itmlst,
115 unsigned int * contxt,
116 const unsigned int * acmode);
119 #ifdef sys$set_security
120 #undef sys$set_security
122 (const struct dsc$descriptor_s * clsnam,
123 const struct dsc$descriptor_s * objnam,
124 const unsigned int *objhan,
126 const struct item_list_3 * itmlst,
127 unsigned int * contxt,
128 const unsigned int * acmode);
131 #ifdef lib$find_image_symbol
132 #undef lib$find_image_symbol
133 int lib$find_image_symbol
134 (const struct dsc$descriptor_s * imgname,
135 const struct dsc$descriptor_s * symname,
137 const struct dsc$descriptor_s * defspec,
141 #ifdef lib$rename_file
142 #undef lib$rename_file
144 (const struct dsc$descriptor_s * old_file_dsc,
145 const struct dsc$descriptor_s * new_file_dsc,
146 const struct dsc$descriptor_s * default_file_dsc,
147 const struct dsc$descriptor_s * related_file_dsc,
148 const unsigned long * flags,
149 void * (success)(const struct dsc$descriptor_s * old_dsc,
150 const struct dsc$descriptor_s * new_dsc,
152 void * (error)(const struct dsc$descriptor_s * old_dsc,
153 const struct dsc$descriptor_s * new_dsc,
156 const int * error_src,
157 const void * usr_arg),
158 int (confirm)(const struct dsc$descriptor_s * old_dsc,
159 const struct dsc$descriptor_s * new_dsc,
160 const void * old_fab,
161 const void * usr_arg),
163 struct dsc$descriptor_s * old_result_name_dsc,
164 struct dsc$descriptor_s * new_result_name_dsc,
165 unsigned long * file_scan_context);
168 #if __CRTL_VER >= 70300000 && !defined(__VAX)
170 static int set_feature_default(const char *name, int value)
175 index = decc$feature_get_index(name);
177 status = decc$feature_set_value(index, 1, value);
178 if (index == -1 || (status == -1)) {
182 status = decc$feature_get_value(index, 1);
183 if (status != value) {
191 /* Older versions of ssdef.h don't have these */
192 #ifndef SS$_INVFILFOROP
193 # define SS$_INVFILFOROP 3930
195 #ifndef SS$_NOSUCHOBJECT
196 # define SS$_NOSUCHOBJECT 2696
199 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
200 #define PERLIO_NOT_STDIO 0
202 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
203 * code below needs to get to the underlying CRTL routines. */
204 #define DONT_MASK_RTL_CALLS
208 /* Anticipating future expansion in lexical warnings . . . */
209 #ifndef WARN_INTERNAL
210 # define WARN_INTERNAL WARN_MISC
213 #ifdef VMS_LONGNAME_SUPPORT
214 #include <libfildef.h>
217 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
218 # define RTL_USES_UTC 1
221 /* Routine to create a decterm for use with the Perl debugger */
222 /* No headers, this information was found in the Programming Concepts Manual */
224 static int (*decw_term_port)
225 (const struct dsc$descriptor_s * display,
226 const struct dsc$descriptor_s * setup_file,
227 const struct dsc$descriptor_s * customization,
228 struct dsc$descriptor_s * result_device_name,
229 unsigned short * result_device_name_length,
232 void * char_change_buffer) = 0;
234 /* gcc's header files don't #define direct access macros
235 * corresponding to VAXC's variant structs */
237 # define uic$v_format uic$r_uic_form.uic$v_format
238 # define uic$v_group uic$r_uic_form.uic$v_group
239 # define uic$v_member uic$r_uic_form.uic$v_member
240 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
241 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
242 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
243 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
246 #if defined(NEED_AN_H_ERRNO)
251 #pragma message disable pragma
252 #pragma member_alignment save
253 #pragma nomember_alignment longword
255 #pragma message disable misalgndmem
258 unsigned short int buflen;
259 unsigned short int itmcode;
261 unsigned short int *retlen;
264 struct filescan_itmlst_2 {
265 unsigned short length;
266 unsigned short itmcode;
271 unsigned short length;
276 #pragma message restore
277 #pragma member_alignment restore
280 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
281 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
282 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
283 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
284 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
285 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
286 #define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c)
287 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
288 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
289 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
290 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
291 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
293 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
294 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
295 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
296 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
298 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
299 #define PERL_LNM_MAX_ALLOWED_INDEX 127
301 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
302 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
305 #define PERL_LNM_MAX_ITER 10
307 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
308 #if __CRTL_VER >= 70302000 && !defined(__VAX)
309 #define MAX_DCL_SYMBOL (8192)
310 #define MAX_DCL_LINE_LENGTH (4096 - 4)
312 #define MAX_DCL_SYMBOL (1024)
313 #define MAX_DCL_LINE_LENGTH (1024 - 4)
316 static char *__mystrtolower(char *str)
318 if (str) for (; *str; ++str) *str= tolower(*str);
322 static struct dsc$descriptor_s fildevdsc =
323 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
324 static struct dsc$descriptor_s crtlenvdsc =
325 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
326 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
327 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
328 static struct dsc$descriptor_s **env_tables = defenv;
329 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
331 /* True if we shouldn't treat barewords as logicals during directory */
333 static int no_translate_barewords;
336 static int tz_updated = 1;
339 /* DECC Features that may need to affect how Perl interprets
340 * displays filename information
342 static int decc_disable_to_vms_logname_translation = 1;
343 static int decc_disable_posix_root = 1;
344 int decc_efs_case_preserve = 0;
345 static int decc_efs_charset = 0;
346 static int decc_filename_unix_no_version = 0;
347 static int decc_filename_unix_only = 0;
348 int decc_filename_unix_report = 0;
349 int decc_posix_compliant_pathnames = 0;
350 int decc_readdir_dropdotnotype = 0;
351 static int vms_process_case_tolerant = 1;
352 int vms_vtf7_filenames = 0;
353 int gnv_unix_shell = 0;
354 static int vms_unlink_all_versions = 0;
356 /* bug workarounds if needed */
357 int decc_bug_readdir_efs1 = 0;
358 int decc_bug_devnull = 1;
359 int decc_bug_fgetname = 0;
360 int decc_dir_barename = 0;
362 static int vms_debug_on_exception = 0;
364 /* Is this a UNIX file specification?
365 * No longer a simple check with EFS file specs
366 * For now, not a full check, but need to
367 * handle POSIX ^UP^ specifications
368 * Fixing to handle ^/ cases would require
369 * changes to many other conversion routines.
372 static int is_unix_filespec(const char *path)
378 if (strncmp(path,"\"^UP^",5) != 0) {
379 pch1 = strchr(path, '/');
384 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
385 if (decc_filename_unix_report || decc_filename_unix_only) {
386 if (strcmp(path,".") == 0)
394 /* This routine converts a UCS-2 character to be VTF-7 encoded.
397 static void ucs2_to_vtf7
399 unsigned long ucs2_char,
402 unsigned char * ucs_ptr;
405 ucs_ptr = (unsigned char *)&ucs2_char;
409 hex = (ucs_ptr[1] >> 4) & 0xf;
411 outspec[2] = hex + '0';
413 outspec[2] = (hex - 9) + 'A';
414 hex = ucs_ptr[1] & 0xF;
416 outspec[3] = hex + '0';
418 outspec[3] = (hex - 9) + 'A';
420 hex = (ucs_ptr[0] >> 4) & 0xf;
422 outspec[4] = hex + '0';
424 outspec[4] = (hex - 9) + 'A';
425 hex = ucs_ptr[1] & 0xF;
427 outspec[5] = hex + '0';
429 outspec[5] = (hex - 9) + 'A';
435 /* This handles the conversion of a UNIX extended character set to a ^
436 * escaped VMS character.
437 * in a UNIX file specification.
439 * The output count variable contains the number of characters added
440 * to the output string.
442 * The return value is the number of characters read from the input string
444 static int copy_expand_unix_filename_escape
445 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
453 utf8_flag = *utf8_fl;
457 if (*inspec >= 0x80) {
458 if (utf8_fl && vms_vtf7_filenames) {
459 unsigned long ucs_char;
463 if ((*inspec & 0xE0) == 0xC0) {
465 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
466 if (ucs_char >= 0x80) {
467 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
470 } else if ((*inspec & 0xF0) == 0xE0) {
472 ucs_char = ((inspec[0] & 0xF) << 12) +
473 ((inspec[1] & 0x3f) << 6) +
475 if (ucs_char >= 0x800) {
476 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
480 #if 0 /* I do not see longer sequences supported by OpenVMS */
481 /* Maybe some one can fix this later */
482 } else if ((*inspec & 0xF8) == 0xF0) {
485 } else if ((*inspec & 0xFC) == 0xF8) {
488 } else if ((*inspec & 0xFE) == 0xFC) {
495 /* High bit set, but not a Unicode character! */
497 /* Non printing DECMCS or ISO Latin-1 character? */
498 if (*inspec <= 0x9F) {
502 hex = (*inspec >> 4) & 0xF;
504 outspec[1] = hex + '0';
506 outspec[1] = (hex - 9) + 'A';
510 outspec[2] = hex + '0';
512 outspec[2] = (hex - 9) + 'A';
516 } else if (*inspec == 0xA0) {
522 } else if (*inspec == 0xFF) {
534 /* Is this a macro that needs to be passed through?
535 * Macros start with $( and an alpha character, followed
536 * by a string of alpha numeric characters ending with a )
537 * If this does not match, then encode it as ODS-5.
539 if ((inspec[0] == '$') && (inspec[1] == '(')) {
542 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
544 outspec[0] = inspec[0];
545 outspec[1] = inspec[1];
546 outspec[2] = inspec[2];
548 while(isalnum(inspec[tcnt]) ||
549 (inspec[2] == '.') || (inspec[2] == '_')) {
550 outspec[tcnt] = inspec[tcnt];
553 if (inspec[tcnt] == ')') {
554 outspec[tcnt] = inspec[tcnt];
571 if (decc_efs_charset == 0)
597 /* Don't escape again if following character is
598 * already something we escape.
600 if (strchr(".~!#&\'`()+@{},;[]%^=_", *(inspec+1))) {
606 /* But otherwise fall through and escape it. */
608 /* Assume that this is to be escaped */
610 outspec[1] = *inspec;
614 case ' ': /* space */
615 /* Assume that this is to be escaped */
630 /* This handles the expansion of a '^' prefix to the proper character
631 * in a UNIX file specification.
633 * The output count variable contains the number of characters added
634 * to the output string.
636 * The return value is the number of characters read from the input
639 static int copy_expand_vms_filename_escape
640 (char *outspec, const char *inspec, int *output_cnt)
647 if (*inspec == '^') {
650 /* Spaces and non-trailing dots should just be passed through,
651 * but eat the escape character.
658 case '_': /* space */
664 /* Hmm. Better leave the escape escaped. */
670 case 'U': /* Unicode - FIX-ME this is wrong. */
673 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
676 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
677 outspec[0] == c1 & 0xff;
678 outspec[1] == c2 & 0xff;
685 /* Error - do best we can to continue */
695 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
699 scnt = sscanf(inspec, "%2x", &c1);
700 outspec[0] = c1 & 0xff;
724 (const struct dsc$descriptor_s * srcstr,
725 struct filescan_itmlst_2 * valuelist,
726 unsigned long * fldflags,
727 struct dsc$descriptor_s *auxout,
728 unsigned short * retlen);
731 /* vms_split_path - Verify that the input file specification is a
732 * VMS format file specification, and provide pointers to the components of
733 * it. With EFS format filenames, this is virtually the only way to
734 * parse a VMS path specification into components.
736 * If the sum of the components do not add up to the length of the
737 * string, then the passed file specification is probably a UNIX style
740 static int vms_split_path
755 struct dsc$descriptor path_desc;
759 struct filescan_itmlst_2 item_list[9];
760 const int filespec = 0;
761 const int nodespec = 1;
762 const int devspec = 2;
763 const int rootspec = 3;
764 const int dirspec = 4;
765 const int namespec = 5;
766 const int typespec = 6;
767 const int verspec = 7;
769 /* Assume the worst for an easy exit */
784 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
785 path_desc.dsc$w_length = strlen(path);
786 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
787 path_desc.dsc$b_class = DSC$K_CLASS_S;
789 /* Get the total length, if it is shorter than the string passed
790 * then this was probably not a VMS formatted file specification
792 item_list[filespec].itmcode = FSCN$_FILESPEC;
793 item_list[filespec].length = 0;
794 item_list[filespec].component = NULL;
796 /* If the node is present, then it gets considered as part of the
797 * volume name to hopefully make things simple.
799 item_list[nodespec].itmcode = FSCN$_NODE;
800 item_list[nodespec].length = 0;
801 item_list[nodespec].component = NULL;
803 item_list[devspec].itmcode = FSCN$_DEVICE;
804 item_list[devspec].length = 0;
805 item_list[devspec].component = NULL;
807 /* root is a special case, adding it to either the directory or
808 * the device components will probalby complicate things for the
809 * callers of this routine, so leave it separate.
811 item_list[rootspec].itmcode = FSCN$_ROOT;
812 item_list[rootspec].length = 0;
813 item_list[rootspec].component = NULL;
815 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
816 item_list[dirspec].length = 0;
817 item_list[dirspec].component = NULL;
819 item_list[namespec].itmcode = FSCN$_NAME;
820 item_list[namespec].length = 0;
821 item_list[namespec].component = NULL;
823 item_list[typespec].itmcode = FSCN$_TYPE;
824 item_list[typespec].length = 0;
825 item_list[typespec].component = NULL;
827 item_list[verspec].itmcode = FSCN$_VERSION;
828 item_list[verspec].length = 0;
829 item_list[verspec].component = NULL;
831 item_list[8].itmcode = 0;
832 item_list[8].length = 0;
833 item_list[8].component = NULL;
835 status = sys$filescan
836 ((const struct dsc$descriptor_s *)&path_desc, item_list,
838 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
840 /* If we parsed it successfully these two lengths should be the same */
841 if (path_desc.dsc$w_length != item_list[filespec].length)
844 /* If we got here, then it is a VMS file specification */
847 /* set the volume name */
848 if (item_list[nodespec].length > 0) {
849 *volume = item_list[nodespec].component;
850 *vol_len = item_list[nodespec].length + item_list[devspec].length;
853 *volume = item_list[devspec].component;
854 *vol_len = item_list[devspec].length;
857 *root = item_list[rootspec].component;
858 *root_len = item_list[rootspec].length;
860 *dir = item_list[dirspec].component;
861 *dir_len = item_list[dirspec].length;
863 /* Now fun with versions and EFS file specifications
864 * The parser can not tell the difference when a "." is a version
865 * delimiter or a part of the file specification.
867 if ((decc_efs_charset) &&
868 (item_list[verspec].length > 0) &&
869 (item_list[verspec].component[0] == '.')) {
870 *name = item_list[namespec].component;
871 *name_len = item_list[namespec].length + item_list[typespec].length;
872 *ext = item_list[verspec].component;
873 *ext_len = item_list[verspec].length;
878 *name = item_list[namespec].component;
879 *name_len = item_list[namespec].length;
880 *ext = item_list[typespec].component;
881 *ext_len = item_list[typespec].length;
882 *version = item_list[verspec].component;
883 *ver_len = item_list[verspec].length;
890 * Routine to retrieve the maximum equivalence index for an input
891 * logical name. Some calls to this routine have no knowledge if
892 * the variable is a logical or not. So on error we return a max
895 /*{{{int my_maxidx(const char *lnm) */
897 my_maxidx(const char *lnm)
901 int attr = LNM$M_CASE_BLIND;
902 struct dsc$descriptor lnmdsc;
903 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
906 lnmdsc.dsc$w_length = strlen(lnm);
907 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
908 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
909 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
911 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
912 if ((status & 1) == 0)
919 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
921 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
922 struct dsc$descriptor_s **tabvec, unsigned long int flags)
925 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
926 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
927 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
929 unsigned char acmode;
930 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
931 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
932 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
933 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
935 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
936 #if defined(PERL_IMPLICIT_CONTEXT)
939 aTHX = PERL_GET_INTERP;
945 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
946 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
948 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
949 *cp2 = _toupper(*cp1);
950 if (cp1 - lnm > LNM$C_NAMLENGTH) {
951 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
955 lnmdsc.dsc$w_length = cp1 - lnm;
956 lnmdsc.dsc$a_pointer = uplnm;
957 uplnm[lnmdsc.dsc$w_length] = '\0';
958 secure = flags & PERL__TRNENV_SECURE;
959 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
960 if (!tabvec || !*tabvec) tabvec = env_tables;
962 for (curtab = 0; tabvec[curtab]; curtab++) {
963 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
964 if (!ivenv && !secure) {
969 Perl_warn(aTHX_ "Can't read CRTL environ\n");
972 retsts = SS$_NOLOGNAM;
973 for (i = 0; environ[i]; i++) {
974 if ((eq = strchr(environ[i],'=')) &&
975 lnmdsc.dsc$w_length == (eq - environ[i]) &&
976 !strncmp(environ[i],uplnm,eq - environ[i])) {
978 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
979 if (!eqvlen) continue;
984 if (retsts != SS$_NOLOGNAM) break;
987 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
988 !str$case_blind_compare(&tmpdsc,&clisym)) {
989 if (!ivsym && !secure) {
990 unsigned short int deflen = LNM$C_NAMLENGTH;
991 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
992 /* dynamic dsc to accomodate possible long value */
993 _ckvmssts(lib$sget1_dd(&deflen,&eqvdsc));
994 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
996 if (eqvlen > MAX_DCL_SYMBOL) {
997 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
998 eqvlen = MAX_DCL_SYMBOL;
999 /* Special hack--we might be called before the interpreter's */
1000 /* fully initialized, in which case either thr or PL_curcop */
1001 /* might be bogus. We have to check, since ckWARN needs them */
1002 /* both to be valid if running threaded */
1003 if (ckWARN(WARN_MISC)) {
1004 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
1007 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
1009 _ckvmssts(lib$sfree1_dd(&eqvdsc));
1010 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1011 if (retsts == LIB$_NOSUCHSYM) continue;
1016 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
1017 midx = my_maxidx(lnm);
1018 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1019 lnmlst[1].bufadr = cp2;
1021 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1022 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1023 if (retsts == SS$_NOLOGNAM) break;
1024 /* PPFs have a prefix */
1027 *((int *)uplnm) == *((int *)"SYS$") &&
1029 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
1030 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
1031 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
1032 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
1033 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
1034 memmove(eqv,eqv+4,eqvlen-4);
1040 if ((retsts == SS$_IVLOGNAM) ||
1041 (retsts == SS$_NOLOGNAM)) { continue; }
1044 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1045 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1046 if (retsts == SS$_NOLOGNAM) continue;
1049 eqvlen = strlen(eqv);
1053 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1054 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1055 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
1056 retsts == SS$_NOLOGNAM) {
1057 set_errno(EINVAL); set_vaxc_errno(retsts);
1059 else _ckvmssts(retsts);
1061 } /* end of vmstrnenv */
1064 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1065 /* Define as a function so we can access statics. */
1066 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1068 return vmstrnenv(lnm,eqv,idx,fildev,
1069 #ifdef SECURE_INTERNAL_GETENV
1070 (PL_curinterp ? PL_tainting : will_taint) ? PERL__TRNENV_SECURE : 0
1079 * Note: Uses Perl temp to store result so char * can be returned to
1080 * caller; this pointer will be invalidated at next Perl statement
1082 * We define this as a function rather than a macro in terms of my_getenv_len()
1083 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1086 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1088 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1091 static char *__my_getenv_eqv = NULL;
1092 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1093 unsigned long int idx = 0;
1094 int trnsuccess, success, secure, saverr, savvmserr;
1098 midx = my_maxidx(lnm) + 1;
1100 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1101 /* Set up a temporary buffer for the return value; Perl will
1102 * clean it up at the next statement transition */
1103 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1104 if (!tmpsv) return NULL;
1108 /* Assume no interpreter ==> single thread */
1109 if (__my_getenv_eqv != NULL) {
1110 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1113 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1115 eqv = __my_getenv_eqv;
1118 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1119 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1121 getcwd(eqv,LNM$C_NAMLENGTH);
1125 /* Get rid of "000000/ in rooted filespecs */
1128 zeros = strstr(eqv, "/000000/");
1129 if (zeros != NULL) {
1131 mlen = len - (zeros - eqv) - 7;
1132 memmove(zeros, &zeros[7], mlen);
1140 /* Impose security constraints only if tainting */
1142 /* Impose security constraints only if tainting */
1143 secure = PL_curinterp ? PL_tainting : will_taint;
1144 saverr = errno; savvmserr = vaxc$errno;
1151 #ifdef SECURE_INTERNAL_GETENV
1152 secure ? PERL__TRNENV_SECURE : 0
1158 /* For the getenv interface we combine all the equivalence names
1159 * of a search list logical into one value to acquire a maximum
1160 * value length of 255*128 (assuming %ENV is using logicals).
1162 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1164 /* If the name contains a semicolon-delimited index, parse it
1165 * off and make sure we only retrieve the equivalence name for
1167 if ((cp2 = strchr(lnm,';')) != NULL) {
1169 uplnm[cp2-lnm] = '\0';
1170 idx = strtoul(cp2+1,NULL,0);
1172 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1175 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1177 /* Discard NOLOGNAM on internal calls since we're often looking
1178 * for an optional name, and this "error" often shows up as the
1179 * (bogus) exit status for a die() call later on. */
1180 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1181 return success ? eqv : NULL;
1184 } /* end of my_getenv() */
1188 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1190 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1194 unsigned long idx = 0;
1196 static char *__my_getenv_len_eqv = NULL;
1197 int secure, saverr, savvmserr;
1200 midx = my_maxidx(lnm) + 1;
1202 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1203 /* Set up a temporary buffer for the return value; Perl will
1204 * clean it up at the next statement transition */
1205 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1206 if (!tmpsv) return NULL;
1210 /* Assume no interpreter ==> single thread */
1211 if (__my_getenv_len_eqv != NULL) {
1212 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1215 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1217 buf = __my_getenv_len_eqv;
1220 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1221 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1224 getcwd(buf,LNM$C_NAMLENGTH);
1227 /* Get rid of "000000/ in rooted filespecs */
1229 zeros = strstr(buf, "/000000/");
1230 if (zeros != NULL) {
1232 mlen = *len - (zeros - buf) - 7;
1233 memmove(zeros, &zeros[7], mlen);
1242 /* Impose security constraints only if tainting */
1243 secure = PL_curinterp ? PL_tainting : will_taint;
1244 saverr = errno; savvmserr = vaxc$errno;
1251 #ifdef SECURE_INTERNAL_GETENV
1252 secure ? PERL__TRNENV_SECURE : 0
1258 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1260 if ((cp2 = strchr(lnm,';')) != NULL) {
1262 buf[cp2-lnm] = '\0';
1263 idx = strtoul(cp2+1,NULL,0);
1265 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1268 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1270 /* Get rid of "000000/ in rooted filespecs */
1273 zeros = strstr(buf, "/000000/");
1274 if (zeros != NULL) {
1276 mlen = *len - (zeros - buf) - 7;
1277 memmove(zeros, &zeros[7], mlen);
1283 /* Discard NOLOGNAM on internal calls since we're often looking
1284 * for an optional name, and this "error" often shows up as the
1285 * (bogus) exit status for a die() call later on. */
1286 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1287 return *len ? buf : NULL;
1290 } /* end of my_getenv_len() */
1293 static void create_mbx(pTHX_ unsigned short int *, struct dsc$descriptor_s *);
1295 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1297 /*{{{ void prime_env_iter() */
1299 prime_env_iter(void)
1300 /* Fill the %ENV associative array with all logical names we can
1301 * find, in preparation for iterating over it.
1304 static int primed = 0;
1305 HV *seenhv = NULL, *envhv;
1307 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1308 unsigned short int chan;
1309 #ifndef CLI$M_TRUSTED
1310 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1312 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1313 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
1315 bool have_sym = FALSE, have_lnm = FALSE;
1316 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1317 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1318 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1319 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1320 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1321 #if defined(PERL_IMPLICIT_CONTEXT)
1324 #if defined(USE_ITHREADS)
1325 static perl_mutex primenv_mutex;
1326 MUTEX_INIT(&primenv_mutex);
1329 #if defined(PERL_IMPLICIT_CONTEXT)
1330 /* We jump through these hoops because we can be called at */
1331 /* platform-specific initialization time, which is before anything is */
1332 /* set up--we can't even do a plain dTHX since that relies on the */
1333 /* interpreter structure to be initialized */
1335 aTHX = PERL_GET_INTERP;
1341 if (primed || !PL_envgv) return;
1342 MUTEX_LOCK(&primenv_mutex);
1343 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1344 envhv = GvHVn(PL_envgv);
1345 /* Perform a dummy fetch as an lval to insure that the hash table is
1346 * set up. Otherwise, the hv_store() will turn into a nullop. */
1347 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1349 for (i = 0; env_tables[i]; i++) {
1350 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1351 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1352 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1354 if (have_sym || have_lnm) {
1355 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1356 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1357 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1358 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1361 for (i--; i >= 0; i--) {
1362 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1365 for (j = 0; environ[j]; j++) {
1366 if (!(start = strchr(environ[j],'='))) {
1367 if (ckWARN(WARN_INTERNAL))
1368 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1372 sv = newSVpv(start,0);
1374 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1379 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1380 !str$case_blind_compare(&tmpdsc,&clisym)) {
1381 strcpy(cmd,"Show Symbol/Global *");
1382 cmddsc.dsc$w_length = 20;
1383 if (env_tables[i]->dsc$w_length == 12 &&
1384 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1385 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
1386 flags = defflags | CLI$M_NOLOGNAM;
1389 strcpy(cmd,"Show Logical *");
1390 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1391 strcat(cmd," /Table=");
1392 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1393 cmddsc.dsc$w_length = strlen(cmd);
1395 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1396 flags = defflags | CLI$M_NOCLISYM;
1399 /* Create a new subprocess to execute each command, to exclude the
1400 * remote possibility that someone could subvert a mbx or file used
1401 * to write multiple commands to a single subprocess.
1404 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1405 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1406 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1407 defflags &= ~CLI$M_TRUSTED;
1408 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1410 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1411 if (seenhv) SvREFCNT_dec(seenhv);
1414 char *cp1, *cp2, *key;
1415 unsigned long int sts, iosb[2], retlen, keylen;
1418 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1419 if (sts & 1) sts = iosb[0] & 0xffff;
1420 if (sts == SS$_ENDOFFILE) {
1422 while (substs == 0) { sys$hiber(); wakect++;}
1423 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1428 retlen = iosb[0] >> 16;
1429 if (!retlen) continue; /* blank line */
1431 if (iosb[1] != subpid) {
1433 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1437 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1438 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1440 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1441 if (*cp1 == '(' || /* Logical name table name */
1442 *cp1 == '=' /* Next eqv of searchlist */) continue;
1443 if (*cp1 == '"') cp1++;
1444 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1445 key = cp1; keylen = cp2 - cp1;
1446 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1447 while (*cp2 && *cp2 != '=') cp2++;
1448 while (*cp2 && *cp2 == '=') cp2++;
1449 while (*cp2 && *cp2 == ' ') cp2++;
1450 if (*cp2 == '"') { /* String translation; may embed "" */
1451 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1452 cp2++; cp1--; /* Skip "" surrounding translation */
1454 else { /* Numeric translation */
1455 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1456 cp1--; /* stop on last non-space char */
1458 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1459 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1462 PERL_HASH(hash,key,keylen);
1464 if (cp1 == cp2 && *cp2 == '.') {
1465 /* A single dot usually means an unprintable character, such as a null
1466 * to indicate a zero-length value. Get the actual value to make sure.
1468 char lnm[LNM$C_NAMLENGTH+1];
1469 char eqv[MAX_DCL_SYMBOL+1];
1471 strncpy(lnm, key, keylen);
1472 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1473 sv = newSVpvn(eqv, strlen(eqv));
1476 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1480 hv_store(envhv,key,keylen,sv,hash);
1481 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1483 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1484 /* get the PPFs for this process, not the subprocess */
1485 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1486 char eqv[LNM$C_NAMLENGTH+1];
1488 for (i = 0; ppfs[i]; i++) {
1489 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1490 sv = newSVpv(eqv,trnlen);
1492 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1497 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1498 if (buf) Safefree(buf);
1499 if (seenhv) SvREFCNT_dec(seenhv);
1500 MUTEX_UNLOCK(&primenv_mutex);
1503 } /* end of prime_env_iter */
1507 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1508 /* Define or delete an element in the same "environment" as
1509 * vmstrnenv(). If an element is to be deleted, it's removed from
1510 * the first place it's found. If it's to be set, it's set in the
1511 * place designated by the first element of the table vector.
1512 * Like setenv() returns 0 for success, non-zero on error.
1515 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1518 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1519 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1521 unsigned long int retsts, usermode = PSL$C_USER;
1522 struct itmlst_3 *ile, *ilist;
1523 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1524 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1525 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1526 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1527 $DESCRIPTOR(local,"_LOCAL");
1530 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1531 return SS$_IVLOGNAM;
1534 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1535 *cp2 = _toupper(*cp1);
1536 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1537 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1538 return SS$_IVLOGNAM;
1541 lnmdsc.dsc$w_length = cp1 - lnm;
1542 if (!tabvec || !*tabvec) tabvec = env_tables;
1544 if (!eqv) { /* we're deleting n element */
1545 for (curtab = 0; tabvec[curtab]; curtab++) {
1546 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1548 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1549 if ((cp1 = strchr(environ[i],'=')) &&
1550 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1551 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1553 return setenv(lnm,"",1) ? vaxc$errno : 0;
1556 ivenv = 1; retsts = SS$_NOLOGNAM;
1558 if (ckWARN(WARN_INTERNAL))
1559 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1560 ivenv = 1; retsts = SS$_NOSUCHPGM;
1566 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1567 !str$case_blind_compare(&tmpdsc,&clisym)) {
1568 unsigned int symtype;
1569 if (tabvec[curtab]->dsc$w_length == 12 &&
1570 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1571 !str$case_blind_compare(&tmpdsc,&local))
1572 symtype = LIB$K_CLI_LOCAL_SYM;
1573 else symtype = LIB$K_CLI_GLOBAL_SYM;
1574 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1575 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1576 if (retsts == LIB$_NOSUCHSYM) continue;
1580 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1581 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1582 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1583 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1584 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1588 else { /* we're defining a value */
1589 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1591 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1593 if (ckWARN(WARN_INTERNAL))
1594 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1595 retsts = SS$_NOSUCHPGM;
1599 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1600 eqvdsc.dsc$w_length = strlen(eqv);
1601 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1602 !str$case_blind_compare(&tmpdsc,&clisym)) {
1603 unsigned int symtype;
1604 if (tabvec[0]->dsc$w_length == 12 &&
1605 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1606 !str$case_blind_compare(&tmpdsc,&local))
1607 symtype = LIB$K_CLI_LOCAL_SYM;
1608 else symtype = LIB$K_CLI_GLOBAL_SYM;
1609 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1612 if (!*eqv) eqvdsc.dsc$w_length = 1;
1613 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1615 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1616 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1617 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1618 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1619 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1620 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1623 Newx(ilist,nseg+1,struct itmlst_3);
1626 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1629 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1631 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1632 ile->itmcode = LNM$_STRING;
1634 if ((j+1) == nseg) {
1635 ile->buflen = strlen(c);
1636 /* in case we are truncating one that's too long */
1637 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1640 ile->buflen = LNM$C_NAMLENGTH;
1644 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1648 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1653 if (!(retsts & 1)) {
1655 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1656 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1657 set_errno(EVMSERR); break;
1658 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1659 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1660 set_errno(EINVAL); break;
1662 set_errno(EACCES); break;
1667 set_vaxc_errno(retsts);
1668 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1671 /* We reset error values on success because Perl does an hv_fetch()
1672 * before each hv_store(), and if the thing we're setting didn't
1673 * previously exist, we've got a leftover error message. (Of course,
1674 * this fails in the face of
1675 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1676 * in that the error reported in $! isn't spurious,
1677 * but it's right more often than not.)
1679 set_errno(0); set_vaxc_errno(retsts);
1683 } /* end of vmssetenv() */
1686 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1687 /* This has to be a function since there's a prototype for it in proto.h */
1689 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1692 int len = strlen(lnm);
1696 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1697 if (!strcmp(uplnm,"DEFAULT")) {
1698 if (eqv && *eqv) my_chdir(eqv);
1702 #ifndef RTL_USES_UTC
1703 if (len == 6 || len == 2) {
1706 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1708 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1709 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1713 (void) vmssetenv(lnm,eqv,NULL);
1717 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1719 * sets a user-mode logical in the process logical name table
1720 * used for redirection of sys$error
1723 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1725 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1726 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1727 unsigned long int iss, attr = LNM$M_CONFINE;
1728 unsigned char acmode = PSL$C_USER;
1729 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1731 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1732 d_name.dsc$w_length = strlen(name);
1734 lnmlst[0].buflen = strlen(eqv);
1735 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1737 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1738 if (!(iss&1)) lib$signal(iss);
1743 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1744 /* my_crypt - VMS password hashing
1745 * my_crypt() provides an interface compatible with the Unix crypt()
1746 * C library function, and uses sys$hash_password() to perform VMS
1747 * password hashing. The quadword hashed password value is returned
1748 * as a NUL-terminated 8 character string. my_crypt() does not change
1749 * the case of its string arguments; in order to match the behavior
1750 * of LOGINOUT et al., alphabetic characters in both arguments must
1751 * be upcased by the caller.
1753 * - fix me to call ACM services when available
1756 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1758 # ifndef UAI$C_PREFERRED_ALGORITHM
1759 # define UAI$C_PREFERRED_ALGORITHM 127
1761 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1762 unsigned short int salt = 0;
1763 unsigned long int sts;
1765 unsigned short int dsc$w_length;
1766 unsigned char dsc$b_type;
1767 unsigned char dsc$b_class;
1768 const char * dsc$a_pointer;
1769 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1770 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1771 struct itmlst_3 uailst[3] = {
1772 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1773 { sizeof salt, UAI$_SALT, &salt, 0},
1774 { 0, 0, NULL, NULL}};
1775 static char hash[9];
1777 usrdsc.dsc$w_length = strlen(usrname);
1778 usrdsc.dsc$a_pointer = usrname;
1779 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1781 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1785 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1790 set_vaxc_errno(sts);
1791 if (sts != RMS$_RNF) return NULL;
1794 txtdsc.dsc$w_length = strlen(textpasswd);
1795 txtdsc.dsc$a_pointer = textpasswd;
1796 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1797 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1800 return (char *) hash;
1802 } /* end of my_crypt() */
1806 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1807 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1808 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1810 /* fixup barenames that are directories for internal use.
1811 * There have been problems with the consistent handling of UNIX
1812 * style directory names when routines are presented with a name that
1813 * has no directory delimitors at all. So this routine will eventually
1816 static char * fixup_bare_dirnames(const char * name)
1818 if (decc_disable_to_vms_logname_translation) {
1824 /* 8.3, remove() is now broken on symbolic links */
1825 static int rms_erase(const char * vmsname);
1829 * A little hack to get around a bug in some implemenation of remove()
1830 * that do not know how to delete a directory
1832 * Delete any file to which user has control access, regardless of whether
1833 * delete access is explicitly allowed.
1834 * Limitations: User must have write access to parent directory.
1835 * Does not block signals or ASTs; if interrupted in midstream
1836 * may leave file with an altered ACL.
1839 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1841 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1845 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1846 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1847 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1849 unsigned char myace$b_length;
1850 unsigned char myace$b_type;
1851 unsigned short int myace$w_flags;
1852 unsigned long int myace$l_access;
1853 unsigned long int myace$l_ident;
1854 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1855 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1856 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1858 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1859 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1860 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1861 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1862 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1863 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1865 /* Expand the input spec using RMS, since the CRTL remove() and
1866 * system services won't do this by themselves, so we may miss
1867 * a file "hiding" behind a logical name or search list. */
1868 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
1869 if (vmsname == NULL) _ckvmssts(SS$_INSFMEM);
1871 rslt = do_rmsexpand(name,
1875 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_SYMLINK,
1879 PerlMem_free(vmsname);
1883 /* Erase the file */
1884 rmsts = rms_erase(vmsname);
1886 /* Did it succeed */
1887 if ($VMS_STATUS_SUCCESS(rmsts)) {
1888 PerlMem_free(vmsname);
1892 /* If not, can changing protections help? */
1893 if (rmsts != RMS$_PRV) {
1894 set_vaxc_errno(rmsts);
1895 PerlMem_free(vmsname);
1899 /* No, so we get our own UIC to use as a rights identifier,
1900 * and the insert an ACE at the head of the ACL which allows us
1901 * to delete the file.
1903 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1904 fildsc.dsc$w_length = strlen(vmsname);
1905 fildsc.dsc$a_pointer = vmsname;
1907 newace.myace$l_ident = oldace.myace$l_ident;
1909 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1911 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1912 set_errno(ENOENT); break;
1914 set_errno(ENOTDIR); break;
1916 set_errno(ENODEV); break;
1917 case RMS$_SYN: case SS$_INVFILFOROP:
1918 set_errno(EINVAL); break;
1920 set_errno(EACCES); break;
1924 set_vaxc_errno(aclsts);
1925 PerlMem_free(vmsname);
1928 /* Grab any existing ACEs with this identifier in case we fail */
1929 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1930 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1931 || fndsts == SS$_NOMOREACE ) {
1932 /* Add the new ACE . . . */
1933 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1936 rmsts = rms_erase(vmsname);
1937 if ($VMS_STATUS_SUCCESS(rmsts)) {
1942 /* We blew it - dir with files in it, no write priv for
1943 * parent directory, etc. Put things back the way they were. */
1944 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
1947 addlst[0].bufadr = &oldace;
1948 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
1955 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
1956 /* We just deleted it, so of course it's not there. Some versions of
1957 * VMS seem to return success on the unlock operation anyhow (after all
1958 * the unlock is successful), but others don't.
1960 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
1961 if (aclsts & 1) aclsts = fndsts;
1962 if (!(aclsts & 1)) {
1964 set_vaxc_errno(aclsts);
1967 PerlMem_free(vmsname);
1970 } /* end of kill_file() */
1974 /*{{{int do_rmdir(char *name)*/
1976 Perl_do_rmdir(pTHX_ const char *name)
1982 dirfile = PerlMem_malloc(VMS_MAXRSS + 1);
1983 if (dirfile == NULL)
1984 _ckvmssts(SS$_INSFMEM);
1986 /* Force to a directory specification */
1987 if (do_fileify_dirspec(name, dirfile, 0, NULL) == NULL) {
1988 PerlMem_free(dirfile);
1991 if (Perl_flex_lstat(aTHX_ dirfile, &st) || !S_ISDIR(st.st_mode)) {
1996 retval = mp_do_kill_file(aTHX_ dirfile, 1);
1998 PerlMem_free(dirfile);
2001 } /* end of do_rmdir */
2005 * Delete any file to which user has control access, regardless of whether
2006 * delete access is explicitly allowed.
2007 * Limitations: User must have write access to parent directory.
2008 * Does not block signals or ASTs; if interrupted in midstream
2009 * may leave file with an altered ACL.
2012 /*{{{int kill_file(char *name)*/
2014 Perl_kill_file(pTHX_ const char *name)
2016 char rspec[NAM$C_MAXRSS+1];
2021 /* Remove() is allowed to delete directories, according to the X/Open
2023 * This may need special handling to work with the ACL hacks.
2025 if ((flex_lstat(name, &st) == 0) && S_ISDIR(st.st_mode)) {
2026 rmsts = Perl_do_rmdir(aTHX_ name);
2030 rmsts = mp_do_kill_file(aTHX_ name, 0);
2034 } /* end of kill_file() */
2038 /*{{{int my_mkdir(char *,Mode_t)*/
2040 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2042 STRLEN dirlen = strlen(dir);
2044 /* zero length string sometimes gives ACCVIO */
2045 if (dirlen == 0) return -1;
2047 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2048 * null file name/type. However, it's commonplace under Unix,
2049 * so we'll allow it for a gain in portability.
2051 if (dir[dirlen-1] == '/') {
2052 char *newdir = savepvn(dir,dirlen-1);
2053 int ret = mkdir(newdir,mode);
2057 else return mkdir(dir,mode);
2058 } /* end of my_mkdir */
2061 /*{{{int my_chdir(char *)*/
2063 Perl_my_chdir(pTHX_ const char *dir)
2065 STRLEN dirlen = strlen(dir);
2067 /* zero length string sometimes gives ACCVIO */
2068 if (dirlen == 0) return -1;
2071 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2072 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2073 * so that existing scripts do not need to be changed.
2076 while ((dirlen > 0) && (*dir1 == ' ')) {
2081 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2083 * null file name/type. However, it's commonplace under Unix,
2084 * so we'll allow it for a gain in portability.
2086 * - Preview- '/' will be valid soon on VMS
2088 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2089 char *newdir = savepvn(dir1,dirlen-1);
2090 int ret = chdir(newdir);
2094 else return chdir(dir1);
2095 } /* end of my_chdir */
2099 /*{{{int my_chmod(char *, mode_t)*/
2101 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2103 STRLEN speclen = strlen(file_spec);
2105 /* zero length string sometimes gives ACCVIO */
2106 if (speclen == 0) return -1;
2108 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2109 * that implies null file name/type. However, it's commonplace under Unix,
2110 * so we'll allow it for a gain in portability.
2112 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2113 * in VMS file.dir notation.
2115 if ((speclen > 1) && (file_spec[speclen-1] == '/')) {
2116 char *vms_src, *vms_dir, *rslt;
2120 /* First convert this to a VMS format specification */
2121 vms_src = PerlMem_malloc(VMS_MAXRSS);
2122 if (vms_src == NULL)
2123 _ckvmssts(SS$_INSFMEM);
2125 rslt = do_tovmsspec(file_spec, vms_src, 0, NULL);
2127 /* If we fail, then not a file specification */
2128 PerlMem_free(vms_src);
2133 /* Now make it a directory spec so chmod is happy */
2134 vms_dir = PerlMem_malloc(VMS_MAXRSS + 1);
2135 if (vms_dir == NULL)
2136 _ckvmssts(SS$_INSFMEM);
2137 rslt = do_fileify_dirspec(vms_src, vms_dir, 0, NULL);
2138 PerlMem_free(vms_src);
2142 ret = chmod(vms_dir, mode);
2146 PerlMem_free(vms_dir);
2149 else return chmod(file_spec, mode);
2150 } /* end of my_chmod */
2154 /*{{{FILE *my_tmpfile()*/
2161 if ((fp = tmpfile())) return fp;
2163 cp = PerlMem_malloc(L_tmpnam+24);
2164 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2166 if (decc_filename_unix_only == 0)
2167 strcpy(cp,"Sys$Scratch:");
2170 tmpnam(cp+strlen(cp));
2171 strcat(cp,".Perltmp");
2172 fp = fopen(cp,"w+","fop=dlt");
2179 #ifndef HOMEGROWN_POSIX_SIGNALS
2181 * The C RTL's sigaction fails to check for invalid signal numbers so we
2182 * help it out a bit. The docs are correct, but the actual routine doesn't
2183 * do what the docs say it will.
2185 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2187 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2188 struct sigaction* oact)
2190 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2191 SETERRNO(EINVAL, SS$_INVARG);
2194 return sigaction(sig, act, oact);
2199 #ifdef KILL_BY_SIGPRC
2200 #include <errnodef.h>
2202 /* We implement our own kill() using the undocumented system service
2203 sys$sigprc for one of two reasons:
2205 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2206 target process to do a sys$exit, which usually can't be handled
2207 gracefully...certainly not by Perl and the %SIG{} mechanism.
2209 2.) If the kill() in the CRTL can't be called from a signal
2210 handler without disappearing into the ether, i.e., the signal
2211 it purportedly sends is never trapped. Still true as of VMS 7.3.
2213 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2214 in the target process rather than calling sys$exit.
2216 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2217 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2218 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2219 with condition codes C$_SIG0+nsig*8, catching the exception on the
2220 target process and resignaling with appropriate arguments.
2222 But we don't have that VMS 7.0+ exception handler, so if you
2223 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2225 Also note that SIGTERM is listed in the docs as being "unimplemented",
2226 yet always seems to be signaled with a VMS condition code of 4 (and
2227 correctly handled for that code). So we hardwire it in.
2229 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2230 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2231 than signalling with an unrecognized (and unhandled by CRTL) code.
2234 #define _MY_SIG_MAX 28
2237 Perl_sig_to_vmscondition_int(int sig)
2239 static unsigned int sig_code[_MY_SIG_MAX+1] =
2242 SS$_HANGUP, /* 1 SIGHUP */
2243 SS$_CONTROLC, /* 2 SIGINT */
2244 SS$_CONTROLY, /* 3 SIGQUIT */
2245 SS$_RADRMOD, /* 4 SIGILL */
2246 SS$_BREAK, /* 5 SIGTRAP */
2247 SS$_OPCCUS, /* 6 SIGABRT */
2248 SS$_COMPAT, /* 7 SIGEMT */
2250 SS$_FLTOVF, /* 8 SIGFPE VAX */
2252 SS$_HPARITH, /* 8 SIGFPE AXP */
2254 SS$_ABORT, /* 9 SIGKILL */
2255 SS$_ACCVIO, /* 10 SIGBUS */
2256 SS$_ACCVIO, /* 11 SIGSEGV */
2257 SS$_BADPARAM, /* 12 SIGSYS */
2258 SS$_NOMBX, /* 13 SIGPIPE */
2259 SS$_ASTFLT, /* 14 SIGALRM */
2276 #if __VMS_VER >= 60200000
2277 static int initted = 0;
2280 sig_code[16] = C$_SIGUSR1;
2281 sig_code[17] = C$_SIGUSR2;
2282 #if __CRTL_VER >= 70000000
2283 sig_code[20] = C$_SIGCHLD;
2285 #if __CRTL_VER >= 70300000
2286 sig_code[28] = C$_SIGWINCH;
2291 if (sig < _SIG_MIN) return 0;
2292 if (sig > _MY_SIG_MAX) return 0;
2293 return sig_code[sig];
2297 Perl_sig_to_vmscondition(int sig)
2300 if (vms_debug_on_exception != 0)
2301 lib$signal(SS$_DEBUG);
2303 return Perl_sig_to_vmscondition_int(sig);
2308 Perl_my_kill(int pid, int sig)
2313 int sys$sigprc(unsigned int *pidadr,
2314 struct dsc$descriptor_s *prcname,
2317 /* sig 0 means validate the PID */
2318 /*------------------------------*/
2320 const unsigned long int jpicode = JPI$_PID;
2323 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2324 if ($VMS_STATUS_SUCCESS(status))
2327 case SS$_NOSUCHNODE:
2328 case SS$_UNREACHABLE:
2342 code = Perl_sig_to_vmscondition_int(sig);
2345 SETERRNO(EINVAL, SS$_BADPARAM);
2349 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2350 * signals are to be sent to multiple processes.
2351 * pid = 0 - all processes in group except ones that the system exempts
2352 * pid = -1 - all processes except ones that the system exempts
2353 * pid = -n - all processes in group (abs(n)) except ...
2354 * For now, just report as not supported.
2358 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2362 iss = sys$sigprc((unsigned int *)&pid,0,code);
2363 if (iss&1) return 0;
2367 set_errno(EPERM); break;
2369 case SS$_NOSUCHNODE:
2370 case SS$_UNREACHABLE:
2371 set_errno(ESRCH); break;
2373 set_errno(ENOMEM); break;
2378 set_vaxc_errno(iss);
2384 /* Routine to convert a VMS status code to a UNIX status code.
2385 ** More tricky than it appears because of conflicting conventions with
2388 ** VMS status codes are a bit mask, with the least significant bit set for
2391 ** Special UNIX status of EVMSERR indicates that no translation is currently
2392 ** available, and programs should check the VMS status code.
2394 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2398 #ifndef C_FACILITY_NO
2399 #define C_FACILITY_NO 0x350000
2402 #define DCL_IVVERB 0x38090
2405 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2413 /* Assume the best or the worst */
2414 if (vms_status & STS$M_SUCCESS)
2417 unix_status = EVMSERR;
2419 msg_status = vms_status & ~STS$M_CONTROL;
2421 facility = vms_status & STS$M_FAC_NO;
2422 fac_sp = vms_status & STS$M_FAC_SP;
2423 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2425 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2431 unix_status = EFAULT;
2433 case SS$_DEVOFFLINE:
2434 unix_status = EBUSY;
2437 unix_status = ENOTCONN;
2445 case SS$_INVFILFOROP:
2449 unix_status = EINVAL;
2451 case SS$_UNSUPPORTED:
2452 unix_status = ENOTSUP;
2457 unix_status = EACCES;
2459 case SS$_DEVICEFULL:
2460 unix_status = ENOSPC;
2463 unix_status = ENODEV;
2465 case SS$_NOSUCHFILE:
2466 case SS$_NOSUCHOBJECT:
2467 unix_status = ENOENT;
2469 case SS$_ABORT: /* Fatal case */
2470 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2471 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2472 unix_status = EINTR;
2475 unix_status = E2BIG;
2478 unix_status = ENOMEM;
2481 unix_status = EPERM;
2483 case SS$_NOSUCHNODE:
2484 case SS$_UNREACHABLE:
2485 unix_status = ESRCH;
2488 unix_status = ECHILD;
2491 if ((facility == 0) && (msg_no < 8)) {
2492 /* These are not real VMS status codes so assume that they are
2493 ** already UNIX status codes
2495 unix_status = msg_no;
2501 /* Translate a POSIX exit code to a UNIX exit code */
2502 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2503 unix_status = (msg_no & 0x07F8) >> 3;
2507 /* Documented traditional behavior for handling VMS child exits */
2508 /*--------------------------------------------------------------*/
2509 if (child_flag != 0) {
2511 /* Success / Informational return 0 */
2512 /*----------------------------------*/
2513 if (msg_no & STS$K_SUCCESS)
2516 /* Warning returns 1 */
2517 /*-------------------*/
2518 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2521 /* Everything else pass through the severity bits */
2522 /*------------------------------------------------*/
2523 return (msg_no & STS$M_SEVERITY);
2526 /* Normal VMS status to ERRNO mapping attempt */
2527 /*--------------------------------------------*/
2528 switch(msg_status) {
2529 /* case RMS$_EOF: */ /* End of File */
2530 case RMS$_FNF: /* File Not Found */
2531 case RMS$_DNF: /* Dir Not Found */
2532 unix_status = ENOENT;
2534 case RMS$_RNF: /* Record Not Found */
2535 unix_status = ESRCH;
2538 unix_status = ENOTDIR;
2541 unix_status = ENODEV;
2546 unix_status = EBADF;
2549 unix_status = EEXIST;
2553 case LIB$_INVSTRDES:
2555 case LIB$_NOSUCHSYM:
2556 case LIB$_INVSYMNAM:
2558 unix_status = EINVAL;
2564 unix_status = E2BIG;
2566 case RMS$_PRV: /* No privilege */
2567 case RMS$_ACC: /* ACP file access failed */
2568 case RMS$_WLK: /* Device write locked */
2569 unix_status = EACCES;
2571 case RMS$_MKD: /* Failed to mark for delete */
2572 unix_status = EPERM;
2574 /* case RMS$_NMF: */ /* No more files */
2582 /* Try to guess at what VMS error status should go with a UNIX errno
2583 * value. This is hard to do as there could be many possible VMS
2584 * error statuses that caused the errno value to be set.
2587 int Perl_unix_status_to_vms(int unix_status)
2589 int test_unix_status;
2591 /* Trivial cases first */
2592 /*---------------------*/
2593 if (unix_status == EVMSERR)
2596 /* Is vaxc$errno sane? */
2597 /*---------------------*/
2598 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2599 if (test_unix_status == unix_status)
2602 /* If way out of range, must be VMS code already */
2603 /*-----------------------------------------------*/
2604 if (unix_status > EVMSERR)
2607 /* If out of range, punt */
2608 /*-----------------------*/
2609 if (unix_status > __ERRNO_MAX)
2613 /* Ok, now we have to do it the hard way. */
2614 /*----------------------------------------*/
2615 switch(unix_status) {
2616 case 0: return SS$_NORMAL;
2617 case EPERM: return SS$_NOPRIV;
2618 case ENOENT: return SS$_NOSUCHOBJECT;
2619 case ESRCH: return SS$_UNREACHABLE;
2620 case EINTR: return SS$_ABORT;
2623 case E2BIG: return SS$_BUFFEROVF;
2625 case EBADF: return RMS$_IFI;
2626 case ECHILD: return SS$_NONEXPR;
2628 case ENOMEM: return SS$_INSFMEM;
2629 case EACCES: return SS$_FILACCERR;
2630 case EFAULT: return SS$_ACCVIO;
2632 case EBUSY: return SS$_DEVOFFLINE;
2633 case EEXIST: return RMS$_FEX;
2635 case ENODEV: return SS$_NOSUCHDEV;
2636 case ENOTDIR: return RMS$_DIR;
2638 case EINVAL: return SS$_INVARG;
2644 case ENOSPC: return SS$_DEVICEFULL;
2645 case ESPIPE: return LIB$_INVARG;
2650 case ERANGE: return LIB$_INVARG;
2651 /* case EWOULDBLOCK */
2652 /* case EINPROGRESS */
2655 /* case EDESTADDRREQ */
2657 /* case EPROTOTYPE */
2658 /* case ENOPROTOOPT */
2659 /* case EPROTONOSUPPORT */
2660 /* case ESOCKTNOSUPPORT */
2661 /* case EOPNOTSUPP */
2662 /* case EPFNOSUPPORT */
2663 /* case EAFNOSUPPORT */
2664 /* case EADDRINUSE */
2665 /* case EADDRNOTAVAIL */
2667 /* case ENETUNREACH */
2668 /* case ENETRESET */
2669 /* case ECONNABORTED */
2670 /* case ECONNRESET */
2673 case ENOTCONN: return SS$_CLEARED;
2674 /* case ESHUTDOWN */
2675 /* case ETOOMANYREFS */
2676 /* case ETIMEDOUT */
2677 /* case ECONNREFUSED */
2679 /* case ENAMETOOLONG */
2680 /* case EHOSTDOWN */
2681 /* case EHOSTUNREACH */
2682 /* case ENOTEMPTY */
2694 /* case ECANCELED */
2698 return SS$_UNSUPPORTED;
2704 /* case EABANDONED */
2706 return SS$_ABORT; /* punt */
2709 return SS$_ABORT; /* Should not get here */
2713 /* default piping mailbox size */
2714 #define PERL_BUFSIZ 512
2718 create_mbx(pTHX_ unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2720 unsigned long int mbxbufsiz;
2721 static unsigned long int syssize = 0;
2722 unsigned long int dviitm = DVI$_DEVNAM;
2723 char csize[LNM$C_NAMLENGTH+1];
2727 unsigned long syiitm = SYI$_MAXBUF;
2729 * Get the SYSGEN parameter MAXBUF
2731 * If the logical 'PERL_MBX_SIZE' is defined
2732 * use the value of the logical instead of PERL_BUFSIZ, but
2733 * keep the size between 128 and MAXBUF.
2736 _ckvmssts(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2739 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2740 mbxbufsiz = atoi(csize);
2742 mbxbufsiz = PERL_BUFSIZ;
2744 if (mbxbufsiz < 128) mbxbufsiz = 128;
2745 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2747 _ckvmssts(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2749 _ckvmssts(sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length));
2750 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2752 } /* end of create_mbx() */
2755 /*{{{ my_popen and my_pclose*/
2757 typedef struct _iosb IOSB;
2758 typedef struct _iosb* pIOSB;
2759 typedef struct _pipe Pipe;
2760 typedef struct _pipe* pPipe;
2761 typedef struct pipe_details Info;
2762 typedef struct pipe_details* pInfo;
2763 typedef struct _srqp RQE;
2764 typedef struct _srqp* pRQE;
2765 typedef struct _tochildbuf CBuf;
2766 typedef struct _tochildbuf* pCBuf;
2769 unsigned short status;
2770 unsigned short count;
2771 unsigned long dvispec;
2774 #pragma member_alignment save
2775 #pragma nomember_alignment quadword
2776 struct _srqp { /* VMS self-relative queue entry */
2777 unsigned long qptr[2];
2779 #pragma member_alignment restore
2780 static RQE RQE_ZERO = {0,0};
2782 struct _tochildbuf {
2785 unsigned short size;
2793 unsigned short chan_in;
2794 unsigned short chan_out;
2796 unsigned int bufsize;
2808 #if defined(PERL_IMPLICIT_CONTEXT)
2809 void *thx; /* Either a thread or an interpreter */
2810 /* pointer, depending on how we're built */
2818 PerlIO *fp; /* file pointer to pipe mailbox */
2819 int useFILE; /* using stdio, not perlio */
2820 int pid; /* PID of subprocess */
2821 int mode; /* == 'r' if pipe open for reading */
2822 int done; /* subprocess has completed */
2823 int waiting; /* waiting for completion/closure */
2824 int closing; /* my_pclose is closing this pipe */
2825 unsigned long completion; /* termination status of subprocess */
2826 pPipe in; /* pipe in to sub */
2827 pPipe out; /* pipe out of sub */
2828 pPipe err; /* pipe of sub's sys$error */
2829 int in_done; /* true when in pipe finished */
2832 unsigned short xchan; /* channel to debug xterm */
2833 unsigned short xchan_valid; /* channel is assigned */
2836 struct exit_control_block
2838 struct exit_control_block *flink;
2839 unsigned long int (*exit_routine)();
2840 unsigned long int arg_count;
2841 unsigned long int *status_address;
2842 unsigned long int exit_status;
2845 typedef struct _closed_pipes Xpipe;
2846 typedef struct _closed_pipes* pXpipe;
2848 struct _closed_pipes {
2849 int pid; /* PID of subprocess */
2850 unsigned long completion; /* termination status of subprocess */
2852 #define NKEEPCLOSED 50
2853 static Xpipe closed_list[NKEEPCLOSED];
2854 static int closed_index = 0;
2855 static int closed_num = 0;
2857 #define RETRY_DELAY "0 ::0.20"
2858 #define MAX_RETRY 50
2860 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2861 static unsigned long mypid;
2862 static unsigned long delaytime[2];
2864 static pInfo open_pipes = NULL;
2865 static $DESCRIPTOR(nl_desc, "NL:");
2867 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2871 static unsigned long int
2872 pipe_exit_routine(pTHX)
2875 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2876 int sts, did_stuff, need_eof, j;
2879 * Flush any pending i/o, but since we are in process run-down, be
2880 * careful about referencing PerlIO structures that may already have
2881 * been deallocated. We may not even have an interpreter anymore.
2887 #if defined(USE_ITHREADS)
2890 && PL_perlio_fd_refcnt)
2891 PerlIO_flush(info->fp);
2893 fflush((FILE *)info->fp);
2899 next we try sending an EOF...ignore if doesn't work, make sure we
2907 _ckvmssts_noperl(sys$setast(0));
2908 if (info->in && !info->in->shut_on_empty) {
2909 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2914 _ckvmssts_noperl(sys$setast(1));
2918 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2920 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2925 _ckvmssts_noperl(sys$setast(0));
2926 if (info->waiting && info->done)
2928 nwait += info->waiting;
2929 _ckvmssts_noperl(sys$setast(1));
2939 _ckvmssts_noperl(sys$setast(0));
2940 if (!info->done) { /* Tap them gently on the shoulder . . .*/
2941 sts = sys$forcex(&info->pid,0,&abort);
2942 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2945 _ckvmssts_noperl(sys$setast(1));
2949 /* again, wait for effect */
2951 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2956 _ckvmssts_noperl(sys$setast(0));
2957 if (info->waiting && info->done)
2959 nwait += info->waiting;
2960 _ckvmssts_noperl(sys$setast(1));
2969 _ckvmssts_noperl(sys$setast(0));
2970 if (!info->done) { /* We tried to be nice . . . */
2971 sts = sys$delprc(&info->pid,0);
2972 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
2973 info->done = 1; /* sys$delprc is as done as we're going to get. */
2975 _ckvmssts_noperl(sys$setast(1));
2980 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
2981 else if (!(sts & 1)) retsts = sts;
2986 static struct exit_control_block pipe_exitblock =
2987 {(struct exit_control_block *) 0,
2988 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
2990 static void pipe_mbxtofd_ast(pPipe p);
2991 static void pipe_tochild1_ast(pPipe p);
2992 static void pipe_tochild2_ast(pPipe p);
2995 popen_completion_ast(pInfo info)
2997 pInfo i = open_pipes;
3002 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3003 closed_list[closed_index].pid = info->pid;
3004 closed_list[closed_index].completion = info->completion;
3006 if (closed_index == NKEEPCLOSED)
3011 if (i == info) break;
3014 if (!i) return; /* unlinked, probably freed too */
3019 Writing to subprocess ...
3020 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3022 chan_out may be waiting for "done" flag, or hung waiting
3023 for i/o completion to child...cancel the i/o. This will
3024 put it into "snarf mode" (done but no EOF yet) that discards
3027 Output from subprocess (stdout, stderr) needs to be flushed and
3028 shut down. We try sending an EOF, but if the mbx is full the pipe
3029 routine should still catch the "shut_on_empty" flag, telling it to
3030 use immediate-style reads so that "mbx empty" -> EOF.
3034 if (info->in && !info->in_done) { /* only for mode=w */
3035 if (info->in->shut_on_empty && info->in->need_wake) {
3036 info->in->need_wake = FALSE;
3037 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3039 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3043 if (info->out && !info->out_done) { /* were we also piping output? */
3044 info->out->shut_on_empty = TRUE;
3045 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3046 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3047 _ckvmssts_noperl(iss);
3050 if (info->err && !info->err_done) { /* we were piping stderr */
3051 info->err->shut_on_empty = TRUE;
3052 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3053 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3054 _ckvmssts_noperl(iss);
3056 _ckvmssts_noperl(sys$setef(pipe_ef));
3060 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3061 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3064 we actually differ from vmstrnenv since we use this to
3065 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
3066 are pointing to the same thing
3069 static unsigned short
3070 popen_translate(pTHX_ char *logical, char *result)
3073 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
3074 $DESCRIPTOR(d_log,"");
3076 unsigned short length;
3077 unsigned short code;
3079 unsigned short *retlenaddr;
3081 unsigned short l, ifi;
3083 d_log.dsc$a_pointer = logical;
3084 d_log.dsc$w_length = strlen(logical);
3086 itmlst[0].code = LNM$_STRING;
3087 itmlst[0].length = 255;
3088 itmlst[0].buffer_addr = result;
3089 itmlst[0].retlenaddr = &l;
3092 itmlst[1].length = 0;
3093 itmlst[1].buffer_addr = 0;
3094 itmlst[1].retlenaddr = 0;
3096 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3097 if (iss == SS$_NOLOGNAM) {
3101 if (!(iss&1)) lib$signal(iss);
3104 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
3105 strip it off and return the ifi, if any
3108 if (result[0] == 0x1b && result[1] == 0x00) {
3109 memmove(&ifi,result+2,2);
3110 strcpy(result,result+4);
3112 return ifi; /* this is the RMS internal file id */
3115 static void pipe_infromchild_ast(pPipe p);
3118 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3119 inside an AST routine without worrying about reentrancy and which Perl
3120 memory allocator is being used.
3122 We read data and queue up the buffers, then spit them out one at a
3123 time to the output mailbox when the output mailbox is ready for one.
3126 #define INITIAL_TOCHILDQUEUE 2
3129 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3133 char mbx1[64], mbx2[64];
3134 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3135 DSC$K_CLASS_S, mbx1},
3136 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3137 DSC$K_CLASS_S, mbx2};
3138 unsigned int dviitm = DVI$_DEVBUFSIZ;
3142 _ckvmssts(lib$get_vm(&n, &p));
3144 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
3145 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
3146 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3149 p->shut_on_empty = FALSE;
3150 p->need_wake = FALSE;
3153 p->iosb.status = SS$_NORMAL;
3154 p->iosb2.status = SS$_NORMAL;
3160 #ifdef PERL_IMPLICIT_CONTEXT
3164 n = sizeof(CBuf) + p->bufsize;
3166 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3167 _ckvmssts(lib$get_vm(&n, &b));
3168 b->buf = (char *) b + sizeof(CBuf);
3169 _ckvmssts(lib$insqhi(b, &p->free));
3172 pipe_tochild2_ast(p);
3173 pipe_tochild1_ast(p);
3179 /* reads the MBX Perl is writing, and queues */
3182 pipe_tochild1_ast(pPipe p)
3185 int iss = p->iosb.status;
3186 int eof = (iss == SS$_ENDOFFILE);
3188 #ifdef PERL_IMPLICIT_CONTEXT
3194 p->shut_on_empty = TRUE;
3196 _ckvmssts(sys$dassgn(p->chan_in));
3202 b->size = p->iosb.count;
3203 _ckvmssts(sts = lib$insqhi(b, &p->wait));
3205 p->need_wake = FALSE;
3206 _ckvmssts(sys$dclast(pipe_tochild2_ast,p,0));
3209 p->retry = 1; /* initial call */
3212 if (eof) { /* flush the free queue, return when done */
3213 int n = sizeof(CBuf) + p->bufsize;
3215 iss = lib$remqti(&p->free, &b);
3216 if (iss == LIB$_QUEWASEMP) return;
3218 _ckvmssts(lib$free_vm(&n, &b));
3222 iss = lib$remqti(&p->free, &b);
3223 if (iss == LIB$_QUEWASEMP) {
3224 int n = sizeof(CBuf) + p->bufsize;
3225 _ckvmssts(lib$get_vm(&n, &b));
3226 b->buf = (char *) b + sizeof(CBuf);
3232 iss = sys$qio(0,p->chan_in,
3233 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3235 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3236 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3241 /* writes queued buffers to output, waits for each to complete before
3245 pipe_tochild2_ast(pPipe p)
3248 int iss = p->iosb2.status;
3249 int n = sizeof(CBuf) + p->bufsize;
3250 int done = (p->info && p->info->done) ||
3251 iss == SS$_CANCEL || iss == SS$_ABORT;
3252 #if defined(PERL_IMPLICIT_CONTEXT)
3257 if (p->type) { /* type=1 has old buffer, dispose */
3258 if (p->shut_on_empty) {
3259 _ckvmssts(lib$free_vm(&n, &b));
3261 _ckvmssts(lib$insqhi(b, &p->free));
3266 iss = lib$remqti(&p->wait, &b);
3267 if (iss == LIB$_QUEWASEMP) {
3268 if (p->shut_on_empty) {
3270 _ckvmssts(sys$dassgn(p->chan_out));
3271 *p->pipe_done = TRUE;
3272 _ckvmssts(sys$setef(pipe_ef));
3274 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
3275 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3279 p->need_wake = TRUE;
3289 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF,
3290 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3292 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3293 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3302 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3305 char mbx1[64], mbx2[64];
3306 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3307 DSC$K_CLASS_S, mbx1},
3308 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3309 DSC$K_CLASS_S, mbx2};
3310 unsigned int dviitm = DVI$_DEVBUFSIZ;
3312 int n = sizeof(Pipe);
3313 _ckvmssts(lib$get_vm(&n, &p));
3314 create_mbx(aTHX_ &p->chan_in , &d_mbx1);
3315 create_mbx(aTHX_ &p->chan_out, &d_mbx2);
3317 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3318 n = p->bufsize * sizeof(char);
3319 _ckvmssts(lib$get_vm(&n, &p->buf));
3320 p->shut_on_empty = FALSE;
3323 p->iosb.status = SS$_NORMAL;
3324 #if defined(PERL_IMPLICIT_CONTEXT)
3327 pipe_infromchild_ast(p);
3335 pipe_infromchild_ast(pPipe p)
3337 int iss = p->iosb.status;
3338 int eof = (iss == SS$_ENDOFFILE);
3339 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3340 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3341 #if defined(PERL_IMPLICIT_CONTEXT)
3345 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3346 _ckvmssts(sys$dassgn(p->chan_out));
3351 input shutdown if EOF from self (done or shut_on_empty)
3352 output shutdown if closing flag set (my_pclose)
3353 send data/eof from child or eof from self
3354 otherwise, re-read (snarf of data from child)
3359 if (myeof && p->chan_in) { /* input shutdown */
3360 _ckvmssts(sys$dassgn(p->chan_in));
3365 if (myeof || kideof) { /* pass EOF to parent */
3366 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3367 pipe_infromchild_ast, p,
3370 } else if (eof) { /* eat EOF --- fall through to read*/
3372 } else { /* transmit data */
3373 _ckvmssts(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3374 pipe_infromchild_ast,p,
3375 p->buf, p->iosb.count, 0, 0, 0, 0));
3381 /* everything shut? flag as done */
3383 if (!p->chan_in && !p->chan_out) {
3384 *p->pipe_done = TRUE;
3385 _ckvmssts(sys$setef(pipe_ef));
3389 /* write completed (or read, if snarfing from child)
3390 if still have input active,
3391 queue read...immediate mode if shut_on_empty so we get EOF if empty
3393 check if Perl reading, generate EOFs as needed
3399 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3400 pipe_infromchild_ast,p,
3401 p->buf, p->bufsize, 0, 0, 0, 0);
3402 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3404 } else { /* send EOFs for extra reads */
3405 p->iosb.status = SS$_ENDOFFILE;
3406 p->iosb.dvispec = 0;
3407 _ckvmssts(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3409 pipe_infromchild_ast, p, 0, 0, 0, 0));
3415 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3419 unsigned long dviitm = DVI$_DEVBUFSIZ;
3421 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3422 DSC$K_CLASS_S, mbx};
3423 int n = sizeof(Pipe);
3425 /* things like terminals and mbx's don't need this filter */
3426 if (fd && fstat(fd,&s) == 0) {
3427 unsigned long dviitm = DVI$_DEVCHAR, devchar;
3429 unsigned short dev_len;
3430 struct dsc$descriptor_s d_dev;
3432 struct item_list_3 items[3];
3434 unsigned short dvi_iosb[4];
3436 cptr = getname(fd, out, 1);
3437 if (cptr == NULL) _ckvmssts(SS$_NOSUCHDEV);
3438 d_dev.dsc$a_pointer = out;
3439 d_dev.dsc$w_length = strlen(out);
3440 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3441 d_dev.dsc$b_class = DSC$K_CLASS_S;
3444 items[0].code = DVI$_DEVCHAR;
3445 items[0].bufadr = &devchar;
3446 items[0].retadr = NULL;
3448 items[1].code = DVI$_FULLDEVNAM;
3449 items[1].bufadr = device;
3450 items[1].retadr = &dev_len;
3454 status = sys$getdviw
3455 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3457 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3458 device[dev_len] = 0;
3460 if (!(devchar & DEV$M_DIR)) {
3461 strcpy(out, device);
3467 _ckvmssts(lib$get_vm(&n, &p));
3468 p->fd_out = dup(fd);
3469 create_mbx(aTHX_ &p->chan_in, &d_mbx);
3470 _ckvmssts(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3471 n = (p->bufsize+1) * sizeof(char);
3472 _ckvmssts(lib$get_vm(&n, &p->buf));
3473 p->shut_on_empty = FALSE;
3478 _ckvmssts(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3479 pipe_mbxtofd_ast, p,
3480 p->buf, p->bufsize, 0, 0, 0, 0));
3486 pipe_mbxtofd_ast(pPipe p)
3488 int iss = p->iosb.status;
3489 int done = p->info->done;
3491 int eof = (iss == SS$_ENDOFFILE);
3492 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3493 int err = !(iss&1) && !eof;
3494 #if defined(PERL_IMPLICIT_CONTEXT)
3498 if (done && myeof) { /* end piping */
3500 sys$dassgn(p->chan_in);
3501 *p->pipe_done = TRUE;
3502 _ckvmssts(sys$setef(pipe_ef));
3506 if (!err && !eof) { /* good data to send to file */
3507 p->buf[p->iosb.count] = '\n';
3508 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3511 if (p->retry < MAX_RETRY) {
3512 _ckvmssts(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3522 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3523 pipe_mbxtofd_ast, p,
3524 p->buf, p->bufsize, 0, 0, 0, 0);
3525 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3530 typedef struct _pipeloc PLOC;
3531 typedef struct _pipeloc* pPLOC;
3535 char dir[NAM$C_MAXRSS+1];
3537 static pPLOC head_PLOC = 0;
3540 free_pipelocs(pTHX_ void *head)
3543 pPLOC *pHead = (pPLOC *)head;
3555 store_pipelocs(pTHX)
3564 char temp[NAM$C_MAXRSS+1];
3568 free_pipelocs(aTHX_ &head_PLOC);
3570 /* the . directory from @INC comes last */
3572 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3573 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3574 p->next = head_PLOC;
3576 strcpy(p->dir,"./");
3578 /* get the directory from $^X */
3580 unixdir = PerlMem_malloc(VMS_MAXRSS);
3581 if (unixdir == NULL) _ckvmssts(SS$_INSFMEM);
3583 #ifdef PERL_IMPLICIT_CONTEXT
3584 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3586 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3588 strcpy(temp, PL_origargv[0]);
3589 x = strrchr(temp,']');
3591 x = strrchr(temp,'>');
3593 /* It could be a UNIX path */
3594 x = strrchr(temp,'/');
3600 /* Got a bare name, so use default directory */
3605 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3606 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3607 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3608 p->next = head_PLOC;
3610 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3611 p->dir[NAM$C_MAXRSS] = '\0';
3615 /* reverse order of @INC entries, skip "." since entered above */
3617 #ifdef PERL_IMPLICIT_CONTEXT
3620 if (PL_incgv) av = GvAVn(PL_incgv);
3622 for (i = 0; av && i <= AvFILL(av); i++) {
3623 dirsv = *av_fetch(av,i,TRUE);
3625 if (SvROK(dirsv)) continue;
3626 dir = SvPVx(dirsv,n_a);
3627 if (strcmp(dir,".") == 0) continue;
3628 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3631 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3632 p->next = head_PLOC;
3634 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3635 p->dir[NAM$C_MAXRSS] = '\0';
3638 /* most likely spot (ARCHLIB) put first in the list */
3641 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3642 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3643 if (p == NULL) _ckvmssts(SS$_INSFMEM);
3644 p->next = head_PLOC;
3646 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3647 p->dir[NAM$C_MAXRSS] = '\0';
3650 PerlMem_free(unixdir);
3654 Perl_cando_by_name_int
3655 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3656 #if !defined(PERL_IMPLICIT_CONTEXT)
3657 #define cando_by_name_int Perl_cando_by_name_int
3659 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3665 static int vmspipe_file_status = 0;
3666 static char vmspipe_file[NAM$C_MAXRSS+1];
3668 /* already found? Check and use ... need read+execute permission */
3670 if (vmspipe_file_status == 1) {
3671 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3672 && cando_by_name_int
3673 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3674 return vmspipe_file;
3676 vmspipe_file_status = 0;
3679 /* scan through stored @INC, $^X */
3681 if (vmspipe_file_status == 0) {
3682 char file[NAM$C_MAXRSS+1];
3683 pPLOC p = head_PLOC;
3688 strcpy(file, p->dir);
3689 dirlen = strlen(file);
3690 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
3691 file[NAM$C_MAXRSS] = '\0';
3694 exp_res = do_rmsexpand
3695 (file, vmspipe_file, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL);
3696 if (!exp_res) continue;
3698 if (cando_by_name_int
3699 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3700 && cando_by_name_int
3701 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3702 vmspipe_file_status = 1;
3703 return vmspipe_file;
3706 vmspipe_file_status = -1; /* failed, use tempfiles */
3713 vmspipe_tempfile(pTHX)
3715 char file[NAM$C_MAXRSS+1];
3717 static int index = 0;
3721 /* create a tempfile */
3723 /* we can't go from W, shr=get to R, shr=get without
3724 an intermediate vulnerable state, so don't bother trying...
3726 and lib$spawn doesn't shr=put, so have to close the write
3728 So... match up the creation date/time and the FID to
3729 make sure we're dealing with the same file
3734 if (!decc_filename_unix_only) {
3735 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3736 fp = fopen(file,"w");
3738 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3739 fp = fopen(file,"w");
3741 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3742 fp = fopen(file,"w");
3747 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3748 fp = fopen(file,"w");
3750 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3751 fp = fopen(file,"w");
3753 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3754 fp = fopen(file,"w");
3758 if (!fp) return 0; /* we're hosed */
3760 fprintf(fp,"$! 'f$verify(0)'\n");
3761 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3762 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3763 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3764 fprintf(fp,"$ perl_on = \"set noon\"\n");
3765 fprintf(fp,"$ perl_exit = \"exit\"\n");
3766 fprintf(fp,"$ perl_del = \"delete\"\n");
3767 fprintf(fp,"$ pif = \"if\"\n");
3768 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3769 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3770 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3771 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3772 fprintf(fp,"$! --- build command line to get max possible length\n");
3773 fprintf(fp,"$c=perl_popen_cmd0\n");
3774 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3775 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3776 fprintf(fp,"$x=perl_popen_cmd3\n");
3777 fprintf(fp,"$c=c+x\n");
3778 fprintf(fp,"$ perl_on\n");
3779 fprintf(fp,"$ 'c'\n");
3780 fprintf(fp,"$ perl_status = $STATUS\n");
3781 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3782 fprintf(fp,"$ perl_exit 'perl_status'\n");
3785 fgetname(fp, file, 1);
3786 fstat(fileno(fp), (struct stat *)&s0);
3789 if (decc_filename_unix_only)
3790 do_tounixspec(file, file, 0, NULL);
3791 fp = fopen(file,"r","shr=get");
3793 fstat(fileno(fp), (struct stat *)&s1);
3795 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3796 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3805 static int vms_is_syscommand_xterm(void)
3807 const static struct dsc$descriptor_s syscommand_dsc =
3808 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3810 const static struct dsc$descriptor_s decwdisplay_dsc =
3811 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3813 struct item_list_3 items[2];
3814 unsigned short dvi_iosb[4];
3815 unsigned long devchar;
3816 unsigned long devclass;
3819 /* Very simple check to guess if sys$command is a decterm? */
3820 /* First see if the DECW$DISPLAY: device exists */
3822 items[0].code = DVI$_DEVCHAR;
3823 items[0].bufadr = &devchar;
3824 items[0].retadr = NULL;
3828 status = sys$getdviw
3829 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3831 if ($VMS_STATUS_SUCCESS(status)) {
3832 status = dvi_iosb[0];
3835 if (!$VMS_STATUS_SUCCESS(status)) {
3836 SETERRNO(EVMSERR, status);
3840 /* If it does, then for now assume that we are on a workstation */
3841 /* Now verify that SYS$COMMAND is a terminal */
3842 /* for creating the debugger DECTerm */
3845 items[0].code = DVI$_DEVCLASS;
3846 items[0].bufadr = &devclass;
3847 items[0].retadr = NULL;
3851 status = sys$getdviw
3852 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3854 if ($VMS_STATUS_SUCCESS(status)) {
3855 status = dvi_iosb[0];
3858 if (!$VMS_STATUS_SUCCESS(status)) {
3859 SETERRNO(EVMSERR, status);
3863 if (devclass == DC$_TERM) {
3870 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3871 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3876 char device_name[65];
3877 unsigned short device_name_len;
3878 struct dsc$descriptor_s customization_dsc;
3879 struct dsc$descriptor_s device_name_dsc;
3882 char customization[200];
3886 unsigned short p_chan;
3888 unsigned short iosb[4];
3889 struct item_list_3 items[2];
3890 const char * cust_str =
3891 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3892 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3893 DSC$K_CLASS_S, mbx1};
3895 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
3896 /*---------------------------------------*/
3897 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
3900 /* Make sure that this is from the Perl debugger */
3901 ret_char = strstr(cmd," xterm ");
3902 if (ret_char == NULL)
3904 cptr = ret_char + 7;
3905 ret_char = strstr(cmd,"tty");
3906 if (ret_char == NULL)
3908 ret_char = strstr(cmd,"sleep");
3909 if (ret_char == NULL)
3912 if (decw_term_port == 0) {
3913 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
3914 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
3915 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
3917 status = lib$find_image_symbol
3919 &decw_term_port_dsc,
3920 (void *)&decw_term_port,
3924 /* Try again with the other image name */
3925 if (!$VMS_STATUS_SUCCESS(status)) {
3927 status = lib$find_image_symbol
3929 &decw_term_port_dsc,
3930 (void *)&decw_term_port,
3939 /* No decw$term_port, give it up */
3940 if (!$VMS_STATUS_SUCCESS(status))
3943 /* Are we on a workstation? */
3944 /* to do: capture the rows / columns and pass their properties */
3945 ret_stat = vms_is_syscommand_xterm();
3949 /* Make the title: */
3950 ret_char = strstr(cptr,"-title");
3951 if (ret_char != NULL) {
3952 while ((*cptr != 0) && (*cptr != '\"')) {
3958 while ((*cptr != 0) && (*cptr != '\"')) {
3971 strcpy(title,"Perl Debug DECTerm");
3973 sprintf(customization, cust_str, title);
3975 customization_dsc.dsc$a_pointer = customization;
3976 customization_dsc.dsc$w_length = strlen(customization);
3977 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3978 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
3980 device_name_dsc.dsc$a_pointer = device_name;
3981 device_name_dsc.dsc$w_length = sizeof device_name -1;
3982 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
3983 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
3985 device_name_len = 0;
3987 /* Try to create the window */
3988 status = (*decw_term_port)
3997 if (!$VMS_STATUS_SUCCESS(status)) {
3998 SETERRNO(EVMSERR, status);
4002 device_name[device_name_len] = '\0';
4004 /* Need to set this up to look like a pipe for cleanup */
4006 status = lib$get_vm(&n, &info);
4007 if (!$VMS_STATUS_SUCCESS(status)) {
4008 SETERRNO(ENOMEM, status);
4014 info->completion = 0;
4015 info->closing = FALSE;
4022 info->in_done = TRUE;
4023 info->out_done = TRUE;
4024 info->err_done = TRUE;
4026 /* Assign a channel on this so that it will persist, and not login */
4027 /* We stash this channel in the info structure for reference. */
4028 /* The created xterm self destructs when the last channel is removed */
4029 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4030 /* So leave this assigned. */
4031 device_name_dsc.dsc$w_length = device_name_len;
4032 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4033 if (!$VMS_STATUS_SUCCESS(status)) {
4034 SETERRNO(EVMSERR, status);
4037 info->xchan_valid = 1;
4039 /* Now create a mailbox to be read by the application */
4041 create_mbx(aTHX_ &p_chan, &d_mbx1);
4043 /* write the name of the created terminal to the mailbox */
4044 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4045 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4047 if (!$VMS_STATUS_SUCCESS(status)) {
4048 SETERRNO(EVMSERR, status);
4052 info->fp = PerlIO_open(mbx1, mode);
4054 /* Done with this channel */
4057 /* If any errors, then clean up */
4060 _ckvmssts(lib$free_vm(&n, &info));
4069 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4071 static int handler_set_up = FALSE;
4072 unsigned long int sts, flags = CLI$M_NOWAIT;
4073 /* The use of a GLOBAL table (as was done previously) rendered
4074 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4075 * environment. Hence we've switched to LOCAL symbol table.
4077 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4079 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4080 char *in, *out, *err, mbx[512];
4082 char tfilebuf[NAM$C_MAXRSS+1];
4084 char cmd_sym_name[20];
4085 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4086 DSC$K_CLASS_S, symbol};
4087 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4089 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4090 DSC$K_CLASS_S, cmd_sym_name};
4091 struct dsc$descriptor_s *vmscmd;
4092 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4093 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4094 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4096 /* Check here for Xterm create request. This means looking for
4097 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4098 * is possible to create an xterm.
4100 if (*in_mode == 'r') {
4103 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4104 if (xterm_fd != NULL)
4108 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4110 /* once-per-program initialization...
4111 note that the SETAST calls and the dual test of pipe_ef
4112 makes sure that only the FIRST thread through here does
4113 the initialization...all other threads wait until it's
4116 Yeah, uglier than a pthread call, it's got all the stuff inline
4117 rather than in a separate routine.
4121 _ckvmssts(sys$setast(0));
4123 unsigned long int pidcode = JPI$_PID;
4124 $DESCRIPTOR(d_delay, RETRY_DELAY);
4125 _ckvmssts(lib$get_ef(&pipe_ef));
4126 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4127 _ckvmssts(sys$bintim(&d_delay, delaytime));
4129 if (!handler_set_up) {
4130 _ckvmssts(sys$dclexh(&pipe_exitblock));
4131 handler_set_up = TRUE;
4133 _ckvmssts(sys$setast(1));
4136 /* see if we can find a VMSPIPE.COM */
4139 vmspipe = find_vmspipe(aTHX);
4141 strcpy(tfilebuf+1,vmspipe);
4142 } else { /* uh, oh...we're in tempfile hell */
4143 tpipe = vmspipe_tempfile(aTHX);
4144 if (!tpipe) { /* a fish popular in Boston */
4145 if (ckWARN(WARN_PIPE)) {
4146 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4150 fgetname(tpipe,tfilebuf+1,1);
4152 vmspipedsc.dsc$a_pointer = tfilebuf;
4153 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4155 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4158 case RMS$_FNF: case RMS$_DNF:
4159 set_errno(ENOENT); break;
4161 set_errno(ENOTDIR); break;
4163 set_errno(ENODEV); break;
4165 set_errno(EACCES); break;
4167 set_errno(EINVAL); break;
4168 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4169 set_errno(E2BIG); break;
4170 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4171 _ckvmssts(sts); /* fall through */
4172 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4175 set_vaxc_errno(sts);
4176 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4177 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4183 _ckvmssts(lib$get_vm(&n, &info));
4185 strcpy(mode,in_mode);
4188 info->completion = 0;
4189 info->closing = FALSE;
4196 info->in_done = TRUE;
4197 info->out_done = TRUE;
4198 info->err_done = TRUE;
4200 info->xchan_valid = 0;
4202 in = PerlMem_malloc(VMS_MAXRSS);
4203 if (in == NULL) _ckvmssts(SS$_INSFMEM);
4204 out = PerlMem_malloc(VMS_MAXRSS);
4205 if (out == NULL) _ckvmssts(SS$_INSFMEM);
4206 err = PerlMem_malloc(VMS_MAXRSS);
4207 if (err == NULL) _ckvmssts(SS$_INSFMEM);
4209 in[0] = out[0] = err[0] = '\0';
4211 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4215 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4220 if (*mode == 'r') { /* piping from subroutine */
4222 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4224 info->out->pipe_done = &info->out_done;
4225 info->out_done = FALSE;
4226 info->out->info = info;
4228 if (!info->useFILE) {
4229 info->fp = PerlIO_open(mbx, mode);
4231 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4232 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4235 if (!info->fp && info->out) {
4236 sys$cancel(info->out->chan_out);
4238 while (!info->out_done) {
4240 _ckvmssts(sys$setast(0));
4241 done = info->out_done;
4242 if (!done) _ckvmssts(sys$clref(pipe_ef));
4243 _ckvmssts(sys$setast(1));
4244 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4247 if (info->out->buf) {
4248 n = info->out->bufsize * sizeof(char);
4249 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4252 _ckvmssts(lib$free_vm(&n, &info->out));
4254 _ckvmssts(lib$free_vm(&n, &info));
4259 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4261 info->err->pipe_done = &info->err_done;
4262 info->err_done = FALSE;
4263 info->err->info = info;
4266 } else if (*mode == 'w') { /* piping to subroutine */
4268 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4270 info->out->pipe_done = &info->out_done;
4271 info->out_done = FALSE;
4272 info->out->info = info;
4275 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4277 info->err->pipe_done = &info->err_done;
4278 info->err_done = FALSE;
4279 info->err->info = info;
4282 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4283 if (!info->useFILE) {
4284 info->fp = PerlIO_open(mbx, mode);
4286 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4287 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4291 info->in->pipe_done = &info->in_done;
4292 info->in_done = FALSE;
4293 info->in->info = info;
4297 if (!info->fp && info->in) {
4299 _ckvmssts(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4300 0, 0, 0, 0, 0, 0, 0, 0));
4302 while (!info->in_done) {
4304 _ckvmssts(sys$setast(0));
4305 done = info->in_done;
4306 if (!done) _ckvmssts(sys$clref(pipe_ef));
4307 _ckvmssts(sys$setast(1));
4308 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4311 if (info->in->buf) {
4312 n = info->in->bufsize * sizeof(char);
4313 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4316 _ckvmssts(lib$free_vm(&n, &info->in));
4318 _ckvmssts(lib$free_vm(&n, &info));
4324 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4325 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4327 info->out->pipe_done = &info->out_done;
4328 info->out_done = FALSE;
4329 info->out->info = info;
4332 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4334 info->err->pipe_done = &info->err_done;
4335 info->err_done = FALSE;
4336 info->err->info = info;
4340 symbol[MAX_DCL_SYMBOL] = '\0';
4342 strncpy(symbol, in, MAX_DCL_SYMBOL);
4343 d_symbol.dsc$w_length = strlen(symbol);
4344 _ckvmssts(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4346 strncpy(symbol, err, MAX_DCL_SYMBOL);
4347 d_symbol.dsc$w_length = strlen(symbol);
4348 _ckvmssts(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4350 strncpy(symbol, out, MAX_DCL_SYMBOL);
4351 d_symbol.dsc$w_length = strlen(symbol);
4352 _ckvmssts(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4354 /* Done with the names for the pipes */
4359 p = vmscmd->dsc$a_pointer;
4360 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4361 if (*p == '$') p++; /* remove leading $ */
4362 while (*p == ' ' || *p == '\t') p++;
4364 for (j = 0; j < 4; j++) {
4365 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4366 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4368 strncpy(symbol, p, MAX_DCL_SYMBOL);
4369 d_symbol.dsc$w_length = strlen(symbol);
4370 _ckvmssts(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4372 if (strlen(p) > MAX_DCL_SYMBOL) {
4373 p += MAX_DCL_SYMBOL;
4378 _ckvmssts(sys$setast(0));
4379 info->next=open_pipes; /* prepend to list */
4381 _ckvmssts(sys$setast(1));
4382 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4383 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4384 * have SYS$COMMAND if we need it.
4386 _ckvmssts(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4387 0, &info->pid, &info->completion,
4388 0, popen_completion_ast,info,0,0,0));
4390 /* if we were using a tempfile, close it now */
4392 if (tpipe) fclose(tpipe);
4394 /* once the subprocess is spawned, it has copied the symbols and
4395 we can get rid of ours */
4397 for (j = 0; j < 4; j++) {
4398 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4399 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4400 _ckvmssts(lib$delete_symbol(&d_sym_cmd, &table));
4402 _ckvmssts(lib$delete_symbol(&d_sym_in, &table));
4403 _ckvmssts(lib$delete_symbol(&d_sym_err, &table));
4404 _ckvmssts(lib$delete_symbol(&d_sym_out, &table));
4405 vms_execfree(vmscmd);
4407 #ifdef PERL_IMPLICIT_CONTEXT
4410 PL_forkprocess = info->pid;
4415 _ckvmssts(sys$setast(0));
4417 if (!done) _ckvmssts(sys$clref(pipe_ef));
4418 _ckvmssts(sys$setast(1));
4419 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4421 *psts = info->completion;
4422 /* Caller thinks it is open and tries to close it. */
4423 /* This causes some problems, as it changes the error status */
4424 /* my_pclose(info->fp); */
4429 } /* end of safe_popen */
4432 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4434 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4438 TAINT_PROPER("popen");
4439 PERL_FLUSHALL_FOR_CHILD;
4440 return safe_popen(aTHX_ cmd,mode,&sts);
4445 /*{{{ I32 my_pclose(PerlIO *fp)*/
4446 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4448 pInfo info, last = NULL;
4449 unsigned long int retsts;
4453 for (info = open_pipes; info != NULL; last = info, info = info->next)
4454 if (info->fp == fp) break;
4456 if (info == NULL) { /* no such pipe open */
4457 set_errno(ECHILD); /* quoth POSIX */
4458 set_vaxc_errno(SS$_NONEXPR);
4462 /* If we were writing to a subprocess, insure that someone reading from
4463 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4464 * produce an EOF record in the mailbox.
4466 * well, at least sometimes it *does*, so we have to watch out for
4467 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4471 #if defined(USE_ITHREADS)
4474 && PL_perlio_fd_refcnt)
4475 PerlIO_flush(info->fp);
4477 fflush((FILE *)info->fp);
4480 _ckvmssts(sys$setast(0));
4481 info->closing = TRUE;
4482 done = info->done && info->in_done && info->out_done && info->err_done;
4483 /* hanging on write to Perl's input? cancel it */
4484 if (info->mode == 'r' && info->out && !info->out_done) {
4485 if (info->out->chan_out) {
4486 _ckvmssts(sys$cancel(info->out->chan_out));
4487 if (!info->out->chan_in) { /* EOF generation, need AST */
4488 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4492 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4493 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4495 _ckvmssts(sys$setast(1));
4498 #if defined(USE_ITHREADS)
4501 && PL_perlio_fd_refcnt)
4502 PerlIO_close(info->fp);
4504 fclose((FILE *)info->fp);
4507 we have to wait until subprocess completes, but ALSO wait until all
4508 the i/o completes...otherwise we'll be freeing the "info" structure
4509 that the i/o ASTs could still be using...
4513 _ckvmssts(sys$setast(0));
4514 done = info->done && info->in_done && info->out_done && info->err_done;
4515 if (!done) _ckvmssts(sys$clref(pipe_ef));
4516 _ckvmssts(sys$setast(1));
4517 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4519 retsts = info->completion;
4521 /* remove from list of open pipes */
4522 _ckvmssts(sys$setast(0));
4523 if (last) last->next = info->next;
4524 else open_pipes = info->next;
4525 _ckvmssts(sys$setast(1));
4527 /* free buffers and structures */
4530 if (info->in->buf) {
4531 n = info->in->bufsize * sizeof(char);
4532 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4535 _ckvmssts(lib$free_vm(&n, &info->in));
4538 if (info->out->buf) {
4539 n = info->out->bufsize * sizeof(char);
4540 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4543 _ckvmssts(lib$free_vm(&n, &info->out));
4546 if (info->err->buf) {
4547 n = info->err->bufsize * sizeof(char);
4548 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4551 _ckvmssts(lib$free_vm(&n, &info->err));
4554 _ckvmssts(lib$free_vm(&n, &info));
4558 } /* end of my_pclose() */
4560 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4561 /* Roll our own prototype because we want this regardless of whether
4562 * _VMS_WAIT is defined.
4564 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4566 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4567 created with popen(); otherwise partially emulate waitpid() unless
4568 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4569 Also check processes not considered by the CRTL waitpid().
4571 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4573 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4580 if (statusp) *statusp = 0;
4582 for (info = open_pipes; info != NULL; info = info->next)
4583 if (info->pid == pid) break;
4585 if (info != NULL) { /* we know about this child */
4586 while (!info->done) {
4587 _ckvmssts(sys$setast(0));
4589 if (!done) _ckvmssts(sys$clref(pipe_ef));
4590 _ckvmssts(sys$setast(1));
4591 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4594 if (statusp) *statusp = info->completion;
4598 /* child that already terminated? */
4600 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4601 if (closed_list[j].pid == pid) {
4602 if (statusp) *statusp = closed_list[j].completion;
4607 /* fall through if this child is not one of our own pipe children */
4609 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4611 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4612 * in 7.2 did we get a version that fills in the VMS completion
4613 * status as Perl has always tried to do.
4616 sts = __vms_waitpid( pid, statusp, flags );
4618 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4621 /* If the real waitpid tells us the child does not exist, we
4622 * fall through here to implement waiting for a child that
4623 * was created by some means other than exec() (say, spawned
4624 * from DCL) or to wait for a process that is not a subprocess
4625 * of the current process.
4628 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4631 $DESCRIPTOR(intdsc,"0 00:00:01");
4632 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4633 unsigned long int pidcode = JPI$_PID, mypid;
4634 unsigned long int interval[2];
4635 unsigned int jpi_iosb[2];
4636 struct itmlst_3 jpilist[2] = {
4637 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4642 /* Sorry folks, we don't presently implement rooting around for
4643 the first child we can find, and we definitely don't want to
4644 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4650 /* Get the owner of the child so I can warn if it's not mine. If the
4651 * process doesn't exist or I don't have the privs to look at it,
4652 * I can go home early.
4654 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4655 if (sts & 1) sts = jpi_iosb[0];
4667 set_vaxc_errno(sts);
4671 if (ckWARN(WARN_EXEC)) {
4672 /* remind folks they are asking for non-standard waitpid behavior */
4673 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4674 if (ownerpid != mypid)
4675 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4676 "waitpid: process %x is not a child of process %x",
4680 /* simply check on it once a second until it's not there anymore. */
4682 _ckvmssts(sys$bintim(&intdsc,interval));
4683 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4684 _ckvmssts(sys$schdwk(0,0,interval,0));
4685 _ckvmssts(sys$hiber());
4687 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4692 } /* end of waitpid() */
4697 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4699 my_gconvert(double val, int ndig, int trail, char *buf)
4701 static char __gcvtbuf[DBL_DIG+1];
4704 loc = buf ? buf : __gcvtbuf;
4706 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
4708 sprintf(loc,"%.*g",ndig,val);
4714 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4715 return gcvt(val,ndig,loc);
4718 loc[0] = '0'; loc[1] = '\0';
4725 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4726 static int rms_free_search_context(struct FAB * fab)
4730 nam = fab->fab$l_nam;
4731 nam->nam$b_nop |= NAM$M_SYNCHK;
4732 nam->nam$l_rlf = NULL;
4734 return sys$parse(fab, NULL, NULL);
4737 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4738 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4739 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4740 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4741 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4742 #define rms_nam_esll(nam) nam.nam$b_esl
4743 #define rms_nam_esl(nam) nam.nam$b_esl
4744 #define rms_nam_name(nam) nam.nam$l_name
4745 #define rms_nam_namel(nam) nam.nam$l_name
4746 #define rms_nam_type(nam) nam.nam$l_type
4747 #define rms_nam_typel(nam) nam.nam$l_type
4748 #define rms_nam_ver(nam) nam.nam$l_ver
4749 #define rms_nam_verl(nam) nam.nam$l_ver
4750 #define rms_nam_rsll(nam) nam.nam$b_rsl
4751 #define rms_nam_rsl(nam) nam.nam$b_rsl
4752 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4753 #define rms_set_fna(fab, nam, name, size) \
4754 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4755 #define rms_get_fna(fab, nam) fab.fab$l_fna
4756 #define rms_set_dna(fab, nam, name, size) \
4757 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4758 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4759 #define rms_set_esa(nam, name, size) \
4760 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4761 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4762 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4763 #define rms_set_rsa(nam, name, size) \
4764 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4765 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4766 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4767 #define rms_nam_name_type_l_size(nam) \
4768 (nam.nam$b_name + nam.nam$b_type)
4770 static int rms_free_search_context(struct FAB * fab)
4774 nam = fab->fab$l_naml;
4775 nam->naml$b_nop |= NAM$M_SYNCHK;
4776 nam->naml$l_rlf = NULL;
4777 nam->naml$l_long_defname_size = 0;
4780 return sys$parse(fab, NULL, NULL);
4783 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4784 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4785 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4786 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4787 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4788 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4789 #define rms_nam_esl(nam) nam.naml$b_esl
4790 #define rms_nam_name(nam) nam.naml$l_name
4791 #define rms_nam_namel(nam) nam.naml$l_long_name
4792 #define rms_nam_type(nam) nam.naml$l_type
4793 #define rms_nam_typel(nam) nam.naml$l_long_type
4794 #define rms_nam_ver(nam) nam.naml$l_ver
4795 #define rms_nam_verl(nam) nam.naml$l_long_ver
4796 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4797 #define rms_nam_rsl(nam) nam.naml$b_rsl
4798 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4799 #define rms_set_fna(fab, nam, name, size) \
4800 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4801 nam.naml$l_long_filename_size = size; \
4802 nam.naml$l_long_filename = name;}
4803 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4804 #define rms_set_dna(fab, nam, name, size) \
4805 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4806 nam.naml$l_long_defname_size = size; \
4807 nam.naml$l_long_defname = name; }
4808 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4809 #define rms_set_esa(nam, name, size) \
4810 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4811 nam.naml$l_long_expand_alloc = size; \
4812 nam.naml$l_long_expand = name; }
4813 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4814 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4815 nam.naml$l_long_expand = l_name; \
4816 nam.naml$l_long_expand_alloc = l_size; }
4817 #define rms_set_rsa(nam, name, size) \
4818 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4819 nam.naml$l_long_result = name; \
4820 nam.naml$l_long_result_alloc = size; }
4821 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4822 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4823 nam.naml$l_long_result = l_name; \
4824 nam.naml$l_long_result_alloc = l_size; }
4825 #define rms_nam_name_type_l_size(nam) \
4826 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4831 * The CRTL for 8.3 and later can create symbolic links in any mode,
4832 * however in 8.3 the unlink/remove/delete routines will only properly handle
4833 * them if one of the PCP modes is active.
4835 static int rms_erase(const char * vmsname)
4838 struct FAB myfab = cc$rms_fab;
4839 rms_setup_nam(mynam);
4841 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4842 rms_bind_fab_nam(myfab, mynam);
4844 /* Are we removing all versions? */
4845 if (vms_unlink_all_versions == 1) {
4846 const char * defspec = ";*";
4847 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
4850 #ifdef NAML$M_OPEN_SPECIAL
4851 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
4854 status = sys$erase(&myfab, 0, 0);
4861 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
4862 const struct dsc$descriptor_s * vms_dst_dsc,
4863 unsigned long flags)
4865 /* VMS and UNIX handle file permissions differently and the
4866 * the same ACL trick may be needed for renaming files,
4867 * especially if they are directories.
4870 /* todo: get kill_file and rename to share common code */
4871 /* I can not find online documentation for $change_acl
4872 * it appears to be replaced by $set_security some time ago */
4874 const unsigned int access_mode = 0;
4875 $DESCRIPTOR(obj_file_dsc,"FILE");
4878 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
4879 int aclsts, fndsts, rnsts = -1;
4880 unsigned int ctx = 0;
4881 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
4882 struct dsc$descriptor_s * clean_dsc;
4885 unsigned char myace$b_length;
4886 unsigned char myace$b_type;
4887 unsigned short int myace$w_flags;
4888 unsigned long int myace$l_access;
4889 unsigned long int myace$l_ident;
4890 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
4891 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
4893 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
4896 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
4897 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
4899 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
4900 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
4904 /* Expand the input spec using RMS, since we do not want to put
4905 * ACLs on the target of a symbolic link */
4906 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
4907 if (vmsname == NULL)
4910 rslt = do_rmsexpand(vms_src_dsc->dsc$a_pointer,
4914 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_SYMLINK,
4918 PerlMem_free(vmsname);
4922 /* So we get our own UIC to use as a rights identifier,
4923 * and the insert an ACE at the head of the ACL which allows us
4924 * to delete the file.
4926 _ckvmssts(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
4928 fildsc.dsc$w_length = strlen(vmsname);
4929 fildsc.dsc$a_pointer = vmsname;
4931 newace.myace$l_ident = oldace.myace$l_ident;
4934 /* Grab any existing ACEs with this identifier in case we fail */
4935 clean_dsc = &fildsc;
4936 aclsts = fndsts = sys$get_security(&obj_file_dsc,
4944 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
4945 /* Add the new ACE . . . */
4947 /* if the sys$get_security succeeded, then ctx is valid, and the
4948 * object/file descriptors will be ignored. But otherwise they
4951 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
4952 OSS$M_RELCTX, addlst, &ctx, &access_mode);
4953 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
4955 set_vaxc_errno(aclsts);
4956 PerlMem_free(vmsname);
4960 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
4963 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
4965 if ($VMS_STATUS_SUCCESS(rnsts)) {
4966 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
4969 /* Put things back the way they were. */
4971 aclsts = sys$get_security(&obj_file_dsc,
4979 if ($VMS_STATUS_SUCCESS(aclsts)) {
4983 if (!$VMS_STATUS_SUCCESS(fndsts))
4984 sec_flags = OSS$M_RELCTX;
4986 /* Get rid of the new ACE */
4987 aclsts = sys$set_security(NULL, NULL, NULL,
4988 sec_flags, dellst, &ctx, &access_mode);
4990 /* If there was an old ACE, put it back */
4991 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
4992 addlst[0].bufadr = &oldace;
4993 aclsts = sys$set_security(NULL, NULL, NULL,
4994 OSS$M_RELCTX, addlst, &ctx, &access_mode);
4995 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
4997 set_vaxc_errno(aclsts);
5003 /* Try to clear the lock on the ACL list */
5004 aclsts2 = sys$set_security(NULL, NULL, NULL,
5005 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5007 /* Rename errors are most important */
5008 if (!$VMS_STATUS_SUCCESS(rnsts))
5011 set_vaxc_errno(aclsts);
5016 if (aclsts != SS$_ACLEMPTY)
5023 PerlMem_free(vmsname);
5028 /*{{{int rename(const char *, const char * */
5029 /* Not exactly what X/Open says to do, but doing it absolutely right
5030 * and efficiently would require a lot more work. This should be close
5031 * enough to pass all but the most strict X/Open compliance test.
5034 Perl_rename(pTHX_ const char *src, const char * dst)
5043 /* Validate the source file */
5044 src_sts = flex_lstat(src, &src_st);
5047 /* No source file or other problem */
5051 dst_sts = flex_lstat(dst, &dst_st);
5054 if (dst_st.st_dev != src_st.st_dev) {
5055 /* Must be on the same device */
5060 /* VMS_INO_T_COMPARE is true if the inodes are different
5061 * to match the output of memcmp
5064 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5065 /* That was easy, the files are the same! */
5069 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5070 /* If source is a directory, so must be dest */
5078 if ((dst_sts == 0) &&
5079 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5081 /* We have issues here if vms_unlink_all_versions is set
5082 * If the destination exists, and is not a directory, then
5083 * we must delete in advance.
5085 * If the src is a directory, then we must always pre-delete
5088 * If we successfully delete the dst in advance, and the rename fails
5089 * X/Open requires that errno be EIO.
5093 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5095 d_sts = mp_do_kill_file(aTHX_ dst, S_ISDIR(dst_st.st_mode));
5099 /* We killed the destination, so only errno now is EIO */
5104 /* Originally the idea was to call the CRTL rename() and only
5105 * try the lib$rename_file if it failed.
5106 * It turns out that there are too many variants in what the
5107 * the CRTL rename might do, so only use lib$rename_file
5112 /* Is the source and dest both in VMS format */
5113 /* if the source is a directory, then need to fileify */
5114 /* and dest must be a directory or non-existant. */
5120 unsigned long flags;
5121 struct dsc$descriptor_s old_file_dsc;
5122 struct dsc$descriptor_s new_file_dsc;
5124 /* We need to modify the src and dst depending
5125 * on if one or more of them are directories.
5128 vms_src = PerlMem_malloc(VMS_MAXRSS);
5129 if (vms_src == NULL)
5130 _ckvmssts(SS$_INSFMEM);
5132 /* Source is always a VMS format file */
5133 ret_str = do_tovmsspec(src, vms_src, 0, NULL);
5134 if (ret_str == NULL) {
5135 PerlMem_free(vms_src);
5140 vms_dst = PerlMem_malloc(VMS_MAXRSS);
5141 if (vms_dst == NULL)
5142 _ckvmssts(SS$_INSFMEM);
5144 if (S_ISDIR(src_st.st_mode)) {
5146 char * vms_dir_file;
5148 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5149 if (vms_dir_file == NULL)
5150 _ckvmssts(SS$_INSFMEM);
5152 /* The source must be a file specification */
5153 ret_str = do_fileify_dirspec(vms_src, vms_dir_file, 0, NULL);
5154 if (ret_str == NULL) {
5155 PerlMem_free(vms_src);
5156 PerlMem_free(vms_dst);
5157 PerlMem_free(vms_dir_file);
5161 PerlMem_free(vms_src);
5162 vms_src = vms_dir_file;
5164 /* If the dest is a directory, we must remove it
5167 d_sts = mp_do_kill_file(aTHX_ dst, 1);
5169 PerlMem_free(vms_src);
5170 PerlMem_free(vms_dst);
5178 /* The dest must be a VMS file specification */
5179 ret_str = do_tovmsspec(dst, vms_dst, 0, NULL);
5180 if (ret_str == NULL) {
5181 PerlMem_free(vms_src);
5182 PerlMem_free(vms_dst);
5187 /* The source must be a file specification */
5188 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5189 if (vms_dir_file == NULL)
5190 _ckvmssts(SS$_INSFMEM);
5192 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5193 if (ret_str == NULL) {
5194 PerlMem_free(vms_src);
5195 PerlMem_free(vms_dst);
5196 PerlMem_free(vms_dir_file);
5200 PerlMem_free(vms_dst);
5201 vms_dst = vms_dir_file;
5204 /* File to file or file to new dir */
5206 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5207 /* VMS pathify a dir target */
5208 ret_str = do_tovmspath(dst, vms_dst, 0, NULL);
5209 if (ret_str == NULL) {
5210 PerlMem_free(vms_src);
5211 PerlMem_free(vms_dst);
5217 /* fileify a target VMS file specification */
5218 ret_str = do_tovmsspec(dst, vms_dst, 0, NULL);
5219 if (ret_str == NULL) {
5220 PerlMem_free(vms_src);
5221 PerlMem_free(vms_dst);
5228 old_file_dsc.dsc$a_pointer = vms_src;
5229 old_file_dsc.dsc$w_length = strlen(vms_src);
5230 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5231 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5233 new_file_dsc.dsc$a_pointer = vms_dst;
5234 new_file_dsc.dsc$w_length = strlen(vms_dst);
5235 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5236 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5239 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5240 flags |= 2; /* LIB$M_FIL_LONG_NAMES */
5243 sts = lib$rename_file(&old_file_dsc,
5247 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5248 if (!$VMS_STATUS_SUCCESS(sts)) {
5250 /* We could have failed because VMS style permissions do not
5251 * permit renames that UNIX will allow. Just like the hack
5254 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5257 PerlMem_free(vms_src);
5258 PerlMem_free(vms_dst);
5259 if (!$VMS_STATUS_SUCCESS(sts)) {
5266 if (vms_unlink_all_versions) {
5267 /* Now get rid of any previous versions of the source file that
5272 src_sts = mp_do_kill_file(aTHX_ src, S_ISDIR(src_st.st_mode));
5276 /* We deleted the destination, so must force the error to be EIO */
5277 if ((retval != 0) && (pre_delete != 0))
5285 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5286 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5287 * to expand file specification. Allows for a single default file
5288 * specification and a simple mask of options. If outbuf is non-NULL,
5289 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5290 * the resultant file specification is placed. If outbuf is NULL, the
5291 * resultant file specification is placed into a static buffer.
5292 * The third argument, if non-NULL, is taken to be a default file
5293 * specification string. The fourth argument is unused at present.
5294 * rmesexpand() returns the address of the resultant string if
5295 * successful, and NULL on error.
5297 * New functionality for previously unused opts value:
5298 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5299 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5300 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5301 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5303 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5307 (pTHX_ const char *filespec,
5310 const char *defspec,
5315 static char __rmsexpand_retbuf[VMS_MAXRSS];
5316 char * vmsfspec, *tmpfspec;
5317 char * esa, *cp, *out = NULL;
5321 struct FAB myfab = cc$rms_fab;
5322 rms_setup_nam(mynam);
5324 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5327 /* temp hack until UTF8 is actually implemented */
5328 if (fs_utf8 != NULL)
5331 if (!filespec || !*filespec) {
5332 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5336 if (ts) out = Newx(outbuf,VMS_MAXRSS,char);
5337 else outbuf = __rmsexpand_retbuf;
5345 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5346 isunix = is_unix_filespec(filespec);
5348 vmsfspec = PerlMem_malloc(VMS_MAXRSS);
5349 if (vmsfspec == NULL) _ckvmssts(SS$_INSFMEM);
5350 if (do_tovmsspec(filespec,vmsfspec,0,fs_utf8) == NULL) {
5351 PerlMem_free(vmsfspec);
5356 filespec = vmsfspec;
5358 /* Unless we are forcing to VMS format, a UNIX input means
5359 * UNIX output, and that requires long names to be used
5361 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5362 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5363 opts |= PERL_RMSEXPAND_M_LONG;
5370 rms_set_fna(myfab, mynam, (char *)filespec, strlen(filespec)); /* cast ok */
5371 rms_bind_fab_nam(myfab, mynam);
5373 if (defspec && *defspec) {
5375 t_isunix = is_unix_filespec(defspec);
5377 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
5378 if (tmpfspec == NULL) _ckvmssts(SS$_INSFMEM);
5379 if (do_tovmsspec(defspec,tmpfspec,0,dfs_utf8) == NULL) {
5380 PerlMem_free(tmpfspec);
5381 if (vmsfspec != NULL)
5382 PerlMem_free(vmsfspec);
5389 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
5392 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
5393 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
5394 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5395 esal = PerlMem_malloc(VMS_MAXRSS);
5396 if (esal == NULL) _ckvmssts(SS$_INSFMEM);
5398 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5400 /* If a NAML block is used RMS always writes to the long and short
5401 * addresses unless you suppress the short name.
5403 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5404 outbufl = PerlMem_malloc(VMS_MAXRSS);
5405 if (outbufl == NULL) _ckvmssts(SS$_INSFMEM);
5407 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5409 #ifdef NAM$M_NO_SHORT_UPCASE
5410 if (decc_efs_case_preserve)
5411 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5414 /* We may not want to follow symbolic links */
5415 #ifdef NAML$M_OPEN_SPECIAL
5416 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5417 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5420 /* First attempt to parse as an existing file */
5421 retsts = sys$parse(&myfab,0,0);
5422 if (!(retsts & STS$K_SUCCESS)) {
5424 /* Could not find the file, try as syntax only if error is not fatal */
5425 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5426 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
5427 retsts = sys$parse(&myfab,0,0);
5428 if (retsts & STS$K_SUCCESS) goto expanded;
5431 /* Still could not parse the file specification */
5432 /*----------------------------------------------*/
5433 sts = rms_free_search_context(&myfab); /* Free search context */
5434 if (out) Safefree(out);
5435 if (tmpfspec != NULL)
5436 PerlMem_free(tmpfspec);
5437 if (vmsfspec != NULL)
5438 PerlMem_free(vmsfspec);
5439 if (outbufl != NULL)
5440 PerlMem_free(outbufl);
5444 set_vaxc_errno(retsts);
5445 if (retsts == RMS$_PRV) set_errno(EACCES);
5446 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5447 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5448 else set_errno(EVMSERR);
5451 retsts = sys$search(&myfab,0,0);
5452 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5453 sts = rms_free_search_context(&myfab); /* Free search context */
5454 if (out) Safefree(out);
5455 if (tmpfspec != NULL)
5456 PerlMem_free(tmpfspec);
5457 if (vmsfspec != NULL)
5458 PerlMem_free(vmsfspec);
5459 if (outbufl != NULL)
5460 PerlMem_free(outbufl);
5464 set_vaxc_errno(retsts);
5465 if (retsts == RMS$_PRV) set_errno(EACCES);
5466 else set_errno(EVMSERR);
5470 /* If the input filespec contained any lowercase characters,
5471 * downcase the result for compatibility with Unix-minded code. */
5473 if (!decc_efs_case_preserve) {
5474 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5475 if (islower(*tbuf)) { haslower = 1; break; }
5478 /* Is a long or a short name expected */
5479 /*------------------------------------*/
5480 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5481 if (rms_nam_rsll(mynam)) {
5483 speclen = rms_nam_rsll(mynam);
5486 tbuf = esal; /* Not esa */
5487 speclen = rms_nam_esll(mynam);
5491 if (rms_nam_rsl(mynam)) {
5493 speclen = rms_nam_rsl(mynam);
5496 tbuf = esa; /* Not esal */
5497 speclen = rms_nam_esl(mynam);
5500 tbuf[speclen] = '\0';
5502 /* Trim off null fields added by $PARSE
5503 * If type > 1 char, must have been specified in original or default spec
5504 * (not true for version; $SEARCH may have added version of existing file).
5506 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5507 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5508 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5509 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5512 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5513 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5515 if (trimver || trimtype) {
5516 if (defspec && *defspec) {
5517 char *defesal = NULL;
5518 char *defesa = NULL;
5519 defesa = PerlMem_malloc(VMS_MAXRSS + 1);
5520 if (defesa != NULL) {
5521 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5522 defesal = PerlMem_malloc(VMS_MAXRSS + 1);
5523 if (defesal == NULL) _ckvmssts(SS$_INSFMEM);
5525 struct FAB deffab = cc$rms_fab;
5526 rms_setup_nam(defnam);
5528 rms_bind_fab_nam(deffab, defnam);
5532 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5534 /* RMS needs the esa/esal as a work area if wildcards are involved */
5535 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5537 rms_clear_nam_nop(defnam);
5538 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5539 #ifdef NAM$M_NO_SHORT_UPCASE
5540 if (decc_efs_case_preserve)
5541 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5543 #ifdef NAML$M_OPEN_SPECIAL
5544 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5545 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5547 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5549 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5552 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5555 if (defesal != NULL)
5556 PerlMem_free(defesal);
5557 PerlMem_free(defesa);
5561 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5562 if (*(rms_nam_verl(mynam)) != '\"')
5563 speclen = rms_nam_verl(mynam) - tbuf;
5566 if (*(rms_nam_ver(mynam)) != '\"')
5567 speclen = rms_nam_ver(mynam) - tbuf;
5571 /* If we didn't already trim version, copy down */
5572 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5573 if (speclen > rms_nam_verl(mynam) - tbuf)
5575 (rms_nam_typel(mynam),
5576 rms_nam_verl(mynam),
5577 speclen - (rms_nam_verl(mynam) - tbuf));
5578 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5581 if (speclen > rms_nam_ver(mynam) - tbuf)
5583 (rms_nam_type(mynam),
5585 speclen - (rms_nam_ver(mynam) - tbuf));
5586 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5591 /* Done with these copies of the input files */
5592 /*-------------------------------------------*/
5593 if (vmsfspec != NULL)
5594 PerlMem_free(vmsfspec);
5595 if (tmpfspec != NULL)
5596 PerlMem_free(tmpfspec);
5598 /* If we just had a directory spec on input, $PARSE "helpfully"
5599 * adds an empty name and type for us */
5600 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5601 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5602 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5603 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5604 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5605 speclen = rms_nam_namel(mynam) - tbuf;
5610 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5611 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5612 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5613 speclen = rms_nam_name(mynam) - tbuf;
5616 /* Posix format specifications must have matching quotes */
5617 if (speclen < (VMS_MAXRSS - 1)) {
5618 if (decc_posix_compliant_pathnames && (tbuf[0] == '\"')) {
5619 if ((speclen > 1) && (tbuf[speclen-1] != '\"')) {
5620 tbuf[speclen] = '\"';
5625 tbuf[speclen] = '\0';
5626 if (haslower && !decc_efs_case_preserve) __mystrtolower(tbuf);
5628 /* Have we been working with an expanded, but not resultant, spec? */
5629 /* Also, convert back to Unix syntax if necessary. */
5633 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5634 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5635 rsl = rms_nam_rsll(mynam);
5639 rsl = rms_nam_rsl(mynam);
5643 if (do_tounixspec(tbuf, outbuf ,0 , fs_utf8) == NULL) {
5644 if (out) Safefree(out);
5648 if (outbufl != NULL)
5649 PerlMem_free(outbufl);
5653 else strcpy(outbuf, tbuf);
5656 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
5657 if (tmpfspec == NULL) _ckvmssts(SS$_INSFMEM);
5658 if (do_tounixspec(tbuf,tmpfspec,0,fs_utf8) == NULL) {
5659 if (out) Safefree(out);
5663 PerlMem_free(tmpfspec);
5664 if (outbufl != NULL)
5665 PerlMem_free(outbufl);
5668 strcpy(outbuf,tmpfspec);
5669 PerlMem_free(tmpfspec);
5672 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5673 sts = rms_free_search_context(&myfab); /* Free search context */
5677 if (outbufl != NULL)
5678 PerlMem_free(outbufl);
5682 /* External entry points */
5683 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5684 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5685 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5686 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5687 char *Perl_rmsexpand_utf8
5688 (pTHX_ const char *spec, char *buf, const char *def,
5689 unsigned opt, int * fs_utf8, int * dfs_utf8)
5690 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5691 char *Perl_rmsexpand_utf8_ts
5692 (pTHX_ const char *spec, char *buf, const char *def,
5693 unsigned opt, int * fs_utf8, int * dfs_utf8)
5694 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
5698 ** The following routines are provided to make life easier when
5699 ** converting among VMS-style and Unix-style directory specifications.
5700 ** All will take input specifications in either VMS or Unix syntax. On
5701 ** failure, all return NULL. If successful, the routines listed below
5702 ** return a pointer to a buffer containing the appropriately
5703 ** reformatted spec (and, therefore, subsequent calls to that routine
5704 ** will clobber the result), while the routines of the same names with
5705 ** a _ts suffix appended will return a pointer to a mallocd string
5706 ** containing the appropriately reformatted spec.
5707 ** In all cases, only explicit syntax is altered; no check is made that
5708 ** the resulting string is valid or that the directory in question
5711 ** fileify_dirspec() - convert a directory spec into the name of the
5712 ** directory file (i.e. what you can stat() to see if it's a dir).
5713 ** The style (VMS or Unix) of the result is the same as the style
5714 ** of the parameter passed in.
5715 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5716 ** what you prepend to a filename to indicate what directory it's in).
5717 ** The style (VMS or Unix) of the result is the same as the style
5718 ** of the parameter passed in.
5719 ** tounixpath() - convert a directory spec into a Unix-style path.
5720 ** tovmspath() - convert a directory spec into a VMS-style path.
5721 ** tounixspec() - convert any file spec into a Unix-style file spec.
5722 ** tovmsspec() - convert any file spec into a VMS-style spec.
5723 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5725 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
5726 ** Permission is given to distribute this code as part of the Perl
5727 ** standard distribution under the terms of the GNU General Public
5728 ** License or the Perl Artistic License. Copies of each may be
5729 ** found in the Perl standard distribution.
5732 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5733 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
5735 static char __fileify_retbuf[VMS_MAXRSS];
5736 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
5737 char *retspec, *cp1, *cp2, *lastdir;
5738 char *trndir, *vmsdir;
5739 unsigned short int trnlnm_iter_count;
5741 if (utf8_fl != NULL)
5744 if (!dir || !*dir) {
5745 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5747 dirlen = strlen(dir);
5748 while (dirlen && dir[dirlen-1] == '/') --dirlen;
5749 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
5750 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
5757 if (dirlen > (VMS_MAXRSS - 1)) {
5758 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
5761 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
5762 if (trndir == NULL) _ckvmssts(SS$_INSFMEM);
5763 if (!strpbrk(dir+1,"/]>:") &&
5764 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
5765 strcpy(trndir,*dir == '/' ? dir + 1: dir);
5766 trnlnm_iter_count = 0;
5767 while (!strpbrk(trndir,"/]>:") && my_trnlnm(trndir,trndir,0)) {
5768 trnlnm_iter_count++;
5769 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5771 dirlen = strlen(trndir);
5774 strncpy(trndir,dir,dirlen);
5775 trndir[dirlen] = '\0';
5778 /* At this point we are done with *dir and use *trndir which is a
5779 * copy that can be modified. *dir must not be modified.
5782 /* If we were handed a rooted logical name or spec, treat it like a
5783 * simple directory, so that
5784 * $ Define myroot dev:[dir.]
5785 * ... do_fileify_dirspec("myroot",buf,1) ...
5786 * does something useful.
5788 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
5789 trndir[--dirlen] = '\0';
5790 trndir[dirlen-1] = ']';
5792 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
5793 trndir[--dirlen] = '\0';
5794 trndir[dirlen-1] = '>';
5797 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
5798 /* If we've got an explicit filename, we can just shuffle the string. */
5799 if (*(cp1+1)) hasfilename = 1;
5800 /* Similarly, we can just back up a level if we've got multiple levels
5801 of explicit directories in a VMS spec which ends with directories. */
5803 for (cp2 = cp1; cp2 > trndir; cp2--) {
5805 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
5806 /* fix-me, can not scan EFS file specs backward like this */
5807 *cp2 = *cp1; *cp1 = '\0';
5812 if (*cp2 == '[' || *cp2 == '<') break;
5817 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
5818 if (vmsdir == NULL) _ckvmssts(SS$_INSFMEM);
5819 cp1 = strpbrk(trndir,"]:>");
5820 if (hasfilename || !cp1) { /* Unix-style path or filename */
5821 if (trndir[0] == '.') {
5822 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
5823 PerlMem_free(trndir);
5824 PerlMem_free(vmsdir);
5825 return do_fileify_dirspec("[]",buf,ts,NULL);
5827 else if (trndir[1] == '.' &&
5828 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
5829 PerlMem_free(trndir);
5830 PerlMem_free(vmsdir);
5831 return do_fileify_dirspec("[-]",buf,ts,NULL);
5834 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
5835 dirlen -= 1; /* to last element */
5836 lastdir = strrchr(trndir,'/');
5838 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
5839 /* If we have "/." or "/..", VMSify it and let the VMS code
5840 * below expand it, rather than repeating the code to handle
5841 * relative components of a filespec here */
5843 if (*(cp1+2) == '.') cp1++;
5844 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
5846 if (do_tovmsspec(trndir,vmsdir,0,NULL) == NULL) {
5847 PerlMem_free(trndir);
5848 PerlMem_free(vmsdir);
5851 if (strchr(vmsdir,'/') != NULL) {
5852 /* If do_tovmsspec() returned it, it must have VMS syntax
5853 * delimiters in it, so it's a mixed VMS/Unix spec. We take
5854 * the time to check this here only so we avoid a recursion
5855 * loop; otherwise, gigo.
5857 PerlMem_free(trndir);
5858 PerlMem_free(vmsdir);
5859 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
5862 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
5863 PerlMem_free(trndir);
5864 PerlMem_free(vmsdir);
5867 ret_chr = do_tounixspec(trndir,buf,ts,NULL);
5868 PerlMem_free(trndir);
5869 PerlMem_free(vmsdir);
5873 } while ((cp1 = strstr(cp1,"/.")) != NULL);
5874 lastdir = strrchr(trndir,'/');
5876 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
5878 /* Ditto for specs that end in an MFD -- let the VMS code
5879 * figure out whether it's a real device or a rooted logical. */
5881 /* This should not happen any more. Allowing the fake /000000
5882 * in a UNIX pathname causes all sorts of problems when trying
5883 * to run in UNIX emulation. So the VMS to UNIX conversions
5884 * now remove the fake /000000 directories.
5887 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
5888 if (do_tovmsspec(trndir,vmsdir,0,NULL) == NULL) {
5889 PerlMem_free(trndir);
5890 PerlMem_free(vmsdir);
5893 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
5894 PerlMem_free(trndir);
5895 PerlMem_free(vmsdir);
5898 ret_chr = do_tounixspec(trndir,buf,ts,NULL);
5899 PerlMem_free(trndir);
5900 PerlMem_free(vmsdir);
5905 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
5906 !(lastdir = cp1 = strrchr(trndir,']')) &&
5907 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
5908 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
5911 /* For EFS or ODS-5 look for the last dot */
5912 if (decc_efs_charset) {
5913 cp2 = strrchr(cp1,'.');
5915 if (vms_process_case_tolerant) {
5916 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
5917 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
5918 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
5919 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5920 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5921 (ver || *cp3)))))) {
5922 PerlMem_free(trndir);
5923 PerlMem_free(vmsdir);
5925 set_vaxc_errno(RMS$_DIR);
5930 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
5931 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
5932 !*(cp2+3) || *(cp2+3) != 'R' ||
5933 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
5934 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
5935 (ver || *cp3)))))) {
5936 PerlMem_free(trndir);
5937 PerlMem_free(vmsdir);
5939 set_vaxc_errno(RMS$_DIR);
5943 dirlen = cp2 - trndir;
5947 retlen = dirlen + 6;
5948 if (buf) retspec = buf;
5949 else if (ts) Newx(retspec,retlen+1,char);
5950 else retspec = __fileify_retbuf;
5951 memcpy(retspec,trndir,dirlen);
5952 retspec[dirlen] = '\0';
5954 /* We've picked up everything up to the directory file name.
5955 Now just add the type and version, and we're set. */
5956 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
5957 strcat(retspec,".dir;1");
5959 strcat(retspec,".DIR;1");
5960 PerlMem_free(trndir);
5961 PerlMem_free(vmsdir);
5964 else { /* VMS-style directory spec */
5966 char *esa, *esal, term, *cp;
5969 unsigned long int sts, cmplen, haslower = 0;
5970 unsigned int nam_fnb;
5972 struct FAB dirfab = cc$rms_fab;
5973 rms_setup_nam(savnam);
5974 rms_setup_nam(dirnam);
5976 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
5977 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
5979 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5980 esal = PerlMem_malloc(VMS_MAXRSS);
5981 if (esal == NULL) _ckvmssts(SS$_INSFMEM);
5983 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
5984 rms_bind_fab_nam(dirfab, dirnam);
5985 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
5986 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
5987 #ifdef NAM$M_NO_SHORT_UPCASE
5988 if (decc_efs_case_preserve)
5989 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
5992 for (cp = trndir; *cp; cp++)
5993 if (islower(*cp)) { haslower = 1; break; }
5994 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
5995 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
5996 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
5997 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
6003 PerlMem_free(trndir);
6004 PerlMem_free(vmsdir);
6006 set_vaxc_errno(dirfab.fab$l_sts);
6012 /* Does the file really exist? */
6013 if (sys$search(&dirfab)& STS$K_SUCCESS) {
6014 /* Yes; fake the fnb bits so we'll check type below */
6015 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6017 else { /* No; just work with potential name */
6018 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6021 fab_sts = dirfab.fab$l_sts;
6022 sts = rms_free_search_context(&dirfab);
6026 PerlMem_free(trndir);
6027 PerlMem_free(vmsdir);
6028 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
6034 /* Make sure we are using the right buffer */
6037 my_esa_len = rms_nam_esll(dirnam);
6040 my_esa_len = rms_nam_esl(dirnam);
6042 my_esa[my_esa_len] = '\0';
6043 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6044 cp1 = strchr(my_esa,']');
6045 if (!cp1) cp1 = strchr(my_esa,'>');
6046 if (cp1) { /* Should always be true */
6047 my_esa_len -= cp1 - my_esa - 1;
6048 memmove(my_esa, cp1 + 1, my_esa_len);
6051 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6052 /* Yep; check version while we're at it, if it's there. */
6053 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6054 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6055 /* Something other than .DIR[;1]. Bzzt. */
6056 sts = rms_free_search_context(&dirfab);
6060 PerlMem_free(trndir);
6061 PerlMem_free(vmsdir);
6063 set_vaxc_errno(RMS$_DIR);
6068 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6069 /* They provided at least the name; we added the type, if necessary, */
6070 if (buf) retspec = buf; /* in sys$parse() */
6071 else if (ts) Newx(retspec, my_esa_len + 1, char);
6072 else retspec = __fileify_retbuf;
6073 strcpy(retspec,my_esa);
6074 sts = rms_free_search_context(&dirfab);
6075 PerlMem_free(trndir);
6079 PerlMem_free(vmsdir);
6082 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6083 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6087 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6088 if (cp1 == NULL) { /* should never happen */
6089 sts = rms_free_search_context(&dirfab);
6090 PerlMem_free(trndir);
6094 PerlMem_free(vmsdir);
6099 retlen = strlen(my_esa);
6100 cp1 = strrchr(my_esa,'.');
6101 /* ODS-5 directory specifications can have extra "." in them. */
6102 /* Fix-me, can not scan EFS file specifications backwards */
6103 while (cp1 != NULL) {
6104 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6108 while ((cp1 > my_esa) && (*cp1 != '.'))
6115 if ((cp1) != NULL) {
6116 /* There's more than one directory in the path. Just roll back. */
6118 if (buf) retspec = buf;
6119 else if (ts) Newx(retspec,retlen+7,char);
6120 else retspec = __fileify_retbuf;
6121 strcpy(retspec,my_esa);
6124 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6125 /* Go back and expand rooted logical name */
6126 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6127 #ifdef NAM$M_NO_SHORT_UPCASE
6128 if (decc_efs_case_preserve)
6129 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6131 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6132 sts = rms_free_search_context(&dirfab);
6136 PerlMem_free(trndir);
6137 PerlMem_free(vmsdir);
6139 set_vaxc_errno(dirfab.fab$l_sts);
6143 /* This changes the length of the string of course */
6145 my_esa_len = rms_nam_esll(dirnam);
6147 my_esa_len = rms_nam_esl(dirnam);
6150 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6151 if (buf) retspec = buf;
6152 else if (ts) Newx(retspec,retlen+16,char);
6153 else retspec = __fileify_retbuf;
6154 cp1 = strstr(my_esa,"][");
6155 if (!cp1) cp1 = strstr(my_esa,"]<");
6156 dirlen = cp1 - my_esa;
6157 memcpy(retspec,my_esa,dirlen);
6158 if (!strncmp(cp1+2,"000000]",7)) {
6159 retspec[dirlen-1] = '\0';
6160 /* fix-me Not full ODS-5, just extra dots in directories for now */
6161 cp1 = retspec + dirlen - 1;
6162 while (cp1 > retspec)
6167 if (*(cp1-1) != '^')
6172 if (*cp1 == '.') *cp1 = ']';
6174 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
6175 memmove(cp1+1,"000000]",7);
6179 memmove(retspec+dirlen,cp1+2,retlen-dirlen);
6180 retspec[retlen] = '\0';
6181 /* Convert last '.' to ']' */
6182 cp1 = retspec+retlen-1;
6183 while (*cp != '[') {
6186 /* Do not trip on extra dots in ODS-5 directories */
6187 if ((cp1 == retspec) || (*(cp1-1) != '^'))
6191 if (*cp1 == '.') *cp1 = ']';
6193 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
6194 memmove(cp1+1,"000000]",7);
6198 else { /* This is a top-level dir. Add the MFD to the path. */
6199 if (buf) retspec = buf;
6200 else if (ts) Newx(retspec,retlen+16,char);
6201 else retspec = __fileify_retbuf;
6204 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
6205 strcpy(cp2,":[000000]");
6210 sts = rms_free_search_context(&dirfab);
6211 /* We've set up the string up through the filename. Add the
6212 type and version, and we're done. */
6213 strcat(retspec,".DIR;1");
6215 /* $PARSE may have upcased filespec, so convert output to lower
6216 * case if input contained any lowercase characters. */
6217 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec);
6218 PerlMem_free(trndir);
6222 PerlMem_free(vmsdir);
6225 } /* end of do_fileify_dirspec() */
6227 /* External entry points */
6228 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6229 { return do_fileify_dirspec(dir,buf,0,NULL); }
6230 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6231 { return do_fileify_dirspec(dir,buf,1,NULL); }
6232 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6233 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6234 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6235 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6237 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6238 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6240 static char __pathify_retbuf[VMS_MAXRSS];
6241 unsigned long int retlen;
6242 char *retpath, *cp1, *cp2, *trndir;
6243 unsigned short int trnlnm_iter_count;
6246 if (utf8_fl != NULL)
6249 if (!dir || !*dir) {
6250 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6253 trndir = PerlMem_malloc(VMS_MAXRSS);
6254 if (trndir == NULL) _ckvmssts(SS$_INSFMEM);
6255 if (*dir) strcpy(trndir,dir);
6256 else getcwd(trndir,VMS_MAXRSS - 1);
6258 trnlnm_iter_count = 0;
6259 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6260 && my_trnlnm(trndir,trndir,0)) {
6261 trnlnm_iter_count++;
6262 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6263 trnlen = strlen(trndir);
6265 /* Trap simple rooted lnms, and return lnm:[000000] */
6266 if (!strcmp(trndir+trnlen-2,".]")) {
6267 if (buf) retpath = buf;
6268 else if (ts) Newx(retpath,strlen(dir)+10,char);
6269 else retpath = __pathify_retbuf;
6270 strcpy(retpath,dir);
6271 strcat(retpath,":[000000]");
6272 PerlMem_free(trndir);
6277 /* At this point we do not work with *dir, but the copy in
6278 * *trndir that is modifiable.
6281 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
6282 if (*trndir == '.' && (*(trndir+1) == '\0' ||
6283 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
6284 retlen = 2 + (*(trndir+1) != '\0');
6286 if ( !(cp1 = strrchr(trndir,'/')) &&
6287 !(cp1 = strrchr(trndir,']')) &&
6288 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
6289 if ((cp2 = strchr(cp1,'.')) != NULL &&
6290 (*(cp2-1) != '/' || /* Trailing '.', '..', */
6291 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
6292 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
6293 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
6296 /* For EFS or ODS-5 look for the last dot */
6297 if (decc_efs_charset) {
6298 cp2 = strrchr(cp1,'.');
6300 if (vms_process_case_tolerant) {
6301 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
6302 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
6303 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
6304 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6305 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6306 (ver || *cp3)))))) {
6307 PerlMem_free(trndir);
6309 set_vaxc_errno(RMS$_DIR);
6314 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
6315 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
6316 !*(cp2+3) || *(cp2+3) != 'R' ||
6317 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6318 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6319 (ver || *cp3)))))) {
6320 PerlMem_free(trndir);
6322 set_vaxc_errno(RMS$_DIR);
6326 retlen = cp2 - trndir + 1;
6328 else { /* No file type present. Treat the filename as a directory. */
6329 retlen = strlen(trndir) + 1;
6332 if (buf) retpath = buf;
6333 else if (ts) Newx(retpath,retlen+1,char);
6334 else retpath = __pathify_retbuf;
6335 strncpy(retpath, trndir, retlen-1);
6336 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
6337 retpath[retlen-1] = '/'; /* with '/', add it. */
6338 retpath[retlen] = '\0';
6340 else retpath[retlen-1] = '\0';
6342 else { /* VMS-style directory spec */
6343 char *esa, *esal, *cp;
6346 unsigned long int sts, cmplen, haslower;
6347 struct FAB dirfab = cc$rms_fab;
6349 rms_setup_nam(savnam);
6350 rms_setup_nam(dirnam);
6352 /* If we've got an explicit filename, we can just shuffle the string. */
6353 if ( ( (cp1 = strrchr(trndir,']')) != NULL ||
6354 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) {
6355 if ((cp2 = strchr(cp1,'.')) != NULL) {
6357 if (vms_process_case_tolerant) {
6358 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
6359 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
6360 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
6361 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6362 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6363 (ver || *cp3)))))) {
6364 PerlMem_free(trndir);
6366 set_vaxc_errno(RMS$_DIR);
6371 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
6372 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
6373 !*(cp2+3) || *(cp2+3) != 'R' ||
6374 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6375 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6376 (ver || *cp3)))))) {
6377 PerlMem_free(trndir);
6379 set_vaxc_errno(RMS$_DIR);
6384 else { /* No file type, so just draw name into directory part */
6385 for (cp2 = cp1; *cp2; cp2++) ;
6388 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
6390 /* We've now got a VMS 'path'; fall through */
6393 dirlen = strlen(trndir);
6394 if (trndir[dirlen-1] == ']' ||
6395 trndir[dirlen-1] == '>' ||
6396 trndir[dirlen-1] == ':') { /* It's already a VMS 'path' */
6397 if (buf) retpath = buf;
6398 else if (ts) Newx(retpath,strlen(trndir)+1,char);
6399 else retpath = __pathify_retbuf;
6400 strcpy(retpath,trndir);
6401 PerlMem_free(trndir);
6404 rms_set_fna(dirfab, dirnam, trndir, dirlen);
6405 esa = PerlMem_malloc(VMS_MAXRSS);
6406 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
6408 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6409 esal = PerlMem_malloc(VMS_MAXRSS);
6410 if (esal == NULL) _ckvmssts(SS$_INSFMEM);
6412 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6413 rms_bind_fab_nam(dirfab, dirnam);
6414 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
6415 #ifdef NAM$M_NO_SHORT_UPCASE
6416 if (decc_efs_case_preserve)
6417 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6420 for (cp = trndir; *cp; cp++)
6421 if (islower(*cp)) { haslower = 1; break; }
6423 if (!(sts = (sys$parse(&dirfab)& STS$K_SUCCESS))) {
6424 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
6425 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6426 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
6429 PerlMem_free(trndir);
6434 set_vaxc_errno(dirfab.fab$l_sts);
6440 /* Does the file really exist? */
6441 if (!(sys$search(&dirfab)&STS$K_SUCCESS)) {
6442 if (dirfab.fab$l_sts != RMS$_FNF) {
6444 sts1 = rms_free_search_context(&dirfab);
6445 PerlMem_free(trndir);
6450 set_vaxc_errno(dirfab.fab$l_sts);
6453 dirnam = savnam; /* No; just work with potential name */
6456 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6457 /* Yep; check version while we're at it, if it's there. */
6458 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6459 if (strncmp(rms_nam_typel(dirnam),".DIR;1",cmplen)) {
6461 /* Something other than .DIR[;1]. Bzzt. */
6462 sts2 = rms_free_search_context(&dirfab);
6463 PerlMem_free(trndir);
6468 set_vaxc_errno(RMS$_DIR);
6472 /* Make sure we are using the right buffer */
6474 /* We only need one, clean up the other */
6476 my_esa_len = rms_nam_esll(dirnam);
6479 my_esa_len = rms_nam_esl(dirnam);
6482 /* Null terminate the buffer */
6483 my_esa[my_esa_len] = '\0';
6485 /* OK, the type was fine. Now pull any file name into the
6487 if ((cp1 = strrchr(my_esa,']'))) *(rms_nam_typel(dirnam)) = ']';
6489 cp1 = strrchr(my_esa,'>');
6490 *(rms_nam_typel(dirnam)) = '>';
6493 *(rms_nam_typel(dirnam) + 1) = '\0';
6494 retlen = (rms_nam_typel(dirnam)) - my_esa + 2;
6495 if (buf) retpath = buf;
6496 else if (ts) Newx(retpath,retlen,char);
6497 else retpath = __pathify_retbuf;
6498 strcpy(retpath,my_esa);
6502 sts = rms_free_search_context(&dirfab);
6503 /* $PARSE may have upcased filespec, so convert output to lower
6504 * case if input contained any lowercase characters. */
6505 if (haslower && !decc_efs_case_preserve) __mystrtolower(retpath);
6508 PerlMem_free(trndir);
6510 } /* end of do_pathify_dirspec() */
6512 /* External entry points */
6513 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6514 { return do_pathify_dirspec(dir,buf,0,NULL); }
6515 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
6516 { return do_pathify_dirspec(dir,buf,1,NULL); }
6517 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
6518 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
6519 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
6520 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
6522 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
6523 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
6525 static char __tounixspec_retbuf[VMS_MAXRSS];
6526 char *dirend, *rslt, *cp1, *cp3, *tmp;
6528 int devlen, dirlen, retlen = VMS_MAXRSS;
6529 int expand = 1; /* guarantee room for leading and trailing slashes */
6530 unsigned short int trnlnm_iter_count;
6532 if (utf8_fl != NULL)
6535 if (spec == NULL) return NULL;
6536 if (strlen(spec) > (VMS_MAXRSS-1)) return NULL;
6537 if (buf) rslt = buf;
6539 Newx(rslt, VMS_MAXRSS, char);
6541 else rslt = __tounixspec_retbuf;
6543 /* New VMS specific format needs translation
6544 * glob passes filenames with trailing '\n' and expects this preserved.
6546 if (decc_posix_compliant_pathnames) {
6547 if (strncmp(spec, "\"^UP^", 5) == 0) {
6553 tunix = PerlMem_malloc(VMS_MAXRSS);
6554 if (tunix == NULL) _ckvmssts(SS$_INSFMEM);
6555 strcpy(tunix, spec);
6556 tunix_len = strlen(tunix);
6558 if (tunix[tunix_len - 1] == '\n') {
6559 tunix[tunix_len - 1] = '\"';
6560 tunix[tunix_len] = '\0';
6564 uspec = decc$translate_vms(tunix);
6565 PerlMem_free(tunix);
6566 if ((int)uspec > 0) {
6572 /* If we can not translate it, makemaker wants as-is */
6580 cmp_rslt = 0; /* Presume VMS */
6581 cp1 = strchr(spec, '/');
6585 /* Look for EFS ^/ */
6586 if (decc_efs_charset) {
6587 while (cp1 != NULL) {
6590 /* Found illegal VMS, assume UNIX */
6595 cp1 = strchr(cp1, '/');
6599 /* Look for "." and ".." */
6600 if (decc_filename_unix_report) {
6601 if (spec[0] == '.') {
6602 if ((spec[1] == '\0') || (spec[1] == '\n')) {
6606 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
6612 /* This is already UNIX or at least nothing VMS understands */
6620 dirend = strrchr(spec,']');
6621 if (dirend == NULL) dirend = strrchr(spec,'>');
6622 if (dirend == NULL) dirend = strchr(spec,':');
6623 if (dirend == NULL) {
6628 /* Special case 1 - sys$posix_root = / */
6629 #if __CRTL_VER >= 70000000
6630 if (!decc_disable_posix_root) {
6631 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
6639 /* Special case 2 - Convert NLA0: to /dev/null */
6640 #if __CRTL_VER < 70000000
6641 cmp_rslt = strncmp(spec,"NLA0:", 5);
6643 cmp_rslt = strncmp(spec,"nla0:", 5);
6645 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
6647 if (cmp_rslt == 0) {
6648 strcpy(rslt, "/dev/null");
6651 if (spec[6] != '\0') {
6658 /* Also handle special case "SYS$SCRATCH:" */
6659 #if __CRTL_VER < 70000000
6660 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
6662 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
6664 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
6666 tmp = PerlMem_malloc(VMS_MAXRSS);
6667 if (tmp == NULL) _ckvmssts(SS$_INSFMEM);
6668 if (cmp_rslt == 0) {
6671 islnm = my_trnlnm(tmp, "TMP", 0);
6673 strcpy(rslt, "/tmp");
6676 if (spec[12] != '\0') {
6684 if (*cp2 != '[' && *cp2 != '<') {
6687 else { /* the VMS spec begins with directories */
6689 if (*cp2 == ']' || *cp2 == '>') {
6690 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
6694 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
6695 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
6696 if (ts) Safefree(rslt);
6700 trnlnm_iter_count = 0;
6703 while (*cp3 != ':' && *cp3) cp3++;
6705 if (strchr(cp3,']') != NULL) break;
6706 trnlnm_iter_count++;
6707 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
6708 } while (vmstrnenv(tmp,tmp,0,fildev,0));
6710 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
6711 retlen = devlen + dirlen;
6712 Renew(rslt,retlen+1+2*expand,char);
6718 *(cp1++) = *(cp3++);
6719 if (cp1 - rslt > (VMS_MAXRSS - 1) && !ts && !buf) {
6721 return NULL; /* No room */
6726 if ((*cp2 == '^')) {
6727 /* EFS file escape, pass the next character as is */
6728 /* Fix me: HEX encoding for Unicode not implemented */
6731 else if ( *cp2 == '.') {
6732 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
6733 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
6740 for (; cp2 <= dirend; cp2++) {
6741 if ((*cp2 == '^')) {
6742 /* EFS file escape, pass the next character as is */
6743 /* Fix me: HEX encoding for Unicode not implemented */
6744 *(cp1++) = *(++cp2);
6745 /* An escaped dot stays as is -- don't convert to slash */
6746 if (*cp2 == '.') cp2++;
6750 if (*(cp2+1) == '[') cp2++;
6752 else if (*cp2 == ']' || *cp2 == '>') {
6753 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
6755 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
6757 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
6758 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
6759 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
6760 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
6761 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
6763 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
6764 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
6768 else if (*cp2 == '-') {
6769 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
6770 while (*cp2 == '-') {
6772 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
6774 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
6775 if (ts) Safefree(rslt); /* filespecs like */
6776 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
6780 else *(cp1++) = *cp2;
6782 else *(cp1++) = *cp2;
6785 if ((*cp2 == '^') && (*(cp2+1) == '.')) cp2++; /* '^.' --> '.' */
6786 *(cp1++) = *(cp2++);
6790 /* This still leaves /000000/ when working with a
6791 * VMS device root or concealed root.
6797 ulen = strlen(rslt);
6799 /* Get rid of "000000/ in rooted filespecs */
6801 zeros = strstr(rslt, "/000000/");
6802 if (zeros != NULL) {
6804 mlen = ulen - (zeros - rslt) - 7;
6805 memmove(zeros, &zeros[7], mlen);
6814 } /* end of do_tounixspec() */
6816 /* External entry points */
6817 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
6818 { return do_tounixspec(spec,buf,0, NULL); }
6819 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
6820 { return do_tounixspec(spec,buf,1, NULL); }
6821 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
6822 { return do_tounixspec(spec,buf,0, utf8_fl); }
6823 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
6824 { return do_tounixspec(spec,buf,1, utf8_fl); }
6826 #if __CRTL_VER >= 70200000 && !defined(__VAX)
6829 This procedure is used to identify if a path is based in either
6830 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
6831 it returns the OpenVMS format directory for it.
6833 It is expecting specifications of only '/' or '/xxxx/'
6835 If a posix root does not exist, or 'xxxx' is not a directory
6836 in the posix root, it returns a failure.
6838 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
6840 It is used only internally by posix_to_vmsspec_hardway().
6843 static int posix_root_to_vms
6844 (char *vmspath, int vmspath_len,
6845 const char *unixpath,
6846 const int * utf8_fl)
6849 struct FAB myfab = cc$rms_fab;
6850 rms_setup_nam(mynam);
6851 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6852 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6853 char * esa, * esal, * rsa, * rsal;
6860 unixlen = strlen(unixpath);
6865 #if __CRTL_VER >= 80200000
6866 /* If not a posix spec already, convert it */
6867 if (decc_posix_compliant_pathnames) {
6868 if (strncmp(unixpath,"\"^UP^",5) != 0) {
6869 sprintf(vmspath,"\"^UP^%s\"",unixpath);
6872 /* This is already a VMS specification, no conversion */
6874 strncpy(vmspath,unixpath, vmspath_len);
6883 /* Check to see if this is under the POSIX root */
6884 if (decc_disable_posix_root) {
6888 /* Skip leading / */
6889 if (unixpath[0] == '/') {
6895 strcpy(vmspath,"SYS$POSIX_ROOT:");
6897 /* If this is only the / , or blank, then... */
6898 if (unixpath[0] == '\0') {
6899 /* by definition, this is the answer */
6903 /* Need to look up a directory */
6907 /* Copy and add '^' escape characters as needed */
6910 while (unixpath[i] != 0) {
6913 j += copy_expand_unix_filename_escape
6914 (&vmspath[j], &unixpath[i], &k, utf8_fl);
6918 path_len = strlen(vmspath);
6919 if (vmspath[path_len - 1] == '/')
6921 vmspath[path_len] = ']';
6923 vmspath[path_len] = '\0';
6926 vmspath[vmspath_len] = 0;
6927 if (unixpath[unixlen - 1] == '/')
6929 esal = PerlMem_malloc(VMS_MAXRSS);
6930 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6931 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
6932 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6933 rsal = PerlMem_malloc(VMS_MAXRSS);
6934 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6935 rsa = PerlMem_malloc(NAM$C_MAXRSS + 1);
6936 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6937 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
6938 rms_bind_fab_nam(myfab, mynam);
6939 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
6940 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
6941 if (decc_efs_case_preserve)
6942 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
6943 #ifdef NAML$M_OPEN_SPECIAL
6944 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
6947 /* Set up the remaining naml fields */
6948 sts = sys$parse(&myfab);
6950 /* It failed! Try again as a UNIX filespec */
6959 /* get the Device ID and the FID */
6960 sts = sys$search(&myfab);
6962 /* These are no longer needed */
6967 /* on any failure, returned the POSIX ^UP^ filespec */
6972 specdsc.dsc$a_pointer = vmspath;
6973 specdsc.dsc$w_length = vmspath_len;
6975 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
6976 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
6977 sts = lib$fid_to_name
6978 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
6980 /* on any failure, returned the POSIX ^UP^ filespec */
6982 /* This can happen if user does not have permission to read directories */
6983 if (strncmp(unixpath,"\"^UP^",5) != 0)
6984 sprintf(vmspath,"\"^UP^%s\"",unixpath);
6986 strcpy(vmspath, unixpath);
6989 vmspath[specdsc.dsc$w_length] = 0;
6991 /* Are we expecting a directory? */
6992 if (dir_flag != 0) {
6998 i = specdsc.dsc$w_length - 1;
7002 /* Version must be '1' */
7003 if (vmspath[i--] != '1')
7005 /* Version delimiter is one of ".;" */
7006 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7009 if (vmspath[i--] != 'R')
7011 if (vmspath[i--] != 'I')
7013 if (vmspath[i--] != 'D')
7015 if (vmspath[i--] != '.')
7017 eptr = &vmspath[i+1];
7019 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7020 if (vmspath[i-1] != '^') {
7028 /* Get rid of 6 imaginary zero directory filename */
7029 vmspath[i+1] = '\0';
7033 if (vmspath[i] == '0')
7047 /* /dev/mumble needs to be handled special.
7048 /dev/null becomes NLA0:, And there is the potential for other stuff
7049 like /dev/tty which may need to be mapped to something.
7053 slash_dev_special_to_vms
7054 (const char * unixptr,
7064 nextslash = strchr(unixptr, '/');
7065 len = strlen(unixptr);
7066 if (nextslash != NULL)
7067 len = nextslash - unixptr;
7068 cmp = strncmp("null", unixptr, 5);
7070 if (vmspath_len >= 6) {
7071 strcpy(vmspath, "_NLA0:");
7078 /* The built in routines do not understand perl's special needs, so
7079 doing a manual conversion from UNIX to VMS
7081 If the utf8_fl is not null and points to a non-zero value, then
7082 treat 8 bit characters as UTF-8.
7084 The sequence starting with '$(' and ending with ')' will be passed
7085 through with out interpretation instead of being escaped.
7088 static int posix_to_vmsspec_hardway
7089 (char *vmspath, int vmspath_len,
7090 const char *unixpath,
7095 const char *unixptr;
7096 const char *unixend;
7098 const char *lastslash;
7099 const char *lastdot;
7105 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7106 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7108 if (utf8_fl != NULL)
7114 /* Ignore leading "/" characters */
7115 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7118 unixlen = strlen(unixptr);
7120 /* Do nothing with blank paths */
7127 /* This could have a "^UP^ on the front */
7128 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7134 lastslash = strrchr(unixptr,'/');
7135 lastdot = strrchr(unixptr,'.');
7136 unixend = strrchr(unixptr,'\"');
7137 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7138 unixend = unixptr + unixlen;
7141 /* last dot is last dot or past end of string */
7142 if (lastdot == NULL)
7143 lastdot = unixptr + unixlen;
7145 /* if no directories, set last slash to beginning of string */
7146 if (lastslash == NULL) {
7147 lastslash = unixptr;
7150 /* Watch out for trailing "." after last slash, still a directory */
7151 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7152 lastslash = unixptr + unixlen;
7155 /* Watch out for traiing ".." after last slash, still a directory */
7156 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7157 lastslash = unixptr + unixlen;
7160 /* dots in directories are aways escaped */
7161 if (lastdot < lastslash)
7162 lastdot = unixptr + unixlen;
7165 /* if (unixptr < lastslash) then we are in a directory */
7172 /* Start with the UNIX path */
7173 if (*unixptr != '/') {
7174 /* relative paths */
7176 /* If allowing logical names on relative pathnames, then handle here */
7177 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7178 !decc_posix_compliant_pathnames) {
7184 /* Find the next slash */
7185 nextslash = strchr(unixptr,'/');
7187 esa = PerlMem_malloc(vmspath_len);
7188 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7190 trn = PerlMem_malloc(VMS_MAXRSS);
7191 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7193 if (nextslash != NULL) {
7195 seg_len = nextslash - unixptr;
7196 strncpy(esa, unixptr, seg_len);
7200 strcpy(esa, unixptr);
7201 seg_len = strlen(unixptr);
7203 /* trnlnm(section) */
7204 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7207 /* Now fix up the directory */
7209 /* Split up the path to find the components */
7210 sts = vms_split_path
7229 /* A logical name must be a directory or the full
7230 specification. It is only a full specification if
7231 it is the only component */
7232 if ((unixptr[seg_len] == '\0') ||
7233 (unixptr[seg_len+1] == '\0')) {
7235 /* Is a directory being required? */
7236 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7237 /* Not a logical name */
7242 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7243 /* This must be a directory */
7244 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7245 strcpy(vmsptr, esa);
7246 vmslen=strlen(vmsptr);
7247 vmsptr[vmslen] = ':';
7249 vmsptr[vmslen] = '\0';
7257 /* must be dev/directory - ignore version */
7258 if ((n_len + e_len) != 0)
7261 /* transfer the volume */
7262 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7263 strncpy(vmsptr, v_spec, v_len);
7269 /* unroot the rooted directory */
7270 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7272 r_spec[r_len - 1] = ']';
7274 /* This should not be there, but nothing is perfect */
7276 cmp = strcmp(&r_spec[1], "000000.");
7286 strncpy(vmsptr, r_spec, r_len);
7292 /* Bring over the directory. */
7294 ((d_len + vmslen) < vmspath_len)) {
7296 d_spec[d_len - 1] = ']';
7298 cmp = strcmp(&d_spec[1], "000000.");
7309 /* Remove the redundant root */
7317 strncpy(vmsptr, d_spec, d_len);
7331 if (lastslash > unixptr) {
7334 /* skip leading ./ */
7336 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7342 /* Are we still in a directory? */
7343 if (unixptr <= lastslash) {
7348 /* if not backing up, then it is relative forward. */
7349 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7350 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7358 /* Perl wants an empty directory here to tell the difference
7359 * between a DCL commmand and a filename
7368 /* Handle two special files . and .. */
7369 if (unixptr[0] == '.') {
7370 if (&unixptr[1] == unixend) {
7377 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7388 else { /* Absolute PATH handling */
7392 /* Need to find out where root is */
7394 /* In theory, this procedure should never get an absolute POSIX pathname
7395 * that can not be found on the POSIX root.
7396 * In practice, that can not be relied on, and things will show up
7397 * here that are a VMS device name or concealed logical name instead.
7398 * So to make things work, this procedure must be tolerant.
7400 esa = PerlMem_malloc(vmspath_len);
7401 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7404 nextslash = strchr(&unixptr[1],'/');
7406 if (nextslash != NULL) {
7408 seg_len = nextslash - &unixptr[1];
7409 strncpy(vmspath, unixptr, seg_len + 1);
7410 vmspath[seg_len+1] = 0;
7413 cmp = strncmp(vmspath, "dev", 4);
7415 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
7416 if (sts = SS$_NORMAL)
7420 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
7423 if ($VMS_STATUS_SUCCESS(sts)) {
7424 /* This is verified to be a real path */
7426 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
7427 if ($VMS_STATUS_SUCCESS(sts)) {
7428 strcpy(vmspath, esa);
7429 vmslen = strlen(vmspath);
7430 vmsptr = vmspath + vmslen;
7432 if (unixptr < lastslash) {
7441 cmp = strcmp(rptr,"000000.");
7446 } /* removing 6 zeros */
7447 } /* vmslen < 7, no 6 zeros possible */
7448 } /* Not in a directory */
7449 } /* Posix root found */
7451 /* No posix root, fall back to default directory */
7452 strcpy(vmspath, "SYS$DISK:[");
7453 vmsptr = &vmspath[10];
7455 if (unixptr > lastslash) {
7464 } /* end of verified real path handling */
7469 /* Ok, we have a device or a concealed root that is not in POSIX
7470 * or we have garbage. Make the best of it.
7473 /* Posix to VMS destroyed this, so copy it again */
7474 strncpy(vmspath, &unixptr[1], seg_len);
7475 vmspath[seg_len] = 0;
7477 vmsptr = &vmsptr[vmslen];
7480 /* Now do we need to add the fake 6 zero directory to it? */
7482 if ((*lastslash == '/') && (nextslash < lastslash)) {
7483 /* No there is another directory */
7490 /* now we have foo:bar or foo:[000000]bar to decide from */
7491 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
7493 if (!islnm && !decc_posix_compliant_pathnames) {
7495 cmp = strncmp("bin", vmspath, 4);
7497 /* bin => SYS$SYSTEM: */
7498 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
7501 /* tmp => SYS$SCRATCH: */
7502 cmp = strncmp("tmp", vmspath, 4);
7504 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
7509 trnend = islnm ? islnm - 1 : 0;
7511 /* if this was a logical name, ']' or '>' must be present */
7512 /* if not a logical name, then assume a device and hope. */
7513 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
7515 /* if log name and trailing '.' then rooted - treat as device */
7516 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
7518 /* Fix me, if not a logical name, a device lookup should be
7519 * done to see if the device is file structured. If the device
7520 * is not file structured, the 6 zeros should not be put on.
7522 * As it is, perl is occasionally looking for dev:[000000]tty.
7523 * which looks a little strange.
7525 * Not that easy to detect as "/dev" may be file structured with
7526 * special device files.
7529 if ((add_6zero == 0) && (*nextslash == '/') &&
7530 (&nextslash[1] == unixend)) {
7531 /* No real directory present */
7536 /* Put the device delimiter on */
7539 unixptr = nextslash;
7542 /* Start directory if needed */
7543 if (!islnm || add_6zero) {
7549 /* add fake 000000] if needed */
7562 } /* non-POSIX translation */
7564 } /* End of relative/absolute path handling */
7566 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
7573 if (dir_start != 0) {
7575 /* First characters in a directory are handled special */
7576 while ((*unixptr == '/') ||
7577 ((*unixptr == '.') &&
7578 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
7579 (&unixptr[1]==unixend)))) {
7584 /* Skip redundant / in specification */
7585 while ((*unixptr == '/') && (dir_start != 0)) {
7588 if (unixptr == lastslash)
7591 if (unixptr == lastslash)
7594 /* Skip redundant ./ characters */
7595 while ((*unixptr == '.') &&
7596 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
7599 if (unixptr == lastslash)
7601 if (*unixptr == '/')
7604 if (unixptr == lastslash)
7607 /* Skip redundant ../ characters */
7608 while ((*unixptr == '.') && (unixptr[1] == '.') &&
7609 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
7610 /* Set the backing up flag */
7616 unixptr++; /* first . */
7617 unixptr++; /* second . */
7618 if (unixptr == lastslash)
7620 if (*unixptr == '/') /* The slash */
7623 if (unixptr == lastslash)
7626 /* To do: Perl expects /.../ to be translated to [...] on VMS */
7627 /* Not needed when VMS is pretending to be UNIX. */
7629 /* Is this loop stuck because of too many dots? */
7630 if (loop_flag == 0) {
7631 /* Exit the loop and pass the rest through */
7636 /* Are we done with directories yet? */
7637 if (unixptr >= lastslash) {
7639 /* Watch out for trailing dots */
7648 if (*unixptr == '/')
7652 /* Have we stopped backing up? */
7657 /* dir_start continues to be = 1 */
7659 if (*unixptr == '-') {
7661 *vmsptr++ = *unixptr++;
7665 /* Now are we done with directories yet? */
7666 if (unixptr >= lastslash) {
7668 /* Watch out for trailing dots */
7684 if (unixptr >= unixend)
7687 /* Normal characters - More EFS work probably needed */
7693 /* remove multiple / */
7694 while (unixptr[1] == '/') {
7697 if (unixptr == lastslash) {
7698 /* Watch out for trailing dots */
7710 /* To do: Perl expects /.../ to be translated to [...] on VMS */
7711 /* Not needed when VMS is pretending to be UNIX. */
7715 if (unixptr != unixend)
7720 if ((unixptr < lastdot) || (unixptr < lastslash) ||
7721 (&unixptr[1] == unixend)) {
7727 /* trailing dot ==> '^..' on VMS */
7728 if (unixptr == unixend) {
7736 *vmsptr++ = *unixptr++;
7740 if (quoted && (&unixptr[1] == unixend)) {
7744 in_cnt = copy_expand_unix_filename_escape
7745 (vmsptr, unixptr, &out_cnt, utf8_fl);
7755 in_cnt = copy_expand_unix_filename_escape
7756 (vmsptr, unixptr, &out_cnt, utf8_fl);
7763 /* Make sure directory is closed */
7764 if (unixptr == lastslash) {
7766 vmsptr2 = vmsptr - 1;
7768 if (*vmsptr2 != ']') {
7771 /* directories do not end in a dot bracket */
7772 if (*vmsptr2 == '.') {
7776 if (*vmsptr2 != '^') {
7777 vmsptr--; /* back up over the dot */
7785 /* Add a trailing dot if a file with no extension */
7786 vmsptr2 = vmsptr - 1;
7788 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
7789 (*vmsptr2 != ')') && (*lastdot != '.')) {
7800 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
7801 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
7806 /* If a UTF8 flag is being passed, honor it */
7808 if (utf8_fl != NULL) {
7809 utf8_flag = *utf8_fl;
7814 /* If there is a possibility of UTF8, then if any UTF8 characters
7815 are present, then they must be converted to VTF-7
7817 result = strcpy(rslt, path); /* FIX-ME */
7820 result = strcpy(rslt, path);
7826 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
7827 static char *mp_do_tovmsspec
7828 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
7829 static char __tovmsspec_retbuf[VMS_MAXRSS];
7830 char *rslt, *dirend;
7835 unsigned long int infront = 0, hasdir = 1;
7838 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7839 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7841 if (path == NULL) return NULL;
7842 rslt_len = VMS_MAXRSS-1;
7843 if (buf) rslt = buf;
7844 else if (ts) Newx(rslt, VMS_MAXRSS, char);
7845 else rslt = __tovmsspec_retbuf;
7847 /* '.' and '..' are "[]" and "[-]" for a quick check */
7848 if (path[0] == '.') {
7849 if (path[1] == '\0') {
7851 if (utf8_flag != NULL)
7856 if (path[1] == '.' && path[2] == '\0') {
7858 if (utf8_flag != NULL)
7865 /* Posix specifications are now a native VMS format */
7866 /*--------------------------------------------------*/
7867 #if __CRTL_VER >= 80200000 && !defined(__VAX)
7868 if (decc_posix_compliant_pathnames) {
7869 if (strncmp(path,"\"^UP^",5) == 0) {
7870 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
7876 /* This is really the only way to see if this is already in VMS format */
7877 sts = vms_split_path
7892 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
7893 replacement, because the above parse just took care of most of
7894 what is needed to do vmspath when the specification is already
7897 And if it is not already, it is easier to do the conversion as
7898 part of this routine than to call this routine and then work on
7902 /* If VMS punctuation was found, it is already VMS format */
7903 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
7904 if (utf8_flag != NULL)
7909 /* Now, what to do with trailing "." cases where there is no
7910 extension? If this is a UNIX specification, and EFS characters
7911 are enabled, then the trailing "." should be converted to a "^.".
7912 But if this was already a VMS specification, then it should be
7915 So in the case of ambiguity, leave the specification alone.
7919 /* If there is a possibility of UTF8, then if any UTF8 characters
7920 are present, then they must be converted to VTF-7
7922 if (utf8_flag != NULL)
7928 dirend = strrchr(path,'/');
7930 if (dirend == NULL) {
7931 /* If we get here with no UNIX directory delimiters, then this is
7932 not a complete file specification, either garbage a UNIX glob
7933 specification that can not be converted to a VMS wildcard, or
7934 it a UNIX shell macro. MakeMaker wants these passed through AS-IS,
7935 so apparently other programs expect this also.
7937 utf8 flag setting needs to be preserved.
7943 /* If POSIX mode active, handle the conversion */
7944 #if __CRTL_VER >= 80200000 && !defined(__VAX)
7945 if (decc_efs_charset) {
7946 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
7951 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
7952 if (!*(dirend+2)) dirend +=2;
7953 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
7954 if (decc_efs_charset == 0) {
7955 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
7961 lastdot = strrchr(cp2,'.');
7967 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
7969 if (decc_disable_posix_root) {
7970 strcpy(rslt,"sys$disk:[000000]");
7973 strcpy(rslt,"sys$posix_root:[000000]");
7975 if (utf8_flag != NULL)
7979 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
7981 trndev = PerlMem_malloc(VMS_MAXRSS);
7982 if (trndev == NULL) _ckvmssts(SS$_INSFMEM);
7983 islnm = my_trnlnm(rslt,trndev,0);
7985 /* DECC special handling */
7987 if (strcmp(rslt,"bin") == 0) {
7988 strcpy(rslt,"sys$system");
7991 islnm = my_trnlnm(rslt,trndev,0);
7993 else if (strcmp(rslt,"tmp") == 0) {
7994 strcpy(rslt,"sys$scratch");
7997 islnm = my_trnlnm(rslt,trndev,0);
7999 else if (!decc_disable_posix_root) {
8000 strcpy(rslt, "sys$posix_root");
8004 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8005 islnm = my_trnlnm(rslt,trndev,0);
8007 else if (strcmp(rslt,"dev") == 0) {
8008 if (strncmp(cp2,"/null", 5) == 0) {
8009 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8010 strcpy(rslt,"NLA0");
8014 islnm = my_trnlnm(rslt,trndev,0);
8020 trnend = islnm ? strlen(trndev) - 1 : 0;
8021 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8022 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8023 /* If the first element of the path is a logical name, determine
8024 * whether it has to be translated so we can add more directories. */
8025 if (!islnm || rooted) {
8028 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8032 if (cp2 != dirend) {
8033 strcpy(rslt,trndev);
8034 cp1 = rslt + trnend;
8041 if (decc_disable_posix_root) {
8047 PerlMem_free(trndev);
8052 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8053 cp2 += 2; /* skip over "./" - it's redundant */
8054 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8056 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8057 *(cp1++) = '-'; /* "../" --> "-" */
8060 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8061 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8062 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8063 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8066 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8067 /* Escape the extra dots in EFS file specifications */
8070 if (cp2 > dirend) cp2 = dirend;
8072 else *(cp1++) = '.';
8074 for (; cp2 < dirend; cp2++) {
8076 if (*(cp2-1) == '/') continue;
8077 if (*(cp1-1) != '.') *(cp1++) = '.';
8080 else if (!infront && *cp2 == '.') {
8081 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8082 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
8083 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8084 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
8085 else if (*(cp1-2) == '[') *(cp1-1) = '-';
8086 else { /* back up over previous directory name */
8088 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
8089 if (*(cp1-1) == '[') {
8090 memcpy(cp1,"000000.",7);
8095 if (cp2 == dirend) break;
8097 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8098 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8099 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8100 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8102 *(cp1++) = '.'; /* Simulate trailing '/' */
8103 cp2 += 2; /* for loop will incr this to == dirend */
8105 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8108 if (decc_efs_charset == 0)
8109 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8111 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
8117 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
8119 if (decc_efs_charset == 0)
8126 else *(cp1++) = *cp2;
8130 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8131 if (hasdir) *(cp1++) = ']';
8132 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
8133 /* fixme for ODS5 */
8140 if (decc_efs_charset == 0)
8151 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8152 decc_readdir_dropdotnotype) {
8157 /* trailing dot ==> '^..' on VMS */
8164 *(cp1++) = *(cp2++);
8169 /* This could be a macro to be passed through */
8170 *(cp1++) = *(cp2++);
8172 const char * save_cp2;
8176 /* paranoid check */
8182 *(cp1++) = *(cp2++);
8183 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8184 *(cp1++) = *(cp2++);
8185 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8186 *(cp1++) = *(cp2++);
8189 *(cp1++) = *(cp2++);
8193 if (is_macro == 0) {
8194 /* Not really a macro - never mind */
8207 /* Don't escape again if following character is
8208 * already something we escape.
8210 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8211 *(cp1++) = *(cp2++);
8214 /* But otherwise fall through and escape it. */
8232 *(cp1++) = *(cp2++);
8235 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
8236 * which is wrong. UNIX notation should be ".dir." unless
8237 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8238 * changing this behavior could break more things at this time.
8239 * efs character set effectively does not allow "." to be a version
8240 * delimiter as a further complication about changing this.
8242 if (decc_filename_unix_report != 0) {
8245 *(cp1++) = *(cp2++);
8248 *(cp1++) = *(cp2++);
8251 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8255 /* Fix me for "^]", but that requires making sure that you do
8256 * not back up past the start of the filename
8258 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8263 if (utf8_flag != NULL)
8267 } /* end of do_tovmsspec() */
8269 /* External entry points */
8270 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8271 { return do_tovmsspec(path,buf,0,NULL); }
8272 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8273 { return do_tovmsspec(path,buf,1,NULL); }
8274 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8275 { return do_tovmsspec(path,buf,0,utf8_fl); }
8276 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8277 { return do_tovmsspec(path,buf,1,utf8_fl); }
8279 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8280 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8281 static char __tovmspath_retbuf[VMS_MAXRSS];
8283 char *pathified, *vmsified, *cp;
8285 if (path == NULL) return NULL;
8286 pathified = PerlMem_malloc(VMS_MAXRSS);
8287 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8288 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
8289 PerlMem_free(pathified);
8295 Newx(vmsified, VMS_MAXRSS, char);
8296 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8297 PerlMem_free(pathified);
8298 if (vmsified) Safefree(vmsified);
8301 PerlMem_free(pathified);
8306 vmslen = strlen(vmsified);
8307 Newx(cp,vmslen+1,char);
8308 memcpy(cp,vmsified,vmslen);
8314 strcpy(__tovmspath_retbuf,vmsified);
8316 return __tovmspath_retbuf;
8319 } /* end of do_tovmspath() */
8321 /* External entry points */
8322 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
8323 { return do_tovmspath(path,buf,0, NULL); }
8324 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
8325 { return do_tovmspath(path,buf,1, NULL); }
8326 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
8327 { return do_tovmspath(path,buf,0,utf8_fl); }
8328 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
8329 { return do_tovmspath(path,buf,1,utf8_fl); }
8332 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
8333 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8334 static char __tounixpath_retbuf[VMS_MAXRSS];
8336 char *pathified, *unixified, *cp;
8338 if (path == NULL) return NULL;
8339 pathified = PerlMem_malloc(VMS_MAXRSS);
8340 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8341 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
8342 PerlMem_free(pathified);
8348 Newx(unixified, VMS_MAXRSS, char);
8350 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
8351 PerlMem_free(pathified);
8352 if (unixified) Safefree(unixified);
8355 PerlMem_free(pathified);
8360 unixlen = strlen(unixified);
8361 Newx(cp,unixlen+1,char);
8362 memcpy(cp,unixified,unixlen);
8364 Safefree(unixified);
8368 strcpy(__tounixpath_retbuf,unixified);
8369 Safefree(unixified);
8370 return __tounixpath_retbuf;
8373 } /* end of do_tounixpath() */
8375 /* External entry points */
8376 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
8377 { return do_tounixpath(path,buf,0,NULL); }
8378 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
8379 { return do_tounixpath(path,buf,1,NULL); }
8380 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8381 { return do_tounixpath(path,buf,0,utf8_fl); }
8382 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8383 { return do_tounixpath(path,buf,1,utf8_fl); }
8386 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
8388 *****************************************************************************
8390 * Copyright (C) 1989-1994, 2007 by *
8391 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
8393 * Permission is hereby granted for the reproduction of this software *
8394 * on condition that this copyright notice is included in source *
8395 * distributions of the software. The code may be modified and *
8396 * distributed under the same terms as Perl itself. *
8398 * 27-Aug-1994 Modified for inclusion in perl5 *
8399 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
8400 *****************************************************************************
8404 * getredirection() is intended to aid in porting C programs
8405 * to VMS (Vax-11 C). The native VMS environment does not support
8406 * '>' and '<' I/O redirection, or command line wild card expansion,
8407 * or a command line pipe mechanism using the '|' AND background
8408 * command execution '&'. All of these capabilities are provided to any
8409 * C program which calls this procedure as the first thing in the
8411 * The piping mechanism will probably work with almost any 'filter' type
8412 * of program. With suitable modification, it may useful for other
8413 * portability problems as well.
8415 * Author: Mark Pizzolato (mark AT infocomm DOT com)
8419 struct list_item *next;
8423 static void add_item(struct list_item **head,
8424 struct list_item **tail,
8428 static void mp_expand_wild_cards(pTHX_ char *item,
8429 struct list_item **head,
8430 struct list_item **tail,
8433 static int background_process(pTHX_ int argc, char **argv);
8435 static void pipe_and_fork(pTHX_ char **cmargv);
8437 /*{{{ void getredirection(int *ac, char ***av)*/
8439 mp_getredirection(pTHX_ int *ac, char ***av)
8441 * Process vms redirection arg's. Exit if any error is seen.
8442 * If getredirection() processes an argument, it is erased
8443 * from the vector. getredirection() returns a new argc and argv value.
8444 * In the event that a background command is requested (by a trailing "&"),
8445 * this routine creates a background subprocess, and simply exits the program.
8447 * Warning: do not try to simplify the code for vms. The code
8448 * presupposes that getredirection() is called before any data is
8449 * read from stdin or written to stdout.
8451 * Normal usage is as follows:
8457 * getredirection(&argc, &argv);
8461 int argc = *ac; /* Argument Count */
8462 char **argv = *av; /* Argument Vector */
8463 char *ap; /* Argument pointer */
8464 int j; /* argv[] index */
8465 int item_count = 0; /* Count of Items in List */
8466 struct list_item *list_head = 0; /* First Item in List */
8467 struct list_item *list_tail; /* Last Item in List */
8468 char *in = NULL; /* Input File Name */
8469 char *out = NULL; /* Output File Name */
8470 char *outmode = "w"; /* Mode to Open Output File */
8471 char *err = NULL; /* Error File Name */
8472 char *errmode = "w"; /* Mode to Open Error File */
8473 int cmargc = 0; /* Piped Command Arg Count */
8474 char **cmargv = NULL;/* Piped Command Arg Vector */
8477 * First handle the case where the last thing on the line ends with
8478 * a '&'. This indicates the desire for the command to be run in a
8479 * subprocess, so we satisfy that desire.
8482 if (0 == strcmp("&", ap))
8483 exit(background_process(aTHX_ --argc, argv));
8484 if (*ap && '&' == ap[strlen(ap)-1])
8486 ap[strlen(ap)-1] = '\0';
8487 exit(background_process(aTHX_ argc, argv));
8490 * Now we handle the general redirection cases that involve '>', '>>',
8491 * '<', and pipes '|'.
8493 for (j = 0; j < argc; ++j)
8495 if (0 == strcmp("<", argv[j]))
8499 fprintf(stderr,"No input file after < on command line");
8500 exit(LIB$_WRONUMARG);
8505 if ('<' == *(ap = argv[j]))
8510 if (0 == strcmp(">", ap))
8514 fprintf(stderr,"No output file after > on command line");
8515 exit(LIB$_WRONUMARG);
8534 fprintf(stderr,"No output file after > or >> on command line");
8535 exit(LIB$_WRONUMARG);
8539 if (('2' == *ap) && ('>' == ap[1]))
8556 fprintf(stderr,"No output file after 2> or 2>> on command line");
8557 exit(LIB$_WRONUMARG);
8561 if (0 == strcmp("|", argv[j]))
8565 fprintf(stderr,"No command into which to pipe on command line");
8566 exit(LIB$_WRONUMARG);
8568 cmargc = argc-(j+1);
8569 cmargv = &argv[j+1];
8573 if ('|' == *(ap = argv[j]))
8581 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
8584 * Allocate and fill in the new argument vector, Some Unix's terminate
8585 * the list with an extra null pointer.
8587 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
8588 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8590 for (j = 0; j < item_count; ++j, list_head = list_head->next)
8591 argv[j] = list_head->value;
8597 fprintf(stderr,"'|' and '>' may not both be specified on command line");
8598 exit(LIB$_INVARGORD);
8600 pipe_and_fork(aTHX_ cmargv);
8603 /* Check for input from a pipe (mailbox) */
8605 if (in == NULL && 1 == isapipe(0))
8607 char mbxname[L_tmpnam];
8609 long int dvi_item = DVI$_DEVBUFSIZ;
8610 $DESCRIPTOR(mbxnam, "");
8611 $DESCRIPTOR(mbxdevnam, "");
8613 /* Input from a pipe, reopen it in binary mode to disable */
8614 /* carriage control processing. */
8616 fgetname(stdin, mbxname);
8617 mbxnam.dsc$a_pointer = mbxname;
8618 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
8619 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
8620 mbxdevnam.dsc$a_pointer = mbxname;
8621 mbxdevnam.dsc$w_length = sizeof(mbxname);
8622 dvi_item = DVI$_DEVNAM;
8623 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
8624 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
8627 freopen(mbxname, "rb", stdin);
8630 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
8634 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
8636 fprintf(stderr,"Can't open input file %s as stdin",in);
8639 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
8641 fprintf(stderr,"Can't open output file %s as stdout",out);
8644 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
8647 if (strcmp(err,"&1") == 0) {
8648 dup2(fileno(stdout), fileno(stderr));
8649 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
8652 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
8654 fprintf(stderr,"Can't open error file %s as stderr",err);
8658 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
8662 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
8665 #ifdef ARGPROC_DEBUG
8666 PerlIO_printf(Perl_debug_log, "Arglist:\n");
8667 for (j = 0; j < *ac; ++j)
8668 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
8670 /* Clear errors we may have hit expanding wildcards, so they don't
8671 show up in Perl's $! later */
8672 set_errno(0); set_vaxc_errno(1);
8673 } /* end of getredirection() */
8676 static void add_item(struct list_item **head,
8677 struct list_item **tail,
8683 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
8684 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8688 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
8689 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8690 *tail = (*tail)->next;
8692 (*tail)->value = value;
8696 static void mp_expand_wild_cards(pTHX_ char *item,
8697 struct list_item **head,
8698 struct list_item **tail,
8702 unsigned long int context = 0;
8710 $DESCRIPTOR(filespec, "");
8711 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
8712 $DESCRIPTOR(resultspec, "");
8713 unsigned long int lff_flags = 0;
8717 #ifdef VMS_LONGNAME_SUPPORT
8718 lff_flags = LIB$M_FIL_LONG_NAMES;
8721 for (cp = item; *cp; cp++) {
8722 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
8723 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
8725 if (!*cp || isspace(*cp))
8727 add_item(head, tail, item, count);
8732 /* "double quoted" wild card expressions pass as is */
8733 /* From DCL that means using e.g.: */
8734 /* perl program """perl.*""" */
8735 item_len = strlen(item);
8736 if ( '"' == *item && '"' == item[item_len-1] )
8739 item[item_len-2] = '\0';
8740 add_item(head, tail, item, count);
8744 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
8745 resultspec.dsc$b_class = DSC$K_CLASS_D;
8746 resultspec.dsc$a_pointer = NULL;
8747 vmsspec = PerlMem_malloc(VMS_MAXRSS);
8748 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8749 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
8750 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0,NULL);
8751 if (!isunix || !filespec.dsc$a_pointer)
8752 filespec.dsc$a_pointer = item;
8753 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
8755 * Only return version specs, if the caller specified a version
8757 had_version = strchr(item, ';');
8759 * Only return device and directory specs, if the caller specifed either.
8761 had_device = strchr(item, ':');
8762 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
8764 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
8765 (&filespec, &resultspec, &context,
8766 &defaultspec, 0, &rms_sts, &lff_flags)))
8771 string = PerlMem_malloc(resultspec.dsc$w_length+1);
8772 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8773 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
8774 string[resultspec.dsc$w_length] = '\0';
8775 if (NULL == had_version)
8776 *(strrchr(string, ';')) = '\0';
8777 if ((!had_directory) && (had_device == NULL))
8779 if (NULL == (devdir = strrchr(string, ']')))
8780 devdir = strrchr(string, '>');
8781 strcpy(string, devdir + 1);
8784 * Be consistent with what the C RTL has already done to the rest of
8785 * the argv items and lowercase all of these names.
8787 if (!decc_efs_case_preserve) {
8788 for (c = string; *c; ++c)
8792 if (isunix) trim_unixpath(string,item,1);
8793 add_item(head, tail, string, count);
8796 PerlMem_free(vmsspec);
8797 if (sts != RMS$_NMF)
8799 set_vaxc_errno(sts);
8802 case RMS$_FNF: case RMS$_DNF:
8803 set_errno(ENOENT); break;
8805 set_errno(ENOTDIR); break;
8807 set_errno(ENODEV); break;
8808 case RMS$_FNM: case RMS$_SYN:
8809 set_errno(EINVAL); break;
8811 set_errno(EACCES); break;
8813 _ckvmssts_noperl(sts);
8817 add_item(head, tail, item, count);
8818 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
8819 _ckvmssts_noperl(lib$find_file_end(&context));
8822 static int child_st[2];/* Event Flag set when child process completes */
8824 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
8826 static unsigned long int exit_handler(int *status)
8830 if (0 == child_st[0])
8832 #ifdef ARGPROC_DEBUG
8833 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
8835 fflush(stdout); /* Have to flush pipe for binary data to */
8836 /* terminate properly -- <tp@mccall.com> */
8837 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
8838 sys$dassgn(child_chan);
8840 sys$synch(0, child_st);
8845 static void sig_child(int chan)
8847 #ifdef ARGPROC_DEBUG
8848 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
8850 if (child_st[0] == 0)
8854 static struct exit_control_block exit_block =
8859 &exit_block.exit_status,
8864 pipe_and_fork(pTHX_ char **cmargv)
8867 struct dsc$descriptor_s *vmscmd;
8868 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
8869 int sts, j, l, ismcr, quote, tquote = 0;
8871 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
8872 vms_execfree(vmscmd);
8877 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
8878 && toupper(*(q+2)) == 'R' && !*(q+3);
8880 while (q && l < MAX_DCL_LINE_LENGTH) {
8882 if (j > 0 && quote) {
8888 if (ismcr && j > 1) quote = 1;
8889 tquote = (strchr(q,' ')) != NULL || *q == '\0';
8892 if (quote || tquote) {
8898 if ((quote||tquote) && *q == '"') {
8908 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
8910 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
8914 static int background_process(pTHX_ int argc, char **argv)
8916 char command[MAX_DCL_SYMBOL + 1] = "$";
8917 $DESCRIPTOR(value, "");
8918 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
8919 static $DESCRIPTOR(null, "NLA0:");
8920 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
8922 $DESCRIPTOR(pidstr, "");
8924 unsigned long int flags = 17, one = 1, retsts;
8927 strcat(command, argv[0]);
8928 len = strlen(command);
8929 while (--argc && (len < MAX_DCL_SYMBOL))
8931 strcat(command, " \"");
8932 strcat(command, *(++argv));
8933 strcat(command, "\"");
8934 len = strlen(command);
8936 value.dsc$a_pointer = command;
8937 value.dsc$w_length = strlen(value.dsc$a_pointer);
8938 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
8939 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
8940 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
8941 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
8944 _ckvmssts_noperl(retsts);
8946 #ifdef ARGPROC_DEBUG
8947 PerlIO_printf(Perl_debug_log, "%s\n", command);
8949 sprintf(pidstring, "%08X", pid);
8950 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
8951 pidstr.dsc$a_pointer = pidstring;
8952 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
8953 lib$set_symbol(&pidsymbol, &pidstr);
8957 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
8960 /* OS-specific initialization at image activation (not thread startup) */
8961 /* Older VAXC header files lack these constants */
8962 #ifndef JPI$_RIGHTS_SIZE
8963 # define JPI$_RIGHTS_SIZE 817
8965 #ifndef KGB$M_SUBSYSTEM
8966 # define KGB$M_SUBSYSTEM 0x8
8969 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
8971 /*{{{void vms_image_init(int *, char ***)*/
8973 vms_image_init(int *argcp, char ***argvp)
8975 char eqv[LNM$C_NAMLENGTH+1] = "";
8976 unsigned int len, tabct = 8, tabidx = 0;
8977 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
8978 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
8979 unsigned short int dummy, rlen;
8980 struct dsc$descriptor_s **tabvec;
8981 #if defined(PERL_IMPLICIT_CONTEXT)
8984 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
8985 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
8986 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
8989 #ifdef KILL_BY_SIGPRC
8990 Perl_csighandler_init();
8993 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
8994 _ckvmssts_noperl(iosb[0]);
8995 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
8996 if (iprv[i]) { /* Running image installed with privs? */
8997 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
9002 /* Rights identifiers might trigger tainting as well. */
9003 if (!will_taint && (rlen || rsz)) {
9004 while (rlen < rsz) {
9005 /* We didn't get all the identifiers on the first pass. Allocate a
9006 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9007 * were needed to hold all identifiers at time of last call; we'll
9008 * allocate that many unsigned long ints), and go back and get 'em.
9009 * If it gave us less than it wanted to despite ample buffer space,
9010 * something's broken. Is your system missing a system identifier?
9012 if (rsz <= jpilist[1].buflen) {
9013 /* Perl_croak accvios when used this early in startup. */
9014 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9015 rsz, (unsigned long) jpilist[1].buflen,
9016 "Check your rights database for corruption.\n");
9019 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9020 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9021 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9022 jpilist[1].buflen = rsz * sizeof(unsigned long int);
9023 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9024 _ckvmssts_noperl(iosb[0]);
9026 mask = jpilist[1].bufadr;
9027 /* Check attribute flags for each identifier (2nd longword); protected
9028 * subsystem identifiers trigger tainting.
9030 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9031 if (mask[i] & KGB$M_SUBSYSTEM) {
9036 if (mask != rlst) PerlMem_free(mask);
9039 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9040 * logical, some versions of the CRTL will add a phanthom /000000/
9041 * directory. This needs to be removed.
9043 if (decc_filename_unix_report) {
9046 ulen = strlen(argvp[0][0]);
9048 zeros = strstr(argvp[0][0], "/000000/");
9049 if (zeros != NULL) {
9051 mlen = ulen - (zeros - argvp[0][0]) - 7;
9052 memmove(zeros, &zeros[7], mlen);
9054 argvp[0][0][ulen] = '\0';
9057 /* It also may have a trailing dot that needs to be removed otherwise
9058 * it will be converted to VMS mode incorrectly.
9061 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9062 argvp[0][0][ulen] = '\0';
9065 /* We need to use this hack to tell Perl it should run with tainting,
9066 * since its tainting flag may be part of the PL_curinterp struct, which
9067 * hasn't been allocated when vms_image_init() is called.
9070 char **newargv, **oldargv;
9072 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9073 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9074 newargv[0] = oldargv[0];
9075 newargv[1] = PerlMem_malloc(3 * sizeof(char));
9076 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9077 strcpy(newargv[1], "-T");
9078 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9080 newargv[*argcp] = NULL;
9081 /* We orphan the old argv, since we don't know where it's come from,
9082 * so we don't know how to free it.
9086 else { /* Did user explicitly request tainting? */
9088 char *cp, **av = *argvp;
9089 for (i = 1; i < *argcp; i++) {
9090 if (*av[i] != '-') break;
9091 for (cp = av[i]+1; *cp; cp++) {
9092 if (*cp == 'T') { will_taint = 1; break; }
9093 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9094 strchr("DFIiMmx",*cp)) break;
9096 if (will_taint) break;
9101 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9104 tabvec = (struct dsc$descriptor_s **)
9105 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9106 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9108 else if (tabidx >= tabct) {
9110 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9111 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9113 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9114 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9115 tabvec[tabidx]->dsc$w_length = 0;
9116 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9117 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
9118 tabvec[tabidx]->dsc$a_pointer = NULL;
9119 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
9121 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9123 getredirection(argcp,argvp);
9124 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9126 # include <reentrancy.h>
9127 decc$set_reentrancy(C$C_MULTITHREAD);
9136 * Trim Unix-style prefix off filespec, so it looks like what a shell
9137 * glob expansion would return (i.e. from specified prefix on, not
9138 * full path). Note that returned filespec is Unix-style, regardless
9139 * of whether input filespec was VMS-style or Unix-style.
9141 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9142 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9143 * vector of options; at present, only bit 0 is used, and if set tells
9144 * trim unixpath to try the current default directory as a prefix when
9145 * presented with a possibly ambiguous ... wildcard.
9147 * Returns !=0 on success, with trimmed filespec replacing contents of
9148 * fspec, and 0 on failure, with contents of fpsec unchanged.
9150 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9152 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9154 char *unixified, *unixwild,
9155 *template, *base, *end, *cp1, *cp2;
9156 register int tmplen, reslen = 0, dirs = 0;
9158 unixwild = PerlMem_malloc(VMS_MAXRSS);
9159 if (unixwild == NULL) _ckvmssts(SS$_INSFMEM);
9160 if (!wildspec || !fspec) return 0;
9161 template = unixwild;
9162 if (strpbrk(wildspec,"]>:") != NULL) {
9163 if (do_tounixspec(wildspec,unixwild,0,NULL) == NULL) {
9164 PerlMem_free(unixwild);
9169 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
9170 unixwild[VMS_MAXRSS-1] = 0;
9172 unixified = PerlMem_malloc(VMS_MAXRSS);
9173 if (unixified == NULL) _ckvmssts(SS$_INSFMEM);
9174 if (strpbrk(fspec,"]>:") != NULL) {
9175 if (do_tounixspec(fspec,unixified,0,NULL) == NULL) {
9176 PerlMem_free(unixwild);
9177 PerlMem_free(unixified);
9180 else base = unixified;
9181 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9182 * check to see that final result fits into (isn't longer than) fspec */
9183 reslen = strlen(fspec);
9187 /* No prefix or absolute path on wildcard, so nothing to remove */
9188 if (!*template || *template == '/') {
9189 PerlMem_free(unixwild);
9190 if (base == fspec) {
9191 PerlMem_free(unixified);
9194 tmplen = strlen(unixified);
9195 if (tmplen > reslen) {
9196 PerlMem_free(unixified);
9197 return 0; /* not enough space */
9199 /* Copy unixified resultant, including trailing NUL */
9200 memmove(fspec,unixified,tmplen+1);
9201 PerlMem_free(unixified);
9205 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9206 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
9207 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
9208 for (cp1 = end ;cp1 >= base; cp1--)
9209 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9211 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9212 PerlMem_free(unixified);
9213 PerlMem_free(unixwild);
9218 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9219 int ells = 1, totells, segdirs, match;
9220 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9221 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9223 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9225 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9226 tpl = PerlMem_malloc(VMS_MAXRSS);
9227 if (tpl == NULL) _ckvmssts(SS$_INSFMEM);
9228 if (ellipsis == template && opts & 1) {
9229 /* Template begins with an ellipsis. Since we can't tell how many
9230 * directory names at the front of the resultant to keep for an
9231 * arbitrary starting point, we arbitrarily choose the current
9232 * default directory as a starting point. If it's there as a prefix,
9233 * clip it off. If not, fall through and act as if the leading
9234 * ellipsis weren't there (i.e. return shortest possible path that
9235 * could match template).
9237 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9239 PerlMem_free(unixified);
9240 PerlMem_free(unixwild);
9243 if (!decc_efs_case_preserve) {
9244 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9245 if (_tolower(*cp1) != _tolower(*cp2)) break;
9247 segdirs = dirs - totells; /* Min # of dirs we must have left */
9248 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9249 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9250 memmove(fspec,cp2+1,end - cp2);
9252 PerlMem_free(unixified);
9253 PerlMem_free(unixwild);
9257 /* First off, back up over constant elements at end of path */
9259 for (front = end ; front >= base; front--)
9260 if (*front == '/' && !dirs--) { front++; break; }
9262 lcres = PerlMem_malloc(VMS_MAXRSS);
9263 if (lcres == NULL) _ckvmssts(SS$_INSFMEM);
9264 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9266 if (!decc_efs_case_preserve) {
9267 *cp2 = _tolower(*cp1); /* Make lc copy for match */
9275 PerlMem_free(unixified);
9276 PerlMem_free(unixwild);
9277 PerlMem_free(lcres);
9278 return 0; /* Path too long. */
9281 *cp2 = '\0'; /* Pick up with memcpy later */
9282 lcfront = lcres + (front - base);
9283 /* Now skip over each ellipsis and try to match the path in front of it. */
9285 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
9286 if (*(cp1) == '.' && *(cp1+1) == '.' &&
9287 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
9288 if (cp1 < template) break; /* template started with an ellipsis */
9289 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9290 ellipsis = cp1; continue;
9292 wilddsc.dsc$a_pointer = tpl;
9293 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9295 for (segdirs = 0, cp2 = tpl;
9296 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
9298 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
9300 if (!decc_efs_case_preserve) {
9301 *cp2 = _tolower(*cp1); /* else lowercase for match */
9304 *cp2 = *cp1; /* else preserve case for match */
9307 if (*cp2 == '/') segdirs++;
9309 if (cp1 != ellipsis - 1) {
9311 PerlMem_free(unixified);
9312 PerlMem_free(unixwild);
9313 PerlMem_free(lcres);
9314 return 0; /* Path too long */
9316 /* Back up at least as many dirs as in template before matching */
9317 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
9318 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
9319 for (match = 0; cp1 > lcres;) {
9320 resdsc.dsc$a_pointer = cp1;
9321 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
9323 if (match == 1) lcfront = cp1;
9325 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
9329 PerlMem_free(unixified);
9330 PerlMem_free(unixwild);
9331 PerlMem_free(lcres);
9332 return 0; /* Can't find prefix ??? */
9334 if (match > 1 && opts & 1) {
9335 /* This ... wildcard could cover more than one set of dirs (i.e.
9336 * a set of similar dir names is repeated). If the template
9337 * contains more than 1 ..., upstream elements could resolve the
9338 * ambiguity, but it's not worth a full backtracking setup here.
9339 * As a quick heuristic, clip off the current default directory
9340 * if it's present to find the trimmed spec, else use the
9341 * shortest string that this ... could cover.
9343 char def[NAM$C_MAXRSS+1], *st;
9345 if (getcwd(def, sizeof def,0) == NULL) {
9346 Safefree(unixified);
9352 if (!decc_efs_case_preserve) {
9353 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9354 if (_tolower(*cp1) != _tolower(*cp2)) break;
9356 segdirs = dirs - totells; /* Min # of dirs we must have left */
9357 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
9358 if (*cp1 == '\0' && *cp2 == '/') {
9359 memmove(fspec,cp2+1,end - cp2);
9361 PerlMem_free(unixified);
9362 PerlMem_free(unixwild);
9363 PerlMem_free(lcres);
9366 /* Nope -- stick with lcfront from above and keep going. */
9369 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
9371 PerlMem_free(unixified);
9372 PerlMem_free(unixwild);
9373 PerlMem_free(lcres);
9378 } /* end of trim_unixpath() */
9383 * VMS readdir() routines.
9384 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
9386 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
9387 * Minor modifications to original routines.
9390 /* readdir may have been redefined by reentr.h, so make sure we get
9391 * the local version for what we do here.
9396 #if !defined(PERL_IMPLICIT_CONTEXT)
9397 # define readdir Perl_readdir
9399 # define readdir(a) Perl_readdir(aTHX_ a)
9402 /* Number of elements in vms_versions array */
9403 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
9406 * Open a directory, return a handle for later use.
9408 /*{{{ DIR *opendir(char*name) */
9410 Perl_opendir(pTHX_ const char *name)
9416 Newx(dir, VMS_MAXRSS, char);
9417 if (do_tovmspath(name,dir,0,NULL) == NULL) {
9421 /* Check access before stat; otherwise stat does not
9422 * accurately report whether it's a directory.
9424 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
9425 /* cando_by_name has already set errno */
9429 if (flex_stat(dir,&sb) == -1) return NULL;
9430 if (!S_ISDIR(sb.st_mode)) {
9432 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
9435 /* Get memory for the handle, and the pattern. */
9437 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
9439 /* Fill in the fields; mainly playing with the descriptor. */
9440 sprintf(dd->pattern, "%s*.*",dir);
9445 /* By saying we always want the result of readdir() in unix format, we
9446 * are really saying we want all the escapes removed. Otherwise the caller,
9447 * having no way to know whether it's already in VMS format, might send it
9448 * through tovmsspec again, thus double escaping.
9450 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
9451 dd->pat.dsc$a_pointer = dd->pattern;
9452 dd->pat.dsc$w_length = strlen(dd->pattern);
9453 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
9454 dd->pat.dsc$b_class = DSC$K_CLASS_S;
9455 #if defined(USE_ITHREADS)
9456 Newx(dd->mutex,1,perl_mutex);
9457 MUTEX_INIT( (perl_mutex *) dd->mutex );
9463 } /* end of opendir() */
9467 * Set the flag to indicate we want versions or not.
9469 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
9471 vmsreaddirversions(DIR *dd, int flag)
9474 dd->flags |= PERL_VMSDIR_M_VERSIONS;
9476 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
9481 * Free up an opened directory.
9483 /*{{{ void closedir(DIR *dd)*/
9485 Perl_closedir(DIR *dd)
9489 sts = lib$find_file_end(&dd->context);
9490 Safefree(dd->pattern);
9491 #if defined(USE_ITHREADS)
9492 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
9493 Safefree(dd->mutex);
9500 * Collect all the version numbers for the current file.
9503 collectversions(pTHX_ DIR *dd)
9505 struct dsc$descriptor_s pat;
9506 struct dsc$descriptor_s res;
9508 char *p, *text, *buff;
9510 unsigned long context, tmpsts;
9512 /* Convenient shorthand. */
9515 /* Add the version wildcard, ignoring the "*.*" put on before */
9516 i = strlen(dd->pattern);
9517 Newx(text,i + e->d_namlen + 3,char);
9518 strcpy(text, dd->pattern);
9519 sprintf(&text[i - 3], "%s;*", e->d_name);
9521 /* Set up the pattern descriptor. */
9522 pat.dsc$a_pointer = text;
9523 pat.dsc$w_length = i + e->d_namlen - 1;
9524 pat.dsc$b_dtype = DSC$K_DTYPE_T;
9525 pat.dsc$b_class = DSC$K_CLASS_S;
9527 /* Set up result descriptor. */
9528 Newx(buff, VMS_MAXRSS, char);
9529 res.dsc$a_pointer = buff;
9530 res.dsc$w_length = VMS_MAXRSS - 1;
9531 res.dsc$b_dtype = DSC$K_DTYPE_T;
9532 res.dsc$b_class = DSC$K_CLASS_S;
9534 /* Read files, collecting versions. */
9535 for (context = 0, e->vms_verscount = 0;
9536 e->vms_verscount < VERSIZE(e);
9537 e->vms_verscount++) {
9539 unsigned long flags = 0;
9541 #ifdef VMS_LONGNAME_SUPPORT
9542 flags = LIB$M_FIL_LONG_NAMES;
9544 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
9545 if (tmpsts == RMS$_NMF || context == 0) break;
9547 buff[VMS_MAXRSS - 1] = '\0';
9548 if ((p = strchr(buff, ';')))
9549 e->vms_versions[e->vms_verscount] = atoi(p + 1);
9551 e->vms_versions[e->vms_verscount] = -1;
9554 _ckvmssts(lib$find_file_end(&context));
9558 } /* end of collectversions() */
9561 * Read the next entry from the directory.
9563 /*{{{ struct dirent *readdir(DIR *dd)*/
9565 Perl_readdir(pTHX_ DIR *dd)
9567 struct dsc$descriptor_s res;
9569 unsigned long int tmpsts;
9571 unsigned long flags = 0;
9572 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
9573 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
9575 /* Set up result descriptor, and get next file. */
9576 Newx(buff, VMS_MAXRSS, char);
9577 res.dsc$a_pointer = buff;
9578 res.dsc$w_length = VMS_MAXRSS - 1;
9579 res.dsc$b_dtype = DSC$K_DTYPE_T;
9580 res.dsc$b_class = DSC$K_CLASS_S;
9582 #ifdef VMS_LONGNAME_SUPPORT
9583 flags = LIB$M_FIL_LONG_NAMES;
9586 tmpsts = lib$find_file
9587 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
9588 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
9589 if (!(tmpsts & 1)) {
9590 set_vaxc_errno(tmpsts);
9593 set_errno(EACCES); break;
9595 set_errno(ENODEV); break;
9597 set_errno(ENOTDIR); break;
9598 case RMS$_FNF: case RMS$_DNF:
9599 set_errno(ENOENT); break;
9607 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
9608 buff[res.dsc$w_length] = '\0';
9609 p = buff + res.dsc$w_length;
9610 while (--p >= buff) if (!isspace(*p)) break;
9612 if (!decc_efs_case_preserve) {
9613 for (p = buff; *p; p++) *p = _tolower(*p);
9616 /* Skip any directory component and just copy the name. */
9617 sts = vms_split_path
9632 /* Drop NULL extensions on UNIX file specification */
9633 if ((dd->flags & PERL_VMSDIR_M_UNIXSPECS &&
9634 (e_len == 1) && decc_readdir_dropdotnotype)) {
9639 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
9640 dd->entry.d_name[n_len + e_len] = '\0';
9641 dd->entry.d_namlen = strlen(dd->entry.d_name);
9643 /* Convert the filename to UNIX format if needed */
9644 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
9646 /* Translate the encoded characters. */
9647 /* Fixme: Unicode handling could result in embedded 0 characters */
9648 if (strchr(dd->entry.d_name, '^') != NULL) {
9651 p = dd->entry.d_name;
9654 int inchars_read, outchars_added;
9655 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
9657 q += outchars_added;
9659 /* if outchars_added > 1, then this is a wide file specification */
9660 /* Wide file specifications need to be passed in Perl */
9661 /* counted strings apparently with a Unicode flag */
9664 strcpy(dd->entry.d_name, new_name);
9665 dd->entry.d_namlen = strlen(dd->entry.d_name);
9669 dd->entry.vms_verscount = 0;
9670 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
9674 } /* end of readdir() */
9678 * Read the next entry from the directory -- thread-safe version.
9680 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
9682 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
9686 MUTEX_LOCK( (perl_mutex *) dd->mutex );
9688 entry = readdir(dd);
9690 retval = ( *result == NULL ? errno : 0 );
9692 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
9696 } /* end of readdir_r() */
9700 * Return something that can be used in a seekdir later.
9702 /*{{{ long telldir(DIR *dd)*/
9704 Perl_telldir(DIR *dd)
9711 * Return to a spot where we used to be. Brute force.
9713 /*{{{ void seekdir(DIR *dd,long count)*/
9715 Perl_seekdir(pTHX_ DIR *dd, long count)
9719 /* If we haven't done anything yet... */
9723 /* Remember some state, and clear it. */
9724 old_flags = dd->flags;
9725 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
9726 _ckvmssts(lib$find_file_end(&dd->context));
9729 /* The increment is in readdir(). */
9730 for (dd->count = 0; dd->count < count; )
9733 dd->flags = old_flags;
9735 } /* end of seekdir() */
9738 /* VMS subprocess management
9740 * my_vfork() - just a vfork(), after setting a flag to record that
9741 * the current script is trying a Unix-style fork/exec.
9743 * vms_do_aexec() and vms_do_exec() are called in response to the
9744 * perl 'exec' function. If this follows a vfork call, then they
9745 * call out the regular perl routines in doio.c which do an
9746 * execvp (for those who really want to try this under VMS).
9747 * Otherwise, they do exactly what the perl docs say exec should
9748 * do - terminate the current script and invoke a new command
9749 * (See below for notes on command syntax.)
9751 * do_aspawn() and do_spawn() implement the VMS side of the perl
9752 * 'system' function.
9754 * Note on command arguments to perl 'exec' and 'system': When handled
9755 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
9756 * are concatenated to form a DCL command string. If the first non-numeric
9757 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
9758 * the command string is handed off to DCL directly. Otherwise,
9759 * the first token of the command is taken as the filespec of an image
9760 * to run. The filespec is expanded using a default type of '.EXE' and
9761 * the process defaults for device, directory, etc., and if found, the resultant
9762 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
9763 * the command string as parameters. This is perhaps a bit complicated,
9764 * but I hope it will form a happy medium between what VMS folks expect
9765 * from lib$spawn and what Unix folks expect from exec.
9768 static int vfork_called;
9770 /*{{{int my_vfork()*/
9781 vms_execfree(struct dsc$descriptor_s *vmscmd)
9784 if (vmscmd->dsc$a_pointer) {
9785 PerlMem_free(vmscmd->dsc$a_pointer);
9787 PerlMem_free(vmscmd);
9792 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
9794 char *junk, *tmps = NULL;
9795 register size_t cmdlen = 0;
9802 tmps = SvPV(really,rlen);
9809 for (idx++; idx <= sp; idx++) {
9811 junk = SvPVx(*idx,rlen);
9812 cmdlen += rlen ? rlen + 1 : 0;
9815 Newx(PL_Cmd, cmdlen+1, char);
9817 if (tmps && *tmps) {
9818 strcpy(PL_Cmd,tmps);
9821 else *PL_Cmd = '\0';
9822 while (++mark <= sp) {
9824 char *s = SvPVx(*mark,n_a);
9826 if (*PL_Cmd) strcat(PL_Cmd," ");
9832 } /* end of setup_argstr() */
9835 static unsigned long int
9836 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
9837 struct dsc$descriptor_s **pvmscmd)
9839 char vmsspec[NAM$C_MAXRSS+1], resspec[NAM$C_MAXRSS+1];
9840 char image_name[NAM$C_MAXRSS+1];
9841 char image_argv[NAM$C_MAXRSS+1];
9842 $DESCRIPTOR(defdsc,".EXE");
9843 $DESCRIPTOR(defdsc2,".");
9844 $DESCRIPTOR(resdsc,resspec);
9845 struct dsc$descriptor_s *vmscmd;
9846 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9847 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
9848 register char *s, *rest, *cp, *wordbreak;
9853 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9854 if (vmscmd == NULL) _ckvmssts(SS$_INSFMEM);
9856 /* Make a copy for modification */
9857 cmdlen = strlen(incmd);
9858 cmd = PerlMem_malloc(cmdlen+1);
9859 if (cmd == NULL) _ckvmssts(SS$_INSFMEM);
9860 strncpy(cmd, incmd, cmdlen);
9865 vmscmd->dsc$a_pointer = NULL;
9866 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
9867 vmscmd->dsc$b_class = DSC$K_CLASS_S;
9868 vmscmd->dsc$w_length = 0;
9869 if (pvmscmd) *pvmscmd = vmscmd;
9871 if (suggest_quote) *suggest_quote = 0;
9873 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
9875 return CLI$_BUFOVF; /* continuation lines currently unsupported */
9880 while (*s && isspace(*s)) s++;
9882 if (*s == '@' || *s == '$') {
9883 vmsspec[0] = *s; rest = s + 1;
9884 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
9886 else { cp = vmsspec; rest = s; }
9887 if (*rest == '.' || *rest == '/') {
9890 *rest && !isspace(*rest) && cp2 - resspec < sizeof resspec;
9891 rest++, cp2++) *cp2 = *rest;
9893 if (do_tovmsspec(resspec,cp,0,NULL)) {
9896 /* When a UNIX spec with no file type is translated to VMS, */
9897 /* A trailing '.' is appended under ODS-5 rules. */
9898 /* Here we do not want that trailing "." as it prevents */
9899 /* Looking for a implied ".exe" type. */
9900 if (decc_efs_charset) {
9902 i = strlen(vmsspec);
9903 if (vmsspec[i-1] == '.') {
9904 vmsspec[i-1] = '\0';
9909 for (cp2 = vmsspec + strlen(vmsspec);
9910 *rest && cp2 - vmsspec < sizeof vmsspec;
9911 rest++, cp2++) *cp2 = *rest;
9916 /* Intuit whether verb (first word of cmd) is a DCL command:
9917 * - if first nonspace char is '@', it's a DCL indirection
9919 * - if verb contains a filespec separator, it's not a DCL command
9920 * - if it doesn't, caller tells us whether to default to a DCL
9921 * command, or to a local image unless told it's DCL (by leading '$')
9925 if (suggest_quote) *suggest_quote = 1;
9927 register char *filespec = strpbrk(s,":<[.;");
9928 rest = wordbreak = strpbrk(s," \"\t/");
9929 if (!wordbreak) wordbreak = s + strlen(s);
9930 if (*s == '$') check_img = 0;
9931 if (filespec && (filespec < wordbreak)) isdcl = 0;
9932 else isdcl = !check_img;
9937 imgdsc.dsc$a_pointer = s;
9938 imgdsc.dsc$w_length = wordbreak - s;
9939 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
9941 _ckvmssts(lib$find_file_end(&cxt));
9942 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
9943 if (!(retsts & 1) && *s == '$') {
9944 _ckvmssts(lib$find_file_end(&cxt));
9945 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
9946 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
9948 _ckvmssts(lib$find_file_end(&cxt));
9949 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
9953 _ckvmssts(lib$find_file_end(&cxt));
9958 while (*s && !isspace(*s)) s++;
9961 /* check that it's really not DCL with no file extension */
9962 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
9964 char b[256] = {0,0,0,0};
9965 read(fileno(fp), b, 256);
9966 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
9970 /* Check for script */
9972 if ((b[0] == '#') && (b[1] == '!'))
9974 #ifdef ALTERNATE_SHEBANG
9976 shebang_len = strlen(ALTERNATE_SHEBANG);
9977 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
9979 perlstr = strstr("perl",b);
9980 if (perlstr == NULL)
9988 if (shebang_len > 0) {
9991 char tmpspec[NAM$C_MAXRSS + 1];
9994 /* Image is following after white space */
9995 /*--------------------------------------*/
9996 while (isprint(b[i]) && isspace(b[i]))
10000 while (isprint(b[i]) && !isspace(b[i])) {
10001 tmpspec[j++] = b[i++];
10002 if (j >= NAM$C_MAXRSS)
10007 /* There may be some default parameters to the image */
10008 /*---------------------------------------------------*/
10010 while (isprint(b[i])) {
10011 image_argv[j++] = b[i++];
10012 if (j >= NAM$C_MAXRSS)
10015 while ((j > 0) && !isprint(image_argv[j-1]))
10019 /* It will need to be converted to VMS format and validated */
10020 if (tmpspec[0] != '\0') {
10023 /* Try to find the exact program requested to be run */
10024 /*---------------------------------------------------*/
10025 iname = do_rmsexpand
10026 (tmpspec, image_name, 0, ".exe",
10027 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10028 if (iname != NULL) {
10029 if (cando_by_name_int
10030 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10031 /* MCR prefix needed */
10035 /* Try again with a null type */
10036 /*----------------------------*/
10037 iname = do_rmsexpand
10038 (tmpspec, image_name, 0, ".",
10039 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10040 if (iname != NULL) {
10041 if (cando_by_name_int
10042 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10043 /* MCR prefix needed */
10049 /* Did we find the image to run the script? */
10050 /*------------------------------------------*/
10054 /* Assume DCL or foreign command exists */
10055 /*--------------------------------------*/
10056 tchr = strrchr(tmpspec, '/');
10057 if (tchr != NULL) {
10063 strcpy(image_name, tchr);
10071 if (check_img && isdcl) return RMS$_FNF;
10073 if (cando_by_name(S_IXUSR,0,resspec)) {
10074 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10075 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM);
10077 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
10078 if (image_name[0] != 0) {
10079 strcat(vmscmd->dsc$a_pointer, image_name);
10080 strcat(vmscmd->dsc$a_pointer, " ");
10082 } else if (image_name[0] != 0) {
10083 strcpy(vmscmd->dsc$a_pointer, image_name);
10084 strcat(vmscmd->dsc$a_pointer, " ");
10086 strcpy(vmscmd->dsc$a_pointer,"@");
10088 if (suggest_quote) *suggest_quote = 1;
10090 /* If there is an image name, use original command */
10091 if (image_name[0] == 0)
10092 strcat(vmscmd->dsc$a_pointer,resspec);
10095 while (*rest && isspace(*rest)) rest++;
10098 if (image_argv[0] != 0) {
10099 strcat(vmscmd->dsc$a_pointer,image_argv);
10100 strcat(vmscmd->dsc$a_pointer, " ");
10106 rest_len = strlen(rest);
10107 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10108 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10109 strcat(vmscmd->dsc$a_pointer,rest);
10111 retsts = CLI$_BUFOVF;
10113 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10115 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10121 /* It's either a DCL command or we couldn't find a suitable image */
10122 vmscmd->dsc$w_length = strlen(cmd);
10124 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
10125 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
10126 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
10130 /* check if it's a symbol (for quoting purposes) */
10131 if (suggest_quote && !*suggest_quote) {
10133 char equiv[LNM$C_NAMLENGTH];
10134 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10135 eqvdsc.dsc$a_pointer = equiv;
10137 iss = lib$get_symbol(vmscmd,&eqvdsc);
10138 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10140 if (!(retsts & 1)) {
10141 /* just hand off status values likely to be due to user error */
10142 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10143 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10144 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10145 else { _ckvmssts(retsts); }
10148 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10150 } /* end of setup_cmddsc() */
10153 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10155 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10161 if (vfork_called) { /* this follows a vfork - act Unixish */
10163 if (vfork_called < 0) {
10164 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10167 else return do_aexec(really,mark,sp);
10169 /* no vfork - act VMSish */
10170 cmd = setup_argstr(aTHX_ really,mark,sp);
10171 exec_sts = vms_do_exec(cmd);
10172 Safefree(cmd); /* Clean up from setup_argstr() */
10177 } /* end of vms_do_aexec() */
10180 /* {{{bool vms_do_exec(char *cmd) */
10182 Perl_vms_do_exec(pTHX_ const char *cmd)
10184 struct dsc$descriptor_s *vmscmd;
10186 if (vfork_called) { /* this follows a vfork - act Unixish */
10188 if (vfork_called < 0) {
10189 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10192 else return do_exec(cmd);
10195 { /* no vfork - act VMSish */
10196 unsigned long int retsts;
10199 TAINT_PROPER("exec");
10200 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10201 retsts = lib$do_command(vmscmd);
10204 case RMS$_FNF: case RMS$_DNF:
10205 set_errno(ENOENT); break;
10207 set_errno(ENOTDIR); break;
10209 set_errno(ENODEV); break;
10211 set_errno(EACCES); break;
10213 set_errno(EINVAL); break;
10214 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10215 set_errno(E2BIG); break;
10216 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10217 _ckvmssts(retsts); /* fall through */
10218 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10219 set_errno(EVMSERR);
10221 set_vaxc_errno(retsts);
10222 if (ckWARN(WARN_EXEC)) {
10223 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
10224 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
10226 vms_execfree(vmscmd);
10231 } /* end of vms_do_exec() */
10234 int do_spawn2(pTHX_ const char *, int);
10237 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
10239 unsigned long int sts;
10245 /* We'll copy the (undocumented?) Win32 behavior and allow a
10246 * numeric first argument. But the only value we'll support
10247 * through do_aspawn is a value of 1, which means spawn without
10248 * waiting for completion -- other values are ignored.
10250 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
10252 flags = SvIVx(*mark);
10255 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
10256 flags = CLI$M_NOWAIT;
10260 cmd = setup_argstr(aTHX_ really, mark, sp);
10261 sts = do_spawn2(aTHX_ cmd, flags);
10262 /* pp_sys will clean up cmd */
10266 } /* end of do_aspawn() */
10270 /* {{{int do_spawn(char* cmd) */
10272 Perl_do_spawn(pTHX_ char* cmd)
10274 PERL_ARGS_ASSERT_DO_SPAWN;
10276 return do_spawn2(aTHX_ cmd, 0);
10280 /* {{{int do_spawn_nowait(char* cmd) */
10282 Perl_do_spawn_nowait(pTHX_ char* cmd)
10284 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
10286 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
10290 /* {{{int do_spawn2(char *cmd) */
10292 do_spawn2(pTHX_ const char *cmd, int flags)
10294 unsigned long int sts, substs;
10296 /* The caller of this routine expects to Safefree(PL_Cmd) */
10297 Newx(PL_Cmd,10,char);
10300 TAINT_PROPER("spawn");
10301 if (!cmd || !*cmd) {
10302 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
10305 case RMS$_FNF: case RMS$_DNF:
10306 set_errno(ENOENT); break;
10308 set_errno(ENOTDIR); break;
10310 set_errno(ENODEV); break;
10312 set_errno(EACCES); break;
10314 set_errno(EINVAL); break;
10315 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10316 set_errno(E2BIG); break;
10317 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10318 _ckvmssts(sts); /* fall through */
10319 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10320 set_errno(EVMSERR);
10322 set_vaxc_errno(sts);
10323 if (ckWARN(WARN_EXEC)) {
10324 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
10333 if (flags & CLI$M_NOWAIT)
10336 strcpy(mode, "nW");
10338 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
10341 /* sts will be the pid in the nowait case */
10344 } /* end of do_spawn2() */
10348 static unsigned int *sockflags, sockflagsize;
10351 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
10352 * routines found in some versions of the CRTL can't deal with sockets.
10353 * We don't shim the other file open routines since a socket isn't
10354 * likely to be opened by a name.
10356 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
10357 FILE *my_fdopen(int fd, const char *mode)
10359 FILE *fp = fdopen(fd, mode);
10362 unsigned int fdoff = fd / sizeof(unsigned int);
10363 Stat_t sbuf; /* native stat; we don't need flex_stat */
10364 if (!sockflagsize || fdoff > sockflagsize) {
10365 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
10366 else Newx (sockflags,fdoff+2,unsigned int);
10367 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
10368 sockflagsize = fdoff + 2;
10370 if (fstat(fd, (struct stat *)&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
10371 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
10380 * Clear the corresponding bit when the (possibly) socket stream is closed.
10381 * There still a small hole: we miss an implicit close which might occur
10382 * via freopen(). >> Todo
10384 /*{{{ int my_fclose(FILE *fp)*/
10385 int my_fclose(FILE *fp) {
10387 unsigned int fd = fileno(fp);
10388 unsigned int fdoff = fd / sizeof(unsigned int);
10390 if (sockflagsize && fdoff < sockflagsize)
10391 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
10399 * A simple fwrite replacement which outputs itmsz*nitm chars without
10400 * introducing record boundaries every itmsz chars.
10401 * We are using fputs, which depends on a terminating null. We may
10402 * well be writing binary data, so we need to accommodate not only
10403 * data with nulls sprinkled in the middle but also data with no null
10406 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
10408 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
10410 register char *cp, *end, *cpd, *data;
10411 register unsigned int fd = fileno(dest);
10412 register unsigned int fdoff = fd / sizeof(unsigned int);
10414 int bufsize = itmsz * nitm + 1;
10416 if (fdoff < sockflagsize &&
10417 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
10418 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
10422 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
10423 memcpy( data, src, itmsz*nitm );
10424 data[itmsz*nitm] = '\0';
10426 end = data + itmsz * nitm;
10427 retval = (int) nitm; /* on success return # items written */
10430 while (cpd <= end) {
10431 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
10432 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
10434 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
10438 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
10441 } /* end of my_fwrite() */
10444 /*{{{ int my_flush(FILE *fp)*/
10446 Perl_my_flush(pTHX_ FILE *fp)
10449 if ((res = fflush(fp)) == 0 && fp) {
10450 #ifdef VMS_DO_SOCKETS
10452 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
10454 res = fsync(fileno(fp));
10457 * If the flush succeeded but set end-of-file, we need to clear
10458 * the error because our caller may check ferror(). BTW, this
10459 * probably means we just flushed an empty file.
10461 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
10468 * Here are replacements for the following Unix routines in the VMS environment:
10469 * getpwuid Get information for a particular UIC or UID
10470 * getpwnam Get information for a named user
10471 * getpwent Get information for each user in the rights database
10472 * setpwent Reset search to the start of the rights database
10473 * endpwent Finish searching for users in the rights database
10475 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
10476 * (defined in pwd.h), which contains the following fields:-
10478 * char *pw_name; Username (in lower case)
10479 * char *pw_passwd; Hashed password
10480 * unsigned int pw_uid; UIC
10481 * unsigned int pw_gid; UIC group number
10482 * char *pw_unixdir; Default device/directory (VMS-style)
10483 * char *pw_gecos; Owner name
10484 * char *pw_dir; Default device/directory (Unix-style)
10485 * char *pw_shell; Default CLI name (eg. DCL)
10487 * If the specified user does not exist, getpwuid and getpwnam return NULL.
10489 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
10490 * not the UIC member number (eg. what's returned by getuid()),
10491 * getpwuid() can accept either as input (if uid is specified, the caller's
10492 * UIC group is used), though it won't recognise gid=0.
10494 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
10495 * information about other users in your group or in other groups, respectively.
10496 * If the required privilege is not available, then these routines fill only
10497 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
10500 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
10503 /* sizes of various UAF record fields */
10504 #define UAI$S_USERNAME 12
10505 #define UAI$S_IDENT 31
10506 #define UAI$S_OWNER 31
10507 #define UAI$S_DEFDEV 31
10508 #define UAI$S_DEFDIR 63
10509 #define UAI$S_DEFCLI 31
10510 #define UAI$S_PWD 8
10512 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
10513 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
10514 (uic).uic$v_group != UIC$K_WILD_GROUP)
10516 static char __empty[]= "";
10517 static struct passwd __passwd_empty=
10518 {(char *) __empty, (char *) __empty, 0, 0,
10519 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
10520 static int contxt= 0;
10521 static struct passwd __pwdcache;
10522 static char __pw_namecache[UAI$S_IDENT+1];
10525 * This routine does most of the work extracting the user information.
10527 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
10530 unsigned char length;
10531 char pw_gecos[UAI$S_OWNER+1];
10533 static union uicdef uic;
10535 unsigned char length;
10536 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
10539 unsigned char length;
10540 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
10543 unsigned char length;
10544 char pw_shell[UAI$S_DEFCLI+1];
10546 static char pw_passwd[UAI$S_PWD+1];
10548 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
10549 struct dsc$descriptor_s name_desc;
10550 unsigned long int sts;
10552 static struct itmlst_3 itmlst[]= {
10553 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
10554 {sizeof(uic), UAI$_UIC, &uic, &luic},
10555 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
10556 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
10557 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
10558 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
10559 {0, 0, NULL, NULL}};
10561 name_desc.dsc$w_length= strlen(name);
10562 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
10563 name_desc.dsc$b_class= DSC$K_CLASS_S;
10564 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
10566 /* Note that sys$getuai returns many fields as counted strings. */
10567 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
10568 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
10569 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
10571 else { _ckvmssts(sts); }
10572 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
10574 if ((int) owner.length < lowner) lowner= (int) owner.length;
10575 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
10576 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
10577 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
10578 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
10579 owner.pw_gecos[lowner]= '\0';
10580 defdev.pw_dir[ldefdev+ldefdir]= '\0';
10581 defcli.pw_shell[ldefcli]= '\0';
10582 if (valid_uic(uic)) {
10583 pwd->pw_uid= uic.uic$l_uic;
10584 pwd->pw_gid= uic.uic$v_group;
10587 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
10588 pwd->pw_passwd= pw_passwd;
10589 pwd->pw_gecos= owner.pw_gecos;
10590 pwd->pw_dir= defdev.pw_dir;
10591 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
10592 pwd->pw_shell= defcli.pw_shell;
10593 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
10595 ldir= strlen(pwd->pw_unixdir) - 1;
10596 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
10599 strcpy(pwd->pw_unixdir, pwd->pw_dir);
10600 if (!decc_efs_case_preserve)
10601 __mystrtolower(pwd->pw_unixdir);
10606 * Get information for a named user.
10608 /*{{{struct passwd *getpwnam(char *name)*/
10609 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
10611 struct dsc$descriptor_s name_desc;
10613 unsigned long int status, sts;
10615 __pwdcache = __passwd_empty;
10616 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
10617 /* We still may be able to determine pw_uid and pw_gid */
10618 name_desc.dsc$w_length= strlen(name);
10619 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
10620 name_desc.dsc$b_class= DSC$K_CLASS_S;
10621 name_desc.dsc$a_pointer= (char *) name;
10622 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
10623 __pwdcache.pw_uid= uic.uic$l_uic;
10624 __pwdcache.pw_gid= uic.uic$v_group;
10627 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
10628 set_vaxc_errno(sts);
10629 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
10632 else { _ckvmssts(sts); }
10635 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
10636 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
10637 __pwdcache.pw_name= __pw_namecache;
10638 return &__pwdcache;
10639 } /* end of my_getpwnam() */
10643 * Get information for a particular UIC or UID.
10644 * Called by my_getpwent with uid=-1 to list all users.
10646 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
10647 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
10649 const $DESCRIPTOR(name_desc,__pw_namecache);
10650 unsigned short lname;
10652 unsigned long int status;
10654 if (uid == (unsigned int) -1) {
10656 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
10657 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
10658 set_vaxc_errno(status);
10659 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
10663 else { _ckvmssts(status); }
10664 } while (!valid_uic (uic));
10667 uic.uic$l_uic= uid;
10668 if (!uic.uic$v_group)
10669 uic.uic$v_group= PerlProc_getgid();
10670 if (valid_uic(uic))
10671 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
10672 else status = SS$_IVIDENT;
10673 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
10674 status == RMS$_PRV) {
10675 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
10678 else { _ckvmssts(status); }
10680 __pw_namecache[lname]= '\0';
10681 __mystrtolower(__pw_namecache);
10683 __pwdcache = __passwd_empty;
10684 __pwdcache.pw_name = __pw_namecache;
10686 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
10687 The identifier's value is usually the UIC, but it doesn't have to be,
10688 so if we can, we let fillpasswd update this. */
10689 __pwdcache.pw_uid = uic.uic$l_uic;
10690 __pwdcache.pw_gid = uic.uic$v_group;
10692 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
10693 return &__pwdcache;
10695 } /* end of my_getpwuid() */
10699 * Get information for next user.
10701 /*{{{struct passwd *my_getpwent()*/
10702 struct passwd *Perl_my_getpwent(pTHX)
10704 return (my_getpwuid((unsigned int) -1));
10709 * Finish searching rights database for users.
10711 /*{{{void my_endpwent()*/
10712 void Perl_my_endpwent(pTHX)
10715 _ckvmssts(sys$finish_rdb(&contxt));
10721 #ifdef HOMEGROWN_POSIX_SIGNALS
10722 /* Signal handling routines, pulled into the core from POSIX.xs.
10724 * We need these for threads, so they've been rolled into the core,
10725 * rather than left in POSIX.xs.
10727 * (DRS, Oct 23, 1997)
10730 /* sigset_t is atomic under VMS, so these routines are easy */
10731 /*{{{int my_sigemptyset(sigset_t *) */
10732 int my_sigemptyset(sigset_t *set) {
10733 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10734 *set = 0; return 0;
10739 /*{{{int my_sigfillset(sigset_t *)*/
10740 int my_sigfillset(sigset_t *set) {
10742 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10743 for (i = 0; i < NSIG; i++) *set |= (1 << i);
10749 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
10750 int my_sigaddset(sigset_t *set, int sig) {
10751 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10752 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
10753 *set |= (1 << (sig - 1));
10759 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
10760 int my_sigdelset(sigset_t *set, int sig) {
10761 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10762 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
10763 *set &= ~(1 << (sig - 1));
10769 /*{{{int my_sigismember(sigset_t *set, int sig)*/
10770 int my_sigismember(sigset_t *set, int sig) {
10771 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10772 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
10773 return *set & (1 << (sig - 1));
10778 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
10779 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
10782 /* If set and oset are both null, then things are badly wrong. Bail out. */
10783 if ((oset == NULL) && (set == NULL)) {
10784 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
10788 /* If set's null, then we're just handling a fetch. */
10790 tempmask = sigblock(0);
10795 tempmask = sigsetmask(*set);
10798 tempmask = sigblock(*set);
10801 tempmask = sigblock(0);
10802 sigsetmask(*oset & ~tempmask);
10805 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
10810 /* Did they pass us an oset? If so, stick our holding mask into it */
10817 #endif /* HOMEGROWN_POSIX_SIGNALS */
10820 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
10821 * my_utime(), and flex_stat(), all of which operate on UTC unless
10822 * VMSISH_TIMES is true.
10824 /* method used to handle UTC conversions:
10825 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
10827 static int gmtime_emulation_type;
10828 /* number of secs to add to UTC POSIX-style time to get local time */
10829 static long int utc_offset_secs;
10831 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
10832 * in vmsish.h. #undef them here so we can call the CRTL routines
10841 * DEC C previous to 6.0 corrupts the behavior of the /prefix
10842 * qualifier with the extern prefix pragma. This provisional
10843 * hack circumvents this prefix pragma problem in previous
10846 #if defined(__VMS_VER) && __VMS_VER >= 70000000
10847 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
10848 # pragma __extern_prefix save
10849 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
10850 # define gmtime decc$__utctz_gmtime
10851 # define localtime decc$__utctz_localtime
10852 # define time decc$__utc_time
10853 # pragma __extern_prefix restore
10855 struct tm *gmtime(), *localtime();
10861 static time_t toutc_dst(time_t loc) {
10864 if ((rsltmp = localtime(&loc)) == NULL) return -1;
10865 loc -= utc_offset_secs;
10866 if (rsltmp->tm_isdst) loc -= 3600;
10869 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
10870 ((gmtime_emulation_type || my_time(NULL)), \
10871 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
10872 ((secs) - utc_offset_secs))))
10874 static time_t toloc_dst(time_t utc) {
10877 utc += utc_offset_secs;
10878 if ((rsltmp = localtime(&utc)) == NULL) return -1;
10879 if (rsltmp->tm_isdst) utc += 3600;
10882 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
10883 ((gmtime_emulation_type || my_time(NULL)), \
10884 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
10885 ((secs) + utc_offset_secs))))
10887 #ifndef RTL_USES_UTC
10890 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
10891 DST starts on 1st sun of april at 02:00 std time
10892 ends on last sun of october at 02:00 dst time
10893 see the UCX management command reference, SET CONFIG TIMEZONE
10894 for formatting info.
10896 No, it's not as general as it should be, but then again, NOTHING
10897 will handle UK times in a sensible way.
10902 parse the DST start/end info:
10903 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
10907 tz_parse_startend(char *s, struct tm *w, int *past)
10909 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
10910 int ly, dozjd, d, m, n, hour, min, sec, j, k;
10915 if (!past) return 0;
10918 if (w->tm_year % 4 == 0) ly = 1;
10919 if (w->tm_year % 100 == 0) ly = 0;
10920 if (w->tm_year+1900 % 400 == 0) ly = 1;
10923 dozjd = isdigit(*s);
10924 if (*s == 'J' || *s == 'j' || dozjd) {
10925 if (!dozjd && !isdigit(*++s)) return 0;
10928 d = d*10 + *s++ - '0';
10930 d = d*10 + *s++ - '0';
10933 if (d == 0) return 0;
10934 if (d > 366) return 0;
10936 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
10939 } else if (*s == 'M' || *s == 'm') {
10940 if (!isdigit(*++s)) return 0;
10942 if (isdigit(*s)) m = 10*m + *s++ - '0';
10943 if (*s != '.') return 0;
10944 if (!isdigit(*++s)) return 0;
10946 if (n < 1 || n > 5) return 0;
10947 if (*s != '.') return 0;
10948 if (!isdigit(*++s)) return 0;
10950 if (d > 6) return 0;
10954 if (!isdigit(*++s)) return 0;
10956 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
10958 if (!isdigit(*++s)) return 0;
10960 if (isdigit(*s)) min = 10*min + *s++ - '0';
10962 if (!isdigit(*++s)) return 0;
10964 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
10974 if (w->tm_yday < d) goto before;
10975 if (w->tm_yday > d) goto after;
10977 if (w->tm_mon+1 < m) goto before;
10978 if (w->tm_mon+1 > m) goto after;
10980 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
10981 k = d - j; /* mday of first d */
10982 if (k <= 0) k += 7;
10983 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
10984 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
10985 if (w->tm_mday < k) goto before;
10986 if (w->tm_mday > k) goto after;
10989 if (w->tm_hour < hour) goto before;
10990 if (w->tm_hour > hour) goto after;
10991 if (w->tm_min < min) goto before;
10992 if (w->tm_min > min) goto after;
10993 if (w->tm_sec < sec) goto before;
11007 /* parse the offset: (+|-)hh[:mm[:ss]] */
11010 tz_parse_offset(char *s, int *offset)
11012 int hour = 0, min = 0, sec = 0;
11015 if (!offset) return 0;
11017 if (*s == '-') {neg++; s++;}
11018 if (*s == '+') s++;
11019 if (!isdigit(*s)) return 0;
11021 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
11022 if (hour > 24) return 0;
11024 if (!isdigit(*++s)) return 0;
11026 if (isdigit(*s)) min = min*10 + (*s++ - '0');
11027 if (min > 59) return 0;
11029 if (!isdigit(*++s)) return 0;
11031 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
11032 if (sec > 59) return 0;
11036 *offset = (hour*60+min)*60 + sec;
11037 if (neg) *offset = -*offset;
11042 input time is w, whatever type of time the CRTL localtime() uses.
11043 sets dst, the zone, and the gmtoff (seconds)
11045 caches the value of TZ and UCX$TZ env variables; note that
11046 my_setenv looks for these and sets a flag if they're changed
11049 We have to watch out for the "australian" case (dst starts in
11050 october, ends in april)...flagged by "reverse" and checked by
11051 scanning through the months of the previous year.
11056 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
11061 char *dstzone, *tz, *s_start, *s_end;
11062 int std_off, dst_off, isdst;
11063 int y, dststart, dstend;
11064 static char envtz[1025]; /* longer than any logical, symbol, ... */
11065 static char ucxtz[1025];
11066 static char reversed = 0;
11072 reversed = -1; /* flag need to check */
11073 envtz[0] = ucxtz[0] = '\0';
11074 tz = my_getenv("TZ",0);
11075 if (tz) strcpy(envtz, tz);
11076 tz = my_getenv("UCX$TZ",0);
11077 if (tz) strcpy(ucxtz, tz);
11078 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
11081 if (!*tz) tz = ucxtz;
11084 while (isalpha(*s)) s++;
11085 s = tz_parse_offset(s, &std_off);
11087 if (!*s) { /* no DST, hurray we're done! */
11093 while (isalpha(*s)) s++;
11094 s2 = tz_parse_offset(s, &dst_off);
11098 dst_off = std_off - 3600;
11101 if (!*s) { /* default dst start/end?? */
11102 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
11103 s = strchr(ucxtz,',');
11105 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
11107 if (*s != ',') return 0;
11110 when = _toutc(when); /* convert to utc */
11111 when = when - std_off; /* convert to pseudolocal time*/
11113 w2 = localtime(&when);
11116 s = tz_parse_startend(s_start,w2,&dststart);
11118 if (*s != ',') return 0;
11121 when = _toutc(when); /* convert to utc */
11122 when = when - dst_off; /* convert to pseudolocal time*/
11123 w2 = localtime(&when);
11124 if (w2->tm_year != y) { /* spans a year, just check one time */
11125 when += dst_off - std_off;
11126 w2 = localtime(&when);
11129 s = tz_parse_startend(s_end,w2,&dstend);
11132 if (reversed == -1) { /* need to check if start later than end */
11136 if (when < 2*365*86400) {
11137 when += 2*365*86400;
11141 w2 =localtime(&when);
11142 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
11144 for (j = 0; j < 12; j++) {
11145 w2 =localtime(&when);
11146 tz_parse_startend(s_start,w2,&ds);
11147 tz_parse_startend(s_end,w2,&de);
11148 if (ds != de) break;
11152 if (de && !ds) reversed = 1;
11155 isdst = dststart && !dstend;
11156 if (reversed) isdst = dststart || !dstend;
11159 if (dst) *dst = isdst;
11160 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
11161 if (isdst) tz = dstzone;
11163 while(isalpha(*tz)) *zone++ = *tz++;
11169 #endif /* !RTL_USES_UTC */
11171 /* my_time(), my_localtime(), my_gmtime()
11172 * By default traffic in UTC time values, using CRTL gmtime() or
11173 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11174 * Note: We need to use these functions even when the CRTL has working
11175 * UTC support, since they also handle C<use vmsish qw(times);>
11177 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
11178 * Modified by Charles Bailey <bailey@newman.upenn.edu>
11181 /*{{{time_t my_time(time_t *timep)*/
11182 time_t Perl_my_time(pTHX_ time_t *timep)
11187 if (gmtime_emulation_type == 0) {
11189 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
11190 /* results of calls to gmtime() and localtime() */
11191 /* for same &base */
11193 gmtime_emulation_type++;
11194 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11195 char off[LNM$C_NAMLENGTH+1];;
11197 gmtime_emulation_type++;
11198 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11199 gmtime_emulation_type++;
11200 utc_offset_secs = 0;
11201 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11203 else { utc_offset_secs = atol(off); }
11205 else { /* We've got a working gmtime() */
11206 struct tm gmt, local;
11209 tm_p = localtime(&base);
11211 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
11212 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11213 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
11214 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
11219 # ifdef VMSISH_TIME
11220 # ifdef RTL_USES_UTC
11221 if (VMSISH_TIME) when = _toloc(when);
11223 if (!VMSISH_TIME) when = _toutc(when);
11226 if (timep != NULL) *timep = when;
11229 } /* end of my_time() */
11233 /*{{{struct tm *my_gmtime(const time_t *timep)*/
11235 Perl_my_gmtime(pTHX_ const time_t *timep)
11241 if (timep == NULL) {
11242 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11245 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11248 # ifdef VMSISH_TIME
11249 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11251 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
11252 return gmtime(&when);
11254 /* CRTL localtime() wants local time as input, so does no tz correction */
11255 rsltmp = localtime(&when);
11256 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
11259 } /* end of my_gmtime() */
11263 /*{{{struct tm *my_localtime(const time_t *timep)*/
11265 Perl_my_localtime(pTHX_ const time_t *timep)
11267 time_t when, whenutc;
11271 if (timep == NULL) {
11272 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11275 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11276 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
11279 # ifdef RTL_USES_UTC
11280 # ifdef VMSISH_TIME
11281 if (VMSISH_TIME) when = _toutc(when);
11283 /* CRTL localtime() wants UTC as input, does tz correction itself */
11284 return localtime(&when);
11286 # else /* !RTL_USES_UTC */
11288 # ifdef VMSISH_TIME
11289 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
11290 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
11293 #ifndef RTL_USES_UTC
11294 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
11295 when = whenutc - offset; /* pseudolocal time*/
11298 /* CRTL localtime() wants local time as input, so does no tz correction */
11299 rsltmp = localtime(&when);
11300 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
11304 } /* end of my_localtime() */
11307 /* Reset definitions for later calls */
11308 #define gmtime(t) my_gmtime(t)
11309 #define localtime(t) my_localtime(t)
11310 #define time(t) my_time(t)
11313 /* my_utime - update modification/access time of a file
11315 * VMS 7.3 and later implementation
11316 * Only the UTC translation is home-grown. The rest is handled by the
11317 * CRTL utime(), which will take into account the relevant feature
11318 * logicals and ODS-5 volume characteristics for true access times.
11320 * pre VMS 7.3 implementation:
11321 * The calling sequence is identical to POSIX utime(), but under
11322 * VMS with ODS-2, only the modification time is changed; ODS-2 does
11323 * not maintain access times. Restrictions differ from the POSIX
11324 * definition in that the time can be changed as long as the
11325 * caller has permission to execute the necessary IO$_MODIFY $QIO;
11326 * no separate checks are made to insure that the caller is the
11327 * owner of the file or has special privs enabled.
11328 * Code here is based on Joe Meadows' FILE utility.
11332 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11333 * to VMS epoch (01-JAN-1858 00:00:00.00)
11334 * in 100 ns intervals.
11336 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11338 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11339 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
11341 #if __CRTL_VER >= 70300000
11342 struct utimbuf utc_utimes, *utc_utimesp;
11344 if (utimes != NULL) {
11345 utc_utimes.actime = utimes->actime;
11346 utc_utimes.modtime = utimes->modtime;
11347 # ifdef VMSISH_TIME
11348 /* If input was local; convert to UTC for sys svc */
11350 utc_utimes.actime = _toutc(utimes->actime);
11351 utc_utimes.modtime = _toutc(utimes->modtime);
11354 utc_utimesp = &utc_utimes;
11357 utc_utimesp = NULL;
11360 return utime(file, utc_utimesp);
11362 #else /* __CRTL_VER < 70300000 */
11366 long int bintime[2], len = 2, lowbit, unixtime,
11367 secscale = 10000000; /* seconds --> 100 ns intervals */
11368 unsigned long int chan, iosb[2], retsts;
11369 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
11370 struct FAB myfab = cc$rms_fab;
11371 struct NAM mynam = cc$rms_nam;
11372 #if defined (__DECC) && defined (__VAX)
11373 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
11374 * at least through VMS V6.1, which causes a type-conversion warning.
11376 # pragma message save
11377 # pragma message disable cvtdiftypes
11379 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
11380 struct fibdef myfib;
11381 #if defined (__DECC) && defined (__VAX)
11382 /* This should be right after the declaration of myatr, but due
11383 * to a bug in VAX DEC C, this takes effect a statement early.
11385 # pragma message restore
11387 /* cast ok for read only parameter */
11388 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
11389 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
11390 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
11392 if (file == NULL || *file == '\0') {
11393 SETERRNO(ENOENT, LIB$_INVARG);
11397 /* Convert to VMS format ensuring that it will fit in 255 characters */
11398 if (do_rmsexpand(file, vmsspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) {
11399 SETERRNO(ENOENT, LIB$_INVARG);
11402 if (utimes != NULL) {
11403 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
11404 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
11405 * Since time_t is unsigned long int, and lib$emul takes a signed long int
11406 * as input, we force the sign bit to be clear by shifting unixtime right
11407 * one bit, then multiplying by an extra factor of 2 in lib$emul().
11409 lowbit = (utimes->modtime & 1) ? secscale : 0;
11410 unixtime = (long int) utimes->modtime;
11411 # ifdef VMSISH_TIME
11412 /* If input was UTC; convert to local for sys svc */
11413 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
11415 unixtime >>= 1; secscale <<= 1;
11416 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
11417 if (!(retsts & 1)) {
11418 SETERRNO(EVMSERR, retsts);
11421 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
11422 if (!(retsts & 1)) {
11423 SETERRNO(EVMSERR, retsts);
11428 /* Just get the current time in VMS format directly */
11429 retsts = sys$gettim(bintime);
11430 if (!(retsts & 1)) {
11431 SETERRNO(EVMSERR, retsts);
11436 myfab.fab$l_fna = vmsspec;
11437 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
11438 myfab.fab$l_nam = &mynam;
11439 mynam.nam$l_esa = esa;
11440 mynam.nam$b_ess = (unsigned char) sizeof esa;
11441 mynam.nam$l_rsa = rsa;
11442 mynam.nam$b_rss = (unsigned char) sizeof rsa;
11443 if (decc_efs_case_preserve)
11444 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
11446 /* Look for the file to be affected, letting RMS parse the file
11447 * specification for us as well. I have set errno using only
11448 * values documented in the utime() man page for VMS POSIX.
11450 retsts = sys$parse(&myfab,0,0);
11451 if (!(retsts & 1)) {
11452 set_vaxc_errno(retsts);
11453 if (retsts == RMS$_PRV) set_errno(EACCES);
11454 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
11455 else set_errno(EVMSERR);
11458 retsts = sys$search(&myfab,0,0);
11459 if (!(retsts & 1)) {
11460 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11461 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11462 set_vaxc_errno(retsts);
11463 if (retsts == RMS$_PRV) set_errno(EACCES);
11464 else if (retsts == RMS$_FNF) set_errno(ENOENT);
11465 else set_errno(EVMSERR);
11469 devdsc.dsc$w_length = mynam.nam$b_dev;
11470 /* cast ok for read only parameter */
11471 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
11473 retsts = sys$assign(&devdsc,&chan,0,0);
11474 if (!(retsts & 1)) {
11475 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11476 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11477 set_vaxc_errno(retsts);
11478 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
11479 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
11480 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
11481 else set_errno(EVMSERR);
11485 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
11486 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
11488 memset((void *) &myfib, 0, sizeof myfib);
11489 #if defined(__DECC) || defined(__DECCXX)
11490 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
11491 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
11492 /* This prevents the revision time of the file being reset to the current
11493 * time as a result of our IO$_MODIFY $QIO. */
11494 myfib.fib$l_acctl = FIB$M_NORECORD;
11496 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
11497 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
11498 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
11500 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
11501 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11502 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11503 _ckvmssts(sys$dassgn(chan));
11504 if (retsts & 1) retsts = iosb[0];
11505 if (!(retsts & 1)) {
11506 set_vaxc_errno(retsts);
11507 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11508 else set_errno(EVMSERR);
11514 #endif /* #if __CRTL_VER >= 70300000 */
11516 } /* end of my_utime() */
11520 * flex_stat, flex_lstat, flex_fstat
11521 * basic stat, but gets it right when asked to stat
11522 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
11525 #ifndef _USE_STD_STAT
11526 /* encode_dev packs a VMS device name string into an integer to allow
11527 * simple comparisons. This can be used, for example, to check whether two
11528 * files are located on the same device, by comparing their encoded device
11529 * names. Even a string comparison would not do, because stat() reuses the
11530 * device name buffer for each call; so without encode_dev, it would be
11531 * necessary to save the buffer and use strcmp (this would mean a number of
11532 * changes to the standard Perl code, to say nothing of what a Perl script
11533 * would have to do.
11535 * The device lock id, if it exists, should be unique (unless perhaps compared
11536 * with lock ids transferred from other nodes). We have a lock id if the disk is
11537 * mounted cluster-wide, which is when we tend to get long (host-qualified)
11538 * device names. Thus we use the lock id in preference, and only if that isn't
11539 * available, do we try to pack the device name into an integer (flagged by
11540 * the sign bit (LOCKID_MASK) being set).
11542 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
11543 * name and its encoded form, but it seems very unlikely that we will find
11544 * two files on different disks that share the same encoded device names,
11545 * and even more remote that they will share the same file id (if the test
11546 * is to check for the same file).
11548 * A better method might be to use sys$device_scan on the first call, and to
11549 * search for the device, returning an index into the cached array.
11550 * The number returned would be more intelligible.
11551 * This is probably not worth it, and anyway would take quite a bit longer
11552 * on the first call.
11554 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
11555 static mydev_t encode_dev (pTHX_ const char *dev)
11558 unsigned long int f;
11563 if (!dev || !dev[0]) return 0;
11567 struct dsc$descriptor_s dev_desc;
11568 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
11570 /* For cluster-mounted disks, the disk lock identifier is unique, so we
11571 can try that first. */
11572 dev_desc.dsc$w_length = strlen (dev);
11573 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
11574 dev_desc.dsc$b_class = DSC$K_CLASS_S;
11575 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
11576 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
11577 if (!$VMS_STATUS_SUCCESS(status)) {
11579 case SS$_NOSUCHDEV:
11580 SETERRNO(ENODEV, status);
11586 if (lockid) return (lockid & ~LOCKID_MASK);
11590 /* Otherwise we try to encode the device name */
11594 for (q = dev + strlen(dev); q--; q >= dev) {
11599 else if (isalpha (toupper (*q)))
11600 c= toupper (*q) - 'A' + (char)10;
11602 continue; /* Skip '$'s */
11604 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
11606 enc += f * (unsigned long int) c;
11608 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
11610 } /* end of encode_dev() */
11611 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11612 device_no = encode_dev(aTHX_ devname)
11614 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11615 device_no = new_dev_no
11619 is_null_device(name)
11622 if (decc_bug_devnull != 0) {
11623 if (strncmp("/dev/null", name, 9) == 0)
11626 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
11627 The underscore prefix, controller letter, and unit number are
11628 independently optional; for our purposes, the colon punctuation
11629 is not. The colon can be trailed by optional directory and/or
11630 filename, but two consecutive colons indicates a nodename rather
11631 than a device. [pr] */
11632 if (*name == '_') ++name;
11633 if (tolower(*name++) != 'n') return 0;
11634 if (tolower(*name++) != 'l') return 0;
11635 if (tolower(*name) == 'a') ++name;
11636 if (*name == '0') ++name;
11637 return (*name++ == ':') && (*name != ':');
11642 Perl_cando_by_name_int
11643 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
11645 char usrname[L_cuserid];
11646 struct dsc$descriptor_s usrdsc =
11647 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
11648 char *vmsname = NULL, *fileified = NULL;
11649 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
11650 unsigned short int retlen, trnlnm_iter_count;
11651 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11652 union prvdef curprv;
11653 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
11654 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
11655 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
11656 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
11657 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
11659 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
11661 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11663 static int profile_context = -1;
11665 if (!fname || !*fname) return FALSE;
11667 /* Make sure we expand logical names, since sys$check_access doesn't */
11668 fileified = PerlMem_malloc(VMS_MAXRSS);
11669 if (fileified == NULL) _ckvmssts(SS$_INSFMEM);
11670 if (!strpbrk(fname,"/]>:")) {
11671 strcpy(fileified,fname);
11672 trnlnm_iter_count = 0;
11673 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
11674 trnlnm_iter_count++;
11675 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
11680 vmsname = PerlMem_malloc(VMS_MAXRSS);
11681 if (vmsname == NULL) _ckvmssts(SS$_INSFMEM);
11682 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
11683 /* Don't know if already in VMS format, so make sure */
11684 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
11685 PerlMem_free(fileified);
11686 PerlMem_free(vmsname);
11691 strcpy(vmsname,fname);
11694 /* sys$check_access needs a file spec, not a directory spec.
11695 * Don't use flex_stat here, as that depends on thread context
11696 * having been initialized, and we may get here during startup.
11699 retlen = namdsc.dsc$w_length = strlen(vmsname);
11700 if (vmsname[retlen-1] == ']'
11701 || vmsname[retlen-1] == '>'
11702 || vmsname[retlen-1] == ':'
11703 || (!stat(vmsname, (stat_t *)&st) && S_ISDIR(st.st_mode))) {
11705 if (!do_fileify_dirspec(vmsname,fileified,1,NULL)) {
11706 PerlMem_free(fileified);
11707 PerlMem_free(vmsname);
11716 retlen = namdsc.dsc$w_length = strlen(fname);
11717 namdsc.dsc$a_pointer = (char *)fname;
11720 case S_IXUSR: case S_IXGRP: case S_IXOTH:
11721 access = ARM$M_EXECUTE;
11722 flags = CHP$M_READ;
11724 case S_IRUSR: case S_IRGRP: case S_IROTH:
11725 access = ARM$M_READ;
11726 flags = CHP$M_READ | CHP$M_USEREADALL;
11728 case S_IWUSR: case S_IWGRP: case S_IWOTH:
11729 access = ARM$M_WRITE;
11730 flags = CHP$M_READ | CHP$M_WRITE;
11732 case S_IDUSR: case S_IDGRP: case S_IDOTH:
11733 access = ARM$M_DELETE;
11734 flags = CHP$M_READ | CHP$M_WRITE;
11737 if (fileified != NULL)
11738 PerlMem_free(fileified);
11739 if (vmsname != NULL)
11740 PerlMem_free(vmsname);
11744 /* Before we call $check_access, create a user profile with the current
11745 * process privs since otherwise it just uses the default privs from the
11746 * UAF and might give false positives or negatives. This only works on
11747 * VMS versions v6.0 and later since that's when sys$create_user_profile
11748 * became available.
11751 /* get current process privs and username */
11752 _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
11753 _ckvmssts(iosb[0]);
11755 #if defined(__VMS_VER) && __VMS_VER >= 60000000
11757 /* find out the space required for the profile */
11758 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
11759 &usrprodsc.dsc$w_length,&profile_context));
11761 /* allocate space for the profile and get it filled in */
11762 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
11763 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts(SS$_INSFMEM);
11764 _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
11765 &usrprodsc.dsc$w_length,&profile_context));
11767 /* use the profile to check access to the file; free profile & analyze results */
11768 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
11769 PerlMem_free(usrprodsc.dsc$a_pointer);
11770 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
11774 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
11778 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
11779 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
11780 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
11781 set_vaxc_errno(retsts);
11782 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11783 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
11784 else set_errno(ENOENT);
11785 if (fileified != NULL)
11786 PerlMem_free(fileified);
11787 if (vmsname != NULL)
11788 PerlMem_free(vmsname);
11791 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
11792 if (fileified != NULL)
11793 PerlMem_free(fileified);
11794 if (vmsname != NULL)
11795 PerlMem_free(vmsname);
11800 if (fileified != NULL)
11801 PerlMem_free(fileified);
11802 if (vmsname != NULL)
11803 PerlMem_free(vmsname);
11804 return FALSE; /* Should never get here */
11808 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
11809 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
11810 * subset of the applicable information.
11813 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
11815 return cando_by_name_int
11816 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
11817 } /* end of cando() */
11821 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
11823 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
11825 return cando_by_name_int(bit, effective, fname, 0);
11827 } /* end of cando_by_name() */
11831 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
11833 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
11835 if (!fstat(fd,(stat_t *) statbufp)) {
11837 char *vms_filename;
11838 vms_filename = PerlMem_malloc(VMS_MAXRSS);
11839 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
11841 /* Save name for cando by name in VMS format */
11842 cptr = getname(fd, vms_filename, 1);
11844 /* This should not happen, but just in case */
11845 if (cptr == NULL) {
11846 statbufp->st_devnam[0] = 0;
11849 /* Make sure that the saved name fits in 255 characters */
11850 cptr = do_rmsexpand
11852 statbufp->st_devnam,
11855 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_VMS_IN,
11859 statbufp->st_devnam[0] = 0;
11861 PerlMem_free(vms_filename);
11863 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
11865 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
11867 # ifdef RTL_USES_UTC
11868 # ifdef VMSISH_TIME
11870 statbufp->st_mtime = _toloc(statbufp->st_mtime);
11871 statbufp->st_atime = _toloc(statbufp->st_atime);
11872 statbufp->st_ctime = _toloc(statbufp->st_ctime);
11876 # ifdef VMSISH_TIME
11877 if (!VMSISH_TIME) { /* Return UTC instead of local time */
11881 statbufp->st_mtime = _toutc(statbufp->st_mtime);
11882 statbufp->st_atime = _toutc(statbufp->st_atime);
11883 statbufp->st_ctime = _toutc(statbufp->st_ctime);
11890 } /* end of flex_fstat() */
11893 #if !defined(__VAX) && __CRTL_VER >= 80200000
11901 #define lstat(_x, _y) stat(_x, _y)
11904 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
11907 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
11909 char fileified[VMS_MAXRSS];
11910 char temp_fspec[VMS_MAXRSS];
11913 int saved_errno, saved_vaxc_errno;
11915 if (!fspec) return retval;
11916 saved_errno = errno; saved_vaxc_errno = vaxc$errno;
11917 strcpy(temp_fspec, fspec);
11919 if (decc_bug_devnull != 0) {
11920 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
11921 memset(statbufp,0,sizeof *statbufp);
11922 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
11923 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
11924 statbufp->st_uid = 0x00010001;
11925 statbufp->st_gid = 0x0001;
11926 time((time_t *)&statbufp->st_mtime);
11927 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
11932 /* Try for a directory name first. If fspec contains a filename without
11933 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
11934 * and sea:[wine.dark]water. exist, we prefer the directory here.
11935 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
11936 * not sea:[wine.dark]., if the latter exists. If the intended target is
11937 * the file with null type, specify this by calling flex_stat() with
11938 * a '.' at the end of fspec.
11940 * If we are in Posix filespec mode, accept the filename as is.
11944 #if __CRTL_VER >= 70300000 && !defined(__VAX)
11945 /* The CRTL stat() falls down hard on multi-dot filenames in unix format unless
11946 * DECC$EFS_CHARSET is in effect, so temporarily enable it if it isn't already.
11948 if (!decc_efs_charset)
11949 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,1);
11952 #if __CRTL_VER >= 80200000 && !defined(__VAX)
11953 if (decc_posix_compliant_pathnames == 0) {
11955 if (do_fileify_dirspec(temp_fspec,fileified,0,NULL) != NULL) {
11956 if (lstat_flag == 0)
11957 retval = stat(fileified,(stat_t *) statbufp);
11959 retval = lstat(fileified,(stat_t *) statbufp);
11960 save_spec = fileified;
11963 if (lstat_flag == 0)
11964 retval = stat(temp_fspec,(stat_t *) statbufp);
11966 retval = lstat(temp_fspec,(stat_t *) statbufp);
11967 save_spec = temp_fspec;
11970 * In debugging, on 8.3 Alpha, I found a case where stat was returning a
11971 * file not found error for a directory named foo:[bar.t] or /foo/bar/t
11972 * and lstat was working correctly for the same file.
11973 * The only syntax that was working for stat was "foo:[bar]t.dir".
11975 * Other directories with the same syntax worked fine.
11976 * So work around the problem when it shows up here.
11979 int save_errno = errno;
11980 if (do_tovmsspec(fspec, temp_fspec, 0, NULL) != NULL) {
11981 if (do_fileify_dirspec(temp_fspec, fileified, 0, NULL) != NULL) {
11982 retval = stat(fileified, (stat_t *) statbufp);
11983 save_spec = fileified;
11986 /* Restore the errno value if third stat does not succeed */
11988 errno = save_errno;
11990 #if __CRTL_VER >= 80200000 && !defined(__VAX)
11992 if (lstat_flag == 0)
11993 retval = stat(temp_fspec,(stat_t *) statbufp);
11995 retval = lstat(temp_fspec,(stat_t *) statbufp);
11996 save_spec = temp_fspec;
12000 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12001 /* As you were... */
12002 if (!decc_efs_charset)
12003 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12008 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12010 /* If this is an lstat, do not follow the link */
12012 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12014 cptr = do_rmsexpand
12015 (save_spec, statbufp->st_devnam, 0, NULL, rmsex_flags, NULL, NULL);
12017 statbufp->st_devnam[0] = 0;
12019 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12021 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12022 # ifdef RTL_USES_UTC
12023 # ifdef VMSISH_TIME
12025 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12026 statbufp->st_atime = _toloc(statbufp->st_atime);
12027 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12031 # ifdef VMSISH_TIME
12032 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12036 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12037 statbufp->st_atime = _toutc(statbufp->st_atime);
12038 statbufp->st_ctime = _toutc(statbufp->st_ctime);
12042 /* If we were successful, leave errno where we found it */
12043 if (retval == 0) { errno = saved_errno; vaxc$errno = saved_vaxc_errno; }
12046 } /* end of flex_stat_int() */
12049 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12051 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12053 return flex_stat_int(fspec, statbufp, 0);
12057 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12059 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12061 return flex_stat_int(fspec, statbufp, 1);
12066 /*{{{char *my_getlogin()*/
12067 /* VMS cuserid == Unix getlogin, except calling sequence */
12071 static char user[L_cuserid];
12072 return cuserid(user);
12077 /* rmscopy - copy a file using VMS RMS routines
12079 * Copies contents and attributes of spec_in to spec_out, except owner
12080 * and protection information. Name and type of spec_in are used as
12081 * defaults for spec_out. The third parameter specifies whether rmscopy()
12082 * should try to propagate timestamps from the input file to the output file.
12083 * If it is less than 0, no timestamps are preserved. If it is 0, then
12084 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12085 * propagated to the output file at creation iff the output file specification
12086 * did not contain an explicit name or type, and the revision date is always
12087 * updated at the end of the copy operation. If it is greater than 0, then
12088 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12089 * other than the revision date should be propagated, and bit 1 indicates
12090 * that the revision date should be propagated.
12092 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12094 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12095 * Incorporates, with permission, some code from EZCOPY by Tim Adye
12096 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12097 * as part of the Perl standard distribution under the terms of the
12098 * GNU General Public License or the Perl Artistic License. Copies
12099 * of each may be found in the Perl standard distribution.
12101 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12103 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12105 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12106 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12107 unsigned long int i, sts, sts2;
12109 struct FAB fab_in, fab_out;
12110 struct RAB rab_in, rab_out;
12111 rms_setup_nam(nam);
12112 rms_setup_nam(nam_out);
12113 struct XABDAT xabdat;
12114 struct XABFHC xabfhc;
12115 struct XABRDT xabrdt;
12116 struct XABSUM xabsum;
12118 vmsin = PerlMem_malloc(VMS_MAXRSS);
12119 if (vmsin == NULL) _ckvmssts(SS$_INSFMEM);
12120 vmsout = PerlMem_malloc(VMS_MAXRSS);
12121 if (vmsout == NULL) _ckvmssts(SS$_INSFMEM);
12122 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1,NULL) ||
12123 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1,NULL)) {
12124 PerlMem_free(vmsin);
12125 PerlMem_free(vmsout);
12126 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12130 esa = PerlMem_malloc(VMS_MAXRSS);
12131 if (esa == NULL) _ckvmssts(SS$_INSFMEM);
12133 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12134 esal = PerlMem_malloc(VMS_MAXRSS);
12135 if (esal == NULL) _ckvmssts(SS$_INSFMEM);
12137 fab_in = cc$rms_fab;
12138 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12139 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12140 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12141 fab_in.fab$l_fop = FAB$M_SQO;
12142 rms_bind_fab_nam(fab_in, nam);
12143 fab_in.fab$l_xab = (void *) &xabdat;
12145 rsa = PerlMem_malloc(VMS_MAXRSS);
12146 if (rsa == NULL) _ckvmssts(SS$_INSFMEM);
12148 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12149 rsal = PerlMem_malloc(VMS_MAXRSS);
12150 if (rsal == NULL) _ckvmssts(SS$_INSFMEM);
12152 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12153 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12154 rms_nam_esl(nam) = 0;
12155 rms_nam_rsl(nam) = 0;
12156 rms_nam_esll(nam) = 0;
12157 rms_nam_rsll(nam) = 0;
12158 #ifdef NAM$M_NO_SHORT_UPCASE
12159 if (decc_efs_case_preserve)
12160 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
12163 xabdat = cc$rms_xabdat; /* To get creation date */
12164 xabdat.xab$l_nxt = (void *) &xabfhc;
12166 xabfhc = cc$rms_xabfhc; /* To get record length */
12167 xabfhc.xab$l_nxt = (void *) &xabsum;
12169 xabsum = cc$rms_xabsum; /* To get key and area information */
12171 if (!((sts = sys$open(&fab_in)) & 1)) {
12172 PerlMem_free(vmsin);
12173 PerlMem_free(vmsout);
12176 PerlMem_free(esal);
12179 PerlMem_free(rsal);
12180 set_vaxc_errno(sts);
12182 case RMS$_FNF: case RMS$_DNF:
12183 set_errno(ENOENT); break;
12185 set_errno(ENOTDIR); break;
12187 set_errno(ENODEV); break;
12189 set_errno(EINVAL); break;
12191 set_errno(EACCES); break;
12193 set_errno(EVMSERR);
12200 fab_out.fab$w_ifi = 0;
12201 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12202 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12203 fab_out.fab$l_fop = FAB$M_SQO;
12204 rms_bind_fab_nam(fab_out, nam_out);
12205 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12206 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12207 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
12208 esa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
12209 if (esa_out == NULL) _ckvmssts(SS$_INSFMEM);
12210 rsa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
12211 if (rsa_out == NULL) _ckvmssts(SS$_INSFMEM);
12214 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12215 esal_out = PerlMem_malloc(VMS_MAXRSS);
12216 if (esal_out == NULL) _ckvmssts(SS$_INSFMEM);
12217 rsal_out = PerlMem_malloc(VMS_MAXRSS);
12218 if (rsal_out == NULL) _ckvmssts(SS$_INSFMEM);
12220 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
12221 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
12223 if (preserve_dates == 0) { /* Act like DCL COPY */
12224 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
12225 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
12226 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
12227 PerlMem_free(vmsin);
12228 PerlMem_free(vmsout);
12231 PerlMem_free(esal);
12234 PerlMem_free(rsal);
12235 PerlMem_free(esa_out);
12236 if (esal_out != NULL)
12237 PerlMem_free(esal_out);
12238 PerlMem_free(rsa_out);
12239 if (rsal_out != NULL)
12240 PerlMem_free(rsal_out);
12241 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12242 set_vaxc_errno(sts);
12245 fab_out.fab$l_xab = (void *) &xabdat;
12246 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12247 preserve_dates = 1;
12249 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
12250 preserve_dates =0; /* bitmask from this point forward */
12252 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
12253 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
12254 PerlMem_free(vmsin);
12255 PerlMem_free(vmsout);
12258 PerlMem_free(esal);
12261 PerlMem_free(rsal);
12262 PerlMem_free(esa_out);
12263 if (esal_out != NULL)
12264 PerlMem_free(esal_out);
12265 PerlMem_free(rsa_out);
12266 if (rsal_out != NULL)
12267 PerlMem_free(rsal_out);
12268 set_vaxc_errno(sts);
12271 set_errno(ENOENT); break;
12273 set_errno(ENOTDIR); break;
12275 set_errno(ENODEV); break;
12277 set_errno(EINVAL); break;
12279 set_errno(EACCES); break;
12281 set_errno(EVMSERR);
12285 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
12286 if (preserve_dates & 2) {
12287 /* sys$close() will process xabrdt, not xabdat */
12288 xabrdt = cc$rms_xabrdt;
12290 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12292 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
12293 * is unsigned long[2], while DECC & VAXC use a struct */
12294 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
12296 fab_out.fab$l_xab = (void *) &xabrdt;
12299 ubf = PerlMem_malloc(32256);
12300 if (ubf == NULL) _ckvmssts(SS$_INSFMEM);
12301 rab_in = cc$rms_rab;
12302 rab_in.rab$l_fab = &fab_in;
12303 rab_in.rab$l_rop = RAB$M_BIO;
12304 rab_in.rab$l_ubf = ubf;
12305 rab_in.rab$w_usz = 32256;
12306 if (!((sts = sys$connect(&rab_in)) & 1)) {
12307 sys$close(&fab_in); sys$close(&fab_out);
12308 PerlMem_free(vmsin);
12309 PerlMem_free(vmsout);
12313 PerlMem_free(esal);
12316 PerlMem_free(rsal);
12317 PerlMem_free(esa_out);
12318 if (esal_out != NULL)
12319 PerlMem_free(esal_out);
12320 PerlMem_free(rsa_out);
12321 if (rsal_out != NULL)
12322 PerlMem_free(rsal_out);
12323 set_errno(EVMSERR); set_vaxc_errno(sts);
12327 rab_out = cc$rms_rab;
12328 rab_out.rab$l_fab = &fab_out;
12329 rab_out.rab$l_rbf = ubf;
12330 if (!((sts = sys$connect(&rab_out)) & 1)) {
12331 sys$close(&fab_in); sys$close(&fab_out);
12332 PerlMem_free(vmsin);
12333 PerlMem_free(vmsout);
12337 PerlMem_free(esal);
12340 PerlMem_free(rsal);
12341 PerlMem_free(esa_out);
12342 if (esal_out != NULL)
12343 PerlMem_free(esal_out);
12344 PerlMem_free(rsa_out);
12345 if (rsal_out != NULL)
12346 PerlMem_free(rsal_out);
12347 set_errno(EVMSERR); set_vaxc_errno(sts);
12351 while ((sts = sys$read(&rab_in))) { /* always true */
12352 if (sts == RMS$_EOF) break;
12353 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12354 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
12355 sys$close(&fab_in); sys$close(&fab_out);
12356 PerlMem_free(vmsin);
12357 PerlMem_free(vmsout);
12361 PerlMem_free(esal);
12364 PerlMem_free(rsal);
12365 PerlMem_free(esa_out);
12366 if (esal_out != NULL)
12367 PerlMem_free(esal_out);
12368 PerlMem_free(rsa_out);
12369 if (rsal_out != NULL)
12370 PerlMem_free(rsal_out);
12371 set_errno(EVMSERR); set_vaxc_errno(sts);
12377 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
12378 sys$close(&fab_in); sys$close(&fab_out);
12379 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
12381 PerlMem_free(vmsin);
12382 PerlMem_free(vmsout);
12386 PerlMem_free(esal);
12389 PerlMem_free(rsal);
12390 PerlMem_free(esa_out);
12391 if (esal_out != NULL)
12392 PerlMem_free(esal_out);
12393 PerlMem_free(rsa_out);
12394 if (rsal_out != NULL)
12395 PerlMem_free(rsal_out);
12398 set_errno(EVMSERR); set_vaxc_errno(sts);
12404 } /* end of rmscopy() */
12408 /*** The following glue provides 'hooks' to make some of the routines
12409 * from this file available from Perl. These routines are sufficiently
12410 * basic, and are required sufficiently early in the build process,
12411 * that's it's nice to have them available to miniperl as well as the
12412 * full Perl, so they're set up here instead of in an extension. The
12413 * Perl code which handles importation of these names into a given
12414 * package lives in [.VMS]Filespec.pm in @INC.
12418 rmsexpand_fromperl(pTHX_ CV *cv)
12421 char *fspec, *defspec = NULL, *rslt;
12423 int fs_utf8, dfs_utf8;
12427 if (!items || items > 2)
12428 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
12429 fspec = SvPV(ST(0),n_a);
12430 fs_utf8 = SvUTF8(ST(0));
12431 if (!fspec || !*fspec) XSRETURN_UNDEF;
12433 defspec = SvPV(ST(1),n_a);
12434 dfs_utf8 = SvUTF8(ST(1));
12436 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
12437 ST(0) = sv_newmortal();
12438 if (rslt != NULL) {
12439 sv_usepvn(ST(0),rslt,strlen(rslt));
12448 vmsify_fromperl(pTHX_ CV *cv)
12455 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
12456 utf8_fl = SvUTF8(ST(0));
12457 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12458 ST(0) = sv_newmortal();
12459 if (vmsified != NULL) {
12460 sv_usepvn(ST(0),vmsified,strlen(vmsified));
12469 unixify_fromperl(pTHX_ CV *cv)
12476 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
12477 utf8_fl = SvUTF8(ST(0));
12478 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12479 ST(0) = sv_newmortal();
12480 if (unixified != NULL) {
12481 sv_usepvn(ST(0),unixified,strlen(unixified));
12490 fileify_fromperl(pTHX_ CV *cv)
12497 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
12498 utf8_fl = SvUTF8(ST(0));
12499 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12500 ST(0) = sv_newmortal();
12501 if (fileified != NULL) {
12502 sv_usepvn(ST(0),fileified,strlen(fileified));
12511 pathify_fromperl(pTHX_ CV *cv)
12518 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
12519 utf8_fl = SvUTF8(ST(0));
12520 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12521 ST(0) = sv_newmortal();
12522 if (pathified != NULL) {
12523 sv_usepvn(ST(0),pathified,strlen(pathified));
12532 vmspath_fromperl(pTHX_ CV *cv)
12539 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
12540 utf8_fl = SvUTF8(ST(0));
12541 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12542 ST(0) = sv_newmortal();
12543 if (vmspath != NULL) {
12544 sv_usepvn(ST(0),vmspath,strlen(vmspath));
12553 unixpath_fromperl(pTHX_ CV *cv)
12560 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
12561 utf8_fl = SvUTF8(ST(0));
12562 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12563 ST(0) = sv_newmortal();
12564 if (unixpath != NULL) {
12565 sv_usepvn(ST(0),unixpath,strlen(unixpath));
12574 candelete_fromperl(pTHX_ CV *cv)
12582 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
12584 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12585 Newx(fspec, VMS_MAXRSS, char);
12586 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
12587 if (SvTYPE(mysv) == SVt_PVGV) {
12588 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
12589 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12597 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
12598 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12605 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
12611 rmscopy_fromperl(pTHX_ CV *cv)
12614 char *inspec, *outspec, *inp, *outp;
12616 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
12617 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12618 unsigned long int sts;
12623 if (items < 2 || items > 3)
12624 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
12626 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12627 Newx(inspec, VMS_MAXRSS, char);
12628 if (SvTYPE(mysv) == SVt_PVGV) {
12629 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
12630 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12638 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
12639 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12645 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
12646 Newx(outspec, VMS_MAXRSS, char);
12647 if (SvTYPE(mysv) == SVt_PVGV) {
12648 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
12649 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12658 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
12659 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12666 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
12668 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
12674 /* The mod2fname is limited to shorter filenames by design, so it should
12675 * not be modified to support longer EFS pathnames
12678 mod2fname(pTHX_ CV *cv)
12681 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
12682 workbuff[NAM$C_MAXRSS*1 + 1];
12683 int total_namelen = 3, counter, num_entries;
12684 /* ODS-5 ups this, but we want to be consistent, so... */
12685 int max_name_len = 39;
12686 AV *in_array = (AV *)SvRV(ST(0));
12688 num_entries = av_len(in_array);
12690 /* All the names start with PL_. */
12691 strcpy(ultimate_name, "PL_");
12693 /* Clean up our working buffer */
12694 Zero(work_name, sizeof(work_name), char);
12696 /* Run through the entries and build up a working name */
12697 for(counter = 0; counter <= num_entries; counter++) {
12698 /* If it's not the first name then tack on a __ */
12700 strcat(work_name, "__");
12702 strcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)));
12705 /* Check to see if we actually have to bother...*/
12706 if (strlen(work_name) + 3 <= max_name_len) {
12707 strcat(ultimate_name, work_name);
12709 /* It's too darned big, so we need to go strip. We use the same */
12710 /* algorithm as xsubpp does. First, strip out doubled __ */
12711 char *source, *dest, last;
12714 for (source = work_name; *source; source++) {
12715 if (last == *source && last == '_') {
12721 /* Go put it back */
12722 strcpy(work_name, workbuff);
12723 /* Is it still too big? */
12724 if (strlen(work_name) + 3 > max_name_len) {
12725 /* Strip duplicate letters */
12728 for (source = work_name; *source; source++) {
12729 if (last == toupper(*source)) {
12733 last = toupper(*source);
12735 strcpy(work_name, workbuff);
12738 /* Is it *still* too big? */
12739 if (strlen(work_name) + 3 > max_name_len) {
12740 /* Too bad, we truncate */
12741 work_name[max_name_len - 2] = 0;
12743 strcat(ultimate_name, work_name);
12746 /* Okay, return it */
12747 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
12752 hushexit_fromperl(pTHX_ CV *cv)
12757 VMSISH_HUSHED = SvTRUE(ST(0));
12759 ST(0) = boolSV(VMSISH_HUSHED);
12765 Perl_vms_start_glob
12766 (pTHX_ SV *tmpglob,
12770 struct vs_str_st *rslt;
12774 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
12777 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12778 struct dsc$descriptor_vs rsdsc;
12779 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
12780 unsigned long hasver = 0, isunix = 0;
12781 unsigned long int lff_flags = 0;
12784 if (!SvOK(tmpglob)) {
12785 SETERRNO(ENOENT,RMS$_FNF);
12789 #ifdef VMS_LONGNAME_SUPPORT
12790 lff_flags = LIB$M_FIL_LONG_NAMES;
12792 /* The Newx macro will not allow me to assign a smaller array
12793 * to the rslt pointer, so we will assign it to the begin char pointer
12794 * and then copy the value into the rslt pointer.
12796 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
12797 rslt = (struct vs_str_st *)begin;
12799 rstr = &rslt->str[0];
12800 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
12801 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
12802 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
12803 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
12805 Newx(vmsspec, VMS_MAXRSS, char);
12807 /* We could find out if there's an explicit dev/dir or version
12808 by peeking into lib$find_file's internal context at
12809 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
12810 but that's unsupported, so I don't want to do it now and
12811 have it bite someone in the future. */
12812 /* Fix-me: vms_split_path() is the only way to do this, the
12813 existing method will fail with many legal EFS or UNIX specifications
12816 cp = SvPV(tmpglob,i);
12819 if (cp[i] == ';') hasver = 1;
12820 if (cp[i] == '.') {
12821 if (sts) hasver = 1;
12824 if (cp[i] == '/') {
12825 hasdir = isunix = 1;
12828 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
12833 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
12837 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
12838 if (!stat_sts && S_ISDIR(st.st_mode)) {
12839 wilddsc.dsc$a_pointer = tovmspath_utf8(SvPVX(tmpglob),vmsspec,NULL);
12840 ok = (wilddsc.dsc$a_pointer != NULL);
12841 /* maybe passed 'foo' rather than '[.foo]', thus not detected above */
12845 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
12846 ok = (wilddsc.dsc$a_pointer != NULL);
12849 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
12851 /* If not extended character set, replace ? with % */
12852 /* With extended character set, ? is a wildcard single character */
12853 if (!decc_efs_case_preserve) {
12854 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++)
12855 if (*cp == '?') *cp = '%';
12858 while (ok && $VMS_STATUS_SUCCESS(sts)) {
12859 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
12860 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
12862 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
12863 &dfltdsc,NULL,&rms_sts,&lff_flags);
12864 if (!$VMS_STATUS_SUCCESS(sts))
12869 /* with varying string, 1st word of buffer contains result length */
12870 rstr[rslt->length] = '\0';
12872 /* Find where all the components are */
12873 v_sts = vms_split_path
12888 /* If no version on input, truncate the version on output */
12889 if (!hasver && (vs_len > 0)) {
12893 /* No version & a null extension on UNIX handling */
12894 if (isunix && (e_len == 1) && decc_readdir_dropdotnotype) {
12900 if (!decc_efs_case_preserve) {
12901 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
12905 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
12909 /* Start with the name */
12912 strcat(begin,"\n");
12913 ok = (PerlIO_puts(tmpfp,begin) != EOF);
12915 if (cxt) (void)lib$find_file_end(&cxt);
12918 /* Be POSIXish: return the input pattern when no matches */
12919 strcpy(rstr,SvPVX(tmpglob));
12921 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
12924 if (ok && sts != RMS$_NMF &&
12925 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
12928 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
12930 PerlIO_close(tmpfp);
12934 PerlIO_rewind(tmpfp);
12935 IoTYPE(io) = IoTYPE_RDONLY;
12936 IoIFP(io) = fp = tmpfp;
12937 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
12947 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
12951 unixrealpath_fromperl(pTHX_ CV *cv)
12954 char *fspec, *rslt_spec, *rslt;
12957 if (!items || items != 1)
12958 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
12960 fspec = SvPV(ST(0),n_a);
12961 if (!fspec || !*fspec) XSRETURN_UNDEF;
12963 Newx(rslt_spec, VMS_MAXRSS + 1, char);
12964 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
12966 ST(0) = sv_newmortal();
12968 sv_usepvn(ST(0),rslt,strlen(rslt));
12970 Safefree(rslt_spec);
12975 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
12979 vmsrealpath_fromperl(pTHX_ CV *cv)
12982 char *fspec, *rslt_spec, *rslt;
12985 if (!items || items != 1)
12986 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
12988 fspec = SvPV(ST(0),n_a);
12989 if (!fspec || !*fspec) XSRETURN_UNDEF;
12991 Newx(rslt_spec, VMS_MAXRSS + 1, char);
12992 rslt = do_vms_realname(fspec, rslt_spec, NULL);
12994 ST(0) = sv_newmortal();
12996 sv_usepvn(ST(0),rslt,strlen(rslt));
12998 Safefree(rslt_spec);
13004 * A thin wrapper around decc$symlink to make sure we follow the
13005 * standard and do not create a symlink with a zero-length name.
13007 * Also in ODS-2 mode, existing tests assume that the link target
13008 * will be converted to UNIX format.
13010 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13011 int Perl_my_symlink(pTHX_ const char *contents, const char *link_name) {
13012 if (!link_name || !*link_name) {
13013 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13017 if (decc_efs_charset) {
13018 return symlink(contents, link_name);
13023 /* Unless we are in ODS-5 mode, convert the symlink target to UNIX */
13024 /* because in order to work, the symlink target must be in UNIX format */
13026 /* As symbolic links can hold things other than files, we will only do */
13027 /* the conversion in in ODS-2 mode */
13029 Newx(utarget, VMS_MAXRSS + 1, char);
13030 if (do_tounixspec(contents, utarget, 0, NULL) == NULL) {
13032 /* This should not fail, as an untranslatable filename */
13033 /* should be passed through */
13034 utarget = (char *)contents;
13036 sts = symlink(utarget, link_name);
13044 #endif /* HAS_SYMLINK */
13046 int do_vms_case_tolerant(void);
13049 case_tolerant_process_fromperl(pTHX_ CV *cv)
13052 ST(0) = boolSV(do_vms_case_tolerant());
13056 #ifdef USE_ITHREADS
13059 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
13060 struct interp_intern *dst)
13062 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13064 memcpy(dst,src,sizeof(struct interp_intern));
13070 Perl_sys_intern_clear(pTHX)
13075 Perl_sys_intern_init(pTHX)
13077 unsigned int ix = RAND_MAX;
13082 /* fix me later to track running under GNV */
13083 /* this allows some limited testing */
13084 MY_POSIX_EXIT = decc_filename_unix_report;
13087 MY_INV_RAND_MAX = 1./x;
13091 init_os_extras(void)
13094 char* file = __FILE__;
13095 if (decc_disable_to_vms_logname_translation) {
13096 no_translate_barewords = TRUE;
13098 no_translate_barewords = FALSE;
13101 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
13102 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
13103 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
13104 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
13105 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
13106 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
13107 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
13108 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
13109 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
13110 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
13111 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
13112 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
13113 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
13114 newXSproto("VMS::Filespec::case_tolerant_process",
13115 case_tolerant_process_fromperl,file,"");
13117 store_pipelocs(aTHX); /* will redo any earlier attempts */
13122 #if __CRTL_VER == 80200000
13123 /* This missed getting in to the DECC SDK for 8.2 */
13124 char *realpath(const char *file_name, char * resolved_name, ...);
13127 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
13128 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
13129 * The perl fallback routine to provide realpath() is not as efficient
13133 /* Hack, use old stat() as fastest way of getting ino_t and device */
13134 int decc$stat(const char *name, void * statbuf);
13137 /* Realpath is fragile. In 8.3 it does not work if the feature
13138 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
13139 * links are implemented in RMS, not the CRTL. It also can fail if the
13140 * user does not have read/execute access to some of the directories.
13141 * So in order for Do What I Mean mode to work, if realpath() fails,
13142 * fall back to looking up the filename by the device name and FID.
13145 int vms_fid_to_name(char * outname, int outlen, const char * name)
13149 unsigned short st_ino[3];
13150 unsigned short padw;
13151 unsigned long padl[30]; /* plenty of room */
13154 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13155 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13157 sts = decc$stat(name, &statbuf);
13160 dvidsc.dsc$a_pointer=statbuf.st_dev;
13161 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
13163 specdsc.dsc$a_pointer = outname;
13164 specdsc.dsc$w_length = outlen-1;
13166 sts = lib$fid_to_name
13167 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
13168 if ($VMS_STATUS_SUCCESS(sts)) {
13169 outname[specdsc.dsc$w_length] = 0;
13179 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
13182 char * rslt = NULL;
13185 if (decc_posix_compliant_pathnames > 0 ) {
13186 /* realpath currently only works if posix compliant pathnames are
13187 * enabled. It may start working when they are not, but in that
13188 * case we still want the fallback behavior for backwards compatibility
13190 rslt = realpath(filespec, outbuf);
13194 if (rslt == NULL) {
13196 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13197 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13200 /* Fall back to fid_to_name */
13202 Newx(vms_spec, VMS_MAXRSS + 1, char);
13204 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec);
13208 /* Now need to trim the version off */
13209 sts = vms_split_path
13229 /* Trim off the version */
13230 int file_len = v_len + r_len + d_len + n_len + e_len;
13231 vms_spec[file_len] = 0;
13233 /* The result is expected to be in UNIX format */
13234 rslt = do_tounixspec(vms_spec, outbuf, 0, utf8_fl);
13236 /* Downcase if input had any lower case letters and
13237 * case preservation is not in effect.
13239 if (!decc_efs_case_preserve) {
13240 for (cp = filespec; *cp; cp++)
13241 if (islower(*cp)) { haslower = 1; break; }
13243 if (haslower) __mystrtolower(rslt);
13248 /* Now for some hacks to deal with backwards and forward */
13250 if (!decc_efs_charset) {
13252 /* 1. ODS-2 mode wants to do a syntax only translation */
13253 rslt = do_rmsexpand(filespec, outbuf,
13254 0, NULL, 0, NULL, utf8_fl);
13257 if (decc_filename_unix_report) {
13259 char * vms_dir_name;
13262 /* 2. ODS-5 / UNIX report mode should return a failure */
13263 /* if the parent directory also does not exist */
13264 /* Otherwise, get the real path for the parent */
13265 /* and add the child to it.
13267 /* basename / dirname only available for VMS 7.0+ */
13268 /* So we may need to implement them as common routines */
13270 Newx(dir_name, VMS_MAXRSS + 1, char);
13271 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
13272 dir_name[0] = '\0';
13275 /* First try a VMS parse */
13276 sts = vms_split_path
13294 int dir_len = v_len + r_len + d_len + n_len;
13296 strncpy(dir_name, filespec, dir_len);
13297 dir_name[dir_len] = '\0';
13298 file_name = (char *)&filespec[dir_len + 1];
13301 /* This must be UNIX */
13304 tchar = strrchr(filespec, '/');
13306 if (tchar != NULL) {
13307 int dir_len = tchar - filespec;
13308 strncpy(dir_name, filespec, dir_len);
13309 dir_name[dir_len] = '\0';
13310 file_name = (char *) &filespec[dir_len + 1];
13314 /* Dir name is defaulted */
13315 if (dir_name[0] == 0) {
13317 dir_name[1] = '\0';
13320 /* Need realpath for the directory */
13321 sts = vms_fid_to_name(vms_dir_name,
13326 /* Now need to pathify it.
13327 char *tdir = do_pathify_dirspec(vms_dir_name,
13330 /* And now add the original filespec to it */
13331 if (file_name != NULL) {
13332 strcat(outbuf, file_name);
13336 Safefree(vms_dir_name);
13337 Safefree(dir_name);
13341 Safefree(vms_spec);
13347 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
13350 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13351 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13354 /* Fall back to fid_to_name */
13356 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec);
13363 /* Now need to trim the version off */
13364 sts = vms_split_path
13384 /* Trim off the version */
13385 int file_len = v_len + r_len + d_len + n_len + e_len;
13386 outbuf[file_len] = 0;
13388 /* Downcase if input had any lower case letters and
13389 * case preservation is not in effect.
13391 if (!decc_efs_case_preserve) {
13392 for (cp = filespec; *cp; cp++)
13393 if (islower(*cp)) { haslower = 1; break; }
13395 if (haslower) __mystrtolower(outbuf);
13404 /* External entry points */
13405 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13406 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
13408 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13409 { return do_vms_realname(filespec, outbuf, utf8_fl); }
13411 /* case_tolerant */
13413 /*{{{int do_vms_case_tolerant(void)*/
13414 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
13415 * controlled by a process setting.
13417 int do_vms_case_tolerant(void)
13419 return vms_process_case_tolerant;
13422 /* External entry points */
13423 #if __CRTL_VER >= 70301000 && !defined(__VAX)
13424 int Perl_vms_case_tolerant(void)
13425 { return do_vms_case_tolerant(); }
13427 int Perl_vms_case_tolerant(void)
13428 { return vms_process_case_tolerant; }
13432 /* Start of DECC RTL Feature handling */
13434 static int sys_trnlnm
13435 (const char * logname,
13439 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
13440 const unsigned long attr = LNM$M_CASE_BLIND;
13441 struct dsc$descriptor_s name_dsc;
13443 unsigned short result;
13444 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
13447 name_dsc.dsc$w_length = strlen(logname);
13448 name_dsc.dsc$a_pointer = (char *)logname;
13449 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
13450 name_dsc.dsc$b_class = DSC$K_CLASS_S;
13452 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
13454 if ($VMS_STATUS_SUCCESS(status)) {
13456 /* Null terminate and return the string */
13457 /*--------------------------------------*/
13464 static int sys_crelnm
13465 (const char * logname,
13466 const char * value)
13469 const char * proc_table = "LNM$PROCESS_TABLE";
13470 struct dsc$descriptor_s proc_table_dsc;
13471 struct dsc$descriptor_s logname_dsc;
13472 struct itmlst_3 item_list[2];
13474 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
13475 proc_table_dsc.dsc$w_length = strlen(proc_table);
13476 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
13477 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
13479 logname_dsc.dsc$a_pointer = (char *) logname;
13480 logname_dsc.dsc$w_length = strlen(logname);
13481 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
13482 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
13484 item_list[0].buflen = strlen(value);
13485 item_list[0].itmcode = LNM$_STRING;
13486 item_list[0].bufadr = (char *)value;
13487 item_list[0].retlen = NULL;
13489 item_list[1].buflen = 0;
13490 item_list[1].itmcode = 0;
13492 ret_val = sys$crelnm
13494 (const struct dsc$descriptor_s *)&proc_table_dsc,
13495 (const struct dsc$descriptor_s *)&logname_dsc,
13497 (const struct item_list_3 *) item_list);
13502 /* C RTL Feature settings */
13504 static int set_features
13505 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
13506 int (* cli_routine)(void), /* Not documented */
13507 void *image_info) /* Not documented */
13514 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
13515 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
13516 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
13517 unsigned long case_perm;
13518 unsigned long case_image;
13521 /* Allow an exception to bring Perl into the VMS debugger */
13522 vms_debug_on_exception = 0;
13523 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
13524 if ($VMS_STATUS_SUCCESS(status)) {
13525 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13526 vms_debug_on_exception = 1;
13528 vms_debug_on_exception = 0;
13531 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
13532 vms_vtf7_filenames = 0;
13533 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
13534 if ($VMS_STATUS_SUCCESS(status)) {
13535 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13536 vms_vtf7_filenames = 1;
13538 vms_vtf7_filenames = 0;
13542 /* unlink all versions on unlink() or rename() */
13543 vms_unlink_all_versions = 0;
13544 status = sys_trnlnm
13545 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
13546 if ($VMS_STATUS_SUCCESS(status)) {
13547 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13548 vms_unlink_all_versions = 1;
13550 vms_unlink_all_versions = 0;
13553 /* Dectect running under GNV Bash or other UNIX like shell */
13554 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13555 gnv_unix_shell = 0;
13556 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
13557 if ($VMS_STATUS_SUCCESS(status)) {
13558 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13559 gnv_unix_shell = 1;
13560 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
13561 set_feature_default("DECC$EFS_CHARSET", 1);
13562 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
13563 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
13564 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
13565 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
13566 vms_unlink_all_versions = 1;
13569 gnv_unix_shell = 0;
13573 /* hacks to see if known bugs are still present for testing */
13575 /* Readdir is returning filenames in VMS syntax always */
13576 decc_bug_readdir_efs1 = 1;
13577 status = sys_trnlnm("DECC_BUG_READDIR_EFS1", val_str, sizeof(val_str));
13578 if ($VMS_STATUS_SUCCESS(status)) {
13579 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13580 decc_bug_readdir_efs1 = 1;
13582 decc_bug_readdir_efs1 = 0;
13585 /* PCP mode requires creating /dev/null special device file */
13586 decc_bug_devnull = 0;
13587 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
13588 if ($VMS_STATUS_SUCCESS(status)) {
13589 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13590 decc_bug_devnull = 1;
13592 decc_bug_devnull = 0;
13595 /* fgetname returning a VMS name in UNIX mode */
13596 decc_bug_fgetname = 1;
13597 status = sys_trnlnm("DECC_BUG_FGETNAME", val_str, sizeof(val_str));
13598 if ($VMS_STATUS_SUCCESS(status)) {
13599 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13600 decc_bug_fgetname = 1;
13602 decc_bug_fgetname = 0;
13605 /* UNIX directory names with no paths are broken in a lot of places */
13606 decc_dir_barename = 1;
13607 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
13608 if ($VMS_STATUS_SUCCESS(status)) {
13609 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13610 decc_dir_barename = 1;
13612 decc_dir_barename = 0;
13615 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13616 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
13618 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
13619 if (decc_disable_to_vms_logname_translation < 0)
13620 decc_disable_to_vms_logname_translation = 0;
13623 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
13625 decc_efs_case_preserve = decc$feature_get_value(s, 1);
13626 if (decc_efs_case_preserve < 0)
13627 decc_efs_case_preserve = 0;
13630 s = decc$feature_get_index("DECC$EFS_CHARSET");
13632 decc_efs_charset = decc$feature_get_value(s, 1);
13633 if (decc_efs_charset < 0)
13634 decc_efs_charset = 0;
13637 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
13639 decc_filename_unix_report = decc$feature_get_value(s, 1);
13640 if (decc_filename_unix_report > 0)
13641 decc_filename_unix_report = 1;
13643 decc_filename_unix_report = 0;
13646 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
13648 decc_filename_unix_only = decc$feature_get_value(s, 1);
13649 if (decc_filename_unix_only > 0) {
13650 decc_filename_unix_only = 1;
13653 decc_filename_unix_only = 0;
13657 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
13659 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
13660 if (decc_filename_unix_no_version < 0)
13661 decc_filename_unix_no_version = 0;
13664 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
13666 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
13667 if (decc_readdir_dropdotnotype < 0)
13668 decc_readdir_dropdotnotype = 0;
13671 status = sys_trnlnm("SYS$POSIX_ROOT", val_str, sizeof(val_str));
13672 if ($VMS_STATUS_SUCCESS(status)) {
13673 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
13675 dflt = decc$feature_get_value(s, 4);
13677 decc_disable_posix_root = decc$feature_get_value(s, 1);
13678 if (decc_disable_posix_root <= 0) {
13679 decc$feature_set_value(s, 1, 1);
13680 decc_disable_posix_root = 1;
13684 /* Traditionally Perl assumes this is off */
13685 decc_disable_posix_root = 1;
13686 decc$feature_set_value(s, 1, 1);
13691 #if __CRTL_VER >= 80200000
13692 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
13694 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
13695 if (decc_posix_compliant_pathnames < 0)
13696 decc_posix_compliant_pathnames = 0;
13697 if (decc_posix_compliant_pathnames > 4)
13698 decc_posix_compliant_pathnames = 0;
13703 status = sys_trnlnm
13704 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
13705 if ($VMS_STATUS_SUCCESS(status)) {
13706 val_str[0] = _toupper(val_str[0]);
13707 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13708 decc_disable_to_vms_logname_translation = 1;
13713 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
13714 if ($VMS_STATUS_SUCCESS(status)) {
13715 val_str[0] = _toupper(val_str[0]);
13716 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13717 decc_efs_case_preserve = 1;
13722 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
13723 if ($VMS_STATUS_SUCCESS(status)) {
13724 val_str[0] = _toupper(val_str[0]);
13725 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13726 decc_filename_unix_report = 1;
13729 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
13730 if ($VMS_STATUS_SUCCESS(status)) {
13731 val_str[0] = _toupper(val_str[0]);
13732 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13733 decc_filename_unix_only = 1;
13734 decc_filename_unix_report = 1;
13737 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
13738 if ($VMS_STATUS_SUCCESS(status)) {
13739 val_str[0] = _toupper(val_str[0]);
13740 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13741 decc_filename_unix_no_version = 1;
13744 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
13745 if ($VMS_STATUS_SUCCESS(status)) {
13746 val_str[0] = _toupper(val_str[0]);
13747 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13748 decc_readdir_dropdotnotype = 1;
13753 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
13755 /* Report true case tolerance */
13756 /*----------------------------*/
13757 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
13758 if (!$VMS_STATUS_SUCCESS(status))
13759 case_perm = PPROP$K_CASE_BLIND;
13760 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
13761 if (!$VMS_STATUS_SUCCESS(status))
13762 case_image = PPROP$K_CASE_BLIND;
13763 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
13764 (case_image == PPROP$K_CASE_SENSITIVE))
13765 vms_process_case_tolerant = 0;
13770 /* CRTL can be initialized past this point, but not before. */
13771 /* DECC$CRTL_INIT(); */
13778 #pragma extern_model save
13779 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
13780 const __align (LONGWORD) int spare[8] = {0};
13782 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
13783 #if __DECC_VER >= 60560002
13784 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
13786 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
13788 #endif /* __DECC */
13790 const long vms_cc_features = (const long)set_features;
13793 ** Force a reference to LIB$INITIALIZE to ensure it
13794 ** exists in the image.
13796 int lib$initialize(void);
13798 #pragma extern_model strict_refdef
13800 int lib_init_ref = (int) lib$initialize;
13803 #pragma extern_model restore