3 * VMS-specific routines for perl5
5 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
6 * 2002, 2003, 2004, 2005, 2006, 2007 by Charles Bailey and others.
8 * You may distribute under the terms of either the GNU General Public
9 * License or the Artistic License, as specified in the README file.
11 * Please see Changes*.* or the Perl Repository Browser for revision history.
15 * Yet small as was their hunted band
16 * still fell and fearless was each hand,
17 * and strong deeds they wrought yet oft,
18 * and loved the woods, whose ways more soft
19 * them seemed than thralls of that black throne
20 * to live and languish in halls of stone.
21 * "The Lay of Leithian", Canto II, lines 135-40
23 * [p.162 of _The Lays of Beleriand_]
32 #include <climsgdef.h>
43 #include <libclidef.h>
45 #include <lib$routines.h>
49 #if __CRTL_VER >= 70301000 && !defined(__VAX)
59 #include <str$routines.h>
66 #if __CRTL_VER >= 70000000 /* FIXME to earliest version */
68 #define NO_EFN EFN$C_ENF
73 #if __CRTL_VER < 70301000 && __CRTL_VER >= 70300000
74 int decc$feature_get_index(const char *name);
75 char* decc$feature_get_name(int index);
76 int decc$feature_get_value(int index, int mode);
77 int decc$feature_set_value(int index, int mode, int value);
82 #pragma member_alignment save
83 #pragma nomember_alignment longword
88 unsigned short * retadr;
90 #pragma member_alignment restore
92 /* More specific prototype than in starlet_c.h makes programming errors
100 const struct dsc$descriptor_s * devnam,
101 const struct item_list_3 * itmlst,
103 void * (astadr)(unsigned long),
108 #ifdef sys$get_security
109 #undef sys$get_security
111 (const struct dsc$descriptor_s * clsnam,
112 const struct dsc$descriptor_s * objnam,
113 const unsigned int *objhan,
115 const struct item_list_3 * itmlst,
116 unsigned int * contxt,
117 const unsigned int * acmode);
120 #ifdef sys$set_security
121 #undef sys$set_security
123 (const struct dsc$descriptor_s * clsnam,
124 const struct dsc$descriptor_s * objnam,
125 const unsigned int *objhan,
127 const struct item_list_3 * itmlst,
128 unsigned int * contxt,
129 const unsigned int * acmode);
132 #ifdef lib$find_image_symbol
133 #undef lib$find_image_symbol
134 int lib$find_image_symbol
135 (const struct dsc$descriptor_s * imgname,
136 const struct dsc$descriptor_s * symname,
138 const struct dsc$descriptor_s * defspec,
142 #ifdef lib$rename_file
143 #undef lib$rename_file
145 (const struct dsc$descriptor_s * old_file_dsc,
146 const struct dsc$descriptor_s * new_file_dsc,
147 const struct dsc$descriptor_s * default_file_dsc,
148 const struct dsc$descriptor_s * related_file_dsc,
149 const unsigned long * flags,
150 void * (success)(const struct dsc$descriptor_s * old_dsc,
151 const struct dsc$descriptor_s * new_dsc,
153 void * (error)(const struct dsc$descriptor_s * old_dsc,
154 const struct dsc$descriptor_s * new_dsc,
157 const int * error_src,
158 const void * usr_arg),
159 int (confirm)(const struct dsc$descriptor_s * old_dsc,
160 const struct dsc$descriptor_s * new_dsc,
161 const void * old_fab,
162 const void * usr_arg),
164 struct dsc$descriptor_s * old_result_name_dsc,
165 struct dsc$descriptor_s * new_result_name_dsc,
166 unsigned long * file_scan_context);
169 #if __CRTL_VER >= 70300000 && !defined(__VAX)
171 static int set_feature_default(const char *name, int value)
176 index = decc$feature_get_index(name);
178 status = decc$feature_set_value(index, 1, value);
179 if (index == -1 || (status == -1)) {
183 status = decc$feature_get_value(index, 1);
184 if (status != value) {
192 /* Older versions of ssdef.h don't have these */
193 #ifndef SS$_INVFILFOROP
194 # define SS$_INVFILFOROP 3930
196 #ifndef SS$_NOSUCHOBJECT
197 # define SS$_NOSUCHOBJECT 2696
200 /* We implement I/O here, so we will be mixing PerlIO and stdio calls. */
201 #define PERLIO_NOT_STDIO 0
203 /* Don't replace system definitions of vfork, getenv, lstat, and stat,
204 * code below needs to get to the underlying CRTL routines. */
205 #define DONT_MASK_RTL_CALLS
209 /* Anticipating future expansion in lexical warnings . . . */
210 #ifndef WARN_INTERNAL
211 # define WARN_INTERNAL WARN_MISC
214 #ifdef VMS_LONGNAME_SUPPORT
215 #include <libfildef.h>
218 #if defined(__VMS_VER) && __VMS_VER >= 70000000 && __DECC_VER >= 50200000
219 # define RTL_USES_UTC 1
222 /* Routine to create a decterm for use with the Perl debugger */
223 /* No headers, this information was found in the Programming Concepts Manual */
225 static int (*decw_term_port)
226 (const struct dsc$descriptor_s * display,
227 const struct dsc$descriptor_s * setup_file,
228 const struct dsc$descriptor_s * customization,
229 struct dsc$descriptor_s * result_device_name,
230 unsigned short * result_device_name_length,
233 void * char_change_buffer) = 0;
235 /* gcc's header files don't #define direct access macros
236 * corresponding to VAXC's variant structs */
238 # define uic$v_format uic$r_uic_form.uic$v_format
239 # define uic$v_group uic$r_uic_form.uic$v_group
240 # define uic$v_member uic$r_uic_form.uic$v_member
241 # define prv$v_bypass prv$r_prvdef_bits0.prv$v_bypass
242 # define prv$v_grpprv prv$r_prvdef_bits0.prv$v_grpprv
243 # define prv$v_readall prv$r_prvdef_bits0.prv$v_readall
244 # define prv$v_sysprv prv$r_prvdef_bits0.prv$v_sysprv
247 #if defined(NEED_AN_H_ERRNO)
252 #pragma message disable pragma
253 #pragma member_alignment save
254 #pragma nomember_alignment longword
256 #pragma message disable misalgndmem
259 unsigned short int buflen;
260 unsigned short int itmcode;
262 unsigned short int *retlen;
265 struct filescan_itmlst_2 {
266 unsigned short length;
267 unsigned short itmcode;
272 unsigned short length;
277 #pragma message restore
278 #pragma member_alignment restore
281 #define do_fileify_dirspec(a,b,c,d) mp_do_fileify_dirspec(aTHX_ a,b,c,d)
282 #define do_pathify_dirspec(a,b,c,d) mp_do_pathify_dirspec(aTHX_ a,b,c,d)
283 #define do_tovmsspec(a,b,c,d) mp_do_tovmsspec(aTHX_ a,b,c,0,d)
284 #define do_tovmspath(a,b,c,d) mp_do_tovmspath(aTHX_ a,b,c,d)
285 #define do_rmsexpand(a,b,c,d,e,f,g) mp_do_rmsexpand(aTHX_ a,b,c,d,e,f,g)
286 #define do_vms_realpath(a,b,c) mp_do_vms_realpath(aTHX_ a,b,c)
287 #define do_vms_realname(a,b,c) mp_do_vms_realname(aTHX_ a,b,c)
288 #define do_tounixspec(a,b,c,d) mp_do_tounixspec(aTHX_ a,b,c,d)
289 #define do_tounixpath(a,b,c,d) mp_do_tounixpath(aTHX_ a,b,c,d)
290 #define do_vms_case_tolerant(a) mp_do_vms_case_tolerant(a)
291 #define expand_wild_cards(a,b,c,d) mp_expand_wild_cards(aTHX_ a,b,c,d)
292 #define getredirection(a,b) mp_getredirection(aTHX_ a,b)
294 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int *);
295 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int *);
296 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
297 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int *);
299 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
300 #define PERL_LNM_MAX_ALLOWED_INDEX 127
302 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
303 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
306 #define PERL_LNM_MAX_ITER 10
308 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
309 #if __CRTL_VER >= 70302000 && !defined(__VAX)
310 #define MAX_DCL_SYMBOL (8192)
311 #define MAX_DCL_LINE_LENGTH (4096 - 4)
313 #define MAX_DCL_SYMBOL (1024)
314 #define MAX_DCL_LINE_LENGTH (1024 - 4)
317 static char *__mystrtolower(char *str)
319 if (str) for (; *str; ++str) *str= tolower(*str);
323 static struct dsc$descriptor_s fildevdsc =
324 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
325 static struct dsc$descriptor_s crtlenvdsc =
326 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
327 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
328 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
329 static struct dsc$descriptor_s **env_tables = defenv;
330 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
332 /* True if we shouldn't treat barewords as logicals during directory */
334 static int no_translate_barewords;
337 static int tz_updated = 1;
340 /* DECC Features that may need to affect how Perl interprets
341 * displays filename information
343 static int decc_disable_to_vms_logname_translation = 1;
344 static int decc_disable_posix_root = 1;
345 int decc_efs_case_preserve = 0;
346 static int decc_efs_charset = 0;
347 static int decc_efs_charset_index = -1;
348 static int decc_filename_unix_no_version = 0;
349 static int decc_filename_unix_only = 0;
350 int decc_filename_unix_report = 0;
351 int decc_posix_compliant_pathnames = 0;
352 int decc_readdir_dropdotnotype = 0;
353 static int vms_process_case_tolerant = 1;
354 int vms_vtf7_filenames = 0;
355 int gnv_unix_shell = 0;
356 static int vms_unlink_all_versions = 0;
357 static int vms_posix_exit = 0;
359 /* bug workarounds if needed */
360 int decc_bug_devnull = 1;
361 int decc_dir_barename = 0;
362 int vms_bug_stat_filename = 0;
364 static int vms_debug_on_exception = 0;
365 static int vms_debug_fileify = 0;
367 /* Simple logical name translation */
368 static int simple_trnlnm
369 (const char * logname,
373 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
374 const unsigned long attr = LNM$M_CASE_BLIND;
375 struct dsc$descriptor_s name_dsc;
377 unsigned short result;
378 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
381 name_dsc.dsc$w_length = strlen(logname);
382 name_dsc.dsc$a_pointer = (char *)logname;
383 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
384 name_dsc.dsc$b_class = DSC$K_CLASS_S;
386 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
388 if ($VMS_STATUS_SUCCESS(status)) {
390 /* Null terminate and return the string */
391 /*--------------------------------------*/
400 /* Is this a UNIX file specification?
401 * No longer a simple check with EFS file specs
402 * For now, not a full check, but need to
403 * handle POSIX ^UP^ specifications
404 * Fixing to handle ^/ cases would require
405 * changes to many other conversion routines.
408 static int is_unix_filespec(const char *path)
414 if (strncmp(path,"\"^UP^",5) != 0) {
415 pch1 = strchr(path, '/');
420 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
421 if (decc_filename_unix_report || decc_filename_unix_only) {
422 if (strcmp(path,".") == 0)
430 /* This routine converts a UCS-2 character to be VTF-7 encoded.
433 static void ucs2_to_vtf7
435 unsigned long ucs2_char,
438 unsigned char * ucs_ptr;
441 ucs_ptr = (unsigned char *)&ucs2_char;
445 hex = (ucs_ptr[1] >> 4) & 0xf;
447 outspec[2] = hex + '0';
449 outspec[2] = (hex - 9) + 'A';
450 hex = ucs_ptr[1] & 0xF;
452 outspec[3] = hex + '0';
454 outspec[3] = (hex - 9) + 'A';
456 hex = (ucs_ptr[0] >> 4) & 0xf;
458 outspec[4] = hex + '0';
460 outspec[4] = (hex - 9) + 'A';
461 hex = ucs_ptr[1] & 0xF;
463 outspec[5] = hex + '0';
465 outspec[5] = (hex - 9) + 'A';
471 /* This handles the conversion of a UNIX extended character set to a ^
472 * escaped VMS character.
473 * in a UNIX file specification.
475 * The output count variable contains the number of characters added
476 * to the output string.
478 * The return value is the number of characters read from the input string
480 static int copy_expand_unix_filename_escape
481 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
489 utf8_flag = *utf8_fl;
493 if (*inspec >= 0x80) {
494 if (utf8_fl && vms_vtf7_filenames) {
495 unsigned long ucs_char;
499 if ((*inspec & 0xE0) == 0xC0) {
501 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
502 if (ucs_char >= 0x80) {
503 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
506 } else if ((*inspec & 0xF0) == 0xE0) {
508 ucs_char = ((inspec[0] & 0xF) << 12) +
509 ((inspec[1] & 0x3f) << 6) +
511 if (ucs_char >= 0x800) {
512 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
516 #if 0 /* I do not see longer sequences supported by OpenVMS */
517 /* Maybe some one can fix this later */
518 } else if ((*inspec & 0xF8) == 0xF0) {
521 } else if ((*inspec & 0xFC) == 0xF8) {
524 } else if ((*inspec & 0xFE) == 0xFC) {
531 /* High bit set, but not a Unicode character! */
533 /* Non printing DECMCS or ISO Latin-1 character? */
534 if (*inspec <= 0x9F) {
538 hex = (*inspec >> 4) & 0xF;
540 outspec[1] = hex + '0';
542 outspec[1] = (hex - 9) + 'A';
546 outspec[2] = hex + '0';
548 outspec[2] = (hex - 9) + 'A';
552 } else if (*inspec == 0xA0) {
558 } else if (*inspec == 0xFF) {
570 /* Is this a macro that needs to be passed through?
571 * Macros start with $( and an alpha character, followed
572 * by a string of alpha numeric characters ending with a )
573 * If this does not match, then encode it as ODS-5.
575 if ((inspec[0] == '$') && (inspec[1] == '(')) {
578 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
580 outspec[0] = inspec[0];
581 outspec[1] = inspec[1];
582 outspec[2] = inspec[2];
584 while(isalnum(inspec[tcnt]) ||
585 (inspec[2] == '.') || (inspec[2] == '_')) {
586 outspec[tcnt] = inspec[tcnt];
589 if (inspec[tcnt] == ')') {
590 outspec[tcnt] = inspec[tcnt];
607 if (decc_efs_charset == 0)
634 /* Don't escape again if following character is
635 * already something we escape.
637 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
643 /* But otherwise fall through and escape it. */
645 /* Assume that this is to be escaped */
647 outspec[1] = *inspec;
651 case ' ': /* space */
652 /* Assume that this is to be escaped */
667 /* This handles the expansion of a '^' prefix to the proper character
668 * in a UNIX file specification.
670 * The output count variable contains the number of characters added
671 * to the output string.
673 * The return value is the number of characters read from the input
676 static int copy_expand_vms_filename_escape
677 (char *outspec, const char *inspec, int *output_cnt)
684 if (*inspec == '^') {
687 /* Spaces and non-trailing dots should just be passed through,
688 * but eat the escape character.
695 case '_': /* space */
701 /* Hmm. Better leave the escape escaped. */
707 case 'U': /* Unicode - FIX-ME this is wrong. */
710 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
713 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
714 outspec[0] == c1 & 0xff;
715 outspec[1] == c2 & 0xff;
722 /* Error - do best we can to continue */
732 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
736 scnt = sscanf(inspec, "%2x", &c1);
737 outspec[0] = c1 & 0xff;
761 (const struct dsc$descriptor_s * srcstr,
762 struct filescan_itmlst_2 * valuelist,
763 unsigned long * fldflags,
764 struct dsc$descriptor_s *auxout,
765 unsigned short * retlen);
768 /* vms_split_path - Verify that the input file specification is a
769 * VMS format file specification, and provide pointers to the components of
770 * it. With EFS format filenames, this is virtually the only way to
771 * parse a VMS path specification into components.
773 * If the sum of the components do not add up to the length of the
774 * string, then the passed file specification is probably a UNIX style
777 static int vms_split_path
792 struct dsc$descriptor path_desc;
796 struct filescan_itmlst_2 item_list[9];
797 const int filespec = 0;
798 const int nodespec = 1;
799 const int devspec = 2;
800 const int rootspec = 3;
801 const int dirspec = 4;
802 const int namespec = 5;
803 const int typespec = 6;
804 const int verspec = 7;
806 /* Assume the worst for an easy exit */
821 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
822 path_desc.dsc$w_length = strlen(path);
823 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
824 path_desc.dsc$b_class = DSC$K_CLASS_S;
826 /* Get the total length, if it is shorter than the string passed
827 * then this was probably not a VMS formatted file specification
829 item_list[filespec].itmcode = FSCN$_FILESPEC;
830 item_list[filespec].length = 0;
831 item_list[filespec].component = NULL;
833 /* If the node is present, then it gets considered as part of the
834 * volume name to hopefully make things simple.
836 item_list[nodespec].itmcode = FSCN$_NODE;
837 item_list[nodespec].length = 0;
838 item_list[nodespec].component = NULL;
840 item_list[devspec].itmcode = FSCN$_DEVICE;
841 item_list[devspec].length = 0;
842 item_list[devspec].component = NULL;
844 /* root is a special case, adding it to either the directory or
845 * the device components will probalby complicate things for the
846 * callers of this routine, so leave it separate.
848 item_list[rootspec].itmcode = FSCN$_ROOT;
849 item_list[rootspec].length = 0;
850 item_list[rootspec].component = NULL;
852 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
853 item_list[dirspec].length = 0;
854 item_list[dirspec].component = NULL;
856 item_list[namespec].itmcode = FSCN$_NAME;
857 item_list[namespec].length = 0;
858 item_list[namespec].component = NULL;
860 item_list[typespec].itmcode = FSCN$_TYPE;
861 item_list[typespec].length = 0;
862 item_list[typespec].component = NULL;
864 item_list[verspec].itmcode = FSCN$_VERSION;
865 item_list[verspec].length = 0;
866 item_list[verspec].component = NULL;
868 item_list[8].itmcode = 0;
869 item_list[8].length = 0;
870 item_list[8].component = NULL;
872 status = sys$filescan
873 ((const struct dsc$descriptor_s *)&path_desc, item_list,
875 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
877 /* If we parsed it successfully these two lengths should be the same */
878 if (path_desc.dsc$w_length != item_list[filespec].length)
881 /* If we got here, then it is a VMS file specification */
884 /* set the volume name */
885 if (item_list[nodespec].length > 0) {
886 *volume = item_list[nodespec].component;
887 *vol_len = item_list[nodespec].length + item_list[devspec].length;
890 *volume = item_list[devspec].component;
891 *vol_len = item_list[devspec].length;
894 *root = item_list[rootspec].component;
895 *root_len = item_list[rootspec].length;
897 *dir = item_list[dirspec].component;
898 *dir_len = item_list[dirspec].length;
900 /* Now fun with versions and EFS file specifications
901 * The parser can not tell the difference when a "." is a version
902 * delimiter or a part of the file specification.
904 if ((decc_efs_charset) &&
905 (item_list[verspec].length > 0) &&
906 (item_list[verspec].component[0] == '.')) {
907 *name = item_list[namespec].component;
908 *name_len = item_list[namespec].length + item_list[typespec].length;
909 *ext = item_list[verspec].component;
910 *ext_len = item_list[verspec].length;
915 *name = item_list[namespec].component;
916 *name_len = item_list[namespec].length;
917 *ext = item_list[typespec].component;
918 *ext_len = item_list[typespec].length;
919 *version = item_list[verspec].component;
920 *ver_len = item_list[verspec].length;
927 * Routine to retrieve the maximum equivalence index for an input
928 * logical name. Some calls to this routine have no knowledge if
929 * the variable is a logical or not. So on error we return a max
932 /*{{{int my_maxidx(const char *lnm) */
934 my_maxidx(const char *lnm)
938 int attr = LNM$M_CASE_BLIND;
939 struct dsc$descriptor lnmdsc;
940 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
943 lnmdsc.dsc$w_length = strlen(lnm);
944 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
945 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
946 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
948 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
949 if ((status & 1) == 0)
956 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
958 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
959 struct dsc$descriptor_s **tabvec, unsigned long int flags)
962 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
963 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
964 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
966 unsigned char acmode;
967 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
968 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
969 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
970 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
972 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
973 #if defined(PERL_IMPLICIT_CONTEXT)
976 aTHX = PERL_GET_INTERP;
982 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
983 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
985 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
986 *cp2 = _toupper(*cp1);
987 if (cp1 - lnm > LNM$C_NAMLENGTH) {
988 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
992 lnmdsc.dsc$w_length = cp1 - lnm;
993 lnmdsc.dsc$a_pointer = uplnm;
994 uplnm[lnmdsc.dsc$w_length] = '\0';
995 secure = flags & PERL__TRNENV_SECURE;
996 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
997 if (!tabvec || !*tabvec) tabvec = env_tables;
999 for (curtab = 0; tabvec[curtab]; curtab++) {
1000 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1001 if (!ivenv && !secure) {
1006 #if defined(PERL_IMPLICIT_CONTEXT)
1009 "%%PERL-W-VMS_INIT Can't read CRTL environ\n");
1012 Perl_warn(aTHX_ "Can't read CRTL environ\n");
1015 retsts = SS$_NOLOGNAM;
1016 for (i = 0; environ[i]; i++) {
1017 if ((eq = strchr(environ[i],'=')) &&
1018 lnmdsc.dsc$w_length == (eq - environ[i]) &&
1019 !strncmp(environ[i],uplnm,eq - environ[i])) {
1021 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
1022 if (!eqvlen) continue;
1023 retsts = SS$_NORMAL;
1027 if (retsts != SS$_NOLOGNAM) break;
1030 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1031 !str$case_blind_compare(&tmpdsc,&clisym)) {
1032 if (!ivsym && !secure) {
1033 unsigned short int deflen = LNM$C_NAMLENGTH;
1034 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1035 /* dynamic dsc to accomodate possible long value */
1036 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
1037 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
1039 if (eqvlen > MAX_DCL_SYMBOL) {
1040 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
1041 eqvlen = MAX_DCL_SYMBOL;
1042 /* Special hack--we might be called before the interpreter's */
1043 /* fully initialized, in which case either thr or PL_curcop */
1044 /* might be bogus. We have to check, since ckWARN needs them */
1045 /* both to be valid if running threaded */
1046 #if defined(PERL_IMPLICIT_CONTEXT)
1049 "%Perl-VMS-Init, Value of CLI symbol \"%s\" too long",lnm);
1052 if (ckWARN(WARN_MISC)) {
1053 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
1056 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
1058 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
1059 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1060 if (retsts == LIB$_NOSUCHSYM) continue;
1065 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
1066 midx = my_maxidx(lnm);
1067 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1068 lnmlst[1].bufadr = cp2;
1070 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1071 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1072 if (retsts == SS$_NOLOGNAM) break;
1073 /* PPFs have a prefix */
1076 *((int *)uplnm) == *((int *)"SYS$") &&
1078 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
1079 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
1080 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
1081 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
1082 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
1083 memmove(eqv,eqv+4,eqvlen-4);
1089 if ((retsts == SS$_IVLOGNAM) ||
1090 (retsts == SS$_NOLOGNAM)) { continue; }
1093 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1094 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1095 if (retsts == SS$_NOLOGNAM) continue;
1098 eqvlen = strlen(eqv);
1102 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1103 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1104 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
1105 retsts == SS$_NOLOGNAM) {
1106 set_errno(EINVAL); set_vaxc_errno(retsts);
1108 else _ckvmssts_noperl(retsts);
1110 } /* end of vmstrnenv */
1113 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1114 /* Define as a function so we can access statics. */
1115 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1119 #if defined(PERL_IMPLICIT_CONTEXT)
1122 #ifdef SECURE_INTERNAL_GETENV
1123 flags = (PL_curinterp ? PL_tainting : will_taint) ?
1124 PERL__TRNENV_SECURE : 0;
1127 return vmstrnenv(lnm, eqv, idx, fildev, flags);
1132 * Note: Uses Perl temp to store result so char * can be returned to
1133 * caller; this pointer will be invalidated at next Perl statement
1135 * We define this as a function rather than a macro in terms of my_getenv_len()
1136 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1139 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1141 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1144 static char *__my_getenv_eqv = NULL;
1145 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1146 unsigned long int idx = 0;
1147 int trnsuccess, success, secure, saverr, savvmserr;
1151 midx = my_maxidx(lnm) + 1;
1153 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1154 /* Set up a temporary buffer for the return value; Perl will
1155 * clean it up at the next statement transition */
1156 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1157 if (!tmpsv) return NULL;
1161 /* Assume no interpreter ==> single thread */
1162 if (__my_getenv_eqv != NULL) {
1163 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1166 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1168 eqv = __my_getenv_eqv;
1171 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1172 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1174 getcwd(eqv,LNM$C_NAMLENGTH);
1178 /* Get rid of "000000/ in rooted filespecs */
1181 zeros = strstr(eqv, "/000000/");
1182 if (zeros != NULL) {
1184 mlen = len - (zeros - eqv) - 7;
1185 memmove(zeros, &zeros[7], mlen);
1193 /* Impose security constraints only if tainting */
1195 /* Impose security constraints only if tainting */
1196 secure = PL_curinterp ? PL_tainting : will_taint;
1197 saverr = errno; savvmserr = vaxc$errno;
1204 #ifdef SECURE_INTERNAL_GETENV
1205 secure ? PERL__TRNENV_SECURE : 0
1211 /* For the getenv interface we combine all the equivalence names
1212 * of a search list logical into one value to acquire a maximum
1213 * value length of 255*128 (assuming %ENV is using logicals).
1215 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1217 /* If the name contains a semicolon-delimited index, parse it
1218 * off and make sure we only retrieve the equivalence name for
1220 if ((cp2 = strchr(lnm,';')) != NULL) {
1222 uplnm[cp2-lnm] = '\0';
1223 idx = strtoul(cp2+1,NULL,0);
1225 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1228 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1230 /* Discard NOLOGNAM on internal calls since we're often looking
1231 * for an optional name, and this "error" often shows up as the
1232 * (bogus) exit status for a die() call later on. */
1233 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1234 return success ? eqv : NULL;
1237 } /* end of my_getenv() */
1241 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1243 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1247 unsigned long idx = 0;
1249 static char *__my_getenv_len_eqv = NULL;
1250 int secure, saverr, savvmserr;
1253 midx = my_maxidx(lnm) + 1;
1255 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1256 /* Set up a temporary buffer for the return value; Perl will
1257 * clean it up at the next statement transition */
1258 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1259 if (!tmpsv) return NULL;
1263 /* Assume no interpreter ==> single thread */
1264 if (__my_getenv_len_eqv != NULL) {
1265 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1268 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1270 buf = __my_getenv_len_eqv;
1273 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1274 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1277 getcwd(buf,LNM$C_NAMLENGTH);
1280 /* Get rid of "000000/ in rooted filespecs */
1282 zeros = strstr(buf, "/000000/");
1283 if (zeros != NULL) {
1285 mlen = *len - (zeros - buf) - 7;
1286 memmove(zeros, &zeros[7], mlen);
1295 /* Impose security constraints only if tainting */
1296 secure = PL_curinterp ? PL_tainting : will_taint;
1297 saverr = errno; savvmserr = vaxc$errno;
1304 #ifdef SECURE_INTERNAL_GETENV
1305 secure ? PERL__TRNENV_SECURE : 0
1311 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1313 if ((cp2 = strchr(lnm,';')) != NULL) {
1315 buf[cp2-lnm] = '\0';
1316 idx = strtoul(cp2+1,NULL,0);
1318 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1321 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1323 /* Get rid of "000000/ in rooted filespecs */
1326 zeros = strstr(buf, "/000000/");
1327 if (zeros != NULL) {
1329 mlen = *len - (zeros - buf) - 7;
1330 memmove(zeros, &zeros[7], mlen);
1336 /* Discard NOLOGNAM on internal calls since we're often looking
1337 * for an optional name, and this "error" often shows up as the
1338 * (bogus) exit status for a die() call later on. */
1339 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1340 return *len ? buf : NULL;
1343 } /* end of my_getenv_len() */
1346 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1348 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1350 /*{{{ void prime_env_iter() */
1352 prime_env_iter(void)
1353 /* Fill the %ENV associative array with all logical names we can
1354 * find, in preparation for iterating over it.
1357 static int primed = 0;
1358 HV *seenhv = NULL, *envhv;
1360 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1361 unsigned short int chan;
1362 #ifndef CLI$M_TRUSTED
1363 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1365 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1366 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
1368 bool have_sym = FALSE, have_lnm = FALSE;
1369 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1370 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1371 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1372 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1373 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1374 #if defined(PERL_IMPLICIT_CONTEXT)
1377 #if defined(USE_ITHREADS)
1378 static perl_mutex primenv_mutex;
1379 MUTEX_INIT(&primenv_mutex);
1382 #if defined(PERL_IMPLICIT_CONTEXT)
1383 /* We jump through these hoops because we can be called at */
1384 /* platform-specific initialization time, which is before anything is */
1385 /* set up--we can't even do a plain dTHX since that relies on the */
1386 /* interpreter structure to be initialized */
1388 aTHX = PERL_GET_INTERP;
1390 /* we never get here because the NULL pointer will cause the */
1391 /* several of the routines called by this routine to access violate */
1393 /* This routine is only called by hv.c/hv_iterinit which has a */
1394 /* context, so the real fix may be to pass it through instead of */
1395 /* the hoops above */
1400 if (primed || !PL_envgv) return;
1401 MUTEX_LOCK(&primenv_mutex);
1402 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1403 envhv = GvHVn(PL_envgv);
1404 /* Perform a dummy fetch as an lval to insure that the hash table is
1405 * set up. Otherwise, the hv_store() will turn into a nullop. */
1406 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1408 for (i = 0; env_tables[i]; i++) {
1409 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1410 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1411 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1413 if (have_sym || have_lnm) {
1414 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1415 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1416 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1417 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1420 for (i--; i >= 0; i--) {
1421 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1424 for (j = 0; environ[j]; j++) {
1425 if (!(start = strchr(environ[j],'='))) {
1426 if (ckWARN(WARN_INTERNAL))
1427 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1431 sv = newSVpv(start,0);
1433 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1438 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1439 !str$case_blind_compare(&tmpdsc,&clisym)) {
1440 strcpy(cmd,"Show Symbol/Global *");
1441 cmddsc.dsc$w_length = 20;
1442 if (env_tables[i]->dsc$w_length == 12 &&
1443 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1444 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
1445 flags = defflags | CLI$M_NOLOGNAM;
1448 strcpy(cmd,"Show Logical *");
1449 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1450 strcat(cmd," /Table=");
1451 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1452 cmddsc.dsc$w_length = strlen(cmd);
1454 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1455 flags = defflags | CLI$M_NOCLISYM;
1458 /* Create a new subprocess to execute each command, to exclude the
1459 * remote possibility that someone could subvert a mbx or file used
1460 * to write multiple commands to a single subprocess.
1463 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1464 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1465 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1466 defflags &= ~CLI$M_TRUSTED;
1467 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1469 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1470 if (seenhv) SvREFCNT_dec(seenhv);
1473 char *cp1, *cp2, *key;
1474 unsigned long int sts, iosb[2], retlen, keylen;
1477 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1478 if (sts & 1) sts = iosb[0] & 0xffff;
1479 if (sts == SS$_ENDOFFILE) {
1481 while (substs == 0) { sys$hiber(); wakect++;}
1482 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1487 retlen = iosb[0] >> 16;
1488 if (!retlen) continue; /* blank line */
1490 if (iosb[1] != subpid) {
1492 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1496 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1497 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1499 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1500 if (*cp1 == '(' || /* Logical name table name */
1501 *cp1 == '=' /* Next eqv of searchlist */) continue;
1502 if (*cp1 == '"') cp1++;
1503 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1504 key = cp1; keylen = cp2 - cp1;
1505 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1506 while (*cp2 && *cp2 != '=') cp2++;
1507 while (*cp2 && *cp2 == '=') cp2++;
1508 while (*cp2 && *cp2 == ' ') cp2++;
1509 if (*cp2 == '"') { /* String translation; may embed "" */
1510 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1511 cp2++; cp1--; /* Skip "" surrounding translation */
1513 else { /* Numeric translation */
1514 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1515 cp1--; /* stop on last non-space char */
1517 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1518 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1521 PERL_HASH(hash,key,keylen);
1523 if (cp1 == cp2 && *cp2 == '.') {
1524 /* A single dot usually means an unprintable character, such as a null
1525 * to indicate a zero-length value. Get the actual value to make sure.
1527 char lnm[LNM$C_NAMLENGTH+1];
1528 char eqv[MAX_DCL_SYMBOL+1];
1530 strncpy(lnm, key, keylen);
1531 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1532 sv = newSVpvn(eqv, strlen(eqv));
1535 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1539 hv_store(envhv,key,keylen,sv,hash);
1540 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1542 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1543 /* get the PPFs for this process, not the subprocess */
1544 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1545 char eqv[LNM$C_NAMLENGTH+1];
1547 for (i = 0; ppfs[i]; i++) {
1548 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1549 sv = newSVpv(eqv,trnlen);
1551 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1556 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1557 if (buf) Safefree(buf);
1558 if (seenhv) SvREFCNT_dec(seenhv);
1559 MUTEX_UNLOCK(&primenv_mutex);
1562 } /* end of prime_env_iter */
1566 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1567 /* Define or delete an element in the same "environment" as
1568 * vmstrnenv(). If an element is to be deleted, it's removed from
1569 * the first place it's found. If it's to be set, it's set in the
1570 * place designated by the first element of the table vector.
1571 * Like setenv() returns 0 for success, non-zero on error.
1574 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1577 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1578 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1580 unsigned long int retsts, usermode = PSL$C_USER;
1581 struct itmlst_3 *ile, *ilist;
1582 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1583 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1584 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1585 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1586 $DESCRIPTOR(local,"_LOCAL");
1589 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1590 return SS$_IVLOGNAM;
1593 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1594 *cp2 = _toupper(*cp1);
1595 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1596 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1597 return SS$_IVLOGNAM;
1600 lnmdsc.dsc$w_length = cp1 - lnm;
1601 if (!tabvec || !*tabvec) tabvec = env_tables;
1603 if (!eqv) { /* we're deleting n element */
1604 for (curtab = 0; tabvec[curtab]; curtab++) {
1605 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1607 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1608 if ((cp1 = strchr(environ[i],'=')) &&
1609 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1610 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1612 return setenv(lnm,"",1) ? vaxc$errno : 0;
1615 ivenv = 1; retsts = SS$_NOLOGNAM;
1617 if (ckWARN(WARN_INTERNAL))
1618 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1619 ivenv = 1; retsts = SS$_NOSUCHPGM;
1625 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1626 !str$case_blind_compare(&tmpdsc,&clisym)) {
1627 unsigned int symtype;
1628 if (tabvec[curtab]->dsc$w_length == 12 &&
1629 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1630 !str$case_blind_compare(&tmpdsc,&local))
1631 symtype = LIB$K_CLI_LOCAL_SYM;
1632 else symtype = LIB$K_CLI_GLOBAL_SYM;
1633 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1634 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1635 if (retsts == LIB$_NOSUCHSYM) continue;
1639 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1640 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1641 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1642 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1643 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1647 else { /* we're defining a value */
1648 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1650 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1652 if (ckWARN(WARN_INTERNAL))
1653 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1654 retsts = SS$_NOSUCHPGM;
1658 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1659 eqvdsc.dsc$w_length = strlen(eqv);
1660 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1661 !str$case_blind_compare(&tmpdsc,&clisym)) {
1662 unsigned int symtype;
1663 if (tabvec[0]->dsc$w_length == 12 &&
1664 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1665 !str$case_blind_compare(&tmpdsc,&local))
1666 symtype = LIB$K_CLI_LOCAL_SYM;
1667 else symtype = LIB$K_CLI_GLOBAL_SYM;
1668 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1671 if (!*eqv) eqvdsc.dsc$w_length = 1;
1672 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1674 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1675 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1676 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1677 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1678 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1679 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1682 Newx(ilist,nseg+1,struct itmlst_3);
1685 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1688 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1690 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1691 ile->itmcode = LNM$_STRING;
1693 if ((j+1) == nseg) {
1694 ile->buflen = strlen(c);
1695 /* in case we are truncating one that's too long */
1696 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1699 ile->buflen = LNM$C_NAMLENGTH;
1703 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1707 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1712 if (!(retsts & 1)) {
1714 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1715 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1716 set_errno(EVMSERR); break;
1717 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1718 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1719 set_errno(EINVAL); break;
1721 set_errno(EACCES); break;
1726 set_vaxc_errno(retsts);
1727 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1730 /* We reset error values on success because Perl does an hv_fetch()
1731 * before each hv_store(), and if the thing we're setting didn't
1732 * previously exist, we've got a leftover error message. (Of course,
1733 * this fails in the face of
1734 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1735 * in that the error reported in $! isn't spurious,
1736 * but it's right more often than not.)
1738 set_errno(0); set_vaxc_errno(retsts);
1742 } /* end of vmssetenv() */
1745 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1746 /* This has to be a function since there's a prototype for it in proto.h */
1748 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1751 int len = strlen(lnm);
1755 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1756 if (!strcmp(uplnm,"DEFAULT")) {
1757 if (eqv && *eqv) my_chdir(eqv);
1761 #ifndef RTL_USES_UTC
1762 if (len == 6 || len == 2) {
1765 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1767 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1768 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1772 (void) vmssetenv(lnm,eqv,NULL);
1776 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1778 * sets a user-mode logical in the process logical name table
1779 * used for redirection of sys$error
1782 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1784 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1785 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1786 unsigned long int iss, attr = LNM$M_CONFINE;
1787 unsigned char acmode = PSL$C_USER;
1788 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1790 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1791 d_name.dsc$w_length = strlen(name);
1793 lnmlst[0].buflen = strlen(eqv);
1794 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1796 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1797 if (!(iss&1)) lib$signal(iss);
1802 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1803 /* my_crypt - VMS password hashing
1804 * my_crypt() provides an interface compatible with the Unix crypt()
1805 * C library function, and uses sys$hash_password() to perform VMS
1806 * password hashing. The quadword hashed password value is returned
1807 * as a NUL-terminated 8 character string. my_crypt() does not change
1808 * the case of its string arguments; in order to match the behavior
1809 * of LOGINOUT et al., alphabetic characters in both arguments must
1810 * be upcased by the caller.
1812 * - fix me to call ACM services when available
1815 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1817 # ifndef UAI$C_PREFERRED_ALGORITHM
1818 # define UAI$C_PREFERRED_ALGORITHM 127
1820 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1821 unsigned short int salt = 0;
1822 unsigned long int sts;
1824 unsigned short int dsc$w_length;
1825 unsigned char dsc$b_type;
1826 unsigned char dsc$b_class;
1827 const char * dsc$a_pointer;
1828 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1829 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1830 struct itmlst_3 uailst[3] = {
1831 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1832 { sizeof salt, UAI$_SALT, &salt, 0},
1833 { 0, 0, NULL, NULL}};
1834 static char hash[9];
1836 usrdsc.dsc$w_length = strlen(usrname);
1837 usrdsc.dsc$a_pointer = usrname;
1838 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1840 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1844 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1849 set_vaxc_errno(sts);
1850 if (sts != RMS$_RNF) return NULL;
1853 txtdsc.dsc$w_length = strlen(textpasswd);
1854 txtdsc.dsc$a_pointer = textpasswd;
1855 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1856 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1859 return (char *) hash;
1861 } /* end of my_crypt() */
1865 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1866 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1867 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1869 /* fixup barenames that are directories for internal use.
1870 * There have been problems with the consistent handling of UNIX
1871 * style directory names when routines are presented with a name that
1872 * has no directory delimitors at all. So this routine will eventually
1875 static char * fixup_bare_dirnames(const char * name)
1877 if (decc_disable_to_vms_logname_translation) {
1883 /* 8.3, remove() is now broken on symbolic links */
1884 static int rms_erase(const char * vmsname);
1888 * A little hack to get around a bug in some implemenation of remove()
1889 * that do not know how to delete a directory
1891 * Delete any file to which user has control access, regardless of whether
1892 * delete access is explicitly allowed.
1893 * Limitations: User must have write access to parent directory.
1894 * Does not block signals or ASTs; if interrupted in midstream
1895 * may leave file with an altered ACL.
1898 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1900 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1904 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1905 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1906 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1908 unsigned char myace$b_length;
1909 unsigned char myace$b_type;
1910 unsigned short int myace$w_flags;
1911 unsigned long int myace$l_access;
1912 unsigned long int myace$l_ident;
1913 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1914 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1915 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1917 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1918 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1919 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1920 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1921 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1922 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1924 /* Expand the input spec using RMS, since the CRTL remove() and
1925 * system services won't do this by themselves, so we may miss
1926 * a file "hiding" behind a logical name or search list. */
1927 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
1928 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1930 rslt = do_rmsexpand(name,
1934 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_SYMLINK,
1938 PerlMem_free(vmsname);
1942 /* Erase the file */
1943 rmsts = rms_erase(vmsname);
1945 /* Did it succeed */
1946 if ($VMS_STATUS_SUCCESS(rmsts)) {
1947 PerlMem_free(vmsname);
1951 /* If not, can changing protections help? */
1952 if (rmsts != RMS$_PRV) {
1953 set_vaxc_errno(rmsts);
1954 PerlMem_free(vmsname);
1958 /* No, so we get our own UIC to use as a rights identifier,
1959 * and the insert an ACE at the head of the ACL which allows us
1960 * to delete the file.
1962 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1963 fildsc.dsc$w_length = strlen(vmsname);
1964 fildsc.dsc$a_pointer = vmsname;
1966 newace.myace$l_ident = oldace.myace$l_ident;
1968 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
1970 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
1971 set_errno(ENOENT); break;
1973 set_errno(ENOTDIR); break;
1975 set_errno(ENODEV); break;
1976 case RMS$_SYN: case SS$_INVFILFOROP:
1977 set_errno(EINVAL); break;
1979 set_errno(EACCES); break;
1981 _ckvmssts_noperl(aclsts);
1983 set_vaxc_errno(aclsts);
1984 PerlMem_free(vmsname);
1987 /* Grab any existing ACEs with this identifier in case we fail */
1988 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
1989 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
1990 || fndsts == SS$_NOMOREACE ) {
1991 /* Add the new ACE . . . */
1992 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
1995 rmsts = rms_erase(vmsname);
1996 if ($VMS_STATUS_SUCCESS(rmsts)) {
2001 /* We blew it - dir with files in it, no write priv for
2002 * parent directory, etc. Put things back the way they were. */
2003 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
2006 addlst[0].bufadr = &oldace;
2007 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
2014 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
2015 /* We just deleted it, so of course it's not there. Some versions of
2016 * VMS seem to return success on the unlock operation anyhow (after all
2017 * the unlock is successful), but others don't.
2019 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
2020 if (aclsts & 1) aclsts = fndsts;
2021 if (!(aclsts & 1)) {
2023 set_vaxc_errno(aclsts);
2026 PerlMem_free(vmsname);
2029 } /* end of kill_file() */
2033 /*{{{int do_rmdir(char *name)*/
2035 Perl_do_rmdir(pTHX_ const char *name)
2041 dirfile = PerlMem_malloc(VMS_MAXRSS + 1);
2042 if (dirfile == NULL)
2043 _ckvmssts(SS$_INSFMEM);
2045 /* Force to a directory specification */
2046 if (do_fileify_dirspec(name, dirfile, 0, NULL) == NULL) {
2047 PerlMem_free(dirfile);
2050 if (Perl_flex_lstat(aTHX_ dirfile, &st) || !S_ISDIR(st.st_mode)) {
2055 retval = mp_do_kill_file(aTHX_ dirfile, 1);
2057 PerlMem_free(dirfile);
2060 } /* end of do_rmdir */
2064 * Delete any file to which user has control access, regardless of whether
2065 * delete access is explicitly allowed.
2066 * Limitations: User must have write access to parent directory.
2067 * Does not block signals or ASTs; if interrupted in midstream
2068 * may leave file with an altered ACL.
2071 /*{{{int kill_file(char *name)*/
2073 Perl_kill_file(pTHX_ const char *name)
2075 char rspec[NAM$C_MAXRSS+1];
2080 /* Remove() is allowed to delete directories, according to the X/Open
2082 * This may need special handling to work with the ACL hacks.
2084 if ((flex_lstat(name, &st) == 0) && S_ISDIR(st.st_mode)) {
2085 rmsts = Perl_do_rmdir(aTHX_ name);
2089 rmsts = mp_do_kill_file(aTHX_ name, 0);
2093 } /* end of kill_file() */
2097 /*{{{int my_mkdir(char *,Mode_t)*/
2099 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2101 STRLEN dirlen = strlen(dir);
2103 /* zero length string sometimes gives ACCVIO */
2104 if (dirlen == 0) return -1;
2106 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2107 * null file name/type. However, it's commonplace under Unix,
2108 * so we'll allow it for a gain in portability.
2110 if (dir[dirlen-1] == '/') {
2111 char *newdir = savepvn(dir,dirlen-1);
2112 int ret = mkdir(newdir,mode);
2116 else return mkdir(dir,mode);
2117 } /* end of my_mkdir */
2120 /*{{{int my_chdir(char *)*/
2122 Perl_my_chdir(pTHX_ const char *dir)
2124 STRLEN dirlen = strlen(dir);
2126 /* zero length string sometimes gives ACCVIO */
2127 if (dirlen == 0) return -1;
2130 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2131 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2132 * so that existing scripts do not need to be changed.
2135 while ((dirlen > 0) && (*dir1 == ' ')) {
2140 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2142 * null file name/type. However, it's commonplace under Unix,
2143 * so we'll allow it for a gain in portability.
2145 * - Preview- '/' will be valid soon on VMS
2147 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2148 char *newdir = savepvn(dir1,dirlen-1);
2149 int ret = chdir(newdir);
2153 else return chdir(dir1);
2154 } /* end of my_chdir */
2158 /*{{{int my_chmod(char *, mode_t)*/
2160 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2162 STRLEN speclen = strlen(file_spec);
2164 /* zero length string sometimes gives ACCVIO */
2165 if (speclen == 0) return -1;
2167 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2168 * that implies null file name/type. However, it's commonplace under Unix,
2169 * so we'll allow it for a gain in portability.
2171 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2172 * in VMS file.dir notation.
2174 if ((speclen > 1) && (file_spec[speclen-1] == '/')) {
2175 char *vms_src, *vms_dir, *rslt;
2179 /* First convert this to a VMS format specification */
2180 vms_src = PerlMem_malloc(VMS_MAXRSS);
2181 if (vms_src == NULL)
2182 _ckvmssts_noperl(SS$_INSFMEM);
2184 rslt = do_tovmsspec(file_spec, vms_src, 0, NULL);
2186 /* If we fail, then not a file specification */
2187 PerlMem_free(vms_src);
2192 /* Now make it a directory spec so chmod is happy */
2193 vms_dir = PerlMem_malloc(VMS_MAXRSS + 1);
2194 if (vms_dir == NULL)
2195 _ckvmssts_noperl(SS$_INSFMEM);
2196 rslt = do_fileify_dirspec(vms_src, vms_dir, 0, NULL);
2197 PerlMem_free(vms_src);
2201 ret = chmod(vms_dir, mode);
2205 PerlMem_free(vms_dir);
2208 else return chmod(file_spec, mode);
2209 } /* end of my_chmod */
2213 /*{{{FILE *my_tmpfile()*/
2220 if ((fp = tmpfile())) return fp;
2222 cp = PerlMem_malloc(L_tmpnam+24);
2223 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2225 if (decc_filename_unix_only == 0)
2226 strcpy(cp,"Sys$Scratch:");
2229 tmpnam(cp+strlen(cp));
2230 strcat(cp,".Perltmp");
2231 fp = fopen(cp,"w+","fop=dlt");
2238 #ifndef HOMEGROWN_POSIX_SIGNALS
2240 * The C RTL's sigaction fails to check for invalid signal numbers so we
2241 * help it out a bit. The docs are correct, but the actual routine doesn't
2242 * do what the docs say it will.
2244 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2246 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2247 struct sigaction* oact)
2249 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2250 SETERRNO(EINVAL, SS$_INVARG);
2253 return sigaction(sig, act, oact);
2258 #ifdef KILL_BY_SIGPRC
2259 #include <errnodef.h>
2261 /* We implement our own kill() using the undocumented system service
2262 sys$sigprc for one of two reasons:
2264 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2265 target process to do a sys$exit, which usually can't be handled
2266 gracefully...certainly not by Perl and the %SIG{} mechanism.
2268 2.) If the kill() in the CRTL can't be called from a signal
2269 handler without disappearing into the ether, i.e., the signal
2270 it purportedly sends is never trapped. Still true as of VMS 7.3.
2272 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2273 in the target process rather than calling sys$exit.
2275 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2276 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2277 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2278 with condition codes C$_SIG0+nsig*8, catching the exception on the
2279 target process and resignaling with appropriate arguments.
2281 But we don't have that VMS 7.0+ exception handler, so if you
2282 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2284 Also note that SIGTERM is listed in the docs as being "unimplemented",
2285 yet always seems to be signaled with a VMS condition code of 4 (and
2286 correctly handled for that code). So we hardwire it in.
2288 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2289 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2290 than signalling with an unrecognized (and unhandled by CRTL) code.
2293 #define _MY_SIG_MAX 28
2296 Perl_sig_to_vmscondition_int(int sig)
2298 static unsigned int sig_code[_MY_SIG_MAX+1] =
2301 SS$_HANGUP, /* 1 SIGHUP */
2302 SS$_CONTROLC, /* 2 SIGINT */
2303 SS$_CONTROLY, /* 3 SIGQUIT */
2304 SS$_RADRMOD, /* 4 SIGILL */
2305 SS$_BREAK, /* 5 SIGTRAP */
2306 SS$_OPCCUS, /* 6 SIGABRT */
2307 SS$_COMPAT, /* 7 SIGEMT */
2309 SS$_FLTOVF, /* 8 SIGFPE VAX */
2311 SS$_HPARITH, /* 8 SIGFPE AXP */
2313 SS$_ABORT, /* 9 SIGKILL */
2314 SS$_ACCVIO, /* 10 SIGBUS */
2315 SS$_ACCVIO, /* 11 SIGSEGV */
2316 SS$_BADPARAM, /* 12 SIGSYS */
2317 SS$_NOMBX, /* 13 SIGPIPE */
2318 SS$_ASTFLT, /* 14 SIGALRM */
2335 #if __VMS_VER >= 60200000
2336 static int initted = 0;
2339 sig_code[16] = C$_SIGUSR1;
2340 sig_code[17] = C$_SIGUSR2;
2341 #if __CRTL_VER >= 70000000
2342 sig_code[20] = C$_SIGCHLD;
2344 #if __CRTL_VER >= 70300000
2345 sig_code[28] = C$_SIGWINCH;
2350 if (sig < _SIG_MIN) return 0;
2351 if (sig > _MY_SIG_MAX) return 0;
2352 return sig_code[sig];
2356 Perl_sig_to_vmscondition(int sig)
2359 if (vms_debug_on_exception != 0)
2360 lib$signal(SS$_DEBUG);
2362 return Perl_sig_to_vmscondition_int(sig);
2367 Perl_my_kill(int pid, int sig)
2372 int sys$sigprc(unsigned int *pidadr,
2373 struct dsc$descriptor_s *prcname,
2376 /* sig 0 means validate the PID */
2377 /*------------------------------*/
2379 const unsigned long int jpicode = JPI$_PID;
2382 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2383 if ($VMS_STATUS_SUCCESS(status))
2386 case SS$_NOSUCHNODE:
2387 case SS$_UNREACHABLE:
2401 code = Perl_sig_to_vmscondition_int(sig);
2404 SETERRNO(EINVAL, SS$_BADPARAM);
2408 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2409 * signals are to be sent to multiple processes.
2410 * pid = 0 - all processes in group except ones that the system exempts
2411 * pid = -1 - all processes except ones that the system exempts
2412 * pid = -n - all processes in group (abs(n)) except ...
2413 * For now, just report as not supported.
2417 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2421 iss = sys$sigprc((unsigned int *)&pid,0,code);
2422 if (iss&1) return 0;
2426 set_errno(EPERM); break;
2428 case SS$_NOSUCHNODE:
2429 case SS$_UNREACHABLE:
2430 set_errno(ESRCH); break;
2432 set_errno(ENOMEM); break;
2434 _ckvmssts_noperl(iss);
2437 set_vaxc_errno(iss);
2443 /* Routine to convert a VMS status code to a UNIX status code.
2444 ** More tricky than it appears because of conflicting conventions with
2447 ** VMS status codes are a bit mask, with the least significant bit set for
2450 ** Special UNIX status of EVMSERR indicates that no translation is currently
2451 ** available, and programs should check the VMS status code.
2453 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2457 #ifndef C_FACILITY_NO
2458 #define C_FACILITY_NO 0x350000
2461 #define DCL_IVVERB 0x38090
2464 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2472 /* Assume the best or the worst */
2473 if (vms_status & STS$M_SUCCESS)
2476 unix_status = EVMSERR;
2478 msg_status = vms_status & ~STS$M_CONTROL;
2480 facility = vms_status & STS$M_FAC_NO;
2481 fac_sp = vms_status & STS$M_FAC_SP;
2482 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2484 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2490 unix_status = EFAULT;
2492 case SS$_DEVOFFLINE:
2493 unix_status = EBUSY;
2496 unix_status = ENOTCONN;
2504 case SS$_INVFILFOROP:
2508 unix_status = EINVAL;
2510 case SS$_UNSUPPORTED:
2511 unix_status = ENOTSUP;
2516 unix_status = EACCES;
2518 case SS$_DEVICEFULL:
2519 unix_status = ENOSPC;
2522 unix_status = ENODEV;
2524 case SS$_NOSUCHFILE:
2525 case SS$_NOSUCHOBJECT:
2526 unix_status = ENOENT;
2528 case SS$_ABORT: /* Fatal case */
2529 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2530 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2531 unix_status = EINTR;
2534 unix_status = E2BIG;
2537 unix_status = ENOMEM;
2540 unix_status = EPERM;
2542 case SS$_NOSUCHNODE:
2543 case SS$_UNREACHABLE:
2544 unix_status = ESRCH;
2547 unix_status = ECHILD;
2550 if ((facility == 0) && (msg_no < 8)) {
2551 /* These are not real VMS status codes so assume that they are
2552 ** already UNIX status codes
2554 unix_status = msg_no;
2560 /* Translate a POSIX exit code to a UNIX exit code */
2561 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2562 unix_status = (msg_no & 0x07F8) >> 3;
2566 /* Documented traditional behavior for handling VMS child exits */
2567 /*--------------------------------------------------------------*/
2568 if (child_flag != 0) {
2570 /* Success / Informational return 0 */
2571 /*----------------------------------*/
2572 if (msg_no & STS$K_SUCCESS)
2575 /* Warning returns 1 */
2576 /*-------------------*/
2577 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2580 /* Everything else pass through the severity bits */
2581 /*------------------------------------------------*/
2582 return (msg_no & STS$M_SEVERITY);
2585 /* Normal VMS status to ERRNO mapping attempt */
2586 /*--------------------------------------------*/
2587 switch(msg_status) {
2588 /* case RMS$_EOF: */ /* End of File */
2589 case RMS$_FNF: /* File Not Found */
2590 case RMS$_DNF: /* Dir Not Found */
2591 unix_status = ENOENT;
2593 case RMS$_RNF: /* Record Not Found */
2594 unix_status = ESRCH;
2597 unix_status = ENOTDIR;
2600 unix_status = ENODEV;
2605 unix_status = EBADF;
2608 unix_status = EEXIST;
2612 case LIB$_INVSTRDES:
2614 case LIB$_NOSUCHSYM:
2615 case LIB$_INVSYMNAM:
2617 unix_status = EINVAL;
2623 unix_status = E2BIG;
2625 case RMS$_PRV: /* No privilege */
2626 case RMS$_ACC: /* ACP file access failed */
2627 case RMS$_WLK: /* Device write locked */
2628 unix_status = EACCES;
2630 case RMS$_MKD: /* Failed to mark for delete */
2631 unix_status = EPERM;
2633 /* case RMS$_NMF: */ /* No more files */
2641 /* Try to guess at what VMS error status should go with a UNIX errno
2642 * value. This is hard to do as there could be many possible VMS
2643 * error statuses that caused the errno value to be set.
2646 int Perl_unix_status_to_vms(int unix_status)
2648 int test_unix_status;
2650 /* Trivial cases first */
2651 /*---------------------*/
2652 if (unix_status == EVMSERR)
2655 /* Is vaxc$errno sane? */
2656 /*---------------------*/
2657 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2658 if (test_unix_status == unix_status)
2661 /* If way out of range, must be VMS code already */
2662 /*-----------------------------------------------*/
2663 if (unix_status > EVMSERR)
2666 /* If out of range, punt */
2667 /*-----------------------*/
2668 if (unix_status > __ERRNO_MAX)
2672 /* Ok, now we have to do it the hard way. */
2673 /*----------------------------------------*/
2674 switch(unix_status) {
2675 case 0: return SS$_NORMAL;
2676 case EPERM: return SS$_NOPRIV;
2677 case ENOENT: return SS$_NOSUCHOBJECT;
2678 case ESRCH: return SS$_UNREACHABLE;
2679 case EINTR: return SS$_ABORT;
2682 case E2BIG: return SS$_BUFFEROVF;
2684 case EBADF: return RMS$_IFI;
2685 case ECHILD: return SS$_NONEXPR;
2687 case ENOMEM: return SS$_INSFMEM;
2688 case EACCES: return SS$_FILACCERR;
2689 case EFAULT: return SS$_ACCVIO;
2691 case EBUSY: return SS$_DEVOFFLINE;
2692 case EEXIST: return RMS$_FEX;
2694 case ENODEV: return SS$_NOSUCHDEV;
2695 case ENOTDIR: return RMS$_DIR;
2697 case EINVAL: return SS$_INVARG;
2703 case ENOSPC: return SS$_DEVICEFULL;
2704 case ESPIPE: return LIB$_INVARG;
2709 case ERANGE: return LIB$_INVARG;
2710 /* case EWOULDBLOCK */
2711 /* case EINPROGRESS */
2714 /* case EDESTADDRREQ */
2716 /* case EPROTOTYPE */
2717 /* case ENOPROTOOPT */
2718 /* case EPROTONOSUPPORT */
2719 /* case ESOCKTNOSUPPORT */
2720 /* case EOPNOTSUPP */
2721 /* case EPFNOSUPPORT */
2722 /* case EAFNOSUPPORT */
2723 /* case EADDRINUSE */
2724 /* case EADDRNOTAVAIL */
2726 /* case ENETUNREACH */
2727 /* case ENETRESET */
2728 /* case ECONNABORTED */
2729 /* case ECONNRESET */
2732 case ENOTCONN: return SS$_CLEARED;
2733 /* case ESHUTDOWN */
2734 /* case ETOOMANYREFS */
2735 /* case ETIMEDOUT */
2736 /* case ECONNREFUSED */
2738 /* case ENAMETOOLONG */
2739 /* case EHOSTDOWN */
2740 /* case EHOSTUNREACH */
2741 /* case ENOTEMPTY */
2753 /* case ECANCELED */
2757 return SS$_UNSUPPORTED;
2763 /* case EABANDONED */
2765 return SS$_ABORT; /* punt */
2768 return SS$_ABORT; /* Should not get here */
2772 /* default piping mailbox size */
2773 #define PERL_BUFSIZ 512
2777 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2779 unsigned long int mbxbufsiz;
2780 static unsigned long int syssize = 0;
2781 unsigned long int dviitm = DVI$_DEVNAM;
2782 char csize[LNM$C_NAMLENGTH+1];
2786 unsigned long syiitm = SYI$_MAXBUF;
2788 * Get the SYSGEN parameter MAXBUF
2790 * If the logical 'PERL_MBX_SIZE' is defined
2791 * use the value of the logical instead of PERL_BUFSIZ, but
2792 * keep the size between 128 and MAXBUF.
2795 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2798 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2799 mbxbufsiz = atoi(csize);
2801 mbxbufsiz = PERL_BUFSIZ;
2803 if (mbxbufsiz < 128) mbxbufsiz = 128;
2804 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2806 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2808 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2809 _ckvmssts_noperl(sts);
2810 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2812 } /* end of create_mbx() */
2815 /*{{{ my_popen and my_pclose*/
2817 typedef struct _iosb IOSB;
2818 typedef struct _iosb* pIOSB;
2819 typedef struct _pipe Pipe;
2820 typedef struct _pipe* pPipe;
2821 typedef struct pipe_details Info;
2822 typedef struct pipe_details* pInfo;
2823 typedef struct _srqp RQE;
2824 typedef struct _srqp* pRQE;
2825 typedef struct _tochildbuf CBuf;
2826 typedef struct _tochildbuf* pCBuf;
2829 unsigned short status;
2830 unsigned short count;
2831 unsigned long dvispec;
2834 #pragma member_alignment save
2835 #pragma nomember_alignment quadword
2836 struct _srqp { /* VMS self-relative queue entry */
2837 unsigned long qptr[2];
2839 #pragma member_alignment restore
2840 static RQE RQE_ZERO = {0,0};
2842 struct _tochildbuf {
2845 unsigned short size;
2853 unsigned short chan_in;
2854 unsigned short chan_out;
2856 unsigned int bufsize;
2868 #if defined(PERL_IMPLICIT_CONTEXT)
2869 void *thx; /* Either a thread or an interpreter */
2870 /* pointer, depending on how we're built */
2878 PerlIO *fp; /* file pointer to pipe mailbox */
2879 int useFILE; /* using stdio, not perlio */
2880 int pid; /* PID of subprocess */
2881 int mode; /* == 'r' if pipe open for reading */
2882 int done; /* subprocess has completed */
2883 int waiting; /* waiting for completion/closure */
2884 int closing; /* my_pclose is closing this pipe */
2885 unsigned long completion; /* termination status of subprocess */
2886 pPipe in; /* pipe in to sub */
2887 pPipe out; /* pipe out of sub */
2888 pPipe err; /* pipe of sub's sys$error */
2889 int in_done; /* true when in pipe finished */
2892 unsigned short xchan; /* channel to debug xterm */
2893 unsigned short xchan_valid; /* channel is assigned */
2896 struct exit_control_block
2898 struct exit_control_block *flink;
2899 unsigned long int (*exit_routine)();
2900 unsigned long int arg_count;
2901 unsigned long int *status_address;
2902 unsigned long int exit_status;
2905 typedef struct _closed_pipes Xpipe;
2906 typedef struct _closed_pipes* pXpipe;
2908 struct _closed_pipes {
2909 int pid; /* PID of subprocess */
2910 unsigned long completion; /* termination status of subprocess */
2912 #define NKEEPCLOSED 50
2913 static Xpipe closed_list[NKEEPCLOSED];
2914 static int closed_index = 0;
2915 static int closed_num = 0;
2917 #define RETRY_DELAY "0 ::0.20"
2918 #define MAX_RETRY 50
2920 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2921 static unsigned long mypid;
2922 static unsigned long delaytime[2];
2924 static pInfo open_pipes = NULL;
2925 static $DESCRIPTOR(nl_desc, "NL:");
2927 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2931 static unsigned long int
2935 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2936 int sts, did_stuff, need_eof, j;
2939 * Flush any pending i/o, but since we are in process run-down, be
2940 * careful about referencing PerlIO structures that may already have
2941 * been deallocated. We may not even have an interpreter anymore.
2946 #if defined(PERL_IMPLICIT_CONTEXT)
2947 /* We need to use the Perl context of the thread that created */
2951 aTHX = info->err->thx;
2953 aTHX = info->out->thx;
2955 aTHX = info->in->thx;
2958 #if defined(USE_ITHREADS)
2961 && PL_perlio_fd_refcnt)
2962 PerlIO_flush(info->fp);
2964 fflush((FILE *)info->fp);
2970 next we try sending an EOF...ignore if doesn't work, make sure we
2978 _ckvmssts_noperl(sys$setast(0));
2979 if (info->in && !info->in->shut_on_empty) {
2980 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
2985 _ckvmssts_noperl(sys$setast(1));
2989 /* wait for EOF to have effect, up to ~ 30 sec [default] */
2991 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
2996 _ckvmssts_noperl(sys$setast(0));
2997 if (info->waiting && info->done)
2999 nwait += info->waiting;
3000 _ckvmssts_noperl(sys$setast(1));
3010 _ckvmssts_noperl(sys$setast(0));
3011 if (!info->done) { /* Tap them gently on the shoulder . . .*/
3012 sts = sys$forcex(&info->pid,0,&abort);
3013 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3016 _ckvmssts_noperl(sys$setast(1));
3020 /* again, wait for effect */
3022 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3027 _ckvmssts_noperl(sys$setast(0));
3028 if (info->waiting && info->done)
3030 nwait += info->waiting;
3031 _ckvmssts_noperl(sys$setast(1));
3040 _ckvmssts_noperl(sys$setast(0));
3041 if (!info->done) { /* We tried to be nice . . . */
3042 sts = sys$delprc(&info->pid,0);
3043 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3044 info->done = 1; /* sys$delprc is as done as we're going to get. */
3046 _ckvmssts_noperl(sys$setast(1));
3052 #if defined(PERL_IMPLICIT_CONTEXT)
3053 /* We need to use the Perl context of the thread that created */
3056 if (open_pipes->err)
3057 aTHX = open_pipes->err->thx;
3058 else if (open_pipes->out)
3059 aTHX = open_pipes->out->thx;
3060 else if (open_pipes->in)
3061 aTHX = open_pipes->in->thx;
3063 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3064 else if (!(sts & 1)) retsts = sts;
3069 static struct exit_control_block pipe_exitblock =
3070 {(struct exit_control_block *) 0,
3071 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3073 static void pipe_mbxtofd_ast(pPipe p);
3074 static void pipe_tochild1_ast(pPipe p);
3075 static void pipe_tochild2_ast(pPipe p);
3078 popen_completion_ast(pInfo info)
3080 pInfo i = open_pipes;
3085 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3086 closed_list[closed_index].pid = info->pid;
3087 closed_list[closed_index].completion = info->completion;
3089 if (closed_index == NKEEPCLOSED)
3094 if (i == info) break;
3097 if (!i) return; /* unlinked, probably freed too */
3102 Writing to subprocess ...
3103 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3105 chan_out may be waiting for "done" flag, or hung waiting
3106 for i/o completion to child...cancel the i/o. This will
3107 put it into "snarf mode" (done but no EOF yet) that discards
3110 Output from subprocess (stdout, stderr) needs to be flushed and
3111 shut down. We try sending an EOF, but if the mbx is full the pipe
3112 routine should still catch the "shut_on_empty" flag, telling it to
3113 use immediate-style reads so that "mbx empty" -> EOF.
3117 if (info->in && !info->in_done) { /* only for mode=w */
3118 if (info->in->shut_on_empty && info->in->need_wake) {
3119 info->in->need_wake = FALSE;
3120 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3122 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3126 if (info->out && !info->out_done) { /* were we also piping output? */
3127 info->out->shut_on_empty = TRUE;
3128 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3129 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3130 _ckvmssts_noperl(iss);
3133 if (info->err && !info->err_done) { /* we were piping stderr */
3134 info->err->shut_on_empty = TRUE;
3135 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3136 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3137 _ckvmssts_noperl(iss);
3139 _ckvmssts_noperl(sys$setef(pipe_ef));
3143 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3144 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3147 we actually differ from vmstrnenv since we use this to
3148 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
3149 are pointing to the same thing
3152 static unsigned short
3153 popen_translate(pTHX_ char *logical, char *result)
3156 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
3157 $DESCRIPTOR(d_log,"");
3159 unsigned short length;
3160 unsigned short code;
3162 unsigned short *retlenaddr;
3164 unsigned short l, ifi;
3166 d_log.dsc$a_pointer = logical;
3167 d_log.dsc$w_length = strlen(logical);
3169 itmlst[0].code = LNM$_STRING;
3170 itmlst[0].length = 255;
3171 itmlst[0].buffer_addr = result;
3172 itmlst[0].retlenaddr = &l;
3175 itmlst[1].length = 0;
3176 itmlst[1].buffer_addr = 0;
3177 itmlst[1].retlenaddr = 0;
3179 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3180 if (iss == SS$_NOLOGNAM) {
3184 if (!(iss&1)) lib$signal(iss);
3187 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
3188 strip it off and return the ifi, if any
3191 if (result[0] == 0x1b && result[1] == 0x00) {
3192 memmove(&ifi,result+2,2);
3193 strcpy(result,result+4);
3195 return ifi; /* this is the RMS internal file id */
3198 static void pipe_infromchild_ast(pPipe p);
3201 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3202 inside an AST routine without worrying about reentrancy and which Perl
3203 memory allocator is being used.
3205 We read data and queue up the buffers, then spit them out one at a
3206 time to the output mailbox when the output mailbox is ready for one.
3209 #define INITIAL_TOCHILDQUEUE 2
3212 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3216 char mbx1[64], mbx2[64];
3217 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3218 DSC$K_CLASS_S, mbx1},
3219 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3220 DSC$K_CLASS_S, mbx2};
3221 unsigned int dviitm = DVI$_DEVBUFSIZ;
3225 _ckvmssts_noperl(lib$get_vm(&n, &p));
3227 create_mbx(&p->chan_in , &d_mbx1);
3228 create_mbx(&p->chan_out, &d_mbx2);
3229 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3232 p->shut_on_empty = FALSE;
3233 p->need_wake = FALSE;
3236 p->iosb.status = SS$_NORMAL;
3237 p->iosb2.status = SS$_NORMAL;
3243 #ifdef PERL_IMPLICIT_CONTEXT
3247 n = sizeof(CBuf) + p->bufsize;
3249 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3250 _ckvmssts_noperl(lib$get_vm(&n, &b));
3251 b->buf = (char *) b + sizeof(CBuf);
3252 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3255 pipe_tochild2_ast(p);
3256 pipe_tochild1_ast(p);
3262 /* reads the MBX Perl is writing, and queues */
3265 pipe_tochild1_ast(pPipe p)
3268 int iss = p->iosb.status;
3269 int eof = (iss == SS$_ENDOFFILE);
3271 #ifdef PERL_IMPLICIT_CONTEXT
3277 p->shut_on_empty = TRUE;
3279 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3281 _ckvmssts_noperl(iss);
3285 b->size = p->iosb.count;
3286 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3288 p->need_wake = FALSE;
3289 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3292 p->retry = 1; /* initial call */
3295 if (eof) { /* flush the free queue, return when done */
3296 int n = sizeof(CBuf) + p->bufsize;
3298 iss = lib$remqti(&p->free, &b);
3299 if (iss == LIB$_QUEWASEMP) return;
3300 _ckvmssts_noperl(iss);
3301 _ckvmssts_noperl(lib$free_vm(&n, &b));
3305 iss = lib$remqti(&p->free, &b);
3306 if (iss == LIB$_QUEWASEMP) {
3307 int n = sizeof(CBuf) + p->bufsize;
3308 _ckvmssts_noperl(lib$get_vm(&n, &b));
3309 b->buf = (char *) b + sizeof(CBuf);
3311 _ckvmssts_noperl(iss);
3315 iss = sys$qio(0,p->chan_in,
3316 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3318 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3319 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3320 _ckvmssts_noperl(iss);
3324 /* writes queued buffers to output, waits for each to complete before
3328 pipe_tochild2_ast(pPipe p)
3331 int iss = p->iosb2.status;
3332 int n = sizeof(CBuf) + p->bufsize;
3333 int done = (p->info && p->info->done) ||
3334 iss == SS$_CANCEL || iss == SS$_ABORT;
3335 #if defined(PERL_IMPLICIT_CONTEXT)
3340 if (p->type) { /* type=1 has old buffer, dispose */
3341 if (p->shut_on_empty) {
3342 _ckvmssts_noperl(lib$free_vm(&n, &b));
3344 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3349 iss = lib$remqti(&p->wait, &b);
3350 if (iss == LIB$_QUEWASEMP) {
3351 if (p->shut_on_empty) {
3353 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3354 *p->pipe_done = TRUE;
3355 _ckvmssts_noperl(sys$setef(pipe_ef));
3357 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3358 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3362 p->need_wake = TRUE;
3365 _ckvmssts_noperl(iss);
3372 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3373 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3375 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3376 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3385 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3388 char mbx1[64], mbx2[64];
3389 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3390 DSC$K_CLASS_S, mbx1},
3391 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3392 DSC$K_CLASS_S, mbx2};
3393 unsigned int dviitm = DVI$_DEVBUFSIZ;
3395 int n = sizeof(Pipe);
3396 _ckvmssts_noperl(lib$get_vm(&n, &p));
3397 create_mbx(&p->chan_in , &d_mbx1);
3398 create_mbx(&p->chan_out, &d_mbx2);
3400 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3401 n = p->bufsize * sizeof(char);
3402 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3403 p->shut_on_empty = FALSE;
3406 p->iosb.status = SS$_NORMAL;
3407 #if defined(PERL_IMPLICIT_CONTEXT)
3410 pipe_infromchild_ast(p);
3418 pipe_infromchild_ast(pPipe p)
3420 int iss = p->iosb.status;
3421 int eof = (iss == SS$_ENDOFFILE);
3422 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3423 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3424 #if defined(PERL_IMPLICIT_CONTEXT)
3428 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3429 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3434 input shutdown if EOF from self (done or shut_on_empty)
3435 output shutdown if closing flag set (my_pclose)
3436 send data/eof from child or eof from self
3437 otherwise, re-read (snarf of data from child)
3442 if (myeof && p->chan_in) { /* input shutdown */
3443 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3448 if (myeof || kideof) { /* pass EOF to parent */
3449 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3450 pipe_infromchild_ast, p,
3453 } else if (eof) { /* eat EOF --- fall through to read*/
3455 } else { /* transmit data */
3456 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3457 pipe_infromchild_ast,p,
3458 p->buf, p->iosb.count, 0, 0, 0, 0));
3464 /* everything shut? flag as done */
3466 if (!p->chan_in && !p->chan_out) {
3467 *p->pipe_done = TRUE;
3468 _ckvmssts_noperl(sys$setef(pipe_ef));
3472 /* write completed (or read, if snarfing from child)
3473 if still have input active,
3474 queue read...immediate mode if shut_on_empty so we get EOF if empty
3476 check if Perl reading, generate EOFs as needed
3482 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3483 pipe_infromchild_ast,p,
3484 p->buf, p->bufsize, 0, 0, 0, 0);
3485 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3486 _ckvmssts_noperl(iss);
3487 } else { /* send EOFs for extra reads */
3488 p->iosb.status = SS$_ENDOFFILE;
3489 p->iosb.dvispec = 0;
3490 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3492 pipe_infromchild_ast, p, 0, 0, 0, 0));
3498 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3502 unsigned long dviitm = DVI$_DEVBUFSIZ;
3504 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3505 DSC$K_CLASS_S, mbx};
3506 int n = sizeof(Pipe);
3508 /* things like terminals and mbx's don't need this filter */
3509 if (fd && fstat(fd,&s) == 0) {
3510 unsigned long dviitm = DVI$_DEVCHAR, devchar;
3512 unsigned short dev_len;
3513 struct dsc$descriptor_s d_dev;
3515 struct item_list_3 items[3];
3517 unsigned short dvi_iosb[4];
3519 cptr = getname(fd, out, 1);
3520 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3521 d_dev.dsc$a_pointer = out;
3522 d_dev.dsc$w_length = strlen(out);
3523 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3524 d_dev.dsc$b_class = DSC$K_CLASS_S;
3527 items[0].code = DVI$_DEVCHAR;
3528 items[0].bufadr = &devchar;
3529 items[0].retadr = NULL;
3531 items[1].code = DVI$_FULLDEVNAM;
3532 items[1].bufadr = device;
3533 items[1].retadr = &dev_len;
3537 status = sys$getdviw
3538 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3539 _ckvmssts_noperl(status);
3540 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3541 device[dev_len] = 0;
3543 if (!(devchar & DEV$M_DIR)) {
3544 strcpy(out, device);
3550 _ckvmssts_noperl(lib$get_vm(&n, &p));
3551 p->fd_out = dup(fd);
3552 create_mbx(&p->chan_in, &d_mbx);
3553 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3554 n = (p->bufsize+1) * sizeof(char);
3555 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3556 p->shut_on_empty = FALSE;
3561 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3562 pipe_mbxtofd_ast, p,
3563 p->buf, p->bufsize, 0, 0, 0, 0));
3569 pipe_mbxtofd_ast(pPipe p)
3571 int iss = p->iosb.status;
3572 int done = p->info->done;
3574 int eof = (iss == SS$_ENDOFFILE);
3575 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3576 int err = !(iss&1) && !eof;
3577 #if defined(PERL_IMPLICIT_CONTEXT)
3581 if (done && myeof) { /* end piping */
3583 sys$dassgn(p->chan_in);
3584 *p->pipe_done = TRUE;
3585 _ckvmssts_noperl(sys$setef(pipe_ef));
3589 if (!err && !eof) { /* good data to send to file */
3590 p->buf[p->iosb.count] = '\n';
3591 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3594 if (p->retry < MAX_RETRY) {
3595 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3601 _ckvmssts_noperl(iss);
3605 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3606 pipe_mbxtofd_ast, p,
3607 p->buf, p->bufsize, 0, 0, 0, 0);
3608 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3609 _ckvmssts_noperl(iss);
3613 typedef struct _pipeloc PLOC;
3614 typedef struct _pipeloc* pPLOC;
3618 char dir[NAM$C_MAXRSS+1];
3620 static pPLOC head_PLOC = 0;
3623 free_pipelocs(pTHX_ void *head)
3626 pPLOC *pHead = (pPLOC *)head;
3638 store_pipelocs(pTHX)
3647 char temp[NAM$C_MAXRSS+1];
3651 free_pipelocs(aTHX_ &head_PLOC);
3653 /* the . directory from @INC comes last */
3655 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3656 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3657 p->next = head_PLOC;
3659 strcpy(p->dir,"./");
3661 /* get the directory from $^X */
3663 unixdir = PerlMem_malloc(VMS_MAXRSS);
3664 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3666 #ifdef PERL_IMPLICIT_CONTEXT
3667 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3669 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3671 strcpy(temp, PL_origargv[0]);
3672 x = strrchr(temp,']');
3674 x = strrchr(temp,'>');
3676 /* It could be a UNIX path */
3677 x = strrchr(temp,'/');
3683 /* Got a bare name, so use default directory */
3688 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3689 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3690 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3691 p->next = head_PLOC;
3693 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3694 p->dir[NAM$C_MAXRSS] = '\0';
3698 /* reverse order of @INC entries, skip "." since entered above */
3700 #ifdef PERL_IMPLICIT_CONTEXT
3703 if (PL_incgv) av = GvAVn(PL_incgv);
3705 for (i = 0; av && i <= AvFILL(av); i++) {
3706 dirsv = *av_fetch(av,i,TRUE);
3708 if (SvROK(dirsv)) continue;
3709 dir = SvPVx(dirsv,n_a);
3710 if (strcmp(dir,".") == 0) continue;
3711 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3714 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3715 p->next = head_PLOC;
3717 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3718 p->dir[NAM$C_MAXRSS] = '\0';
3721 /* most likely spot (ARCHLIB) put first in the list */
3724 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3725 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3726 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3727 p->next = head_PLOC;
3729 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3730 p->dir[NAM$C_MAXRSS] = '\0';
3733 PerlMem_free(unixdir);
3737 Perl_cando_by_name_int
3738 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3739 #if !defined(PERL_IMPLICIT_CONTEXT)
3740 #define cando_by_name_int Perl_cando_by_name_int
3742 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3748 static int vmspipe_file_status = 0;
3749 static char vmspipe_file[NAM$C_MAXRSS+1];
3751 /* already found? Check and use ... need read+execute permission */
3753 if (vmspipe_file_status == 1) {
3754 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3755 && cando_by_name_int
3756 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3757 return vmspipe_file;
3759 vmspipe_file_status = 0;
3762 /* scan through stored @INC, $^X */
3764 if (vmspipe_file_status == 0) {
3765 char file[NAM$C_MAXRSS+1];
3766 pPLOC p = head_PLOC;
3771 strcpy(file, p->dir);
3772 dirlen = strlen(file);
3773 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
3774 file[NAM$C_MAXRSS] = '\0';
3777 exp_res = do_rmsexpand
3778 (file, vmspipe_file, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL);
3779 if (!exp_res) continue;
3781 if (cando_by_name_int
3782 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3783 && cando_by_name_int
3784 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3785 vmspipe_file_status = 1;
3786 return vmspipe_file;
3789 vmspipe_file_status = -1; /* failed, use tempfiles */
3796 vmspipe_tempfile(pTHX)
3798 char file[NAM$C_MAXRSS+1];
3800 static int index = 0;
3804 /* create a tempfile */
3806 /* we can't go from W, shr=get to R, shr=get without
3807 an intermediate vulnerable state, so don't bother trying...
3809 and lib$spawn doesn't shr=put, so have to close the write
3811 So... match up the creation date/time and the FID to
3812 make sure we're dealing with the same file
3817 if (!decc_filename_unix_only) {
3818 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3819 fp = fopen(file,"w");
3821 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3822 fp = fopen(file,"w");
3824 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3825 fp = fopen(file,"w");
3830 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3831 fp = fopen(file,"w");
3833 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3834 fp = fopen(file,"w");
3836 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3837 fp = fopen(file,"w");
3841 if (!fp) return 0; /* we're hosed */
3843 fprintf(fp,"$! 'f$verify(0)'\n");
3844 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3845 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3846 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3847 fprintf(fp,"$ perl_on = \"set noon\"\n");
3848 fprintf(fp,"$ perl_exit = \"exit\"\n");
3849 fprintf(fp,"$ perl_del = \"delete\"\n");
3850 fprintf(fp,"$ pif = \"if\"\n");
3851 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3852 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3853 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3854 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3855 fprintf(fp,"$! --- build command line to get max possible length\n");
3856 fprintf(fp,"$c=perl_popen_cmd0\n");
3857 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3858 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3859 fprintf(fp,"$x=perl_popen_cmd3\n");
3860 fprintf(fp,"$c=c+x\n");
3861 fprintf(fp,"$ perl_on\n");
3862 fprintf(fp,"$ 'c'\n");
3863 fprintf(fp,"$ perl_status = $STATUS\n");
3864 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3865 fprintf(fp,"$ perl_exit 'perl_status'\n");
3868 fgetname(fp, file, 1);
3869 fstat(fileno(fp), (struct stat *)&s0);
3872 if (decc_filename_unix_only)
3873 do_tounixspec(file, file, 0, NULL);
3874 fp = fopen(file,"r","shr=get");
3876 fstat(fileno(fp), (struct stat *)&s1);
3878 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3879 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3888 static int vms_is_syscommand_xterm(void)
3890 const static struct dsc$descriptor_s syscommand_dsc =
3891 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3893 const static struct dsc$descriptor_s decwdisplay_dsc =
3894 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3896 struct item_list_3 items[2];
3897 unsigned short dvi_iosb[4];
3898 unsigned long devchar;
3899 unsigned long devclass;
3902 /* Very simple check to guess if sys$command is a decterm? */
3903 /* First see if the DECW$DISPLAY: device exists */
3905 items[0].code = DVI$_DEVCHAR;
3906 items[0].bufadr = &devchar;
3907 items[0].retadr = NULL;
3911 status = sys$getdviw
3912 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3914 if ($VMS_STATUS_SUCCESS(status)) {
3915 status = dvi_iosb[0];
3918 if (!$VMS_STATUS_SUCCESS(status)) {
3919 SETERRNO(EVMSERR, status);
3923 /* If it does, then for now assume that we are on a workstation */
3924 /* Now verify that SYS$COMMAND is a terminal */
3925 /* for creating the debugger DECTerm */
3928 items[0].code = DVI$_DEVCLASS;
3929 items[0].bufadr = &devclass;
3930 items[0].retadr = NULL;
3934 status = sys$getdviw
3935 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3937 if ($VMS_STATUS_SUCCESS(status)) {
3938 status = dvi_iosb[0];
3941 if (!$VMS_STATUS_SUCCESS(status)) {
3942 SETERRNO(EVMSERR, status);
3946 if (devclass == DC$_TERM) {
3953 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3954 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3959 char device_name[65];
3960 unsigned short device_name_len;
3961 struct dsc$descriptor_s customization_dsc;
3962 struct dsc$descriptor_s device_name_dsc;
3965 char customization[200];
3969 unsigned short p_chan;
3971 unsigned short iosb[4];
3972 struct item_list_3 items[2];
3973 const char * cust_str =
3974 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
3975 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3976 DSC$K_CLASS_S, mbx1};
3978 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
3979 /*---------------------------------------*/
3980 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
3983 /* Make sure that this is from the Perl debugger */
3984 ret_char = strstr(cmd," xterm ");
3985 if (ret_char == NULL)
3987 cptr = ret_char + 7;
3988 ret_char = strstr(cmd,"tty");
3989 if (ret_char == NULL)
3991 ret_char = strstr(cmd,"sleep");
3992 if (ret_char == NULL)
3995 if (decw_term_port == 0) {
3996 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
3997 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
3998 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
4000 status = lib$find_image_symbol
4002 &decw_term_port_dsc,
4003 (void *)&decw_term_port,
4007 /* Try again with the other image name */
4008 if (!$VMS_STATUS_SUCCESS(status)) {
4010 status = lib$find_image_symbol
4012 &decw_term_port_dsc,
4013 (void *)&decw_term_port,
4022 /* No decw$term_port, give it up */
4023 if (!$VMS_STATUS_SUCCESS(status))
4026 /* Are we on a workstation? */
4027 /* to do: capture the rows / columns and pass their properties */
4028 ret_stat = vms_is_syscommand_xterm();
4032 /* Make the title: */
4033 ret_char = strstr(cptr,"-title");
4034 if (ret_char != NULL) {
4035 while ((*cptr != 0) && (*cptr != '\"')) {
4041 while ((*cptr != 0) && (*cptr != '\"')) {
4054 strcpy(title,"Perl Debug DECTerm");
4056 sprintf(customization, cust_str, title);
4058 customization_dsc.dsc$a_pointer = customization;
4059 customization_dsc.dsc$w_length = strlen(customization);
4060 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4061 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
4063 device_name_dsc.dsc$a_pointer = device_name;
4064 device_name_dsc.dsc$w_length = sizeof device_name -1;
4065 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4066 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
4068 device_name_len = 0;
4070 /* Try to create the window */
4071 status = (*decw_term_port)
4080 if (!$VMS_STATUS_SUCCESS(status)) {
4081 SETERRNO(EVMSERR, status);
4085 device_name[device_name_len] = '\0';
4087 /* Need to set this up to look like a pipe for cleanup */
4089 status = lib$get_vm(&n, &info);
4090 if (!$VMS_STATUS_SUCCESS(status)) {
4091 SETERRNO(ENOMEM, status);
4097 info->completion = 0;
4098 info->closing = FALSE;
4105 info->in_done = TRUE;
4106 info->out_done = TRUE;
4107 info->err_done = TRUE;
4109 /* Assign a channel on this so that it will persist, and not login */
4110 /* We stash this channel in the info structure for reference. */
4111 /* The created xterm self destructs when the last channel is removed */
4112 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4113 /* So leave this assigned. */
4114 device_name_dsc.dsc$w_length = device_name_len;
4115 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4116 if (!$VMS_STATUS_SUCCESS(status)) {
4117 SETERRNO(EVMSERR, status);
4120 info->xchan_valid = 1;
4122 /* Now create a mailbox to be read by the application */
4124 create_mbx(&p_chan, &d_mbx1);
4126 /* write the name of the created terminal to the mailbox */
4127 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4128 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4130 if (!$VMS_STATUS_SUCCESS(status)) {
4131 SETERRNO(EVMSERR, status);
4135 info->fp = PerlIO_open(mbx1, mode);
4137 /* Done with this channel */
4140 /* If any errors, then clean up */
4143 _ckvmssts_noperl(lib$free_vm(&n, &info));
4151 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4154 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4156 static int handler_set_up = FALSE;
4158 unsigned long int sts, flags = CLI$M_NOWAIT;
4159 /* The use of a GLOBAL table (as was done previously) rendered
4160 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4161 * environment. Hence we've switched to LOCAL symbol table.
4163 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4165 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4166 char *in, *out, *err, mbx[512];
4168 char tfilebuf[NAM$C_MAXRSS+1];
4170 char cmd_sym_name[20];
4171 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4172 DSC$K_CLASS_S, symbol};
4173 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4175 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4176 DSC$K_CLASS_S, cmd_sym_name};
4177 struct dsc$descriptor_s *vmscmd;
4178 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4179 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4180 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4182 /* Check here for Xterm create request. This means looking for
4183 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4184 * is possible to create an xterm.
4186 if (*in_mode == 'r') {
4189 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4190 if (xterm_fd != NULL)
4194 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4196 /* once-per-program initialization...
4197 note that the SETAST calls and the dual test of pipe_ef
4198 makes sure that only the FIRST thread through here does
4199 the initialization...all other threads wait until it's
4202 Yeah, uglier than a pthread call, it's got all the stuff inline
4203 rather than in a separate routine.
4207 _ckvmssts_noperl(sys$setast(0));
4209 unsigned long int pidcode = JPI$_PID;
4210 $DESCRIPTOR(d_delay, RETRY_DELAY);
4211 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4212 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4213 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4215 if (!handler_set_up) {
4216 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4217 handler_set_up = TRUE;
4219 _ckvmssts_noperl(sys$setast(1));
4222 /* see if we can find a VMSPIPE.COM */
4225 vmspipe = find_vmspipe(aTHX);
4227 strcpy(tfilebuf+1,vmspipe);
4228 } else { /* uh, oh...we're in tempfile hell */
4229 tpipe = vmspipe_tempfile(aTHX);
4230 if (!tpipe) { /* a fish popular in Boston */
4231 if (ckWARN(WARN_PIPE)) {
4232 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4236 fgetname(tpipe,tfilebuf+1,1);
4238 vmspipedsc.dsc$a_pointer = tfilebuf;
4239 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4241 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4244 case RMS$_FNF: case RMS$_DNF:
4245 set_errno(ENOENT); break;
4247 set_errno(ENOTDIR); break;
4249 set_errno(ENODEV); break;
4251 set_errno(EACCES); break;
4253 set_errno(EINVAL); break;
4254 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4255 set_errno(E2BIG); break;
4256 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4257 _ckvmssts_noperl(sts); /* fall through */
4258 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4261 set_vaxc_errno(sts);
4262 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4263 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4269 _ckvmssts_noperl(lib$get_vm(&n, &info));
4271 strcpy(mode,in_mode);
4274 info->completion = 0;
4275 info->closing = FALSE;
4282 info->in_done = TRUE;
4283 info->out_done = TRUE;
4284 info->err_done = TRUE;
4286 info->xchan_valid = 0;
4288 in = PerlMem_malloc(VMS_MAXRSS);
4289 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4290 out = PerlMem_malloc(VMS_MAXRSS);
4291 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4292 err = PerlMem_malloc(VMS_MAXRSS);
4293 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4295 in[0] = out[0] = err[0] = '\0';
4297 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4301 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4306 if (*mode == 'r') { /* piping from subroutine */
4308 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4310 info->out->pipe_done = &info->out_done;
4311 info->out_done = FALSE;
4312 info->out->info = info;
4314 if (!info->useFILE) {
4315 info->fp = PerlIO_open(mbx, mode);
4317 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4318 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4321 if (!info->fp && info->out) {
4322 sys$cancel(info->out->chan_out);
4324 while (!info->out_done) {
4326 _ckvmssts_noperl(sys$setast(0));
4327 done = info->out_done;
4328 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4329 _ckvmssts_noperl(sys$setast(1));
4330 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4333 if (info->out->buf) {
4334 n = info->out->bufsize * sizeof(char);
4335 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4338 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4340 _ckvmssts_noperl(lib$free_vm(&n, &info));
4345 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4347 info->err->pipe_done = &info->err_done;
4348 info->err_done = FALSE;
4349 info->err->info = info;
4352 } else if (*mode == 'w') { /* piping to subroutine */
4354 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4356 info->out->pipe_done = &info->out_done;
4357 info->out_done = FALSE;
4358 info->out->info = info;
4361 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4363 info->err->pipe_done = &info->err_done;
4364 info->err_done = FALSE;
4365 info->err->info = info;
4368 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4369 if (!info->useFILE) {
4370 info->fp = PerlIO_open(mbx, mode);
4372 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4373 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4377 info->in->pipe_done = &info->in_done;
4378 info->in_done = FALSE;
4379 info->in->info = info;
4383 if (!info->fp && info->in) {
4385 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4386 0, 0, 0, 0, 0, 0, 0, 0));
4388 while (!info->in_done) {
4390 _ckvmssts_noperl(sys$setast(0));
4391 done = info->in_done;
4392 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4393 _ckvmssts_noperl(sys$setast(1));
4394 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4397 if (info->in->buf) {
4398 n = info->in->bufsize * sizeof(char);
4399 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4402 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4404 _ckvmssts_noperl(lib$free_vm(&n, &info));
4410 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4411 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4413 info->out->pipe_done = &info->out_done;
4414 info->out_done = FALSE;
4415 info->out->info = info;
4418 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4420 info->err->pipe_done = &info->err_done;
4421 info->err_done = FALSE;
4422 info->err->info = info;
4426 symbol[MAX_DCL_SYMBOL] = '\0';
4428 strncpy(symbol, in, MAX_DCL_SYMBOL);
4429 d_symbol.dsc$w_length = strlen(symbol);
4430 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4432 strncpy(symbol, err, MAX_DCL_SYMBOL);
4433 d_symbol.dsc$w_length = strlen(symbol);
4434 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4436 strncpy(symbol, out, MAX_DCL_SYMBOL);
4437 d_symbol.dsc$w_length = strlen(symbol);
4438 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4440 /* Done with the names for the pipes */
4445 p = vmscmd->dsc$a_pointer;
4446 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4447 if (*p == '$') p++; /* remove leading $ */
4448 while (*p == ' ' || *p == '\t') p++;
4450 for (j = 0; j < 4; j++) {
4451 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4452 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4454 strncpy(symbol, p, MAX_DCL_SYMBOL);
4455 d_symbol.dsc$w_length = strlen(symbol);
4456 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4458 if (strlen(p) > MAX_DCL_SYMBOL) {
4459 p += MAX_DCL_SYMBOL;
4464 _ckvmssts_noperl(sys$setast(0));
4465 info->next=open_pipes; /* prepend to list */
4467 _ckvmssts_noperl(sys$setast(1));
4468 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4469 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4470 * have SYS$COMMAND if we need it.
4472 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4473 0, &info->pid, &info->completion,
4474 0, popen_completion_ast,info,0,0,0));
4476 /* if we were using a tempfile, close it now */
4478 if (tpipe) fclose(tpipe);
4480 /* once the subprocess is spawned, it has copied the symbols and
4481 we can get rid of ours */
4483 for (j = 0; j < 4; j++) {
4484 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4485 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4486 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4488 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4489 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4490 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4491 vms_execfree(vmscmd);
4493 #ifdef PERL_IMPLICIT_CONTEXT
4496 PL_forkprocess = info->pid;
4503 _ckvmssts_noperl(sys$setast(0));
4505 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4506 _ckvmssts_noperl(sys$setast(1));
4507 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4509 *psts = info->completion;
4510 /* Caller thinks it is open and tries to close it. */
4511 /* This causes some problems, as it changes the error status */
4512 /* my_pclose(info->fp); */
4514 /* If we did not have a file pointer open, then we have to */
4515 /* clean up here or eventually we will run out of something */
4517 if (info->fp == NULL) {
4518 my_pclose_pinfo(aTHX_ info);
4526 } /* end of safe_popen */
4529 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4531 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4535 TAINT_PROPER("popen");
4536 PERL_FLUSHALL_FOR_CHILD;
4537 return safe_popen(aTHX_ cmd,mode,&sts);
4543 /* Routine to close and cleanup a pipe info structure */
4545 static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4547 unsigned long int retsts;
4552 /* If we were writing to a subprocess, insure that someone reading from
4553 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4554 * produce an EOF record in the mailbox.
4556 * well, at least sometimes it *does*, so we have to watch out for
4557 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4561 #if defined(USE_ITHREADS)
4564 && PL_perlio_fd_refcnt)
4565 PerlIO_flush(info->fp);
4567 fflush((FILE *)info->fp);
4570 _ckvmssts(sys$setast(0));
4571 info->closing = TRUE;
4572 done = info->done && info->in_done && info->out_done && info->err_done;
4573 /* hanging on write to Perl's input? cancel it */
4574 if (info->mode == 'r' && info->out && !info->out_done) {
4575 if (info->out->chan_out) {
4576 _ckvmssts(sys$cancel(info->out->chan_out));
4577 if (!info->out->chan_in) { /* EOF generation, need AST */
4578 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4582 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4583 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4585 _ckvmssts(sys$setast(1));
4588 #if defined(USE_ITHREADS)
4591 && PL_perlio_fd_refcnt)
4592 PerlIO_close(info->fp);
4594 fclose((FILE *)info->fp);
4597 we have to wait until subprocess completes, but ALSO wait until all
4598 the i/o completes...otherwise we'll be freeing the "info" structure
4599 that the i/o ASTs could still be using...
4603 _ckvmssts(sys$setast(0));
4604 done = info->done && info->in_done && info->out_done && info->err_done;
4605 if (!done) _ckvmssts(sys$clref(pipe_ef));
4606 _ckvmssts(sys$setast(1));
4607 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4609 retsts = info->completion;
4611 /* remove from list of open pipes */
4612 _ckvmssts(sys$setast(0));
4614 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4620 last->next = info->next;
4622 open_pipes = info->next;
4623 _ckvmssts(sys$setast(1));
4625 /* free buffers and structures */
4628 if (info->in->buf) {
4629 n = info->in->bufsize * sizeof(char);
4630 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4633 _ckvmssts(lib$free_vm(&n, &info->in));
4636 if (info->out->buf) {
4637 n = info->out->bufsize * sizeof(char);
4638 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4641 _ckvmssts(lib$free_vm(&n, &info->out));
4644 if (info->err->buf) {
4645 n = info->err->bufsize * sizeof(char);
4646 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4649 _ckvmssts(lib$free_vm(&n, &info->err));
4652 _ckvmssts(lib$free_vm(&n, &info));
4658 /*{{{ I32 my_pclose(PerlIO *fp)*/
4659 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4661 pInfo info, last = NULL;
4664 /* Fixme - need ast and mutex protection here */
4665 for (info = open_pipes; info != NULL; last = info, info = info->next)
4666 if (info->fp == fp) break;
4668 if (info == NULL) { /* no such pipe open */
4669 set_errno(ECHILD); /* quoth POSIX */
4670 set_vaxc_errno(SS$_NONEXPR);
4674 ret_status = my_pclose_pinfo(aTHX_ info);
4678 } /* end of my_pclose() */
4680 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4681 /* Roll our own prototype because we want this regardless of whether
4682 * _VMS_WAIT is defined.
4684 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4686 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4687 created with popen(); otherwise partially emulate waitpid() unless
4688 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4689 Also check processes not considered by the CRTL waitpid().
4691 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4693 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4700 if (statusp) *statusp = 0;
4702 for (info = open_pipes; info != NULL; info = info->next)
4703 if (info->pid == pid) break;
4705 if (info != NULL) { /* we know about this child */
4706 while (!info->done) {
4707 _ckvmssts(sys$setast(0));
4709 if (!done) _ckvmssts(sys$clref(pipe_ef));
4710 _ckvmssts(sys$setast(1));
4711 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4714 if (statusp) *statusp = info->completion;
4718 /* child that already terminated? */
4720 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4721 if (closed_list[j].pid == pid) {
4722 if (statusp) *statusp = closed_list[j].completion;
4727 /* fall through if this child is not one of our own pipe children */
4729 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4731 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4732 * in 7.2 did we get a version that fills in the VMS completion
4733 * status as Perl has always tried to do.
4736 sts = __vms_waitpid( pid, statusp, flags );
4738 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4741 /* If the real waitpid tells us the child does not exist, we
4742 * fall through here to implement waiting for a child that
4743 * was created by some means other than exec() (say, spawned
4744 * from DCL) or to wait for a process that is not a subprocess
4745 * of the current process.
4748 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4751 $DESCRIPTOR(intdsc,"0 00:00:01");
4752 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4753 unsigned long int pidcode = JPI$_PID, mypid;
4754 unsigned long int interval[2];
4755 unsigned int jpi_iosb[2];
4756 struct itmlst_3 jpilist[2] = {
4757 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4762 /* Sorry folks, we don't presently implement rooting around for
4763 the first child we can find, and we definitely don't want to
4764 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4770 /* Get the owner of the child so I can warn if it's not mine. If the
4771 * process doesn't exist or I don't have the privs to look at it,
4772 * I can go home early.
4774 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4775 if (sts & 1) sts = jpi_iosb[0];
4787 set_vaxc_errno(sts);
4791 if (ckWARN(WARN_EXEC)) {
4792 /* remind folks they are asking for non-standard waitpid behavior */
4793 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4794 if (ownerpid != mypid)
4795 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4796 "waitpid: process %x is not a child of process %x",
4800 /* simply check on it once a second until it's not there anymore. */
4802 _ckvmssts(sys$bintim(&intdsc,interval));
4803 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4804 _ckvmssts(sys$schdwk(0,0,interval,0));
4805 _ckvmssts(sys$hiber());
4807 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4812 } /* end of waitpid() */
4817 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4819 my_gconvert(double val, int ndig, int trail, char *buf)
4821 static char __gcvtbuf[DBL_DIG+1];
4824 loc = buf ? buf : __gcvtbuf;
4826 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
4828 sprintf(loc,"%.*g",ndig,val);
4834 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4835 return gcvt(val,ndig,loc);
4838 loc[0] = '0'; loc[1] = '\0';
4845 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4846 static int rms_free_search_context(struct FAB * fab)
4850 nam = fab->fab$l_nam;
4851 nam->nam$b_nop |= NAM$M_SYNCHK;
4852 nam->nam$l_rlf = NULL;
4854 return sys$parse(fab, NULL, NULL);
4857 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4858 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4859 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4860 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4861 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4862 #define rms_nam_esll(nam) nam.nam$b_esl
4863 #define rms_nam_esl(nam) nam.nam$b_esl
4864 #define rms_nam_name(nam) nam.nam$l_name
4865 #define rms_nam_namel(nam) nam.nam$l_name
4866 #define rms_nam_type(nam) nam.nam$l_type
4867 #define rms_nam_typel(nam) nam.nam$l_type
4868 #define rms_nam_ver(nam) nam.nam$l_ver
4869 #define rms_nam_verl(nam) nam.nam$l_ver
4870 #define rms_nam_rsll(nam) nam.nam$b_rsl
4871 #define rms_nam_rsl(nam) nam.nam$b_rsl
4872 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4873 #define rms_set_fna(fab, nam, name, size) \
4874 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4875 #define rms_get_fna(fab, nam) fab.fab$l_fna
4876 #define rms_set_dna(fab, nam, name, size) \
4877 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4878 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4879 #define rms_set_esa(nam, name, size) \
4880 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4881 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4882 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4883 #define rms_set_rsa(nam, name, size) \
4884 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4885 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4886 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4887 #define rms_nam_name_type_l_size(nam) \
4888 (nam.nam$b_name + nam.nam$b_type)
4890 static int rms_free_search_context(struct FAB * fab)
4894 nam = fab->fab$l_naml;
4895 nam->naml$b_nop |= NAM$M_SYNCHK;
4896 nam->naml$l_rlf = NULL;
4897 nam->naml$l_long_defname_size = 0;
4900 return sys$parse(fab, NULL, NULL);
4903 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4904 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4905 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4906 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4907 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4908 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4909 #define rms_nam_esl(nam) nam.naml$b_esl
4910 #define rms_nam_name(nam) nam.naml$l_name
4911 #define rms_nam_namel(nam) nam.naml$l_long_name
4912 #define rms_nam_type(nam) nam.naml$l_type
4913 #define rms_nam_typel(nam) nam.naml$l_long_type
4914 #define rms_nam_ver(nam) nam.naml$l_ver
4915 #define rms_nam_verl(nam) nam.naml$l_long_ver
4916 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4917 #define rms_nam_rsl(nam) nam.naml$b_rsl
4918 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4919 #define rms_set_fna(fab, nam, name, size) \
4920 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4921 nam.naml$l_long_filename_size = size; \
4922 nam.naml$l_long_filename = name;}
4923 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4924 #define rms_set_dna(fab, nam, name, size) \
4925 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4926 nam.naml$l_long_defname_size = size; \
4927 nam.naml$l_long_defname = name; }
4928 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4929 #define rms_set_esa(nam, name, size) \
4930 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4931 nam.naml$l_long_expand_alloc = size; \
4932 nam.naml$l_long_expand = name; }
4933 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4934 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4935 nam.naml$l_long_expand = l_name; \
4936 nam.naml$l_long_expand_alloc = l_size; }
4937 #define rms_set_rsa(nam, name, size) \
4938 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4939 nam.naml$l_long_result = name; \
4940 nam.naml$l_long_result_alloc = size; }
4941 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4942 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4943 nam.naml$l_long_result = l_name; \
4944 nam.naml$l_long_result_alloc = l_size; }
4945 #define rms_nam_name_type_l_size(nam) \
4946 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4951 * The CRTL for 8.3 and later can create symbolic links in any mode,
4952 * however in 8.3 the unlink/remove/delete routines will only properly handle
4953 * them if one of the PCP modes is active.
4955 static int rms_erase(const char * vmsname)
4958 struct FAB myfab = cc$rms_fab;
4959 rms_setup_nam(mynam);
4961 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4962 rms_bind_fab_nam(myfab, mynam);
4964 /* Are we removing all versions? */
4965 if (vms_unlink_all_versions == 1) {
4966 const char * defspec = ";*";
4967 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
4970 #ifdef NAML$M_OPEN_SPECIAL
4971 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
4974 status = sys$erase(&myfab, 0, 0);
4981 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
4982 const struct dsc$descriptor_s * vms_dst_dsc,
4983 unsigned long flags)
4985 /* VMS and UNIX handle file permissions differently and the
4986 * the same ACL trick may be needed for renaming files,
4987 * especially if they are directories.
4990 /* todo: get kill_file and rename to share common code */
4991 /* I can not find online documentation for $change_acl
4992 * it appears to be replaced by $set_security some time ago */
4994 const unsigned int access_mode = 0;
4995 $DESCRIPTOR(obj_file_dsc,"FILE");
4998 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
4999 int aclsts, fndsts, rnsts = -1;
5000 unsigned int ctx = 0;
5001 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5002 struct dsc$descriptor_s * clean_dsc;
5005 unsigned char myace$b_length;
5006 unsigned char myace$b_type;
5007 unsigned short int myace$w_flags;
5008 unsigned long int myace$l_access;
5009 unsigned long int myace$l_ident;
5010 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
5011 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
5013 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
5016 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
5017 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
5019 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
5020 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
5024 /* Expand the input spec using RMS, since we do not want to put
5025 * ACLs on the target of a symbolic link */
5026 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
5027 if (vmsname == NULL)
5030 rslt = do_rmsexpand(vms_src_dsc->dsc$a_pointer,
5034 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_SYMLINK,
5038 PerlMem_free(vmsname);
5042 /* So we get our own UIC to use as a rights identifier,
5043 * and the insert an ACE at the head of the ACL which allows us
5044 * to delete the file.
5046 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
5048 fildsc.dsc$w_length = strlen(vmsname);
5049 fildsc.dsc$a_pointer = vmsname;
5051 newace.myace$l_ident = oldace.myace$l_ident;
5054 /* Grab any existing ACEs with this identifier in case we fail */
5055 clean_dsc = &fildsc;
5056 aclsts = fndsts = sys$get_security(&obj_file_dsc,
5064 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
5065 /* Add the new ACE . . . */
5067 /* if the sys$get_security succeeded, then ctx is valid, and the
5068 * object/file descriptors will be ignored. But otherwise they
5071 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
5072 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5073 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5075 set_vaxc_errno(aclsts);
5076 PerlMem_free(vmsname);
5080 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5083 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5085 if ($VMS_STATUS_SUCCESS(rnsts)) {
5086 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5089 /* Put things back the way they were. */
5091 aclsts = sys$get_security(&obj_file_dsc,
5099 if ($VMS_STATUS_SUCCESS(aclsts)) {
5103 if (!$VMS_STATUS_SUCCESS(fndsts))
5104 sec_flags = OSS$M_RELCTX;
5106 /* Get rid of the new ACE */
5107 aclsts = sys$set_security(NULL, NULL, NULL,
5108 sec_flags, dellst, &ctx, &access_mode);
5110 /* If there was an old ACE, put it back */
5111 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5112 addlst[0].bufadr = &oldace;
5113 aclsts = sys$set_security(NULL, NULL, NULL,
5114 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5115 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5117 set_vaxc_errno(aclsts);
5123 /* Try to clear the lock on the ACL list */
5124 aclsts2 = sys$set_security(NULL, NULL, NULL,
5125 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5127 /* Rename errors are most important */
5128 if (!$VMS_STATUS_SUCCESS(rnsts))
5131 set_vaxc_errno(aclsts);
5136 if (aclsts != SS$_ACLEMPTY)
5143 PerlMem_free(vmsname);
5148 /*{{{int rename(const char *, const char * */
5149 /* Not exactly what X/Open says to do, but doing it absolutely right
5150 * and efficiently would require a lot more work. This should be close
5151 * enough to pass all but the most strict X/Open compliance test.
5154 Perl_rename(pTHX_ const char *src, const char * dst)
5163 /* Validate the source file */
5164 src_sts = flex_lstat(src, &src_st);
5167 /* No source file or other problem */
5171 dst_sts = flex_lstat(dst, &dst_st);
5174 if (dst_st.st_dev != src_st.st_dev) {
5175 /* Must be on the same device */
5180 /* VMS_INO_T_COMPARE is true if the inodes are different
5181 * to match the output of memcmp
5184 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5185 /* That was easy, the files are the same! */
5189 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5190 /* If source is a directory, so must be dest */
5198 if ((dst_sts == 0) &&
5199 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5201 /* We have issues here if vms_unlink_all_versions is set
5202 * If the destination exists, and is not a directory, then
5203 * we must delete in advance.
5205 * If the src is a directory, then we must always pre-delete
5208 * If we successfully delete the dst in advance, and the rename fails
5209 * X/Open requires that errno be EIO.
5213 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5215 d_sts = mp_do_kill_file(aTHX_ dst, S_ISDIR(dst_st.st_mode));
5219 /* We killed the destination, so only errno now is EIO */
5224 /* Originally the idea was to call the CRTL rename() and only
5225 * try the lib$rename_file if it failed.
5226 * It turns out that there are too many variants in what the
5227 * the CRTL rename might do, so only use lib$rename_file
5232 /* Is the source and dest both in VMS format */
5233 /* if the source is a directory, then need to fileify */
5234 /* and dest must be a directory or non-existant. */
5240 unsigned long flags;
5241 struct dsc$descriptor_s old_file_dsc;
5242 struct dsc$descriptor_s new_file_dsc;
5244 /* We need to modify the src and dst depending
5245 * on if one or more of them are directories.
5248 vms_src = PerlMem_malloc(VMS_MAXRSS);
5249 if (vms_src == NULL)
5250 _ckvmssts_noperl(SS$_INSFMEM);
5252 /* Source is always a VMS format file */
5253 ret_str = do_tovmsspec(src, vms_src, 0, NULL);
5254 if (ret_str == NULL) {
5255 PerlMem_free(vms_src);
5260 vms_dst = PerlMem_malloc(VMS_MAXRSS);
5261 if (vms_dst == NULL)
5262 _ckvmssts_noperl(SS$_INSFMEM);
5264 if (S_ISDIR(src_st.st_mode)) {
5266 char * vms_dir_file;
5268 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5269 if (vms_dir_file == NULL)
5270 _ckvmssts_noperl(SS$_INSFMEM);
5272 /* The source must be a file specification */
5273 ret_str = do_fileify_dirspec(vms_src, vms_dir_file, 0, NULL);
5274 if (ret_str == NULL) {
5275 PerlMem_free(vms_src);
5276 PerlMem_free(vms_dst);
5277 PerlMem_free(vms_dir_file);
5281 PerlMem_free(vms_src);
5282 vms_src = vms_dir_file;
5284 /* If the dest is a directory, we must remove it
5287 d_sts = mp_do_kill_file(aTHX_ dst, 1);
5289 PerlMem_free(vms_src);
5290 PerlMem_free(vms_dst);
5298 /* The dest must be a VMS file specification */
5299 ret_str = do_tovmsspec(dst, vms_dst, 0, NULL);
5300 if (ret_str == NULL) {
5301 PerlMem_free(vms_src);
5302 PerlMem_free(vms_dst);
5307 /* The source must be a file specification */
5308 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5309 if (vms_dir_file == NULL)
5310 _ckvmssts_noperl(SS$_INSFMEM);
5312 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5313 if (ret_str == NULL) {
5314 PerlMem_free(vms_src);
5315 PerlMem_free(vms_dst);
5316 PerlMem_free(vms_dir_file);
5320 PerlMem_free(vms_dst);
5321 vms_dst = vms_dir_file;
5324 /* File to file or file to new dir */
5326 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5327 /* VMS pathify a dir target */
5328 ret_str = do_tovmspath(dst, vms_dst, 0, NULL);
5329 if (ret_str == NULL) {
5330 PerlMem_free(vms_src);
5331 PerlMem_free(vms_dst);
5337 /* fileify a target VMS file specification */
5338 ret_str = do_tovmsspec(dst, vms_dst, 0, NULL);
5339 if (ret_str == NULL) {
5340 PerlMem_free(vms_src);
5341 PerlMem_free(vms_dst);
5348 old_file_dsc.dsc$a_pointer = vms_src;
5349 old_file_dsc.dsc$w_length = strlen(vms_src);
5350 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5351 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5353 new_file_dsc.dsc$a_pointer = vms_dst;
5354 new_file_dsc.dsc$w_length = strlen(vms_dst);
5355 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5356 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5359 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5360 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5363 sts = lib$rename_file(&old_file_dsc,
5367 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5368 if (!$VMS_STATUS_SUCCESS(sts)) {
5370 /* We could have failed because VMS style permissions do not
5371 * permit renames that UNIX will allow. Just like the hack
5374 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5377 PerlMem_free(vms_src);
5378 PerlMem_free(vms_dst);
5379 if (!$VMS_STATUS_SUCCESS(sts)) {
5386 if (vms_unlink_all_versions) {
5387 /* Now get rid of any previous versions of the source file that
5392 src_sts = mp_do_kill_file(aTHX_ src, S_ISDIR(src_st.st_mode));
5396 /* We deleted the destination, so must force the error to be EIO */
5397 if ((retval != 0) && (pre_delete != 0))
5405 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5406 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5407 * to expand file specification. Allows for a single default file
5408 * specification and a simple mask of options. If outbuf is non-NULL,
5409 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5410 * the resultant file specification is placed. If outbuf is NULL, the
5411 * resultant file specification is placed into a static buffer.
5412 * The third argument, if non-NULL, is taken to be a default file
5413 * specification string. The fourth argument is unused at present.
5414 * rmesexpand() returns the address of the resultant string if
5415 * successful, and NULL on error.
5417 * New functionality for previously unused opts value:
5418 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5419 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5420 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5421 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5423 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5427 (pTHX_ const char *filespec,
5430 const char *defspec,
5435 static char __rmsexpand_retbuf[VMS_MAXRSS];
5436 char * vmsfspec, *tmpfspec;
5437 char * esa, *cp, *out = NULL;
5441 struct FAB myfab = cc$rms_fab;
5442 rms_setup_nam(mynam);
5444 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5447 /* temp hack until UTF8 is actually implemented */
5448 if (fs_utf8 != NULL)
5451 if (!filespec || !*filespec) {
5452 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5456 if (ts) out = Newx(outbuf,VMS_MAXRSS,char);
5457 else outbuf = __rmsexpand_retbuf;
5465 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5466 isunix = is_unix_filespec(filespec);
5468 vmsfspec = PerlMem_malloc(VMS_MAXRSS);
5469 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5470 if (do_tovmsspec(filespec,vmsfspec,0,fs_utf8) == NULL) {
5471 PerlMem_free(vmsfspec);
5476 filespec = vmsfspec;
5478 /* Unless we are forcing to VMS format, a UNIX input means
5479 * UNIX output, and that requires long names to be used
5481 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5482 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5483 opts |= PERL_RMSEXPAND_M_LONG;
5490 rms_set_fna(myfab, mynam, (char *)filespec, strlen(filespec)); /* cast ok */
5491 rms_bind_fab_nam(myfab, mynam);
5493 if (defspec && *defspec) {
5495 t_isunix = is_unix_filespec(defspec);
5497 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
5498 if (tmpfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5499 if (do_tovmsspec(defspec,tmpfspec,0,dfs_utf8) == NULL) {
5500 PerlMem_free(tmpfspec);
5501 if (vmsfspec != NULL)
5502 PerlMem_free(vmsfspec);
5509 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
5512 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
5513 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5514 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5515 esal = PerlMem_malloc(VMS_MAXRSS);
5516 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5518 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5520 /* If a NAML block is used RMS always writes to the long and short
5521 * addresses unless you suppress the short name.
5523 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5524 outbufl = PerlMem_malloc(VMS_MAXRSS);
5525 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5527 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5529 #ifdef NAM$M_NO_SHORT_UPCASE
5530 if (decc_efs_case_preserve)
5531 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5534 /* We may not want to follow symbolic links */
5535 #ifdef NAML$M_OPEN_SPECIAL
5536 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5537 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5540 /* First attempt to parse as an existing file */
5541 retsts = sys$parse(&myfab,0,0);
5542 if (!(retsts & STS$K_SUCCESS)) {
5544 /* Could not find the file, try as syntax only if error is not fatal */
5545 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5546 if (retsts == RMS$_DNF || retsts == RMS$_DIR || retsts == RMS$_DEV) {
5547 retsts = sys$parse(&myfab,0,0);
5548 if (retsts & STS$K_SUCCESS) goto expanded;
5551 /* Still could not parse the file specification */
5552 /*----------------------------------------------*/
5553 sts = rms_free_search_context(&myfab); /* Free search context */
5554 if (out) Safefree(out);
5555 if (tmpfspec != NULL)
5556 PerlMem_free(tmpfspec);
5557 if (vmsfspec != NULL)
5558 PerlMem_free(vmsfspec);
5559 if (outbufl != NULL)
5560 PerlMem_free(outbufl);
5564 set_vaxc_errno(retsts);
5565 if (retsts == RMS$_PRV) set_errno(EACCES);
5566 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5567 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5568 else set_errno(EVMSERR);
5571 retsts = sys$search(&myfab,0,0);
5572 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5573 sts = rms_free_search_context(&myfab); /* Free search context */
5574 if (out) Safefree(out);
5575 if (tmpfspec != NULL)
5576 PerlMem_free(tmpfspec);
5577 if (vmsfspec != NULL)
5578 PerlMem_free(vmsfspec);
5579 if (outbufl != NULL)
5580 PerlMem_free(outbufl);
5584 set_vaxc_errno(retsts);
5585 if (retsts == RMS$_PRV) set_errno(EACCES);
5586 else set_errno(EVMSERR);
5590 /* If the input filespec contained any lowercase characters,
5591 * downcase the result for compatibility with Unix-minded code. */
5593 if (!decc_efs_case_preserve) {
5594 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5595 if (islower(*tbuf)) { haslower = 1; break; }
5598 /* Is a long or a short name expected */
5599 /*------------------------------------*/
5600 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5601 if (rms_nam_rsll(mynam)) {
5603 speclen = rms_nam_rsll(mynam);
5606 tbuf = esal; /* Not esa */
5607 speclen = rms_nam_esll(mynam);
5611 if (rms_nam_rsl(mynam)) {
5613 speclen = rms_nam_rsl(mynam);
5616 tbuf = esa; /* Not esal */
5617 speclen = rms_nam_esl(mynam);
5620 tbuf[speclen] = '\0';
5622 /* Trim off null fields added by $PARSE
5623 * If type > 1 char, must have been specified in original or default spec
5624 * (not true for version; $SEARCH may have added version of existing file).
5626 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5627 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5628 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5629 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5632 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5633 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5635 if (trimver || trimtype) {
5636 if (defspec && *defspec) {
5637 char *defesal = NULL;
5638 char *defesa = NULL;
5639 defesa = PerlMem_malloc(VMS_MAXRSS + 1);
5640 if (defesa != NULL) {
5641 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5642 defesal = PerlMem_malloc(VMS_MAXRSS + 1);
5643 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5645 struct FAB deffab = cc$rms_fab;
5646 rms_setup_nam(defnam);
5648 rms_bind_fab_nam(deffab, defnam);
5652 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5654 /* RMS needs the esa/esal as a work area if wildcards are involved */
5655 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5657 rms_clear_nam_nop(defnam);
5658 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5659 #ifdef NAM$M_NO_SHORT_UPCASE
5660 if (decc_efs_case_preserve)
5661 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5663 #ifdef NAML$M_OPEN_SPECIAL
5664 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5665 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5667 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5669 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5672 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5675 if (defesal != NULL)
5676 PerlMem_free(defesal);
5677 PerlMem_free(defesa);
5681 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5682 if (*(rms_nam_verl(mynam)) != '\"')
5683 speclen = rms_nam_verl(mynam) - tbuf;
5686 if (*(rms_nam_ver(mynam)) != '\"')
5687 speclen = rms_nam_ver(mynam) - tbuf;
5691 /* If we didn't already trim version, copy down */
5692 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5693 if (speclen > rms_nam_verl(mynam) - tbuf)
5695 (rms_nam_typel(mynam),
5696 rms_nam_verl(mynam),
5697 speclen - (rms_nam_verl(mynam) - tbuf));
5698 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5701 if (speclen > rms_nam_ver(mynam) - tbuf)
5703 (rms_nam_type(mynam),
5705 speclen - (rms_nam_ver(mynam) - tbuf));
5706 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5711 /* Done with these copies of the input files */
5712 /*-------------------------------------------*/
5713 if (vmsfspec != NULL)
5714 PerlMem_free(vmsfspec);
5715 if (tmpfspec != NULL)
5716 PerlMem_free(tmpfspec);
5718 /* If we just had a directory spec on input, $PARSE "helpfully"
5719 * adds an empty name and type for us */
5720 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5721 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5722 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5723 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5724 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5725 speclen = rms_nam_namel(mynam) - tbuf;
5730 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5731 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5732 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5733 speclen = rms_nam_name(mynam) - tbuf;
5736 /* Posix format specifications must have matching quotes */
5737 if (speclen < (VMS_MAXRSS - 1)) {
5738 if (decc_posix_compliant_pathnames && (tbuf[0] == '\"')) {
5739 if ((speclen > 1) && (tbuf[speclen-1] != '\"')) {
5740 tbuf[speclen] = '\"';
5745 tbuf[speclen] = '\0';
5746 if (haslower && !decc_efs_case_preserve) __mystrtolower(tbuf);
5748 /* Have we been working with an expanded, but not resultant, spec? */
5749 /* Also, convert back to Unix syntax if necessary. */
5753 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5754 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5755 rsl = rms_nam_rsll(mynam);
5759 rsl = rms_nam_rsl(mynam);
5763 if (do_tounixspec(tbuf, outbuf ,0 , fs_utf8) == NULL) {
5764 if (out) Safefree(out);
5768 if (outbufl != NULL)
5769 PerlMem_free(outbufl);
5773 else strcpy(outbuf, tbuf);
5776 tmpfspec = PerlMem_malloc(VMS_MAXRSS);
5777 if (tmpfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5778 if (do_tounixspec(tbuf,tmpfspec,0,fs_utf8) == NULL) {
5779 if (out) Safefree(out);
5783 PerlMem_free(tmpfspec);
5784 if (outbufl != NULL)
5785 PerlMem_free(outbufl);
5788 strcpy(outbuf,tmpfspec);
5789 PerlMem_free(tmpfspec);
5792 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5793 sts = rms_free_search_context(&myfab); /* Free search context */
5797 if (outbufl != NULL)
5798 PerlMem_free(outbufl);
5802 /* External entry points */
5803 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5804 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5805 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5806 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5807 char *Perl_rmsexpand_utf8
5808 (pTHX_ const char *spec, char *buf, const char *def,
5809 unsigned opt, int * fs_utf8, int * dfs_utf8)
5810 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5811 char *Perl_rmsexpand_utf8_ts
5812 (pTHX_ const char *spec, char *buf, const char *def,
5813 unsigned opt, int * fs_utf8, int * dfs_utf8)
5814 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
5818 ** The following routines are provided to make life easier when
5819 ** converting among VMS-style and Unix-style directory specifications.
5820 ** All will take input specifications in either VMS or Unix syntax. On
5821 ** failure, all return NULL. If successful, the routines listed below
5822 ** return a pointer to a buffer containing the appropriately
5823 ** reformatted spec (and, therefore, subsequent calls to that routine
5824 ** will clobber the result), while the routines of the same names with
5825 ** a _ts suffix appended will return a pointer to a mallocd string
5826 ** containing the appropriately reformatted spec.
5827 ** In all cases, only explicit syntax is altered; no check is made that
5828 ** the resulting string is valid or that the directory in question
5831 ** fileify_dirspec() - convert a directory spec into the name of the
5832 ** directory file (i.e. what you can stat() to see if it's a dir).
5833 ** The style (VMS or Unix) of the result is the same as the style
5834 ** of the parameter passed in.
5835 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5836 ** what you prepend to a filename to indicate what directory it's in).
5837 ** The style (VMS or Unix) of the result is the same as the style
5838 ** of the parameter passed in.
5839 ** tounixpath() - convert a directory spec into a Unix-style path.
5840 ** tovmspath() - convert a directory spec into a VMS-style path.
5841 ** tounixspec() - convert any file spec into a Unix-style file spec.
5842 ** tovmsspec() - convert any file spec into a VMS-style spec.
5843 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5845 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
5846 ** Permission is given to distribute this code as part of the Perl
5847 ** standard distribution under the terms of the GNU General Public
5848 ** License or the Perl Artistic License. Copies of each may be
5849 ** found in the Perl standard distribution.
5852 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5853 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
5855 static char __fileify_retbuf[VMS_MAXRSS];
5856 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
5857 char *retspec, *cp1, *cp2, *lastdir;
5858 char *trndir, *vmsdir;
5859 unsigned short int trnlnm_iter_count;
5861 if (utf8_fl != NULL)
5864 if (!dir || !*dir) {
5865 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5867 dirlen = strlen(dir);
5868 while (dirlen && dir[dirlen-1] == '/') --dirlen;
5869 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
5870 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
5877 if (dirlen > (VMS_MAXRSS - 1)) {
5878 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
5881 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
5882 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5883 if (!strpbrk(dir+1,"/]>:") &&
5884 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
5885 strcpy(trndir,*dir == '/' ? dir + 1: dir);
5886 trnlnm_iter_count = 0;
5887 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
5888 trnlnm_iter_count++;
5889 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
5891 dirlen = strlen(trndir);
5894 strncpy(trndir,dir,dirlen);
5895 trndir[dirlen] = '\0';
5898 /* At this point we are done with *dir and use *trndir which is a
5899 * copy that can be modified. *dir must not be modified.
5902 /* If we were handed a rooted logical name or spec, treat it like a
5903 * simple directory, so that
5904 * $ Define myroot dev:[dir.]
5905 * ... do_fileify_dirspec("myroot",buf,1) ...
5906 * does something useful.
5908 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
5909 trndir[--dirlen] = '\0';
5910 trndir[dirlen-1] = ']';
5912 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
5913 trndir[--dirlen] = '\0';
5914 trndir[dirlen-1] = '>';
5917 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
5918 /* If we've got an explicit filename, we can just shuffle the string. */
5919 if (*(cp1+1)) hasfilename = 1;
5920 /* Similarly, we can just back up a level if we've got multiple levels
5921 of explicit directories in a VMS spec which ends with directories. */
5923 for (cp2 = cp1; cp2 > trndir; cp2--) {
5925 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
5926 /* fix-me, can not scan EFS file specs backward like this */
5927 *cp2 = *cp1; *cp1 = '\0';
5932 if (*cp2 == '[' || *cp2 == '<') break;
5937 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
5938 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5939 cp1 = strpbrk(trndir,"]:>");
5940 if (hasfilename || !cp1) { /* Unix-style path or filename */
5941 if (trndir[0] == '.') {
5942 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
5943 PerlMem_free(trndir);
5944 PerlMem_free(vmsdir);
5945 return do_fileify_dirspec("[]",buf,ts,NULL);
5947 else if (trndir[1] == '.' &&
5948 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
5949 PerlMem_free(trndir);
5950 PerlMem_free(vmsdir);
5951 return do_fileify_dirspec("[-]",buf,ts,NULL);
5954 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
5955 dirlen -= 1; /* to last element */
5956 lastdir = strrchr(trndir,'/');
5958 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
5959 /* If we have "/." or "/..", VMSify it and let the VMS code
5960 * below expand it, rather than repeating the code to handle
5961 * relative components of a filespec here */
5963 if (*(cp1+2) == '.') cp1++;
5964 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
5966 if (do_tovmsspec(trndir,vmsdir,0,NULL) == NULL) {
5967 PerlMem_free(trndir);
5968 PerlMem_free(vmsdir);
5971 if (strchr(vmsdir,'/') != NULL) {
5972 /* If do_tovmsspec() returned it, it must have VMS syntax
5973 * delimiters in it, so it's a mixed VMS/Unix spec. We take
5974 * the time to check this here only so we avoid a recursion
5975 * loop; otherwise, gigo.
5977 PerlMem_free(trndir);
5978 PerlMem_free(vmsdir);
5979 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
5982 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
5983 PerlMem_free(trndir);
5984 PerlMem_free(vmsdir);
5987 ret_chr = do_tounixspec(trndir,buf,ts,NULL);
5988 PerlMem_free(trndir);
5989 PerlMem_free(vmsdir);
5993 } while ((cp1 = strstr(cp1,"/.")) != NULL);
5994 lastdir = strrchr(trndir,'/');
5996 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
5998 /* Ditto for specs that end in an MFD -- let the VMS code
5999 * figure out whether it's a real device or a rooted logical. */
6001 /* This should not happen any more. Allowing the fake /000000
6002 * in a UNIX pathname causes all sorts of problems when trying
6003 * to run in UNIX emulation. So the VMS to UNIX conversions
6004 * now remove the fake /000000 directories.
6007 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6008 if (do_tovmsspec(trndir,vmsdir,0,NULL) == NULL) {
6009 PerlMem_free(trndir);
6010 PerlMem_free(vmsdir);
6013 if (do_fileify_dirspec(vmsdir,trndir,0,NULL) == NULL) {
6014 PerlMem_free(trndir);
6015 PerlMem_free(vmsdir);
6018 ret_chr = do_tounixspec(trndir,buf,ts,NULL);
6019 PerlMem_free(trndir);
6020 PerlMem_free(vmsdir);
6025 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6026 !(lastdir = cp1 = strrchr(trndir,']')) &&
6027 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6028 if ((cp2 = strchr(cp1,'.'))) { /* look for explicit type */
6031 /* For EFS or ODS-5 look for the last dot */
6032 if (decc_efs_charset) {
6033 cp2 = strrchr(cp1,'.');
6035 if (vms_process_case_tolerant) {
6036 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
6037 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
6038 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
6039 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6040 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6041 (ver || *cp3)))))) {
6042 PerlMem_free(trndir);
6043 PerlMem_free(vmsdir);
6045 set_vaxc_errno(RMS$_DIR);
6050 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
6051 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
6052 !*(cp2+3) || *(cp2+3) != 'R' ||
6053 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6054 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6055 (ver || *cp3)))))) {
6056 PerlMem_free(trndir);
6057 PerlMem_free(vmsdir);
6059 set_vaxc_errno(RMS$_DIR);
6063 dirlen = cp2 - trndir;
6067 retlen = dirlen + 6;
6068 if (buf) retspec = buf;
6069 else if (ts) Newx(retspec,retlen+1,char);
6070 else retspec = __fileify_retbuf;
6071 memcpy(retspec,trndir,dirlen);
6072 retspec[dirlen] = '\0';
6074 /* We've picked up everything up to the directory file name.
6075 Now just add the type and version, and we're set. */
6076 if ((!decc_efs_case_preserve) && vms_process_case_tolerant)
6077 strcat(retspec,".dir;1");
6079 strcat(retspec,".DIR;1");
6080 PerlMem_free(trndir);
6081 PerlMem_free(vmsdir);
6084 else { /* VMS-style directory spec */
6086 char *esa, *esal, term, *cp;
6089 unsigned long int sts, cmplen, haslower = 0;
6090 unsigned int nam_fnb;
6092 struct FAB dirfab = cc$rms_fab;
6093 rms_setup_nam(savnam);
6094 rms_setup_nam(dirnam);
6096 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
6097 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6099 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6100 esal = PerlMem_malloc(VMS_MAXRSS);
6101 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6103 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6104 rms_bind_fab_nam(dirfab, dirnam);
6105 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6106 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6107 #ifdef NAM$M_NO_SHORT_UPCASE
6108 if (decc_efs_case_preserve)
6109 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6112 for (cp = trndir; *cp; cp++)
6113 if (islower(*cp)) { haslower = 1; break; }
6114 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6115 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
6116 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6117 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
6123 PerlMem_free(trndir);
6124 PerlMem_free(vmsdir);
6126 set_vaxc_errno(dirfab.fab$l_sts);
6132 /* Does the file really exist? */
6133 if (sys$search(&dirfab)& STS$K_SUCCESS) {
6134 /* Yes; fake the fnb bits so we'll check type below */
6135 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6137 else { /* No; just work with potential name */
6138 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6141 fab_sts = dirfab.fab$l_sts;
6142 sts = rms_free_search_context(&dirfab);
6146 PerlMem_free(trndir);
6147 PerlMem_free(vmsdir);
6148 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
6154 /* Make sure we are using the right buffer */
6157 my_esa_len = rms_nam_esll(dirnam);
6160 my_esa_len = rms_nam_esl(dirnam);
6162 my_esa[my_esa_len] = '\0';
6163 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6164 cp1 = strchr(my_esa,']');
6165 if (!cp1) cp1 = strchr(my_esa,'>');
6166 if (cp1) { /* Should always be true */
6167 my_esa_len -= cp1 - my_esa - 1;
6168 memmove(my_esa, cp1 + 1, my_esa_len);
6171 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6172 /* Yep; check version while we're at it, if it's there. */
6173 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6174 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6175 /* Something other than .DIR[;1]. Bzzt. */
6176 sts = rms_free_search_context(&dirfab);
6180 PerlMem_free(trndir);
6181 PerlMem_free(vmsdir);
6183 set_vaxc_errno(RMS$_DIR);
6188 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6189 /* They provided at least the name; we added the type, if necessary, */
6190 if (buf) retspec = buf; /* in sys$parse() */
6191 else if (ts) Newx(retspec, my_esa_len + 1, char);
6192 else retspec = __fileify_retbuf;
6193 strcpy(retspec,my_esa);
6194 sts = rms_free_search_context(&dirfab);
6195 PerlMem_free(trndir);
6199 PerlMem_free(vmsdir);
6202 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6203 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6207 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6208 if (cp1 == NULL) { /* should never happen */
6209 sts = rms_free_search_context(&dirfab);
6210 PerlMem_free(trndir);
6214 PerlMem_free(vmsdir);
6219 retlen = strlen(my_esa);
6220 cp1 = strrchr(my_esa,'.');
6221 /* ODS-5 directory specifications can have extra "." in them. */
6222 /* Fix-me, can not scan EFS file specifications backwards */
6223 while (cp1 != NULL) {
6224 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6228 while ((cp1 > my_esa) && (*cp1 != '.'))
6235 if ((cp1) != NULL) {
6236 /* There's more than one directory in the path. Just roll back. */
6238 if (buf) retspec = buf;
6239 else if (ts) Newx(retspec,retlen+7,char);
6240 else retspec = __fileify_retbuf;
6241 strcpy(retspec,my_esa);
6244 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6245 /* Go back and expand rooted logical name */
6246 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6247 #ifdef NAM$M_NO_SHORT_UPCASE
6248 if (decc_efs_case_preserve)
6249 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6251 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6252 sts = rms_free_search_context(&dirfab);
6256 PerlMem_free(trndir);
6257 PerlMem_free(vmsdir);
6259 set_vaxc_errno(dirfab.fab$l_sts);
6263 /* This changes the length of the string of course */
6265 my_esa_len = rms_nam_esll(dirnam);
6267 my_esa_len = rms_nam_esl(dirnam);
6270 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6271 if (buf) retspec = buf;
6272 else if (ts) Newx(retspec,retlen+16,char);
6273 else retspec = __fileify_retbuf;
6274 cp1 = strstr(my_esa,"][");
6275 if (!cp1) cp1 = strstr(my_esa,"]<");
6276 dirlen = cp1 - my_esa;
6277 memcpy(retspec,my_esa,dirlen);
6278 if (!strncmp(cp1+2,"000000]",7)) {
6279 retspec[dirlen-1] = '\0';
6280 /* fix-me Not full ODS-5, just extra dots in directories for now */
6281 cp1 = retspec + dirlen - 1;
6282 while (cp1 > retspec)
6287 if (*(cp1-1) != '^')
6292 if (*cp1 == '.') *cp1 = ']';
6294 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
6295 memmove(cp1+1,"000000]",7);
6299 memmove(retspec+dirlen,cp1+2,retlen-dirlen);
6300 retspec[retlen] = '\0';
6301 /* Convert last '.' to ']' */
6302 cp1 = retspec+retlen-1;
6303 while (*cp != '[') {
6306 /* Do not trip on extra dots in ODS-5 directories */
6307 if ((cp1 == retspec) || (*(cp1-1) != '^'))
6311 if (*cp1 == '.') *cp1 = ']';
6313 memmove(cp1+8,cp1+1,retspec+dirlen-cp1);
6314 memmove(cp1+1,"000000]",7);
6318 else { /* This is a top-level dir. Add the MFD to the path. */
6319 if (buf) retspec = buf;
6320 else if (ts) Newx(retspec,retlen+16,char);
6321 else retspec = __fileify_retbuf;
6324 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
6325 strcpy(cp2,":[000000]");
6330 sts = rms_free_search_context(&dirfab);
6331 /* We've set up the string up through the filename. Add the
6332 type and version, and we're done. */
6333 strcat(retspec,".DIR;1");
6335 /* $PARSE may have upcased filespec, so convert output to lower
6336 * case if input contained any lowercase characters. */
6337 if (haslower && !decc_efs_case_preserve) __mystrtolower(retspec);
6338 PerlMem_free(trndir);
6342 PerlMem_free(vmsdir);
6345 } /* end of do_fileify_dirspec() */
6347 /* External entry points */
6348 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6349 { return do_fileify_dirspec(dir,buf,0,NULL); }
6350 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6351 { return do_fileify_dirspec(dir,buf,1,NULL); }
6352 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6353 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6354 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6355 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6357 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6358 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6360 static char __pathify_retbuf[VMS_MAXRSS];
6361 unsigned long int retlen;
6362 char *retpath, *cp1, *cp2, *trndir;
6363 unsigned short int trnlnm_iter_count;
6366 if (utf8_fl != NULL)
6369 if (!dir || !*dir) {
6370 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6373 trndir = PerlMem_malloc(VMS_MAXRSS);
6374 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6375 if (*dir) strcpy(trndir,dir);
6376 else getcwd(trndir,VMS_MAXRSS - 1);
6378 trnlnm_iter_count = 0;
6379 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6380 && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
6381 trnlnm_iter_count++;
6382 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6383 trnlen = strlen(trndir);
6385 /* Trap simple rooted lnms, and return lnm:[000000] */
6386 if (!strcmp(trndir+trnlen-2,".]")) {
6387 if (buf) retpath = buf;
6388 else if (ts) Newx(retpath,strlen(dir)+10,char);
6389 else retpath = __pathify_retbuf;
6390 strcpy(retpath,dir);
6391 strcat(retpath,":[000000]");
6392 PerlMem_free(trndir);
6397 /* At this point we do not work with *dir, but the copy in
6398 * *trndir that is modifiable.
6401 if (!strpbrk(trndir,"]:>")) { /* Unix-style path or plain name */
6402 if (*trndir == '.' && (*(trndir+1) == '\0' ||
6403 (*(trndir+1) == '.' && *(trndir+2) == '\0')))
6404 retlen = 2 + (*(trndir+1) != '\0');
6406 if ( !(cp1 = strrchr(trndir,'/')) &&
6407 !(cp1 = strrchr(trndir,']')) &&
6408 !(cp1 = strrchr(trndir,'>')) ) cp1 = trndir;
6409 if ((cp2 = strchr(cp1,'.')) != NULL &&
6410 (*(cp2-1) != '/' || /* Trailing '.', '..', */
6411 !(*(cp2+1) == '\0' || /* or '...' are dirs. */
6412 (*(cp2+1) == '.' && *(cp2+2) == '\0') ||
6413 (*(cp2+1) == '.' && *(cp2+2) == '.' && *(cp2+3) == '\0')))) {
6416 /* For EFS or ODS-5 look for the last dot */
6417 if (decc_efs_charset) {
6418 cp2 = strrchr(cp1,'.');
6420 if (vms_process_case_tolerant) {
6421 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
6422 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
6423 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
6424 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6425 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6426 (ver || *cp3)))))) {
6427 PerlMem_free(trndir);
6429 set_vaxc_errno(RMS$_DIR);
6434 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
6435 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
6436 !*(cp2+3) || *(cp2+3) != 'R' ||
6437 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6438 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6439 (ver || *cp3)))))) {
6440 PerlMem_free(trndir);
6442 set_vaxc_errno(RMS$_DIR);
6446 retlen = cp2 - trndir + 1;
6448 else { /* No file type present. Treat the filename as a directory. */
6449 retlen = strlen(trndir) + 1;
6452 if (buf) retpath = buf;
6453 else if (ts) Newx(retpath,retlen+1,char);
6454 else retpath = __pathify_retbuf;
6455 strncpy(retpath, trndir, retlen-1);
6456 if (retpath[retlen-2] != '/') { /* If the path doesn't already end */
6457 retpath[retlen-1] = '/'; /* with '/', add it. */
6458 retpath[retlen] = '\0';
6460 else retpath[retlen-1] = '\0';
6462 else { /* VMS-style directory spec */
6463 char *esa, *esal, *cp;
6466 unsigned long int sts, cmplen, haslower;
6467 struct FAB dirfab = cc$rms_fab;
6469 rms_setup_nam(savnam);
6470 rms_setup_nam(dirnam);
6472 /* If we've got an explicit filename, we can just shuffle the string. */
6473 if ( ( (cp1 = strrchr(trndir,']')) != NULL ||
6474 (cp1 = strrchr(trndir,'>')) != NULL ) && *(cp1+1)) {
6475 if ((cp2 = strchr(cp1,'.')) != NULL) {
6477 if (vms_process_case_tolerant) {
6478 if (!*(cp2+1) || toupper(*(cp2+1)) != 'D' || /* Wrong type. */
6479 !*(cp2+2) || toupper(*(cp2+2)) != 'I' || /* Bzzt. */
6480 !*(cp2+3) || toupper(*(cp2+3)) != 'R' ||
6481 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6482 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6483 (ver || *cp3)))))) {
6484 PerlMem_free(trndir);
6486 set_vaxc_errno(RMS$_DIR);
6491 if (!*(cp2+1) || *(cp2+1) != 'D' || /* Wrong type. */
6492 !*(cp2+2) || *(cp2+2) != 'I' || /* Bzzt. */
6493 !*(cp2+3) || *(cp2+3) != 'R' ||
6494 (*(cp2+4) && ((*(cp2+4) != ';' && *(cp2+4) != '.') ||
6495 (*(cp2+5) && ((ver = strtol(cp2+5,&cp3,10)) != 1 &&
6496 (ver || *cp3)))))) {
6497 PerlMem_free(trndir);
6499 set_vaxc_errno(RMS$_DIR);
6504 else { /* No file type, so just draw name into directory part */
6505 for (cp2 = cp1; *cp2; cp2++) ;
6508 *(cp2+1) = '\0'; /* OK; trndir is guaranteed to be long enough */
6510 /* We've now got a VMS 'path'; fall through */
6513 dirlen = strlen(trndir);
6514 if (trndir[dirlen-1] == ']' ||
6515 trndir[dirlen-1] == '>' ||
6516 trndir[dirlen-1] == ':') { /* It's already a VMS 'path' */
6517 if (buf) retpath = buf;
6518 else if (ts) Newx(retpath,strlen(trndir)+1,char);
6519 else retpath = __pathify_retbuf;
6520 strcpy(retpath,trndir);
6521 PerlMem_free(trndir);
6524 rms_set_fna(dirfab, dirnam, trndir, dirlen);
6525 esa = PerlMem_malloc(VMS_MAXRSS);
6526 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6528 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6529 esal = PerlMem_malloc(VMS_MAXRSS);
6530 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6532 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6533 rms_bind_fab_nam(dirfab, dirnam);
6534 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
6535 #ifdef NAM$M_NO_SHORT_UPCASE
6536 if (decc_efs_case_preserve)
6537 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6540 for (cp = trndir; *cp; cp++)
6541 if (islower(*cp)) { haslower = 1; break; }
6543 if (!(sts = (sys$parse(&dirfab)& STS$K_SUCCESS))) {
6544 if ((dirfab.fab$l_sts == RMS$_DIR) || (dirfab.fab$l_sts == RMS$_DNF)) {
6545 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6546 sts = sys$parse(&dirfab) & STS$K_SUCCESS;
6549 PerlMem_free(trndir);
6554 set_vaxc_errno(dirfab.fab$l_sts);
6560 /* Does the file really exist? */
6561 if (!(sys$search(&dirfab)&STS$K_SUCCESS)) {
6562 if (dirfab.fab$l_sts != RMS$_FNF) {
6564 sts1 = rms_free_search_context(&dirfab);
6565 PerlMem_free(trndir);
6570 set_vaxc_errno(dirfab.fab$l_sts);
6573 dirnam = savnam; /* No; just work with potential name */
6576 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6577 /* Yep; check version while we're at it, if it's there. */
6578 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6579 if (strncmp(rms_nam_typel(dirnam),".DIR;1",cmplen)) {
6581 /* Something other than .DIR[;1]. Bzzt. */
6582 sts2 = rms_free_search_context(&dirfab);
6583 PerlMem_free(trndir);
6588 set_vaxc_errno(RMS$_DIR);
6592 /* Make sure we are using the right buffer */
6594 /* We only need one, clean up the other */
6596 my_esa_len = rms_nam_esll(dirnam);
6599 my_esa_len = rms_nam_esl(dirnam);
6602 /* Null terminate the buffer */
6603 my_esa[my_esa_len] = '\0';
6605 /* OK, the type was fine. Now pull any file name into the
6607 if ((cp1 = strrchr(my_esa,']'))) *(rms_nam_typel(dirnam)) = ']';
6609 cp1 = strrchr(my_esa,'>');
6610 *(rms_nam_typel(dirnam)) = '>';
6613 *(rms_nam_typel(dirnam) + 1) = '\0';
6614 retlen = (rms_nam_typel(dirnam)) - my_esa + 2;
6615 if (buf) retpath = buf;
6616 else if (ts) Newx(retpath,retlen,char);
6617 else retpath = __pathify_retbuf;
6618 strcpy(retpath,my_esa);
6622 sts = rms_free_search_context(&dirfab);
6623 /* $PARSE may have upcased filespec, so convert output to lower
6624 * case if input contained any lowercase characters. */
6625 if (haslower && !decc_efs_case_preserve) __mystrtolower(retpath);
6628 PerlMem_free(trndir);
6630 } /* end of do_pathify_dirspec() */
6632 /* External entry points */
6633 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6634 { return do_pathify_dirspec(dir,buf,0,NULL); }
6635 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
6636 { return do_pathify_dirspec(dir,buf,1,NULL); }
6637 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
6638 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
6639 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
6640 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
6642 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
6643 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
6645 static char __tounixspec_retbuf[VMS_MAXRSS];
6646 char *dirend, *rslt, *cp1, *cp3, *tmp;
6648 int devlen, dirlen, retlen = VMS_MAXRSS;
6649 int expand = 1; /* guarantee room for leading and trailing slashes */
6650 unsigned short int trnlnm_iter_count;
6652 if (utf8_fl != NULL)
6655 if (spec == NULL) return NULL;
6656 if (strlen(spec) > (VMS_MAXRSS-1)) return NULL;
6657 if (buf) rslt = buf;
6659 Newx(rslt, VMS_MAXRSS, char);
6661 else rslt = __tounixspec_retbuf;
6663 /* New VMS specific format needs translation
6664 * glob passes filenames with trailing '\n' and expects this preserved.
6666 if (decc_posix_compliant_pathnames) {
6667 if (strncmp(spec, "\"^UP^", 5) == 0) {
6673 tunix = PerlMem_malloc(VMS_MAXRSS);
6674 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6675 strcpy(tunix, spec);
6676 tunix_len = strlen(tunix);
6678 if (tunix[tunix_len - 1] == '\n') {
6679 tunix[tunix_len - 1] = '\"';
6680 tunix[tunix_len] = '\0';
6684 uspec = decc$translate_vms(tunix);
6685 PerlMem_free(tunix);
6686 if ((int)uspec > 0) {
6692 /* If we can not translate it, makemaker wants as-is */
6700 cmp_rslt = 0; /* Presume VMS */
6701 cp1 = strchr(spec, '/');
6705 /* Look for EFS ^/ */
6706 if (decc_efs_charset) {
6707 while (cp1 != NULL) {
6710 /* Found illegal VMS, assume UNIX */
6715 cp1 = strchr(cp1, '/');
6719 /* Look for "." and ".." */
6720 if (decc_filename_unix_report) {
6721 if (spec[0] == '.') {
6722 if ((spec[1] == '\0') || (spec[1] == '\n')) {
6726 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
6732 /* This is already UNIX or at least nothing VMS understands */
6740 dirend = strrchr(spec,']');
6741 if (dirend == NULL) dirend = strrchr(spec,'>');
6742 if (dirend == NULL) dirend = strchr(spec,':');
6743 if (dirend == NULL) {
6748 /* Special case 1 - sys$posix_root = / */
6749 #if __CRTL_VER >= 70000000
6750 if (!decc_disable_posix_root) {
6751 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
6759 /* Special case 2 - Convert NLA0: to /dev/null */
6760 #if __CRTL_VER < 70000000
6761 cmp_rslt = strncmp(spec,"NLA0:", 5);
6763 cmp_rslt = strncmp(spec,"nla0:", 5);
6765 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
6767 if (cmp_rslt == 0) {
6768 strcpy(rslt, "/dev/null");
6771 if (spec[6] != '\0') {
6778 /* Also handle special case "SYS$SCRATCH:" */
6779 #if __CRTL_VER < 70000000
6780 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
6782 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
6784 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
6786 tmp = PerlMem_malloc(VMS_MAXRSS);
6787 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6788 if (cmp_rslt == 0) {
6791 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
6793 strcpy(rslt, "/tmp");
6796 if (spec[12] != '\0') {
6804 if (*cp2 != '[' && *cp2 != '<') {
6807 else { /* the VMS spec begins with directories */
6809 if (*cp2 == ']' || *cp2 == '>') {
6810 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
6814 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
6815 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
6816 if (ts) Safefree(rslt);
6820 trnlnm_iter_count = 0;
6823 while (*cp3 != ':' && *cp3) cp3++;
6825 if (strchr(cp3,']') != NULL) break;
6826 trnlnm_iter_count++;
6827 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
6828 } while (vmstrnenv(tmp,tmp,0,fildev,0));
6830 ((devlen = strlen(tmp)) + (dirlen = strlen(cp2)) + 1 > retlen)) {
6831 retlen = devlen + dirlen;
6832 Renew(rslt,retlen+1+2*expand,char);
6838 *(cp1++) = *(cp3++);
6839 if (cp1 - rslt > (VMS_MAXRSS - 1) && !ts && !buf) {
6841 return NULL; /* No room */
6846 if ((*cp2 == '^')) {
6847 /* EFS file escape, pass the next character as is */
6848 /* Fix me: HEX encoding for Unicode not implemented */
6851 else if ( *cp2 == '.') {
6852 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
6853 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
6860 for (; cp2 <= dirend; cp2++) {
6861 if ((*cp2 == '^')) {
6862 /* EFS file escape, pass the next character as is */
6863 /* Fix me: HEX encoding for Unicode not implemented */
6864 *(cp1++) = *(++cp2);
6865 /* An escaped dot stays as is -- don't convert to slash */
6866 if (*cp2 == '.') cp2++;
6870 if (*(cp2+1) == '[') cp2++;
6872 else if (*cp2 == ']' || *cp2 == '>') {
6873 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
6875 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
6877 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
6878 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
6879 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
6880 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
6881 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
6883 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
6884 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
6888 else if (*cp2 == '-') {
6889 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
6890 while (*cp2 == '-') {
6892 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
6894 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
6895 if (ts) Safefree(rslt); /* filespecs like */
6896 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
6900 else *(cp1++) = *cp2;
6902 else *(cp1++) = *cp2;
6905 if ((*cp2 == '^') && (*(cp2+1) == '.')) cp2++; /* '^.' --> '.' */
6906 *(cp1++) = *(cp2++);
6910 /* This still leaves /000000/ when working with a
6911 * VMS device root or concealed root.
6917 ulen = strlen(rslt);
6919 /* Get rid of "000000/ in rooted filespecs */
6921 zeros = strstr(rslt, "/000000/");
6922 if (zeros != NULL) {
6924 mlen = ulen - (zeros - rslt) - 7;
6925 memmove(zeros, &zeros[7], mlen);
6934 } /* end of do_tounixspec() */
6936 /* External entry points */
6937 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
6938 { return do_tounixspec(spec,buf,0, NULL); }
6939 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
6940 { return do_tounixspec(spec,buf,1, NULL); }
6941 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
6942 { return do_tounixspec(spec,buf,0, utf8_fl); }
6943 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
6944 { return do_tounixspec(spec,buf,1, utf8_fl); }
6946 #if __CRTL_VER >= 70200000 && !defined(__VAX)
6949 This procedure is used to identify if a path is based in either
6950 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
6951 it returns the OpenVMS format directory for it.
6953 It is expecting specifications of only '/' or '/xxxx/'
6955 If a posix root does not exist, or 'xxxx' is not a directory
6956 in the posix root, it returns a failure.
6958 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
6960 It is used only internally by posix_to_vmsspec_hardway().
6963 static int posix_root_to_vms
6964 (char *vmspath, int vmspath_len,
6965 const char *unixpath,
6966 const int * utf8_fl)
6969 struct FAB myfab = cc$rms_fab;
6970 rms_setup_nam(mynam);
6971 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6972 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
6973 char * esa, * esal, * rsa, * rsal;
6980 unixlen = strlen(unixpath);
6985 #if __CRTL_VER >= 80200000
6986 /* If not a posix spec already, convert it */
6987 if (decc_posix_compliant_pathnames) {
6988 if (strncmp(unixpath,"\"^UP^",5) != 0) {
6989 sprintf(vmspath,"\"^UP^%s\"",unixpath);
6992 /* This is already a VMS specification, no conversion */
6994 strncpy(vmspath,unixpath, vmspath_len);
7003 /* Check to see if this is under the POSIX root */
7004 if (decc_disable_posix_root) {
7008 /* Skip leading / */
7009 if (unixpath[0] == '/') {
7015 strcpy(vmspath,"SYS$POSIX_ROOT:");
7017 /* If this is only the / , or blank, then... */
7018 if (unixpath[0] == '\0') {
7019 /* by definition, this is the answer */
7023 /* Need to look up a directory */
7027 /* Copy and add '^' escape characters as needed */
7030 while (unixpath[i] != 0) {
7033 j += copy_expand_unix_filename_escape
7034 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7038 path_len = strlen(vmspath);
7039 if (vmspath[path_len - 1] == '/')
7041 vmspath[path_len] = ']';
7043 vmspath[path_len] = '\0';
7046 vmspath[vmspath_len] = 0;
7047 if (unixpath[unixlen - 1] == '/')
7049 esal = PerlMem_malloc(VMS_MAXRSS);
7050 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7051 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7052 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7053 rsal = PerlMem_malloc(VMS_MAXRSS);
7054 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7055 rsa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7056 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7057 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7058 rms_bind_fab_nam(myfab, mynam);
7059 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7060 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7061 if (decc_efs_case_preserve)
7062 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7063 #ifdef NAML$M_OPEN_SPECIAL
7064 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7067 /* Set up the remaining naml fields */
7068 sts = sys$parse(&myfab);
7070 /* It failed! Try again as a UNIX filespec */
7079 /* get the Device ID and the FID */
7080 sts = sys$search(&myfab);
7082 /* These are no longer needed */
7087 /* on any failure, returned the POSIX ^UP^ filespec */
7092 specdsc.dsc$a_pointer = vmspath;
7093 specdsc.dsc$w_length = vmspath_len;
7095 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7096 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7097 sts = lib$fid_to_name
7098 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7100 /* on any failure, returned the POSIX ^UP^ filespec */
7102 /* This can happen if user does not have permission to read directories */
7103 if (strncmp(unixpath,"\"^UP^",5) != 0)
7104 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7106 strcpy(vmspath, unixpath);
7109 vmspath[specdsc.dsc$w_length] = 0;
7111 /* Are we expecting a directory? */
7112 if (dir_flag != 0) {
7118 i = specdsc.dsc$w_length - 1;
7122 /* Version must be '1' */
7123 if (vmspath[i--] != '1')
7125 /* Version delimiter is one of ".;" */
7126 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7129 if (vmspath[i--] != 'R')
7131 if (vmspath[i--] != 'I')
7133 if (vmspath[i--] != 'D')
7135 if (vmspath[i--] != '.')
7137 eptr = &vmspath[i+1];
7139 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7140 if (vmspath[i-1] != '^') {
7148 /* Get rid of 6 imaginary zero directory filename */
7149 vmspath[i+1] = '\0';
7153 if (vmspath[i] == '0')
7167 /* /dev/mumble needs to be handled special.
7168 /dev/null becomes NLA0:, And there is the potential for other stuff
7169 like /dev/tty which may need to be mapped to something.
7173 slash_dev_special_to_vms
7174 (const char * unixptr,
7184 nextslash = strchr(unixptr, '/');
7185 len = strlen(unixptr);
7186 if (nextslash != NULL)
7187 len = nextslash - unixptr;
7188 cmp = strncmp("null", unixptr, 5);
7190 if (vmspath_len >= 6) {
7191 strcpy(vmspath, "_NLA0:");
7198 /* The built in routines do not understand perl's special needs, so
7199 doing a manual conversion from UNIX to VMS
7201 If the utf8_fl is not null and points to a non-zero value, then
7202 treat 8 bit characters as UTF-8.
7204 The sequence starting with '$(' and ending with ')' will be passed
7205 through with out interpretation instead of being escaped.
7208 static int posix_to_vmsspec_hardway
7209 (char *vmspath, int vmspath_len,
7210 const char *unixpath,
7215 const char *unixptr;
7216 const char *unixend;
7218 const char *lastslash;
7219 const char *lastdot;
7225 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7226 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7228 if (utf8_fl != NULL)
7234 /* Ignore leading "/" characters */
7235 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7238 unixlen = strlen(unixptr);
7240 /* Do nothing with blank paths */
7247 /* This could have a "^UP^ on the front */
7248 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7254 lastslash = strrchr(unixptr,'/');
7255 lastdot = strrchr(unixptr,'.');
7256 unixend = strrchr(unixptr,'\"');
7257 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7258 unixend = unixptr + unixlen;
7261 /* last dot is last dot or past end of string */
7262 if (lastdot == NULL)
7263 lastdot = unixptr + unixlen;
7265 /* if no directories, set last slash to beginning of string */
7266 if (lastslash == NULL) {
7267 lastslash = unixptr;
7270 /* Watch out for trailing "." after last slash, still a directory */
7271 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7272 lastslash = unixptr + unixlen;
7275 /* Watch out for traiing ".." after last slash, still a directory */
7276 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7277 lastslash = unixptr + unixlen;
7280 /* dots in directories are aways escaped */
7281 if (lastdot < lastslash)
7282 lastdot = unixptr + unixlen;
7285 /* if (unixptr < lastslash) then we are in a directory */
7292 /* Start with the UNIX path */
7293 if (*unixptr != '/') {
7294 /* relative paths */
7296 /* If allowing logical names on relative pathnames, then handle here */
7297 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7298 !decc_posix_compliant_pathnames) {
7304 /* Find the next slash */
7305 nextslash = strchr(unixptr,'/');
7307 esa = PerlMem_malloc(vmspath_len);
7308 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7310 trn = PerlMem_malloc(VMS_MAXRSS);
7311 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7313 if (nextslash != NULL) {
7315 seg_len = nextslash - unixptr;
7316 strncpy(esa, unixptr, seg_len);
7320 strcpy(esa, unixptr);
7321 seg_len = strlen(unixptr);
7323 /* trnlnm(section) */
7324 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7327 /* Now fix up the directory */
7329 /* Split up the path to find the components */
7330 sts = vms_split_path
7349 /* A logical name must be a directory or the full
7350 specification. It is only a full specification if
7351 it is the only component */
7352 if ((unixptr[seg_len] == '\0') ||
7353 (unixptr[seg_len+1] == '\0')) {
7355 /* Is a directory being required? */
7356 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7357 /* Not a logical name */
7362 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7363 /* This must be a directory */
7364 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7365 strcpy(vmsptr, esa);
7366 vmslen=strlen(vmsptr);
7367 vmsptr[vmslen] = ':';
7369 vmsptr[vmslen] = '\0';
7377 /* must be dev/directory - ignore version */
7378 if ((n_len + e_len) != 0)
7381 /* transfer the volume */
7382 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7383 strncpy(vmsptr, v_spec, v_len);
7389 /* unroot the rooted directory */
7390 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7392 r_spec[r_len - 1] = ']';
7394 /* This should not be there, but nothing is perfect */
7396 cmp = strcmp(&r_spec[1], "000000.");
7406 strncpy(vmsptr, r_spec, r_len);
7412 /* Bring over the directory. */
7414 ((d_len + vmslen) < vmspath_len)) {
7416 d_spec[d_len - 1] = ']';
7418 cmp = strcmp(&d_spec[1], "000000.");
7429 /* Remove the redundant root */
7437 strncpy(vmsptr, d_spec, d_len);
7451 if (lastslash > unixptr) {
7454 /* skip leading ./ */
7456 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7462 /* Are we still in a directory? */
7463 if (unixptr <= lastslash) {
7468 /* if not backing up, then it is relative forward. */
7469 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7470 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7478 /* Perl wants an empty directory here to tell the difference
7479 * between a DCL commmand and a filename
7488 /* Handle two special files . and .. */
7489 if (unixptr[0] == '.') {
7490 if (&unixptr[1] == unixend) {
7497 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7508 else { /* Absolute PATH handling */
7512 /* Need to find out where root is */
7514 /* In theory, this procedure should never get an absolute POSIX pathname
7515 * that can not be found on the POSIX root.
7516 * In practice, that can not be relied on, and things will show up
7517 * here that are a VMS device name or concealed logical name instead.
7518 * So to make things work, this procedure must be tolerant.
7520 esa = PerlMem_malloc(vmspath_len);
7521 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7524 nextslash = strchr(&unixptr[1],'/');
7526 if (nextslash != NULL) {
7528 seg_len = nextslash - &unixptr[1];
7529 strncpy(vmspath, unixptr, seg_len + 1);
7530 vmspath[seg_len+1] = 0;
7533 cmp = strncmp(vmspath, "dev", 4);
7535 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
7536 if (sts = SS$_NORMAL)
7540 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
7543 if ($VMS_STATUS_SUCCESS(sts)) {
7544 /* This is verified to be a real path */
7546 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
7547 if ($VMS_STATUS_SUCCESS(sts)) {
7548 strcpy(vmspath, esa);
7549 vmslen = strlen(vmspath);
7550 vmsptr = vmspath + vmslen;
7552 if (unixptr < lastslash) {
7561 cmp = strcmp(rptr,"000000.");
7566 } /* removing 6 zeros */
7567 } /* vmslen < 7, no 6 zeros possible */
7568 } /* Not in a directory */
7569 } /* Posix root found */
7571 /* No posix root, fall back to default directory */
7572 strcpy(vmspath, "SYS$DISK:[");
7573 vmsptr = &vmspath[10];
7575 if (unixptr > lastslash) {
7584 } /* end of verified real path handling */
7589 /* Ok, we have a device or a concealed root that is not in POSIX
7590 * or we have garbage. Make the best of it.
7593 /* Posix to VMS destroyed this, so copy it again */
7594 strncpy(vmspath, &unixptr[1], seg_len);
7595 vmspath[seg_len] = 0;
7597 vmsptr = &vmsptr[vmslen];
7600 /* Now do we need to add the fake 6 zero directory to it? */
7602 if ((*lastslash == '/') && (nextslash < lastslash)) {
7603 /* No there is another directory */
7610 /* now we have foo:bar or foo:[000000]bar to decide from */
7611 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
7613 if (!islnm && !decc_posix_compliant_pathnames) {
7615 cmp = strncmp("bin", vmspath, 4);
7617 /* bin => SYS$SYSTEM: */
7618 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
7621 /* tmp => SYS$SCRATCH: */
7622 cmp = strncmp("tmp", vmspath, 4);
7624 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
7629 trnend = islnm ? islnm - 1 : 0;
7631 /* if this was a logical name, ']' or '>' must be present */
7632 /* if not a logical name, then assume a device and hope. */
7633 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
7635 /* if log name and trailing '.' then rooted - treat as device */
7636 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
7638 /* Fix me, if not a logical name, a device lookup should be
7639 * done to see if the device is file structured. If the device
7640 * is not file structured, the 6 zeros should not be put on.
7642 * As it is, perl is occasionally looking for dev:[000000]tty.
7643 * which looks a little strange.
7645 * Not that easy to detect as "/dev" may be file structured with
7646 * special device files.
7649 if ((add_6zero == 0) && (*nextslash == '/') &&
7650 (&nextslash[1] == unixend)) {
7651 /* No real directory present */
7656 /* Put the device delimiter on */
7659 unixptr = nextslash;
7662 /* Start directory if needed */
7663 if (!islnm || add_6zero) {
7669 /* add fake 000000] if needed */
7682 } /* non-POSIX translation */
7684 } /* End of relative/absolute path handling */
7686 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
7693 if (dir_start != 0) {
7695 /* First characters in a directory are handled special */
7696 while ((*unixptr == '/') ||
7697 ((*unixptr == '.') &&
7698 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
7699 (&unixptr[1]==unixend)))) {
7704 /* Skip redundant / in specification */
7705 while ((*unixptr == '/') && (dir_start != 0)) {
7708 if (unixptr == lastslash)
7711 if (unixptr == lastslash)
7714 /* Skip redundant ./ characters */
7715 while ((*unixptr == '.') &&
7716 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
7719 if (unixptr == lastslash)
7721 if (*unixptr == '/')
7724 if (unixptr == lastslash)
7727 /* Skip redundant ../ characters */
7728 while ((*unixptr == '.') && (unixptr[1] == '.') &&
7729 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
7730 /* Set the backing up flag */
7736 unixptr++; /* first . */
7737 unixptr++; /* second . */
7738 if (unixptr == lastslash)
7740 if (*unixptr == '/') /* The slash */
7743 if (unixptr == lastslash)
7746 /* To do: Perl expects /.../ to be translated to [...] on VMS */
7747 /* Not needed when VMS is pretending to be UNIX. */
7749 /* Is this loop stuck because of too many dots? */
7750 if (loop_flag == 0) {
7751 /* Exit the loop and pass the rest through */
7756 /* Are we done with directories yet? */
7757 if (unixptr >= lastslash) {
7759 /* Watch out for trailing dots */
7768 if (*unixptr == '/')
7772 /* Have we stopped backing up? */
7777 /* dir_start continues to be = 1 */
7779 if (*unixptr == '-') {
7781 *vmsptr++ = *unixptr++;
7785 /* Now are we done with directories yet? */
7786 if (unixptr >= lastslash) {
7788 /* Watch out for trailing dots */
7804 if (unixptr >= unixend)
7807 /* Normal characters - More EFS work probably needed */
7813 /* remove multiple / */
7814 while (unixptr[1] == '/') {
7817 if (unixptr == lastslash) {
7818 /* Watch out for trailing dots */
7830 /* To do: Perl expects /.../ to be translated to [...] on VMS */
7831 /* Not needed when VMS is pretending to be UNIX. */
7835 if (unixptr != unixend)
7840 if ((unixptr < lastdot) || (unixptr < lastslash) ||
7841 (&unixptr[1] == unixend)) {
7847 /* trailing dot ==> '^..' on VMS */
7848 if (unixptr == unixend) {
7856 *vmsptr++ = *unixptr++;
7860 if (quoted && (&unixptr[1] == unixend)) {
7864 in_cnt = copy_expand_unix_filename_escape
7865 (vmsptr, unixptr, &out_cnt, utf8_fl);
7875 in_cnt = copy_expand_unix_filename_escape
7876 (vmsptr, unixptr, &out_cnt, utf8_fl);
7883 /* Make sure directory is closed */
7884 if (unixptr == lastslash) {
7886 vmsptr2 = vmsptr - 1;
7888 if (*vmsptr2 != ']') {
7891 /* directories do not end in a dot bracket */
7892 if (*vmsptr2 == '.') {
7896 if (*vmsptr2 != '^') {
7897 vmsptr--; /* back up over the dot */
7905 /* Add a trailing dot if a file with no extension */
7906 vmsptr2 = vmsptr - 1;
7908 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
7909 (*vmsptr2 != ')') && (*lastdot != '.')) {
7920 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
7921 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
7926 /* If a UTF8 flag is being passed, honor it */
7928 if (utf8_fl != NULL) {
7929 utf8_flag = *utf8_fl;
7934 /* If there is a possibility of UTF8, then if any UTF8 characters
7935 are present, then they must be converted to VTF-7
7937 result = strcpy(rslt, path); /* FIX-ME */
7940 result = strcpy(rslt, path);
7946 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
7947 static char *mp_do_tovmsspec
7948 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
7949 static char __tovmsspec_retbuf[VMS_MAXRSS];
7950 char *rslt, *dirend;
7955 unsigned long int infront = 0, hasdir = 1;
7958 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7959 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7961 if (path == NULL) return NULL;
7962 rslt_len = VMS_MAXRSS-1;
7963 if (buf) rslt = buf;
7964 else if (ts) Newx(rslt, VMS_MAXRSS, char);
7965 else rslt = __tovmsspec_retbuf;
7967 /* '.' and '..' are "[]" and "[-]" for a quick check */
7968 if (path[0] == '.') {
7969 if (path[1] == '\0') {
7971 if (utf8_flag != NULL)
7976 if (path[1] == '.' && path[2] == '\0') {
7978 if (utf8_flag != NULL)
7985 /* Posix specifications are now a native VMS format */
7986 /*--------------------------------------------------*/
7987 #if __CRTL_VER >= 80200000 && !defined(__VAX)
7988 if (decc_posix_compliant_pathnames) {
7989 if (strncmp(path,"\"^UP^",5) == 0) {
7990 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
7996 /* This is really the only way to see if this is already in VMS format */
7997 sts = vms_split_path
8012 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8013 replacement, because the above parse just took care of most of
8014 what is needed to do vmspath when the specification is already
8017 And if it is not already, it is easier to do the conversion as
8018 part of this routine than to call this routine and then work on
8022 /* If VMS punctuation was found, it is already VMS format */
8023 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8024 if (utf8_flag != NULL)
8029 /* Now, what to do with trailing "." cases where there is no
8030 extension? If this is a UNIX specification, and EFS characters
8031 are enabled, then the trailing "." should be converted to a "^.".
8032 But if this was already a VMS specification, then it should be
8035 So in the case of ambiguity, leave the specification alone.
8039 /* If there is a possibility of UTF8, then if any UTF8 characters
8040 are present, then they must be converted to VTF-7
8042 if (utf8_flag != NULL)
8048 dirend = strrchr(path,'/');
8050 if (dirend == NULL) {
8051 /* If we get here with no UNIX directory delimiters, then this is
8052 not a complete file specification, either garbage a UNIX glob
8053 specification that can not be converted to a VMS wildcard, or
8054 it a UNIX shell macro. MakeMaker wants these passed through AS-IS,
8055 so apparently other programs expect this also.
8057 utf8 flag setting needs to be preserved.
8063 /* If POSIX mode active, handle the conversion */
8064 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8065 if (decc_efs_charset) {
8066 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8071 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
8072 if (!*(dirend+2)) dirend +=2;
8073 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8074 if (decc_efs_charset == 0) {
8075 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8081 lastdot = strrchr(cp2,'.');
8087 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8089 if (decc_disable_posix_root) {
8090 strcpy(rslt,"sys$disk:[000000]");
8093 strcpy(rslt,"sys$posix_root:[000000]");
8095 if (utf8_flag != NULL)
8099 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8101 trndev = PerlMem_malloc(VMS_MAXRSS);
8102 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8103 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8105 /* DECC special handling */
8107 if (strcmp(rslt,"bin") == 0) {
8108 strcpy(rslt,"sys$system");
8111 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8113 else if (strcmp(rslt,"tmp") == 0) {
8114 strcpy(rslt,"sys$scratch");
8117 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8119 else if (!decc_disable_posix_root) {
8120 strcpy(rslt, "sys$posix_root");
8124 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8125 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8127 else if (strcmp(rslt,"dev") == 0) {
8128 if (strncmp(cp2,"/null", 5) == 0) {
8129 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8130 strcpy(rslt,"NLA0");
8134 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8140 trnend = islnm ? strlen(trndev) - 1 : 0;
8141 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8142 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8143 /* If the first element of the path is a logical name, determine
8144 * whether it has to be translated so we can add more directories. */
8145 if (!islnm || rooted) {
8148 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8152 if (cp2 != dirend) {
8153 strcpy(rslt,trndev);
8154 cp1 = rslt + trnend;
8161 if (decc_disable_posix_root) {
8167 PerlMem_free(trndev);
8172 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8173 cp2 += 2; /* skip over "./" - it's redundant */
8174 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8176 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8177 *(cp1++) = '-'; /* "../" --> "-" */
8180 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8181 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8182 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8183 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8186 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8187 /* Escape the extra dots in EFS file specifications */
8190 if (cp2 > dirend) cp2 = dirend;
8192 else *(cp1++) = '.';
8194 for (; cp2 < dirend; cp2++) {
8196 if (*(cp2-1) == '/') continue;
8197 if (*(cp1-1) != '.') *(cp1++) = '.';
8200 else if (!infront && *cp2 == '.') {
8201 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8202 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
8203 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8204 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
8205 else if (*(cp1-2) == '[') *(cp1-1) = '-';
8206 else { /* back up over previous directory name */
8208 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
8209 if (*(cp1-1) == '[') {
8210 memcpy(cp1,"000000.",7);
8215 if (cp2 == dirend) break;
8217 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8218 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8219 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8220 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8222 *(cp1++) = '.'; /* Simulate trailing '/' */
8223 cp2 += 2; /* for loop will incr this to == dirend */
8225 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8228 if (decc_efs_charset == 0)
8229 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8231 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
8237 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
8239 if (decc_efs_charset == 0)
8246 else *(cp1++) = *cp2;
8250 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8251 if (hasdir) *(cp1++) = ']';
8252 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
8253 /* fixme for ODS5 */
8260 if (decc_efs_charset == 0)
8271 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8272 decc_readdir_dropdotnotype) {
8277 /* trailing dot ==> '^..' on VMS */
8284 *(cp1++) = *(cp2++);
8289 /* This could be a macro to be passed through */
8290 *(cp1++) = *(cp2++);
8292 const char * save_cp2;
8296 /* paranoid check */
8302 *(cp1++) = *(cp2++);
8303 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8304 *(cp1++) = *(cp2++);
8305 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8306 *(cp1++) = *(cp2++);
8309 *(cp1++) = *(cp2++);
8313 if (is_macro == 0) {
8314 /* Not really a macro - never mind */
8327 /* Don't escape again if following character is
8328 * already something we escape.
8330 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8331 *(cp1++) = *(cp2++);
8334 /* But otherwise fall through and escape it. */
8352 *(cp1++) = *(cp2++);
8355 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
8356 * which is wrong. UNIX notation should be ".dir." unless
8357 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8358 * changing this behavior could break more things at this time.
8359 * efs character set effectively does not allow "." to be a version
8360 * delimiter as a further complication about changing this.
8362 if (decc_filename_unix_report != 0) {
8365 *(cp1++) = *(cp2++);
8368 *(cp1++) = *(cp2++);
8371 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8375 /* Fix me for "^]", but that requires making sure that you do
8376 * not back up past the start of the filename
8378 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8383 if (utf8_flag != NULL)
8387 } /* end of do_tovmsspec() */
8389 /* External entry points */
8390 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8391 { return do_tovmsspec(path,buf,0,NULL); }
8392 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8393 { return do_tovmsspec(path,buf,1,NULL); }
8394 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8395 { return do_tovmsspec(path,buf,0,utf8_fl); }
8396 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8397 { return do_tovmsspec(path,buf,1,utf8_fl); }
8399 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8400 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8401 static char __tovmspath_retbuf[VMS_MAXRSS];
8403 char *pathified, *vmsified, *cp;
8405 if (path == NULL) return NULL;
8406 pathified = PerlMem_malloc(VMS_MAXRSS);
8407 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8408 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
8409 PerlMem_free(pathified);
8415 Newx(vmsified, VMS_MAXRSS, char);
8416 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8417 PerlMem_free(pathified);
8418 if (vmsified) Safefree(vmsified);
8421 PerlMem_free(pathified);
8426 vmslen = strlen(vmsified);
8427 Newx(cp,vmslen+1,char);
8428 memcpy(cp,vmsified,vmslen);
8434 strcpy(__tovmspath_retbuf,vmsified);
8436 return __tovmspath_retbuf;
8439 } /* end of do_tovmspath() */
8441 /* External entry points */
8442 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
8443 { return do_tovmspath(path,buf,0, NULL); }
8444 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
8445 { return do_tovmspath(path,buf,1, NULL); }
8446 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
8447 { return do_tovmspath(path,buf,0,utf8_fl); }
8448 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
8449 { return do_tovmspath(path,buf,1,utf8_fl); }
8452 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
8453 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8454 static char __tounixpath_retbuf[VMS_MAXRSS];
8456 char *pathified, *unixified, *cp;
8458 if (path == NULL) return NULL;
8459 pathified = PerlMem_malloc(VMS_MAXRSS);
8460 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8461 if (do_pathify_dirspec(path,pathified,0,NULL) == NULL) {
8462 PerlMem_free(pathified);
8468 Newx(unixified, VMS_MAXRSS, char);
8470 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
8471 PerlMem_free(pathified);
8472 if (unixified) Safefree(unixified);
8475 PerlMem_free(pathified);
8480 unixlen = strlen(unixified);
8481 Newx(cp,unixlen+1,char);
8482 memcpy(cp,unixified,unixlen);
8484 Safefree(unixified);
8488 strcpy(__tounixpath_retbuf,unixified);
8489 Safefree(unixified);
8490 return __tounixpath_retbuf;
8493 } /* end of do_tounixpath() */
8495 /* External entry points */
8496 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
8497 { return do_tounixpath(path,buf,0,NULL); }
8498 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
8499 { return do_tounixpath(path,buf,1,NULL); }
8500 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8501 { return do_tounixpath(path,buf,0,utf8_fl); }
8502 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8503 { return do_tounixpath(path,buf,1,utf8_fl); }
8506 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
8508 *****************************************************************************
8510 * Copyright (C) 1989-1994, 2007 by *
8511 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
8513 * Permission is hereby granted for the reproduction of this software *
8514 * on condition that this copyright notice is included in source *
8515 * distributions of the software. The code may be modified and *
8516 * distributed under the same terms as Perl itself. *
8518 * 27-Aug-1994 Modified for inclusion in perl5 *
8519 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
8520 *****************************************************************************
8524 * getredirection() is intended to aid in porting C programs
8525 * to VMS (Vax-11 C). The native VMS environment does not support
8526 * '>' and '<' I/O redirection, or command line wild card expansion,
8527 * or a command line pipe mechanism using the '|' AND background
8528 * command execution '&'. All of these capabilities are provided to any
8529 * C program which calls this procedure as the first thing in the
8531 * The piping mechanism will probably work with almost any 'filter' type
8532 * of program. With suitable modification, it may useful for other
8533 * portability problems as well.
8535 * Author: Mark Pizzolato (mark AT infocomm DOT com)
8539 struct list_item *next;
8543 static void add_item(struct list_item **head,
8544 struct list_item **tail,
8548 static void mp_expand_wild_cards(pTHX_ char *item,
8549 struct list_item **head,
8550 struct list_item **tail,
8553 static int background_process(pTHX_ int argc, char **argv);
8555 static void pipe_and_fork(pTHX_ char **cmargv);
8557 /*{{{ void getredirection(int *ac, char ***av)*/
8559 mp_getredirection(pTHX_ int *ac, char ***av)
8561 * Process vms redirection arg's. Exit if any error is seen.
8562 * If getredirection() processes an argument, it is erased
8563 * from the vector. getredirection() returns a new argc and argv value.
8564 * In the event that a background command is requested (by a trailing "&"),
8565 * this routine creates a background subprocess, and simply exits the program.
8567 * Warning: do not try to simplify the code for vms. The code
8568 * presupposes that getredirection() is called before any data is
8569 * read from stdin or written to stdout.
8571 * Normal usage is as follows:
8577 * getredirection(&argc, &argv);
8581 int argc = *ac; /* Argument Count */
8582 char **argv = *av; /* Argument Vector */
8583 char *ap; /* Argument pointer */
8584 int j; /* argv[] index */
8585 int item_count = 0; /* Count of Items in List */
8586 struct list_item *list_head = 0; /* First Item in List */
8587 struct list_item *list_tail; /* Last Item in List */
8588 char *in = NULL; /* Input File Name */
8589 char *out = NULL; /* Output File Name */
8590 char *outmode = "w"; /* Mode to Open Output File */
8591 char *err = NULL; /* Error File Name */
8592 char *errmode = "w"; /* Mode to Open Error File */
8593 int cmargc = 0; /* Piped Command Arg Count */
8594 char **cmargv = NULL;/* Piped Command Arg Vector */
8597 * First handle the case where the last thing on the line ends with
8598 * a '&'. This indicates the desire for the command to be run in a
8599 * subprocess, so we satisfy that desire.
8602 if (0 == strcmp("&", ap))
8603 exit(background_process(aTHX_ --argc, argv));
8604 if (*ap && '&' == ap[strlen(ap)-1])
8606 ap[strlen(ap)-1] = '\0';
8607 exit(background_process(aTHX_ argc, argv));
8610 * Now we handle the general redirection cases that involve '>', '>>',
8611 * '<', and pipes '|'.
8613 for (j = 0; j < argc; ++j)
8615 if (0 == strcmp("<", argv[j]))
8619 fprintf(stderr,"No input file after < on command line");
8620 exit(LIB$_WRONUMARG);
8625 if ('<' == *(ap = argv[j]))
8630 if (0 == strcmp(">", ap))
8634 fprintf(stderr,"No output file after > on command line");
8635 exit(LIB$_WRONUMARG);
8654 fprintf(stderr,"No output file after > or >> on command line");
8655 exit(LIB$_WRONUMARG);
8659 if (('2' == *ap) && ('>' == ap[1]))
8676 fprintf(stderr,"No output file after 2> or 2>> on command line");
8677 exit(LIB$_WRONUMARG);
8681 if (0 == strcmp("|", argv[j]))
8685 fprintf(stderr,"No command into which to pipe on command line");
8686 exit(LIB$_WRONUMARG);
8688 cmargc = argc-(j+1);
8689 cmargv = &argv[j+1];
8693 if ('|' == *(ap = argv[j]))
8701 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
8704 * Allocate and fill in the new argument vector, Some Unix's terminate
8705 * the list with an extra null pointer.
8707 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
8708 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8710 for (j = 0; j < item_count; ++j, list_head = list_head->next)
8711 argv[j] = list_head->value;
8717 fprintf(stderr,"'|' and '>' may not both be specified on command line");
8718 exit(LIB$_INVARGORD);
8720 pipe_and_fork(aTHX_ cmargv);
8723 /* Check for input from a pipe (mailbox) */
8725 if (in == NULL && 1 == isapipe(0))
8727 char mbxname[L_tmpnam];
8729 long int dvi_item = DVI$_DEVBUFSIZ;
8730 $DESCRIPTOR(mbxnam, "");
8731 $DESCRIPTOR(mbxdevnam, "");
8733 /* Input from a pipe, reopen it in binary mode to disable */
8734 /* carriage control processing. */
8736 fgetname(stdin, mbxname);
8737 mbxnam.dsc$a_pointer = mbxname;
8738 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
8739 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
8740 mbxdevnam.dsc$a_pointer = mbxname;
8741 mbxdevnam.dsc$w_length = sizeof(mbxname);
8742 dvi_item = DVI$_DEVNAM;
8743 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
8744 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
8747 freopen(mbxname, "rb", stdin);
8750 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
8754 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
8756 fprintf(stderr,"Can't open input file %s as stdin",in);
8759 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
8761 fprintf(stderr,"Can't open output file %s as stdout",out);
8764 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
8767 if (strcmp(err,"&1") == 0) {
8768 dup2(fileno(stdout), fileno(stderr));
8769 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
8772 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
8774 fprintf(stderr,"Can't open error file %s as stderr",err);
8778 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
8782 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
8785 #ifdef ARGPROC_DEBUG
8786 PerlIO_printf(Perl_debug_log, "Arglist:\n");
8787 for (j = 0; j < *ac; ++j)
8788 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
8790 /* Clear errors we may have hit expanding wildcards, so they don't
8791 show up in Perl's $! later */
8792 set_errno(0); set_vaxc_errno(1);
8793 } /* end of getredirection() */
8796 static void add_item(struct list_item **head,
8797 struct list_item **tail,
8803 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
8804 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8808 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
8809 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8810 *tail = (*tail)->next;
8812 (*tail)->value = value;
8816 static void mp_expand_wild_cards(pTHX_ char *item,
8817 struct list_item **head,
8818 struct list_item **tail,
8822 unsigned long int context = 0;
8830 $DESCRIPTOR(filespec, "");
8831 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
8832 $DESCRIPTOR(resultspec, "");
8833 unsigned long int lff_flags = 0;
8837 #ifdef VMS_LONGNAME_SUPPORT
8838 lff_flags = LIB$M_FIL_LONG_NAMES;
8841 for (cp = item; *cp; cp++) {
8842 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
8843 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
8845 if (!*cp || isspace(*cp))
8847 add_item(head, tail, item, count);
8852 /* "double quoted" wild card expressions pass as is */
8853 /* From DCL that means using e.g.: */
8854 /* perl program """perl.*""" */
8855 item_len = strlen(item);
8856 if ( '"' == *item && '"' == item[item_len-1] )
8859 item[item_len-2] = '\0';
8860 add_item(head, tail, item, count);
8864 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
8865 resultspec.dsc$b_class = DSC$K_CLASS_D;
8866 resultspec.dsc$a_pointer = NULL;
8867 vmsspec = PerlMem_malloc(VMS_MAXRSS);
8868 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8869 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
8870 filespec.dsc$a_pointer = do_tovmsspec(item,vmsspec,0,NULL);
8871 if (!isunix || !filespec.dsc$a_pointer)
8872 filespec.dsc$a_pointer = item;
8873 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
8875 * Only return version specs, if the caller specified a version
8877 had_version = strchr(item, ';');
8879 * Only return device and directory specs, if the caller specifed either.
8881 had_device = strchr(item, ':');
8882 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
8884 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
8885 (&filespec, &resultspec, &context,
8886 &defaultspec, 0, &rms_sts, &lff_flags)))
8891 string = PerlMem_malloc(resultspec.dsc$w_length+1);
8892 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8893 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
8894 string[resultspec.dsc$w_length] = '\0';
8895 if (NULL == had_version)
8896 *(strrchr(string, ';')) = '\0';
8897 if ((!had_directory) && (had_device == NULL))
8899 if (NULL == (devdir = strrchr(string, ']')))
8900 devdir = strrchr(string, '>');
8901 strcpy(string, devdir + 1);
8904 * Be consistent with what the C RTL has already done to the rest of
8905 * the argv items and lowercase all of these names.
8907 if (!decc_efs_case_preserve) {
8908 for (c = string; *c; ++c)
8912 if (isunix) trim_unixpath(string,item,1);
8913 add_item(head, tail, string, count);
8916 PerlMem_free(vmsspec);
8917 if (sts != RMS$_NMF)
8919 set_vaxc_errno(sts);
8922 case RMS$_FNF: case RMS$_DNF:
8923 set_errno(ENOENT); break;
8925 set_errno(ENOTDIR); break;
8927 set_errno(ENODEV); break;
8928 case RMS$_FNM: case RMS$_SYN:
8929 set_errno(EINVAL); break;
8931 set_errno(EACCES); break;
8933 _ckvmssts_noperl(sts);
8937 add_item(head, tail, item, count);
8938 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
8939 _ckvmssts_noperl(lib$find_file_end(&context));
8942 static int child_st[2];/* Event Flag set when child process completes */
8944 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
8946 static unsigned long int exit_handler(int *status)
8950 if (0 == child_st[0])
8952 #ifdef ARGPROC_DEBUG
8953 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
8955 fflush(stdout); /* Have to flush pipe for binary data to */
8956 /* terminate properly -- <tp@mccall.com> */
8957 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
8958 sys$dassgn(child_chan);
8960 sys$synch(0, child_st);
8965 static void sig_child(int chan)
8967 #ifdef ARGPROC_DEBUG
8968 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
8970 if (child_st[0] == 0)
8974 static struct exit_control_block exit_block =
8979 &exit_block.exit_status,
8984 pipe_and_fork(pTHX_ char **cmargv)
8987 struct dsc$descriptor_s *vmscmd;
8988 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
8989 int sts, j, l, ismcr, quote, tquote = 0;
8991 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
8992 vms_execfree(vmscmd);
8997 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
8998 && toupper(*(q+2)) == 'R' && !*(q+3);
9000 while (q && l < MAX_DCL_LINE_LENGTH) {
9002 if (j > 0 && quote) {
9008 if (ismcr && j > 1) quote = 1;
9009 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9012 if (quote || tquote) {
9018 if ((quote||tquote) && *q == '"') {
9028 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9030 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9034 static int background_process(pTHX_ int argc, char **argv)
9036 char command[MAX_DCL_SYMBOL + 1] = "$";
9037 $DESCRIPTOR(value, "");
9038 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9039 static $DESCRIPTOR(null, "NLA0:");
9040 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9042 $DESCRIPTOR(pidstr, "");
9044 unsigned long int flags = 17, one = 1, retsts;
9047 strcat(command, argv[0]);
9048 len = strlen(command);
9049 while (--argc && (len < MAX_DCL_SYMBOL))
9051 strcat(command, " \"");
9052 strcat(command, *(++argv));
9053 strcat(command, "\"");
9054 len = strlen(command);
9056 value.dsc$a_pointer = command;
9057 value.dsc$w_length = strlen(value.dsc$a_pointer);
9058 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9059 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9060 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9061 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9064 _ckvmssts_noperl(retsts);
9066 #ifdef ARGPROC_DEBUG
9067 PerlIO_printf(Perl_debug_log, "%s\n", command);
9069 sprintf(pidstring, "%08X", pid);
9070 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9071 pidstr.dsc$a_pointer = pidstring;
9072 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9073 lib$set_symbol(&pidsymbol, &pidstr);
9077 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9080 /* OS-specific initialization at image activation (not thread startup) */
9081 /* Older VAXC header files lack these constants */
9082 #ifndef JPI$_RIGHTS_SIZE
9083 # define JPI$_RIGHTS_SIZE 817
9085 #ifndef KGB$M_SUBSYSTEM
9086 # define KGB$M_SUBSYSTEM 0x8
9089 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9091 /*{{{void vms_image_init(int *, char ***)*/
9093 vms_image_init(int *argcp, char ***argvp)
9097 char eqv[LNM$C_NAMLENGTH+1] = "";
9098 unsigned int len, tabct = 8, tabidx = 0;
9099 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9100 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9101 unsigned short int dummy, rlen;
9102 struct dsc$descriptor_s **tabvec;
9103 #if defined(PERL_IMPLICIT_CONTEXT)
9106 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9107 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9108 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9111 #ifdef KILL_BY_SIGPRC
9112 Perl_csighandler_init();
9115 /* This was moved from the pre-image init handler because on threaded */
9116 /* Perl it was always returning 0 for the default value. */
9117 status = simple_trnlnm("SYS$POSIX_ROOT", val_str, sizeof(val_str));
9120 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9123 initial = decc$feature_get_value(s, 4);
9125 /* initial is -1 if nothing has set the feature */
9126 /* initial is 1 if the logical name is present */
9127 decc_disable_posix_root = decc$feature_get_value(s, 1);
9129 /* If the value is not valid, force the feature off */
9130 if (decc_disable_posix_root < 0) {
9131 decc$feature_set_value(s, 1, 1);
9132 decc_disable_posix_root = 1;
9136 /* Traditionally Perl assumes this is off */
9137 decc_disable_posix_root = 1;
9138 decc$feature_set_value(s, 1, 1);
9144 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9145 _ckvmssts_noperl(iosb[0]);
9146 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9147 if (iprv[i]) { /* Running image installed with privs? */
9148 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
9153 /* Rights identifiers might trigger tainting as well. */
9154 if (!will_taint && (rlen || rsz)) {
9155 while (rlen < rsz) {
9156 /* We didn't get all the identifiers on the first pass. Allocate a
9157 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9158 * were needed to hold all identifiers at time of last call; we'll
9159 * allocate that many unsigned long ints), and go back and get 'em.
9160 * If it gave us less than it wanted to despite ample buffer space,
9161 * something's broken. Is your system missing a system identifier?
9163 if (rsz <= jpilist[1].buflen) {
9164 /* Perl_croak accvios when used this early in startup. */
9165 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9166 rsz, (unsigned long) jpilist[1].buflen,
9167 "Check your rights database for corruption.\n");
9170 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9171 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9172 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9173 jpilist[1].buflen = rsz * sizeof(unsigned long int);
9174 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9175 _ckvmssts_noperl(iosb[0]);
9177 mask = jpilist[1].bufadr;
9178 /* Check attribute flags for each identifier (2nd longword); protected
9179 * subsystem identifiers trigger tainting.
9181 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9182 if (mask[i] & KGB$M_SUBSYSTEM) {
9187 if (mask != rlst) PerlMem_free(mask);
9190 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9191 * logical, some versions of the CRTL will add a phanthom /000000/
9192 * directory. This needs to be removed.
9194 if (decc_filename_unix_report) {
9197 ulen = strlen(argvp[0][0]);
9199 zeros = strstr(argvp[0][0], "/000000/");
9200 if (zeros != NULL) {
9202 mlen = ulen - (zeros - argvp[0][0]) - 7;
9203 memmove(zeros, &zeros[7], mlen);
9205 argvp[0][0][ulen] = '\0';
9208 /* It also may have a trailing dot that needs to be removed otherwise
9209 * it will be converted to VMS mode incorrectly.
9212 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9213 argvp[0][0][ulen] = '\0';
9216 /* We need to use this hack to tell Perl it should run with tainting,
9217 * since its tainting flag may be part of the PL_curinterp struct, which
9218 * hasn't been allocated when vms_image_init() is called.
9221 char **newargv, **oldargv;
9223 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9224 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9225 newargv[0] = oldargv[0];
9226 newargv[1] = PerlMem_malloc(3 * sizeof(char));
9227 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9228 strcpy(newargv[1], "-T");
9229 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9231 newargv[*argcp] = NULL;
9232 /* We orphan the old argv, since we don't know where it's come from,
9233 * so we don't know how to free it.
9237 else { /* Did user explicitly request tainting? */
9239 char *cp, **av = *argvp;
9240 for (i = 1; i < *argcp; i++) {
9241 if (*av[i] != '-') break;
9242 for (cp = av[i]+1; *cp; cp++) {
9243 if (*cp == 'T') { will_taint = 1; break; }
9244 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9245 strchr("DFIiMmx",*cp)) break;
9247 if (will_taint) break;
9252 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9255 tabvec = (struct dsc$descriptor_s **)
9256 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9257 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9259 else if (tabidx >= tabct) {
9261 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9262 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9264 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9265 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9266 tabvec[tabidx]->dsc$w_length = 0;
9267 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9268 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
9269 tabvec[tabidx]->dsc$a_pointer = NULL;
9270 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
9272 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9274 getredirection(argcp,argvp);
9275 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9277 # include <reentrancy.h>
9278 decc$set_reentrancy(C$C_MULTITHREAD);
9287 * Trim Unix-style prefix off filespec, so it looks like what a shell
9288 * glob expansion would return (i.e. from specified prefix on, not
9289 * full path). Note that returned filespec is Unix-style, regardless
9290 * of whether input filespec was VMS-style or Unix-style.
9292 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9293 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9294 * vector of options; at present, only bit 0 is used, and if set tells
9295 * trim unixpath to try the current default directory as a prefix when
9296 * presented with a possibly ambiguous ... wildcard.
9298 * Returns !=0 on success, with trimmed filespec replacing contents of
9299 * fspec, and 0 on failure, with contents of fpsec unchanged.
9301 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9303 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9305 char *unixified, *unixwild,
9306 *template, *base, *end, *cp1, *cp2;
9307 register int tmplen, reslen = 0, dirs = 0;
9309 if (!wildspec || !fspec) return 0;
9311 unixwild = PerlMem_malloc(VMS_MAXRSS);
9312 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9313 template = unixwild;
9314 if (strpbrk(wildspec,"]>:") != NULL) {
9315 if (do_tounixspec(wildspec,unixwild,0,NULL) == NULL) {
9316 PerlMem_free(unixwild);
9321 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
9322 unixwild[VMS_MAXRSS-1] = 0;
9324 unixified = PerlMem_malloc(VMS_MAXRSS);
9325 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9326 if (strpbrk(fspec,"]>:") != NULL) {
9327 if (do_tounixspec(fspec,unixified,0,NULL) == NULL) {
9328 PerlMem_free(unixwild);
9329 PerlMem_free(unixified);
9332 else base = unixified;
9333 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9334 * check to see that final result fits into (isn't longer than) fspec */
9335 reslen = strlen(fspec);
9339 /* No prefix or absolute path on wildcard, so nothing to remove */
9340 if (!*template || *template == '/') {
9341 PerlMem_free(unixwild);
9342 if (base == fspec) {
9343 PerlMem_free(unixified);
9346 tmplen = strlen(unixified);
9347 if (tmplen > reslen) {
9348 PerlMem_free(unixified);
9349 return 0; /* not enough space */
9351 /* Copy unixified resultant, including trailing NUL */
9352 memmove(fspec,unixified,tmplen+1);
9353 PerlMem_free(unixified);
9357 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9358 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
9359 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
9360 for (cp1 = end ;cp1 >= base; cp1--)
9361 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9363 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9364 PerlMem_free(unixified);
9365 PerlMem_free(unixwild);
9370 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9371 int ells = 1, totells, segdirs, match;
9372 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9373 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9375 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9377 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9378 tpl = PerlMem_malloc(VMS_MAXRSS);
9379 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9380 if (ellipsis == template && opts & 1) {
9381 /* Template begins with an ellipsis. Since we can't tell how many
9382 * directory names at the front of the resultant to keep for an
9383 * arbitrary starting point, we arbitrarily choose the current
9384 * default directory as a starting point. If it's there as a prefix,
9385 * clip it off. If not, fall through and act as if the leading
9386 * ellipsis weren't there (i.e. return shortest possible path that
9387 * could match template).
9389 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9391 PerlMem_free(unixified);
9392 PerlMem_free(unixwild);
9395 if (!decc_efs_case_preserve) {
9396 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9397 if (_tolower(*cp1) != _tolower(*cp2)) break;
9399 segdirs = dirs - totells; /* Min # of dirs we must have left */
9400 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9401 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9402 memmove(fspec,cp2+1,end - cp2);
9404 PerlMem_free(unixified);
9405 PerlMem_free(unixwild);
9409 /* First off, back up over constant elements at end of path */
9411 for (front = end ; front >= base; front--)
9412 if (*front == '/' && !dirs--) { front++; break; }
9414 lcres = PerlMem_malloc(VMS_MAXRSS);
9415 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9416 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9418 if (!decc_efs_case_preserve) {
9419 *cp2 = _tolower(*cp1); /* Make lc copy for match */
9427 PerlMem_free(unixified);
9428 PerlMem_free(unixwild);
9429 PerlMem_free(lcres);
9430 return 0; /* Path too long. */
9433 *cp2 = '\0'; /* Pick up with memcpy later */
9434 lcfront = lcres + (front - base);
9435 /* Now skip over each ellipsis and try to match the path in front of it. */
9437 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
9438 if (*(cp1) == '.' && *(cp1+1) == '.' &&
9439 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
9440 if (cp1 < template) break; /* template started with an ellipsis */
9441 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
9442 ellipsis = cp1; continue;
9444 wilddsc.dsc$a_pointer = tpl;
9445 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
9447 for (segdirs = 0, cp2 = tpl;
9448 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
9450 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
9452 if (!decc_efs_case_preserve) {
9453 *cp2 = _tolower(*cp1); /* else lowercase for match */
9456 *cp2 = *cp1; /* else preserve case for match */
9459 if (*cp2 == '/') segdirs++;
9461 if (cp1 != ellipsis - 1) {
9463 PerlMem_free(unixified);
9464 PerlMem_free(unixwild);
9465 PerlMem_free(lcres);
9466 return 0; /* Path too long */
9468 /* Back up at least as many dirs as in template before matching */
9469 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
9470 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
9471 for (match = 0; cp1 > lcres;) {
9472 resdsc.dsc$a_pointer = cp1;
9473 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
9475 if (match == 1) lcfront = cp1;
9477 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
9481 PerlMem_free(unixified);
9482 PerlMem_free(unixwild);
9483 PerlMem_free(lcres);
9484 return 0; /* Can't find prefix ??? */
9486 if (match > 1 && opts & 1) {
9487 /* This ... wildcard could cover more than one set of dirs (i.e.
9488 * a set of similar dir names is repeated). If the template
9489 * contains more than 1 ..., upstream elements could resolve the
9490 * ambiguity, but it's not worth a full backtracking setup here.
9491 * As a quick heuristic, clip off the current default directory
9492 * if it's present to find the trimmed spec, else use the
9493 * shortest string that this ... could cover.
9495 char def[NAM$C_MAXRSS+1], *st;
9497 if (getcwd(def, sizeof def,0) == NULL) {
9498 PerlMem_free(unixified);
9499 PerlMem_free(unixwild);
9500 PerlMem_free(lcres);
9504 if (!decc_efs_case_preserve) {
9505 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9506 if (_tolower(*cp1) != _tolower(*cp2)) break;
9508 segdirs = dirs - totells; /* Min # of dirs we must have left */
9509 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
9510 if (*cp1 == '\0' && *cp2 == '/') {
9511 memmove(fspec,cp2+1,end - cp2);
9513 PerlMem_free(unixified);
9514 PerlMem_free(unixwild);
9515 PerlMem_free(lcres);
9518 /* Nope -- stick with lcfront from above and keep going. */
9521 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
9523 PerlMem_free(unixified);
9524 PerlMem_free(unixwild);
9525 PerlMem_free(lcres);
9530 } /* end of trim_unixpath() */
9535 * VMS readdir() routines.
9536 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
9538 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
9539 * Minor modifications to original routines.
9542 /* readdir may have been redefined by reentr.h, so make sure we get
9543 * the local version for what we do here.
9548 #if !defined(PERL_IMPLICIT_CONTEXT)
9549 # define readdir Perl_readdir
9551 # define readdir(a) Perl_readdir(aTHX_ a)
9554 /* Number of elements in vms_versions array */
9555 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
9558 * Open a directory, return a handle for later use.
9560 /*{{{ DIR *opendir(char*name) */
9562 Perl_opendir(pTHX_ const char *name)
9568 Newx(dir, VMS_MAXRSS, char);
9569 if (do_tovmspath(name,dir,0,NULL) == NULL) {
9573 /* Check access before stat; otherwise stat does not
9574 * accurately report whether it's a directory.
9576 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
9577 /* cando_by_name has already set errno */
9581 if (flex_stat(dir,&sb) == -1) return NULL;
9582 if (!S_ISDIR(sb.st_mode)) {
9584 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
9587 /* Get memory for the handle, and the pattern. */
9589 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
9591 /* Fill in the fields; mainly playing with the descriptor. */
9592 sprintf(dd->pattern, "%s*.*",dir);
9597 /* By saying we always want the result of readdir() in unix format, we
9598 * are really saying we want all the escapes removed. Otherwise the caller,
9599 * having no way to know whether it's already in VMS format, might send it
9600 * through tovmsspec again, thus double escaping.
9602 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
9603 dd->pat.dsc$a_pointer = dd->pattern;
9604 dd->pat.dsc$w_length = strlen(dd->pattern);
9605 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
9606 dd->pat.dsc$b_class = DSC$K_CLASS_S;
9607 #if defined(USE_ITHREADS)
9608 Newx(dd->mutex,1,perl_mutex);
9609 MUTEX_INIT( (perl_mutex *) dd->mutex );
9615 } /* end of opendir() */
9619 * Set the flag to indicate we want versions or not.
9621 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
9623 vmsreaddirversions(DIR *dd, int flag)
9626 dd->flags |= PERL_VMSDIR_M_VERSIONS;
9628 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
9633 * Free up an opened directory.
9635 /*{{{ void closedir(DIR *dd)*/
9637 Perl_closedir(DIR *dd)
9641 sts = lib$find_file_end(&dd->context);
9642 Safefree(dd->pattern);
9643 #if defined(USE_ITHREADS)
9644 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
9645 Safefree(dd->mutex);
9652 * Collect all the version numbers for the current file.
9655 collectversions(pTHX_ DIR *dd)
9657 struct dsc$descriptor_s pat;
9658 struct dsc$descriptor_s res;
9660 char *p, *text, *buff;
9662 unsigned long context, tmpsts;
9664 /* Convenient shorthand. */
9667 /* Add the version wildcard, ignoring the "*.*" put on before */
9668 i = strlen(dd->pattern);
9669 Newx(text,i + e->d_namlen + 3,char);
9670 strcpy(text, dd->pattern);
9671 sprintf(&text[i - 3], "%s;*", e->d_name);
9673 /* Set up the pattern descriptor. */
9674 pat.dsc$a_pointer = text;
9675 pat.dsc$w_length = i + e->d_namlen - 1;
9676 pat.dsc$b_dtype = DSC$K_DTYPE_T;
9677 pat.dsc$b_class = DSC$K_CLASS_S;
9679 /* Set up result descriptor. */
9680 Newx(buff, VMS_MAXRSS, char);
9681 res.dsc$a_pointer = buff;
9682 res.dsc$w_length = VMS_MAXRSS - 1;
9683 res.dsc$b_dtype = DSC$K_DTYPE_T;
9684 res.dsc$b_class = DSC$K_CLASS_S;
9686 /* Read files, collecting versions. */
9687 for (context = 0, e->vms_verscount = 0;
9688 e->vms_verscount < VERSIZE(e);
9689 e->vms_verscount++) {
9691 unsigned long flags = 0;
9693 #ifdef VMS_LONGNAME_SUPPORT
9694 flags = LIB$M_FIL_LONG_NAMES;
9696 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
9697 if (tmpsts == RMS$_NMF || context == 0) break;
9699 buff[VMS_MAXRSS - 1] = '\0';
9700 if ((p = strchr(buff, ';')))
9701 e->vms_versions[e->vms_verscount] = atoi(p + 1);
9703 e->vms_versions[e->vms_verscount] = -1;
9706 _ckvmssts(lib$find_file_end(&context));
9710 } /* end of collectversions() */
9713 * Read the next entry from the directory.
9715 /*{{{ struct dirent *readdir(DIR *dd)*/
9717 Perl_readdir(pTHX_ DIR *dd)
9719 struct dsc$descriptor_s res;
9721 unsigned long int tmpsts;
9723 unsigned long flags = 0;
9724 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
9725 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
9727 /* Set up result descriptor, and get next file. */
9728 Newx(buff, VMS_MAXRSS, char);
9729 res.dsc$a_pointer = buff;
9730 res.dsc$w_length = VMS_MAXRSS - 1;
9731 res.dsc$b_dtype = DSC$K_DTYPE_T;
9732 res.dsc$b_class = DSC$K_CLASS_S;
9734 #ifdef VMS_LONGNAME_SUPPORT
9735 flags = LIB$M_FIL_LONG_NAMES;
9738 tmpsts = lib$find_file
9739 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
9740 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
9741 if (!(tmpsts & 1)) {
9742 set_vaxc_errno(tmpsts);
9745 set_errno(EACCES); break;
9747 set_errno(ENODEV); break;
9749 set_errno(ENOTDIR); break;
9750 case RMS$_FNF: case RMS$_DNF:
9751 set_errno(ENOENT); break;
9759 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
9760 buff[res.dsc$w_length] = '\0';
9761 p = buff + res.dsc$w_length;
9762 while (--p >= buff) if (!isspace(*p)) break;
9764 if (!decc_efs_case_preserve) {
9765 for (p = buff; *p; p++) *p = _tolower(*p);
9768 /* Skip any directory component and just copy the name. */
9769 sts = vms_split_path
9784 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
9786 /* In Unix report mode, remove the ".dir;1" from the name */
9787 /* if it is a real directory. */
9788 if (decc_filename_unix_report || decc_efs_charset) {
9789 if ((e_len == 4) && (vs_len == 2) && (vs_spec[1] == '1')) {
9790 if ((toupper(e_spec[1]) == 'D') &&
9791 (toupper(e_spec[2]) == 'I') &&
9792 (toupper(e_spec[3]) == 'R')) {
9796 ret_sts = stat(buff, (stat_t *)&statbuf);
9797 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
9805 /* Drop NULL extensions on UNIX file specification */
9806 if ((e_len == 1) && decc_readdir_dropdotnotype) {
9812 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
9813 dd->entry.d_name[n_len + e_len] = '\0';
9814 dd->entry.d_namlen = strlen(dd->entry.d_name);
9816 /* Convert the filename to UNIX format if needed */
9817 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
9819 /* Translate the encoded characters. */
9820 /* Fixme: Unicode handling could result in embedded 0 characters */
9821 if (strchr(dd->entry.d_name, '^') != NULL) {
9824 p = dd->entry.d_name;
9827 int inchars_read, outchars_added;
9828 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
9830 q += outchars_added;
9832 /* if outchars_added > 1, then this is a wide file specification */
9833 /* Wide file specifications need to be passed in Perl */
9834 /* counted strings apparently with a Unicode flag */
9837 strcpy(dd->entry.d_name, new_name);
9838 dd->entry.d_namlen = strlen(dd->entry.d_name);
9842 dd->entry.vms_verscount = 0;
9843 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
9847 } /* end of readdir() */
9851 * Read the next entry from the directory -- thread-safe version.
9853 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
9855 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
9859 MUTEX_LOCK( (perl_mutex *) dd->mutex );
9861 entry = readdir(dd);
9863 retval = ( *result == NULL ? errno : 0 );
9865 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
9869 } /* end of readdir_r() */
9873 * Return something that can be used in a seekdir later.
9875 /*{{{ long telldir(DIR *dd)*/
9877 Perl_telldir(DIR *dd)
9884 * Return to a spot where we used to be. Brute force.
9886 /*{{{ void seekdir(DIR *dd,long count)*/
9888 Perl_seekdir(pTHX_ DIR *dd, long count)
9892 /* If we haven't done anything yet... */
9896 /* Remember some state, and clear it. */
9897 old_flags = dd->flags;
9898 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
9899 _ckvmssts(lib$find_file_end(&dd->context));
9902 /* The increment is in readdir(). */
9903 for (dd->count = 0; dd->count < count; )
9906 dd->flags = old_flags;
9908 } /* end of seekdir() */
9911 /* VMS subprocess management
9913 * my_vfork() - just a vfork(), after setting a flag to record that
9914 * the current script is trying a Unix-style fork/exec.
9916 * vms_do_aexec() and vms_do_exec() are called in response to the
9917 * perl 'exec' function. If this follows a vfork call, then they
9918 * call out the regular perl routines in doio.c which do an
9919 * execvp (for those who really want to try this under VMS).
9920 * Otherwise, they do exactly what the perl docs say exec should
9921 * do - terminate the current script and invoke a new command
9922 * (See below for notes on command syntax.)
9924 * do_aspawn() and do_spawn() implement the VMS side of the perl
9925 * 'system' function.
9927 * Note on command arguments to perl 'exec' and 'system': When handled
9928 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
9929 * are concatenated to form a DCL command string. If the first non-numeric
9930 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
9931 * the command string is handed off to DCL directly. Otherwise,
9932 * the first token of the command is taken as the filespec of an image
9933 * to run. The filespec is expanded using a default type of '.EXE' and
9934 * the process defaults for device, directory, etc., and if found, the resultant
9935 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
9936 * the command string as parameters. This is perhaps a bit complicated,
9937 * but I hope it will form a happy medium between what VMS folks expect
9938 * from lib$spawn and what Unix folks expect from exec.
9941 static int vfork_called;
9943 /*{{{int my_vfork()*/
9954 vms_execfree(struct dsc$descriptor_s *vmscmd)
9957 if (vmscmd->dsc$a_pointer) {
9958 PerlMem_free(vmscmd->dsc$a_pointer);
9960 PerlMem_free(vmscmd);
9965 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
9967 char *junk, *tmps = NULL;
9968 register size_t cmdlen = 0;
9975 tmps = SvPV(really,rlen);
9982 for (idx++; idx <= sp; idx++) {
9984 junk = SvPVx(*idx,rlen);
9985 cmdlen += rlen ? rlen + 1 : 0;
9988 Newx(PL_Cmd, cmdlen+1, char);
9990 if (tmps && *tmps) {
9991 strcpy(PL_Cmd,tmps);
9994 else *PL_Cmd = '\0';
9995 while (++mark <= sp) {
9997 char *s = SvPVx(*mark,n_a);
9999 if (*PL_Cmd) strcat(PL_Cmd," ");
10005 } /* end of setup_argstr() */
10008 static unsigned long int
10009 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10010 struct dsc$descriptor_s **pvmscmd)
10014 char image_name[NAM$C_MAXRSS+1];
10015 char image_argv[NAM$C_MAXRSS+1];
10016 $DESCRIPTOR(defdsc,".EXE");
10017 $DESCRIPTOR(defdsc2,".");
10018 struct dsc$descriptor_s resdsc;
10019 struct dsc$descriptor_s *vmscmd;
10020 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10021 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10022 register char *s, *rest, *cp, *wordbreak;
10025 register int isdcl;
10027 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10028 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10030 /* vmsspec is a DCL command buffer, not just a filename */
10031 vmsspec = PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10032 if (vmsspec == NULL)
10033 _ckvmssts_noperl(SS$_INSFMEM);
10035 resspec = PerlMem_malloc(VMS_MAXRSS);
10036 if (resspec == NULL)
10037 _ckvmssts_noperl(SS$_INSFMEM);
10039 /* Make a copy for modification */
10040 cmdlen = strlen(incmd);
10041 cmd = PerlMem_malloc(cmdlen+1);
10042 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10043 strncpy(cmd, incmd, cmdlen);
10048 resdsc.dsc$a_pointer = resspec;
10049 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10050 resdsc.dsc$b_class = DSC$K_CLASS_S;
10051 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10053 vmscmd->dsc$a_pointer = NULL;
10054 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10055 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10056 vmscmd->dsc$w_length = 0;
10057 if (pvmscmd) *pvmscmd = vmscmd;
10059 if (suggest_quote) *suggest_quote = 0;
10061 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10063 PerlMem_free(vmsspec);
10064 PerlMem_free(resspec);
10065 return CLI$_BUFOVF; /* continuation lines currently unsupported */
10070 while (*s && isspace(*s)) s++;
10072 if (*s == '@' || *s == '$') {
10073 vmsspec[0] = *s; rest = s + 1;
10074 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10076 else { cp = vmsspec; rest = s; }
10077 if (*rest == '.' || *rest == '/') {
10079 for (cp2 = resspec;
10080 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10081 rest++, cp2++) *cp2 = *rest;
10083 if (do_tovmsspec(resspec,cp,0,NULL)) {
10086 /* When a UNIX spec with no file type is translated to VMS, */
10087 /* A trailing '.' is appended under ODS-5 rules. */
10088 /* Here we do not want that trailing "." as it prevents */
10089 /* Looking for a implied ".exe" type. */
10090 if (decc_efs_charset) {
10092 i = strlen(vmsspec);
10093 if (vmsspec[i-1] == '.') {
10094 vmsspec[i-1] = '\0';
10099 for (cp2 = vmsspec + strlen(vmsspec);
10100 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10101 rest++, cp2++) *cp2 = *rest;
10106 /* Intuit whether verb (first word of cmd) is a DCL command:
10107 * - if first nonspace char is '@', it's a DCL indirection
10109 * - if verb contains a filespec separator, it's not a DCL command
10110 * - if it doesn't, caller tells us whether to default to a DCL
10111 * command, or to a local image unless told it's DCL (by leading '$')
10115 if (suggest_quote) *suggest_quote = 1;
10117 register char *filespec = strpbrk(s,":<[.;");
10118 rest = wordbreak = strpbrk(s," \"\t/");
10119 if (!wordbreak) wordbreak = s + strlen(s);
10120 if (*s == '$') check_img = 0;
10121 if (filespec && (filespec < wordbreak)) isdcl = 0;
10122 else isdcl = !check_img;
10127 imgdsc.dsc$a_pointer = s;
10128 imgdsc.dsc$w_length = wordbreak - s;
10129 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10131 _ckvmssts_noperl(lib$find_file_end(&cxt));
10132 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10133 if (!(retsts & 1) && *s == '$') {
10134 _ckvmssts_noperl(lib$find_file_end(&cxt));
10135 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10136 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10138 _ckvmssts_noperl(lib$find_file_end(&cxt));
10139 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10143 _ckvmssts_noperl(lib$find_file_end(&cxt));
10148 while (*s && !isspace(*s)) s++;
10151 /* check that it's really not DCL with no file extension */
10152 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10154 char b[256] = {0,0,0,0};
10155 read(fileno(fp), b, 256);
10156 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10160 /* Check for script */
10162 if ((b[0] == '#') && (b[1] == '!'))
10164 #ifdef ALTERNATE_SHEBANG
10166 shebang_len = strlen(ALTERNATE_SHEBANG);
10167 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10169 perlstr = strstr("perl",b);
10170 if (perlstr == NULL)
10178 if (shebang_len > 0) {
10181 char tmpspec[NAM$C_MAXRSS + 1];
10184 /* Image is following after white space */
10185 /*--------------------------------------*/
10186 while (isprint(b[i]) && isspace(b[i]))
10190 while (isprint(b[i]) && !isspace(b[i])) {
10191 tmpspec[j++] = b[i++];
10192 if (j >= NAM$C_MAXRSS)
10197 /* There may be some default parameters to the image */
10198 /*---------------------------------------------------*/
10200 while (isprint(b[i])) {
10201 image_argv[j++] = b[i++];
10202 if (j >= NAM$C_MAXRSS)
10205 while ((j > 0) && !isprint(image_argv[j-1]))
10209 /* It will need to be converted to VMS format and validated */
10210 if (tmpspec[0] != '\0') {
10213 /* Try to find the exact program requested to be run */
10214 /*---------------------------------------------------*/
10215 iname = do_rmsexpand
10216 (tmpspec, image_name, 0, ".exe",
10217 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10218 if (iname != NULL) {
10219 if (cando_by_name_int
10220 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10221 /* MCR prefix needed */
10225 /* Try again with a null type */
10226 /*----------------------------*/
10227 iname = do_rmsexpand
10228 (tmpspec, image_name, 0, ".",
10229 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10230 if (iname != NULL) {
10231 if (cando_by_name_int
10232 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10233 /* MCR prefix needed */
10239 /* Did we find the image to run the script? */
10240 /*------------------------------------------*/
10244 /* Assume DCL or foreign command exists */
10245 /*--------------------------------------*/
10246 tchr = strrchr(tmpspec, '/');
10247 if (tchr != NULL) {
10253 strcpy(image_name, tchr);
10261 if (check_img && isdcl) {
10263 PerlMem_free(resspec);
10264 PerlMem_free(vmsspec);
10268 if (cando_by_name(S_IXUSR,0,resspec)) {
10269 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10270 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10272 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
10273 if (image_name[0] != 0) {
10274 strcat(vmscmd->dsc$a_pointer, image_name);
10275 strcat(vmscmd->dsc$a_pointer, " ");
10277 } else if (image_name[0] != 0) {
10278 strcpy(vmscmd->dsc$a_pointer, image_name);
10279 strcat(vmscmd->dsc$a_pointer, " ");
10281 strcpy(vmscmd->dsc$a_pointer,"@");
10283 if (suggest_quote) *suggest_quote = 1;
10285 /* If there is an image name, use original command */
10286 if (image_name[0] == 0)
10287 strcat(vmscmd->dsc$a_pointer,resspec);
10290 while (*rest && isspace(*rest)) rest++;
10293 if (image_argv[0] != 0) {
10294 strcat(vmscmd->dsc$a_pointer,image_argv);
10295 strcat(vmscmd->dsc$a_pointer, " ");
10301 rest_len = strlen(rest);
10302 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10303 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10304 strcat(vmscmd->dsc$a_pointer,rest);
10306 retsts = CLI$_BUFOVF;
10308 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10310 PerlMem_free(vmsspec);
10311 PerlMem_free(resspec);
10312 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10318 /* It's either a DCL command or we couldn't find a suitable image */
10319 vmscmd->dsc$w_length = strlen(cmd);
10321 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
10322 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
10323 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
10326 PerlMem_free(resspec);
10327 PerlMem_free(vmsspec);
10329 /* check if it's a symbol (for quoting purposes) */
10330 if (suggest_quote && !*suggest_quote) {
10332 char equiv[LNM$C_NAMLENGTH];
10333 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10334 eqvdsc.dsc$a_pointer = equiv;
10336 iss = lib$get_symbol(vmscmd,&eqvdsc);
10337 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10339 if (!(retsts & 1)) {
10340 /* just hand off status values likely to be due to user error */
10341 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10342 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10343 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10344 else { _ckvmssts_noperl(retsts); }
10347 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10349 } /* end of setup_cmddsc() */
10352 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10354 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10360 if (vfork_called) { /* this follows a vfork - act Unixish */
10362 if (vfork_called < 0) {
10363 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10366 else return do_aexec(really,mark,sp);
10368 /* no vfork - act VMSish */
10369 cmd = setup_argstr(aTHX_ really,mark,sp);
10370 exec_sts = vms_do_exec(cmd);
10371 Safefree(cmd); /* Clean up from setup_argstr() */
10376 } /* end of vms_do_aexec() */
10379 /* {{{bool vms_do_exec(char *cmd) */
10381 Perl_vms_do_exec(pTHX_ const char *cmd)
10383 struct dsc$descriptor_s *vmscmd;
10385 if (vfork_called) { /* this follows a vfork - act Unixish */
10387 if (vfork_called < 0) {
10388 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10391 else return do_exec(cmd);
10394 { /* no vfork - act VMSish */
10395 unsigned long int retsts;
10398 TAINT_PROPER("exec");
10399 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10400 retsts = lib$do_command(vmscmd);
10403 case RMS$_FNF: case RMS$_DNF:
10404 set_errno(ENOENT); break;
10406 set_errno(ENOTDIR); break;
10408 set_errno(ENODEV); break;
10410 set_errno(EACCES); break;
10412 set_errno(EINVAL); break;
10413 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10414 set_errno(E2BIG); break;
10415 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10416 _ckvmssts_noperl(retsts); /* fall through */
10417 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10418 set_errno(EVMSERR);
10420 set_vaxc_errno(retsts);
10421 if (ckWARN(WARN_EXEC)) {
10422 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
10423 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
10425 vms_execfree(vmscmd);
10430 } /* end of vms_do_exec() */
10433 int do_spawn2(pTHX_ const char *, int);
10436 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
10438 unsigned long int sts;
10444 /* We'll copy the (undocumented?) Win32 behavior and allow a
10445 * numeric first argument. But the only value we'll support
10446 * through do_aspawn is a value of 1, which means spawn without
10447 * waiting for completion -- other values are ignored.
10449 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
10451 flags = SvIVx(*mark);
10454 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
10455 flags = CLI$M_NOWAIT;
10459 cmd = setup_argstr(aTHX_ really, mark, sp);
10460 sts = do_spawn2(aTHX_ cmd, flags);
10461 /* pp_sys will clean up cmd */
10465 } /* end of do_aspawn() */
10469 /* {{{int do_spawn(char* cmd) */
10471 Perl_do_spawn(pTHX_ char* cmd)
10473 PERL_ARGS_ASSERT_DO_SPAWN;
10475 return do_spawn2(aTHX_ cmd, 0);
10479 /* {{{int do_spawn_nowait(char* cmd) */
10481 Perl_do_spawn_nowait(pTHX_ char* cmd)
10483 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
10485 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
10489 /* {{{int do_spawn2(char *cmd) */
10491 do_spawn2(pTHX_ const char *cmd, int flags)
10493 unsigned long int sts, substs;
10495 /* The caller of this routine expects to Safefree(PL_Cmd) */
10496 Newx(PL_Cmd,10,char);
10499 TAINT_PROPER("spawn");
10500 if (!cmd || !*cmd) {
10501 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
10504 case RMS$_FNF: case RMS$_DNF:
10505 set_errno(ENOENT); break;
10507 set_errno(ENOTDIR); break;
10509 set_errno(ENODEV); break;
10511 set_errno(EACCES); break;
10513 set_errno(EINVAL); break;
10514 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10515 set_errno(E2BIG); break;
10516 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10517 _ckvmssts_noperl(sts); /* fall through */
10518 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10519 set_errno(EVMSERR);
10521 set_vaxc_errno(sts);
10522 if (ckWARN(WARN_EXEC)) {
10523 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
10532 if (flags & CLI$M_NOWAIT)
10535 strcpy(mode, "nW");
10537 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
10540 /* sts will be the pid in the nowait case */
10543 } /* end of do_spawn2() */
10547 static unsigned int *sockflags, sockflagsize;
10550 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
10551 * routines found in some versions of the CRTL can't deal with sockets.
10552 * We don't shim the other file open routines since a socket isn't
10553 * likely to be opened by a name.
10555 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
10556 FILE *my_fdopen(int fd, const char *mode)
10558 FILE *fp = fdopen(fd, mode);
10561 unsigned int fdoff = fd / sizeof(unsigned int);
10562 Stat_t sbuf; /* native stat; we don't need flex_stat */
10563 if (!sockflagsize || fdoff > sockflagsize) {
10564 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
10565 else Newx (sockflags,fdoff+2,unsigned int);
10566 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
10567 sockflagsize = fdoff + 2;
10569 if (fstat(fd, (struct stat *)&sbuf) == 0 && S_ISSOCK(sbuf.st_mode))
10570 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
10579 * Clear the corresponding bit when the (possibly) socket stream is closed.
10580 * There still a small hole: we miss an implicit close which might occur
10581 * via freopen(). >> Todo
10583 /*{{{ int my_fclose(FILE *fp)*/
10584 int my_fclose(FILE *fp) {
10586 unsigned int fd = fileno(fp);
10587 unsigned int fdoff = fd / sizeof(unsigned int);
10589 if (sockflagsize && fdoff < sockflagsize)
10590 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
10598 * A simple fwrite replacement which outputs itmsz*nitm chars without
10599 * introducing record boundaries every itmsz chars.
10600 * We are using fputs, which depends on a terminating null. We may
10601 * well be writing binary data, so we need to accommodate not only
10602 * data with nulls sprinkled in the middle but also data with no null
10605 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
10607 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
10609 register char *cp, *end, *cpd, *data;
10610 register unsigned int fd = fileno(dest);
10611 register unsigned int fdoff = fd / sizeof(unsigned int);
10613 int bufsize = itmsz * nitm + 1;
10615 if (fdoff < sockflagsize &&
10616 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
10617 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
10621 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
10622 memcpy( data, src, itmsz*nitm );
10623 data[itmsz*nitm] = '\0';
10625 end = data + itmsz * nitm;
10626 retval = (int) nitm; /* on success return # items written */
10629 while (cpd <= end) {
10630 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
10631 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
10633 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
10637 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
10640 } /* end of my_fwrite() */
10643 /*{{{ int my_flush(FILE *fp)*/
10645 Perl_my_flush(pTHX_ FILE *fp)
10648 if ((res = fflush(fp)) == 0 && fp) {
10649 #ifdef VMS_DO_SOCKETS
10651 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
10653 res = fsync(fileno(fp));
10656 * If the flush succeeded but set end-of-file, we need to clear
10657 * the error because our caller may check ferror(). BTW, this
10658 * probably means we just flushed an empty file.
10660 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
10667 * Here are replacements for the following Unix routines in the VMS environment:
10668 * getpwuid Get information for a particular UIC or UID
10669 * getpwnam Get information for a named user
10670 * getpwent Get information for each user in the rights database
10671 * setpwent Reset search to the start of the rights database
10672 * endpwent Finish searching for users in the rights database
10674 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
10675 * (defined in pwd.h), which contains the following fields:-
10677 * char *pw_name; Username (in lower case)
10678 * char *pw_passwd; Hashed password
10679 * unsigned int pw_uid; UIC
10680 * unsigned int pw_gid; UIC group number
10681 * char *pw_unixdir; Default device/directory (VMS-style)
10682 * char *pw_gecos; Owner name
10683 * char *pw_dir; Default device/directory (Unix-style)
10684 * char *pw_shell; Default CLI name (eg. DCL)
10686 * If the specified user does not exist, getpwuid and getpwnam return NULL.
10688 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
10689 * not the UIC member number (eg. what's returned by getuid()),
10690 * getpwuid() can accept either as input (if uid is specified, the caller's
10691 * UIC group is used), though it won't recognise gid=0.
10693 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
10694 * information about other users in your group or in other groups, respectively.
10695 * If the required privilege is not available, then these routines fill only
10696 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
10699 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
10702 /* sizes of various UAF record fields */
10703 #define UAI$S_USERNAME 12
10704 #define UAI$S_IDENT 31
10705 #define UAI$S_OWNER 31
10706 #define UAI$S_DEFDEV 31
10707 #define UAI$S_DEFDIR 63
10708 #define UAI$S_DEFCLI 31
10709 #define UAI$S_PWD 8
10711 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
10712 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
10713 (uic).uic$v_group != UIC$K_WILD_GROUP)
10715 static char __empty[]= "";
10716 static struct passwd __passwd_empty=
10717 {(char *) __empty, (char *) __empty, 0, 0,
10718 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
10719 static int contxt= 0;
10720 static struct passwd __pwdcache;
10721 static char __pw_namecache[UAI$S_IDENT+1];
10724 * This routine does most of the work extracting the user information.
10726 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
10729 unsigned char length;
10730 char pw_gecos[UAI$S_OWNER+1];
10732 static union uicdef uic;
10734 unsigned char length;
10735 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
10738 unsigned char length;
10739 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
10742 unsigned char length;
10743 char pw_shell[UAI$S_DEFCLI+1];
10745 static char pw_passwd[UAI$S_PWD+1];
10747 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
10748 struct dsc$descriptor_s name_desc;
10749 unsigned long int sts;
10751 static struct itmlst_3 itmlst[]= {
10752 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
10753 {sizeof(uic), UAI$_UIC, &uic, &luic},
10754 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
10755 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
10756 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
10757 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
10758 {0, 0, NULL, NULL}};
10760 name_desc.dsc$w_length= strlen(name);
10761 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
10762 name_desc.dsc$b_class= DSC$K_CLASS_S;
10763 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
10765 /* Note that sys$getuai returns many fields as counted strings. */
10766 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
10767 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
10768 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
10770 else { _ckvmssts(sts); }
10771 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
10773 if ((int) owner.length < lowner) lowner= (int) owner.length;
10774 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
10775 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
10776 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
10777 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
10778 owner.pw_gecos[lowner]= '\0';
10779 defdev.pw_dir[ldefdev+ldefdir]= '\0';
10780 defcli.pw_shell[ldefcli]= '\0';
10781 if (valid_uic(uic)) {
10782 pwd->pw_uid= uic.uic$l_uic;
10783 pwd->pw_gid= uic.uic$v_group;
10786 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
10787 pwd->pw_passwd= pw_passwd;
10788 pwd->pw_gecos= owner.pw_gecos;
10789 pwd->pw_dir= defdev.pw_dir;
10790 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
10791 pwd->pw_shell= defcli.pw_shell;
10792 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
10794 ldir= strlen(pwd->pw_unixdir) - 1;
10795 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
10798 strcpy(pwd->pw_unixdir, pwd->pw_dir);
10799 if (!decc_efs_case_preserve)
10800 __mystrtolower(pwd->pw_unixdir);
10805 * Get information for a named user.
10807 /*{{{struct passwd *getpwnam(char *name)*/
10808 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
10810 struct dsc$descriptor_s name_desc;
10812 unsigned long int status, sts;
10814 __pwdcache = __passwd_empty;
10815 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
10816 /* We still may be able to determine pw_uid and pw_gid */
10817 name_desc.dsc$w_length= strlen(name);
10818 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
10819 name_desc.dsc$b_class= DSC$K_CLASS_S;
10820 name_desc.dsc$a_pointer= (char *) name;
10821 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
10822 __pwdcache.pw_uid= uic.uic$l_uic;
10823 __pwdcache.pw_gid= uic.uic$v_group;
10826 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
10827 set_vaxc_errno(sts);
10828 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
10831 else { _ckvmssts(sts); }
10834 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
10835 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
10836 __pwdcache.pw_name= __pw_namecache;
10837 return &__pwdcache;
10838 } /* end of my_getpwnam() */
10842 * Get information for a particular UIC or UID.
10843 * Called by my_getpwent with uid=-1 to list all users.
10845 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
10846 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
10848 const $DESCRIPTOR(name_desc,__pw_namecache);
10849 unsigned short lname;
10851 unsigned long int status;
10853 if (uid == (unsigned int) -1) {
10855 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
10856 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
10857 set_vaxc_errno(status);
10858 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
10862 else { _ckvmssts(status); }
10863 } while (!valid_uic (uic));
10866 uic.uic$l_uic= uid;
10867 if (!uic.uic$v_group)
10868 uic.uic$v_group= PerlProc_getgid();
10869 if (valid_uic(uic))
10870 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
10871 else status = SS$_IVIDENT;
10872 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
10873 status == RMS$_PRV) {
10874 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
10877 else { _ckvmssts(status); }
10879 __pw_namecache[lname]= '\0';
10880 __mystrtolower(__pw_namecache);
10882 __pwdcache = __passwd_empty;
10883 __pwdcache.pw_name = __pw_namecache;
10885 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
10886 The identifier's value is usually the UIC, but it doesn't have to be,
10887 so if we can, we let fillpasswd update this. */
10888 __pwdcache.pw_uid = uic.uic$l_uic;
10889 __pwdcache.pw_gid = uic.uic$v_group;
10891 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
10892 return &__pwdcache;
10894 } /* end of my_getpwuid() */
10898 * Get information for next user.
10900 /*{{{struct passwd *my_getpwent()*/
10901 struct passwd *Perl_my_getpwent(pTHX)
10903 return (my_getpwuid((unsigned int) -1));
10908 * Finish searching rights database for users.
10910 /*{{{void my_endpwent()*/
10911 void Perl_my_endpwent(pTHX)
10914 _ckvmssts(sys$finish_rdb(&contxt));
10920 #ifdef HOMEGROWN_POSIX_SIGNALS
10921 /* Signal handling routines, pulled into the core from POSIX.xs.
10923 * We need these for threads, so they've been rolled into the core,
10924 * rather than left in POSIX.xs.
10926 * (DRS, Oct 23, 1997)
10929 /* sigset_t is atomic under VMS, so these routines are easy */
10930 /*{{{int my_sigemptyset(sigset_t *) */
10931 int my_sigemptyset(sigset_t *set) {
10932 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10933 *set = 0; return 0;
10938 /*{{{int my_sigfillset(sigset_t *)*/
10939 int my_sigfillset(sigset_t *set) {
10941 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10942 for (i = 0; i < NSIG; i++) *set |= (1 << i);
10948 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
10949 int my_sigaddset(sigset_t *set, int sig) {
10950 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10951 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
10952 *set |= (1 << (sig - 1));
10958 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
10959 int my_sigdelset(sigset_t *set, int sig) {
10960 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10961 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
10962 *set &= ~(1 << (sig - 1));
10968 /*{{{int my_sigismember(sigset_t *set, int sig)*/
10969 int my_sigismember(sigset_t *set, int sig) {
10970 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
10971 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
10972 return *set & (1 << (sig - 1));
10977 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
10978 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
10981 /* If set and oset are both null, then things are badly wrong. Bail out. */
10982 if ((oset == NULL) && (set == NULL)) {
10983 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
10987 /* If set's null, then we're just handling a fetch. */
10989 tempmask = sigblock(0);
10994 tempmask = sigsetmask(*set);
10997 tempmask = sigblock(*set);
11000 tempmask = sigblock(0);
11001 sigsetmask(*oset & ~tempmask);
11004 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11009 /* Did they pass us an oset? If so, stick our holding mask into it */
11016 #endif /* HOMEGROWN_POSIX_SIGNALS */
11019 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11020 * my_utime(), and flex_stat(), all of which operate on UTC unless
11021 * VMSISH_TIMES is true.
11023 /* method used to handle UTC conversions:
11024 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
11026 static int gmtime_emulation_type;
11027 /* number of secs to add to UTC POSIX-style time to get local time */
11028 static long int utc_offset_secs;
11030 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11031 * in vmsish.h. #undef them here so we can call the CRTL routines
11040 * DEC C previous to 6.0 corrupts the behavior of the /prefix
11041 * qualifier with the extern prefix pragma. This provisional
11042 * hack circumvents this prefix pragma problem in previous
11045 #if defined(__VMS_VER) && __VMS_VER >= 70000000
11046 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
11047 # pragma __extern_prefix save
11048 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
11049 # define gmtime decc$__utctz_gmtime
11050 # define localtime decc$__utctz_localtime
11051 # define time decc$__utc_time
11052 # pragma __extern_prefix restore
11054 struct tm *gmtime(), *localtime();
11060 static time_t toutc_dst(time_t loc) {
11063 if ((rsltmp = localtime(&loc)) == NULL) return -1;
11064 loc -= utc_offset_secs;
11065 if (rsltmp->tm_isdst) loc -= 3600;
11068 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11069 ((gmtime_emulation_type || my_time(NULL)), \
11070 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11071 ((secs) - utc_offset_secs))))
11073 static time_t toloc_dst(time_t utc) {
11076 utc += utc_offset_secs;
11077 if ((rsltmp = localtime(&utc)) == NULL) return -1;
11078 if (rsltmp->tm_isdst) utc += 3600;
11081 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11082 ((gmtime_emulation_type || my_time(NULL)), \
11083 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11084 ((secs) + utc_offset_secs))))
11086 #ifndef RTL_USES_UTC
11089 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
11090 DST starts on 1st sun of april at 02:00 std time
11091 ends on last sun of october at 02:00 dst time
11092 see the UCX management command reference, SET CONFIG TIMEZONE
11093 for formatting info.
11095 No, it's not as general as it should be, but then again, NOTHING
11096 will handle UK times in a sensible way.
11101 parse the DST start/end info:
11102 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
11106 tz_parse_startend(char *s, struct tm *w, int *past)
11108 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
11109 int ly, dozjd, d, m, n, hour, min, sec, j, k;
11114 if (!past) return 0;
11117 if (w->tm_year % 4 == 0) ly = 1;
11118 if (w->tm_year % 100 == 0) ly = 0;
11119 if (w->tm_year+1900 % 400 == 0) ly = 1;
11122 dozjd = isdigit(*s);
11123 if (*s == 'J' || *s == 'j' || dozjd) {
11124 if (!dozjd && !isdigit(*++s)) return 0;
11127 d = d*10 + *s++ - '0';
11129 d = d*10 + *s++ - '0';
11132 if (d == 0) return 0;
11133 if (d > 366) return 0;
11135 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
11138 } else if (*s == 'M' || *s == 'm') {
11139 if (!isdigit(*++s)) return 0;
11141 if (isdigit(*s)) m = 10*m + *s++ - '0';
11142 if (*s != '.') return 0;
11143 if (!isdigit(*++s)) return 0;
11145 if (n < 1 || n > 5) return 0;
11146 if (*s != '.') return 0;
11147 if (!isdigit(*++s)) return 0;
11149 if (d > 6) return 0;
11153 if (!isdigit(*++s)) return 0;
11155 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
11157 if (!isdigit(*++s)) return 0;
11159 if (isdigit(*s)) min = 10*min + *s++ - '0';
11161 if (!isdigit(*++s)) return 0;
11163 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
11173 if (w->tm_yday < d) goto before;
11174 if (w->tm_yday > d) goto after;
11176 if (w->tm_mon+1 < m) goto before;
11177 if (w->tm_mon+1 > m) goto after;
11179 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
11180 k = d - j; /* mday of first d */
11181 if (k <= 0) k += 7;
11182 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
11183 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
11184 if (w->tm_mday < k) goto before;
11185 if (w->tm_mday > k) goto after;
11188 if (w->tm_hour < hour) goto before;
11189 if (w->tm_hour > hour) goto after;
11190 if (w->tm_min < min) goto before;
11191 if (w->tm_min > min) goto after;
11192 if (w->tm_sec < sec) goto before;
11206 /* parse the offset: (+|-)hh[:mm[:ss]] */
11209 tz_parse_offset(char *s, int *offset)
11211 int hour = 0, min = 0, sec = 0;
11214 if (!offset) return 0;
11216 if (*s == '-') {neg++; s++;}
11217 if (*s == '+') s++;
11218 if (!isdigit(*s)) return 0;
11220 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
11221 if (hour > 24) return 0;
11223 if (!isdigit(*++s)) return 0;
11225 if (isdigit(*s)) min = min*10 + (*s++ - '0');
11226 if (min > 59) return 0;
11228 if (!isdigit(*++s)) return 0;
11230 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
11231 if (sec > 59) return 0;
11235 *offset = (hour*60+min)*60 + sec;
11236 if (neg) *offset = -*offset;
11241 input time is w, whatever type of time the CRTL localtime() uses.
11242 sets dst, the zone, and the gmtoff (seconds)
11244 caches the value of TZ and UCX$TZ env variables; note that
11245 my_setenv looks for these and sets a flag if they're changed
11248 We have to watch out for the "australian" case (dst starts in
11249 october, ends in april)...flagged by "reverse" and checked by
11250 scanning through the months of the previous year.
11255 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
11260 char *dstzone, *tz, *s_start, *s_end;
11261 int std_off, dst_off, isdst;
11262 int y, dststart, dstend;
11263 static char envtz[1025]; /* longer than any logical, symbol, ... */
11264 static char ucxtz[1025];
11265 static char reversed = 0;
11271 reversed = -1; /* flag need to check */
11272 envtz[0] = ucxtz[0] = '\0';
11273 tz = my_getenv("TZ",0);
11274 if (tz) strcpy(envtz, tz);
11275 tz = my_getenv("UCX$TZ",0);
11276 if (tz) strcpy(ucxtz, tz);
11277 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
11280 if (!*tz) tz = ucxtz;
11283 while (isalpha(*s)) s++;
11284 s = tz_parse_offset(s, &std_off);
11286 if (!*s) { /* no DST, hurray we're done! */
11292 while (isalpha(*s)) s++;
11293 s2 = tz_parse_offset(s, &dst_off);
11297 dst_off = std_off - 3600;
11300 if (!*s) { /* default dst start/end?? */
11301 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
11302 s = strchr(ucxtz,',');
11304 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
11306 if (*s != ',') return 0;
11309 when = _toutc(when); /* convert to utc */
11310 when = when - std_off; /* convert to pseudolocal time*/
11312 w2 = localtime(&when);
11315 s = tz_parse_startend(s_start,w2,&dststart);
11317 if (*s != ',') return 0;
11320 when = _toutc(when); /* convert to utc */
11321 when = when - dst_off; /* convert to pseudolocal time*/
11322 w2 = localtime(&when);
11323 if (w2->tm_year != y) { /* spans a year, just check one time */
11324 when += dst_off - std_off;
11325 w2 = localtime(&when);
11328 s = tz_parse_startend(s_end,w2,&dstend);
11331 if (reversed == -1) { /* need to check if start later than end */
11335 if (when < 2*365*86400) {
11336 when += 2*365*86400;
11340 w2 =localtime(&when);
11341 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
11343 for (j = 0; j < 12; j++) {
11344 w2 =localtime(&when);
11345 tz_parse_startend(s_start,w2,&ds);
11346 tz_parse_startend(s_end,w2,&de);
11347 if (ds != de) break;
11351 if (de && !ds) reversed = 1;
11354 isdst = dststart && !dstend;
11355 if (reversed) isdst = dststart || !dstend;
11358 if (dst) *dst = isdst;
11359 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
11360 if (isdst) tz = dstzone;
11362 while(isalpha(*tz)) *zone++ = *tz++;
11368 #endif /* !RTL_USES_UTC */
11370 /* my_time(), my_localtime(), my_gmtime()
11371 * By default traffic in UTC time values, using CRTL gmtime() or
11372 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11373 * Note: We need to use these functions even when the CRTL has working
11374 * UTC support, since they also handle C<use vmsish qw(times);>
11376 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
11377 * Modified by Charles Bailey <bailey@newman.upenn.edu>
11380 /*{{{time_t my_time(time_t *timep)*/
11381 time_t Perl_my_time(pTHX_ time_t *timep)
11386 if (gmtime_emulation_type == 0) {
11388 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
11389 /* results of calls to gmtime() and localtime() */
11390 /* for same &base */
11392 gmtime_emulation_type++;
11393 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11394 char off[LNM$C_NAMLENGTH+1];;
11396 gmtime_emulation_type++;
11397 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11398 gmtime_emulation_type++;
11399 utc_offset_secs = 0;
11400 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11402 else { utc_offset_secs = atol(off); }
11404 else { /* We've got a working gmtime() */
11405 struct tm gmt, local;
11408 tm_p = localtime(&base);
11410 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
11411 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11412 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
11413 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
11418 # ifdef VMSISH_TIME
11419 # ifdef RTL_USES_UTC
11420 if (VMSISH_TIME) when = _toloc(when);
11422 if (!VMSISH_TIME) when = _toutc(when);
11425 if (timep != NULL) *timep = when;
11428 } /* end of my_time() */
11432 /*{{{struct tm *my_gmtime(const time_t *timep)*/
11434 Perl_my_gmtime(pTHX_ const time_t *timep)
11440 if (timep == NULL) {
11441 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11444 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11447 # ifdef VMSISH_TIME
11448 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
11450 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
11451 return gmtime(&when);
11453 /* CRTL localtime() wants local time as input, so does no tz correction */
11454 rsltmp = localtime(&when);
11455 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
11458 } /* end of my_gmtime() */
11462 /*{{{struct tm *my_localtime(const time_t *timep)*/
11464 Perl_my_localtime(pTHX_ const time_t *timep)
11466 time_t when, whenutc;
11470 if (timep == NULL) {
11471 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11474 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
11475 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
11478 # ifdef RTL_USES_UTC
11479 # ifdef VMSISH_TIME
11480 if (VMSISH_TIME) when = _toutc(when);
11482 /* CRTL localtime() wants UTC as input, does tz correction itself */
11483 return localtime(&when);
11485 # else /* !RTL_USES_UTC */
11487 # ifdef VMSISH_TIME
11488 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
11489 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
11492 #ifndef RTL_USES_UTC
11493 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
11494 when = whenutc - offset; /* pseudolocal time*/
11497 /* CRTL localtime() wants local time as input, so does no tz correction */
11498 rsltmp = localtime(&when);
11499 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
11503 } /* end of my_localtime() */
11506 /* Reset definitions for later calls */
11507 #define gmtime(t) my_gmtime(t)
11508 #define localtime(t) my_localtime(t)
11509 #define time(t) my_time(t)
11512 /* my_utime - update modification/access time of a file
11514 * VMS 7.3 and later implementation
11515 * Only the UTC translation is home-grown. The rest is handled by the
11516 * CRTL utime(), which will take into account the relevant feature
11517 * logicals and ODS-5 volume characteristics for true access times.
11519 * pre VMS 7.3 implementation:
11520 * The calling sequence is identical to POSIX utime(), but under
11521 * VMS with ODS-2, only the modification time is changed; ODS-2 does
11522 * not maintain access times. Restrictions differ from the POSIX
11523 * definition in that the time can be changed as long as the
11524 * caller has permission to execute the necessary IO$_MODIFY $QIO;
11525 * no separate checks are made to insure that the caller is the
11526 * owner of the file or has special privs enabled.
11527 * Code here is based on Joe Meadows' FILE utility.
11531 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
11532 * to VMS epoch (01-JAN-1858 00:00:00.00)
11533 * in 100 ns intervals.
11535 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
11537 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
11538 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
11540 #if __CRTL_VER >= 70300000
11541 struct utimbuf utc_utimes, *utc_utimesp;
11543 if (utimes != NULL) {
11544 utc_utimes.actime = utimes->actime;
11545 utc_utimes.modtime = utimes->modtime;
11546 # ifdef VMSISH_TIME
11547 /* If input was local; convert to UTC for sys svc */
11549 utc_utimes.actime = _toutc(utimes->actime);
11550 utc_utimes.modtime = _toutc(utimes->modtime);
11553 utc_utimesp = &utc_utimes;
11556 utc_utimesp = NULL;
11559 return utime(file, utc_utimesp);
11561 #else /* __CRTL_VER < 70300000 */
11565 long int bintime[2], len = 2, lowbit, unixtime,
11566 secscale = 10000000; /* seconds --> 100 ns intervals */
11567 unsigned long int chan, iosb[2], retsts;
11568 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
11569 struct FAB myfab = cc$rms_fab;
11570 struct NAM mynam = cc$rms_nam;
11571 #if defined (__DECC) && defined (__VAX)
11572 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
11573 * at least through VMS V6.1, which causes a type-conversion warning.
11575 # pragma message save
11576 # pragma message disable cvtdiftypes
11578 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
11579 struct fibdef myfib;
11580 #if defined (__DECC) && defined (__VAX)
11581 /* This should be right after the declaration of myatr, but due
11582 * to a bug in VAX DEC C, this takes effect a statement early.
11584 # pragma message restore
11586 /* cast ok for read only parameter */
11587 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
11588 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
11589 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
11591 if (file == NULL || *file == '\0') {
11592 SETERRNO(ENOENT, LIB$_INVARG);
11596 /* Convert to VMS format ensuring that it will fit in 255 characters */
11597 if (do_rmsexpand(file, vmsspec, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL) == NULL) {
11598 SETERRNO(ENOENT, LIB$_INVARG);
11601 if (utimes != NULL) {
11602 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
11603 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
11604 * Since time_t is unsigned long int, and lib$emul takes a signed long int
11605 * as input, we force the sign bit to be clear by shifting unixtime right
11606 * one bit, then multiplying by an extra factor of 2 in lib$emul().
11608 lowbit = (utimes->modtime & 1) ? secscale : 0;
11609 unixtime = (long int) utimes->modtime;
11610 # ifdef VMSISH_TIME
11611 /* If input was UTC; convert to local for sys svc */
11612 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
11614 unixtime >>= 1; secscale <<= 1;
11615 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
11616 if (!(retsts & 1)) {
11617 SETERRNO(EVMSERR, retsts);
11620 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
11621 if (!(retsts & 1)) {
11622 SETERRNO(EVMSERR, retsts);
11627 /* Just get the current time in VMS format directly */
11628 retsts = sys$gettim(bintime);
11629 if (!(retsts & 1)) {
11630 SETERRNO(EVMSERR, retsts);
11635 myfab.fab$l_fna = vmsspec;
11636 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
11637 myfab.fab$l_nam = &mynam;
11638 mynam.nam$l_esa = esa;
11639 mynam.nam$b_ess = (unsigned char) sizeof esa;
11640 mynam.nam$l_rsa = rsa;
11641 mynam.nam$b_rss = (unsigned char) sizeof rsa;
11642 if (decc_efs_case_preserve)
11643 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
11645 /* Look for the file to be affected, letting RMS parse the file
11646 * specification for us as well. I have set errno using only
11647 * values documented in the utime() man page for VMS POSIX.
11649 retsts = sys$parse(&myfab,0,0);
11650 if (!(retsts & 1)) {
11651 set_vaxc_errno(retsts);
11652 if (retsts == RMS$_PRV) set_errno(EACCES);
11653 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
11654 else set_errno(EVMSERR);
11657 retsts = sys$search(&myfab,0,0);
11658 if (!(retsts & 1)) {
11659 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11660 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11661 set_vaxc_errno(retsts);
11662 if (retsts == RMS$_PRV) set_errno(EACCES);
11663 else if (retsts == RMS$_FNF) set_errno(ENOENT);
11664 else set_errno(EVMSERR);
11668 devdsc.dsc$w_length = mynam.nam$b_dev;
11669 /* cast ok for read only parameter */
11670 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
11672 retsts = sys$assign(&devdsc,&chan,0,0);
11673 if (!(retsts & 1)) {
11674 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11675 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11676 set_vaxc_errno(retsts);
11677 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
11678 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
11679 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
11680 else set_errno(EVMSERR);
11684 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
11685 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
11687 memset((void *) &myfib, 0, sizeof myfib);
11688 #if defined(__DECC) || defined(__DECCXX)
11689 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
11690 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
11691 /* This prevents the revision time of the file being reset to the current
11692 * time as a result of our IO$_MODIFY $QIO. */
11693 myfib.fib$l_acctl = FIB$M_NORECORD;
11695 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
11696 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
11697 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
11699 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
11700 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
11701 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
11702 _ckvmssts(sys$dassgn(chan));
11703 if (retsts & 1) retsts = iosb[0];
11704 if (!(retsts & 1)) {
11705 set_vaxc_errno(retsts);
11706 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11707 else set_errno(EVMSERR);
11713 #endif /* #if __CRTL_VER >= 70300000 */
11715 } /* end of my_utime() */
11719 * flex_stat, flex_lstat, flex_fstat
11720 * basic stat, but gets it right when asked to stat
11721 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
11724 #ifndef _USE_STD_STAT
11725 /* encode_dev packs a VMS device name string into an integer to allow
11726 * simple comparisons. This can be used, for example, to check whether two
11727 * files are located on the same device, by comparing their encoded device
11728 * names. Even a string comparison would not do, because stat() reuses the
11729 * device name buffer for each call; so without encode_dev, it would be
11730 * necessary to save the buffer and use strcmp (this would mean a number of
11731 * changes to the standard Perl code, to say nothing of what a Perl script
11732 * would have to do.
11734 * The device lock id, if it exists, should be unique (unless perhaps compared
11735 * with lock ids transferred from other nodes). We have a lock id if the disk is
11736 * mounted cluster-wide, which is when we tend to get long (host-qualified)
11737 * device names. Thus we use the lock id in preference, and only if that isn't
11738 * available, do we try to pack the device name into an integer (flagged by
11739 * the sign bit (LOCKID_MASK) being set).
11741 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
11742 * name and its encoded form, but it seems very unlikely that we will find
11743 * two files on different disks that share the same encoded device names,
11744 * and even more remote that they will share the same file id (if the test
11745 * is to check for the same file).
11747 * A better method might be to use sys$device_scan on the first call, and to
11748 * search for the device, returning an index into the cached array.
11749 * The number returned would be more intelligible.
11750 * This is probably not worth it, and anyway would take quite a bit longer
11751 * on the first call.
11753 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
11754 static mydev_t encode_dev (pTHX_ const char *dev)
11757 unsigned long int f;
11762 if (!dev || !dev[0]) return 0;
11766 struct dsc$descriptor_s dev_desc;
11767 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
11769 /* For cluster-mounted disks, the disk lock identifier is unique, so we
11770 can try that first. */
11771 dev_desc.dsc$w_length = strlen (dev);
11772 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
11773 dev_desc.dsc$b_class = DSC$K_CLASS_S;
11774 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
11775 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
11776 if (!$VMS_STATUS_SUCCESS(status)) {
11778 case SS$_NOSUCHDEV:
11779 SETERRNO(ENODEV, status);
11785 if (lockid) return (lockid & ~LOCKID_MASK);
11789 /* Otherwise we try to encode the device name */
11793 for (q = dev + strlen(dev); q--; q >= dev) {
11798 else if (isalpha (toupper (*q)))
11799 c= toupper (*q) - 'A' + (char)10;
11801 continue; /* Skip '$'s */
11803 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
11805 enc += f * (unsigned long int) c;
11807 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
11809 } /* end of encode_dev() */
11810 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11811 device_no = encode_dev(aTHX_ devname)
11813 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
11814 device_no = new_dev_no
11818 is_null_device(name)
11821 if (decc_bug_devnull != 0) {
11822 if (strncmp("/dev/null", name, 9) == 0)
11825 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
11826 The underscore prefix, controller letter, and unit number are
11827 independently optional; for our purposes, the colon punctuation
11828 is not. The colon can be trailed by optional directory and/or
11829 filename, but two consecutive colons indicates a nodename rather
11830 than a device. [pr] */
11831 if (*name == '_') ++name;
11832 if (tolower(*name++) != 'n') return 0;
11833 if (tolower(*name++) != 'l') return 0;
11834 if (tolower(*name) == 'a') ++name;
11835 if (*name == '0') ++name;
11836 return (*name++ == ':') && (*name != ':');
11841 Perl_cando_by_name_int
11842 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
11844 char usrname[L_cuserid];
11845 struct dsc$descriptor_s usrdsc =
11846 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
11847 char *vmsname = NULL, *fileified = NULL;
11848 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
11849 unsigned short int retlen, trnlnm_iter_count;
11850 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11851 union prvdef curprv;
11852 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
11853 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
11854 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
11855 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
11856 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
11858 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
11860 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11862 static int profile_context = -1;
11864 if (!fname || !*fname) return FALSE;
11866 /* Make sure we expand logical names, since sys$check_access doesn't */
11867 fileified = PerlMem_malloc(VMS_MAXRSS);
11868 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11869 if (!strpbrk(fname,"/]>:")) {
11870 strcpy(fileified,fname);
11871 trnlnm_iter_count = 0;
11872 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
11873 trnlnm_iter_count++;
11874 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
11879 vmsname = PerlMem_malloc(VMS_MAXRSS);
11880 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11881 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
11882 /* Don't know if already in VMS format, so make sure */
11883 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
11884 PerlMem_free(fileified);
11885 PerlMem_free(vmsname);
11890 strcpy(vmsname,fname);
11893 /* sys$check_access needs a file spec, not a directory spec.
11894 * Don't use flex_stat here, as that depends on thread context
11895 * having been initialized, and we may get here during startup.
11898 retlen = namdsc.dsc$w_length = strlen(vmsname);
11899 if (vmsname[retlen-1] == ']'
11900 || vmsname[retlen-1] == '>'
11901 || vmsname[retlen-1] == ':'
11902 || (!stat(vmsname, (stat_t *)&st) && S_ISDIR(st.st_mode))) {
11904 if (!do_fileify_dirspec(vmsname,fileified,1,NULL)) {
11905 PerlMem_free(fileified);
11906 PerlMem_free(vmsname);
11915 retlen = namdsc.dsc$w_length = strlen(fname);
11916 namdsc.dsc$a_pointer = (char *)fname;
11919 case S_IXUSR: case S_IXGRP: case S_IXOTH:
11920 access = ARM$M_EXECUTE;
11921 flags = CHP$M_READ;
11923 case S_IRUSR: case S_IRGRP: case S_IROTH:
11924 access = ARM$M_READ;
11925 flags = CHP$M_READ | CHP$M_USEREADALL;
11927 case S_IWUSR: case S_IWGRP: case S_IWOTH:
11928 access = ARM$M_WRITE;
11929 flags = CHP$M_READ | CHP$M_WRITE;
11931 case S_IDUSR: case S_IDGRP: case S_IDOTH:
11932 access = ARM$M_DELETE;
11933 flags = CHP$M_READ | CHP$M_WRITE;
11936 if (fileified != NULL)
11937 PerlMem_free(fileified);
11938 if (vmsname != NULL)
11939 PerlMem_free(vmsname);
11943 /* Before we call $check_access, create a user profile with the current
11944 * process privs since otherwise it just uses the default privs from the
11945 * UAF and might give false positives or negatives. This only works on
11946 * VMS versions v6.0 and later since that's when sys$create_user_profile
11947 * became available.
11950 /* get current process privs and username */
11951 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
11952 _ckvmssts_noperl(iosb[0]);
11954 #if defined(__VMS_VER) && __VMS_VER >= 60000000
11956 /* find out the space required for the profile */
11957 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
11958 &usrprodsc.dsc$w_length,&profile_context));
11960 /* allocate space for the profile and get it filled in */
11961 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
11962 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
11963 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
11964 &usrprodsc.dsc$w_length,&profile_context));
11966 /* use the profile to check access to the file; free profile & analyze results */
11967 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
11968 PerlMem_free(usrprodsc.dsc$a_pointer);
11969 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
11973 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
11977 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
11978 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
11979 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
11980 set_vaxc_errno(retsts);
11981 if (retsts == SS$_NOPRIV) set_errno(EACCES);
11982 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
11983 else set_errno(ENOENT);
11984 if (fileified != NULL)
11985 PerlMem_free(fileified);
11986 if (vmsname != NULL)
11987 PerlMem_free(vmsname);
11990 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
11991 if (fileified != NULL)
11992 PerlMem_free(fileified);
11993 if (vmsname != NULL)
11994 PerlMem_free(vmsname);
11997 _ckvmssts_noperl(retsts);
11999 if (fileified != NULL)
12000 PerlMem_free(fileified);
12001 if (vmsname != NULL)
12002 PerlMem_free(vmsname);
12003 return FALSE; /* Should never get here */
12007 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
12008 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12009 * subset of the applicable information.
12012 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12014 return cando_by_name_int
12015 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12016 } /* end of cando() */
12020 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12022 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12024 return cando_by_name_int(bit, effective, fname, 0);
12026 } /* end of cando_by_name() */
12030 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12032 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12034 if (!fstat(fd,(stat_t *) statbufp)) {
12036 char *vms_filename;
12037 vms_filename = PerlMem_malloc(VMS_MAXRSS);
12038 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12040 /* Save name for cando by name in VMS format */
12041 cptr = getname(fd, vms_filename, 1);
12043 /* This should not happen, but just in case */
12044 if (cptr == NULL) {
12045 statbufp->st_devnam[0] = 0;
12048 /* Make sure that the saved name fits in 255 characters */
12049 cptr = do_rmsexpand
12051 statbufp->st_devnam,
12054 PERL_RMSEXPAND_M_VMS | PERL_RMSEXPAND_M_VMS_IN,
12058 statbufp->st_devnam[0] = 0;
12060 PerlMem_free(vms_filename);
12062 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12064 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12066 # ifdef RTL_USES_UTC
12067 # ifdef VMSISH_TIME
12069 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12070 statbufp->st_atime = _toloc(statbufp->st_atime);
12071 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12075 # ifdef VMSISH_TIME
12076 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12080 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12081 statbufp->st_atime = _toutc(statbufp->st_atime);
12082 statbufp->st_ctime = _toutc(statbufp->st_ctime);
12089 } /* end of flex_fstat() */
12092 #if !defined(__VAX) && __CRTL_VER >= 80200000
12100 #define lstat(_x, _y) stat(_x, _y)
12103 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
12106 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12108 char fileified[VMS_MAXRSS];
12109 char temp_fspec[VMS_MAXRSS];
12114 if (!fspec) return retval;
12116 strcpy(temp_fspec, fspec);
12118 if (decc_bug_devnull != 0) {
12119 if (is_null_device(temp_fspec)) { /* Fake a stat() for the null device */
12120 memset(statbufp,0,sizeof *statbufp);
12121 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12122 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12123 statbufp->st_uid = 0x00010001;
12124 statbufp->st_gid = 0x0001;
12125 time((time_t *)&statbufp->st_mtime);
12126 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12131 /* Try for a directory name first. If fspec contains a filename without
12132 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12133 * and sea:[wine.dark]water. exist, we prefer the directory here.
12134 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12135 * not sea:[wine.dark]., if the latter exists. If the intended target is
12136 * the file with null type, specify this by calling flex_stat() with
12137 * a '.' at the end of fspec.
12139 * If we are in Posix filespec mode, accept the filename as is.
12143 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12144 /* The CRTL stat() falls down hard on multi-dot filenames in unix format unless
12145 * DECC$EFS_CHARSET is in effect, so temporarily enable it if it isn't already.
12147 if (!decc_efs_charset)
12148 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,1);
12151 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12152 if (decc_posix_compliant_pathnames == 0) {
12154 if (do_fileify_dirspec(temp_fspec,fileified,0,NULL) != NULL) {
12155 if (lstat_flag == 0)
12156 retval = stat(fileified,(stat_t *) statbufp);
12158 retval = lstat(fileified,(stat_t *) statbufp);
12159 save_spec = fileified;
12162 if (lstat_flag == 0)
12163 retval = stat(temp_fspec,(stat_t *) statbufp);
12165 retval = lstat(temp_fspec,(stat_t *) statbufp);
12166 save_spec = temp_fspec;
12169 * In debugging, on 8.3 Alpha, I found a case where stat was returning a
12170 * file not found error for a directory named foo:[bar.t] or /foo/bar/t
12171 * and lstat was working correctly for the same file.
12172 * The only syntax that was working for stat was "foo:[bar]t.dir".
12174 * Other directories with the same syntax worked fine.
12175 * So work around the problem when it shows up here.
12178 int save_errno = errno;
12179 if (do_tovmsspec(fspec, temp_fspec, 0, NULL) != NULL) {
12180 if (do_fileify_dirspec(temp_fspec, fileified, 0, NULL) != NULL) {
12181 retval = stat(fileified, (stat_t *) statbufp);
12182 save_spec = fileified;
12185 /* Restore the errno value if third stat does not succeed */
12187 errno = save_errno;
12189 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12191 if (lstat_flag == 0)
12192 retval = stat(temp_fspec,(stat_t *) statbufp);
12194 retval = lstat(temp_fspec,(stat_t *) statbufp);
12195 save_spec = temp_fspec;
12199 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12200 /* As you were... */
12201 if (!decc_efs_charset)
12202 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12207 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12209 /* If this is an lstat, do not follow the link */
12211 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12213 cptr = do_rmsexpand
12214 (save_spec, statbufp->st_devnam, 0, NULL, rmsex_flags, NULL, NULL);
12216 statbufp->st_devnam[0] = 0;
12218 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12220 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12221 # ifdef RTL_USES_UTC
12222 # ifdef VMSISH_TIME
12224 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12225 statbufp->st_atime = _toloc(statbufp->st_atime);
12226 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12230 # ifdef VMSISH_TIME
12231 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12235 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12236 statbufp->st_atime = _toutc(statbufp->st_atime);
12237 statbufp->st_ctime = _toutc(statbufp->st_ctime);
12241 /* If we were successful, leave errno where we found it */
12242 if (retval == 0) RESTORE_ERRNO;
12245 } /* end of flex_stat_int() */
12248 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12250 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12252 return flex_stat_int(fspec, statbufp, 0);
12256 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12258 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12260 return flex_stat_int(fspec, statbufp, 1);
12265 /*{{{char *my_getlogin()*/
12266 /* VMS cuserid == Unix getlogin, except calling sequence */
12270 static char user[L_cuserid];
12271 return cuserid(user);
12276 /* rmscopy - copy a file using VMS RMS routines
12278 * Copies contents and attributes of spec_in to spec_out, except owner
12279 * and protection information. Name and type of spec_in are used as
12280 * defaults for spec_out. The third parameter specifies whether rmscopy()
12281 * should try to propagate timestamps from the input file to the output file.
12282 * If it is less than 0, no timestamps are preserved. If it is 0, then
12283 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12284 * propagated to the output file at creation iff the output file specification
12285 * did not contain an explicit name or type, and the revision date is always
12286 * updated at the end of the copy operation. If it is greater than 0, then
12287 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12288 * other than the revision date should be propagated, and bit 1 indicates
12289 * that the revision date should be propagated.
12291 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12293 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12294 * Incorporates, with permission, some code from EZCOPY by Tim Adye
12295 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12296 * as part of the Perl standard distribution under the terms of the
12297 * GNU General Public License or the Perl Artistic License. Copies
12298 * of each may be found in the Perl standard distribution.
12300 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12302 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12304 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12305 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12306 unsigned long int i, sts, sts2;
12308 struct FAB fab_in, fab_out;
12309 struct RAB rab_in, rab_out;
12310 rms_setup_nam(nam);
12311 rms_setup_nam(nam_out);
12312 struct XABDAT xabdat;
12313 struct XABFHC xabfhc;
12314 struct XABRDT xabrdt;
12315 struct XABSUM xabsum;
12317 vmsin = PerlMem_malloc(VMS_MAXRSS);
12318 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12319 vmsout = PerlMem_malloc(VMS_MAXRSS);
12320 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12321 if (!spec_in || !*spec_in || !do_tovmsspec(spec_in,vmsin,1,NULL) ||
12322 !spec_out || !*spec_out || !do_tovmsspec(spec_out,vmsout,1,NULL)) {
12323 PerlMem_free(vmsin);
12324 PerlMem_free(vmsout);
12325 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12329 esa = PerlMem_malloc(VMS_MAXRSS);
12330 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12332 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12333 esal = PerlMem_malloc(VMS_MAXRSS);
12334 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12336 fab_in = cc$rms_fab;
12337 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12338 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12339 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12340 fab_in.fab$l_fop = FAB$M_SQO;
12341 rms_bind_fab_nam(fab_in, nam);
12342 fab_in.fab$l_xab = (void *) &xabdat;
12344 rsa = PerlMem_malloc(VMS_MAXRSS);
12345 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12347 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12348 rsal = PerlMem_malloc(VMS_MAXRSS);
12349 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12351 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12352 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12353 rms_nam_esl(nam) = 0;
12354 rms_nam_rsl(nam) = 0;
12355 rms_nam_esll(nam) = 0;
12356 rms_nam_rsll(nam) = 0;
12357 #ifdef NAM$M_NO_SHORT_UPCASE
12358 if (decc_efs_case_preserve)
12359 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
12362 xabdat = cc$rms_xabdat; /* To get creation date */
12363 xabdat.xab$l_nxt = (void *) &xabfhc;
12365 xabfhc = cc$rms_xabfhc; /* To get record length */
12366 xabfhc.xab$l_nxt = (void *) &xabsum;
12368 xabsum = cc$rms_xabsum; /* To get key and area information */
12370 if (!((sts = sys$open(&fab_in)) & 1)) {
12371 PerlMem_free(vmsin);
12372 PerlMem_free(vmsout);
12375 PerlMem_free(esal);
12378 PerlMem_free(rsal);
12379 set_vaxc_errno(sts);
12381 case RMS$_FNF: case RMS$_DNF:
12382 set_errno(ENOENT); break;
12384 set_errno(ENOTDIR); break;
12386 set_errno(ENODEV); break;
12388 set_errno(EINVAL); break;
12390 set_errno(EACCES); break;
12392 set_errno(EVMSERR);
12399 fab_out.fab$w_ifi = 0;
12400 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
12401 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
12402 fab_out.fab$l_fop = FAB$M_SQO;
12403 rms_bind_fab_nam(fab_out, nam_out);
12404 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
12405 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
12406 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
12407 esa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
12408 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12409 rsa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
12410 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12413 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12414 esal_out = PerlMem_malloc(VMS_MAXRSS);
12415 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12416 rsal_out = PerlMem_malloc(VMS_MAXRSS);
12417 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12419 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
12420 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
12422 if (preserve_dates == 0) { /* Act like DCL COPY */
12423 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
12424 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
12425 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
12426 PerlMem_free(vmsin);
12427 PerlMem_free(vmsout);
12430 PerlMem_free(esal);
12433 PerlMem_free(rsal);
12434 PerlMem_free(esa_out);
12435 if (esal_out != NULL)
12436 PerlMem_free(esal_out);
12437 PerlMem_free(rsa_out);
12438 if (rsal_out != NULL)
12439 PerlMem_free(rsal_out);
12440 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
12441 set_vaxc_errno(sts);
12444 fab_out.fab$l_xab = (void *) &xabdat;
12445 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
12446 preserve_dates = 1;
12448 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
12449 preserve_dates =0; /* bitmask from this point forward */
12451 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
12452 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
12453 PerlMem_free(vmsin);
12454 PerlMem_free(vmsout);
12457 PerlMem_free(esal);
12460 PerlMem_free(rsal);
12461 PerlMem_free(esa_out);
12462 if (esal_out != NULL)
12463 PerlMem_free(esal_out);
12464 PerlMem_free(rsa_out);
12465 if (rsal_out != NULL)
12466 PerlMem_free(rsal_out);
12467 set_vaxc_errno(sts);
12470 set_errno(ENOENT); break;
12472 set_errno(ENOTDIR); break;
12474 set_errno(ENODEV); break;
12476 set_errno(EINVAL); break;
12478 set_errno(EACCES); break;
12480 set_errno(EVMSERR);
12484 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
12485 if (preserve_dates & 2) {
12486 /* sys$close() will process xabrdt, not xabdat */
12487 xabrdt = cc$rms_xabrdt;
12489 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
12491 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
12492 * is unsigned long[2], while DECC & VAXC use a struct */
12493 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
12495 fab_out.fab$l_xab = (void *) &xabrdt;
12498 ubf = PerlMem_malloc(32256);
12499 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12500 rab_in = cc$rms_rab;
12501 rab_in.rab$l_fab = &fab_in;
12502 rab_in.rab$l_rop = RAB$M_BIO;
12503 rab_in.rab$l_ubf = ubf;
12504 rab_in.rab$w_usz = 32256;
12505 if (!((sts = sys$connect(&rab_in)) & 1)) {
12506 sys$close(&fab_in); sys$close(&fab_out);
12507 PerlMem_free(vmsin);
12508 PerlMem_free(vmsout);
12512 PerlMem_free(esal);
12515 PerlMem_free(rsal);
12516 PerlMem_free(esa_out);
12517 if (esal_out != NULL)
12518 PerlMem_free(esal_out);
12519 PerlMem_free(rsa_out);
12520 if (rsal_out != NULL)
12521 PerlMem_free(rsal_out);
12522 set_errno(EVMSERR); set_vaxc_errno(sts);
12526 rab_out = cc$rms_rab;
12527 rab_out.rab$l_fab = &fab_out;
12528 rab_out.rab$l_rbf = ubf;
12529 if (!((sts = sys$connect(&rab_out)) & 1)) {
12530 sys$close(&fab_in); sys$close(&fab_out);
12531 PerlMem_free(vmsin);
12532 PerlMem_free(vmsout);
12536 PerlMem_free(esal);
12539 PerlMem_free(rsal);
12540 PerlMem_free(esa_out);
12541 if (esal_out != NULL)
12542 PerlMem_free(esal_out);
12543 PerlMem_free(rsa_out);
12544 if (rsal_out != NULL)
12545 PerlMem_free(rsal_out);
12546 set_errno(EVMSERR); set_vaxc_errno(sts);
12550 while ((sts = sys$read(&rab_in))) { /* always true */
12551 if (sts == RMS$_EOF) break;
12552 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
12553 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
12554 sys$close(&fab_in); sys$close(&fab_out);
12555 PerlMem_free(vmsin);
12556 PerlMem_free(vmsout);
12560 PerlMem_free(esal);
12563 PerlMem_free(rsal);
12564 PerlMem_free(esa_out);
12565 if (esal_out != NULL)
12566 PerlMem_free(esal_out);
12567 PerlMem_free(rsa_out);
12568 if (rsal_out != NULL)
12569 PerlMem_free(rsal_out);
12570 set_errno(EVMSERR); set_vaxc_errno(sts);
12576 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
12577 sys$close(&fab_in); sys$close(&fab_out);
12578 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
12580 PerlMem_free(vmsin);
12581 PerlMem_free(vmsout);
12585 PerlMem_free(esal);
12588 PerlMem_free(rsal);
12589 PerlMem_free(esa_out);
12590 if (esal_out != NULL)
12591 PerlMem_free(esal_out);
12592 PerlMem_free(rsa_out);
12593 if (rsal_out != NULL)
12594 PerlMem_free(rsal_out);
12597 set_errno(EVMSERR); set_vaxc_errno(sts);
12603 } /* end of rmscopy() */
12607 /*** The following glue provides 'hooks' to make some of the routines
12608 * from this file available from Perl. These routines are sufficiently
12609 * basic, and are required sufficiently early in the build process,
12610 * that's it's nice to have them available to miniperl as well as the
12611 * full Perl, so they're set up here instead of in an extension. The
12612 * Perl code which handles importation of these names into a given
12613 * package lives in [.VMS]Filespec.pm in @INC.
12617 rmsexpand_fromperl(pTHX_ CV *cv)
12620 char *fspec, *defspec = NULL, *rslt;
12622 int fs_utf8, dfs_utf8;
12626 if (!items || items > 2)
12627 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
12628 fspec = SvPV(ST(0),n_a);
12629 fs_utf8 = SvUTF8(ST(0));
12630 if (!fspec || !*fspec) XSRETURN_UNDEF;
12632 defspec = SvPV(ST(1),n_a);
12633 dfs_utf8 = SvUTF8(ST(1));
12635 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
12636 ST(0) = sv_newmortal();
12637 if (rslt != NULL) {
12638 sv_usepvn(ST(0),rslt,strlen(rslt));
12647 vmsify_fromperl(pTHX_ CV *cv)
12654 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
12655 utf8_fl = SvUTF8(ST(0));
12656 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12657 ST(0) = sv_newmortal();
12658 if (vmsified != NULL) {
12659 sv_usepvn(ST(0),vmsified,strlen(vmsified));
12668 unixify_fromperl(pTHX_ CV *cv)
12675 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
12676 utf8_fl = SvUTF8(ST(0));
12677 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12678 ST(0) = sv_newmortal();
12679 if (unixified != NULL) {
12680 sv_usepvn(ST(0),unixified,strlen(unixified));
12689 fileify_fromperl(pTHX_ CV *cv)
12696 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
12697 utf8_fl = SvUTF8(ST(0));
12698 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12699 ST(0) = sv_newmortal();
12700 if (fileified != NULL) {
12701 sv_usepvn(ST(0),fileified,strlen(fileified));
12710 pathify_fromperl(pTHX_ CV *cv)
12717 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
12718 utf8_fl = SvUTF8(ST(0));
12719 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12720 ST(0) = sv_newmortal();
12721 if (pathified != NULL) {
12722 sv_usepvn(ST(0),pathified,strlen(pathified));
12731 vmspath_fromperl(pTHX_ CV *cv)
12738 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
12739 utf8_fl = SvUTF8(ST(0));
12740 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12741 ST(0) = sv_newmortal();
12742 if (vmspath != NULL) {
12743 sv_usepvn(ST(0),vmspath,strlen(vmspath));
12752 unixpath_fromperl(pTHX_ CV *cv)
12759 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
12760 utf8_fl = SvUTF8(ST(0));
12761 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
12762 ST(0) = sv_newmortal();
12763 if (unixpath != NULL) {
12764 sv_usepvn(ST(0),unixpath,strlen(unixpath));
12773 candelete_fromperl(pTHX_ CV *cv)
12781 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
12783 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12784 Newx(fspec, VMS_MAXRSS, char);
12785 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
12786 if (SvTYPE(mysv) == SVt_PVGV) {
12787 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
12788 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12796 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
12797 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12804 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
12810 rmscopy_fromperl(pTHX_ CV *cv)
12813 char *inspec, *outspec, *inp, *outp;
12815 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
12816 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12817 unsigned long int sts;
12822 if (items < 2 || items > 3)
12823 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
12825 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
12826 Newx(inspec, VMS_MAXRSS, char);
12827 if (SvTYPE(mysv) == SVt_PVGV) {
12828 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
12829 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12837 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
12838 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12844 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
12845 Newx(outspec, VMS_MAXRSS, char);
12846 if (SvTYPE(mysv) == SVt_PVGV) {
12847 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
12848 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12857 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
12858 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12865 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
12867 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
12873 /* The mod2fname is limited to shorter filenames by design, so it should
12874 * not be modified to support longer EFS pathnames
12877 mod2fname(pTHX_ CV *cv)
12880 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
12881 workbuff[NAM$C_MAXRSS*1 + 1];
12882 int total_namelen = 3, counter, num_entries;
12883 /* ODS-5 ups this, but we want to be consistent, so... */
12884 int max_name_len = 39;
12885 AV *in_array = (AV *)SvRV(ST(0));
12887 num_entries = av_len(in_array);
12889 /* All the names start with PL_. */
12890 strcpy(ultimate_name, "PL_");
12892 /* Clean up our working buffer */
12893 Zero(work_name, sizeof(work_name), char);
12895 /* Run through the entries and build up a working name */
12896 for(counter = 0; counter <= num_entries; counter++) {
12897 /* If it's not the first name then tack on a __ */
12899 strcat(work_name, "__");
12901 strcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)));
12904 /* Check to see if we actually have to bother...*/
12905 if (strlen(work_name) + 3 <= max_name_len) {
12906 strcat(ultimate_name, work_name);
12908 /* It's too darned big, so we need to go strip. We use the same */
12909 /* algorithm as xsubpp does. First, strip out doubled __ */
12910 char *source, *dest, last;
12913 for (source = work_name; *source; source++) {
12914 if (last == *source && last == '_') {
12920 /* Go put it back */
12921 strcpy(work_name, workbuff);
12922 /* Is it still too big? */
12923 if (strlen(work_name) + 3 > max_name_len) {
12924 /* Strip duplicate letters */
12927 for (source = work_name; *source; source++) {
12928 if (last == toupper(*source)) {
12932 last = toupper(*source);
12934 strcpy(work_name, workbuff);
12937 /* Is it *still* too big? */
12938 if (strlen(work_name) + 3 > max_name_len) {
12939 /* Too bad, we truncate */
12940 work_name[max_name_len - 2] = 0;
12942 strcat(ultimate_name, work_name);
12945 /* Okay, return it */
12946 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
12951 hushexit_fromperl(pTHX_ CV *cv)
12956 VMSISH_HUSHED = SvTRUE(ST(0));
12958 ST(0) = boolSV(VMSISH_HUSHED);
12964 Perl_vms_start_glob
12965 (pTHX_ SV *tmpglob,
12969 struct vs_str_st *rslt;
12973 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
12976 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12977 struct dsc$descriptor_vs rsdsc;
12978 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
12979 unsigned long hasver = 0, isunix = 0;
12980 unsigned long int lff_flags = 0;
12983 if (!SvOK(tmpglob)) {
12984 SETERRNO(ENOENT,RMS$_FNF);
12988 #ifdef VMS_LONGNAME_SUPPORT
12989 lff_flags = LIB$M_FIL_LONG_NAMES;
12991 /* The Newx macro will not allow me to assign a smaller array
12992 * to the rslt pointer, so we will assign it to the begin char pointer
12993 * and then copy the value into the rslt pointer.
12995 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
12996 rslt = (struct vs_str_st *)begin;
12998 rstr = &rslt->str[0];
12999 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13000 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13001 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13002 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13004 Newx(vmsspec, VMS_MAXRSS, char);
13006 /* We could find out if there's an explicit dev/dir or version
13007 by peeking into lib$find_file's internal context at
13008 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13009 but that's unsupported, so I don't want to do it now and
13010 have it bite someone in the future. */
13011 /* Fix-me: vms_split_path() is the only way to do this, the
13012 existing method will fail with many legal EFS or UNIX specifications
13015 cp = SvPV(tmpglob,i);
13018 if (cp[i] == ';') hasver = 1;
13019 if (cp[i] == '.') {
13020 if (sts) hasver = 1;
13023 if (cp[i] == '/') {
13024 hasdir = isunix = 1;
13027 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13032 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13036 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13037 if (!stat_sts && S_ISDIR(st.st_mode)) {
13038 wilddsc.dsc$a_pointer = tovmspath_utf8(SvPVX(tmpglob),vmsspec,NULL);
13039 ok = (wilddsc.dsc$a_pointer != NULL);
13040 /* maybe passed 'foo' rather than '[.foo]', thus not detected above */
13044 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13045 ok = (wilddsc.dsc$a_pointer != NULL);
13048 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13050 /* If not extended character set, replace ? with % */
13051 /* With extended character set, ? is a wildcard single character */
13052 if (!decc_efs_case_preserve) {
13053 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++)
13054 if (*cp == '?') *cp = '%';
13057 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13058 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13059 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13061 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13062 &dfltdsc,NULL,&rms_sts,&lff_flags);
13063 if (!$VMS_STATUS_SUCCESS(sts))
13068 /* with varying string, 1st word of buffer contains result length */
13069 rstr[rslt->length] = '\0';
13071 /* Find where all the components are */
13072 v_sts = vms_split_path
13087 /* If no version on input, truncate the version on output */
13088 if (!hasver && (vs_len > 0)) {
13092 /* No version & a null extension on UNIX handling */
13093 if (isunix && (e_len == 1) && decc_readdir_dropdotnotype) {
13099 if (!decc_efs_case_preserve) {
13100 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13104 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13108 /* Start with the name */
13111 strcat(begin,"\n");
13112 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13114 if (cxt) (void)lib$find_file_end(&cxt);
13117 /* Be POSIXish: return the input pattern when no matches */
13118 strcpy(rstr,SvPVX(tmpglob));
13120 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13123 if (ok && sts != RMS$_NMF &&
13124 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13127 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13129 PerlIO_close(tmpfp);
13133 PerlIO_rewind(tmpfp);
13134 IoTYPE(io) = IoTYPE_RDONLY;
13135 IoIFP(io) = fp = tmpfp;
13136 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13146 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13150 unixrealpath_fromperl(pTHX_ CV *cv)
13153 char *fspec, *rslt_spec, *rslt;
13156 if (!items || items != 1)
13157 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13159 fspec = SvPV(ST(0),n_a);
13160 if (!fspec || !*fspec) XSRETURN_UNDEF;
13162 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13163 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13165 ST(0) = sv_newmortal();
13167 sv_usepvn(ST(0),rslt,strlen(rslt));
13169 Safefree(rslt_spec);
13174 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13178 vmsrealpath_fromperl(pTHX_ CV *cv)
13181 char *fspec, *rslt_spec, *rslt;
13184 if (!items || items != 1)
13185 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13187 fspec = SvPV(ST(0),n_a);
13188 if (!fspec || !*fspec) XSRETURN_UNDEF;
13190 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13191 rslt = do_vms_realname(fspec, rslt_spec, NULL);
13193 ST(0) = sv_newmortal();
13195 sv_usepvn(ST(0),rslt,strlen(rslt));
13197 Safefree(rslt_spec);
13203 * A thin wrapper around decc$symlink to make sure we follow the
13204 * standard and do not create a symlink with a zero-length name.
13206 * Also in ODS-2 mode, existing tests assume that the link target
13207 * will be converted to UNIX format.
13209 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13210 int Perl_my_symlink(pTHX_ const char *contents, const char *link_name) {
13211 if (!link_name || !*link_name) {
13212 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13216 if (decc_efs_charset) {
13217 return symlink(contents, link_name);
13222 /* Unless we are in ODS-5 mode, convert the symlink target to UNIX */
13223 /* because in order to work, the symlink target must be in UNIX format */
13225 /* As symbolic links can hold things other than files, we will only do */
13226 /* the conversion in in ODS-2 mode */
13228 Newx(utarget, VMS_MAXRSS + 1, char);
13229 if (do_tounixspec(contents, utarget, 0, NULL) == NULL) {
13231 /* This should not fail, as an untranslatable filename */
13232 /* should be passed through */
13233 utarget = (char *)contents;
13235 sts = symlink(utarget, link_name);
13243 #endif /* HAS_SYMLINK */
13245 int do_vms_case_tolerant(void);
13248 case_tolerant_process_fromperl(pTHX_ CV *cv)
13251 ST(0) = boolSV(do_vms_case_tolerant());
13255 #ifdef USE_ITHREADS
13258 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
13259 struct interp_intern *dst)
13261 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13263 memcpy(dst,src,sizeof(struct interp_intern));
13269 Perl_sys_intern_clear(pTHX)
13274 Perl_sys_intern_init(pTHX)
13276 unsigned int ix = RAND_MAX;
13281 MY_POSIX_EXIT = vms_posix_exit;
13284 MY_INV_RAND_MAX = 1./x;
13288 init_os_extras(void)
13291 char* file = __FILE__;
13292 if (decc_disable_to_vms_logname_translation) {
13293 no_translate_barewords = TRUE;
13295 no_translate_barewords = FALSE;
13298 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
13299 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
13300 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
13301 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
13302 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
13303 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
13304 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
13305 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
13306 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
13307 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
13308 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
13309 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
13310 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
13311 newXSproto("VMS::Filespec::case_tolerant_process",
13312 case_tolerant_process_fromperl,file,"");
13314 store_pipelocs(aTHX); /* will redo any earlier attempts */
13319 #if __CRTL_VER == 80200000
13320 /* This missed getting in to the DECC SDK for 8.2 */
13321 char *realpath(const char *file_name, char * resolved_name, ...);
13324 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
13325 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
13326 * The perl fallback routine to provide realpath() is not as efficient
13330 /* Hack, use old stat() as fastest way of getting ino_t and device */
13331 int decc$stat(const char *name, void * statbuf);
13334 /* Realpath is fragile. In 8.3 it does not work if the feature
13335 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
13336 * links are implemented in RMS, not the CRTL. It also can fail if the
13337 * user does not have read/execute access to some of the directories.
13338 * So in order for Do What I Mean mode to work, if realpath() fails,
13339 * fall back to looking up the filename by the device name and FID.
13342 int vms_fid_to_name(char * outname, int outlen, const char * name)
13346 unsigned short st_ino[3];
13347 unsigned short padw;
13348 unsigned long padl[30]; /* plenty of room */
13351 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13352 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13354 sts = decc$stat(name, &statbuf);
13357 dvidsc.dsc$a_pointer=statbuf.st_dev;
13358 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
13360 specdsc.dsc$a_pointer = outname;
13361 specdsc.dsc$w_length = outlen-1;
13363 sts = lib$fid_to_name
13364 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
13365 if ($VMS_STATUS_SUCCESS(sts)) {
13366 outname[specdsc.dsc$w_length] = 0;
13376 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
13379 char * rslt = NULL;
13382 if (decc_posix_compliant_pathnames > 0 ) {
13383 /* realpath currently only works if posix compliant pathnames are
13384 * enabled. It may start working when they are not, but in that
13385 * case we still want the fallback behavior for backwards compatibility
13387 rslt = realpath(filespec, outbuf);
13391 if (rslt == NULL) {
13393 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13394 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13397 /* Fall back to fid_to_name */
13399 Newx(vms_spec, VMS_MAXRSS + 1, char);
13401 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec);
13405 /* Now need to trim the version off */
13406 sts = vms_split_path
13426 /* Trim off the version */
13427 int file_len = v_len + r_len + d_len + n_len + e_len;
13428 vms_spec[file_len] = 0;
13430 /* The result is expected to be in UNIX format */
13431 rslt = do_tounixspec(vms_spec, outbuf, 0, utf8_fl);
13433 /* Downcase if input had any lower case letters and
13434 * case preservation is not in effect.
13436 if (!decc_efs_case_preserve) {
13437 for (cp = filespec; *cp; cp++)
13438 if (islower(*cp)) { haslower = 1; break; }
13440 if (haslower) __mystrtolower(rslt);
13445 /* Now for some hacks to deal with backwards and forward */
13447 if (!decc_efs_charset) {
13449 /* 1. ODS-2 mode wants to do a syntax only translation */
13450 rslt = do_rmsexpand(filespec, outbuf,
13451 0, NULL, 0, NULL, utf8_fl);
13454 if (decc_filename_unix_report) {
13456 char * vms_dir_name;
13459 /* 2. ODS-5 / UNIX report mode should return a failure */
13460 /* if the parent directory also does not exist */
13461 /* Otherwise, get the real path for the parent */
13462 /* and add the child to it.
13464 /* basename / dirname only available for VMS 7.0+ */
13465 /* So we may need to implement them as common routines */
13467 Newx(dir_name, VMS_MAXRSS + 1, char);
13468 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
13469 dir_name[0] = '\0';
13472 /* First try a VMS parse */
13473 sts = vms_split_path
13491 int dir_len = v_len + r_len + d_len + n_len;
13493 strncpy(dir_name, filespec, dir_len);
13494 dir_name[dir_len] = '\0';
13495 file_name = (char *)&filespec[dir_len + 1];
13498 /* This must be UNIX */
13501 tchar = strrchr(filespec, '/');
13503 if (tchar != NULL) {
13504 int dir_len = tchar - filespec;
13505 strncpy(dir_name, filespec, dir_len);
13506 dir_name[dir_len] = '\0';
13507 file_name = (char *) &filespec[dir_len + 1];
13511 /* Dir name is defaulted */
13512 if (dir_name[0] == 0) {
13514 dir_name[1] = '\0';
13517 /* Need realpath for the directory */
13518 sts = vms_fid_to_name(vms_dir_name,
13523 /* Now need to pathify it.
13524 char *tdir = do_pathify_dirspec(vms_dir_name,
13527 /* And now add the original filespec to it */
13528 if (file_name != NULL) {
13529 strcat(outbuf, file_name);
13533 Safefree(vms_dir_name);
13534 Safefree(dir_name);
13538 Safefree(vms_spec);
13544 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
13547 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13548 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13551 /* Fall back to fid_to_name */
13553 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec);
13560 /* Now need to trim the version off */
13561 sts = vms_split_path
13581 /* Trim off the version */
13582 int file_len = v_len + r_len + d_len + n_len + e_len;
13583 outbuf[file_len] = 0;
13585 /* Downcase if input had any lower case letters and
13586 * case preservation is not in effect.
13588 if (!decc_efs_case_preserve) {
13589 for (cp = filespec; *cp; cp++)
13590 if (islower(*cp)) { haslower = 1; break; }
13592 if (haslower) __mystrtolower(outbuf);
13601 /* External entry points */
13602 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13603 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
13605 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
13606 { return do_vms_realname(filespec, outbuf, utf8_fl); }
13608 /* case_tolerant */
13610 /*{{{int do_vms_case_tolerant(void)*/
13611 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
13612 * controlled by a process setting.
13614 int do_vms_case_tolerant(void)
13616 return vms_process_case_tolerant;
13619 /* External entry points */
13620 #if __CRTL_VER >= 70301000 && !defined(__VAX)
13621 int Perl_vms_case_tolerant(void)
13622 { return do_vms_case_tolerant(); }
13624 int Perl_vms_case_tolerant(void)
13625 { return vms_process_case_tolerant; }
13629 /* Start of DECC RTL Feature handling */
13631 static int sys_trnlnm
13632 (const char * logname,
13636 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
13637 const unsigned long attr = LNM$M_CASE_BLIND;
13638 struct dsc$descriptor_s name_dsc;
13640 unsigned short result;
13641 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
13644 name_dsc.dsc$w_length = strlen(logname);
13645 name_dsc.dsc$a_pointer = (char *)logname;
13646 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
13647 name_dsc.dsc$b_class = DSC$K_CLASS_S;
13649 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
13651 if ($VMS_STATUS_SUCCESS(status)) {
13653 /* Null terminate and return the string */
13654 /*--------------------------------------*/
13661 static int sys_crelnm
13662 (const char * logname,
13663 const char * value)
13666 const char * proc_table = "LNM$PROCESS_TABLE";
13667 struct dsc$descriptor_s proc_table_dsc;
13668 struct dsc$descriptor_s logname_dsc;
13669 struct itmlst_3 item_list[2];
13671 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
13672 proc_table_dsc.dsc$w_length = strlen(proc_table);
13673 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
13674 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
13676 logname_dsc.dsc$a_pointer = (char *) logname;
13677 logname_dsc.dsc$w_length = strlen(logname);
13678 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
13679 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
13681 item_list[0].buflen = strlen(value);
13682 item_list[0].itmcode = LNM$_STRING;
13683 item_list[0].bufadr = (char *)value;
13684 item_list[0].retlen = NULL;
13686 item_list[1].buflen = 0;
13687 item_list[1].itmcode = 0;
13689 ret_val = sys$crelnm
13691 (const struct dsc$descriptor_s *)&proc_table_dsc,
13692 (const struct dsc$descriptor_s *)&logname_dsc,
13694 (const struct item_list_3 *) item_list);
13699 /* C RTL Feature settings */
13701 static int set_features
13702 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
13703 int (* cli_routine)(void), /* Not documented */
13704 void *image_info) /* Not documented */
13710 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
13711 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
13712 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
13713 unsigned long case_perm;
13714 unsigned long case_image;
13717 /* Allow an exception to bring Perl into the VMS debugger */
13718 vms_debug_on_exception = 0;
13719 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
13720 if ($VMS_STATUS_SUCCESS(status)) {
13721 val_str[0] = _toupper(val_str[0]);
13722 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13723 vms_debug_on_exception = 1;
13725 vms_debug_on_exception = 0;
13728 /* Debug unix/vms file translation routines */
13729 vms_debug_fileify = 0;
13730 status = sys_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
13731 if ($VMS_STATUS_SUCCESS(status)) {
13732 val_str[0] = _toupper(val_str[0]);
13733 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13734 vms_debug_fileify = 1;
13736 vms_debug_fileify = 0;
13740 /* Historically PERL has been doing vmsify / stat differently than */
13741 /* the CRTL. In particular, under some conditions the CRTL will */
13742 /* remove some illegal characters like spaces from filenames */
13743 /* resulting in some differences. The stat()/lstat() wrapper has */
13744 /* been reporting such file names as invalid and fails to stat them */
13745 /* fixing this bug so that stat()/lstat() accept these like the */
13746 /* CRTL does will result in several tests failing. */
13747 /* This should really be fixed, but for now, set up a feature to */
13748 /* enable it so that the impact can be studied. */
13749 vms_bug_stat_filename = 0;
13750 status = sys_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
13751 if ($VMS_STATUS_SUCCESS(status)) {
13752 val_str[0] = _toupper(val_str[0]);
13753 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13754 vms_bug_stat_filename = 1;
13756 vms_bug_stat_filename = 0;
13760 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
13761 vms_vtf7_filenames = 0;
13762 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
13763 if ($VMS_STATUS_SUCCESS(status)) {
13764 val_str[0] = _toupper(val_str[0]);
13765 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13766 vms_vtf7_filenames = 1;
13768 vms_vtf7_filenames = 0;
13771 /* unlink all versions on unlink() or rename() */
13772 vms_unlink_all_versions = 0;
13773 status = sys_trnlnm
13774 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
13775 if ($VMS_STATUS_SUCCESS(status)) {
13776 val_str[0] = _toupper(val_str[0]);
13777 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13778 vms_unlink_all_versions = 1;
13780 vms_unlink_all_versions = 0;
13783 /* Dectect running under GNV Bash or other UNIX like shell */
13784 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13785 gnv_unix_shell = 0;
13786 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
13787 if ($VMS_STATUS_SUCCESS(status)) {
13788 gnv_unix_shell = 1;
13789 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
13790 set_feature_default("DECC$EFS_CHARSET", 1);
13791 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
13792 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
13793 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
13794 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
13795 vms_unlink_all_versions = 1;
13796 vms_posix_exit = 1;
13800 /* hacks to see if known bugs are still present for testing */
13802 /* PCP mode requires creating /dev/null special device file */
13803 decc_bug_devnull = 0;
13804 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
13805 if ($VMS_STATUS_SUCCESS(status)) {
13806 val_str[0] = _toupper(val_str[0]);
13807 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13808 decc_bug_devnull = 1;
13810 decc_bug_devnull = 0;
13813 /* UNIX directory names with no paths are broken in a lot of places */
13814 decc_dir_barename = 1;
13815 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
13816 if ($VMS_STATUS_SUCCESS(status)) {
13817 val_str[0] = _toupper(val_str[0]);
13818 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13819 decc_dir_barename = 1;
13821 decc_dir_barename = 0;
13824 #if __CRTL_VER >= 70300000 && !defined(__VAX)
13825 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
13827 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
13828 if (decc_disable_to_vms_logname_translation < 0)
13829 decc_disable_to_vms_logname_translation = 0;
13832 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
13834 decc_efs_case_preserve = decc$feature_get_value(s, 1);
13835 if (decc_efs_case_preserve < 0)
13836 decc_efs_case_preserve = 0;
13839 s = decc$feature_get_index("DECC$EFS_CHARSET");
13840 decc_efs_charset_index = s;
13842 decc_efs_charset = decc$feature_get_value(s, 1);
13843 if (decc_efs_charset < 0)
13844 decc_efs_charset = 0;
13847 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
13849 decc_filename_unix_report = decc$feature_get_value(s, 1);
13850 if (decc_filename_unix_report > 0) {
13851 decc_filename_unix_report = 1;
13852 vms_posix_exit = 1;
13855 decc_filename_unix_report = 0;
13858 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
13860 decc_filename_unix_only = decc$feature_get_value(s, 1);
13861 if (decc_filename_unix_only > 0) {
13862 decc_filename_unix_only = 1;
13865 decc_filename_unix_only = 0;
13869 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
13871 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
13872 if (decc_filename_unix_no_version < 0)
13873 decc_filename_unix_no_version = 0;
13876 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
13878 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
13879 if (decc_readdir_dropdotnotype < 0)
13880 decc_readdir_dropdotnotype = 0;
13883 #if __CRTL_VER >= 80200000
13884 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
13886 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
13887 if (decc_posix_compliant_pathnames < 0)
13888 decc_posix_compliant_pathnames = 0;
13889 if (decc_posix_compliant_pathnames > 4)
13890 decc_posix_compliant_pathnames = 0;
13895 status = sys_trnlnm
13896 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
13897 if ($VMS_STATUS_SUCCESS(status)) {
13898 val_str[0] = _toupper(val_str[0]);
13899 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13900 decc_disable_to_vms_logname_translation = 1;
13905 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
13906 if ($VMS_STATUS_SUCCESS(status)) {
13907 val_str[0] = _toupper(val_str[0]);
13908 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13909 decc_efs_case_preserve = 1;
13914 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
13915 if ($VMS_STATUS_SUCCESS(status)) {
13916 val_str[0] = _toupper(val_str[0]);
13917 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13918 decc_filename_unix_report = 1;
13921 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
13922 if ($VMS_STATUS_SUCCESS(status)) {
13923 val_str[0] = _toupper(val_str[0]);
13924 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13925 decc_filename_unix_only = 1;
13926 decc_filename_unix_report = 1;
13929 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
13930 if ($VMS_STATUS_SUCCESS(status)) {
13931 val_str[0] = _toupper(val_str[0]);
13932 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13933 decc_filename_unix_no_version = 1;
13936 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
13937 if ($VMS_STATUS_SUCCESS(status)) {
13938 val_str[0] = _toupper(val_str[0]);
13939 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
13940 decc_readdir_dropdotnotype = 1;
13945 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
13947 /* Report true case tolerance */
13948 /*----------------------------*/
13949 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
13950 if (!$VMS_STATUS_SUCCESS(status))
13951 case_perm = PPROP$K_CASE_BLIND;
13952 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
13953 if (!$VMS_STATUS_SUCCESS(status))
13954 case_image = PPROP$K_CASE_BLIND;
13955 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
13956 (case_image == PPROP$K_CASE_SENSITIVE))
13957 vms_process_case_tolerant = 0;
13961 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
13962 /* for strict backward compatibilty */
13963 status = sys_trnlnm
13964 ("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
13965 if ($VMS_STATUS_SUCCESS(status)) {
13966 val_str[0] = _toupper(val_str[0]);
13967 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
13968 vms_posix_exit = 1;
13970 vms_posix_exit = 0;
13974 /* CRTL can be initialized past this point, but not before. */
13975 /* DECC$CRTL_INIT(); */
13982 #pragma extern_model save
13983 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
13984 const __align (LONGWORD) int spare[8] = {0};
13986 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
13987 #if __DECC_VER >= 60560002
13988 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
13990 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
13992 #endif /* __DECC */
13994 const long vms_cc_features = (const long)set_features;
13997 ** Force a reference to LIB$INITIALIZE to ensure it
13998 ** exists in the image.
14000 int lib$initialize(void);
14002 #pragma extern_model strict_refdef
14004 int lib_init_ref = (int) lib$initialize;
14007 #pragma extern_model restore