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 static char * int_rmsexpand_vms(
300 const char * filespec, char * outbuf, unsigned opts);
301 static char * int_rmsexpand_tovms(
302 const char * filespec, char * outbuf, unsigned opts);
303 static char *int_tovmsspec
304 (const char *path, char *buf, int dir_flag, int * utf8_flag);
305 static char * int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl);
306 static char * int_tounixspec(const char *spec, char *buf, int * utf8_fl);
307 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl);
309 /* see system service docs for $TRNLNM -- NOT the same as LNM$_MAX_INDEX */
310 #define PERL_LNM_MAX_ALLOWED_INDEX 127
312 /* OpenVMS User's Guide says at least 9 iterative translations will be performed,
313 * depending on the facility. SHOW LOGICAL does 10, so we'll imitate that for
316 #define PERL_LNM_MAX_ITER 10
318 /* New values with 7.3-2*/ /* why does max DCL have 4 byte subtracted from it? */
319 #if __CRTL_VER >= 70302000 && !defined(__VAX)
320 #define MAX_DCL_SYMBOL (8192)
321 #define MAX_DCL_LINE_LENGTH (4096 - 4)
323 #define MAX_DCL_SYMBOL (1024)
324 #define MAX_DCL_LINE_LENGTH (1024 - 4)
327 static char *__mystrtolower(char *str)
329 if (str) for (; *str; ++str) *str= tolower(*str);
333 static struct dsc$descriptor_s fildevdsc =
334 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
335 static struct dsc$descriptor_s crtlenvdsc =
336 { 8, DSC$K_DTYPE_T, DSC$K_CLASS_S, "CRTL_ENV" };
337 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
338 static struct dsc$descriptor_s *defenv[] = { &fildevdsc, &crtlenvdsc, NULL };
339 static struct dsc$descriptor_s **env_tables = defenv;
340 static bool will_taint = FALSE; /* tainting active, but no PL_curinterp yet */
342 /* True if we shouldn't treat barewords as logicals during directory */
344 static int no_translate_barewords;
347 static int tz_updated = 1;
350 /* DECC Features that may need to affect how Perl interprets
351 * displays filename information
353 static int decc_disable_to_vms_logname_translation = 1;
354 static int decc_disable_posix_root = 1;
355 int decc_efs_case_preserve = 0;
356 static int decc_efs_charset = 0;
357 static int decc_efs_charset_index = -1;
358 static int decc_filename_unix_no_version = 0;
359 static int decc_filename_unix_only = 0;
360 int decc_filename_unix_report = 0;
361 int decc_posix_compliant_pathnames = 0;
362 int decc_readdir_dropdotnotype = 0;
363 static int vms_process_case_tolerant = 1;
364 int vms_vtf7_filenames = 0;
365 int gnv_unix_shell = 0;
366 static int vms_unlink_all_versions = 0;
367 static int vms_posix_exit = 0;
369 /* bug workarounds if needed */
370 int decc_bug_devnull = 1;
371 int decc_dir_barename = 0;
372 int vms_bug_stat_filename = 0;
374 static int vms_debug_on_exception = 0;
375 static int vms_debug_fileify = 0;
377 /* Simple logical name translation */
378 static int simple_trnlnm
379 (const char * logname,
383 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
384 const unsigned long attr = LNM$M_CASE_BLIND;
385 struct dsc$descriptor_s name_dsc;
387 unsigned short result;
388 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
391 name_dsc.dsc$w_length = strlen(logname);
392 name_dsc.dsc$a_pointer = (char *)logname;
393 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
394 name_dsc.dsc$b_class = DSC$K_CLASS_S;
396 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
398 if ($VMS_STATUS_SUCCESS(status)) {
400 /* Null terminate and return the string */
401 /*--------------------------------------*/
410 /* Is this a UNIX file specification?
411 * No longer a simple check with EFS file specs
412 * For now, not a full check, but need to
413 * handle POSIX ^UP^ specifications
414 * Fixing to handle ^/ cases would require
415 * changes to many other conversion routines.
418 static int is_unix_filespec(const char *path)
424 if (strncmp(path,"\"^UP^",5) != 0) {
425 pch1 = strchr(path, '/');
430 /* If the user wants UNIX files, "." needs to be treated as in UNIX */
431 if (decc_filename_unix_report || decc_filename_unix_only) {
432 if (strcmp(path,".") == 0)
440 /* This routine converts a UCS-2 character to be VTF-7 encoded.
443 static void ucs2_to_vtf7
445 unsigned long ucs2_char,
448 unsigned char * ucs_ptr;
451 ucs_ptr = (unsigned char *)&ucs2_char;
455 hex = (ucs_ptr[1] >> 4) & 0xf;
457 outspec[2] = hex + '0';
459 outspec[2] = (hex - 9) + 'A';
460 hex = ucs_ptr[1] & 0xF;
462 outspec[3] = hex + '0';
464 outspec[3] = (hex - 9) + 'A';
466 hex = (ucs_ptr[0] >> 4) & 0xf;
468 outspec[4] = hex + '0';
470 outspec[4] = (hex - 9) + 'A';
471 hex = ucs_ptr[1] & 0xF;
473 outspec[5] = hex + '0';
475 outspec[5] = (hex - 9) + 'A';
481 /* This handles the conversion of a UNIX extended character set to a ^
482 * escaped VMS character.
483 * in a UNIX file specification.
485 * The output count variable contains the number of characters added
486 * to the output string.
488 * The return value is the number of characters read from the input string
490 static int copy_expand_unix_filename_escape
491 (char *outspec, const char *inspec, int *output_cnt, const int * utf8_fl)
499 utf8_flag = *utf8_fl;
503 if (*inspec >= 0x80) {
504 if (utf8_fl && vms_vtf7_filenames) {
505 unsigned long ucs_char;
509 if ((*inspec & 0xE0) == 0xC0) {
511 ucs_char = ((inspec[0] & 0x1F) << 6) + (inspec[1] & 0x3f);
512 if (ucs_char >= 0x80) {
513 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
516 } else if ((*inspec & 0xF0) == 0xE0) {
518 ucs_char = ((inspec[0] & 0xF) << 12) +
519 ((inspec[1] & 0x3f) << 6) +
521 if (ucs_char >= 0x800) {
522 ucs2_to_vtf7(outspec, ucs_char, output_cnt);
526 #if 0 /* I do not see longer sequences supported by OpenVMS */
527 /* Maybe some one can fix this later */
528 } else if ((*inspec & 0xF8) == 0xF0) {
531 } else if ((*inspec & 0xFC) == 0xF8) {
534 } else if ((*inspec & 0xFE) == 0xFC) {
541 /* High bit set, but not a Unicode character! */
543 /* Non printing DECMCS or ISO Latin-1 character? */
544 if (*inspec <= 0x9F) {
548 hex = (*inspec >> 4) & 0xF;
550 outspec[1] = hex + '0';
552 outspec[1] = (hex - 9) + 'A';
556 outspec[2] = hex + '0';
558 outspec[2] = (hex - 9) + 'A';
562 } else if (*inspec == 0xA0) {
568 } else if (*inspec == 0xFF) {
580 /* Is this a macro that needs to be passed through?
581 * Macros start with $( and an alpha character, followed
582 * by a string of alpha numeric characters ending with a )
583 * If this does not match, then encode it as ODS-5.
585 if ((inspec[0] == '$') && (inspec[1] == '(')) {
588 if (isalnum(inspec[2]) || (inspec[2] == '.') || (inspec[2] == '_')) {
590 outspec[0] = inspec[0];
591 outspec[1] = inspec[1];
592 outspec[2] = inspec[2];
594 while(isalnum(inspec[tcnt]) ||
595 (inspec[2] == '.') || (inspec[2] == '_')) {
596 outspec[tcnt] = inspec[tcnt];
599 if (inspec[tcnt] == ')') {
600 outspec[tcnt] = inspec[tcnt];
617 if (decc_efs_charset == 0)
644 /* Don't escape again if following character is
645 * already something we escape.
647 if (strchr(".~!#&\'`()+@{},;[]%^=_\\", *(inspec+1))) {
653 /* But otherwise fall through and escape it. */
655 /* Assume that this is to be escaped */
657 outspec[1] = *inspec;
661 case ' ': /* space */
662 /* Assume that this is to be escaped */
677 /* This handles the expansion of a '^' prefix to the proper character
678 * in a UNIX file specification.
680 * The output count variable contains the number of characters added
681 * to the output string.
683 * The return value is the number of characters read from the input
686 static int copy_expand_vms_filename_escape
687 (char *outspec, const char *inspec, int *output_cnt)
694 if (*inspec == '^') {
697 /* Spaces and non-trailing dots should just be passed through,
698 * but eat the escape character.
705 case '_': /* space */
711 /* Hmm. Better leave the escape escaped. */
717 case 'U': /* Unicode - FIX-ME this is wrong. */
720 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
723 scnt = sscanf(inspec, "%2x%2x", &c1, &c2);
724 outspec[0] == c1 & 0xff;
725 outspec[1] == c2 & 0xff;
732 /* Error - do best we can to continue */
742 scnt = strspn(inspec, "0123456789ABCDEFabcdef");
746 scnt = sscanf(inspec, "%2x", &c1);
747 outspec[0] = c1 & 0xff;
771 (const struct dsc$descriptor_s * srcstr,
772 struct filescan_itmlst_2 * valuelist,
773 unsigned long * fldflags,
774 struct dsc$descriptor_s *auxout,
775 unsigned short * retlen);
778 /* vms_split_path - Verify that the input file specification is a
779 * VMS format file specification, and provide pointers to the components of
780 * it. With EFS format filenames, this is virtually the only way to
781 * parse a VMS path specification into components.
783 * If the sum of the components do not add up to the length of the
784 * string, then the passed file specification is probably a UNIX style
787 static int vms_split_path
802 struct dsc$descriptor path_desc;
806 struct filescan_itmlst_2 item_list[9];
807 const int filespec = 0;
808 const int nodespec = 1;
809 const int devspec = 2;
810 const int rootspec = 3;
811 const int dirspec = 4;
812 const int namespec = 5;
813 const int typespec = 6;
814 const int verspec = 7;
816 /* Assume the worst for an easy exit */
831 path_desc.dsc$a_pointer = (char *)path; /* cast ok */
832 path_desc.dsc$w_length = strlen(path);
833 path_desc.dsc$b_dtype = DSC$K_DTYPE_T;
834 path_desc.dsc$b_class = DSC$K_CLASS_S;
836 /* Get the total length, if it is shorter than the string passed
837 * then this was probably not a VMS formatted file specification
839 item_list[filespec].itmcode = FSCN$_FILESPEC;
840 item_list[filespec].length = 0;
841 item_list[filespec].component = NULL;
843 /* If the node is present, then it gets considered as part of the
844 * volume name to hopefully make things simple.
846 item_list[nodespec].itmcode = FSCN$_NODE;
847 item_list[nodespec].length = 0;
848 item_list[nodespec].component = NULL;
850 item_list[devspec].itmcode = FSCN$_DEVICE;
851 item_list[devspec].length = 0;
852 item_list[devspec].component = NULL;
854 /* root is a special case, adding it to either the directory or
855 * the device components will probalby complicate things for the
856 * callers of this routine, so leave it separate.
858 item_list[rootspec].itmcode = FSCN$_ROOT;
859 item_list[rootspec].length = 0;
860 item_list[rootspec].component = NULL;
862 item_list[dirspec].itmcode = FSCN$_DIRECTORY;
863 item_list[dirspec].length = 0;
864 item_list[dirspec].component = NULL;
866 item_list[namespec].itmcode = FSCN$_NAME;
867 item_list[namespec].length = 0;
868 item_list[namespec].component = NULL;
870 item_list[typespec].itmcode = FSCN$_TYPE;
871 item_list[typespec].length = 0;
872 item_list[typespec].component = NULL;
874 item_list[verspec].itmcode = FSCN$_VERSION;
875 item_list[verspec].length = 0;
876 item_list[verspec].component = NULL;
878 item_list[8].itmcode = 0;
879 item_list[8].length = 0;
880 item_list[8].component = NULL;
882 status = sys$filescan
883 ((const struct dsc$descriptor_s *)&path_desc, item_list,
885 _ckvmssts_noperl(status); /* All failure status values indicate a coding error */
887 /* If we parsed it successfully these two lengths should be the same */
888 if (path_desc.dsc$w_length != item_list[filespec].length)
891 /* If we got here, then it is a VMS file specification */
894 /* set the volume name */
895 if (item_list[nodespec].length > 0) {
896 *volume = item_list[nodespec].component;
897 *vol_len = item_list[nodespec].length + item_list[devspec].length;
900 *volume = item_list[devspec].component;
901 *vol_len = item_list[devspec].length;
904 *root = item_list[rootspec].component;
905 *root_len = item_list[rootspec].length;
907 *dir = item_list[dirspec].component;
908 *dir_len = item_list[dirspec].length;
910 /* Now fun with versions and EFS file specifications
911 * The parser can not tell the difference when a "." is a version
912 * delimiter or a part of the file specification.
914 if ((decc_efs_charset) &&
915 (item_list[verspec].length > 0) &&
916 (item_list[verspec].component[0] == '.')) {
917 *name = item_list[namespec].component;
918 *name_len = item_list[namespec].length + item_list[typespec].length;
919 *ext = item_list[verspec].component;
920 *ext_len = item_list[verspec].length;
925 *name = item_list[namespec].component;
926 *name_len = item_list[namespec].length;
927 *ext = item_list[typespec].component;
928 *ext_len = item_list[typespec].length;
929 *version = item_list[verspec].component;
930 *ver_len = item_list[verspec].length;
935 /* Routine to determine if the file specification ends with .dir */
936 static int is_dir_ext(char * e_spec, int e_len, char * vs_spec, int vs_len) {
938 /* e_len must be 4, and version must be <= 2 characters */
939 if (e_len != 4 || vs_len > 2)
942 /* If a version number is present, it needs to be one */
943 if ((vs_len == 2) && (vs_spec[1] != '1'))
946 /* Look for the DIR on the extension */
947 if (vms_process_case_tolerant) {
948 if ((toupper(e_spec[1]) == 'D') &&
949 (toupper(e_spec[2]) == 'I') &&
950 (toupper(e_spec[3]) == 'R')) {
954 /* Directory extensions are supposed to be in upper case only */
955 /* I would not be surprised if this rule can not be enforced */
956 /* if and when someone fully debugs the case sensitive mode */
957 if ((e_spec[1] == 'D') &&
958 (e_spec[2] == 'I') &&
959 (e_spec[3] == 'R')) {
968 * Routine to retrieve the maximum equivalence index for an input
969 * logical name. Some calls to this routine have no knowledge if
970 * the variable is a logical or not. So on error we return a max
973 /*{{{int my_maxidx(const char *lnm) */
975 my_maxidx(const char *lnm)
979 int attr = LNM$M_CASE_BLIND;
980 struct dsc$descriptor lnmdsc;
981 struct itmlst_3 itlst[2] = {{sizeof(midx), LNM$_MAX_INDEX, &midx, 0},
984 lnmdsc.dsc$w_length = strlen(lnm);
985 lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
986 lnmdsc.dsc$b_class = DSC$K_CLASS_S;
987 lnmdsc.dsc$a_pointer = (char *) lnm; /* Cast ok for read only parameter */
989 status = sys$trnlnm(&attr, &fildevdsc, &lnmdsc, 0, itlst);
990 if ((status & 1) == 0)
997 /*{{{int vmstrnenv(const char *lnm, char *eqv, unsigned long int idx, struct dsc$descriptor_s **tabvec, unsigned long int flags) */
999 Perl_vmstrnenv(const char *lnm, char *eqv, unsigned long int idx,
1000 struct dsc$descriptor_s **tabvec, unsigned long int flags)
1003 char uplnm[LNM$C_NAMLENGTH+1], *cp2;
1004 unsigned short int eqvlen, curtab, ivlnm = 0, ivsym = 0, ivenv = 0, secure;
1005 unsigned long int retsts, attr = LNM$M_CASE_BLIND;
1007 unsigned char acmode;
1008 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1009 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1010 struct itmlst_3 lnmlst[3] = {{sizeof idx, LNM$_INDEX, &idx, 0},
1011 {LNM$C_NAMLENGTH, LNM$_STRING, eqv, &eqvlen},
1013 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1014 #if defined(PERL_IMPLICIT_CONTEXT)
1017 aTHX = PERL_GET_INTERP;
1023 if (!lnm || !eqv || ((idx != 0) && ((idx-1) > PERL_LNM_MAX_ALLOWED_INDEX))) {
1024 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return 0;
1026 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1027 *cp2 = _toupper(*cp1);
1028 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1029 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1033 lnmdsc.dsc$w_length = cp1 - lnm;
1034 lnmdsc.dsc$a_pointer = uplnm;
1035 uplnm[lnmdsc.dsc$w_length] = '\0';
1036 secure = flags & PERL__TRNENV_SECURE;
1037 acmode = secure ? PSL$C_EXEC : PSL$C_USER;
1038 if (!tabvec || !*tabvec) tabvec = env_tables;
1040 for (curtab = 0; tabvec[curtab]; curtab++) {
1041 if (!str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1042 if (!ivenv && !secure) {
1047 #if defined(PERL_IMPLICIT_CONTEXT)
1050 "%%PERL-W-VMS_INIT Can't read CRTL environ\n");
1053 Perl_warn(aTHX_ "Can't read CRTL environ\n");
1056 retsts = SS$_NOLOGNAM;
1057 for (i = 0; environ[i]; i++) {
1058 if ((eq = strchr(environ[i],'=')) &&
1059 lnmdsc.dsc$w_length == (eq - environ[i]) &&
1060 !strncmp(environ[i],uplnm,eq - environ[i])) {
1062 for (eqvlen = 0; eq[eqvlen]; eqvlen++) eqv[eqvlen] = eq[eqvlen];
1063 if (!eqvlen) continue;
1064 retsts = SS$_NORMAL;
1068 if (retsts != SS$_NOLOGNAM) break;
1071 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1072 !str$case_blind_compare(&tmpdsc,&clisym)) {
1073 if (!ivsym && !secure) {
1074 unsigned short int deflen = LNM$C_NAMLENGTH;
1075 struct dsc$descriptor_d eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1076 /* dynamic dsc to accomodate possible long value */
1077 _ckvmssts_noperl(lib$sget1_dd(&deflen,&eqvdsc));
1078 retsts = lib$get_symbol(&lnmdsc,&eqvdsc,&eqvlen,0);
1080 if (eqvlen > MAX_DCL_SYMBOL) {
1081 set_errno(EVMSERR); set_vaxc_errno(LIB$_STRTRU);
1082 eqvlen = MAX_DCL_SYMBOL;
1083 /* Special hack--we might be called before the interpreter's */
1084 /* fully initialized, in which case either thr or PL_curcop */
1085 /* might be bogus. We have to check, since ckWARN needs them */
1086 /* both to be valid if running threaded */
1087 #if defined(PERL_IMPLICIT_CONTEXT)
1090 "%Perl-VMS-Init, Value of CLI symbol \"%s\" too long",lnm);
1093 if (ckWARN(WARN_MISC)) {
1094 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of CLI symbol \"%s\" too long",lnm);
1097 strncpy(eqv,eqvdsc.dsc$a_pointer,eqvlen);
1099 _ckvmssts_noperl(lib$sfree1_dd(&eqvdsc));
1100 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1101 if (retsts == LIB$_NOSUCHSYM) continue;
1106 if ( (idx == 0) && (flags & PERL__TRNENV_JOIN_SEARCHLIST) ) {
1107 midx = my_maxidx(lnm);
1108 for (idx = 0, cp2 = eqv; idx <= midx; idx++) {
1109 lnmlst[1].bufadr = cp2;
1111 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1112 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; break; }
1113 if (retsts == SS$_NOLOGNAM) break;
1114 /* PPFs have a prefix */
1117 *((int *)uplnm) == *((int *)"SYS$") &&
1119 eqvlen >= 4 && eqv[0] == 0x1b && eqv[1] == 0x00 &&
1120 ( (uplnm[4] == 'O' && !strcmp(uplnm,"SYS$OUTPUT")) ||
1121 (uplnm[4] == 'I' && !strcmp(uplnm,"SYS$INPUT")) ||
1122 (uplnm[4] == 'E' && !strcmp(uplnm,"SYS$ERROR")) ||
1123 (uplnm[4] == 'C' && !strcmp(uplnm,"SYS$COMMAND")) ) ) {
1124 memmove(eqv,eqv+4,eqvlen-4);
1130 if ((retsts == SS$_IVLOGNAM) ||
1131 (retsts == SS$_NOLOGNAM)) { continue; }
1134 retsts = sys$trnlnm(&attr,tabvec[curtab],&lnmdsc,&acmode,lnmlst);
1135 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1136 if (retsts == SS$_NOLOGNAM) continue;
1139 eqvlen = strlen(eqv);
1143 if (retsts & 1) { eqv[eqvlen] = '\0'; return eqvlen; }
1144 else if (retsts == LIB$_NOSUCHSYM || retsts == LIB$_INVSYMNAM ||
1145 retsts == SS$_IVLOGNAM || retsts == SS$_IVLOGTAB ||
1146 retsts == SS$_NOLOGNAM) {
1147 set_errno(EINVAL); set_vaxc_errno(retsts);
1149 else _ckvmssts_noperl(retsts);
1151 } /* end of vmstrnenv */
1154 /*{{{ int my_trnlnm(const char *lnm, char *eqv, unsigned long int idx)*/
1155 /* Define as a function so we can access statics. */
1156 int Perl_my_trnlnm(pTHX_ const char *lnm, char *eqv, unsigned long int idx)
1160 #if defined(PERL_IMPLICIT_CONTEXT)
1163 #ifdef SECURE_INTERNAL_GETENV
1164 flags = (PL_curinterp ? PL_tainting : will_taint) ?
1165 PERL__TRNENV_SECURE : 0;
1168 return vmstrnenv(lnm, eqv, idx, fildev, flags);
1173 * Note: Uses Perl temp to store result so char * can be returned to
1174 * caller; this pointer will be invalidated at next Perl statement
1176 * We define this as a function rather than a macro in terms of my_getenv_len()
1177 * so that it'll work when PL_curinterp is undefined (and we therefore can't
1180 /*{{{ char *my_getenv(const char *lnm, bool sys)*/
1182 Perl_my_getenv(pTHX_ const char *lnm, bool sys)
1185 static char *__my_getenv_eqv = NULL;
1186 char uplnm[LNM$C_NAMLENGTH+1], *cp2, *eqv;
1187 unsigned long int idx = 0;
1188 int trnsuccess, success, secure, saverr, savvmserr;
1192 midx = my_maxidx(lnm) + 1;
1194 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1195 /* Set up a temporary buffer for the return value; Perl will
1196 * clean it up at the next statement transition */
1197 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1198 if (!tmpsv) return NULL;
1202 /* Assume no interpreter ==> single thread */
1203 if (__my_getenv_eqv != NULL) {
1204 Renew(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1207 Newx(__my_getenv_eqv,LNM$C_NAMLENGTH*midx+1,char);
1209 eqv = __my_getenv_eqv;
1212 for (cp1 = lnm, cp2 = eqv; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1213 if (cp1 - lnm == 7 && !strncmp(eqv,"DEFAULT",7)) {
1215 getcwd(eqv,LNM$C_NAMLENGTH);
1219 /* Get rid of "000000/ in rooted filespecs */
1222 zeros = strstr(eqv, "/000000/");
1223 if (zeros != NULL) {
1225 mlen = len - (zeros - eqv) - 7;
1226 memmove(zeros, &zeros[7], mlen);
1234 /* Impose security constraints only if tainting */
1236 /* Impose security constraints only if tainting */
1237 secure = PL_curinterp ? PL_tainting : will_taint;
1238 saverr = errno; savvmserr = vaxc$errno;
1245 #ifdef SECURE_INTERNAL_GETENV
1246 secure ? PERL__TRNENV_SECURE : 0
1252 /* For the getenv interface we combine all the equivalence names
1253 * of a search list logical into one value to acquire a maximum
1254 * value length of 255*128 (assuming %ENV is using logicals).
1256 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1258 /* If the name contains a semicolon-delimited index, parse it
1259 * off and make sure we only retrieve the equivalence name for
1261 if ((cp2 = strchr(lnm,';')) != NULL) {
1263 uplnm[cp2-lnm] = '\0';
1264 idx = strtoul(cp2+1,NULL,0);
1266 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1269 success = vmstrnenv(lnm,eqv,idx,secure ? fildev : NULL,flags);
1271 /* Discard NOLOGNAM on internal calls since we're often looking
1272 * for an optional name, and this "error" often shows up as the
1273 * (bogus) exit status for a die() call later on. */
1274 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1275 return success ? eqv : NULL;
1278 } /* end of my_getenv() */
1282 /*{{{ SV *my_getenv_len(const char *lnm, bool sys)*/
1284 Perl_my_getenv_len(pTHX_ const char *lnm, unsigned long *len, bool sys)
1288 unsigned long idx = 0;
1290 static char *__my_getenv_len_eqv = NULL;
1291 int secure, saverr, savvmserr;
1294 midx = my_maxidx(lnm) + 1;
1296 if (PL_curinterp) { /* Perl interpreter running -- may be threaded */
1297 /* Set up a temporary buffer for the return value; Perl will
1298 * clean it up at the next statement transition */
1299 tmpsv = sv_2mortal(newSVpv("",(LNM$C_NAMLENGTH*midx)+1));
1300 if (!tmpsv) return NULL;
1304 /* Assume no interpreter ==> single thread */
1305 if (__my_getenv_len_eqv != NULL) {
1306 Renew(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1309 Newx(__my_getenv_len_eqv,LNM$C_NAMLENGTH*midx+1,char);
1311 buf = __my_getenv_len_eqv;
1314 for (cp1 = lnm, cp2 = buf; *cp1; cp1++,cp2++) *cp2 = _toupper(*cp1);
1315 if (cp1 - lnm == 7 && !strncmp(buf,"DEFAULT",7)) {
1318 getcwd(buf,LNM$C_NAMLENGTH);
1321 /* Get rid of "000000/ in rooted filespecs */
1323 zeros = strstr(buf, "/000000/");
1324 if (zeros != NULL) {
1326 mlen = *len - (zeros - buf) - 7;
1327 memmove(zeros, &zeros[7], mlen);
1336 /* Impose security constraints only if tainting */
1337 secure = PL_curinterp ? PL_tainting : will_taint;
1338 saverr = errno; savvmserr = vaxc$errno;
1345 #ifdef SECURE_INTERNAL_GETENV
1346 secure ? PERL__TRNENV_SECURE : 0
1352 flags |= PERL__TRNENV_JOIN_SEARCHLIST;
1354 if ((cp2 = strchr(lnm,';')) != NULL) {
1356 buf[cp2-lnm] = '\0';
1357 idx = strtoul(cp2+1,NULL,0);
1359 flags &= ~PERL__TRNENV_JOIN_SEARCHLIST;
1362 *len = vmstrnenv(lnm,buf,idx,secure ? fildev : NULL,flags);
1364 /* Get rid of "000000/ in rooted filespecs */
1367 zeros = strstr(buf, "/000000/");
1368 if (zeros != NULL) {
1370 mlen = *len - (zeros - buf) - 7;
1371 memmove(zeros, &zeros[7], mlen);
1377 /* Discard NOLOGNAM on internal calls since we're often looking
1378 * for an optional name, and this "error" often shows up as the
1379 * (bogus) exit status for a die() call later on. */
1380 if (sys && vaxc$errno == SS$_NOLOGNAM) SETERRNO(saverr,savvmserr);
1381 return *len ? buf : NULL;
1384 } /* end of my_getenv_len() */
1387 static void create_mbx(unsigned short int *, struct dsc$descriptor_s *);
1389 static void riseandshine(unsigned long int dummy) { sys$wake(0,0); }
1391 /*{{{ void prime_env_iter() */
1393 prime_env_iter(void)
1394 /* Fill the %ENV associative array with all logical names we can
1395 * find, in preparation for iterating over it.
1398 static int primed = 0;
1399 HV *seenhv = NULL, *envhv;
1401 char cmd[LNM$C_NAMLENGTH+24], mbxnam[LNM$C_NAMLENGTH], *buf = NULL;
1402 unsigned short int chan;
1403 #ifndef CLI$M_TRUSTED
1404 # define CLI$M_TRUSTED 0x40 /* Missing from VAXC headers */
1406 unsigned long int defflags = CLI$M_NOWAIT | CLI$M_NOKEYPAD | CLI$M_TRUSTED;
1407 unsigned long int mbxbufsiz, flags, retsts, subpid = 0, substs = 0, wakect = 0;
1409 bool have_sym = FALSE, have_lnm = FALSE;
1410 struct dsc$descriptor_s tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1411 $DESCRIPTOR(cmddsc,cmd); $DESCRIPTOR(nldsc,"_NLA0:");
1412 $DESCRIPTOR(clidsc,"DCL"); $DESCRIPTOR(clitabdsc,"DCLTABLES");
1413 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1414 $DESCRIPTOR(local,"_LOCAL"); $DESCRIPTOR(mbxdsc,mbxnam);
1415 #if defined(PERL_IMPLICIT_CONTEXT)
1418 #if defined(USE_ITHREADS)
1419 static perl_mutex primenv_mutex;
1420 MUTEX_INIT(&primenv_mutex);
1423 #if defined(PERL_IMPLICIT_CONTEXT)
1424 /* We jump through these hoops because we can be called at */
1425 /* platform-specific initialization time, which is before anything is */
1426 /* set up--we can't even do a plain dTHX since that relies on the */
1427 /* interpreter structure to be initialized */
1429 aTHX = PERL_GET_INTERP;
1431 /* we never get here because the NULL pointer will cause the */
1432 /* several of the routines called by this routine to access violate */
1434 /* This routine is only called by hv.c/hv_iterinit which has a */
1435 /* context, so the real fix may be to pass it through instead of */
1436 /* the hoops above */
1441 if (primed || !PL_envgv) return;
1442 MUTEX_LOCK(&primenv_mutex);
1443 if (primed) { MUTEX_UNLOCK(&primenv_mutex); return; }
1444 envhv = GvHVn(PL_envgv);
1445 /* Perform a dummy fetch as an lval to insure that the hash table is
1446 * set up. Otherwise, the hv_store() will turn into a nullop. */
1447 (void) hv_fetch(envhv,"DEFAULT",7,TRUE);
1449 for (i = 0; env_tables[i]; i++) {
1450 if (!have_sym && (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1451 !str$case_blind_compare(&tmpdsc,&clisym)) have_sym = 1;
1452 if (!have_lnm && str$case_blind_compare(env_tables[i],&crtlenv)) have_lnm = 1;
1454 if (have_sym || have_lnm) {
1455 long int syiitm = SYI$_MAXBUF, dviitm = DVI$_DEVNAM;
1456 _ckvmssts(lib$getsyi(&syiitm, &mbxbufsiz, 0, 0, 0, 0));
1457 _ckvmssts(sys$crembx(0,&chan,mbxbufsiz,mbxbufsiz,0xff0f,0,0));
1458 _ckvmssts(lib$getdvi(&dviitm, &chan, NULL, NULL, &mbxdsc, &mbxdsc.dsc$w_length));
1461 for (i--; i >= 0; i--) {
1462 if (!str$case_blind_compare(env_tables[i],&crtlenv)) {
1465 for (j = 0; environ[j]; j++) {
1466 if (!(start = strchr(environ[j],'='))) {
1467 if (ckWARN(WARN_INTERNAL))
1468 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed CRTL environ value \"%s\"\n",environ[j]);
1472 sv = newSVpv(start,0);
1474 (void) hv_store(envhv,environ[j],start - environ[j] - 1,sv,0);
1479 else if ((tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer) &&
1480 !str$case_blind_compare(&tmpdsc,&clisym)) {
1481 strcpy(cmd,"Show Symbol/Global *");
1482 cmddsc.dsc$w_length = 20;
1483 if (env_tables[i]->dsc$w_length == 12 &&
1484 (tmpdsc.dsc$a_pointer = env_tables[i]->dsc$a_pointer + 6) &&
1485 !str$case_blind_compare(&tmpdsc,&local)) strcpy(cmd+12,"Local *");
1486 flags = defflags | CLI$M_NOLOGNAM;
1489 strcpy(cmd,"Show Logical *");
1490 if (str$case_blind_compare(env_tables[i],&fildevdsc)) {
1491 strcat(cmd," /Table=");
1492 strncat(cmd,env_tables[i]->dsc$a_pointer,env_tables[i]->dsc$w_length);
1493 cmddsc.dsc$w_length = strlen(cmd);
1495 else cmddsc.dsc$w_length = 14; /* N.B. We test this below */
1496 flags = defflags | CLI$M_NOCLISYM;
1499 /* Create a new subprocess to execute each command, to exclude the
1500 * remote possibility that someone could subvert a mbx or file used
1501 * to write multiple commands to a single subprocess.
1504 retsts = lib$spawn(&cmddsc,&nldsc,&mbxdsc,&flags,0,&subpid,&substs,
1505 0,&riseandshine,0,0,&clidsc,&clitabdsc);
1506 flags &= ~CLI$M_TRUSTED; /* Just in case we hit a really old version */
1507 defflags &= ~CLI$M_TRUSTED;
1508 } while (retsts == LIB$_INVARG && (flags | CLI$M_TRUSTED));
1510 if (!buf) Newx(buf,mbxbufsiz + 1,char);
1511 if (seenhv) SvREFCNT_dec(seenhv);
1514 char *cp1, *cp2, *key;
1515 unsigned long int sts, iosb[2], retlen, keylen;
1518 sts = sys$qiow(0,chan,IO$_READVBLK,iosb,0,0,buf,mbxbufsiz,0,0,0,0);
1519 if (sts & 1) sts = iosb[0] & 0xffff;
1520 if (sts == SS$_ENDOFFILE) {
1522 while (substs == 0) { sys$hiber(); wakect++;}
1523 if (wakect > 1) sys$wake(0,0); /* Stole someone else's wake */
1528 retlen = iosb[0] >> 16;
1529 if (!retlen) continue; /* blank line */
1531 if (iosb[1] != subpid) {
1533 Perl_croak(aTHX_ "Unknown process %x sent message to prime_env_iter: %s",buf);
1537 if (sts == SS$_BUFFEROVF && ckWARN(WARN_INTERNAL))
1538 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Buffer overflow in prime_env_iter: %s",buf);
1540 for (cp1 = buf; *cp1 && isspace(*cp1); cp1++) ;
1541 if (*cp1 == '(' || /* Logical name table name */
1542 *cp1 == '=' /* Next eqv of searchlist */) continue;
1543 if (*cp1 == '"') cp1++;
1544 for (cp2 = cp1; *cp2 && *cp2 != '"' && *cp2 != ' '; cp2++) ;
1545 key = cp1; keylen = cp2 - cp1;
1546 if (keylen && hv_exists(seenhv,key,keylen)) continue;
1547 while (*cp2 && *cp2 != '=') cp2++;
1548 while (*cp2 && *cp2 == '=') cp2++;
1549 while (*cp2 && *cp2 == ' ') cp2++;
1550 if (*cp2 == '"') { /* String translation; may embed "" */
1551 for (cp1 = buf + retlen; *cp1 != '"'; cp1--) ;
1552 cp2++; cp1--; /* Skip "" surrounding translation */
1554 else { /* Numeric translation */
1555 for (cp1 = cp2; *cp1 && *cp1 != ' '; cp1++) ;
1556 cp1--; /* stop on last non-space char */
1558 if ((!keylen || (cp1 - cp2 < -1)) && ckWARN(WARN_INTERNAL)) {
1559 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"Ill-formed message in prime_env_iter: |%s|",buf);
1562 PERL_HASH(hash,key,keylen);
1564 if (cp1 == cp2 && *cp2 == '.') {
1565 /* A single dot usually means an unprintable character, such as a null
1566 * to indicate a zero-length value. Get the actual value to make sure.
1568 char lnm[LNM$C_NAMLENGTH+1];
1569 char eqv[MAX_DCL_SYMBOL+1];
1571 strncpy(lnm, key, keylen);
1572 trnlen = vmstrnenv(lnm, eqv, 0, fildev, 0);
1573 sv = newSVpvn(eqv, strlen(eqv));
1576 sv = newSVpvn(cp2,cp1 - cp2 + 1);
1580 hv_store(envhv,key,keylen,sv,hash);
1581 hv_store(seenhv,key,keylen,&PL_sv_yes,hash);
1583 if (cmddsc.dsc$w_length == 14) { /* We just read LNM$FILE_DEV */
1584 /* get the PPFs for this process, not the subprocess */
1585 const char *ppfs[] = {"SYS$COMMAND", "SYS$INPUT", "SYS$OUTPUT", "SYS$ERROR", NULL};
1586 char eqv[LNM$C_NAMLENGTH+1];
1588 for (i = 0; ppfs[i]; i++) {
1589 trnlen = vmstrnenv(ppfs[i],eqv,0,fildev,0);
1590 sv = newSVpv(eqv,trnlen);
1592 hv_store(envhv,ppfs[i],strlen(ppfs[i]),sv,0);
1597 if (have_sym || have_lnm) _ckvmssts(sys$dassgn(chan));
1598 if (buf) Safefree(buf);
1599 if (seenhv) SvREFCNT_dec(seenhv);
1600 MUTEX_UNLOCK(&primenv_mutex);
1603 } /* end of prime_env_iter */
1607 /*{{{ int vmssetenv(const char *lnm, const char *eqv)*/
1608 /* Define or delete an element in the same "environment" as
1609 * vmstrnenv(). If an element is to be deleted, it's removed from
1610 * the first place it's found. If it's to be set, it's set in the
1611 * place designated by the first element of the table vector.
1612 * Like setenv() returns 0 for success, non-zero on error.
1615 Perl_vmssetenv(pTHX_ const char *lnm, const char *eqv, struct dsc$descriptor_s **tabvec)
1618 char uplnm[LNM$C_NAMLENGTH], *cp2, *c;
1619 unsigned short int curtab, ivlnm = 0, ivsym = 0, ivenv = 0;
1621 unsigned long int retsts, usermode = PSL$C_USER;
1622 struct itmlst_3 *ile, *ilist;
1623 struct dsc$descriptor_s lnmdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,uplnm},
1624 eqvdsc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0},
1625 tmpdsc = {6,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
1626 $DESCRIPTOR(crtlenv,"CRTL_ENV"); $DESCRIPTOR(clisym,"CLISYM");
1627 $DESCRIPTOR(local,"_LOCAL");
1630 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1631 return SS$_IVLOGNAM;
1634 for (cp1 = lnm, cp2 = uplnm; *cp1; cp1++, cp2++) {
1635 *cp2 = _toupper(*cp1);
1636 if (cp1 - lnm > LNM$C_NAMLENGTH) {
1637 set_errno(EINVAL); set_vaxc_errno(SS$_IVLOGNAM);
1638 return SS$_IVLOGNAM;
1641 lnmdsc.dsc$w_length = cp1 - lnm;
1642 if (!tabvec || !*tabvec) tabvec = env_tables;
1644 if (!eqv) { /* we're deleting n element */
1645 for (curtab = 0; tabvec[curtab]; curtab++) {
1646 if (!ivenv && !str$case_blind_compare(tabvec[curtab],&crtlenv)) {
1648 for (i = 0; environ[i]; i++) { /* If it's an environ elt, reset */
1649 if ((cp1 = strchr(environ[i],'=')) &&
1650 lnmdsc.dsc$w_length == (cp1 - environ[i]) &&
1651 !strncmp(environ[i],lnm,cp1 - environ[i])) {
1653 return setenv(lnm,"",1) ? vaxc$errno : 0;
1656 ivenv = 1; retsts = SS$_NOLOGNAM;
1658 if (ckWARN(WARN_INTERNAL))
1659 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't reset CRTL environ elements (%s)",lnm);
1660 ivenv = 1; retsts = SS$_NOSUCHPGM;
1666 else if ((tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer) &&
1667 !str$case_blind_compare(&tmpdsc,&clisym)) {
1668 unsigned int symtype;
1669 if (tabvec[curtab]->dsc$w_length == 12 &&
1670 (tmpdsc.dsc$a_pointer = tabvec[curtab]->dsc$a_pointer + 6) &&
1671 !str$case_blind_compare(&tmpdsc,&local))
1672 symtype = LIB$K_CLI_LOCAL_SYM;
1673 else symtype = LIB$K_CLI_GLOBAL_SYM;
1674 retsts = lib$delete_symbol(&lnmdsc,&symtype);
1675 if (retsts == LIB$_INVSYMNAM) { ivsym = 1; continue; }
1676 if (retsts == LIB$_NOSUCHSYM) continue;
1680 retsts = sys$dellnm(tabvec[curtab],&lnmdsc,&usermode); /* try user mode first */
1681 if (retsts == SS$_IVLOGNAM) { ivlnm = 1; continue; }
1682 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1683 retsts = lib$delete_logical(&lnmdsc,tabvec[curtab]); /* then supervisor mode */
1684 if (retsts != SS$_NOLOGNAM && retsts != SS$_NOLOGTAB) break;
1688 else { /* we're defining a value */
1689 if (!ivenv && !str$case_blind_compare(tabvec[0],&crtlenv)) {
1691 return setenv(lnm,eqv,1) ? vaxc$errno : 0;
1693 if (ckWARN(WARN_INTERNAL))
1694 Perl_warner(aTHX_ packWARN(WARN_INTERNAL),"This Perl can't set CRTL environ elements (%s=%s)",lnm,eqv);
1695 retsts = SS$_NOSUCHPGM;
1699 eqvdsc.dsc$a_pointer = (char *) eqv; /* cast ok to readonly parameter */
1700 eqvdsc.dsc$w_length = strlen(eqv);
1701 if ((tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer) &&
1702 !str$case_blind_compare(&tmpdsc,&clisym)) {
1703 unsigned int symtype;
1704 if (tabvec[0]->dsc$w_length == 12 &&
1705 (tmpdsc.dsc$a_pointer = tabvec[0]->dsc$a_pointer + 6) &&
1706 !str$case_blind_compare(&tmpdsc,&local))
1707 symtype = LIB$K_CLI_LOCAL_SYM;
1708 else symtype = LIB$K_CLI_GLOBAL_SYM;
1709 retsts = lib$set_symbol(&lnmdsc,&eqvdsc,&symtype);
1712 if (!*eqv) eqvdsc.dsc$w_length = 1;
1713 if (eqvdsc.dsc$w_length > LNM$C_NAMLENGTH) {
1715 nseg = (eqvdsc.dsc$w_length + LNM$C_NAMLENGTH - 1) / LNM$C_NAMLENGTH;
1716 if (nseg > PERL_LNM_MAX_ALLOWED_INDEX + 1) {
1717 Perl_warner(aTHX_ packWARN(WARN_MISC),"Value of logical \"%s\" too long. Truncating to %i bytes",
1718 lnm, LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1));
1719 eqvdsc.dsc$w_length = LNM$C_NAMLENGTH * (PERL_LNM_MAX_ALLOWED_INDEX+1);
1720 nseg = PERL_LNM_MAX_ALLOWED_INDEX + 1;
1723 Newx(ilist,nseg+1,struct itmlst_3);
1726 set_errno(ENOMEM); set_vaxc_errno(SS$_INSFMEM);
1729 memset(ilist, 0, (sizeof(struct itmlst_3) * (nseg+1)));
1731 for (j = 0, c = eqvdsc.dsc$a_pointer; j < nseg; j++, ile++, c += LNM$C_NAMLENGTH) {
1732 ile->itmcode = LNM$_STRING;
1734 if ((j+1) == nseg) {
1735 ile->buflen = strlen(c);
1736 /* in case we are truncating one that's too long */
1737 if (ile->buflen > LNM$C_NAMLENGTH) ile->buflen = LNM$C_NAMLENGTH;
1740 ile->buflen = LNM$C_NAMLENGTH;
1744 retsts = lib$set_logical(&lnmdsc,0,tabvec[0],0,ilist);
1748 retsts = lib$set_logical(&lnmdsc,&eqvdsc,tabvec[0],0,0);
1753 if (!(retsts & 1)) {
1755 case LIB$_AMBSYMDEF: case LIB$_INSCLIMEM:
1756 case SS$_NOLOGTAB: case SS$_TOOMANYLNAM: case SS$_IVLOGTAB:
1757 set_errno(EVMSERR); break;
1758 case LIB$_INVARG: case LIB$_INVSYMNAM: case SS$_IVLOGNAM:
1759 case LIB$_NOSUCHSYM: case SS$_NOLOGNAM:
1760 set_errno(EINVAL); break;
1762 set_errno(EACCES); break;
1767 set_vaxc_errno(retsts);
1768 return (int) retsts || 44; /* retsts should never be 0, but just in case */
1771 /* We reset error values on success because Perl does an hv_fetch()
1772 * before each hv_store(), and if the thing we're setting didn't
1773 * previously exist, we've got a leftover error message. (Of course,
1774 * this fails in the face of
1775 * $foo = $ENV{nonexistent}; $ENV{existent} = 'foo';
1776 * in that the error reported in $! isn't spurious,
1777 * but it's right more often than not.)
1779 set_errno(0); set_vaxc_errno(retsts);
1783 } /* end of vmssetenv() */
1786 /*{{{ void my_setenv(const char *lnm, const char *eqv)*/
1787 /* This has to be a function since there's a prototype for it in proto.h */
1789 Perl_my_setenv(pTHX_ const char *lnm, const char *eqv)
1792 int len = strlen(lnm);
1796 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1797 if (!strcmp(uplnm,"DEFAULT")) {
1798 if (eqv && *eqv) my_chdir(eqv);
1802 #ifndef RTL_USES_UTC
1803 if (len == 6 || len == 2) {
1806 for (i = 0; lnm[i]; i++) uplnm[i] = _toupper(lnm[i]);
1808 if (!strcmp(uplnm,"UCX$TZ")) tz_updated = 1;
1809 if (!strcmp(uplnm,"TZ")) tz_updated = 1;
1813 (void) vmssetenv(lnm,eqv,NULL);
1817 /*{{{static void vmssetuserlnm(char *name, char *eqv); */
1819 * sets a user-mode logical in the process logical name table
1820 * used for redirection of sys$error
1823 Perl_vmssetuserlnm(pTHX_ const char *name, const char *eqv)
1825 $DESCRIPTOR(d_tab, "LNM$PROCESS");
1826 struct dsc$descriptor_d d_name = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0};
1827 unsigned long int iss, attr = LNM$M_CONFINE;
1828 unsigned char acmode = PSL$C_USER;
1829 struct itmlst_3 lnmlst[2] = {{0, LNM$_STRING, 0, 0},
1831 d_name.dsc$a_pointer = (char *)name; /* Cast OK for read only parameter */
1832 d_name.dsc$w_length = strlen(name);
1834 lnmlst[0].buflen = strlen(eqv);
1835 lnmlst[0].bufadr = (char *)eqv; /* Cast OK for read only parameter */
1837 iss = sys$crelnm(&attr,&d_tab,&d_name,&acmode,lnmlst);
1838 if (!(iss&1)) lib$signal(iss);
1843 /*{{{ char *my_crypt(const char *textpasswd, const char *usrname)*/
1844 /* my_crypt - VMS password hashing
1845 * my_crypt() provides an interface compatible with the Unix crypt()
1846 * C library function, and uses sys$hash_password() to perform VMS
1847 * password hashing. The quadword hashed password value is returned
1848 * as a NUL-terminated 8 character string. my_crypt() does not change
1849 * the case of its string arguments; in order to match the behavior
1850 * of LOGINOUT et al., alphabetic characters in both arguments must
1851 * be upcased by the caller.
1853 * - fix me to call ACM services when available
1856 Perl_my_crypt(pTHX_ const char *textpasswd, const char *usrname)
1858 # ifndef UAI$C_PREFERRED_ALGORITHM
1859 # define UAI$C_PREFERRED_ALGORITHM 127
1861 unsigned char alg = UAI$C_PREFERRED_ALGORITHM;
1862 unsigned short int salt = 0;
1863 unsigned long int sts;
1865 unsigned short int dsc$w_length;
1866 unsigned char dsc$b_type;
1867 unsigned char dsc$b_class;
1868 const char * dsc$a_pointer;
1869 } usrdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
1870 txtdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1871 struct itmlst_3 uailst[3] = {
1872 { sizeof alg, UAI$_ENCRYPT, &alg, 0},
1873 { sizeof salt, UAI$_SALT, &salt, 0},
1874 { 0, 0, NULL, NULL}};
1875 static char hash[9];
1877 usrdsc.dsc$w_length = strlen(usrname);
1878 usrdsc.dsc$a_pointer = usrname;
1879 if (!((sts = sys$getuai(0, 0, &usrdsc, uailst, 0, 0, 0)) & 1)) {
1881 case SS$_NOGRPPRV: case SS$_NOSYSPRV:
1885 set_errno(ESRCH); /* There isn't a Unix no-such-user error */
1890 set_vaxc_errno(sts);
1891 if (sts != RMS$_RNF) return NULL;
1894 txtdsc.dsc$w_length = strlen(textpasswd);
1895 txtdsc.dsc$a_pointer = textpasswd;
1896 if (!((sts = sys$hash_password(&txtdsc, alg, salt, &usrdsc, &hash)) & 1)) {
1897 set_errno(EVMSERR); set_vaxc_errno(sts); return NULL;
1900 return (char *) hash;
1902 } /* end of my_crypt() */
1906 static char *mp_do_rmsexpand(pTHX_ const char *, char *, int, const char *, unsigned, int *, int *);
1907 static char *mp_do_fileify_dirspec(pTHX_ const char *, char *, int, int *);
1908 static char *mp_do_tovmsspec(pTHX_ const char *, char *, int, int, int *);
1910 /* fixup barenames that are directories for internal use.
1911 * There have been problems with the consistent handling of UNIX
1912 * style directory names when routines are presented with a name that
1913 * has no directory delimitors at all. So this routine will eventually
1916 static char * fixup_bare_dirnames(const char * name)
1918 if (decc_disable_to_vms_logname_translation) {
1924 /* 8.3, remove() is now broken on symbolic links */
1925 static int rms_erase(const char * vmsname);
1929 * A little hack to get around a bug in some implemenation of remove()
1930 * that do not know how to delete a directory
1932 * Delete any file to which user has control access, regardless of whether
1933 * delete access is explicitly allowed.
1934 * Limitations: User must have write access to parent directory.
1935 * Does not block signals or ASTs; if interrupted in midstream
1936 * may leave file with an altered ACL.
1939 /*{{{int mp_do_kill_file(const char *name, int dirflag)*/
1941 mp_do_kill_file(pTHX_ const char *name, int dirflag)
1945 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
1946 unsigned long int cxt = 0, aclsts, fndsts, rmsts = -1;
1947 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
1949 unsigned char myace$b_length;
1950 unsigned char myace$b_type;
1951 unsigned short int myace$w_flags;
1952 unsigned long int myace$l_access;
1953 unsigned long int myace$l_ident;
1954 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
1955 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL, 0},
1956 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
1958 findlst[3] = {{sizeof oldace, ACL$C_FNDACLENT, &oldace, 0},
1959 {sizeof oldace, ACL$C_READACE, &oldace, 0},{0,0,0,0}},
1960 addlst[2] = {{sizeof newace, ACL$C_ADDACLENT, &newace, 0},{0,0,0,0}},
1961 dellst[2] = {{sizeof newace, ACL$C_DELACLENT, &newace, 0},{0,0,0,0}},
1962 lcklst[2] = {{sizeof newace, ACL$C_WLOCK_ACL, &newace, 0},{0,0,0,0}},
1963 ulklst[2] = {{sizeof newace, ACL$C_UNLOCK_ACL, &newace, 0},{0,0,0,0}};
1965 /* Expand the input spec using RMS, since the CRTL remove() and
1966 * system services won't do this by themselves, so we may miss
1967 * a file "hiding" behind a logical name or search list. */
1968 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
1969 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
1971 rslt = int_rmsexpand_tovms(name, vmsname, PERL_RMSEXPAND_M_SYMLINK);
1973 PerlMem_free(vmsname);
1977 /* Erase the file */
1978 rmsts = rms_erase(vmsname);
1980 /* Did it succeed */
1981 if ($VMS_STATUS_SUCCESS(rmsts)) {
1982 PerlMem_free(vmsname);
1986 /* If not, can changing protections help? */
1987 if (rmsts != RMS$_PRV) {
1988 set_vaxc_errno(rmsts);
1989 PerlMem_free(vmsname);
1993 /* No, so we get our own UIC to use as a rights identifier,
1994 * and the insert an ACE at the head of the ACL which allows us
1995 * to delete the file.
1997 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
1998 fildsc.dsc$w_length = strlen(vmsname);
1999 fildsc.dsc$a_pointer = vmsname;
2001 newace.myace$l_ident = oldace.myace$l_ident;
2003 if (!((aclsts = sys$change_acl(0,&type,&fildsc,lcklst,0,0,0)) & 1)) {
2005 case RMS$_FNF: case RMS$_DNF: case SS$_NOSUCHOBJECT:
2006 set_errno(ENOENT); break;
2008 set_errno(ENOTDIR); break;
2010 set_errno(ENODEV); break;
2011 case RMS$_SYN: case SS$_INVFILFOROP:
2012 set_errno(EINVAL); break;
2014 set_errno(EACCES); break;
2016 _ckvmssts_noperl(aclsts);
2018 set_vaxc_errno(aclsts);
2019 PerlMem_free(vmsname);
2022 /* Grab any existing ACEs with this identifier in case we fail */
2023 aclsts = fndsts = sys$change_acl(0,&type,&fildsc,findlst,0,0,&cxt);
2024 if ( fndsts & 1 || fndsts == SS$_ACLEMPTY || fndsts == SS$_NOENTRY
2025 || fndsts == SS$_NOMOREACE ) {
2026 /* Add the new ACE . . . */
2027 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,0)) & 1))
2030 rmsts = rms_erase(vmsname);
2031 if ($VMS_STATUS_SUCCESS(rmsts)) {
2036 /* We blew it - dir with files in it, no write priv for
2037 * parent directory, etc. Put things back the way they were. */
2038 if (!((aclsts = sys$change_acl(0,&type,&fildsc,dellst,0,0,0)) & 1))
2041 addlst[0].bufadr = &oldace;
2042 if (!((aclsts = sys$change_acl(0,&type,&fildsc,addlst,0,0,&cxt)) & 1))
2049 fndsts = sys$change_acl(0,&type,&fildsc,ulklst,0,0,0);
2050 /* We just deleted it, so of course it's not there. Some versions of
2051 * VMS seem to return success on the unlock operation anyhow (after all
2052 * the unlock is successful), but others don't.
2054 if (fndsts == RMS$_FNF || fndsts == SS$_NOSUCHOBJECT) fndsts = SS$_NORMAL;
2055 if (aclsts & 1) aclsts = fndsts;
2056 if (!(aclsts & 1)) {
2058 set_vaxc_errno(aclsts);
2061 PerlMem_free(vmsname);
2064 } /* end of kill_file() */
2068 /*{{{int do_rmdir(char *name)*/
2070 Perl_do_rmdir(pTHX_ const char *name)
2076 /* lstat returns a VMS fileified specification of the name */
2077 /* that is looked up, and also lets verifies that this is a directory */
2079 retval = Perl_flex_lstat(NULL, name, &st);
2083 /* Due to a historical feature, flex_stat/lstat can not see some */
2084 /* Unix format file names that the rest of the CRTL can see */
2085 /* Fixing that feature will cause some perl tests to fail */
2086 /* So try this one more time. */
2088 retval = lstat(name, &st.crtl_stat);
2092 /* force it to a file spec for the kill file to work. */
2093 ret_spec = do_fileify_dirspec(name, st.st_devnam, 0, NULL);
2094 if (ret_spec == NULL) {
2100 if (!S_ISDIR(st.st_mode)) {
2105 dirfile = st.st_devnam;
2107 /* It may be possible for flex_stat to find a file and vmsify() to */
2108 /* fail with ODS-2 specifications. mp_do_kill_file can not deal */
2109 /* with that case, so fail it */
2110 if (dirfile[0] == 0) {
2115 retval = mp_do_kill_file(aTHX_ dirfile, 1);
2120 } /* end of do_rmdir */
2124 * Delete any file to which user has control access, regardless of whether
2125 * delete access is explicitly allowed.
2126 * Limitations: User must have write access to parent directory.
2127 * Does not block signals or ASTs; if interrupted in midstream
2128 * may leave file with an altered ACL.
2131 /*{{{int kill_file(char *name)*/
2133 Perl_kill_file(pTHX_ const char *name)
2139 /* Convert the filename to VMS format and see if it is a directory */
2140 /* flex_lstat returns a vmsified file specification */
2141 rmsts = Perl_flex_lstat(NULL, name, &st);
2144 /* Due to a historical feature, flex_stat/lstat can not see some */
2145 /* Unix format file names that the rest of the CRTL can see when */
2146 /* ODS-2 file specifications are in use. */
2147 /* Fixing that feature will cause some perl tests to fail */
2148 /* [.lib.ExtUtils.t]Manifest.t is one of them */
2150 vmsfile = (char *) name; /* cast ok */
2153 vmsfile = st.st_devnam;
2154 if (vmsfile[0] == 0) {
2155 /* It may be possible for flex_stat to find a file and vmsify() */
2156 /* to fail with ODS-2 specifications. mp_do_kill_file can not */
2157 /* deal with that case, so fail it */
2163 /* Remove() is allowed to delete directories, according to the X/Open
2165 * This may need special handling to work with the ACL hacks.
2167 if (S_ISDIR(st.st_mode)) {
2168 rmsts = mp_do_kill_file(aTHX_ vmsfile, 1);
2172 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2174 /* Need to delete all versions ? */
2175 if ((rmsts == 0) && (vms_unlink_all_versions == 1)) {
2178 /* Just use lstat() here as do not need st_dev */
2179 /* and we know that the file is in VMS format or that */
2180 /* because of a historical bug, flex_stat can not see the file */
2181 while (lstat(vmsfile, (stat_t *)&st) == 0) {
2182 rmsts = mp_do_kill_file(aTHX_ vmsfile, 0);
2187 /* Make sure that we do not loop forever */
2198 } /* end of kill_file() */
2202 /*{{{int my_mkdir(char *,Mode_t)*/
2204 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2206 STRLEN dirlen = strlen(dir);
2208 /* zero length string sometimes gives ACCVIO */
2209 if (dirlen == 0) return -1;
2211 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2212 * null file name/type. However, it's commonplace under Unix,
2213 * so we'll allow it for a gain in portability.
2215 if (dir[dirlen-1] == '/') {
2216 char *newdir = savepvn(dir,dirlen-1);
2217 int ret = mkdir(newdir,mode);
2221 else return mkdir(dir,mode);
2222 } /* end of my_mkdir */
2225 /*{{{int my_chdir(char *)*/
2227 Perl_my_chdir(pTHX_ const char *dir)
2229 STRLEN dirlen = strlen(dir);
2231 /* zero length string sometimes gives ACCVIO */
2232 if (dirlen == 0) return -1;
2235 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2236 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2237 * so that existing scripts do not need to be changed.
2240 while ((dirlen > 0) && (*dir1 == ' ')) {
2245 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2247 * null file name/type. However, it's commonplace under Unix,
2248 * so we'll allow it for a gain in portability.
2250 * - Preview- '/' will be valid soon on VMS
2252 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2253 char *newdir = savepvn(dir1,dirlen-1);
2254 int ret = chdir(newdir);
2258 else return chdir(dir1);
2259 } /* end of my_chdir */
2263 /*{{{int my_chmod(char *, mode_t)*/
2265 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2267 STRLEN speclen = strlen(file_spec);
2269 /* zero length string sometimes gives ACCVIO */
2270 if (speclen == 0) return -1;
2272 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2273 * that implies null file name/type. However, it's commonplace under Unix,
2274 * so we'll allow it for a gain in portability.
2276 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2277 * in VMS file.dir notation.
2279 if ((speclen > 1) && (file_spec[speclen-1] == '/')) {
2280 char *vms_src, *vms_dir, *rslt;
2284 /* First convert this to a VMS format specification */
2285 vms_src = PerlMem_malloc(VMS_MAXRSS);
2286 if (vms_src == NULL)
2287 _ckvmssts_noperl(SS$_INSFMEM);
2289 rslt = do_tovmsspec(file_spec, vms_src, 0, NULL);
2291 /* If we fail, then not a file specification */
2292 PerlMem_free(vms_src);
2297 /* Now make it a directory spec so chmod is happy */
2298 vms_dir = PerlMem_malloc(VMS_MAXRSS + 1);
2299 if (vms_dir == NULL)
2300 _ckvmssts_noperl(SS$_INSFMEM);
2301 rslt = do_fileify_dirspec(vms_src, vms_dir, 0, NULL);
2302 PerlMem_free(vms_src);
2306 ret = chmod(vms_dir, mode);
2310 PerlMem_free(vms_dir);
2313 else return chmod(file_spec, mode);
2314 } /* end of my_chmod */
2318 /*{{{FILE *my_tmpfile()*/
2325 if ((fp = tmpfile())) return fp;
2327 cp = PerlMem_malloc(L_tmpnam+24);
2328 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2330 if (decc_filename_unix_only == 0)
2331 strcpy(cp,"Sys$Scratch:");
2334 tmpnam(cp+strlen(cp));
2335 strcat(cp,".Perltmp");
2336 fp = fopen(cp,"w+","fop=dlt");
2343 #ifndef HOMEGROWN_POSIX_SIGNALS
2345 * The C RTL's sigaction fails to check for invalid signal numbers so we
2346 * help it out a bit. The docs are correct, but the actual routine doesn't
2347 * do what the docs say it will.
2349 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2351 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2352 struct sigaction* oact)
2354 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2355 SETERRNO(EINVAL, SS$_INVARG);
2358 return sigaction(sig, act, oact);
2363 #ifdef KILL_BY_SIGPRC
2364 #include <errnodef.h>
2366 /* We implement our own kill() using the undocumented system service
2367 sys$sigprc for one of two reasons:
2369 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2370 target process to do a sys$exit, which usually can't be handled
2371 gracefully...certainly not by Perl and the %SIG{} mechanism.
2373 2.) If the kill() in the CRTL can't be called from a signal
2374 handler without disappearing into the ether, i.e., the signal
2375 it purportedly sends is never trapped. Still true as of VMS 7.3.
2377 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2378 in the target process rather than calling sys$exit.
2380 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2381 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2382 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2383 with condition codes C$_SIG0+nsig*8, catching the exception on the
2384 target process and resignaling with appropriate arguments.
2386 But we don't have that VMS 7.0+ exception handler, so if you
2387 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2389 Also note that SIGTERM is listed in the docs as being "unimplemented",
2390 yet always seems to be signaled with a VMS condition code of 4 (and
2391 correctly handled for that code). So we hardwire it in.
2393 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2394 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2395 than signalling with an unrecognized (and unhandled by CRTL) code.
2398 #define _MY_SIG_MAX 28
2401 Perl_sig_to_vmscondition_int(int sig)
2403 static unsigned int sig_code[_MY_SIG_MAX+1] =
2406 SS$_HANGUP, /* 1 SIGHUP */
2407 SS$_CONTROLC, /* 2 SIGINT */
2408 SS$_CONTROLY, /* 3 SIGQUIT */
2409 SS$_RADRMOD, /* 4 SIGILL */
2410 SS$_BREAK, /* 5 SIGTRAP */
2411 SS$_OPCCUS, /* 6 SIGABRT */
2412 SS$_COMPAT, /* 7 SIGEMT */
2414 SS$_FLTOVF, /* 8 SIGFPE VAX */
2416 SS$_HPARITH, /* 8 SIGFPE AXP */
2418 SS$_ABORT, /* 9 SIGKILL */
2419 SS$_ACCVIO, /* 10 SIGBUS */
2420 SS$_ACCVIO, /* 11 SIGSEGV */
2421 SS$_BADPARAM, /* 12 SIGSYS */
2422 SS$_NOMBX, /* 13 SIGPIPE */
2423 SS$_ASTFLT, /* 14 SIGALRM */
2440 #if __VMS_VER >= 60200000
2441 static int initted = 0;
2444 sig_code[16] = C$_SIGUSR1;
2445 sig_code[17] = C$_SIGUSR2;
2446 #if __CRTL_VER >= 70000000
2447 sig_code[20] = C$_SIGCHLD;
2449 #if __CRTL_VER >= 70300000
2450 sig_code[28] = C$_SIGWINCH;
2455 if (sig < _SIG_MIN) return 0;
2456 if (sig > _MY_SIG_MAX) return 0;
2457 return sig_code[sig];
2461 Perl_sig_to_vmscondition(int sig)
2464 if (vms_debug_on_exception != 0)
2465 lib$signal(SS$_DEBUG);
2467 return Perl_sig_to_vmscondition_int(sig);
2472 Perl_my_kill(int pid, int sig)
2477 int sys$sigprc(unsigned int *pidadr,
2478 struct dsc$descriptor_s *prcname,
2481 /* sig 0 means validate the PID */
2482 /*------------------------------*/
2484 const unsigned long int jpicode = JPI$_PID;
2487 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2488 if ($VMS_STATUS_SUCCESS(status))
2491 case SS$_NOSUCHNODE:
2492 case SS$_UNREACHABLE:
2506 code = Perl_sig_to_vmscondition_int(sig);
2509 SETERRNO(EINVAL, SS$_BADPARAM);
2513 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2514 * signals are to be sent to multiple processes.
2515 * pid = 0 - all processes in group except ones that the system exempts
2516 * pid = -1 - all processes except ones that the system exempts
2517 * pid = -n - all processes in group (abs(n)) except ...
2518 * For now, just report as not supported.
2522 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2526 iss = sys$sigprc((unsigned int *)&pid,0,code);
2527 if (iss&1) return 0;
2531 set_errno(EPERM); break;
2533 case SS$_NOSUCHNODE:
2534 case SS$_UNREACHABLE:
2535 set_errno(ESRCH); break;
2537 set_errno(ENOMEM); break;
2539 _ckvmssts_noperl(iss);
2542 set_vaxc_errno(iss);
2548 /* Routine to convert a VMS status code to a UNIX status code.
2549 ** More tricky than it appears because of conflicting conventions with
2552 ** VMS status codes are a bit mask, with the least significant bit set for
2555 ** Special UNIX status of EVMSERR indicates that no translation is currently
2556 ** available, and programs should check the VMS status code.
2558 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2562 #ifndef C_FACILITY_NO
2563 #define C_FACILITY_NO 0x350000
2566 #define DCL_IVVERB 0x38090
2569 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2577 /* Assume the best or the worst */
2578 if (vms_status & STS$M_SUCCESS)
2581 unix_status = EVMSERR;
2583 msg_status = vms_status & ~STS$M_CONTROL;
2585 facility = vms_status & STS$M_FAC_NO;
2586 fac_sp = vms_status & STS$M_FAC_SP;
2587 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2589 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2595 unix_status = EFAULT;
2597 case SS$_DEVOFFLINE:
2598 unix_status = EBUSY;
2601 unix_status = ENOTCONN;
2609 case SS$_INVFILFOROP:
2613 unix_status = EINVAL;
2615 case SS$_UNSUPPORTED:
2616 unix_status = ENOTSUP;
2621 unix_status = EACCES;
2623 case SS$_DEVICEFULL:
2624 unix_status = ENOSPC;
2627 unix_status = ENODEV;
2629 case SS$_NOSUCHFILE:
2630 case SS$_NOSUCHOBJECT:
2631 unix_status = ENOENT;
2633 case SS$_ABORT: /* Fatal case */
2634 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2635 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2636 unix_status = EINTR;
2639 unix_status = E2BIG;
2642 unix_status = ENOMEM;
2645 unix_status = EPERM;
2647 case SS$_NOSUCHNODE:
2648 case SS$_UNREACHABLE:
2649 unix_status = ESRCH;
2652 unix_status = ECHILD;
2655 if ((facility == 0) && (msg_no < 8)) {
2656 /* These are not real VMS status codes so assume that they are
2657 ** already UNIX status codes
2659 unix_status = msg_no;
2665 /* Translate a POSIX exit code to a UNIX exit code */
2666 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2667 unix_status = (msg_no & 0x07F8) >> 3;
2671 /* Documented traditional behavior for handling VMS child exits */
2672 /*--------------------------------------------------------------*/
2673 if (child_flag != 0) {
2675 /* Success / Informational return 0 */
2676 /*----------------------------------*/
2677 if (msg_no & STS$K_SUCCESS)
2680 /* Warning returns 1 */
2681 /*-------------------*/
2682 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2685 /* Everything else pass through the severity bits */
2686 /*------------------------------------------------*/
2687 return (msg_no & STS$M_SEVERITY);
2690 /* Normal VMS status to ERRNO mapping attempt */
2691 /*--------------------------------------------*/
2692 switch(msg_status) {
2693 /* case RMS$_EOF: */ /* End of File */
2694 case RMS$_FNF: /* File Not Found */
2695 case RMS$_DNF: /* Dir Not Found */
2696 unix_status = ENOENT;
2698 case RMS$_RNF: /* Record Not Found */
2699 unix_status = ESRCH;
2702 unix_status = ENOTDIR;
2705 unix_status = ENODEV;
2710 unix_status = EBADF;
2713 unix_status = EEXIST;
2717 case LIB$_INVSTRDES:
2719 case LIB$_NOSUCHSYM:
2720 case LIB$_INVSYMNAM:
2722 unix_status = EINVAL;
2728 unix_status = E2BIG;
2730 case RMS$_PRV: /* No privilege */
2731 case RMS$_ACC: /* ACP file access failed */
2732 case RMS$_WLK: /* Device write locked */
2733 unix_status = EACCES;
2735 case RMS$_MKD: /* Failed to mark for delete */
2736 unix_status = EPERM;
2738 /* case RMS$_NMF: */ /* No more files */
2746 /* Try to guess at what VMS error status should go with a UNIX errno
2747 * value. This is hard to do as there could be many possible VMS
2748 * error statuses that caused the errno value to be set.
2751 int Perl_unix_status_to_vms(int unix_status)
2753 int test_unix_status;
2755 /* Trivial cases first */
2756 /*---------------------*/
2757 if (unix_status == EVMSERR)
2760 /* Is vaxc$errno sane? */
2761 /*---------------------*/
2762 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2763 if (test_unix_status == unix_status)
2766 /* If way out of range, must be VMS code already */
2767 /*-----------------------------------------------*/
2768 if (unix_status > EVMSERR)
2771 /* If out of range, punt */
2772 /*-----------------------*/
2773 if (unix_status > __ERRNO_MAX)
2777 /* Ok, now we have to do it the hard way. */
2778 /*----------------------------------------*/
2779 switch(unix_status) {
2780 case 0: return SS$_NORMAL;
2781 case EPERM: return SS$_NOPRIV;
2782 case ENOENT: return SS$_NOSUCHOBJECT;
2783 case ESRCH: return SS$_UNREACHABLE;
2784 case EINTR: return SS$_ABORT;
2787 case E2BIG: return SS$_BUFFEROVF;
2789 case EBADF: return RMS$_IFI;
2790 case ECHILD: return SS$_NONEXPR;
2792 case ENOMEM: return SS$_INSFMEM;
2793 case EACCES: return SS$_FILACCERR;
2794 case EFAULT: return SS$_ACCVIO;
2796 case EBUSY: return SS$_DEVOFFLINE;
2797 case EEXIST: return RMS$_FEX;
2799 case ENODEV: return SS$_NOSUCHDEV;
2800 case ENOTDIR: return RMS$_DIR;
2802 case EINVAL: return SS$_INVARG;
2808 case ENOSPC: return SS$_DEVICEFULL;
2809 case ESPIPE: return LIB$_INVARG;
2814 case ERANGE: return LIB$_INVARG;
2815 /* case EWOULDBLOCK */
2816 /* case EINPROGRESS */
2819 /* case EDESTADDRREQ */
2821 /* case EPROTOTYPE */
2822 /* case ENOPROTOOPT */
2823 /* case EPROTONOSUPPORT */
2824 /* case ESOCKTNOSUPPORT */
2825 /* case EOPNOTSUPP */
2826 /* case EPFNOSUPPORT */
2827 /* case EAFNOSUPPORT */
2828 /* case EADDRINUSE */
2829 /* case EADDRNOTAVAIL */
2831 /* case ENETUNREACH */
2832 /* case ENETRESET */
2833 /* case ECONNABORTED */
2834 /* case ECONNRESET */
2837 case ENOTCONN: return SS$_CLEARED;
2838 /* case ESHUTDOWN */
2839 /* case ETOOMANYREFS */
2840 /* case ETIMEDOUT */
2841 /* case ECONNREFUSED */
2843 /* case ENAMETOOLONG */
2844 /* case EHOSTDOWN */
2845 /* case EHOSTUNREACH */
2846 /* case ENOTEMPTY */
2858 /* case ECANCELED */
2862 return SS$_UNSUPPORTED;
2868 /* case EABANDONED */
2870 return SS$_ABORT; /* punt */
2873 return SS$_ABORT; /* Should not get here */
2877 /* default piping mailbox size */
2878 #define PERL_BUFSIZ 512
2882 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2884 unsigned long int mbxbufsiz;
2885 static unsigned long int syssize = 0;
2886 unsigned long int dviitm = DVI$_DEVNAM;
2887 char csize[LNM$C_NAMLENGTH+1];
2891 unsigned long syiitm = SYI$_MAXBUF;
2893 * Get the SYSGEN parameter MAXBUF
2895 * If the logical 'PERL_MBX_SIZE' is defined
2896 * use the value of the logical instead of PERL_BUFSIZ, but
2897 * keep the size between 128 and MAXBUF.
2900 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2903 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2904 mbxbufsiz = atoi(csize);
2906 mbxbufsiz = PERL_BUFSIZ;
2908 if (mbxbufsiz < 128) mbxbufsiz = 128;
2909 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2911 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2913 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2914 _ckvmssts_noperl(sts);
2915 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2917 } /* end of create_mbx() */
2920 /*{{{ my_popen and my_pclose*/
2922 typedef struct _iosb IOSB;
2923 typedef struct _iosb* pIOSB;
2924 typedef struct _pipe Pipe;
2925 typedef struct _pipe* pPipe;
2926 typedef struct pipe_details Info;
2927 typedef struct pipe_details* pInfo;
2928 typedef struct _srqp RQE;
2929 typedef struct _srqp* pRQE;
2930 typedef struct _tochildbuf CBuf;
2931 typedef struct _tochildbuf* pCBuf;
2934 unsigned short status;
2935 unsigned short count;
2936 unsigned long dvispec;
2939 #pragma member_alignment save
2940 #pragma nomember_alignment quadword
2941 struct _srqp { /* VMS self-relative queue entry */
2942 unsigned long qptr[2];
2944 #pragma member_alignment restore
2945 static RQE RQE_ZERO = {0,0};
2947 struct _tochildbuf {
2950 unsigned short size;
2958 unsigned short chan_in;
2959 unsigned short chan_out;
2961 unsigned int bufsize;
2973 #if defined(PERL_IMPLICIT_CONTEXT)
2974 void *thx; /* Either a thread or an interpreter */
2975 /* pointer, depending on how we're built */
2983 PerlIO *fp; /* file pointer to pipe mailbox */
2984 int useFILE; /* using stdio, not perlio */
2985 int pid; /* PID of subprocess */
2986 int mode; /* == 'r' if pipe open for reading */
2987 int done; /* subprocess has completed */
2988 int waiting; /* waiting for completion/closure */
2989 int closing; /* my_pclose is closing this pipe */
2990 unsigned long completion; /* termination status of subprocess */
2991 pPipe in; /* pipe in to sub */
2992 pPipe out; /* pipe out of sub */
2993 pPipe err; /* pipe of sub's sys$error */
2994 int in_done; /* true when in pipe finished */
2997 unsigned short xchan; /* channel to debug xterm */
2998 unsigned short xchan_valid; /* channel is assigned */
3001 struct exit_control_block
3003 struct exit_control_block *flink;
3004 unsigned long int (*exit_routine)();
3005 unsigned long int arg_count;
3006 unsigned long int *status_address;
3007 unsigned long int exit_status;
3010 typedef struct _closed_pipes Xpipe;
3011 typedef struct _closed_pipes* pXpipe;
3013 struct _closed_pipes {
3014 int pid; /* PID of subprocess */
3015 unsigned long completion; /* termination status of subprocess */
3017 #define NKEEPCLOSED 50
3018 static Xpipe closed_list[NKEEPCLOSED];
3019 static int closed_index = 0;
3020 static int closed_num = 0;
3022 #define RETRY_DELAY "0 ::0.20"
3023 #define MAX_RETRY 50
3025 static int pipe_ef = 0; /* first call to safe_popen inits these*/
3026 static unsigned long mypid;
3027 static unsigned long delaytime[2];
3029 static pInfo open_pipes = NULL;
3030 static $DESCRIPTOR(nl_desc, "NL:");
3032 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
3036 static unsigned long int
3040 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
3041 int sts, did_stuff, need_eof, j;
3044 * Flush any pending i/o, but since we are in process run-down, be
3045 * careful about referencing PerlIO structures that may already have
3046 * been deallocated. We may not even have an interpreter anymore.
3051 #if defined(PERL_IMPLICIT_CONTEXT)
3052 /* We need to use the Perl context of the thread that created */
3056 aTHX = info->err->thx;
3058 aTHX = info->out->thx;
3060 aTHX = info->in->thx;
3063 #if defined(USE_ITHREADS)
3066 && PL_perlio_fd_refcnt)
3067 PerlIO_flush(info->fp);
3069 fflush((FILE *)info->fp);
3075 next we try sending an EOF...ignore if doesn't work, make sure we
3083 _ckvmssts_noperl(sys$setast(0));
3084 if (info->in && !info->in->shut_on_empty) {
3085 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3090 _ckvmssts_noperl(sys$setast(1));
3094 /* wait for EOF to have effect, up to ~ 30 sec [default] */
3096 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3101 _ckvmssts_noperl(sys$setast(0));
3102 if (info->waiting && info->done)
3104 nwait += info->waiting;
3105 _ckvmssts_noperl(sys$setast(1));
3115 _ckvmssts_noperl(sys$setast(0));
3116 if (!info->done) { /* Tap them gently on the shoulder . . .*/
3117 sts = sys$forcex(&info->pid,0,&abort);
3118 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3121 _ckvmssts_noperl(sys$setast(1));
3125 /* again, wait for effect */
3127 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3132 _ckvmssts_noperl(sys$setast(0));
3133 if (info->waiting && info->done)
3135 nwait += info->waiting;
3136 _ckvmssts_noperl(sys$setast(1));
3145 _ckvmssts_noperl(sys$setast(0));
3146 if (!info->done) { /* We tried to be nice . . . */
3147 sts = sys$delprc(&info->pid,0);
3148 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3149 info->done = 1; /* sys$delprc is as done as we're going to get. */
3151 _ckvmssts_noperl(sys$setast(1));
3157 #if defined(PERL_IMPLICIT_CONTEXT)
3158 /* We need to use the Perl context of the thread that created */
3161 if (open_pipes->err)
3162 aTHX = open_pipes->err->thx;
3163 else if (open_pipes->out)
3164 aTHX = open_pipes->out->thx;
3165 else if (open_pipes->in)
3166 aTHX = open_pipes->in->thx;
3168 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3169 else if (!(sts & 1)) retsts = sts;
3174 static struct exit_control_block pipe_exitblock =
3175 {(struct exit_control_block *) 0,
3176 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3178 static void pipe_mbxtofd_ast(pPipe p);
3179 static void pipe_tochild1_ast(pPipe p);
3180 static void pipe_tochild2_ast(pPipe p);
3183 popen_completion_ast(pInfo info)
3185 pInfo i = open_pipes;
3190 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3191 closed_list[closed_index].pid = info->pid;
3192 closed_list[closed_index].completion = info->completion;
3194 if (closed_index == NKEEPCLOSED)
3199 if (i == info) break;
3202 if (!i) return; /* unlinked, probably freed too */
3207 Writing to subprocess ...
3208 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3210 chan_out may be waiting for "done" flag, or hung waiting
3211 for i/o completion to child...cancel the i/o. This will
3212 put it into "snarf mode" (done but no EOF yet) that discards
3215 Output from subprocess (stdout, stderr) needs to be flushed and
3216 shut down. We try sending an EOF, but if the mbx is full the pipe
3217 routine should still catch the "shut_on_empty" flag, telling it to
3218 use immediate-style reads so that "mbx empty" -> EOF.
3222 if (info->in && !info->in_done) { /* only for mode=w */
3223 if (info->in->shut_on_empty && info->in->need_wake) {
3224 info->in->need_wake = FALSE;
3225 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3227 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3231 if (info->out && !info->out_done) { /* were we also piping output? */
3232 info->out->shut_on_empty = TRUE;
3233 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3234 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3235 _ckvmssts_noperl(iss);
3238 if (info->err && !info->err_done) { /* we were piping stderr */
3239 info->err->shut_on_empty = TRUE;
3240 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3241 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3242 _ckvmssts_noperl(iss);
3244 _ckvmssts_noperl(sys$setef(pipe_ef));
3248 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3249 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3252 we actually differ from vmstrnenv since we use this to
3253 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
3254 are pointing to the same thing
3257 static unsigned short
3258 popen_translate(pTHX_ char *logical, char *result)
3261 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
3262 $DESCRIPTOR(d_log,"");
3264 unsigned short length;
3265 unsigned short code;
3267 unsigned short *retlenaddr;
3269 unsigned short l, ifi;
3271 d_log.dsc$a_pointer = logical;
3272 d_log.dsc$w_length = strlen(logical);
3274 itmlst[0].code = LNM$_STRING;
3275 itmlst[0].length = 255;
3276 itmlst[0].buffer_addr = result;
3277 itmlst[0].retlenaddr = &l;
3280 itmlst[1].length = 0;
3281 itmlst[1].buffer_addr = 0;
3282 itmlst[1].retlenaddr = 0;
3284 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3285 if (iss == SS$_NOLOGNAM) {
3289 if (!(iss&1)) lib$signal(iss);
3292 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
3293 strip it off and return the ifi, if any
3296 if (result[0] == 0x1b && result[1] == 0x00) {
3297 memmove(&ifi,result+2,2);
3298 strcpy(result,result+4);
3300 return ifi; /* this is the RMS internal file id */
3303 static void pipe_infromchild_ast(pPipe p);
3306 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3307 inside an AST routine without worrying about reentrancy and which Perl
3308 memory allocator is being used.
3310 We read data and queue up the buffers, then spit them out one at a
3311 time to the output mailbox when the output mailbox is ready for one.
3314 #define INITIAL_TOCHILDQUEUE 2
3317 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3321 char mbx1[64], mbx2[64];
3322 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3323 DSC$K_CLASS_S, mbx1},
3324 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3325 DSC$K_CLASS_S, mbx2};
3326 unsigned int dviitm = DVI$_DEVBUFSIZ;
3330 _ckvmssts_noperl(lib$get_vm(&n, &p));
3332 create_mbx(&p->chan_in , &d_mbx1);
3333 create_mbx(&p->chan_out, &d_mbx2);
3334 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3337 p->shut_on_empty = FALSE;
3338 p->need_wake = FALSE;
3341 p->iosb.status = SS$_NORMAL;
3342 p->iosb2.status = SS$_NORMAL;
3348 #ifdef PERL_IMPLICIT_CONTEXT
3352 n = sizeof(CBuf) + p->bufsize;
3354 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3355 _ckvmssts_noperl(lib$get_vm(&n, &b));
3356 b->buf = (char *) b + sizeof(CBuf);
3357 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3360 pipe_tochild2_ast(p);
3361 pipe_tochild1_ast(p);
3367 /* reads the MBX Perl is writing, and queues */
3370 pipe_tochild1_ast(pPipe p)
3373 int iss = p->iosb.status;
3374 int eof = (iss == SS$_ENDOFFILE);
3376 #ifdef PERL_IMPLICIT_CONTEXT
3382 p->shut_on_empty = TRUE;
3384 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3386 _ckvmssts_noperl(iss);
3390 b->size = p->iosb.count;
3391 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3393 p->need_wake = FALSE;
3394 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3397 p->retry = 1; /* initial call */
3400 if (eof) { /* flush the free queue, return when done */
3401 int n = sizeof(CBuf) + p->bufsize;
3403 iss = lib$remqti(&p->free, &b);
3404 if (iss == LIB$_QUEWASEMP) return;
3405 _ckvmssts_noperl(iss);
3406 _ckvmssts_noperl(lib$free_vm(&n, &b));
3410 iss = lib$remqti(&p->free, &b);
3411 if (iss == LIB$_QUEWASEMP) {
3412 int n = sizeof(CBuf) + p->bufsize;
3413 _ckvmssts_noperl(lib$get_vm(&n, &b));
3414 b->buf = (char *) b + sizeof(CBuf);
3416 _ckvmssts_noperl(iss);
3420 iss = sys$qio(0,p->chan_in,
3421 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3423 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3424 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3425 _ckvmssts_noperl(iss);
3429 /* writes queued buffers to output, waits for each to complete before
3433 pipe_tochild2_ast(pPipe p)
3436 int iss = p->iosb2.status;
3437 int n = sizeof(CBuf) + p->bufsize;
3438 int done = (p->info && p->info->done) ||
3439 iss == SS$_CANCEL || iss == SS$_ABORT;
3440 #if defined(PERL_IMPLICIT_CONTEXT)
3445 if (p->type) { /* type=1 has old buffer, dispose */
3446 if (p->shut_on_empty) {
3447 _ckvmssts_noperl(lib$free_vm(&n, &b));
3449 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3454 iss = lib$remqti(&p->wait, &b);
3455 if (iss == LIB$_QUEWASEMP) {
3456 if (p->shut_on_empty) {
3458 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3459 *p->pipe_done = TRUE;
3460 _ckvmssts_noperl(sys$setef(pipe_ef));
3462 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3463 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3467 p->need_wake = TRUE;
3470 _ckvmssts_noperl(iss);
3477 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3478 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3480 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3481 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3490 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3493 char mbx1[64], mbx2[64];
3494 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3495 DSC$K_CLASS_S, mbx1},
3496 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3497 DSC$K_CLASS_S, mbx2};
3498 unsigned int dviitm = DVI$_DEVBUFSIZ;
3500 int n = sizeof(Pipe);
3501 _ckvmssts_noperl(lib$get_vm(&n, &p));
3502 create_mbx(&p->chan_in , &d_mbx1);
3503 create_mbx(&p->chan_out, &d_mbx2);
3505 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3506 n = p->bufsize * sizeof(char);
3507 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3508 p->shut_on_empty = FALSE;
3511 p->iosb.status = SS$_NORMAL;
3512 #if defined(PERL_IMPLICIT_CONTEXT)
3515 pipe_infromchild_ast(p);
3523 pipe_infromchild_ast(pPipe p)
3525 int iss = p->iosb.status;
3526 int eof = (iss == SS$_ENDOFFILE);
3527 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3528 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3529 #if defined(PERL_IMPLICIT_CONTEXT)
3533 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3534 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3539 input shutdown if EOF from self (done or shut_on_empty)
3540 output shutdown if closing flag set (my_pclose)
3541 send data/eof from child or eof from self
3542 otherwise, re-read (snarf of data from child)
3547 if (myeof && p->chan_in) { /* input shutdown */
3548 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3553 if (myeof || kideof) { /* pass EOF to parent */
3554 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3555 pipe_infromchild_ast, p,
3558 } else if (eof) { /* eat EOF --- fall through to read*/
3560 } else { /* transmit data */
3561 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3562 pipe_infromchild_ast,p,
3563 p->buf, p->iosb.count, 0, 0, 0, 0));
3569 /* everything shut? flag as done */
3571 if (!p->chan_in && !p->chan_out) {
3572 *p->pipe_done = TRUE;
3573 _ckvmssts_noperl(sys$setef(pipe_ef));
3577 /* write completed (or read, if snarfing from child)
3578 if still have input active,
3579 queue read...immediate mode if shut_on_empty so we get EOF if empty
3581 check if Perl reading, generate EOFs as needed
3587 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3588 pipe_infromchild_ast,p,
3589 p->buf, p->bufsize, 0, 0, 0, 0);
3590 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3591 _ckvmssts_noperl(iss);
3592 } else { /* send EOFs for extra reads */
3593 p->iosb.status = SS$_ENDOFFILE;
3594 p->iosb.dvispec = 0;
3595 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3597 pipe_infromchild_ast, p, 0, 0, 0, 0));
3603 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3607 unsigned long dviitm = DVI$_DEVBUFSIZ;
3609 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3610 DSC$K_CLASS_S, mbx};
3611 int n = sizeof(Pipe);
3613 /* things like terminals and mbx's don't need this filter */
3614 if (fd && fstat(fd,&s) == 0) {
3615 unsigned long dviitm = DVI$_DEVCHAR, devchar;
3617 unsigned short dev_len;
3618 struct dsc$descriptor_s d_dev;
3620 struct item_list_3 items[3];
3622 unsigned short dvi_iosb[4];
3624 cptr = getname(fd, out, 1);
3625 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3626 d_dev.dsc$a_pointer = out;
3627 d_dev.dsc$w_length = strlen(out);
3628 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3629 d_dev.dsc$b_class = DSC$K_CLASS_S;
3632 items[0].code = DVI$_DEVCHAR;
3633 items[0].bufadr = &devchar;
3634 items[0].retadr = NULL;
3636 items[1].code = DVI$_FULLDEVNAM;
3637 items[1].bufadr = device;
3638 items[1].retadr = &dev_len;
3642 status = sys$getdviw
3643 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3644 _ckvmssts_noperl(status);
3645 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3646 device[dev_len] = 0;
3648 if (!(devchar & DEV$M_DIR)) {
3649 strcpy(out, device);
3655 _ckvmssts_noperl(lib$get_vm(&n, &p));
3656 p->fd_out = dup(fd);
3657 create_mbx(&p->chan_in, &d_mbx);
3658 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3659 n = (p->bufsize+1) * sizeof(char);
3660 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3661 p->shut_on_empty = FALSE;
3666 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3667 pipe_mbxtofd_ast, p,
3668 p->buf, p->bufsize, 0, 0, 0, 0));
3674 pipe_mbxtofd_ast(pPipe p)
3676 int iss = p->iosb.status;
3677 int done = p->info->done;
3679 int eof = (iss == SS$_ENDOFFILE);
3680 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3681 int err = !(iss&1) && !eof;
3682 #if defined(PERL_IMPLICIT_CONTEXT)
3686 if (done && myeof) { /* end piping */
3688 sys$dassgn(p->chan_in);
3689 *p->pipe_done = TRUE;
3690 _ckvmssts_noperl(sys$setef(pipe_ef));
3694 if (!err && !eof) { /* good data to send to file */
3695 p->buf[p->iosb.count] = '\n';
3696 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3699 if (p->retry < MAX_RETRY) {
3700 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3706 _ckvmssts_noperl(iss);
3710 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3711 pipe_mbxtofd_ast, p,
3712 p->buf, p->bufsize, 0, 0, 0, 0);
3713 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3714 _ckvmssts_noperl(iss);
3718 typedef struct _pipeloc PLOC;
3719 typedef struct _pipeloc* pPLOC;
3723 char dir[NAM$C_MAXRSS+1];
3725 static pPLOC head_PLOC = 0;
3728 free_pipelocs(pTHX_ void *head)
3731 pPLOC *pHead = (pPLOC *)head;
3743 store_pipelocs(pTHX)
3752 char temp[NAM$C_MAXRSS+1];
3756 free_pipelocs(aTHX_ &head_PLOC);
3758 /* the . directory from @INC comes last */
3760 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3761 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3762 p->next = head_PLOC;
3764 strcpy(p->dir,"./");
3766 /* get the directory from $^X */
3768 unixdir = PerlMem_malloc(VMS_MAXRSS);
3769 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3771 #ifdef PERL_IMPLICIT_CONTEXT
3772 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3774 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3776 strcpy(temp, PL_origargv[0]);
3777 x = strrchr(temp,']');
3779 x = strrchr(temp,'>');
3781 /* It could be a UNIX path */
3782 x = strrchr(temp,'/');
3788 /* Got a bare name, so use default directory */
3793 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3794 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3795 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3796 p->next = head_PLOC;
3798 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3799 p->dir[NAM$C_MAXRSS] = '\0';
3803 /* reverse order of @INC entries, skip "." since entered above */
3805 #ifdef PERL_IMPLICIT_CONTEXT
3808 if (PL_incgv) av = GvAVn(PL_incgv);
3810 for (i = 0; av && i <= AvFILL(av); i++) {
3811 dirsv = *av_fetch(av,i,TRUE);
3813 if (SvROK(dirsv)) continue;
3814 dir = SvPVx(dirsv,n_a);
3815 if (strcmp(dir,".") == 0) continue;
3816 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3819 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3820 p->next = head_PLOC;
3822 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3823 p->dir[NAM$C_MAXRSS] = '\0';
3826 /* most likely spot (ARCHLIB) put first in the list */
3829 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3830 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3831 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3832 p->next = head_PLOC;
3834 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3835 p->dir[NAM$C_MAXRSS] = '\0';
3838 PerlMem_free(unixdir);
3842 Perl_cando_by_name_int
3843 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3844 #if !defined(PERL_IMPLICIT_CONTEXT)
3845 #define cando_by_name_int Perl_cando_by_name_int
3847 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3853 static int vmspipe_file_status = 0;
3854 static char vmspipe_file[NAM$C_MAXRSS+1];
3856 /* already found? Check and use ... need read+execute permission */
3858 if (vmspipe_file_status == 1) {
3859 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3860 && cando_by_name_int
3861 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3862 return vmspipe_file;
3864 vmspipe_file_status = 0;
3867 /* scan through stored @INC, $^X */
3869 if (vmspipe_file_status == 0) {
3870 char file[NAM$C_MAXRSS+1];
3871 pPLOC p = head_PLOC;
3876 strcpy(file, p->dir);
3877 dirlen = strlen(file);
3878 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
3879 file[NAM$C_MAXRSS] = '\0';
3882 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3883 if (!exp_res) continue;
3885 if (cando_by_name_int
3886 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3887 && cando_by_name_int
3888 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3889 vmspipe_file_status = 1;
3890 return vmspipe_file;
3893 vmspipe_file_status = -1; /* failed, use tempfiles */
3900 vmspipe_tempfile(pTHX)
3902 char file[NAM$C_MAXRSS+1];
3904 static int index = 0;
3908 /* create a tempfile */
3910 /* we can't go from W, shr=get to R, shr=get without
3911 an intermediate vulnerable state, so don't bother trying...
3913 and lib$spawn doesn't shr=put, so have to close the write
3915 So... match up the creation date/time and the FID to
3916 make sure we're dealing with the same file
3921 if (!decc_filename_unix_only) {
3922 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3923 fp = fopen(file,"w");
3925 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3926 fp = fopen(file,"w");
3928 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3929 fp = fopen(file,"w");
3934 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3935 fp = fopen(file,"w");
3937 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3938 fp = fopen(file,"w");
3940 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3941 fp = fopen(file,"w");
3945 if (!fp) return 0; /* we're hosed */
3947 fprintf(fp,"$! 'f$verify(0)'\n");
3948 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3949 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3950 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3951 fprintf(fp,"$ perl_on = \"set noon\"\n");
3952 fprintf(fp,"$ perl_exit = \"exit\"\n");
3953 fprintf(fp,"$ perl_del = \"delete\"\n");
3954 fprintf(fp,"$ pif = \"if\"\n");
3955 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3956 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3957 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3958 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3959 fprintf(fp,"$! --- build command line to get max possible length\n");
3960 fprintf(fp,"$c=perl_popen_cmd0\n");
3961 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3962 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3963 fprintf(fp,"$x=perl_popen_cmd3\n");
3964 fprintf(fp,"$c=c+x\n");
3965 fprintf(fp,"$ perl_on\n");
3966 fprintf(fp,"$ 'c'\n");
3967 fprintf(fp,"$ perl_status = $STATUS\n");
3968 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3969 fprintf(fp,"$ perl_exit 'perl_status'\n");
3972 fgetname(fp, file, 1);
3973 fstat(fileno(fp), &s0.crtl_stat);
3976 if (decc_filename_unix_only)
3977 int_tounixspec(file, file, NULL);
3978 fp = fopen(file,"r","shr=get");
3980 fstat(fileno(fp), &s1.crtl_stat);
3982 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3983 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3992 static int vms_is_syscommand_xterm(void)
3994 const static struct dsc$descriptor_s syscommand_dsc =
3995 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3997 const static struct dsc$descriptor_s decwdisplay_dsc =
3998 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
4000 struct item_list_3 items[2];
4001 unsigned short dvi_iosb[4];
4002 unsigned long devchar;
4003 unsigned long devclass;
4006 /* Very simple check to guess if sys$command is a decterm? */
4007 /* First see if the DECW$DISPLAY: device exists */
4009 items[0].code = DVI$_DEVCHAR;
4010 items[0].bufadr = &devchar;
4011 items[0].retadr = NULL;
4015 status = sys$getdviw
4016 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
4018 if ($VMS_STATUS_SUCCESS(status)) {
4019 status = dvi_iosb[0];
4022 if (!$VMS_STATUS_SUCCESS(status)) {
4023 SETERRNO(EVMSERR, status);
4027 /* If it does, then for now assume that we are on a workstation */
4028 /* Now verify that SYS$COMMAND is a terminal */
4029 /* for creating the debugger DECTerm */
4032 items[0].code = DVI$_DEVCLASS;
4033 items[0].bufadr = &devclass;
4034 items[0].retadr = NULL;
4038 status = sys$getdviw
4039 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
4041 if ($VMS_STATUS_SUCCESS(status)) {
4042 status = dvi_iosb[0];
4045 if (!$VMS_STATUS_SUCCESS(status)) {
4046 SETERRNO(EVMSERR, status);
4050 if (devclass == DC$_TERM) {
4057 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
4058 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
4063 char device_name[65];
4064 unsigned short device_name_len;
4065 struct dsc$descriptor_s customization_dsc;
4066 struct dsc$descriptor_s device_name_dsc;
4069 char customization[200];
4073 unsigned short p_chan;
4075 unsigned short iosb[4];
4076 struct item_list_3 items[2];
4077 const char * cust_str =
4078 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
4079 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
4080 DSC$K_CLASS_S, mbx1};
4082 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
4083 /*---------------------------------------*/
4084 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
4087 /* Make sure that this is from the Perl debugger */
4088 ret_char = strstr(cmd," xterm ");
4089 if (ret_char == NULL)
4091 cptr = ret_char + 7;
4092 ret_char = strstr(cmd,"tty");
4093 if (ret_char == NULL)
4095 ret_char = strstr(cmd,"sleep");
4096 if (ret_char == NULL)
4099 if (decw_term_port == 0) {
4100 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
4101 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
4102 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
4104 status = lib$find_image_symbol
4106 &decw_term_port_dsc,
4107 (void *)&decw_term_port,
4111 /* Try again with the other image name */
4112 if (!$VMS_STATUS_SUCCESS(status)) {
4114 status = lib$find_image_symbol
4116 &decw_term_port_dsc,
4117 (void *)&decw_term_port,
4126 /* No decw$term_port, give it up */
4127 if (!$VMS_STATUS_SUCCESS(status))
4130 /* Are we on a workstation? */
4131 /* to do: capture the rows / columns and pass their properties */
4132 ret_stat = vms_is_syscommand_xterm();
4136 /* Make the title: */
4137 ret_char = strstr(cptr,"-title");
4138 if (ret_char != NULL) {
4139 while ((*cptr != 0) && (*cptr != '\"')) {
4145 while ((*cptr != 0) && (*cptr != '\"')) {
4158 strcpy(title,"Perl Debug DECTerm");
4160 sprintf(customization, cust_str, title);
4162 customization_dsc.dsc$a_pointer = customization;
4163 customization_dsc.dsc$w_length = strlen(customization);
4164 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4165 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
4167 device_name_dsc.dsc$a_pointer = device_name;
4168 device_name_dsc.dsc$w_length = sizeof device_name -1;
4169 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4170 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
4172 device_name_len = 0;
4174 /* Try to create the window */
4175 status = (*decw_term_port)
4184 if (!$VMS_STATUS_SUCCESS(status)) {
4185 SETERRNO(EVMSERR, status);
4189 device_name[device_name_len] = '\0';
4191 /* Need to set this up to look like a pipe for cleanup */
4193 status = lib$get_vm(&n, &info);
4194 if (!$VMS_STATUS_SUCCESS(status)) {
4195 SETERRNO(ENOMEM, status);
4201 info->completion = 0;
4202 info->closing = FALSE;
4209 info->in_done = TRUE;
4210 info->out_done = TRUE;
4211 info->err_done = TRUE;
4213 /* Assign a channel on this so that it will persist, and not login */
4214 /* We stash this channel in the info structure for reference. */
4215 /* The created xterm self destructs when the last channel is removed */
4216 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4217 /* So leave this assigned. */
4218 device_name_dsc.dsc$w_length = device_name_len;
4219 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4220 if (!$VMS_STATUS_SUCCESS(status)) {
4221 SETERRNO(EVMSERR, status);
4224 info->xchan_valid = 1;
4226 /* Now create a mailbox to be read by the application */
4228 create_mbx(&p_chan, &d_mbx1);
4230 /* write the name of the created terminal to the mailbox */
4231 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4232 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4234 if (!$VMS_STATUS_SUCCESS(status)) {
4235 SETERRNO(EVMSERR, status);
4239 info->fp = PerlIO_open(mbx1, mode);
4241 /* Done with this channel */
4244 /* If any errors, then clean up */
4247 _ckvmssts_noperl(lib$free_vm(&n, &info));
4255 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4258 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4260 static int handler_set_up = FALSE;
4262 unsigned long int sts, flags = CLI$M_NOWAIT;
4263 /* The use of a GLOBAL table (as was done previously) rendered
4264 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4265 * environment. Hence we've switched to LOCAL symbol table.
4267 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4269 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4270 char *in, *out, *err, mbx[512];
4272 char tfilebuf[NAM$C_MAXRSS+1];
4274 char cmd_sym_name[20];
4275 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4276 DSC$K_CLASS_S, symbol};
4277 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4279 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4280 DSC$K_CLASS_S, cmd_sym_name};
4281 struct dsc$descriptor_s *vmscmd;
4282 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4283 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4284 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4286 /* Check here for Xterm create request. This means looking for
4287 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4288 * is possible to create an xterm.
4290 if (*in_mode == 'r') {
4293 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4294 if (xterm_fd != NULL)
4298 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4300 /* once-per-program initialization...
4301 note that the SETAST calls and the dual test of pipe_ef
4302 makes sure that only the FIRST thread through here does
4303 the initialization...all other threads wait until it's
4306 Yeah, uglier than a pthread call, it's got all the stuff inline
4307 rather than in a separate routine.
4311 _ckvmssts_noperl(sys$setast(0));
4313 unsigned long int pidcode = JPI$_PID;
4314 $DESCRIPTOR(d_delay, RETRY_DELAY);
4315 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4316 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4317 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4319 if (!handler_set_up) {
4320 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4321 handler_set_up = TRUE;
4323 _ckvmssts_noperl(sys$setast(1));
4326 /* see if we can find a VMSPIPE.COM */
4329 vmspipe = find_vmspipe(aTHX);
4331 strcpy(tfilebuf+1,vmspipe);
4332 } else { /* uh, oh...we're in tempfile hell */
4333 tpipe = vmspipe_tempfile(aTHX);
4334 if (!tpipe) { /* a fish popular in Boston */
4335 if (ckWARN(WARN_PIPE)) {
4336 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4340 fgetname(tpipe,tfilebuf+1,1);
4342 vmspipedsc.dsc$a_pointer = tfilebuf;
4343 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4345 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4348 case RMS$_FNF: case RMS$_DNF:
4349 set_errno(ENOENT); break;
4351 set_errno(ENOTDIR); break;
4353 set_errno(ENODEV); break;
4355 set_errno(EACCES); break;
4357 set_errno(EINVAL); break;
4358 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4359 set_errno(E2BIG); break;
4360 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4361 _ckvmssts_noperl(sts); /* fall through */
4362 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4365 set_vaxc_errno(sts);
4366 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4367 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4373 _ckvmssts_noperl(lib$get_vm(&n, &info));
4375 strcpy(mode,in_mode);
4378 info->completion = 0;
4379 info->closing = FALSE;
4386 info->in_done = TRUE;
4387 info->out_done = TRUE;
4388 info->err_done = TRUE;
4390 info->xchan_valid = 0;
4392 in = PerlMem_malloc(VMS_MAXRSS);
4393 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4394 out = PerlMem_malloc(VMS_MAXRSS);
4395 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4396 err = PerlMem_malloc(VMS_MAXRSS);
4397 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4399 in[0] = out[0] = err[0] = '\0';
4401 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4405 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4410 if (*mode == 'r') { /* piping from subroutine */
4412 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4414 info->out->pipe_done = &info->out_done;
4415 info->out_done = FALSE;
4416 info->out->info = info;
4418 if (!info->useFILE) {
4419 info->fp = PerlIO_open(mbx, mode);
4421 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4422 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4425 if (!info->fp && info->out) {
4426 sys$cancel(info->out->chan_out);
4428 while (!info->out_done) {
4430 _ckvmssts_noperl(sys$setast(0));
4431 done = info->out_done;
4432 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4433 _ckvmssts_noperl(sys$setast(1));
4434 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4437 if (info->out->buf) {
4438 n = info->out->bufsize * sizeof(char);
4439 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4442 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4444 _ckvmssts_noperl(lib$free_vm(&n, &info));
4449 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4451 info->err->pipe_done = &info->err_done;
4452 info->err_done = FALSE;
4453 info->err->info = info;
4456 } else if (*mode == 'w') { /* piping to subroutine */
4458 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4460 info->out->pipe_done = &info->out_done;
4461 info->out_done = FALSE;
4462 info->out->info = info;
4465 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4467 info->err->pipe_done = &info->err_done;
4468 info->err_done = FALSE;
4469 info->err->info = info;
4472 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4473 if (!info->useFILE) {
4474 info->fp = PerlIO_open(mbx, mode);
4476 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4477 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4481 info->in->pipe_done = &info->in_done;
4482 info->in_done = FALSE;
4483 info->in->info = info;
4487 if (!info->fp && info->in) {
4489 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4490 0, 0, 0, 0, 0, 0, 0, 0));
4492 while (!info->in_done) {
4494 _ckvmssts_noperl(sys$setast(0));
4495 done = info->in_done;
4496 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4497 _ckvmssts_noperl(sys$setast(1));
4498 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4501 if (info->in->buf) {
4502 n = info->in->bufsize * sizeof(char);
4503 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4506 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4508 _ckvmssts_noperl(lib$free_vm(&n, &info));
4514 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4515 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4517 info->out->pipe_done = &info->out_done;
4518 info->out_done = FALSE;
4519 info->out->info = info;
4522 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4524 info->err->pipe_done = &info->err_done;
4525 info->err_done = FALSE;
4526 info->err->info = info;
4530 symbol[MAX_DCL_SYMBOL] = '\0';
4532 strncpy(symbol, in, MAX_DCL_SYMBOL);
4533 d_symbol.dsc$w_length = strlen(symbol);
4534 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4536 strncpy(symbol, err, MAX_DCL_SYMBOL);
4537 d_symbol.dsc$w_length = strlen(symbol);
4538 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4540 strncpy(symbol, out, MAX_DCL_SYMBOL);
4541 d_symbol.dsc$w_length = strlen(symbol);
4542 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4544 /* Done with the names for the pipes */
4549 p = vmscmd->dsc$a_pointer;
4550 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4551 if (*p == '$') p++; /* remove leading $ */
4552 while (*p == ' ' || *p == '\t') p++;
4554 for (j = 0; j < 4; j++) {
4555 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4556 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4558 strncpy(symbol, p, MAX_DCL_SYMBOL);
4559 d_symbol.dsc$w_length = strlen(symbol);
4560 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4562 if (strlen(p) > MAX_DCL_SYMBOL) {
4563 p += MAX_DCL_SYMBOL;
4568 _ckvmssts_noperl(sys$setast(0));
4569 info->next=open_pipes; /* prepend to list */
4571 _ckvmssts_noperl(sys$setast(1));
4572 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4573 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4574 * have SYS$COMMAND if we need it.
4576 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4577 0, &info->pid, &info->completion,
4578 0, popen_completion_ast,info,0,0,0));
4580 /* if we were using a tempfile, close it now */
4582 if (tpipe) fclose(tpipe);
4584 /* once the subprocess is spawned, it has copied the symbols and
4585 we can get rid of ours */
4587 for (j = 0; j < 4; j++) {
4588 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4589 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4590 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4592 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4593 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4594 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4595 vms_execfree(vmscmd);
4597 #ifdef PERL_IMPLICIT_CONTEXT
4600 PL_forkprocess = info->pid;
4607 _ckvmssts_noperl(sys$setast(0));
4609 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4610 _ckvmssts_noperl(sys$setast(1));
4611 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4613 *psts = info->completion;
4614 /* Caller thinks it is open and tries to close it. */
4615 /* This causes some problems, as it changes the error status */
4616 /* my_pclose(info->fp); */
4618 /* If we did not have a file pointer open, then we have to */
4619 /* clean up here or eventually we will run out of something */
4621 if (info->fp == NULL) {
4622 my_pclose_pinfo(aTHX_ info);
4630 } /* end of safe_popen */
4633 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4635 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4639 TAINT_PROPER("popen");
4640 PERL_FLUSHALL_FOR_CHILD;
4641 return safe_popen(aTHX_ cmd,mode,&sts);
4647 /* Routine to close and cleanup a pipe info structure */
4649 static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4651 unsigned long int retsts;
4656 /* If we were writing to a subprocess, insure that someone reading from
4657 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4658 * produce an EOF record in the mailbox.
4660 * well, at least sometimes it *does*, so we have to watch out for
4661 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4665 #if defined(USE_ITHREADS)
4668 && PL_perlio_fd_refcnt)
4669 PerlIO_flush(info->fp);
4671 fflush((FILE *)info->fp);
4674 _ckvmssts(sys$setast(0));
4675 info->closing = TRUE;
4676 done = info->done && info->in_done && info->out_done && info->err_done;
4677 /* hanging on write to Perl's input? cancel it */
4678 if (info->mode == 'r' && info->out && !info->out_done) {
4679 if (info->out->chan_out) {
4680 _ckvmssts(sys$cancel(info->out->chan_out));
4681 if (!info->out->chan_in) { /* EOF generation, need AST */
4682 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4686 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4687 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4689 _ckvmssts(sys$setast(1));
4692 #if defined(USE_ITHREADS)
4695 && PL_perlio_fd_refcnt)
4696 PerlIO_close(info->fp);
4698 fclose((FILE *)info->fp);
4701 we have to wait until subprocess completes, but ALSO wait until all
4702 the i/o completes...otherwise we'll be freeing the "info" structure
4703 that the i/o ASTs could still be using...
4707 _ckvmssts(sys$setast(0));
4708 done = info->done && info->in_done && info->out_done && info->err_done;
4709 if (!done) _ckvmssts(sys$clref(pipe_ef));
4710 _ckvmssts(sys$setast(1));
4711 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4713 retsts = info->completion;
4715 /* remove from list of open pipes */
4716 _ckvmssts(sys$setast(0));
4718 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4724 last->next = info->next;
4726 open_pipes = info->next;
4727 _ckvmssts(sys$setast(1));
4729 /* free buffers and structures */
4732 if (info->in->buf) {
4733 n = info->in->bufsize * sizeof(char);
4734 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4737 _ckvmssts(lib$free_vm(&n, &info->in));
4740 if (info->out->buf) {
4741 n = info->out->bufsize * sizeof(char);
4742 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4745 _ckvmssts(lib$free_vm(&n, &info->out));
4748 if (info->err->buf) {
4749 n = info->err->bufsize * sizeof(char);
4750 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4753 _ckvmssts(lib$free_vm(&n, &info->err));
4756 _ckvmssts(lib$free_vm(&n, &info));
4762 /*{{{ I32 my_pclose(PerlIO *fp)*/
4763 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4765 pInfo info, last = NULL;
4768 /* Fixme - need ast and mutex protection here */
4769 for (info = open_pipes; info != NULL; last = info, info = info->next)
4770 if (info->fp == fp) break;
4772 if (info == NULL) { /* no such pipe open */
4773 set_errno(ECHILD); /* quoth POSIX */
4774 set_vaxc_errno(SS$_NONEXPR);
4778 ret_status = my_pclose_pinfo(aTHX_ info);
4782 } /* end of my_pclose() */
4784 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4785 /* Roll our own prototype because we want this regardless of whether
4786 * _VMS_WAIT is defined.
4788 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4790 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4791 created with popen(); otherwise partially emulate waitpid() unless
4792 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4793 Also check processes not considered by the CRTL waitpid().
4795 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4797 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4804 if (statusp) *statusp = 0;
4806 for (info = open_pipes; info != NULL; info = info->next)
4807 if (info->pid == pid) break;
4809 if (info != NULL) { /* we know about this child */
4810 while (!info->done) {
4811 _ckvmssts(sys$setast(0));
4813 if (!done) _ckvmssts(sys$clref(pipe_ef));
4814 _ckvmssts(sys$setast(1));
4815 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4818 if (statusp) *statusp = info->completion;
4822 /* child that already terminated? */
4824 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4825 if (closed_list[j].pid == pid) {
4826 if (statusp) *statusp = closed_list[j].completion;
4831 /* fall through if this child is not one of our own pipe children */
4833 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4835 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4836 * in 7.2 did we get a version that fills in the VMS completion
4837 * status as Perl has always tried to do.
4840 sts = __vms_waitpid( pid, statusp, flags );
4842 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4845 /* If the real waitpid tells us the child does not exist, we
4846 * fall through here to implement waiting for a child that
4847 * was created by some means other than exec() (say, spawned
4848 * from DCL) or to wait for a process that is not a subprocess
4849 * of the current process.
4852 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4855 $DESCRIPTOR(intdsc,"0 00:00:01");
4856 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4857 unsigned long int pidcode = JPI$_PID, mypid;
4858 unsigned long int interval[2];
4859 unsigned int jpi_iosb[2];
4860 struct itmlst_3 jpilist[2] = {
4861 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4866 /* Sorry folks, we don't presently implement rooting around for
4867 the first child we can find, and we definitely don't want to
4868 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4874 /* Get the owner of the child so I can warn if it's not mine. If the
4875 * process doesn't exist or I don't have the privs to look at it,
4876 * I can go home early.
4878 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4879 if (sts & 1) sts = jpi_iosb[0];
4891 set_vaxc_errno(sts);
4895 if (ckWARN(WARN_EXEC)) {
4896 /* remind folks they are asking for non-standard waitpid behavior */
4897 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4898 if (ownerpid != mypid)
4899 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4900 "waitpid: process %x is not a child of process %x",
4904 /* simply check on it once a second until it's not there anymore. */
4906 _ckvmssts(sys$bintim(&intdsc,interval));
4907 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4908 _ckvmssts(sys$schdwk(0,0,interval,0));
4909 _ckvmssts(sys$hiber());
4911 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4916 } /* end of waitpid() */
4921 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4923 my_gconvert(double val, int ndig, int trail, char *buf)
4925 static char __gcvtbuf[DBL_DIG+1];
4928 loc = buf ? buf : __gcvtbuf;
4930 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
4932 sprintf(loc,"%.*g",ndig,val);
4938 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4939 return gcvt(val,ndig,loc);
4942 loc[0] = '0'; loc[1] = '\0';
4949 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4950 static int rms_free_search_context(struct FAB * fab)
4954 nam = fab->fab$l_nam;
4955 nam->nam$b_nop |= NAM$M_SYNCHK;
4956 nam->nam$l_rlf = NULL;
4958 return sys$parse(fab, NULL, NULL);
4961 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4962 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4963 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4964 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4965 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4966 #define rms_nam_esll(nam) nam.nam$b_esl
4967 #define rms_nam_esl(nam) nam.nam$b_esl
4968 #define rms_nam_name(nam) nam.nam$l_name
4969 #define rms_nam_namel(nam) nam.nam$l_name
4970 #define rms_nam_type(nam) nam.nam$l_type
4971 #define rms_nam_typel(nam) nam.nam$l_type
4972 #define rms_nam_ver(nam) nam.nam$l_ver
4973 #define rms_nam_verl(nam) nam.nam$l_ver
4974 #define rms_nam_rsll(nam) nam.nam$b_rsl
4975 #define rms_nam_rsl(nam) nam.nam$b_rsl
4976 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4977 #define rms_set_fna(fab, nam, name, size) \
4978 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4979 #define rms_get_fna(fab, nam) fab.fab$l_fna
4980 #define rms_set_dna(fab, nam, name, size) \
4981 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4982 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4983 #define rms_set_esa(nam, name, size) \
4984 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4985 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4986 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4987 #define rms_set_rsa(nam, name, size) \
4988 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4989 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4990 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4991 #define rms_nam_name_type_l_size(nam) \
4992 (nam.nam$b_name + nam.nam$b_type)
4994 static int rms_free_search_context(struct FAB * fab)
4998 nam = fab->fab$l_naml;
4999 nam->naml$b_nop |= NAM$M_SYNCHK;
5000 nam->naml$l_rlf = NULL;
5001 nam->naml$l_long_defname_size = 0;
5004 return sys$parse(fab, NULL, NULL);
5007 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
5008 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
5009 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
5010 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
5011 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
5012 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
5013 #define rms_nam_esl(nam) nam.naml$b_esl
5014 #define rms_nam_name(nam) nam.naml$l_name
5015 #define rms_nam_namel(nam) nam.naml$l_long_name
5016 #define rms_nam_type(nam) nam.naml$l_type
5017 #define rms_nam_typel(nam) nam.naml$l_long_type
5018 #define rms_nam_ver(nam) nam.naml$l_ver
5019 #define rms_nam_verl(nam) nam.naml$l_long_ver
5020 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
5021 #define rms_nam_rsl(nam) nam.naml$b_rsl
5022 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
5023 #define rms_set_fna(fab, nam, name, size) \
5024 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
5025 nam.naml$l_long_filename_size = size; \
5026 nam.naml$l_long_filename = name;}
5027 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
5028 #define rms_set_dna(fab, nam, name, size) \
5029 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
5030 nam.naml$l_long_defname_size = size; \
5031 nam.naml$l_long_defname = name; }
5032 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
5033 #define rms_set_esa(nam, name, size) \
5034 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
5035 nam.naml$l_long_expand_alloc = size; \
5036 nam.naml$l_long_expand = name; }
5037 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
5038 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
5039 nam.naml$l_long_expand = l_name; \
5040 nam.naml$l_long_expand_alloc = l_size; }
5041 #define rms_set_rsa(nam, name, size) \
5042 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
5043 nam.naml$l_long_result = name; \
5044 nam.naml$l_long_result_alloc = size; }
5045 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
5046 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
5047 nam.naml$l_long_result = l_name; \
5048 nam.naml$l_long_result_alloc = l_size; }
5049 #define rms_nam_name_type_l_size(nam) \
5050 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
5055 * The CRTL for 8.3 and later can create symbolic links in any mode,
5056 * however in 8.3 the unlink/remove/delete routines will only properly handle
5057 * them if one of the PCP modes is active.
5059 static int rms_erase(const char * vmsname)
5062 struct FAB myfab = cc$rms_fab;
5063 rms_setup_nam(mynam);
5065 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
5066 rms_bind_fab_nam(myfab, mynam);
5068 /* Are we removing all versions? */
5069 if (vms_unlink_all_versions == 1) {
5070 const char * defspec = ";*";
5071 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
5074 #ifdef NAML$M_OPEN_SPECIAL
5075 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5078 status = sys$erase(&myfab, 0, 0);
5085 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
5086 const struct dsc$descriptor_s * vms_dst_dsc,
5087 unsigned long flags)
5089 /* VMS and UNIX handle file permissions differently and the
5090 * the same ACL trick may be needed for renaming files,
5091 * especially if they are directories.
5094 /* todo: get kill_file and rename to share common code */
5095 /* I can not find online documentation for $change_acl
5096 * it appears to be replaced by $set_security some time ago */
5098 const unsigned int access_mode = 0;
5099 $DESCRIPTOR(obj_file_dsc,"FILE");
5102 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
5103 int aclsts, fndsts, rnsts = -1;
5104 unsigned int ctx = 0;
5105 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5106 struct dsc$descriptor_s * clean_dsc;
5109 unsigned char myace$b_length;
5110 unsigned char myace$b_type;
5111 unsigned short int myace$w_flags;
5112 unsigned long int myace$l_access;
5113 unsigned long int myace$l_ident;
5114 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
5115 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
5117 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
5120 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
5121 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
5123 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
5124 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
5128 /* Expand the input spec using RMS, since we do not want to put
5129 * ACLs on the target of a symbolic link */
5130 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
5131 if (vmsname == NULL)
5134 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
5136 PERL_RMSEXPAND_M_SYMLINK);
5138 PerlMem_free(vmsname);
5142 /* So we get our own UIC to use as a rights identifier,
5143 * and the insert an ACE at the head of the ACL which allows us
5144 * to delete the file.
5146 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
5148 fildsc.dsc$w_length = strlen(vmsname);
5149 fildsc.dsc$a_pointer = vmsname;
5151 newace.myace$l_ident = oldace.myace$l_ident;
5154 /* Grab any existing ACEs with this identifier in case we fail */
5155 clean_dsc = &fildsc;
5156 aclsts = fndsts = sys$get_security(&obj_file_dsc,
5164 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
5165 /* Add the new ACE . . . */
5167 /* if the sys$get_security succeeded, then ctx is valid, and the
5168 * object/file descriptors will be ignored. But otherwise they
5171 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
5172 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5173 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5175 set_vaxc_errno(aclsts);
5176 PerlMem_free(vmsname);
5180 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5183 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5185 if ($VMS_STATUS_SUCCESS(rnsts)) {
5186 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5189 /* Put things back the way they were. */
5191 aclsts = sys$get_security(&obj_file_dsc,
5199 if ($VMS_STATUS_SUCCESS(aclsts)) {
5203 if (!$VMS_STATUS_SUCCESS(fndsts))
5204 sec_flags = OSS$M_RELCTX;
5206 /* Get rid of the new ACE */
5207 aclsts = sys$set_security(NULL, NULL, NULL,
5208 sec_flags, dellst, &ctx, &access_mode);
5210 /* If there was an old ACE, put it back */
5211 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5212 addlst[0].bufadr = &oldace;
5213 aclsts = sys$set_security(NULL, NULL, NULL,
5214 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5215 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5217 set_vaxc_errno(aclsts);
5223 /* Try to clear the lock on the ACL list */
5224 aclsts2 = sys$set_security(NULL, NULL, NULL,
5225 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5227 /* Rename errors are most important */
5228 if (!$VMS_STATUS_SUCCESS(rnsts))
5231 set_vaxc_errno(aclsts);
5236 if (aclsts != SS$_ACLEMPTY)
5243 PerlMem_free(vmsname);
5248 /*{{{int rename(const char *, const char * */
5249 /* Not exactly what X/Open says to do, but doing it absolutely right
5250 * and efficiently would require a lot more work. This should be close
5251 * enough to pass all but the most strict X/Open compliance test.
5254 Perl_rename(pTHX_ const char *src, const char * dst)
5263 /* Validate the source file */
5264 src_sts = Perl_flex_lstat(NULL, src, &src_st);
5267 /* No source file or other problem */
5270 if (src_st.st_devnam[0] == 0) {
5271 /* This may be possible so fail if it is seen. */
5276 dst_sts = Perl_flex_lstat(NULL, dst, &dst_st);
5279 if (dst_st.st_dev != src_st.st_dev) {
5280 /* Must be on the same device */
5285 /* VMS_INO_T_COMPARE is true if the inodes are different
5286 * to match the output of memcmp
5289 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5290 /* That was easy, the files are the same! */
5294 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5295 /* If source is a directory, so must be dest */
5303 if ((dst_sts == 0) &&
5304 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5306 /* We have issues here if vms_unlink_all_versions is set
5307 * If the destination exists, and is not a directory, then
5308 * we must delete in advance.
5310 * If the src is a directory, then we must always pre-delete
5313 * If we successfully delete the dst in advance, and the rename fails
5314 * X/Open requires that errno be EIO.
5318 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5320 d_sts = mp_do_kill_file(NULL, dst_st.st_devnam,
5321 S_ISDIR(dst_st.st_mode));
5323 /* Need to delete all versions ? */
5324 if ((d_sts == 0) && (vms_unlink_all_versions == 1)) {
5327 while (lstat(dst_st.st_devnam, &dst_st.crtl_stat) == 0) {
5328 d_sts = mp_do_kill_file(NULL, dst_st.st_devnam, 0);
5333 /* Make sure that we do not loop forever */
5345 /* We killed the destination, so only errno now is EIO */
5350 /* Originally the idea was to call the CRTL rename() and only
5351 * try the lib$rename_file if it failed.
5352 * It turns out that there are too many variants in what the
5353 * the CRTL rename might do, so only use lib$rename_file
5358 /* Is the source and dest both in VMS format */
5359 /* if the source is a directory, then need to fileify */
5360 /* and dest must be a directory or non-existant. */
5365 unsigned long flags;
5366 struct dsc$descriptor_s old_file_dsc;
5367 struct dsc$descriptor_s new_file_dsc;
5369 /* We need to modify the src and dst depending
5370 * on if one or more of them are directories.
5373 vms_dst = PerlMem_malloc(VMS_MAXRSS);
5374 if (vms_dst == NULL)
5375 _ckvmssts_noperl(SS$_INSFMEM);
5377 if (S_ISDIR(src_st.st_mode)) {
5379 char * vms_dir_file;
5381 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5382 if (vms_dir_file == NULL)
5383 _ckvmssts_noperl(SS$_INSFMEM);
5385 /* If the dest is a directory, we must remove it
5388 d_sts = mp_do_kill_file(NULL dst_st.st_devnam, 1);
5390 PerlMem_free(vms_dst);
5398 /* The dest must be a VMS file specification */
5399 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5400 if (ret_str == NULL) {
5401 PerlMem_free(vms_dst);
5406 /* The source must be a file specification */
5407 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5408 if (vms_dir_file == NULL)
5409 _ckvmssts_noperl(SS$_INSFMEM);
5411 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5412 if (ret_str == NULL) {
5413 PerlMem_free(vms_dst);
5414 PerlMem_free(vms_dir_file);
5418 PerlMem_free(vms_dst);
5419 vms_dst = vms_dir_file;
5422 /* File to file or file to new dir */
5424 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5425 /* VMS pathify a dir target */
5426 ret_str = int_tovmspath(dst, vms_dst, NULL);
5427 if (ret_str == NULL) {
5428 PerlMem_free(vms_dst);
5433 char * v_spec, * r_spec, * d_spec, * n_spec;
5434 char * e_spec, * vs_spec;
5435 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5437 /* fileify a target VMS file specification */
5438 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5439 if (ret_str == NULL) {
5440 PerlMem_free(vms_dst);
5445 sts = vms_split_path(vms_dst, &v_spec, &v_len, &r_spec, &r_len,
5446 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5447 &e_len, &vs_spec, &vs_len);
5450 /* Get rid of the version */
5454 /* Need to specify a '.' so that the extension */
5455 /* is not inherited */
5456 strcat(vms_dst,".");
5462 old_file_dsc.dsc$a_pointer = src_st.st_devnam;
5463 old_file_dsc.dsc$w_length = strlen(src_st.st_devnam);
5464 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5465 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5467 new_file_dsc.dsc$a_pointer = vms_dst;
5468 new_file_dsc.dsc$w_length = strlen(vms_dst);
5469 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5470 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5473 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5474 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5477 sts = lib$rename_file(&old_file_dsc,
5481 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5482 if (!$VMS_STATUS_SUCCESS(sts)) {
5484 /* We could have failed because VMS style permissions do not
5485 * permit renames that UNIX will allow. Just like the hack
5488 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5491 PerlMem_free(vms_dst);
5492 if (!$VMS_STATUS_SUCCESS(sts)) {
5499 if (vms_unlink_all_versions) {
5500 /* Now get rid of any previous versions of the source file that
5506 src_sts = mp_do_kill_file(NULL, src_st.st_devnam,
5507 S_ISDIR(src_st.st_mode));
5508 while (lstat(src_st.st_devnam, &src_st.crtl_stat) == 0) {
5509 src_sts = mp_do_kill_file(NULL, src_st.st_devnam,
5510 S_ISDIR(src_st.st_mode));
5515 /* Make sure that we do not loop forever */
5524 /* We deleted the destination, so must force the error to be EIO */
5525 if ((retval != 0) && (pre_delete != 0))
5533 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5534 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5535 * to expand file specification. Allows for a single default file
5536 * specification and a simple mask of options. If outbuf is non-NULL,
5537 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5538 * the resultant file specification is placed. If outbuf is NULL, the
5539 * resultant file specification is placed into a static buffer.
5540 * The third argument, if non-NULL, is taken to be a default file
5541 * specification string. The fourth argument is unused at present.
5542 * rmesexpand() returns the address of the resultant string if
5543 * successful, and NULL on error.
5545 * New functionality for previously unused opts value:
5546 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5547 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5548 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5549 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5551 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5555 (const char *filespec,
5557 const char *defspec,
5563 const char * in_spec;
5565 const char * def_spec;
5566 char * vmsfspec, *vmsdefspec;
5570 struct FAB myfab = cc$rms_fab;
5571 rms_setup_nam(mynam);
5573 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5576 /* temp hack until UTF8 is actually implemented */
5577 if (fs_utf8 != NULL)
5580 if (!filespec || !*filespec) {
5581 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5591 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5592 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5593 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5595 /* If this is a UNIX file spec, convert it to VMS */
5596 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5597 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5598 &e_len, &vs_spec, &vs_len);
5603 vmsfspec = PerlMem_malloc(VMS_MAXRSS);
5604 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5605 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5606 if (ret_spec == NULL) {
5607 PerlMem_free(vmsfspec);
5610 in_spec = (const char *)vmsfspec;
5612 /* Unless we are forcing to VMS format, a UNIX input means
5613 * UNIX output, and that requires long names to be used
5615 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5616 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5617 opts |= PERL_RMSEXPAND_M_LONG;
5625 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5626 rms_bind_fab_nam(myfab, mynam);
5628 /* Process the default file specification if present */
5630 if (defspec && *defspec) {
5632 t_isunix = is_unix_filespec(defspec);
5634 vmsdefspec = PerlMem_malloc(VMS_MAXRSS);
5635 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5636 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5638 if (ret_spec == NULL) {
5639 /* Clean up and bail */
5640 PerlMem_free(vmsdefspec);
5641 if (vmsfspec != NULL)
5642 PerlMem_free(vmsfspec);
5645 def_spec = (const char *)vmsdefspec;
5647 rms_set_dna(myfab, mynam,
5648 (char *)def_spec, strlen(def_spec)); /* cast ok */
5651 /* Now we need the expansion buffers */
5652 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
5653 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5654 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5655 esal = PerlMem_malloc(VMS_MAXRSS);
5656 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5658 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5660 /* If a NAML block is used RMS always writes to the long and short
5661 * addresses unless you suppress the short name.
5663 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5664 outbufl = PerlMem_malloc(VMS_MAXRSS);
5665 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5667 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5669 #ifdef NAM$M_NO_SHORT_UPCASE
5670 if (decc_efs_case_preserve)
5671 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5674 /* We may not want to follow symbolic links */
5675 #ifdef NAML$M_OPEN_SPECIAL
5676 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5677 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5680 /* First attempt to parse as an existing file */
5681 retsts = sys$parse(&myfab,0,0);
5682 if (!(retsts & STS$K_SUCCESS)) {
5684 /* Could not find the file, try as syntax only if error is not fatal */
5685 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5686 if (retsts == RMS$_DNF ||
5687 retsts == RMS$_DIR ||
5688 retsts == RMS$_DEV ||
5689 retsts == RMS$_PRV) {
5690 retsts = sys$parse(&myfab,0,0);
5691 if (retsts & STS$K_SUCCESS) goto int_expanded;
5694 /* Still could not parse the file specification */
5695 /*----------------------------------------------*/
5696 sts = rms_free_search_context(&myfab); /* Free search context */
5697 if (vmsdefspec != NULL)
5698 PerlMem_free(vmsdefspec);
5699 if (vmsfspec != NULL)
5700 PerlMem_free(vmsfspec);
5701 if (outbufl != NULL)
5702 PerlMem_free(outbufl);
5706 set_vaxc_errno(retsts);
5707 if (retsts == RMS$_PRV) set_errno(EACCES);
5708 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5709 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5710 else set_errno(EVMSERR);
5713 retsts = sys$search(&myfab,0,0);
5714 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5715 sts = rms_free_search_context(&myfab); /* Free search context */
5716 if (vmsdefspec != NULL)
5717 PerlMem_free(vmsdefspec);
5718 if (vmsfspec != NULL)
5719 PerlMem_free(vmsfspec);
5720 if (outbufl != NULL)
5721 PerlMem_free(outbufl);
5725 set_vaxc_errno(retsts);
5726 if (retsts == RMS$_PRV) set_errno(EACCES);
5727 else set_errno(EVMSERR);
5731 /* If the input filespec contained any lowercase characters,
5732 * downcase the result for compatibility with Unix-minded code. */
5734 if (!decc_efs_case_preserve) {
5736 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5737 if (islower(*tbuf)) { haslower = 1; break; }
5740 /* Is a long or a short name expected */
5741 /*------------------------------------*/
5743 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5744 if (rms_nam_rsll(mynam)) {
5746 speclen = rms_nam_rsll(mynam);
5749 spec_buf = esal; /* Not esa */
5750 speclen = rms_nam_esll(mynam);
5754 if (rms_nam_rsl(mynam)) {
5756 speclen = rms_nam_rsl(mynam);
5759 spec_buf = esa; /* Not esal */
5760 speclen = rms_nam_esl(mynam);
5763 spec_buf[speclen] = '\0';
5765 /* Trim off null fields added by $PARSE
5766 * If type > 1 char, must have been specified in original or default spec
5767 * (not true for version; $SEARCH may have added version of existing file).
5769 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5770 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5771 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5772 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5775 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5776 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5778 if (trimver || trimtype) {
5779 if (defspec && *defspec) {
5780 char *defesal = NULL;
5781 char *defesa = NULL;
5782 defesa = PerlMem_malloc(VMS_MAXRSS + 1);
5783 if (defesa != NULL) {
5784 struct FAB deffab = cc$rms_fab;
5785 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5786 defesal = PerlMem_malloc(VMS_MAXRSS + 1);
5787 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5789 rms_setup_nam(defnam);
5791 rms_bind_fab_nam(deffab, defnam);
5795 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5797 /* RMS needs the esa/esal as a work area if wildcards are involved */
5798 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5800 rms_clear_nam_nop(defnam);
5801 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5802 #ifdef NAM$M_NO_SHORT_UPCASE
5803 if (decc_efs_case_preserve)
5804 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5806 #ifdef NAML$M_OPEN_SPECIAL
5807 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5808 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5810 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5812 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5815 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5818 if (defesal != NULL)
5819 PerlMem_free(defesal);
5820 PerlMem_free(defesa);
5822 _ckvmssts_noperl(SS$_INSFMEM);
5826 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5827 if (*(rms_nam_verl(mynam)) != '\"')
5828 speclen = rms_nam_verl(mynam) - spec_buf;
5831 if (*(rms_nam_ver(mynam)) != '\"')
5832 speclen = rms_nam_ver(mynam) - spec_buf;
5836 /* If we didn't already trim version, copy down */
5837 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5838 if (speclen > rms_nam_verl(mynam) - spec_buf)
5840 (rms_nam_typel(mynam),
5841 rms_nam_verl(mynam),
5842 speclen - (rms_nam_verl(mynam) - spec_buf));
5843 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5846 if (speclen > rms_nam_ver(mynam) - spec_buf)
5848 (rms_nam_type(mynam),
5850 speclen - (rms_nam_ver(mynam) - spec_buf));
5851 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5856 /* Done with these copies of the input files */
5857 /*-------------------------------------------*/
5858 if (vmsfspec != NULL)
5859 PerlMem_free(vmsfspec);
5860 if (vmsdefspec != NULL)
5861 PerlMem_free(vmsdefspec);
5863 /* If we just had a directory spec on input, $PARSE "helpfully"
5864 * adds an empty name and type for us */
5865 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5866 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5867 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5868 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5869 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5870 speclen = rms_nam_namel(mynam) - spec_buf;
5875 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5876 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5877 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5878 speclen = rms_nam_name(mynam) - spec_buf;
5881 /* Posix format specifications must have matching quotes */
5882 if (speclen < (VMS_MAXRSS - 1)) {
5883 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5884 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5885 spec_buf[speclen] = '\"';
5890 spec_buf[speclen] = '\0';
5891 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5893 /* Have we been working with an expanded, but not resultant, spec? */
5894 /* Also, convert back to Unix syntax if necessary. */
5898 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5899 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5900 rsl = rms_nam_rsll(mynam);
5904 rsl = rms_nam_rsl(mynam);
5907 /* rsl is not present, it means that spec_buf is either */
5908 /* esa or esal, and needs to be copied to outbuf */
5909 /* convert to Unix if desired */
5911 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5913 /* VMS file specs are not in UTF-8 */
5914 if (fs_utf8 != NULL)
5916 strcpy(outbuf, spec_buf);
5921 /* Now spec_buf is either outbuf or outbufl */
5922 /* We need the result into outbuf */
5924 /* If we need this in UNIX, then we need another buffer */
5925 /* to keep things in order */
5927 char * new_src = NULL;
5928 if (spec_buf == outbuf) {
5929 new_src = PerlMem_malloc(VMS_MAXRSS);
5930 strcpy(new_src, spec_buf);
5934 ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5936 PerlMem_free(new_src);
5939 /* VMS file specs are not in UTF-8 */
5940 if (fs_utf8 != NULL)
5943 /* Copy the buffer if needed */
5944 if (outbuf != spec_buf)
5945 strcpy(outbuf, spec_buf);
5951 /* Need to clean up the search context */
5952 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5953 sts = rms_free_search_context(&myfab); /* Free search context */
5955 /* Clean up the extra buffers */
5959 if (outbufl != NULL)
5960 PerlMem_free(outbufl);
5962 /* Return the result */
5966 /* Common simple case - Expand an already VMS spec */
5968 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5969 opts |= PERL_RMSEXPAND_M_VMS_IN;
5970 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5973 /* Common simple case - Expand to a VMS spec */
5975 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5976 opts |= PERL_RMSEXPAND_M_VMS;
5977 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5981 /* Entry point used by perl routines */
5984 (pTHX_ const char *filespec,
5987 const char *defspec,
5992 static char __rmsexpand_retbuf[VMS_MAXRSS];
5993 char * expanded, *ret_spec, *ret_buf;
5997 if (ret_buf == NULL) {
5999 Newx(expanded, VMS_MAXRSS, char);
6000 if (expanded == NULL)
6001 _ckvmssts(SS$_INSFMEM);
6004 ret_buf = __rmsexpand_retbuf;
6009 ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
6010 opts, fs_utf8, dfs_utf8);
6012 if (ret_spec == NULL) {
6013 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6021 /* External entry points */
6022 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
6023 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
6024 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
6025 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
6026 char *Perl_rmsexpand_utf8
6027 (pTHX_ const char *spec, char *buf, const char *def,
6028 unsigned opt, int * fs_utf8, int * dfs_utf8)
6029 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
6030 char *Perl_rmsexpand_utf8_ts
6031 (pTHX_ const char *spec, char *buf, const char *def,
6032 unsigned opt, int * fs_utf8, int * dfs_utf8)
6033 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
6037 ** The following routines are provided to make life easier when
6038 ** converting among VMS-style and Unix-style directory specifications.
6039 ** All will take input specifications in either VMS or Unix syntax. On
6040 ** failure, all return NULL. If successful, the routines listed below
6041 ** return a pointer to a buffer containing the appropriately
6042 ** reformatted spec (and, therefore, subsequent calls to that routine
6043 ** will clobber the result), while the routines of the same names with
6044 ** a _ts suffix appended will return a pointer to a mallocd string
6045 ** containing the appropriately reformatted spec.
6046 ** In all cases, only explicit syntax is altered; no check is made that
6047 ** the resulting string is valid or that the directory in question
6050 ** fileify_dirspec() - convert a directory spec into the name of the
6051 ** directory file (i.e. what you can stat() to see if it's a dir).
6052 ** The style (VMS or Unix) of the result is the same as the style
6053 ** of the parameter passed in.
6054 ** pathify_dirspec() - convert a directory spec into a path (i.e.
6055 ** what you prepend to a filename to indicate what directory it's in).
6056 ** The style (VMS or Unix) of the result is the same as the style
6057 ** of the parameter passed in.
6058 ** tounixpath() - convert a directory spec into a Unix-style path.
6059 ** tovmspath() - convert a directory spec into a VMS-style path.
6060 ** tounixspec() - convert any file spec into a Unix-style file spec.
6061 ** tovmsspec() - convert any file spec into a VMS-style spec.
6062 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
6064 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
6065 ** Permission is given to distribute this code as part of the Perl
6066 ** standard distribution under the terms of the GNU General Public
6067 ** License or the Perl Artistic License. Copies of each may be
6068 ** found in the Perl standard distribution.
6071 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6073 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
6075 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
6076 char *cp1, *cp2, *lastdir;
6077 char *trndir, *vmsdir;
6078 unsigned short int trnlnm_iter_count;
6082 if (utf8_fl != NULL)
6085 if (!dir || !*dir) {
6086 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6088 dirlen = strlen(dir);
6089 while (dirlen && dir[dirlen-1] == '/') --dirlen;
6090 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
6091 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
6098 if (dirlen > (VMS_MAXRSS - 1)) {
6099 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
6102 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
6103 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6104 if (!strpbrk(dir+1,"/]>:") &&
6105 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
6106 strcpy(trndir,*dir == '/' ? dir + 1: dir);
6107 trnlnm_iter_count = 0;
6108 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
6109 trnlnm_iter_count++;
6110 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6112 dirlen = strlen(trndir);
6115 strncpy(trndir,dir,dirlen);
6116 trndir[dirlen] = '\0';
6119 /* At this point we are done with *dir and use *trndir which is a
6120 * copy that can be modified. *dir must not be modified.
6123 /* If we were handed a rooted logical name or spec, treat it like a
6124 * simple directory, so that
6125 * $ Define myroot dev:[dir.]
6126 * ... do_fileify_dirspec("myroot",buf,1) ...
6127 * does something useful.
6129 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
6130 trndir[--dirlen] = '\0';
6131 trndir[dirlen-1] = ']';
6133 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
6134 trndir[--dirlen] = '\0';
6135 trndir[dirlen-1] = '>';
6138 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
6139 /* If we've got an explicit filename, we can just shuffle the string. */
6140 if (*(cp1+1)) hasfilename = 1;
6141 /* Similarly, we can just back up a level if we've got multiple levels
6142 of explicit directories in a VMS spec which ends with directories. */
6144 for (cp2 = cp1; cp2 > trndir; cp2--) {
6146 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
6147 /* fix-me, can not scan EFS file specs backward like this */
6148 *cp2 = *cp1; *cp1 = '\0';
6153 if (*cp2 == '[' || *cp2 == '<') break;
6158 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
6159 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6160 cp1 = strpbrk(trndir,"]:>");
6161 if (hasfilename || !cp1) { /* filename present or not VMS */
6163 if (decc_efs_charset && !cp1) {
6165 /* EFS handling for UNIX mode */
6167 /* Just remove the trailing '/' and we should be done */
6169 trndir_len = strlen(trndir);
6171 if (trndir_len > 1) {
6173 if (trndir[trndir_len] == '/') {
6174 trndir[trndir_len] = '\0';
6177 strcpy(buf, trndir);
6178 PerlMem_free(trndir);
6179 PerlMem_free(vmsdir);
6183 /* For non-EFS mode, this is left for backwards compatibility */
6184 /* For EFS mode, this is only done for VMS format filespecs as */
6185 /* Perl programs generally have problems when a UNIX format spec */
6186 /* returns a VMS format spec */
6187 if (trndir[0] == '.') {
6188 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
6189 PerlMem_free(trndir);
6190 PerlMem_free(vmsdir);
6191 return int_fileify_dirspec("[]", buf, NULL);
6193 else if (trndir[1] == '.' &&
6194 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
6195 PerlMem_free(trndir);
6196 PerlMem_free(vmsdir);
6197 return int_fileify_dirspec("[-]", buf, NULL);
6200 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
6201 dirlen -= 1; /* to last element */
6202 lastdir = strrchr(trndir,'/');
6204 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
6205 /* If we have "/." or "/..", VMSify it and let the VMS code
6206 * below expand it, rather than repeating the code to handle
6207 * relative components of a filespec here */
6209 if (*(cp1+2) == '.') cp1++;
6210 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6212 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6213 PerlMem_free(trndir);
6214 PerlMem_free(vmsdir);
6217 if (strchr(vmsdir,'/') != NULL) {
6218 /* If int_tovmsspec() returned it, it must have VMS syntax
6219 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6220 * the time to check this here only so we avoid a recursion
6221 * loop; otherwise, gigo.
6223 PerlMem_free(trndir);
6224 PerlMem_free(vmsdir);
6225 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6228 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6229 PerlMem_free(trndir);
6230 PerlMem_free(vmsdir);
6233 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6234 PerlMem_free(trndir);
6235 PerlMem_free(vmsdir);
6239 } while ((cp1 = strstr(cp1,"/.")) != NULL);
6240 lastdir = strrchr(trndir,'/');
6242 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6244 /* Ditto for specs that end in an MFD -- let the VMS code
6245 * figure out whether it's a real device or a rooted logical. */
6247 /* This should not happen any more. Allowing the fake /000000
6248 * in a UNIX pathname causes all sorts of problems when trying
6249 * to run in UNIX emulation. So the VMS to UNIX conversions
6250 * now remove the fake /000000 directories.
6253 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6254 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6255 PerlMem_free(trndir);
6256 PerlMem_free(vmsdir);
6259 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6260 PerlMem_free(trndir);
6261 PerlMem_free(vmsdir);
6264 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6265 PerlMem_free(trndir);
6266 PerlMem_free(vmsdir);
6271 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6272 !(lastdir = cp1 = strrchr(trndir,']')) &&
6273 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6275 cp2 = strrchr(cp1,'.');
6277 int e_len, vs_len = 0;
6280 cp3 = strchr(cp2,';');
6281 e_len = strlen(cp2);
6283 vs_len = strlen(cp3);
6284 e_len = e_len - vs_len;
6286 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6288 if (!decc_efs_charset) {
6289 /* If this is not EFS, then not a directory */
6290 PerlMem_free(trndir);
6291 PerlMem_free(vmsdir);
6293 set_vaxc_errno(RMS$_DIR);
6297 /* Ok, here we have an issue, technically if a .dir shows */
6298 /* from inside a directory, then we should treat it as */
6299 /* xxx^.dir.dir. But we do not have that context at this */
6300 /* point unless this is totally restructured, so we remove */
6301 /* The .dir for now, and fix this better later */
6302 dirlen = cp2 - trndir;
6308 retlen = dirlen + 6;
6309 memcpy(buf, trndir, dirlen);
6312 /* We've picked up everything up to the directory file name.
6313 Now just add the type and version, and we're set. */
6315 /* We should only add type for VMS syntax, but historically Perl
6316 has added it for UNIX style also */
6318 /* Fix me - we should not be using the same routine for VMS and
6319 UNIX format files. Things are too tangled so we need to lookup
6320 what syntax the output is */
6324 lastdir = strrchr(trndir,'/');
6328 lastdir = strpbrk(trndir,"]:>");
6334 if ((is_vms == 0) && (is_unix == 0)) {
6335 /* We still do not know? */
6336 is_unix = decc_filename_unix_report;
6341 if ((is_unix && !decc_efs_charset) || is_vms) {
6343 /* It is a bug to add a .dir to a UNIX format directory spec */
6344 /* However Perl on VMS may have programs that expect this so */
6345 /* If not using EFS character specifications allow it. */
6347 if ((!decc_efs_case_preserve) && vms_process_case_tolerant) {
6348 /* Traditionally Perl expects filenames in lower case */
6349 strcat(buf, ".dir");
6351 /* VMS expects the .DIR to be in upper case */
6352 strcat(buf, ".DIR");
6355 /* It is also a bug to put a VMS format version on a UNIX file */
6356 /* specification. Perl self tests are looking for this */
6357 if (is_vms || !(decc_efs_charset || decc_filename_unix_report))
6360 PerlMem_free(trndir);
6361 PerlMem_free(vmsdir);
6364 else { /* VMS-style directory spec */
6366 char *esa, *esal, term, *cp;
6369 unsigned long int sts, cmplen, haslower = 0;
6370 unsigned int nam_fnb;
6372 struct FAB dirfab = cc$rms_fab;
6373 rms_setup_nam(savnam);
6374 rms_setup_nam(dirnam);
6376 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
6377 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6379 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6380 esal = PerlMem_malloc(VMS_MAXRSS);
6381 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6383 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6384 rms_bind_fab_nam(dirfab, dirnam);
6385 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6386 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6387 #ifdef NAM$M_NO_SHORT_UPCASE
6388 if (decc_efs_case_preserve)
6389 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6392 for (cp = trndir; *cp; cp++)
6393 if (islower(*cp)) { haslower = 1; break; }
6394 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6395 if ((dirfab.fab$l_sts == RMS$_DIR) ||
6396 (dirfab.fab$l_sts == RMS$_DNF) ||
6397 (dirfab.fab$l_sts == RMS$_PRV)) {
6398 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6399 sts = sys$parse(&dirfab);
6405 PerlMem_free(trndir);
6406 PerlMem_free(vmsdir);
6408 set_vaxc_errno(dirfab.fab$l_sts);
6414 /* Does the file really exist? */
6415 if (sys$search(&dirfab)& STS$K_SUCCESS) {
6416 /* Yes; fake the fnb bits so we'll check type below */
6417 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6419 else { /* No; just work with potential name */
6420 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6423 fab_sts = dirfab.fab$l_sts;
6424 sts = rms_free_search_context(&dirfab);
6428 PerlMem_free(trndir);
6429 PerlMem_free(vmsdir);
6430 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
6436 /* Make sure we are using the right buffer */
6439 my_esa_len = rms_nam_esll(dirnam);
6442 my_esa_len = rms_nam_esl(dirnam);
6444 my_esa[my_esa_len] = '\0';
6445 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6446 cp1 = strchr(my_esa,']');
6447 if (!cp1) cp1 = strchr(my_esa,'>');
6448 if (cp1) { /* Should always be true */
6449 my_esa_len -= cp1 - my_esa - 1;
6450 memmove(my_esa, cp1 + 1, my_esa_len);
6453 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6454 /* Yep; check version while we're at it, if it's there. */
6455 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6456 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6457 /* Something other than .DIR[;1]. Bzzt. */
6458 sts = rms_free_search_context(&dirfab);
6462 PerlMem_free(trndir);
6463 PerlMem_free(vmsdir);
6465 set_vaxc_errno(RMS$_DIR);
6470 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6471 /* They provided at least the name; we added the type, if necessary, */
6472 strcpy(buf, my_esa);
6473 sts = rms_free_search_context(&dirfab);
6474 PerlMem_free(trndir);
6478 PerlMem_free(vmsdir);
6481 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6482 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6486 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6487 if (cp1 == NULL) { /* should never happen */
6488 sts = rms_free_search_context(&dirfab);
6489 PerlMem_free(trndir);
6493 PerlMem_free(vmsdir);
6498 retlen = strlen(my_esa);
6499 cp1 = strrchr(my_esa,'.');
6500 /* ODS-5 directory specifications can have extra "." in them. */
6501 /* Fix-me, can not scan EFS file specifications backwards */
6502 while (cp1 != NULL) {
6503 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6507 while ((cp1 > my_esa) && (*cp1 != '.'))
6514 if ((cp1) != NULL) {
6515 /* There's more than one directory in the path. Just roll back. */
6517 strcpy(buf, my_esa);
6520 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6521 /* Go back and expand rooted logical name */
6522 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6523 #ifdef NAM$M_NO_SHORT_UPCASE
6524 if (decc_efs_case_preserve)
6525 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6527 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6528 sts = rms_free_search_context(&dirfab);
6532 PerlMem_free(trndir);
6533 PerlMem_free(vmsdir);
6535 set_vaxc_errno(dirfab.fab$l_sts);
6539 /* This changes the length of the string of course */
6541 my_esa_len = rms_nam_esll(dirnam);
6543 my_esa_len = rms_nam_esl(dirnam);
6546 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6547 cp1 = strstr(my_esa,"][");
6548 if (!cp1) cp1 = strstr(my_esa,"]<");
6549 dirlen = cp1 - my_esa;
6550 memcpy(buf, my_esa, dirlen);
6551 if (!strncmp(cp1+2,"000000]",7)) {
6552 buf[dirlen-1] = '\0';
6553 /* fix-me Not full ODS-5, just extra dots in directories for now */
6554 cp1 = buf + dirlen - 1;
6560 if (*(cp1-1) != '^')
6565 if (*cp1 == '.') *cp1 = ']';
6567 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6568 memmove(cp1+1,"000000]",7);
6572 memmove(buf+dirlen, cp1+2, retlen-dirlen);
6574 /* Convert last '.' to ']' */
6576 while (*cp != '[') {
6579 /* Do not trip on extra dots in ODS-5 directories */
6580 if ((cp1 == buf) || (*(cp1-1) != '^'))
6584 if (*cp1 == '.') *cp1 = ']';
6586 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6587 memmove(cp1+1,"000000]",7);
6591 else { /* This is a top-level dir. Add the MFD to the path. */
6594 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
6595 strcpy(cp2,":[000000]");
6600 sts = rms_free_search_context(&dirfab);
6601 /* We've set up the string up through the filename. Add the
6602 type and version, and we're done. */
6603 strcat(buf,".DIR;1");
6605 /* $PARSE may have upcased filespec, so convert output to lower
6606 * case if input contained any lowercase characters. */
6607 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
6608 PerlMem_free(trndir);
6612 PerlMem_free(vmsdir);
6615 } /* end of int_fileify_dirspec() */
6618 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6619 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6621 static char __fileify_retbuf[VMS_MAXRSS];
6622 char * fileified, *ret_spec, *ret_buf;
6626 if (ret_buf == NULL) {
6628 Newx(fileified, VMS_MAXRSS, char);
6629 if (fileified == NULL)
6630 _ckvmssts(SS$_INSFMEM);
6631 ret_buf = fileified;
6633 ret_buf = __fileify_retbuf;
6637 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6639 if (ret_spec == NULL) {
6640 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6642 Safefree(fileified);
6646 } /* end of do_fileify_dirspec() */
6649 /* External entry points */
6650 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6651 { return do_fileify_dirspec(dir,buf,0,NULL); }
6652 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6653 { return do_fileify_dirspec(dir,buf,1,NULL); }
6654 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6655 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6656 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6657 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6659 static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6660 char * v_spec, int v_len, char * r_spec, int r_len,
6661 char * d_spec, int d_len, char * n_spec, int n_len,
6662 char * e_spec, int e_len, char * vs_spec, int vs_len) {
6664 /* VMS specification - Try to do this the simple way */
6665 if ((v_len + r_len > 0) || (d_len > 0)) {
6668 /* No name or extension component, already a directory */
6669 if ((n_len + e_len + vs_len) == 0) {
6674 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6675 /* This results from catfile() being used instead of catdir() */
6676 /* So even though it should not work, we need to allow it */
6678 /* If this is .DIR;1 then do a simple conversion */
6679 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6680 if (is_dir || (e_len == 0) && (d_len > 0)) {
6682 len = v_len + r_len + d_len - 1;
6683 char dclose = d_spec[d_len - 1];
6684 strncpy(buf, dir, len);
6687 strncpy(&buf[len], n_spec, n_len);
6690 buf[len + 1] = '\0';
6695 else if (d_len > 0) {
6696 /* In the olden days, a directory needed to have a .DIR */
6697 /* extension to be a valid directory, but now it could */
6698 /* be a symbolic link */
6700 len = v_len + r_len + d_len - 1;
6701 char dclose = d_spec[d_len - 1];
6702 strncpy(buf, dir, len);
6705 strncpy(&buf[len], n_spec, n_len);
6708 if (decc_efs_charset) {
6711 strncpy(&buf[len], e_spec, e_len);
6714 set_vaxc_errno(RMS$_DIR);
6720 buf[len + 1] = '\0';
6725 set_vaxc_errno(RMS$_DIR);
6731 set_vaxc_errno(RMS$_DIR);
6737 /* Internal routine to make sure or convert a directory to be in a */
6738 /* path specification. No utf8 flag because it is not changed or used */
6739 static char *int_pathify_dirspec(const char *dir, char *buf)
6741 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6742 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6743 char * exp_spec, *ret_spec;
6745 unsigned short int trnlnm_iter_count;
6749 if (vms_debug_fileify) {
6751 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6753 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6756 /* We may need to lower case the result if we translated */
6757 /* a logical name or got the current working directory */
6760 if (!dir || !*dir) {
6762 set_vaxc_errno(SS$_BADPARAM);
6766 trndir = PerlMem_malloc(VMS_MAXRSS);
6768 _ckvmssts_noperl(SS$_INSFMEM);
6770 /* If no directory specified use the current default */
6772 strcpy(trndir, dir);
6774 getcwd(trndir, VMS_MAXRSS - 1);
6778 /* now deal with bare names that could be logical names */
6779 trnlnm_iter_count = 0;
6780 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6781 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6782 trnlnm_iter_count++;
6784 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6786 trnlen = strlen(trndir);
6788 /* Trap simple rooted lnms, and return lnm:[000000] */
6789 if (!strcmp(trndir+trnlen-2,".]")) {
6791 strcat(buf, ":[000000]");
6792 PerlMem_free(trndir);
6794 if (vms_debug_fileify) {
6795 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6801 /* At this point we do not work with *dir, but the copy in *trndir */
6803 if (need_to_lower && !decc_efs_case_preserve) {
6804 /* Legacy mode, lower case the returned value */
6805 __mystrtolower(trndir);
6809 /* Some special cases, '..', '.' */
6811 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6812 /* Force UNIX filespec */
6816 /* Is this Unix or VMS format? */
6817 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6818 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6819 &e_len, &vs_spec, &vs_len);
6822 /* Just a filename? */
6823 if ((v_len + r_len + d_len) == 0) {
6825 /* Now we have a problem, this could be Unix or VMS */
6826 /* We have to guess. .DIR usually means VMS */
6828 /* In UNIX report mode, the .DIR extension is removed */
6829 /* if one shows up, it is for a non-directory or a directory */
6830 /* in EFS charset mode */
6832 /* So if we are in Unix report mode, assume that this */
6833 /* is a relative Unix directory specification */
6836 if (!decc_filename_unix_report && decc_efs_charset) {
6838 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6841 /* Traditional mode, assume .DIR is directory */
6844 strncpy(&buf[2], n_spec, n_len);
6845 buf[n_len + 2] = ']';
6846 buf[n_len + 3] = '\0';
6847 PerlMem_free(trndir);
6848 if (vms_debug_fileify) {
6850 "int_pathify_dirspec: buf = %s\n",
6860 ret_spec = int_pathify_dirspec_simple(trndir, buf,
6861 v_spec, v_len, r_spec, r_len,
6862 d_spec, d_len, n_spec, n_len,
6863 e_spec, e_len, vs_spec, vs_len);
6865 if (ret_spec != NULL) {
6866 PerlMem_free(trndir);
6867 if (vms_debug_fileify) {
6869 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6874 /* Simple way did not work, which means that a logical name */
6875 /* was present for the directory specification. */
6876 /* Need to use an rmsexpand variant to decode it completely */
6877 exp_spec = PerlMem_malloc(VMS_MAXRSS);
6878 if (exp_spec == NULL)
6879 _ckvmssts_noperl(SS$_INSFMEM);
6881 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6882 if (ret_spec != NULL) {
6883 sts = vms_split_path(exp_spec, &v_spec, &v_len,
6884 &r_spec, &r_len, &d_spec, &d_len,
6885 &n_spec, &n_len, &e_spec,
6886 &e_len, &vs_spec, &vs_len);
6888 ret_spec = int_pathify_dirspec_simple(
6889 exp_spec, buf, v_spec, v_len, r_spec, r_len,
6890 d_spec, d_len, n_spec, n_len,
6891 e_spec, e_len, vs_spec, vs_len);
6893 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6894 /* Legacy mode, lower case the returned value */
6895 __mystrtolower(ret_spec);
6898 set_vaxc_errno(RMS$_DIR);
6903 PerlMem_free(exp_spec);
6904 PerlMem_free(trndir);
6905 if (vms_debug_fileify) {
6906 if (ret_spec == NULL)
6907 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6910 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6915 /* Unix specification, Could be trivial conversion */
6917 dir_len = strlen(trndir);
6919 /* If the extended file character set is in effect */
6920 /* then pathify is simple */
6922 if (!decc_efs_charset) {
6923 /* Have to deal with traiing '.dir' or extra '.' */
6924 /* that should not be there in legacy mode, but is */
6930 lastslash = strrchr(trndir, '/');
6931 if (lastslash == NULL)
6938 /* '..' or '.' are valid directory components */
6940 if (lastslash[0] == '.') {
6941 if (lastslash[1] == '\0') {
6943 } else if (lastslash[1] == '.') {
6944 if (lastslash[2] == '\0') {
6947 /* And finally allow '...' */
6948 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6956 lastdot = strrchr(lastslash, '.');
6958 if (lastdot != NULL) {
6961 /* '.dir' is discarded, and any other '.' is invalid */
6962 e_len = strlen(lastdot);
6964 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6967 dir_len = dir_len - 4;
6973 strcpy(buf, trndir);
6974 if (buf[dir_len - 1] != '/') {
6976 buf[dir_len + 1] = '\0';
6979 /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6980 if (!decc_efs_charset) {
6983 if (str[0] == '.') {
6986 while ((dots[cnt] == '.') && (cnt < 3))
6989 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6995 for (; *str; ++str) {
6996 while (*str == '/') {
7002 /* Have to skip up to three dots which could be */
7003 /* directories, 3 dots being a VMS extension for Perl */
7006 while ((dots[cnt] == '.') && (cnt < 3)) {
7009 if (dots[cnt] == '\0')
7011 if ((cnt > 1) && (dots[cnt] != '/')) {
7017 /* too many dots? */
7018 if ((cnt == 0) || (cnt > 3)) {
7022 if (!dir_start && (*str == '.')) {
7027 PerlMem_free(trndir);
7029 if (vms_debug_fileify) {
7030 if (ret_spec == NULL)
7031 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
7034 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
7040 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
7041 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
7043 static char __pathify_retbuf[VMS_MAXRSS];
7044 char * pathified, *ret_spec, *ret_buf;
7048 if (ret_buf == NULL) {
7050 Newx(pathified, VMS_MAXRSS, char);
7051 if (pathified == NULL)
7052 _ckvmssts(SS$_INSFMEM);
7053 ret_buf = pathified;
7055 ret_buf = __pathify_retbuf;
7059 ret_spec = int_pathify_dirspec(dir, ret_buf);
7061 if (ret_spec == NULL) {
7062 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7064 Safefree(pathified);
7069 } /* end of do_pathify_dirspec() */
7072 /* External entry points */
7073 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
7074 { return do_pathify_dirspec(dir,buf,0,NULL); }
7075 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
7076 { return do_pathify_dirspec(dir,buf,1,NULL); }
7077 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
7078 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
7079 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
7080 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
7082 /* Internal tounixspec routine that does not use a thread context */
7083 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
7084 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
7086 char *dirend, *cp1, *cp3, *tmp;
7088 int devlen, dirlen, retlen = VMS_MAXRSS;
7089 int expand = 1; /* guarantee room for leading and trailing slashes */
7090 unsigned short int trnlnm_iter_count;
7092 if (utf8_fl != NULL)
7095 if (vms_debug_fileify) {
7097 fprintf(stderr, "int_tounixspec: spec = NULL\n");
7099 fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
7105 set_vaxc_errno(SS$_BADPARAM);
7108 if (strlen(spec) > (VMS_MAXRSS-1)) {
7110 set_vaxc_errno(SS$_BUFFEROVF);
7114 /* New VMS specific format needs translation
7115 * glob passes filenames with trailing '\n' and expects this preserved.
7117 if (decc_posix_compliant_pathnames) {
7118 if (strncmp(spec, "\"^UP^", 5) == 0) {
7124 tunix = PerlMem_malloc(VMS_MAXRSS);
7125 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7126 strcpy(tunix, spec);
7127 tunix_len = strlen(tunix);
7129 if (tunix[tunix_len - 1] == '\n') {
7130 tunix[tunix_len - 1] = '\"';
7131 tunix[tunix_len] = '\0';
7135 uspec = decc$translate_vms(tunix);
7136 PerlMem_free(tunix);
7137 if ((int)uspec > 0) {
7143 /* If we can not translate it, makemaker wants as-is */
7151 cmp_rslt = 0; /* Presume VMS */
7152 cp1 = strchr(spec, '/');
7156 /* Look for EFS ^/ */
7157 if (decc_efs_charset) {
7158 while (cp1 != NULL) {
7161 /* Found illegal VMS, assume UNIX */
7166 cp1 = strchr(cp1, '/');
7170 /* Look for "." and ".." */
7171 if (decc_filename_unix_report) {
7172 if (spec[0] == '.') {
7173 if ((spec[1] == '\0') || (spec[1] == '\n')) {
7177 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
7183 /* This is already UNIX or at least nothing VMS understands */
7186 if (vms_debug_fileify) {
7187 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7194 dirend = strrchr(spec,']');
7195 if (dirend == NULL) dirend = strrchr(spec,'>');
7196 if (dirend == NULL) dirend = strchr(spec,':');
7197 if (dirend == NULL) {
7199 if (vms_debug_fileify) {
7200 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7205 /* Special case 1 - sys$posix_root = / */
7206 #if __CRTL_VER >= 70000000
7207 if (!decc_disable_posix_root) {
7208 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7216 /* Special case 2 - Convert NLA0: to /dev/null */
7217 #if __CRTL_VER < 70000000
7218 cmp_rslt = strncmp(spec,"NLA0:", 5);
7220 cmp_rslt = strncmp(spec,"nla0:", 5);
7222 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7224 if (cmp_rslt == 0) {
7225 strcpy(rslt, "/dev/null");
7228 if (spec[6] != '\0') {
7235 /* Also handle special case "SYS$SCRATCH:" */
7236 #if __CRTL_VER < 70000000
7237 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
7239 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
7241 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7243 tmp = PerlMem_malloc(VMS_MAXRSS);
7244 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7245 if (cmp_rslt == 0) {
7248 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7250 strcpy(rslt, "/tmp");
7253 if (spec[12] != '\0') {
7261 if (*cp2 != '[' && *cp2 != '<') {
7264 else { /* the VMS spec begins with directories */
7266 if (*cp2 == ']' || *cp2 == '>') {
7267 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
7271 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7272 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7274 if (vms_debug_fileify) {
7275 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7279 trnlnm_iter_count = 0;
7282 while (*cp3 != ':' && *cp3) cp3++;
7284 if (strchr(cp3,']') != NULL) break;
7285 trnlnm_iter_count++;
7286 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7287 } while (vmstrnenv(tmp,tmp,0,fildev,0));
7292 *(cp1++) = *(cp3++);
7293 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7295 set_errno(ENAMETOOLONG);
7296 set_vaxc_errno(SS$_BUFFEROVF);
7297 if (vms_debug_fileify) {
7298 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7300 return NULL; /* No room */
7305 if ((*cp2 == '^')) {
7306 /* EFS file escape, pass the next character as is */
7307 /* Fix me: HEX encoding for Unicode not implemented */
7310 else if ( *cp2 == '.') {
7311 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7312 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7319 for (; cp2 <= dirend; cp2++) {
7320 if ((*cp2 == '^')) {
7321 /* EFS file escape, pass the next character as is */
7322 /* Fix me: HEX encoding for Unicode not implemented */
7323 *(cp1++) = *(++cp2);
7324 /* An escaped dot stays as is -- don't convert to slash */
7325 if (*cp2 == '.') cp2++;
7329 if (*(cp2+1) == '[') cp2++;
7331 else if (*cp2 == ']' || *cp2 == '>') {
7332 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7334 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7336 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7337 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7338 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7339 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7340 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7342 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7343 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7347 else if (*cp2 == '-') {
7348 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7349 while (*cp2 == '-') {
7351 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7353 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7354 /* filespecs like */
7355 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
7356 if (vms_debug_fileify) {
7357 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7362 else *(cp1++) = *cp2;
7364 else *(cp1++) = *cp2;
7366 /* Translate the rest of the filename. */
7371 /* Fixme - for compatibility with the CRTL we should be removing */
7372 /* spaces from the file specifications, but this may show that */
7373 /* some tests that were appearing to pass are not really passing */
7379 /* Fix me hex expansions not implemented */
7380 cp2++; /* '^.' --> '.' and other. */
7386 *(cp1++) = *(cp2++);
7391 if (decc_filename_unix_no_version) {
7392 /* Easy, drop the version */
7397 /* Punt - passing the version as a dot will probably */
7398 /* break perl in weird ways, but so did passing */
7399 /* through the ; as a version. Follow the CRTL and */
7400 /* hope for the best. */
7407 /* We will need to fix this properly later */
7408 /* As Perl may be installed on an ODS-5 volume, but not */
7409 /* have the EFS_CHARSET enabled, it still may encounter */
7410 /* filenames with extra dots in them, and a precedent got */
7411 /* set which allowed them to work, that we will uphold here */
7412 /* If extra dots are present in a name and no ^ is on them */
7413 /* VMS assumes that the first one is the extension delimiter */
7414 /* the rest have an implied ^. */
7416 /* this is also a conflict as the . is also a version */
7417 /* delimiter in VMS, */
7419 *(cp1++) = *(cp2++);
7423 /* This is an extension */
7424 if (decc_readdir_dropdotnotype) {
7426 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7427 /* Drop the dot for the extension */
7435 *(cp1++) = *(cp2++);
7440 /* This still leaves /000000/ when working with a
7441 * VMS device root or concealed root.
7447 ulen = strlen(rslt);
7449 /* Get rid of "000000/ in rooted filespecs */
7451 zeros = strstr(rslt, "/000000/");
7452 if (zeros != NULL) {
7454 mlen = ulen - (zeros - rslt) - 7;
7455 memmove(zeros, &zeros[7], mlen);
7462 if (vms_debug_fileify) {
7463 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7467 } /* end of int_tounixspec() */
7470 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7471 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7473 static char __tounixspec_retbuf[VMS_MAXRSS];
7474 char * unixspec, *ret_spec, *ret_buf;
7478 if (ret_buf == NULL) {
7480 Newx(unixspec, VMS_MAXRSS, char);
7481 if (unixspec == NULL)
7482 _ckvmssts(SS$_INSFMEM);
7485 ret_buf = __tounixspec_retbuf;
7489 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7491 if (ret_spec == NULL) {
7492 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7499 } /* end of do_tounixspec() */
7501 /* External entry points */
7502 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7503 { return do_tounixspec(spec,buf,0, NULL); }
7504 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7505 { return do_tounixspec(spec,buf,1, NULL); }
7506 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7507 { return do_tounixspec(spec,buf,0, utf8_fl); }
7508 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7509 { return do_tounixspec(spec,buf,1, utf8_fl); }
7511 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7514 This procedure is used to identify if a path is based in either
7515 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7516 it returns the OpenVMS format directory for it.
7518 It is expecting specifications of only '/' or '/xxxx/'
7520 If a posix root does not exist, or 'xxxx' is not a directory
7521 in the posix root, it returns a failure.
7523 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7525 It is used only internally by posix_to_vmsspec_hardway().
7528 static int posix_root_to_vms
7529 (char *vmspath, int vmspath_len,
7530 const char *unixpath,
7531 const int * utf8_fl)
7534 struct FAB myfab = cc$rms_fab;
7535 rms_setup_nam(mynam);
7536 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7537 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7538 char * esa, * esal, * rsa, * rsal;
7545 unixlen = strlen(unixpath);
7550 #if __CRTL_VER >= 80200000
7551 /* If not a posix spec already, convert it */
7552 if (decc_posix_compliant_pathnames) {
7553 if (strncmp(unixpath,"\"^UP^",5) != 0) {
7554 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7557 /* This is already a VMS specification, no conversion */
7559 strncpy(vmspath,unixpath, vmspath_len);
7568 /* Check to see if this is under the POSIX root */
7569 if (decc_disable_posix_root) {
7573 /* Skip leading / */
7574 if (unixpath[0] == '/') {
7580 strcpy(vmspath,"SYS$POSIX_ROOT:");
7582 /* If this is only the / , or blank, then... */
7583 if (unixpath[0] == '\0') {
7584 /* by definition, this is the answer */
7588 /* Need to look up a directory */
7592 /* Copy and add '^' escape characters as needed */
7595 while (unixpath[i] != 0) {
7598 j += copy_expand_unix_filename_escape
7599 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7603 path_len = strlen(vmspath);
7604 if (vmspath[path_len - 1] == '/')
7606 vmspath[path_len] = ']';
7608 vmspath[path_len] = '\0';
7611 vmspath[vmspath_len] = 0;
7612 if (unixpath[unixlen - 1] == '/')
7614 esal = PerlMem_malloc(VMS_MAXRSS);
7615 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7616 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7617 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7618 rsal = PerlMem_malloc(VMS_MAXRSS);
7619 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7620 rsa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7621 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7622 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7623 rms_bind_fab_nam(myfab, mynam);
7624 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7625 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7626 if (decc_efs_case_preserve)
7627 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7628 #ifdef NAML$M_OPEN_SPECIAL
7629 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7632 /* Set up the remaining naml fields */
7633 sts = sys$parse(&myfab);
7635 /* It failed! Try again as a UNIX filespec */
7644 /* get the Device ID and the FID */
7645 sts = sys$search(&myfab);
7647 /* These are no longer needed */
7652 /* on any failure, returned the POSIX ^UP^ filespec */
7657 specdsc.dsc$a_pointer = vmspath;
7658 specdsc.dsc$w_length = vmspath_len;
7660 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7661 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7662 sts = lib$fid_to_name
7663 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7665 /* on any failure, returned the POSIX ^UP^ filespec */
7667 /* This can happen if user does not have permission to read directories */
7668 if (strncmp(unixpath,"\"^UP^",5) != 0)
7669 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7671 strcpy(vmspath, unixpath);
7674 vmspath[specdsc.dsc$w_length] = 0;
7676 /* Are we expecting a directory? */
7677 if (dir_flag != 0) {
7683 i = specdsc.dsc$w_length - 1;
7687 /* Version must be '1' */
7688 if (vmspath[i--] != '1')
7690 /* Version delimiter is one of ".;" */
7691 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7694 if (vmspath[i--] != 'R')
7696 if (vmspath[i--] != 'I')
7698 if (vmspath[i--] != 'D')
7700 if (vmspath[i--] != '.')
7702 eptr = &vmspath[i+1];
7704 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7705 if (vmspath[i-1] != '^') {
7713 /* Get rid of 6 imaginary zero directory filename */
7714 vmspath[i+1] = '\0';
7718 if (vmspath[i] == '0')
7732 /* /dev/mumble needs to be handled special.
7733 /dev/null becomes NLA0:, And there is the potential for other stuff
7734 like /dev/tty which may need to be mapped to something.
7738 slash_dev_special_to_vms
7739 (const char * unixptr,
7749 nextslash = strchr(unixptr, '/');
7750 len = strlen(unixptr);
7751 if (nextslash != NULL)
7752 len = nextslash - unixptr;
7753 cmp = strncmp("null", unixptr, 5);
7755 if (vmspath_len >= 6) {
7756 strcpy(vmspath, "_NLA0:");
7763 /* The built in routines do not understand perl's special needs, so
7764 doing a manual conversion from UNIX to VMS
7766 If the utf8_fl is not null and points to a non-zero value, then
7767 treat 8 bit characters as UTF-8.
7769 The sequence starting with '$(' and ending with ')' will be passed
7770 through with out interpretation instead of being escaped.
7773 static int posix_to_vmsspec_hardway
7774 (char *vmspath, int vmspath_len,
7775 const char *unixpath,
7780 const char *unixptr;
7781 const char *unixend;
7783 const char *lastslash;
7784 const char *lastdot;
7790 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7791 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7793 if (utf8_fl != NULL)
7799 /* Ignore leading "/" characters */
7800 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7803 unixlen = strlen(unixptr);
7805 /* Do nothing with blank paths */
7812 /* This could have a "^UP^ on the front */
7813 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7819 lastslash = strrchr(unixptr,'/');
7820 lastdot = strrchr(unixptr,'.');
7821 unixend = strrchr(unixptr,'\"');
7822 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7823 unixend = unixptr + unixlen;
7826 /* last dot is last dot or past end of string */
7827 if (lastdot == NULL)
7828 lastdot = unixptr + unixlen;
7830 /* if no directories, set last slash to beginning of string */
7831 if (lastslash == NULL) {
7832 lastslash = unixptr;
7835 /* Watch out for trailing "." after last slash, still a directory */
7836 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7837 lastslash = unixptr + unixlen;
7840 /* Watch out for traiing ".." after last slash, still a directory */
7841 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7842 lastslash = unixptr + unixlen;
7845 /* dots in directories are aways escaped */
7846 if (lastdot < lastslash)
7847 lastdot = unixptr + unixlen;
7850 /* if (unixptr < lastslash) then we are in a directory */
7857 /* Start with the UNIX path */
7858 if (*unixptr != '/') {
7859 /* relative paths */
7861 /* If allowing logical names on relative pathnames, then handle here */
7862 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7863 !decc_posix_compliant_pathnames) {
7869 /* Find the next slash */
7870 nextslash = strchr(unixptr,'/');
7872 esa = PerlMem_malloc(vmspath_len);
7873 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7875 trn = PerlMem_malloc(VMS_MAXRSS);
7876 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7878 if (nextslash != NULL) {
7880 seg_len = nextslash - unixptr;
7881 strncpy(esa, unixptr, seg_len);
7885 strcpy(esa, unixptr);
7886 seg_len = strlen(unixptr);
7888 /* trnlnm(section) */
7889 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7892 /* Now fix up the directory */
7894 /* Split up the path to find the components */
7895 sts = vms_split_path
7914 /* A logical name must be a directory or the full
7915 specification. It is only a full specification if
7916 it is the only component */
7917 if ((unixptr[seg_len] == '\0') ||
7918 (unixptr[seg_len+1] == '\0')) {
7920 /* Is a directory being required? */
7921 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7922 /* Not a logical name */
7927 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7928 /* This must be a directory */
7929 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7930 strcpy(vmsptr, esa);
7931 vmslen=strlen(vmsptr);
7932 vmsptr[vmslen] = ':';
7934 vmsptr[vmslen] = '\0';
7942 /* must be dev/directory - ignore version */
7943 if ((n_len + e_len) != 0)
7946 /* transfer the volume */
7947 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7948 strncpy(vmsptr, v_spec, v_len);
7954 /* unroot the rooted directory */
7955 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7957 r_spec[r_len - 1] = ']';
7959 /* This should not be there, but nothing is perfect */
7961 cmp = strcmp(&r_spec[1], "000000.");
7971 strncpy(vmsptr, r_spec, r_len);
7977 /* Bring over the directory. */
7979 ((d_len + vmslen) < vmspath_len)) {
7981 d_spec[d_len - 1] = ']';
7983 cmp = strcmp(&d_spec[1], "000000.");
7994 /* Remove the redundant root */
8002 strncpy(vmsptr, d_spec, d_len);
8016 if (lastslash > unixptr) {
8019 /* skip leading ./ */
8021 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
8027 /* Are we still in a directory? */
8028 if (unixptr <= lastslash) {
8033 /* if not backing up, then it is relative forward. */
8034 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
8035 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
8043 /* Perl wants an empty directory here to tell the difference
8044 * between a DCL commmand and a filename
8053 /* Handle two special files . and .. */
8054 if (unixptr[0] == '.') {
8055 if (&unixptr[1] == unixend) {
8062 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
8073 else { /* Absolute PATH handling */
8077 /* Need to find out where root is */
8079 /* In theory, this procedure should never get an absolute POSIX pathname
8080 * that can not be found on the POSIX root.
8081 * In practice, that can not be relied on, and things will show up
8082 * here that are a VMS device name or concealed logical name instead.
8083 * So to make things work, this procedure must be tolerant.
8085 esa = PerlMem_malloc(vmspath_len);
8086 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8089 nextslash = strchr(&unixptr[1],'/');
8091 if (nextslash != NULL) {
8093 seg_len = nextslash - &unixptr[1];
8094 strncpy(vmspath, unixptr, seg_len + 1);
8095 vmspath[seg_len+1] = 0;
8098 cmp = strncmp(vmspath, "dev", 4);
8100 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
8101 if (sts = SS$_NORMAL)
8105 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
8108 if ($VMS_STATUS_SUCCESS(sts)) {
8109 /* This is verified to be a real path */
8111 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
8112 if ($VMS_STATUS_SUCCESS(sts)) {
8113 strcpy(vmspath, esa);
8114 vmslen = strlen(vmspath);
8115 vmsptr = vmspath + vmslen;
8117 if (unixptr < lastslash) {
8126 cmp = strcmp(rptr,"000000.");
8131 } /* removing 6 zeros */
8132 } /* vmslen < 7, no 6 zeros possible */
8133 } /* Not in a directory */
8134 } /* Posix root found */
8136 /* No posix root, fall back to default directory */
8137 strcpy(vmspath, "SYS$DISK:[");
8138 vmsptr = &vmspath[10];
8140 if (unixptr > lastslash) {
8149 } /* end of verified real path handling */
8154 /* Ok, we have a device or a concealed root that is not in POSIX
8155 * or we have garbage. Make the best of it.
8158 /* Posix to VMS destroyed this, so copy it again */
8159 strncpy(vmspath, &unixptr[1], seg_len);
8160 vmspath[seg_len] = 0;
8162 vmsptr = &vmsptr[vmslen];
8165 /* Now do we need to add the fake 6 zero directory to it? */
8167 if ((*lastslash == '/') && (nextslash < lastslash)) {
8168 /* No there is another directory */
8175 /* now we have foo:bar or foo:[000000]bar to decide from */
8176 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
8178 if (!islnm && !decc_posix_compliant_pathnames) {
8180 cmp = strncmp("bin", vmspath, 4);
8182 /* bin => SYS$SYSTEM: */
8183 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
8186 /* tmp => SYS$SCRATCH: */
8187 cmp = strncmp("tmp", vmspath, 4);
8189 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
8194 trnend = islnm ? islnm - 1 : 0;
8196 /* if this was a logical name, ']' or '>' must be present */
8197 /* if not a logical name, then assume a device and hope. */
8198 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
8200 /* if log name and trailing '.' then rooted - treat as device */
8201 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
8203 /* Fix me, if not a logical name, a device lookup should be
8204 * done to see if the device is file structured. If the device
8205 * is not file structured, the 6 zeros should not be put on.
8207 * As it is, perl is occasionally looking for dev:[000000]tty.
8208 * which looks a little strange.
8210 * Not that easy to detect as "/dev" may be file structured with
8211 * special device files.
8214 if ((add_6zero == 0) && (*nextslash == '/') &&
8215 (&nextslash[1] == unixend)) {
8216 /* No real directory present */
8221 /* Put the device delimiter on */
8224 unixptr = nextslash;
8227 /* Start directory if needed */
8228 if (!islnm || add_6zero) {
8234 /* add fake 000000] if needed */
8247 } /* non-POSIX translation */
8249 } /* End of relative/absolute path handling */
8251 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
8258 if (dir_start != 0) {
8260 /* First characters in a directory are handled special */
8261 while ((*unixptr == '/') ||
8262 ((*unixptr == '.') &&
8263 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8264 (&unixptr[1]==unixend)))) {
8269 /* Skip redundant / in specification */
8270 while ((*unixptr == '/') && (dir_start != 0)) {
8273 if (unixptr == lastslash)
8276 if (unixptr == lastslash)
8279 /* Skip redundant ./ characters */
8280 while ((*unixptr == '.') &&
8281 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8284 if (unixptr == lastslash)
8286 if (*unixptr == '/')
8289 if (unixptr == lastslash)
8292 /* Skip redundant ../ characters */
8293 while ((*unixptr == '.') && (unixptr[1] == '.') &&
8294 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8295 /* Set the backing up flag */
8301 unixptr++; /* first . */
8302 unixptr++; /* second . */
8303 if (unixptr == lastslash)
8305 if (*unixptr == '/') /* The slash */
8308 if (unixptr == lastslash)
8311 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8312 /* Not needed when VMS is pretending to be UNIX. */
8314 /* Is this loop stuck because of too many dots? */
8315 if (loop_flag == 0) {
8316 /* Exit the loop and pass the rest through */
8321 /* Are we done with directories yet? */
8322 if (unixptr >= lastslash) {
8324 /* Watch out for trailing dots */
8333 if (*unixptr == '/')
8337 /* Have we stopped backing up? */
8342 /* dir_start continues to be = 1 */
8344 if (*unixptr == '-') {
8346 *vmsptr++ = *unixptr++;
8350 /* Now are we done with directories yet? */
8351 if (unixptr >= lastslash) {
8353 /* Watch out for trailing dots */
8369 if (unixptr >= unixend)
8372 /* Normal characters - More EFS work probably needed */
8378 /* remove multiple / */
8379 while (unixptr[1] == '/') {
8382 if (unixptr == lastslash) {
8383 /* Watch out for trailing dots */
8395 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8396 /* Not needed when VMS is pretending to be UNIX. */
8400 if (unixptr != unixend)
8405 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8406 (&unixptr[1] == unixend)) {
8412 /* trailing dot ==> '^..' on VMS */
8413 if (unixptr == unixend) {
8421 *vmsptr++ = *unixptr++;
8425 if (quoted && (&unixptr[1] == unixend)) {
8429 in_cnt = copy_expand_unix_filename_escape
8430 (vmsptr, unixptr, &out_cnt, utf8_fl);
8440 in_cnt = copy_expand_unix_filename_escape
8441 (vmsptr, unixptr, &out_cnt, utf8_fl);
8448 /* Make sure directory is closed */
8449 if (unixptr == lastslash) {
8451 vmsptr2 = vmsptr - 1;
8453 if (*vmsptr2 != ']') {
8456 /* directories do not end in a dot bracket */
8457 if (*vmsptr2 == '.') {
8461 if (*vmsptr2 != '^') {
8462 vmsptr--; /* back up over the dot */
8470 /* Add a trailing dot if a file with no extension */
8471 vmsptr2 = vmsptr - 1;
8473 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8474 (*vmsptr2 != ')') && (*lastdot != '.')) {
8485 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8486 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8491 /* If a UTF8 flag is being passed, honor it */
8493 if (utf8_fl != NULL) {
8494 utf8_flag = *utf8_fl;
8499 /* If there is a possibility of UTF8, then if any UTF8 characters
8500 are present, then they must be converted to VTF-7
8502 result = strcpy(rslt, path); /* FIX-ME */
8505 result = strcpy(rslt, path);
8512 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8513 static char *int_tovmsspec
8514 (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8520 unsigned long int infront = 0, hasdir = 1;
8523 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8524 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8526 if (vms_debug_fileify) {
8528 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8530 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8534 /* If we fail, we should be setting errno */
8536 set_vaxc_errno(SS$_BADPARAM);
8539 rslt_len = VMS_MAXRSS-1;
8541 /* '.' and '..' are "[]" and "[-]" for a quick check */
8542 if (path[0] == '.') {
8543 if (path[1] == '\0') {
8545 if (utf8_flag != NULL)
8550 if (path[1] == '.' && path[2] == '\0') {
8552 if (utf8_flag != NULL)
8559 /* Posix specifications are now a native VMS format */
8560 /*--------------------------------------------------*/
8561 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8562 if (decc_posix_compliant_pathnames) {
8563 if (strncmp(path,"\"^UP^",5) == 0) {
8564 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8570 /* This is really the only way to see if this is already in VMS format */
8571 sts = vms_split_path
8586 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8587 replacement, because the above parse just took care of most of
8588 what is needed to do vmspath when the specification is already
8591 And if it is not already, it is easier to do the conversion as
8592 part of this routine than to call this routine and then work on
8596 /* If VMS punctuation was found, it is already VMS format */
8597 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8598 if (utf8_flag != NULL)
8601 if (vms_debug_fileify) {
8602 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8606 /* Now, what to do with trailing "." cases where there is no
8607 extension? If this is a UNIX specification, and EFS characters
8608 are enabled, then the trailing "." should be converted to a "^.".
8609 But if this was already a VMS specification, then it should be
8612 So in the case of ambiguity, leave the specification alone.
8616 /* If there is a possibility of UTF8, then if any UTF8 characters
8617 are present, then they must be converted to VTF-7
8619 if (utf8_flag != NULL)
8622 if (vms_debug_fileify) {
8623 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8628 dirend = strrchr(path,'/');
8630 if (dirend == NULL) {
8634 /* If we get here with no UNIX directory delimiters, then this is
8635 not a complete file specification, either garbage a UNIX glob
8636 specification that can not be converted to a VMS wildcard, or
8637 it a UNIX shell macro. MakeMaker wants shell macros passed
8640 utf8 flag setting needs to be preserved.
8645 macro_start = strchr(path,'$');
8646 if (macro_start != NULL) {
8647 if (macro_start[1] == '(') {
8651 if ((decc_efs_charset == 0) || (has_macro)) {
8653 if (vms_debug_fileify) {
8654 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8660 /* If POSIX mode active, handle the conversion */
8661 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8662 if (decc_efs_charset) {
8663 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8664 if (vms_debug_fileify) {
8665 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8671 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
8672 if (!*(dirend+2)) dirend +=2;
8673 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8674 if (decc_efs_charset == 0) {
8675 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8681 lastdot = strrchr(cp2,'.');
8687 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8689 if (decc_disable_posix_root) {
8690 strcpy(rslt,"sys$disk:[000000]");
8693 strcpy(rslt,"sys$posix_root:[000000]");
8695 if (utf8_flag != NULL)
8697 if (vms_debug_fileify) {
8698 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8702 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8704 trndev = PerlMem_malloc(VMS_MAXRSS);
8705 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8706 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8708 /* DECC special handling */
8710 if (strcmp(rslt,"bin") == 0) {
8711 strcpy(rslt,"sys$system");
8714 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8716 else if (strcmp(rslt,"tmp") == 0) {
8717 strcpy(rslt,"sys$scratch");
8720 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8722 else if (!decc_disable_posix_root) {
8723 strcpy(rslt, "sys$posix_root");
8727 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8728 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8730 else if (strcmp(rslt,"dev") == 0) {
8731 if (strncmp(cp2,"/null", 5) == 0) {
8732 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8733 strcpy(rslt,"NLA0");
8737 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8743 trnend = islnm ? strlen(trndev) - 1 : 0;
8744 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8745 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8746 /* If the first element of the path is a logical name, determine
8747 * whether it has to be translated so we can add more directories. */
8748 if (!islnm || rooted) {
8751 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8755 if (cp2 != dirend) {
8756 strcpy(rslt,trndev);
8757 cp1 = rslt + trnend;
8764 if (decc_disable_posix_root) {
8770 PerlMem_free(trndev);
8775 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8776 cp2 += 2; /* skip over "./" - it's redundant */
8777 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8779 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8780 *(cp1++) = '-'; /* "../" --> "-" */
8783 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8784 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8785 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8786 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8789 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8790 /* Escape the extra dots in EFS file specifications */
8793 if (cp2 > dirend) cp2 = dirend;
8795 else *(cp1++) = '.';
8797 for (; cp2 < dirend; cp2++) {
8799 if (*(cp2-1) == '/') continue;
8800 if (*(cp1-1) != '.') *(cp1++) = '.';
8803 else if (!infront && *cp2 == '.') {
8804 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8805 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
8806 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8807 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
8808 else if (*(cp1-2) == '[') *(cp1-1) = '-';
8809 else { /* back up over previous directory name */
8811 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
8812 if (*(cp1-1) == '[') {
8813 memcpy(cp1,"000000.",7);
8818 if (cp2 == dirend) break;
8820 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8821 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8822 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8823 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8825 *(cp1++) = '.'; /* Simulate trailing '/' */
8826 cp2 += 2; /* for loop will incr this to == dirend */
8828 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8831 if (decc_efs_charset == 0)
8832 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8834 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
8840 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
8842 if (decc_efs_charset == 0)
8849 else *(cp1++) = *cp2;
8853 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8854 if (hasdir) *(cp1++) = ']';
8855 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
8856 /* fixme for ODS5 */
8863 if (decc_efs_charset == 0)
8874 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8875 decc_readdir_dropdotnotype) {
8880 /* trailing dot ==> '^..' on VMS */
8887 *(cp1++) = *(cp2++);
8892 /* This could be a macro to be passed through */
8893 *(cp1++) = *(cp2++);
8895 const char * save_cp2;
8899 /* paranoid check */
8905 *(cp1++) = *(cp2++);
8906 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8907 *(cp1++) = *(cp2++);
8908 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8909 *(cp1++) = *(cp2++);
8912 *(cp1++) = *(cp2++);
8916 if (is_macro == 0) {
8917 /* Not really a macro - never mind */
8930 /* Don't escape again if following character is
8931 * already something we escape.
8933 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8934 *(cp1++) = *(cp2++);
8937 /* But otherwise fall through and escape it. */
8955 *(cp1++) = *(cp2++);
8958 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
8959 * which is wrong. UNIX notation should be ".dir." unless
8960 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8961 * changing this behavior could break more things at this time.
8962 * efs character set effectively does not allow "." to be a version
8963 * delimiter as a further complication about changing this.
8965 if (decc_filename_unix_report != 0) {
8968 *(cp1++) = *(cp2++);
8971 *(cp1++) = *(cp2++);
8974 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8978 /* Fix me for "^]", but that requires making sure that you do
8979 * not back up past the start of the filename
8981 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8986 if (utf8_flag != NULL)
8988 if (vms_debug_fileify) {
8989 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8993 } /* end of int_tovmsspec() */
8996 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8997 static char *mp_do_tovmsspec
8998 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
8999 static char __tovmsspec_retbuf[VMS_MAXRSS];
9000 char * vmsspec, *ret_spec, *ret_buf;
9004 if (ret_buf == NULL) {
9006 Newx(vmsspec, VMS_MAXRSS, char);
9007 if (vmsspec == NULL)
9008 _ckvmssts(SS$_INSFMEM);
9011 ret_buf = __tovmsspec_retbuf;
9015 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
9017 if (ret_spec == NULL) {
9018 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
9025 } /* end of mp_do_tovmsspec() */
9027 /* External entry points */
9028 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
9029 { return do_tovmsspec(path,buf,0,NULL); }
9030 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
9031 { return do_tovmsspec(path,buf,1,NULL); }
9032 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9033 { return do_tovmsspec(path,buf,0,utf8_fl); }
9034 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9035 { return do_tovmsspec(path,buf,1,utf8_fl); }
9037 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
9038 /* Internal routine for use with out an explict context present */
9039 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
9041 char * ret_spec, *pathified;
9046 pathified = PerlMem_malloc(VMS_MAXRSS);
9047 if (pathified == NULL)
9048 _ckvmssts_noperl(SS$_INSFMEM);
9050 ret_spec = int_pathify_dirspec(path, pathified);
9052 if (ret_spec == NULL) {
9053 PerlMem_free(pathified);
9057 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
9059 PerlMem_free(pathified);
9064 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
9065 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
9066 static char __tovmspath_retbuf[VMS_MAXRSS];
9068 char *pathified, *vmsified, *cp;
9070 if (path == NULL) return NULL;
9071 pathified = PerlMem_malloc(VMS_MAXRSS);
9072 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
9073 if (int_pathify_dirspec(path, pathified) == NULL) {
9074 PerlMem_free(pathified);
9080 Newx(vmsified, VMS_MAXRSS, char);
9081 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
9082 PerlMem_free(pathified);
9083 if (vmsified) Safefree(vmsified);
9086 PerlMem_free(pathified);
9091 vmslen = strlen(vmsified);
9092 Newx(cp,vmslen+1,char);
9093 memcpy(cp,vmsified,vmslen);
9099 strcpy(__tovmspath_retbuf,vmsified);
9101 return __tovmspath_retbuf;
9104 } /* end of do_tovmspath() */
9106 /* External entry points */
9107 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
9108 { return do_tovmspath(path,buf,0, NULL); }
9109 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
9110 { return do_tovmspath(path,buf,1, NULL); }
9111 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
9112 { return do_tovmspath(path,buf,0,utf8_fl); }
9113 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
9114 { return do_tovmspath(path,buf,1,utf8_fl); }
9117 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
9118 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
9119 static char __tounixpath_retbuf[VMS_MAXRSS];
9121 char *pathified, *unixified, *cp;
9123 if (path == NULL) return NULL;
9124 pathified = PerlMem_malloc(VMS_MAXRSS);
9125 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
9126 if (int_pathify_dirspec(path, pathified) == NULL) {
9127 PerlMem_free(pathified);
9133 Newx(unixified, VMS_MAXRSS, char);
9135 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
9136 PerlMem_free(pathified);
9137 if (unixified) Safefree(unixified);
9140 PerlMem_free(pathified);
9145 unixlen = strlen(unixified);
9146 Newx(cp,unixlen+1,char);
9147 memcpy(cp,unixified,unixlen);
9149 Safefree(unixified);
9153 strcpy(__tounixpath_retbuf,unixified);
9154 Safefree(unixified);
9155 return __tounixpath_retbuf;
9158 } /* end of do_tounixpath() */
9160 /* External entry points */
9161 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
9162 { return do_tounixpath(path,buf,0,NULL); }
9163 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
9164 { return do_tounixpath(path,buf,1,NULL); }
9165 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9166 { return do_tounixpath(path,buf,0,utf8_fl); }
9167 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9168 { return do_tounixpath(path,buf,1,utf8_fl); }
9171 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
9173 *****************************************************************************
9175 * Copyright (C) 1989-1994, 2007 by *
9176 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
9178 * Permission is hereby granted for the reproduction of this software *
9179 * on condition that this copyright notice is included in source *
9180 * distributions of the software. The code may be modified and *
9181 * distributed under the same terms as Perl itself. *
9183 * 27-Aug-1994 Modified for inclusion in perl5 *
9184 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
9185 *****************************************************************************
9189 * getredirection() is intended to aid in porting C programs
9190 * to VMS (Vax-11 C). The native VMS environment does not support
9191 * '>' and '<' I/O redirection, or command line wild card expansion,
9192 * or a command line pipe mechanism using the '|' AND background
9193 * command execution '&'. All of these capabilities are provided to any
9194 * C program which calls this procedure as the first thing in the
9196 * The piping mechanism will probably work with almost any 'filter' type
9197 * of program. With suitable modification, it may useful for other
9198 * portability problems as well.
9200 * Author: Mark Pizzolato (mark AT infocomm DOT com)
9204 struct list_item *next;
9208 static void add_item(struct list_item **head,
9209 struct list_item **tail,
9213 static void mp_expand_wild_cards(pTHX_ char *item,
9214 struct list_item **head,
9215 struct list_item **tail,
9218 static int background_process(pTHX_ int argc, char **argv);
9220 static void pipe_and_fork(pTHX_ char **cmargv);
9222 /*{{{ void getredirection(int *ac, char ***av)*/
9224 mp_getredirection(pTHX_ int *ac, char ***av)
9226 * Process vms redirection arg's. Exit if any error is seen.
9227 * If getredirection() processes an argument, it is erased
9228 * from the vector. getredirection() returns a new argc and argv value.
9229 * In the event that a background command is requested (by a trailing "&"),
9230 * this routine creates a background subprocess, and simply exits the program.
9232 * Warning: do not try to simplify the code for vms. The code
9233 * presupposes that getredirection() is called before any data is
9234 * read from stdin or written to stdout.
9236 * Normal usage is as follows:
9242 * getredirection(&argc, &argv);
9246 int argc = *ac; /* Argument Count */
9247 char **argv = *av; /* Argument Vector */
9248 char *ap; /* Argument pointer */
9249 int j; /* argv[] index */
9250 int item_count = 0; /* Count of Items in List */
9251 struct list_item *list_head = 0; /* First Item in List */
9252 struct list_item *list_tail; /* Last Item in List */
9253 char *in = NULL; /* Input File Name */
9254 char *out = NULL; /* Output File Name */
9255 char *outmode = "w"; /* Mode to Open Output File */
9256 char *err = NULL; /* Error File Name */
9257 char *errmode = "w"; /* Mode to Open Error File */
9258 int cmargc = 0; /* Piped Command Arg Count */
9259 char **cmargv = NULL;/* Piped Command Arg Vector */
9262 * First handle the case where the last thing on the line ends with
9263 * a '&'. This indicates the desire for the command to be run in a
9264 * subprocess, so we satisfy that desire.
9267 if (0 == strcmp("&", ap))
9268 exit(background_process(aTHX_ --argc, argv));
9269 if (*ap && '&' == ap[strlen(ap)-1])
9271 ap[strlen(ap)-1] = '\0';
9272 exit(background_process(aTHX_ argc, argv));
9275 * Now we handle the general redirection cases that involve '>', '>>',
9276 * '<', and pipes '|'.
9278 for (j = 0; j < argc; ++j)
9280 if (0 == strcmp("<", argv[j]))
9284 fprintf(stderr,"No input file after < on command line");
9285 exit(LIB$_WRONUMARG);
9290 if ('<' == *(ap = argv[j]))
9295 if (0 == strcmp(">", ap))
9299 fprintf(stderr,"No output file after > on command line");
9300 exit(LIB$_WRONUMARG);
9319 fprintf(stderr,"No output file after > or >> on command line");
9320 exit(LIB$_WRONUMARG);
9324 if (('2' == *ap) && ('>' == ap[1]))
9341 fprintf(stderr,"No output file after 2> or 2>> on command line");
9342 exit(LIB$_WRONUMARG);
9346 if (0 == strcmp("|", argv[j]))
9350 fprintf(stderr,"No command into which to pipe on command line");
9351 exit(LIB$_WRONUMARG);
9353 cmargc = argc-(j+1);
9354 cmargv = &argv[j+1];
9358 if ('|' == *(ap = argv[j]))
9366 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9369 * Allocate and fill in the new argument vector, Some Unix's terminate
9370 * the list with an extra null pointer.
9372 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9373 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9375 for (j = 0; j < item_count; ++j, list_head = list_head->next)
9376 argv[j] = list_head->value;
9382 fprintf(stderr,"'|' and '>' may not both be specified on command line");
9383 exit(LIB$_INVARGORD);
9385 pipe_and_fork(aTHX_ cmargv);
9388 /* Check for input from a pipe (mailbox) */
9390 if (in == NULL && 1 == isapipe(0))
9392 char mbxname[L_tmpnam];
9394 long int dvi_item = DVI$_DEVBUFSIZ;
9395 $DESCRIPTOR(mbxnam, "");
9396 $DESCRIPTOR(mbxdevnam, "");
9398 /* Input from a pipe, reopen it in binary mode to disable */
9399 /* carriage control processing. */
9401 fgetname(stdin, mbxname);
9402 mbxnam.dsc$a_pointer = mbxname;
9403 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9404 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9405 mbxdevnam.dsc$a_pointer = mbxname;
9406 mbxdevnam.dsc$w_length = sizeof(mbxname);
9407 dvi_item = DVI$_DEVNAM;
9408 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9409 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9412 freopen(mbxname, "rb", stdin);
9415 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9419 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9421 fprintf(stderr,"Can't open input file %s as stdin",in);
9424 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9426 fprintf(stderr,"Can't open output file %s as stdout",out);
9429 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
9432 if (strcmp(err,"&1") == 0) {
9433 dup2(fileno(stdout), fileno(stderr));
9434 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
9437 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9439 fprintf(stderr,"Can't open error file %s as stderr",err);
9443 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9447 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
9450 #ifdef ARGPROC_DEBUG
9451 PerlIO_printf(Perl_debug_log, "Arglist:\n");
9452 for (j = 0; j < *ac; ++j)
9453 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9455 /* Clear errors we may have hit expanding wildcards, so they don't
9456 show up in Perl's $! later */
9457 set_errno(0); set_vaxc_errno(1);
9458 } /* end of getredirection() */
9461 static void add_item(struct list_item **head,
9462 struct list_item **tail,
9468 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9469 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9473 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9474 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9475 *tail = (*tail)->next;
9477 (*tail)->value = value;
9481 static void mp_expand_wild_cards(pTHX_ char *item,
9482 struct list_item **head,
9483 struct list_item **tail,
9487 unsigned long int context = 0;
9495 $DESCRIPTOR(filespec, "");
9496 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9497 $DESCRIPTOR(resultspec, "");
9498 unsigned long int lff_flags = 0;
9502 #ifdef VMS_LONGNAME_SUPPORT
9503 lff_flags = LIB$M_FIL_LONG_NAMES;
9506 for (cp = item; *cp; cp++) {
9507 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9508 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9510 if (!*cp || isspace(*cp))
9512 add_item(head, tail, item, count);
9517 /* "double quoted" wild card expressions pass as is */
9518 /* From DCL that means using e.g.: */
9519 /* perl program """perl.*""" */
9520 item_len = strlen(item);
9521 if ( '"' == *item && '"' == item[item_len-1] )
9524 item[item_len-2] = '\0';
9525 add_item(head, tail, item, count);
9529 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9530 resultspec.dsc$b_class = DSC$K_CLASS_D;
9531 resultspec.dsc$a_pointer = NULL;
9532 vmsspec = PerlMem_malloc(VMS_MAXRSS);
9533 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9534 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9535 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9536 if (!isunix || !filespec.dsc$a_pointer)
9537 filespec.dsc$a_pointer = item;
9538 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9540 * Only return version specs, if the caller specified a version
9542 had_version = strchr(item, ';');
9544 * Only return device and directory specs, if the caller specifed either.
9546 had_device = strchr(item, ':');
9547 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9549 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9550 (&filespec, &resultspec, &context,
9551 &defaultspec, 0, &rms_sts, &lff_flags)))
9556 string = PerlMem_malloc(resultspec.dsc$w_length+1);
9557 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9558 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
9559 string[resultspec.dsc$w_length] = '\0';
9560 if (NULL == had_version)
9561 *(strrchr(string, ';')) = '\0';
9562 if ((!had_directory) && (had_device == NULL))
9564 if (NULL == (devdir = strrchr(string, ']')))
9565 devdir = strrchr(string, '>');
9566 strcpy(string, devdir + 1);
9569 * Be consistent with what the C RTL has already done to the rest of
9570 * the argv items and lowercase all of these names.
9572 if (!decc_efs_case_preserve) {
9573 for (c = string; *c; ++c)
9577 if (isunix) trim_unixpath(string,item,1);
9578 add_item(head, tail, string, count);
9581 PerlMem_free(vmsspec);
9582 if (sts != RMS$_NMF)
9584 set_vaxc_errno(sts);
9587 case RMS$_FNF: case RMS$_DNF:
9588 set_errno(ENOENT); break;
9590 set_errno(ENOTDIR); break;
9592 set_errno(ENODEV); break;
9593 case RMS$_FNM: case RMS$_SYN:
9594 set_errno(EINVAL); break;
9596 set_errno(EACCES); break;
9598 _ckvmssts_noperl(sts);
9602 add_item(head, tail, item, count);
9603 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9604 _ckvmssts_noperl(lib$find_file_end(&context));
9607 static int child_st[2];/* Event Flag set when child process completes */
9609 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
9611 static unsigned long int exit_handler(int *status)
9615 if (0 == child_st[0])
9617 #ifdef ARGPROC_DEBUG
9618 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
9620 fflush(stdout); /* Have to flush pipe for binary data to */
9621 /* terminate properly -- <tp@mccall.com> */
9622 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9623 sys$dassgn(child_chan);
9625 sys$synch(0, child_st);
9630 static void sig_child(int chan)
9632 #ifdef ARGPROC_DEBUG
9633 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
9635 if (child_st[0] == 0)
9639 static struct exit_control_block exit_block =
9644 &exit_block.exit_status,
9649 pipe_and_fork(pTHX_ char **cmargv)
9652 struct dsc$descriptor_s *vmscmd;
9653 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9654 int sts, j, l, ismcr, quote, tquote = 0;
9656 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
9657 vms_execfree(vmscmd);
9662 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
9663 && toupper(*(q+2)) == 'R' && !*(q+3);
9665 while (q && l < MAX_DCL_LINE_LENGTH) {
9667 if (j > 0 && quote) {
9673 if (ismcr && j > 1) quote = 1;
9674 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9677 if (quote || tquote) {
9683 if ((quote||tquote) && *q == '"') {
9693 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9695 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9699 static int background_process(pTHX_ int argc, char **argv)
9701 char command[MAX_DCL_SYMBOL + 1] = "$";
9702 $DESCRIPTOR(value, "");
9703 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9704 static $DESCRIPTOR(null, "NLA0:");
9705 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9707 $DESCRIPTOR(pidstr, "");
9709 unsigned long int flags = 17, one = 1, retsts;
9712 strcat(command, argv[0]);
9713 len = strlen(command);
9714 while (--argc && (len < MAX_DCL_SYMBOL))
9716 strcat(command, " \"");
9717 strcat(command, *(++argv));
9718 strcat(command, "\"");
9719 len = strlen(command);
9721 value.dsc$a_pointer = command;
9722 value.dsc$w_length = strlen(value.dsc$a_pointer);
9723 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9724 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9725 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9726 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9729 _ckvmssts_noperl(retsts);
9731 #ifdef ARGPROC_DEBUG
9732 PerlIO_printf(Perl_debug_log, "%s\n", command);
9734 sprintf(pidstring, "%08X", pid);
9735 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9736 pidstr.dsc$a_pointer = pidstring;
9737 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9738 lib$set_symbol(&pidsymbol, &pidstr);
9742 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9745 /* OS-specific initialization at image activation (not thread startup) */
9746 /* Older VAXC header files lack these constants */
9747 #ifndef JPI$_RIGHTS_SIZE
9748 # define JPI$_RIGHTS_SIZE 817
9750 #ifndef KGB$M_SUBSYSTEM
9751 # define KGB$M_SUBSYSTEM 0x8
9754 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9756 /*{{{void vms_image_init(int *, char ***)*/
9758 vms_image_init(int *argcp, char ***argvp)
9761 char eqv[LNM$C_NAMLENGTH+1] = "";
9762 unsigned int len, tabct = 8, tabidx = 0;
9763 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9764 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9765 unsigned short int dummy, rlen;
9766 struct dsc$descriptor_s **tabvec;
9767 #if defined(PERL_IMPLICIT_CONTEXT)
9770 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9771 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9772 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9775 #ifdef KILL_BY_SIGPRC
9776 Perl_csighandler_init();
9779 /* This was moved from the pre-image init handler because on threaded */
9780 /* Perl it was always returning 0 for the default value. */
9781 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9784 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9787 initial = decc$feature_get_value(s, 4);
9789 /* initial is: 0 if nothing has set the feature */
9790 /* -1 if initialized to default */
9791 /* 1 if set by logical name */
9792 /* 2 if set by decc$feature_set_value */
9793 decc_disable_posix_root = decc$feature_get_value(s, 1);
9795 /* If the value is not valid, force the feature off */
9796 if (decc_disable_posix_root < 0) {
9797 decc$feature_set_value(s, 1, 1);
9798 decc_disable_posix_root = 1;
9802 /* Nothing has asked for it explicitly, so use our own default. */
9803 decc_disable_posix_root = 1;
9804 decc$feature_set_value(s, 1, 1);
9810 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9811 _ckvmssts_noperl(iosb[0]);
9812 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9813 if (iprv[i]) { /* Running image installed with privs? */
9814 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
9819 /* Rights identifiers might trigger tainting as well. */
9820 if (!will_taint && (rlen || rsz)) {
9821 while (rlen < rsz) {
9822 /* We didn't get all the identifiers on the first pass. Allocate a
9823 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9824 * were needed to hold all identifiers at time of last call; we'll
9825 * allocate that many unsigned long ints), and go back and get 'em.
9826 * If it gave us less than it wanted to despite ample buffer space,
9827 * something's broken. Is your system missing a system identifier?
9829 if (rsz <= jpilist[1].buflen) {
9830 /* Perl_croak accvios when used this early in startup. */
9831 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9832 rsz, (unsigned long) jpilist[1].buflen,
9833 "Check your rights database for corruption.\n");
9836 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9837 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9838 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9839 jpilist[1].buflen = rsz * sizeof(unsigned long int);
9840 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9841 _ckvmssts_noperl(iosb[0]);
9843 mask = jpilist[1].bufadr;
9844 /* Check attribute flags for each identifier (2nd longword); protected
9845 * subsystem identifiers trigger tainting.
9847 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9848 if (mask[i] & KGB$M_SUBSYSTEM) {
9853 if (mask != rlst) PerlMem_free(mask);
9856 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9857 * logical, some versions of the CRTL will add a phanthom /000000/
9858 * directory. This needs to be removed.
9860 if (decc_filename_unix_report) {
9863 ulen = strlen(argvp[0][0]);
9865 zeros = strstr(argvp[0][0], "/000000/");
9866 if (zeros != NULL) {
9868 mlen = ulen - (zeros - argvp[0][0]) - 7;
9869 memmove(zeros, &zeros[7], mlen);
9871 argvp[0][0][ulen] = '\0';
9874 /* It also may have a trailing dot that needs to be removed otherwise
9875 * it will be converted to VMS mode incorrectly.
9878 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9879 argvp[0][0][ulen] = '\0';
9882 /* We need to use this hack to tell Perl it should run with tainting,
9883 * since its tainting flag may be part of the PL_curinterp struct, which
9884 * hasn't been allocated when vms_image_init() is called.
9887 char **newargv, **oldargv;
9889 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9890 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9891 newargv[0] = oldargv[0];
9892 newargv[1] = PerlMem_malloc(3 * sizeof(char));
9893 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9894 strcpy(newargv[1], "-T");
9895 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9897 newargv[*argcp] = NULL;
9898 /* We orphan the old argv, since we don't know where it's come from,
9899 * so we don't know how to free it.
9903 else { /* Did user explicitly request tainting? */
9905 char *cp, **av = *argvp;
9906 for (i = 1; i < *argcp; i++) {
9907 if (*av[i] != '-') break;
9908 for (cp = av[i]+1; *cp; cp++) {
9909 if (*cp == 'T') { will_taint = 1; break; }
9910 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9911 strchr("DFIiMmx",*cp)) break;
9913 if (will_taint) break;
9918 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9921 tabvec = (struct dsc$descriptor_s **)
9922 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9923 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9925 else if (tabidx >= tabct) {
9927 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9928 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9930 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9931 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9932 tabvec[tabidx]->dsc$w_length = 0;
9933 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9934 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
9935 tabvec[tabidx]->dsc$a_pointer = NULL;
9936 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
9938 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9940 getredirection(argcp,argvp);
9941 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9943 # include <reentrancy.h>
9944 decc$set_reentrancy(C$C_MULTITHREAD);
9953 * Trim Unix-style prefix off filespec, so it looks like what a shell
9954 * glob expansion would return (i.e. from specified prefix on, not
9955 * full path). Note that returned filespec is Unix-style, regardless
9956 * of whether input filespec was VMS-style or Unix-style.
9958 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9959 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9960 * vector of options; at present, only bit 0 is used, and if set tells
9961 * trim unixpath to try the current default directory as a prefix when
9962 * presented with a possibly ambiguous ... wildcard.
9964 * Returns !=0 on success, with trimmed filespec replacing contents of
9965 * fspec, and 0 on failure, with contents of fpsec unchanged.
9967 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9969 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9971 char *unixified, *unixwild,
9972 *template, *base, *end, *cp1, *cp2;
9973 register int tmplen, reslen = 0, dirs = 0;
9975 if (!wildspec || !fspec) return 0;
9977 unixwild = PerlMem_malloc(VMS_MAXRSS);
9978 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9979 template = unixwild;
9980 if (strpbrk(wildspec,"]>:") != NULL) {
9981 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
9982 PerlMem_free(unixwild);
9987 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
9988 unixwild[VMS_MAXRSS-1] = 0;
9990 unixified = PerlMem_malloc(VMS_MAXRSS);
9991 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9992 if (strpbrk(fspec,"]>:") != NULL) {
9993 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
9994 PerlMem_free(unixwild);
9995 PerlMem_free(unixified);
9998 else base = unixified;
9999 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
10000 * check to see that final result fits into (isn't longer than) fspec */
10001 reslen = strlen(fspec);
10005 /* No prefix or absolute path on wildcard, so nothing to remove */
10006 if (!*template || *template == '/') {
10007 PerlMem_free(unixwild);
10008 if (base == fspec) {
10009 PerlMem_free(unixified);
10012 tmplen = strlen(unixified);
10013 if (tmplen > reslen) {
10014 PerlMem_free(unixified);
10015 return 0; /* not enough space */
10017 /* Copy unixified resultant, including trailing NUL */
10018 memmove(fspec,unixified,tmplen+1);
10019 PerlMem_free(unixified);
10023 for (end = base; *end; end++) ; /* Find end of resultant filespec */
10024 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
10025 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
10026 for (cp1 = end ;cp1 >= base; cp1--)
10027 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
10029 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
10030 PerlMem_free(unixified);
10031 PerlMem_free(unixwild);
10036 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
10037 int ells = 1, totells, segdirs, match;
10038 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
10039 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10041 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
10043 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
10044 tpl = PerlMem_malloc(VMS_MAXRSS);
10045 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10046 if (ellipsis == template && opts & 1) {
10047 /* Template begins with an ellipsis. Since we can't tell how many
10048 * directory names at the front of the resultant to keep for an
10049 * arbitrary starting point, we arbitrarily choose the current
10050 * default directory as a starting point. If it's there as a prefix,
10051 * clip it off. If not, fall through and act as if the leading
10052 * ellipsis weren't there (i.e. return shortest possible path that
10053 * could match template).
10055 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
10057 PerlMem_free(unixified);
10058 PerlMem_free(unixwild);
10061 if (!decc_efs_case_preserve) {
10062 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10063 if (_tolower(*cp1) != _tolower(*cp2)) break;
10065 segdirs = dirs - totells; /* Min # of dirs we must have left */
10066 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
10067 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
10068 memmove(fspec,cp2+1,end - cp2);
10070 PerlMem_free(unixified);
10071 PerlMem_free(unixwild);
10075 /* First off, back up over constant elements at end of path */
10077 for (front = end ; front >= base; front--)
10078 if (*front == '/' && !dirs--) { front++; break; }
10080 lcres = PerlMem_malloc(VMS_MAXRSS);
10081 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10082 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
10084 if (!decc_efs_case_preserve) {
10085 *cp2 = _tolower(*cp1); /* Make lc copy for match */
10093 PerlMem_free(unixified);
10094 PerlMem_free(unixwild);
10095 PerlMem_free(lcres);
10096 return 0; /* Path too long. */
10099 *cp2 = '\0'; /* Pick up with memcpy later */
10100 lcfront = lcres + (front - base);
10101 /* Now skip over each ellipsis and try to match the path in front of it. */
10103 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
10104 if (*(cp1) == '.' && *(cp1+1) == '.' &&
10105 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
10106 if (cp1 < template) break; /* template started with an ellipsis */
10107 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
10108 ellipsis = cp1; continue;
10110 wilddsc.dsc$a_pointer = tpl;
10111 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
10113 for (segdirs = 0, cp2 = tpl;
10114 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
10116 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
10118 if (!decc_efs_case_preserve) {
10119 *cp2 = _tolower(*cp1); /* else lowercase for match */
10122 *cp2 = *cp1; /* else preserve case for match */
10125 if (*cp2 == '/') segdirs++;
10127 if (cp1 != ellipsis - 1) {
10129 PerlMem_free(unixified);
10130 PerlMem_free(unixwild);
10131 PerlMem_free(lcres);
10132 return 0; /* Path too long */
10134 /* Back up at least as many dirs as in template before matching */
10135 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
10136 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
10137 for (match = 0; cp1 > lcres;) {
10138 resdsc.dsc$a_pointer = cp1;
10139 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
10141 if (match == 1) lcfront = cp1;
10143 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
10147 PerlMem_free(unixified);
10148 PerlMem_free(unixwild);
10149 PerlMem_free(lcres);
10150 return 0; /* Can't find prefix ??? */
10152 if (match > 1 && opts & 1) {
10153 /* This ... wildcard could cover more than one set of dirs (i.e.
10154 * a set of similar dir names is repeated). If the template
10155 * contains more than 1 ..., upstream elements could resolve the
10156 * ambiguity, but it's not worth a full backtracking setup here.
10157 * As a quick heuristic, clip off the current default directory
10158 * if it's present to find the trimmed spec, else use the
10159 * shortest string that this ... could cover.
10161 char def[NAM$C_MAXRSS+1], *st;
10163 if (getcwd(def, sizeof def,0) == NULL) {
10164 PerlMem_free(unixified);
10165 PerlMem_free(unixwild);
10166 PerlMem_free(lcres);
10170 if (!decc_efs_case_preserve) {
10171 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10172 if (_tolower(*cp1) != _tolower(*cp2)) break;
10174 segdirs = dirs - totells; /* Min # of dirs we must have left */
10175 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
10176 if (*cp1 == '\0' && *cp2 == '/') {
10177 memmove(fspec,cp2+1,end - cp2);
10179 PerlMem_free(unixified);
10180 PerlMem_free(unixwild);
10181 PerlMem_free(lcres);
10184 /* Nope -- stick with lcfront from above and keep going. */
10187 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
10189 PerlMem_free(unixified);
10190 PerlMem_free(unixwild);
10191 PerlMem_free(lcres);
10193 ellipsis = nextell;
10196 } /* end of trim_unixpath() */
10201 * VMS readdir() routines.
10202 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
10204 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
10205 * Minor modifications to original routines.
10208 /* readdir may have been redefined by reentr.h, so make sure we get
10209 * the local version for what we do here.
10214 #if !defined(PERL_IMPLICIT_CONTEXT)
10215 # define readdir Perl_readdir
10217 # define readdir(a) Perl_readdir(aTHX_ a)
10220 /* Number of elements in vms_versions array */
10221 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
10224 * Open a directory, return a handle for later use.
10226 /*{{{ DIR *opendir(char*name) */
10228 Perl_opendir(pTHX_ const char *name)
10234 Newx(dir, VMS_MAXRSS, char);
10235 if (int_tovmspath(name, dir, NULL) == NULL) {
10239 /* Check access before stat; otherwise stat does not
10240 * accurately report whether it's a directory.
10242 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
10243 /* cando_by_name has already set errno */
10247 if (flex_stat(dir,&sb) == -1) return NULL;
10248 if (!S_ISDIR(sb.st_mode)) {
10250 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
10253 /* Get memory for the handle, and the pattern. */
10255 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
10257 /* Fill in the fields; mainly playing with the descriptor. */
10258 sprintf(dd->pattern, "%s*.*",dir);
10263 /* By saying we always want the result of readdir() in unix format, we
10264 * are really saying we want all the escapes removed. Otherwise the caller,
10265 * having no way to know whether it's already in VMS format, might send it
10266 * through tovmsspec again, thus double escaping.
10268 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
10269 dd->pat.dsc$a_pointer = dd->pattern;
10270 dd->pat.dsc$w_length = strlen(dd->pattern);
10271 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10272 dd->pat.dsc$b_class = DSC$K_CLASS_S;
10273 #if defined(USE_ITHREADS)
10274 Newx(dd->mutex,1,perl_mutex);
10275 MUTEX_INIT( (perl_mutex *) dd->mutex );
10281 } /* end of opendir() */
10285 * Set the flag to indicate we want versions or not.
10287 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10289 vmsreaddirversions(DIR *dd, int flag)
10292 dd->flags |= PERL_VMSDIR_M_VERSIONS;
10294 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10299 * Free up an opened directory.
10301 /*{{{ void closedir(DIR *dd)*/
10303 Perl_closedir(DIR *dd)
10307 sts = lib$find_file_end(&dd->context);
10308 Safefree(dd->pattern);
10309 #if defined(USE_ITHREADS)
10310 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10311 Safefree(dd->mutex);
10318 * Collect all the version numbers for the current file.
10321 collectversions(pTHX_ DIR *dd)
10323 struct dsc$descriptor_s pat;
10324 struct dsc$descriptor_s res;
10326 char *p, *text, *buff;
10328 unsigned long context, tmpsts;
10330 /* Convenient shorthand. */
10333 /* Add the version wildcard, ignoring the "*.*" put on before */
10334 i = strlen(dd->pattern);
10335 Newx(text,i + e->d_namlen + 3,char);
10336 strcpy(text, dd->pattern);
10337 sprintf(&text[i - 3], "%s;*", e->d_name);
10339 /* Set up the pattern descriptor. */
10340 pat.dsc$a_pointer = text;
10341 pat.dsc$w_length = i + e->d_namlen - 1;
10342 pat.dsc$b_dtype = DSC$K_DTYPE_T;
10343 pat.dsc$b_class = DSC$K_CLASS_S;
10345 /* Set up result descriptor. */
10346 Newx(buff, VMS_MAXRSS, char);
10347 res.dsc$a_pointer = buff;
10348 res.dsc$w_length = VMS_MAXRSS - 1;
10349 res.dsc$b_dtype = DSC$K_DTYPE_T;
10350 res.dsc$b_class = DSC$K_CLASS_S;
10352 /* Read files, collecting versions. */
10353 for (context = 0, e->vms_verscount = 0;
10354 e->vms_verscount < VERSIZE(e);
10355 e->vms_verscount++) {
10356 unsigned long rsts;
10357 unsigned long flags = 0;
10359 #ifdef VMS_LONGNAME_SUPPORT
10360 flags = LIB$M_FIL_LONG_NAMES;
10362 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10363 if (tmpsts == RMS$_NMF || context == 0) break;
10365 buff[VMS_MAXRSS - 1] = '\0';
10366 if ((p = strchr(buff, ';')))
10367 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10369 e->vms_versions[e->vms_verscount] = -1;
10372 _ckvmssts(lib$find_file_end(&context));
10376 } /* end of collectversions() */
10379 * Read the next entry from the directory.
10381 /*{{{ struct dirent *readdir(DIR *dd)*/
10383 Perl_readdir(pTHX_ DIR *dd)
10385 struct dsc$descriptor_s res;
10387 unsigned long int tmpsts;
10388 unsigned long rsts;
10389 unsigned long flags = 0;
10390 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10391 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10393 /* Set up result descriptor, and get next file. */
10394 Newx(buff, VMS_MAXRSS, char);
10395 res.dsc$a_pointer = buff;
10396 res.dsc$w_length = VMS_MAXRSS - 1;
10397 res.dsc$b_dtype = DSC$K_DTYPE_T;
10398 res.dsc$b_class = DSC$K_CLASS_S;
10400 #ifdef VMS_LONGNAME_SUPPORT
10401 flags = LIB$M_FIL_LONG_NAMES;
10404 tmpsts = lib$find_file
10405 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10406 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
10407 if (!(tmpsts & 1)) {
10408 set_vaxc_errno(tmpsts);
10411 set_errno(EACCES); break;
10413 set_errno(ENODEV); break;
10415 set_errno(ENOTDIR); break;
10416 case RMS$_FNF: case RMS$_DNF:
10417 set_errno(ENOENT); break;
10419 set_errno(EVMSERR);
10425 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10426 buff[res.dsc$w_length] = '\0';
10427 p = buff + res.dsc$w_length;
10428 while (--p >= buff) if (!isspace(*p)) break;
10430 if (!decc_efs_case_preserve) {
10431 for (p = buff; *p; p++) *p = _tolower(*p);
10434 /* Skip any directory component and just copy the name. */
10435 sts = vms_split_path
10450 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10452 /* In Unix report mode, remove the ".dir;1" from the name */
10453 /* if it is a real directory. */
10454 if (decc_filename_unix_report || decc_efs_charset) {
10455 if ((e_len == 4) && (vs_len == 2) && (vs_spec[1] == '1')) {
10456 if ((toupper(e_spec[1]) == 'D') &&
10457 (toupper(e_spec[2]) == 'I') &&
10458 (toupper(e_spec[3]) == 'R')) {
10462 ret_sts = stat(buff, &statbuf.crtl_stat);
10463 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10471 /* Drop NULL extensions on UNIX file specification */
10472 if ((e_len == 1) && decc_readdir_dropdotnotype) {
10478 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
10479 dd->entry.d_name[n_len + e_len] = '\0';
10480 dd->entry.d_namlen = strlen(dd->entry.d_name);
10482 /* Convert the filename to UNIX format if needed */
10483 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10485 /* Translate the encoded characters. */
10486 /* Fixme: Unicode handling could result in embedded 0 characters */
10487 if (strchr(dd->entry.d_name, '^') != NULL) {
10488 char new_name[256];
10490 p = dd->entry.d_name;
10493 int inchars_read, outchars_added;
10494 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10496 q += outchars_added;
10498 /* if outchars_added > 1, then this is a wide file specification */
10499 /* Wide file specifications need to be passed in Perl */
10500 /* counted strings apparently with a Unicode flag */
10503 strcpy(dd->entry.d_name, new_name);
10504 dd->entry.d_namlen = strlen(dd->entry.d_name);
10508 dd->entry.vms_verscount = 0;
10509 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10513 } /* end of readdir() */
10517 * Read the next entry from the directory -- thread-safe version.
10519 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10521 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10525 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10527 entry = readdir(dd);
10529 retval = ( *result == NULL ? errno : 0 );
10531 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10535 } /* end of readdir_r() */
10539 * Return something that can be used in a seekdir later.
10541 /*{{{ long telldir(DIR *dd)*/
10543 Perl_telldir(DIR *dd)
10550 * Return to a spot where we used to be. Brute force.
10552 /*{{{ void seekdir(DIR *dd,long count)*/
10554 Perl_seekdir(pTHX_ DIR *dd, long count)
10558 /* If we haven't done anything yet... */
10559 if (dd->count == 0)
10562 /* Remember some state, and clear it. */
10563 old_flags = dd->flags;
10564 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10565 _ckvmssts(lib$find_file_end(&dd->context));
10568 /* The increment is in readdir(). */
10569 for (dd->count = 0; dd->count < count; )
10572 dd->flags = old_flags;
10574 } /* end of seekdir() */
10577 /* VMS subprocess management
10579 * my_vfork() - just a vfork(), after setting a flag to record that
10580 * the current script is trying a Unix-style fork/exec.
10582 * vms_do_aexec() and vms_do_exec() are called in response to the
10583 * perl 'exec' function. If this follows a vfork call, then they
10584 * call out the regular perl routines in doio.c which do an
10585 * execvp (for those who really want to try this under VMS).
10586 * Otherwise, they do exactly what the perl docs say exec should
10587 * do - terminate the current script and invoke a new command
10588 * (See below for notes on command syntax.)
10590 * do_aspawn() and do_spawn() implement the VMS side of the perl
10591 * 'system' function.
10593 * Note on command arguments to perl 'exec' and 'system': When handled
10594 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10595 * are concatenated to form a DCL command string. If the first non-numeric
10596 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10597 * the command string is handed off to DCL directly. Otherwise,
10598 * the first token of the command is taken as the filespec of an image
10599 * to run. The filespec is expanded using a default type of '.EXE' and
10600 * the process defaults for device, directory, etc., and if found, the resultant
10601 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10602 * the command string as parameters. This is perhaps a bit complicated,
10603 * but I hope it will form a happy medium between what VMS folks expect
10604 * from lib$spawn and what Unix folks expect from exec.
10607 static int vfork_called;
10609 /*{{{int my_vfork()*/
10620 vms_execfree(struct dsc$descriptor_s *vmscmd)
10623 if (vmscmd->dsc$a_pointer) {
10624 PerlMem_free(vmscmd->dsc$a_pointer);
10626 PerlMem_free(vmscmd);
10631 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10633 char *junk, *tmps = NULL;
10634 register size_t cmdlen = 0;
10641 tmps = SvPV(really,rlen);
10643 cmdlen += rlen + 1;
10648 for (idx++; idx <= sp; idx++) {
10650 junk = SvPVx(*idx,rlen);
10651 cmdlen += rlen ? rlen + 1 : 0;
10654 Newx(PL_Cmd, cmdlen+1, char);
10656 if (tmps && *tmps) {
10657 strcpy(PL_Cmd,tmps);
10660 else *PL_Cmd = '\0';
10661 while (++mark <= sp) {
10663 char *s = SvPVx(*mark,n_a);
10665 if (*PL_Cmd) strcat(PL_Cmd," ");
10671 } /* end of setup_argstr() */
10674 static unsigned long int
10675 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10676 struct dsc$descriptor_s **pvmscmd)
10680 char image_name[NAM$C_MAXRSS+1];
10681 char image_argv[NAM$C_MAXRSS+1];
10682 $DESCRIPTOR(defdsc,".EXE");
10683 $DESCRIPTOR(defdsc2,".");
10684 struct dsc$descriptor_s resdsc;
10685 struct dsc$descriptor_s *vmscmd;
10686 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10687 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10688 register char *s, *rest, *cp, *wordbreak;
10691 register int isdcl;
10693 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10694 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10696 /* vmsspec is a DCL command buffer, not just a filename */
10697 vmsspec = PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10698 if (vmsspec == NULL)
10699 _ckvmssts_noperl(SS$_INSFMEM);
10701 resspec = PerlMem_malloc(VMS_MAXRSS);
10702 if (resspec == NULL)
10703 _ckvmssts_noperl(SS$_INSFMEM);
10705 /* Make a copy for modification */
10706 cmdlen = strlen(incmd);
10707 cmd = PerlMem_malloc(cmdlen+1);
10708 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10709 strncpy(cmd, incmd, cmdlen);
10714 resdsc.dsc$a_pointer = resspec;
10715 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10716 resdsc.dsc$b_class = DSC$K_CLASS_S;
10717 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10719 vmscmd->dsc$a_pointer = NULL;
10720 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10721 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10722 vmscmd->dsc$w_length = 0;
10723 if (pvmscmd) *pvmscmd = vmscmd;
10725 if (suggest_quote) *suggest_quote = 0;
10727 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10729 PerlMem_free(vmsspec);
10730 PerlMem_free(resspec);
10731 return CLI$_BUFOVF; /* continuation lines currently unsupported */
10736 while (*s && isspace(*s)) s++;
10738 if (*s == '@' || *s == '$') {
10739 vmsspec[0] = *s; rest = s + 1;
10740 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10742 else { cp = vmsspec; rest = s; }
10743 if (*rest == '.' || *rest == '/') {
10745 for (cp2 = resspec;
10746 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10747 rest++, cp2++) *cp2 = *rest;
10749 if (int_tovmsspec(resspec, cp, 0, NULL)) {
10752 /* When a UNIX spec with no file type is translated to VMS, */
10753 /* A trailing '.' is appended under ODS-5 rules. */
10754 /* Here we do not want that trailing "." as it prevents */
10755 /* Looking for a implied ".exe" type. */
10756 if (decc_efs_charset) {
10758 i = strlen(vmsspec);
10759 if (vmsspec[i-1] == '.') {
10760 vmsspec[i-1] = '\0';
10765 for (cp2 = vmsspec + strlen(vmsspec);
10766 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10767 rest++, cp2++) *cp2 = *rest;
10772 /* Intuit whether verb (first word of cmd) is a DCL command:
10773 * - if first nonspace char is '@', it's a DCL indirection
10775 * - if verb contains a filespec separator, it's not a DCL command
10776 * - if it doesn't, caller tells us whether to default to a DCL
10777 * command, or to a local image unless told it's DCL (by leading '$')
10781 if (suggest_quote) *suggest_quote = 1;
10783 register char *filespec = strpbrk(s,":<[.;");
10784 rest = wordbreak = strpbrk(s," \"\t/");
10785 if (!wordbreak) wordbreak = s + strlen(s);
10786 if (*s == '$') check_img = 0;
10787 if (filespec && (filespec < wordbreak)) isdcl = 0;
10788 else isdcl = !check_img;
10793 imgdsc.dsc$a_pointer = s;
10794 imgdsc.dsc$w_length = wordbreak - s;
10795 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10797 _ckvmssts_noperl(lib$find_file_end(&cxt));
10798 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10799 if (!(retsts & 1) && *s == '$') {
10800 _ckvmssts_noperl(lib$find_file_end(&cxt));
10801 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10802 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10804 _ckvmssts_noperl(lib$find_file_end(&cxt));
10805 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10809 _ckvmssts_noperl(lib$find_file_end(&cxt));
10814 while (*s && !isspace(*s)) s++;
10817 /* check that it's really not DCL with no file extension */
10818 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10820 char b[256] = {0,0,0,0};
10821 read(fileno(fp), b, 256);
10822 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10826 /* Check for script */
10828 if ((b[0] == '#') && (b[1] == '!'))
10830 #ifdef ALTERNATE_SHEBANG
10832 shebang_len = strlen(ALTERNATE_SHEBANG);
10833 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10835 perlstr = strstr("perl",b);
10836 if (perlstr == NULL)
10844 if (shebang_len > 0) {
10847 char tmpspec[NAM$C_MAXRSS + 1];
10850 /* Image is following after white space */
10851 /*--------------------------------------*/
10852 while (isprint(b[i]) && isspace(b[i]))
10856 while (isprint(b[i]) && !isspace(b[i])) {
10857 tmpspec[j++] = b[i++];
10858 if (j >= NAM$C_MAXRSS)
10863 /* There may be some default parameters to the image */
10864 /*---------------------------------------------------*/
10866 while (isprint(b[i])) {
10867 image_argv[j++] = b[i++];
10868 if (j >= NAM$C_MAXRSS)
10871 while ((j > 0) && !isprint(image_argv[j-1]))
10875 /* It will need to be converted to VMS format and validated */
10876 if (tmpspec[0] != '\0') {
10879 /* Try to find the exact program requested to be run */
10880 /*---------------------------------------------------*/
10881 iname = int_rmsexpand
10882 (tmpspec, image_name, ".exe",
10883 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10884 if (iname != NULL) {
10885 if (cando_by_name_int
10886 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10887 /* MCR prefix needed */
10891 /* Try again with a null type */
10892 /*----------------------------*/
10893 iname = int_rmsexpand
10894 (tmpspec, image_name, ".",
10895 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10896 if (iname != NULL) {
10897 if (cando_by_name_int
10898 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10899 /* MCR prefix needed */
10905 /* Did we find the image to run the script? */
10906 /*------------------------------------------*/
10910 /* Assume DCL or foreign command exists */
10911 /*--------------------------------------*/
10912 tchr = strrchr(tmpspec, '/');
10913 if (tchr != NULL) {
10919 strcpy(image_name, tchr);
10927 if (check_img && isdcl) {
10929 PerlMem_free(resspec);
10930 PerlMem_free(vmsspec);
10934 if (cando_by_name(S_IXUSR,0,resspec)) {
10935 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10936 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10938 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
10939 if (image_name[0] != 0) {
10940 strcat(vmscmd->dsc$a_pointer, image_name);
10941 strcat(vmscmd->dsc$a_pointer, " ");
10943 } else if (image_name[0] != 0) {
10944 strcpy(vmscmd->dsc$a_pointer, image_name);
10945 strcat(vmscmd->dsc$a_pointer, " ");
10947 strcpy(vmscmd->dsc$a_pointer,"@");
10949 if (suggest_quote) *suggest_quote = 1;
10951 /* If there is an image name, use original command */
10952 if (image_name[0] == 0)
10953 strcat(vmscmd->dsc$a_pointer,resspec);
10956 while (*rest && isspace(*rest)) rest++;
10959 if (image_argv[0] != 0) {
10960 strcat(vmscmd->dsc$a_pointer,image_argv);
10961 strcat(vmscmd->dsc$a_pointer, " ");
10967 rest_len = strlen(rest);
10968 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10969 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10970 strcat(vmscmd->dsc$a_pointer,rest);
10972 retsts = CLI$_BUFOVF;
10974 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10976 PerlMem_free(vmsspec);
10977 PerlMem_free(resspec);
10978 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10984 /* It's either a DCL command or we couldn't find a suitable image */
10985 vmscmd->dsc$w_length = strlen(cmd);
10987 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
10988 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
10989 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
10992 PerlMem_free(resspec);
10993 PerlMem_free(vmsspec);
10995 /* check if it's a symbol (for quoting purposes) */
10996 if (suggest_quote && !*suggest_quote) {
10998 char equiv[LNM$C_NAMLENGTH];
10999 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
11000 eqvdsc.dsc$a_pointer = equiv;
11002 iss = lib$get_symbol(vmscmd,&eqvdsc);
11003 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
11005 if (!(retsts & 1)) {
11006 /* just hand off status values likely to be due to user error */
11007 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
11008 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
11009 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
11010 else { _ckvmssts_noperl(retsts); }
11013 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
11015 } /* end of setup_cmddsc() */
11018 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
11020 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
11026 if (vfork_called) { /* this follows a vfork - act Unixish */
11028 if (vfork_called < 0) {
11029 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
11032 else return do_aexec(really,mark,sp);
11034 /* no vfork - act VMSish */
11035 cmd = setup_argstr(aTHX_ really,mark,sp);
11036 exec_sts = vms_do_exec(cmd);
11037 Safefree(cmd); /* Clean up from setup_argstr() */
11042 } /* end of vms_do_aexec() */
11045 /* {{{bool vms_do_exec(char *cmd) */
11047 Perl_vms_do_exec(pTHX_ const char *cmd)
11049 struct dsc$descriptor_s *vmscmd;
11051 if (vfork_called) { /* this follows a vfork - act Unixish */
11053 if (vfork_called < 0) {
11054 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
11057 else return do_exec(cmd);
11060 { /* no vfork - act VMSish */
11061 unsigned long int retsts;
11064 TAINT_PROPER("exec");
11065 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
11066 retsts = lib$do_command(vmscmd);
11069 case RMS$_FNF: case RMS$_DNF:
11070 set_errno(ENOENT); break;
11072 set_errno(ENOTDIR); break;
11074 set_errno(ENODEV); break;
11076 set_errno(EACCES); break;
11078 set_errno(EINVAL); break;
11079 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11080 set_errno(E2BIG); break;
11081 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11082 _ckvmssts_noperl(retsts); /* fall through */
11083 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11084 set_errno(EVMSERR);
11086 set_vaxc_errno(retsts);
11087 if (ckWARN(WARN_EXEC)) {
11088 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
11089 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
11091 vms_execfree(vmscmd);
11096 } /* end of vms_do_exec() */
11099 int do_spawn2(pTHX_ const char *, int);
11102 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
11104 unsigned long int sts;
11110 /* We'll copy the (undocumented?) Win32 behavior and allow a
11111 * numeric first argument. But the only value we'll support
11112 * through do_aspawn is a value of 1, which means spawn without
11113 * waiting for completion -- other values are ignored.
11115 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
11117 flags = SvIVx(*mark);
11120 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
11121 flags = CLI$M_NOWAIT;
11125 cmd = setup_argstr(aTHX_ really, mark, sp);
11126 sts = do_spawn2(aTHX_ cmd, flags);
11127 /* pp_sys will clean up cmd */
11131 } /* end of do_aspawn() */
11135 /* {{{int do_spawn(char* cmd) */
11137 Perl_do_spawn(pTHX_ char* cmd)
11139 PERL_ARGS_ASSERT_DO_SPAWN;
11141 return do_spawn2(aTHX_ cmd, 0);
11145 /* {{{int do_spawn_nowait(char* cmd) */
11147 Perl_do_spawn_nowait(pTHX_ char* cmd)
11149 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
11151 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
11155 /* {{{int do_spawn2(char *cmd) */
11157 do_spawn2(pTHX_ const char *cmd, int flags)
11159 unsigned long int sts, substs;
11161 /* The caller of this routine expects to Safefree(PL_Cmd) */
11162 Newx(PL_Cmd,10,char);
11165 TAINT_PROPER("spawn");
11166 if (!cmd || !*cmd) {
11167 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
11170 case RMS$_FNF: case RMS$_DNF:
11171 set_errno(ENOENT); break;
11173 set_errno(ENOTDIR); break;
11175 set_errno(ENODEV); break;
11177 set_errno(EACCES); break;
11179 set_errno(EINVAL); break;
11180 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11181 set_errno(E2BIG); break;
11182 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11183 _ckvmssts_noperl(sts); /* fall through */
11184 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11185 set_errno(EVMSERR);
11187 set_vaxc_errno(sts);
11188 if (ckWARN(WARN_EXEC)) {
11189 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
11198 if (flags & CLI$M_NOWAIT)
11201 strcpy(mode, "nW");
11203 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
11206 /* sts will be the pid in the nowait case */
11209 } /* end of do_spawn2() */
11213 static unsigned int *sockflags, sockflagsize;
11216 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11217 * routines found in some versions of the CRTL can't deal with sockets.
11218 * We don't shim the other file open routines since a socket isn't
11219 * likely to be opened by a name.
11221 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11222 FILE *my_fdopen(int fd, const char *mode)
11224 FILE *fp = fdopen(fd, mode);
11227 unsigned int fdoff = fd / sizeof(unsigned int);
11228 Stat_t sbuf; /* native stat; we don't need flex_stat */
11229 if (!sockflagsize || fdoff > sockflagsize) {
11230 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
11231 else Newx (sockflags,fdoff+2,unsigned int);
11232 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11233 sockflagsize = fdoff + 2;
11235 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11236 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11245 * Clear the corresponding bit when the (possibly) socket stream is closed.
11246 * There still a small hole: we miss an implicit close which might occur
11247 * via freopen(). >> Todo
11249 /*{{{ int my_fclose(FILE *fp)*/
11250 int my_fclose(FILE *fp) {
11252 unsigned int fd = fileno(fp);
11253 unsigned int fdoff = fd / sizeof(unsigned int);
11255 if (sockflagsize && fdoff < sockflagsize)
11256 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11264 * A simple fwrite replacement which outputs itmsz*nitm chars without
11265 * introducing record boundaries every itmsz chars.
11266 * We are using fputs, which depends on a terminating null. We may
11267 * well be writing binary data, so we need to accommodate not only
11268 * data with nulls sprinkled in the middle but also data with no null
11271 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11273 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11275 register char *cp, *end, *cpd, *data;
11276 register unsigned int fd = fileno(dest);
11277 register unsigned int fdoff = fd / sizeof(unsigned int);
11279 int bufsize = itmsz * nitm + 1;
11281 if (fdoff < sockflagsize &&
11282 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11283 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11287 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11288 memcpy( data, src, itmsz*nitm );
11289 data[itmsz*nitm] = '\0';
11291 end = data + itmsz * nitm;
11292 retval = (int) nitm; /* on success return # items written */
11295 while (cpd <= end) {
11296 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11297 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11299 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11303 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11306 } /* end of my_fwrite() */
11309 /*{{{ int my_flush(FILE *fp)*/
11311 Perl_my_flush(pTHX_ FILE *fp)
11314 if ((res = fflush(fp)) == 0 && fp) {
11315 #ifdef VMS_DO_SOCKETS
11317 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11319 res = fsync(fileno(fp));
11322 * If the flush succeeded but set end-of-file, we need to clear
11323 * the error because our caller may check ferror(). BTW, this
11324 * probably means we just flushed an empty file.
11326 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11333 * Here are replacements for the following Unix routines in the VMS environment:
11334 * getpwuid Get information for a particular UIC or UID
11335 * getpwnam Get information for a named user
11336 * getpwent Get information for each user in the rights database
11337 * setpwent Reset search to the start of the rights database
11338 * endpwent Finish searching for users in the rights database
11340 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11341 * (defined in pwd.h), which contains the following fields:-
11343 * char *pw_name; Username (in lower case)
11344 * char *pw_passwd; Hashed password
11345 * unsigned int pw_uid; UIC
11346 * unsigned int pw_gid; UIC group number
11347 * char *pw_unixdir; Default device/directory (VMS-style)
11348 * char *pw_gecos; Owner name
11349 * char *pw_dir; Default device/directory (Unix-style)
11350 * char *pw_shell; Default CLI name (eg. DCL)
11352 * If the specified user does not exist, getpwuid and getpwnam return NULL.
11354 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11355 * not the UIC member number (eg. what's returned by getuid()),
11356 * getpwuid() can accept either as input (if uid is specified, the caller's
11357 * UIC group is used), though it won't recognise gid=0.
11359 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11360 * information about other users in your group or in other groups, respectively.
11361 * If the required privilege is not available, then these routines fill only
11362 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11365 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11368 /* sizes of various UAF record fields */
11369 #define UAI$S_USERNAME 12
11370 #define UAI$S_IDENT 31
11371 #define UAI$S_OWNER 31
11372 #define UAI$S_DEFDEV 31
11373 #define UAI$S_DEFDIR 63
11374 #define UAI$S_DEFCLI 31
11375 #define UAI$S_PWD 8
11377 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
11378 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11379 (uic).uic$v_group != UIC$K_WILD_GROUP)
11381 static char __empty[]= "";
11382 static struct passwd __passwd_empty=
11383 {(char *) __empty, (char *) __empty, 0, 0,
11384 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11385 static int contxt= 0;
11386 static struct passwd __pwdcache;
11387 static char __pw_namecache[UAI$S_IDENT+1];
11390 * This routine does most of the work extracting the user information.
11392 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11395 unsigned char length;
11396 char pw_gecos[UAI$S_OWNER+1];
11398 static union uicdef uic;
11400 unsigned char length;
11401 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11404 unsigned char length;
11405 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11408 unsigned char length;
11409 char pw_shell[UAI$S_DEFCLI+1];
11411 static char pw_passwd[UAI$S_PWD+1];
11413 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11414 struct dsc$descriptor_s name_desc;
11415 unsigned long int sts;
11417 static struct itmlst_3 itmlst[]= {
11418 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
11419 {sizeof(uic), UAI$_UIC, &uic, &luic},
11420 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
11421 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
11422 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
11423 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
11424 {0, 0, NULL, NULL}};
11426 name_desc.dsc$w_length= strlen(name);
11427 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11428 name_desc.dsc$b_class= DSC$K_CLASS_S;
11429 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11431 /* Note that sys$getuai returns many fields as counted strings. */
11432 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11433 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11434 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11436 else { _ckvmssts(sts); }
11437 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
11439 if ((int) owner.length < lowner) lowner= (int) owner.length;
11440 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11441 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11442 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11443 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11444 owner.pw_gecos[lowner]= '\0';
11445 defdev.pw_dir[ldefdev+ldefdir]= '\0';
11446 defcli.pw_shell[ldefcli]= '\0';
11447 if (valid_uic(uic)) {
11448 pwd->pw_uid= uic.uic$l_uic;
11449 pwd->pw_gid= uic.uic$v_group;
11452 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11453 pwd->pw_passwd= pw_passwd;
11454 pwd->pw_gecos= owner.pw_gecos;
11455 pwd->pw_dir= defdev.pw_dir;
11456 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11457 pwd->pw_shell= defcli.pw_shell;
11458 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11460 ldir= strlen(pwd->pw_unixdir) - 1;
11461 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11464 strcpy(pwd->pw_unixdir, pwd->pw_dir);
11465 if (!decc_efs_case_preserve)
11466 __mystrtolower(pwd->pw_unixdir);
11471 * Get information for a named user.
11473 /*{{{struct passwd *getpwnam(char *name)*/
11474 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
11476 struct dsc$descriptor_s name_desc;
11478 unsigned long int status, sts;
11480 __pwdcache = __passwd_empty;
11481 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11482 /* We still may be able to determine pw_uid and pw_gid */
11483 name_desc.dsc$w_length= strlen(name);
11484 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11485 name_desc.dsc$b_class= DSC$K_CLASS_S;
11486 name_desc.dsc$a_pointer= (char *) name;
11487 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11488 __pwdcache.pw_uid= uic.uic$l_uic;
11489 __pwdcache.pw_gid= uic.uic$v_group;
11492 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11493 set_vaxc_errno(sts);
11494 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11497 else { _ckvmssts(sts); }
11500 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
11501 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
11502 __pwdcache.pw_name= __pw_namecache;
11503 return &__pwdcache;
11504 } /* end of my_getpwnam() */
11508 * Get information for a particular UIC or UID.
11509 * Called by my_getpwent with uid=-1 to list all users.
11511 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11512 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
11514 const $DESCRIPTOR(name_desc,__pw_namecache);
11515 unsigned short lname;
11517 unsigned long int status;
11519 if (uid == (unsigned int) -1) {
11521 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11522 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11523 set_vaxc_errno(status);
11524 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11528 else { _ckvmssts(status); }
11529 } while (!valid_uic (uic));
11532 uic.uic$l_uic= uid;
11533 if (!uic.uic$v_group)
11534 uic.uic$v_group= PerlProc_getgid();
11535 if (valid_uic(uic))
11536 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11537 else status = SS$_IVIDENT;
11538 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11539 status == RMS$_PRV) {
11540 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11543 else { _ckvmssts(status); }
11545 __pw_namecache[lname]= '\0';
11546 __mystrtolower(__pw_namecache);
11548 __pwdcache = __passwd_empty;
11549 __pwdcache.pw_name = __pw_namecache;
11551 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11552 The identifier's value is usually the UIC, but it doesn't have to be,
11553 so if we can, we let fillpasswd update this. */
11554 __pwdcache.pw_uid = uic.uic$l_uic;
11555 __pwdcache.pw_gid = uic.uic$v_group;
11557 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11558 return &__pwdcache;
11560 } /* end of my_getpwuid() */
11564 * Get information for next user.
11566 /*{{{struct passwd *my_getpwent()*/
11567 struct passwd *Perl_my_getpwent(pTHX)
11569 return (my_getpwuid((unsigned int) -1));
11574 * Finish searching rights database for users.
11576 /*{{{void my_endpwent()*/
11577 void Perl_my_endpwent(pTHX)
11580 _ckvmssts(sys$finish_rdb(&contxt));
11586 #ifdef HOMEGROWN_POSIX_SIGNALS
11587 /* Signal handling routines, pulled into the core from POSIX.xs.
11589 * We need these for threads, so they've been rolled into the core,
11590 * rather than left in POSIX.xs.
11592 * (DRS, Oct 23, 1997)
11595 /* sigset_t is atomic under VMS, so these routines are easy */
11596 /*{{{int my_sigemptyset(sigset_t *) */
11597 int my_sigemptyset(sigset_t *set) {
11598 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11599 *set = 0; return 0;
11604 /*{{{int my_sigfillset(sigset_t *)*/
11605 int my_sigfillset(sigset_t *set) {
11607 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11608 for (i = 0; i < NSIG; i++) *set |= (1 << i);
11614 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
11615 int my_sigaddset(sigset_t *set, int sig) {
11616 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11617 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11618 *set |= (1 << (sig - 1));
11624 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
11625 int my_sigdelset(sigset_t *set, int sig) {
11626 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11627 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11628 *set &= ~(1 << (sig - 1));
11634 /*{{{int my_sigismember(sigset_t *set, int sig)*/
11635 int my_sigismember(sigset_t *set, int sig) {
11636 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11637 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11638 return *set & (1 << (sig - 1));
11643 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
11644 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
11647 /* If set and oset are both null, then things are badly wrong. Bail out. */
11648 if ((oset == NULL) && (set == NULL)) {
11649 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
11653 /* If set's null, then we're just handling a fetch. */
11655 tempmask = sigblock(0);
11660 tempmask = sigsetmask(*set);
11663 tempmask = sigblock(*set);
11666 tempmask = sigblock(0);
11667 sigsetmask(*oset & ~tempmask);
11670 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11675 /* Did they pass us an oset? If so, stick our holding mask into it */
11682 #endif /* HOMEGROWN_POSIX_SIGNALS */
11685 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11686 * my_utime(), and flex_stat(), all of which operate on UTC unless
11687 * VMSISH_TIMES is true.
11689 /* method used to handle UTC conversions:
11690 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
11692 static int gmtime_emulation_type;
11693 /* number of secs to add to UTC POSIX-style time to get local time */
11694 static long int utc_offset_secs;
11696 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11697 * in vmsish.h. #undef them here so we can call the CRTL routines
11706 * DEC C previous to 6.0 corrupts the behavior of the /prefix
11707 * qualifier with the extern prefix pragma. This provisional
11708 * hack circumvents this prefix pragma problem in previous
11711 #if defined(__VMS_VER) && __VMS_VER >= 70000000
11712 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
11713 # pragma __extern_prefix save
11714 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
11715 # define gmtime decc$__utctz_gmtime
11716 # define localtime decc$__utctz_localtime
11717 # define time decc$__utc_time
11718 # pragma __extern_prefix restore
11720 struct tm *gmtime(), *localtime();
11726 static time_t toutc_dst(time_t loc) {
11729 if ((rsltmp = localtime(&loc)) == NULL) return -1;
11730 loc -= utc_offset_secs;
11731 if (rsltmp->tm_isdst) loc -= 3600;
11734 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11735 ((gmtime_emulation_type || my_time(NULL)), \
11736 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11737 ((secs) - utc_offset_secs))))
11739 static time_t toloc_dst(time_t utc) {
11742 utc += utc_offset_secs;
11743 if ((rsltmp = localtime(&utc)) == NULL) return -1;
11744 if (rsltmp->tm_isdst) utc += 3600;
11747 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11748 ((gmtime_emulation_type || my_time(NULL)), \
11749 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11750 ((secs) + utc_offset_secs))))
11752 #ifndef RTL_USES_UTC
11755 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
11756 DST starts on 1st sun of april at 02:00 std time
11757 ends on last sun of october at 02:00 dst time
11758 see the UCX management command reference, SET CONFIG TIMEZONE
11759 for formatting info.
11761 No, it's not as general as it should be, but then again, NOTHING
11762 will handle UK times in a sensible way.
11767 parse the DST start/end info:
11768 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
11772 tz_parse_startend(char *s, struct tm *w, int *past)
11774 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
11775 int ly, dozjd, d, m, n, hour, min, sec, j, k;
11780 if (!past) return 0;
11783 if (w->tm_year % 4 == 0) ly = 1;
11784 if (w->tm_year % 100 == 0) ly = 0;
11785 if (w->tm_year+1900 % 400 == 0) ly = 1;
11788 dozjd = isdigit(*s);
11789 if (*s == 'J' || *s == 'j' || dozjd) {
11790 if (!dozjd && !isdigit(*++s)) return 0;
11793 d = d*10 + *s++ - '0';
11795 d = d*10 + *s++ - '0';
11798 if (d == 0) return 0;
11799 if (d > 366) return 0;
11801 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
11804 } else if (*s == 'M' || *s == 'm') {
11805 if (!isdigit(*++s)) return 0;
11807 if (isdigit(*s)) m = 10*m + *s++ - '0';
11808 if (*s != '.') return 0;
11809 if (!isdigit(*++s)) return 0;
11811 if (n < 1 || n > 5) return 0;
11812 if (*s != '.') return 0;
11813 if (!isdigit(*++s)) return 0;
11815 if (d > 6) return 0;
11819 if (!isdigit(*++s)) return 0;
11821 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
11823 if (!isdigit(*++s)) return 0;
11825 if (isdigit(*s)) min = 10*min + *s++ - '0';
11827 if (!isdigit(*++s)) return 0;
11829 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
11839 if (w->tm_yday < d) goto before;
11840 if (w->tm_yday > d) goto after;
11842 if (w->tm_mon+1 < m) goto before;
11843 if (w->tm_mon+1 > m) goto after;
11845 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
11846 k = d - j; /* mday of first d */
11847 if (k <= 0) k += 7;
11848 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
11849 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
11850 if (w->tm_mday < k) goto before;
11851 if (w->tm_mday > k) goto after;
11854 if (w->tm_hour < hour) goto before;
11855 if (w->tm_hour > hour) goto after;
11856 if (w->tm_min < min) goto before;
11857 if (w->tm_min > min) goto after;
11858 if (w->tm_sec < sec) goto before;
11872 /* parse the offset: (+|-)hh[:mm[:ss]] */
11875 tz_parse_offset(char *s, int *offset)
11877 int hour = 0, min = 0, sec = 0;
11880 if (!offset) return 0;
11882 if (*s == '-') {neg++; s++;}
11883 if (*s == '+') s++;
11884 if (!isdigit(*s)) return 0;
11886 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
11887 if (hour > 24) return 0;
11889 if (!isdigit(*++s)) return 0;
11891 if (isdigit(*s)) min = min*10 + (*s++ - '0');
11892 if (min > 59) return 0;
11894 if (!isdigit(*++s)) return 0;
11896 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
11897 if (sec > 59) return 0;
11901 *offset = (hour*60+min)*60 + sec;
11902 if (neg) *offset = -*offset;
11907 input time is w, whatever type of time the CRTL localtime() uses.
11908 sets dst, the zone, and the gmtoff (seconds)
11910 caches the value of TZ and UCX$TZ env variables; note that
11911 my_setenv looks for these and sets a flag if they're changed
11914 We have to watch out for the "australian" case (dst starts in
11915 october, ends in april)...flagged by "reverse" and checked by
11916 scanning through the months of the previous year.
11921 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
11926 char *dstzone, *tz, *s_start, *s_end;
11927 int std_off, dst_off, isdst;
11928 int y, dststart, dstend;
11929 static char envtz[1025]; /* longer than any logical, symbol, ... */
11930 static char ucxtz[1025];
11931 static char reversed = 0;
11937 reversed = -1; /* flag need to check */
11938 envtz[0] = ucxtz[0] = '\0';
11939 tz = my_getenv("TZ",0);
11940 if (tz) strcpy(envtz, tz);
11941 tz = my_getenv("UCX$TZ",0);
11942 if (tz) strcpy(ucxtz, tz);
11943 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
11946 if (!*tz) tz = ucxtz;
11949 while (isalpha(*s)) s++;
11950 s = tz_parse_offset(s, &std_off);
11952 if (!*s) { /* no DST, hurray we're done! */
11958 while (isalpha(*s)) s++;
11959 s2 = tz_parse_offset(s, &dst_off);
11963 dst_off = std_off - 3600;
11966 if (!*s) { /* default dst start/end?? */
11967 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
11968 s = strchr(ucxtz,',');
11970 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
11972 if (*s != ',') return 0;
11975 when = _toutc(when); /* convert to utc */
11976 when = when - std_off; /* convert to pseudolocal time*/
11978 w2 = localtime(&when);
11981 s = tz_parse_startend(s_start,w2,&dststart);
11983 if (*s != ',') return 0;
11986 when = _toutc(when); /* convert to utc */
11987 when = when - dst_off; /* convert to pseudolocal time*/
11988 w2 = localtime(&when);
11989 if (w2->tm_year != y) { /* spans a year, just check one time */
11990 when += dst_off - std_off;
11991 w2 = localtime(&when);
11994 s = tz_parse_startend(s_end,w2,&dstend);
11997 if (reversed == -1) { /* need to check if start later than end */
12001 if (when < 2*365*86400) {
12002 when += 2*365*86400;
12006 w2 =localtime(&when);
12007 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
12009 for (j = 0; j < 12; j++) {
12010 w2 =localtime(&when);
12011 tz_parse_startend(s_start,w2,&ds);
12012 tz_parse_startend(s_end,w2,&de);
12013 if (ds != de) break;
12017 if (de && !ds) reversed = 1;
12020 isdst = dststart && !dstend;
12021 if (reversed) isdst = dststart || !dstend;
12024 if (dst) *dst = isdst;
12025 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
12026 if (isdst) tz = dstzone;
12028 while(isalpha(*tz)) *zone++ = *tz++;
12034 #endif /* !RTL_USES_UTC */
12036 /* my_time(), my_localtime(), my_gmtime()
12037 * By default traffic in UTC time values, using CRTL gmtime() or
12038 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
12039 * Note: We need to use these functions even when the CRTL has working
12040 * UTC support, since they also handle C<use vmsish qw(times);>
12042 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
12043 * Modified by Charles Bailey <bailey@newman.upenn.edu>
12046 /*{{{time_t my_time(time_t *timep)*/
12047 time_t Perl_my_time(pTHX_ time_t *timep)
12052 if (gmtime_emulation_type == 0) {
12054 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
12055 /* results of calls to gmtime() and localtime() */
12056 /* for same &base */
12058 gmtime_emulation_type++;
12059 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
12060 char off[LNM$C_NAMLENGTH+1];;
12062 gmtime_emulation_type++;
12063 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
12064 gmtime_emulation_type++;
12065 utc_offset_secs = 0;
12066 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
12068 else { utc_offset_secs = atol(off); }
12070 else { /* We've got a working gmtime() */
12071 struct tm gmt, local;
12074 tm_p = localtime(&base);
12076 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
12077 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
12078 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
12079 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
12084 # ifdef VMSISH_TIME
12085 # ifdef RTL_USES_UTC
12086 if (VMSISH_TIME) when = _toloc(when);
12088 if (!VMSISH_TIME) when = _toutc(when);
12091 if (timep != NULL) *timep = when;
12094 } /* end of my_time() */
12098 /*{{{struct tm *my_gmtime(const time_t *timep)*/
12100 Perl_my_gmtime(pTHX_ const time_t *timep)
12106 if (timep == NULL) {
12107 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12110 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
12113 # ifdef VMSISH_TIME
12114 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
12116 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
12117 return gmtime(&when);
12119 /* CRTL localtime() wants local time as input, so does no tz correction */
12120 rsltmp = localtime(&when);
12121 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
12124 } /* end of my_gmtime() */
12128 /*{{{struct tm *my_localtime(const time_t *timep)*/
12130 Perl_my_localtime(pTHX_ const time_t *timep)
12132 time_t when, whenutc;
12136 if (timep == NULL) {
12137 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12140 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
12141 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
12144 # ifdef RTL_USES_UTC
12145 # ifdef VMSISH_TIME
12146 if (VMSISH_TIME) when = _toutc(when);
12148 /* CRTL localtime() wants UTC as input, does tz correction itself */
12149 return localtime(&when);
12151 # else /* !RTL_USES_UTC */
12153 # ifdef VMSISH_TIME
12154 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
12155 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
12158 #ifndef RTL_USES_UTC
12159 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
12160 when = whenutc - offset; /* pseudolocal time*/
12163 /* CRTL localtime() wants local time as input, so does no tz correction */
12164 rsltmp = localtime(&when);
12165 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
12169 } /* end of my_localtime() */
12172 /* Reset definitions for later calls */
12173 #define gmtime(t) my_gmtime(t)
12174 #define localtime(t) my_localtime(t)
12175 #define time(t) my_time(t)
12178 /* my_utime - update modification/access time of a file
12180 * VMS 7.3 and later implementation
12181 * Only the UTC translation is home-grown. The rest is handled by the
12182 * CRTL utime(), which will take into account the relevant feature
12183 * logicals and ODS-5 volume characteristics for true access times.
12185 * pre VMS 7.3 implementation:
12186 * The calling sequence is identical to POSIX utime(), but under
12187 * VMS with ODS-2, only the modification time is changed; ODS-2 does
12188 * not maintain access times. Restrictions differ from the POSIX
12189 * definition in that the time can be changed as long as the
12190 * caller has permission to execute the necessary IO$_MODIFY $QIO;
12191 * no separate checks are made to insure that the caller is the
12192 * owner of the file or has special privs enabled.
12193 * Code here is based on Joe Meadows' FILE utility.
12197 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
12198 * to VMS epoch (01-JAN-1858 00:00:00.00)
12199 * in 100 ns intervals.
12201 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
12203 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
12204 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
12206 #if __CRTL_VER >= 70300000
12207 struct utimbuf utc_utimes, *utc_utimesp;
12209 if (utimes != NULL) {
12210 utc_utimes.actime = utimes->actime;
12211 utc_utimes.modtime = utimes->modtime;
12212 # ifdef VMSISH_TIME
12213 /* If input was local; convert to UTC for sys svc */
12215 utc_utimes.actime = _toutc(utimes->actime);
12216 utc_utimes.modtime = _toutc(utimes->modtime);
12219 utc_utimesp = &utc_utimes;
12222 utc_utimesp = NULL;
12225 return utime(file, utc_utimesp);
12227 #else /* __CRTL_VER < 70300000 */
12231 long int bintime[2], len = 2, lowbit, unixtime,
12232 secscale = 10000000; /* seconds --> 100 ns intervals */
12233 unsigned long int chan, iosb[2], retsts;
12234 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
12235 struct FAB myfab = cc$rms_fab;
12236 struct NAM mynam = cc$rms_nam;
12237 #if defined (__DECC) && defined (__VAX)
12238 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
12239 * at least through VMS V6.1, which causes a type-conversion warning.
12241 # pragma message save
12242 # pragma message disable cvtdiftypes
12244 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
12245 struct fibdef myfib;
12246 #if defined (__DECC) && defined (__VAX)
12247 /* This should be right after the declaration of myatr, but due
12248 * to a bug in VAX DEC C, this takes effect a statement early.
12250 # pragma message restore
12252 /* cast ok for read only parameter */
12253 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
12254 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
12255 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
12257 if (file == NULL || *file == '\0') {
12258 SETERRNO(ENOENT, LIB$_INVARG);
12262 /* Convert to VMS format ensuring that it will fit in 255 characters */
12263 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
12264 SETERRNO(ENOENT, LIB$_INVARG);
12267 if (utimes != NULL) {
12268 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
12269 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
12270 * Since time_t is unsigned long int, and lib$emul takes a signed long int
12271 * as input, we force the sign bit to be clear by shifting unixtime right
12272 * one bit, then multiplying by an extra factor of 2 in lib$emul().
12274 lowbit = (utimes->modtime & 1) ? secscale : 0;
12275 unixtime = (long int) utimes->modtime;
12276 # ifdef VMSISH_TIME
12277 /* If input was UTC; convert to local for sys svc */
12278 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
12280 unixtime >>= 1; secscale <<= 1;
12281 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
12282 if (!(retsts & 1)) {
12283 SETERRNO(EVMSERR, retsts);
12286 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
12287 if (!(retsts & 1)) {
12288 SETERRNO(EVMSERR, retsts);
12293 /* Just get the current time in VMS format directly */
12294 retsts = sys$gettim(bintime);
12295 if (!(retsts & 1)) {
12296 SETERRNO(EVMSERR, retsts);
12301 myfab.fab$l_fna = vmsspec;
12302 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
12303 myfab.fab$l_nam = &mynam;
12304 mynam.nam$l_esa = esa;
12305 mynam.nam$b_ess = (unsigned char) sizeof esa;
12306 mynam.nam$l_rsa = rsa;
12307 mynam.nam$b_rss = (unsigned char) sizeof rsa;
12308 if (decc_efs_case_preserve)
12309 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
12311 /* Look for the file to be affected, letting RMS parse the file
12312 * specification for us as well. I have set errno using only
12313 * values documented in the utime() man page for VMS POSIX.
12315 retsts = sys$parse(&myfab,0,0);
12316 if (!(retsts & 1)) {
12317 set_vaxc_errno(retsts);
12318 if (retsts == RMS$_PRV) set_errno(EACCES);
12319 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
12320 else set_errno(EVMSERR);
12323 retsts = sys$search(&myfab,0,0);
12324 if (!(retsts & 1)) {
12325 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
12326 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
12327 set_vaxc_errno(retsts);
12328 if (retsts == RMS$_PRV) set_errno(EACCES);
12329 else if (retsts == RMS$_FNF) set_errno(ENOENT);
12330 else set_errno(EVMSERR);
12334 devdsc.dsc$w_length = mynam.nam$b_dev;
12335 /* cast ok for read only parameter */
12336 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
12338 retsts = sys$assign(&devdsc,&chan,0,0);
12339 if (!(retsts & 1)) {
12340 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
12341 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
12342 set_vaxc_errno(retsts);
12343 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
12344 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
12345 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
12346 else set_errno(EVMSERR);
12350 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
12351 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
12353 memset((void *) &myfib, 0, sizeof myfib);
12354 #if defined(__DECC) || defined(__DECCXX)
12355 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
12356 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
12357 /* This prevents the revision time of the file being reset to the current
12358 * time as a result of our IO$_MODIFY $QIO. */
12359 myfib.fib$l_acctl = FIB$M_NORECORD;
12361 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
12362 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
12363 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
12365 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
12366 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
12367 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
12368 _ckvmssts(sys$dassgn(chan));
12369 if (retsts & 1) retsts = iosb[0];
12370 if (!(retsts & 1)) {
12371 set_vaxc_errno(retsts);
12372 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12373 else set_errno(EVMSERR);
12379 #endif /* #if __CRTL_VER >= 70300000 */
12381 } /* end of my_utime() */
12385 * flex_stat, flex_lstat, flex_fstat
12386 * basic stat, but gets it right when asked to stat
12387 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
12390 #ifndef _USE_STD_STAT
12391 /* encode_dev packs a VMS device name string into an integer to allow
12392 * simple comparisons. This can be used, for example, to check whether two
12393 * files are located on the same device, by comparing their encoded device
12394 * names. Even a string comparison would not do, because stat() reuses the
12395 * device name buffer for each call; so without encode_dev, it would be
12396 * necessary to save the buffer and use strcmp (this would mean a number of
12397 * changes to the standard Perl code, to say nothing of what a Perl script
12398 * would have to do.
12400 * The device lock id, if it exists, should be unique (unless perhaps compared
12401 * with lock ids transferred from other nodes). We have a lock id if the disk is
12402 * mounted cluster-wide, which is when we tend to get long (host-qualified)
12403 * device names. Thus we use the lock id in preference, and only if that isn't
12404 * available, do we try to pack the device name into an integer (flagged by
12405 * the sign bit (LOCKID_MASK) being set).
12407 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
12408 * name and its encoded form, but it seems very unlikely that we will find
12409 * two files on different disks that share the same encoded device names,
12410 * and even more remote that they will share the same file id (if the test
12411 * is to check for the same file).
12413 * A better method might be to use sys$device_scan on the first call, and to
12414 * search for the device, returning an index into the cached array.
12415 * The number returned would be more intelligible.
12416 * This is probably not worth it, and anyway would take quite a bit longer
12417 * on the first call.
12419 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
12420 static mydev_t encode_dev (pTHX_ const char *dev)
12423 unsigned long int f;
12428 if (!dev || !dev[0]) return 0;
12432 struct dsc$descriptor_s dev_desc;
12433 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
12435 /* For cluster-mounted disks, the disk lock identifier is unique, so we
12436 can try that first. */
12437 dev_desc.dsc$w_length = strlen (dev);
12438 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
12439 dev_desc.dsc$b_class = DSC$K_CLASS_S;
12440 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
12441 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
12442 if (!$VMS_STATUS_SUCCESS(status)) {
12444 case SS$_NOSUCHDEV:
12445 SETERRNO(ENODEV, status);
12451 if (lockid) return (lockid & ~LOCKID_MASK);
12455 /* Otherwise we try to encode the device name */
12459 for (q = dev + strlen(dev); q--; q >= dev) {
12464 else if (isalpha (toupper (*q)))
12465 c= toupper (*q) - 'A' + (char)10;
12467 continue; /* Skip '$'s */
12469 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
12471 enc += f * (unsigned long int) c;
12473 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
12475 } /* end of encode_dev() */
12476 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12477 device_no = encode_dev(aTHX_ devname)
12479 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12480 device_no = new_dev_no
12484 is_null_device(name)
12487 if (decc_bug_devnull != 0) {
12488 if (strncmp("/dev/null", name, 9) == 0)
12491 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
12492 The underscore prefix, controller letter, and unit number are
12493 independently optional; for our purposes, the colon punctuation
12494 is not. The colon can be trailed by optional directory and/or
12495 filename, but two consecutive colons indicates a nodename rather
12496 than a device. [pr] */
12497 if (*name == '_') ++name;
12498 if (tolower(*name++) != 'n') return 0;
12499 if (tolower(*name++) != 'l') return 0;
12500 if (tolower(*name) == 'a') ++name;
12501 if (*name == '0') ++name;
12502 return (*name++ == ':') && (*name != ':');
12506 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
12509 Perl_cando_by_name_int
12510 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
12512 char usrname[L_cuserid];
12513 struct dsc$descriptor_s usrdsc =
12514 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
12515 char *vmsname = NULL, *fileified = NULL;
12516 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
12517 unsigned short int retlen, trnlnm_iter_count;
12518 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12519 union prvdef curprv;
12520 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
12521 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
12522 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
12523 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
12524 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
12526 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
12528 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12530 static int profile_context = -1;
12532 if (!fname || !*fname) return FALSE;
12534 /* Make sure we expand logical names, since sys$check_access doesn't */
12535 fileified = PerlMem_malloc(VMS_MAXRSS);
12536 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12537 if (!strpbrk(fname,"/]>:")) {
12538 strcpy(fileified,fname);
12539 trnlnm_iter_count = 0;
12540 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
12541 trnlnm_iter_count++;
12542 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
12547 vmsname = PerlMem_malloc(VMS_MAXRSS);
12548 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12549 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
12550 /* Don't know if already in VMS format, so make sure */
12551 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
12552 PerlMem_free(fileified);
12553 PerlMem_free(vmsname);
12558 strcpy(vmsname,fname);
12561 /* sys$check_access needs a file spec, not a directory spec.
12562 * flex_stat now will handle a null thread context during startup.
12565 retlen = namdsc.dsc$w_length = strlen(vmsname);
12566 if (vmsname[retlen-1] == ']'
12567 || vmsname[retlen-1] == '>'
12568 || vmsname[retlen-1] == ':'
12569 || (!Perl_flex_stat_int(NULL, vmsname, &st, 1) &&
12570 S_ISDIR(st.st_mode))) {
12572 if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
12573 PerlMem_free(fileified);
12574 PerlMem_free(vmsname);
12583 retlen = namdsc.dsc$w_length = strlen(fname);
12584 namdsc.dsc$a_pointer = (char *)fname;
12587 case S_IXUSR: case S_IXGRP: case S_IXOTH:
12588 access = ARM$M_EXECUTE;
12589 flags = CHP$M_READ;
12591 case S_IRUSR: case S_IRGRP: case S_IROTH:
12592 access = ARM$M_READ;
12593 flags = CHP$M_READ | CHP$M_USEREADALL;
12595 case S_IWUSR: case S_IWGRP: case S_IWOTH:
12596 access = ARM$M_WRITE;
12597 flags = CHP$M_READ | CHP$M_WRITE;
12599 case S_IDUSR: case S_IDGRP: case S_IDOTH:
12600 access = ARM$M_DELETE;
12601 flags = CHP$M_READ | CHP$M_WRITE;
12604 if (fileified != NULL)
12605 PerlMem_free(fileified);
12606 if (vmsname != NULL)
12607 PerlMem_free(vmsname);
12611 /* Before we call $check_access, create a user profile with the current
12612 * process privs since otherwise it just uses the default privs from the
12613 * UAF and might give false positives or negatives. This only works on
12614 * VMS versions v6.0 and later since that's when sys$create_user_profile
12615 * became available.
12618 /* get current process privs and username */
12619 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
12620 _ckvmssts_noperl(iosb[0]);
12622 #if defined(__VMS_VER) && __VMS_VER >= 60000000
12624 /* find out the space required for the profile */
12625 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
12626 &usrprodsc.dsc$w_length,&profile_context));
12628 /* allocate space for the profile and get it filled in */
12629 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
12630 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12631 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
12632 &usrprodsc.dsc$w_length,&profile_context));
12634 /* use the profile to check access to the file; free profile & analyze results */
12635 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
12636 PerlMem_free(usrprodsc.dsc$a_pointer);
12637 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
12641 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
12645 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
12646 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
12647 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
12648 set_vaxc_errno(retsts);
12649 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12650 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12651 else set_errno(ENOENT);
12652 if (fileified != NULL)
12653 PerlMem_free(fileified);
12654 if (vmsname != NULL)
12655 PerlMem_free(vmsname);
12658 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12659 if (fileified != NULL)
12660 PerlMem_free(fileified);
12661 if (vmsname != NULL)
12662 PerlMem_free(vmsname);
12665 _ckvmssts_noperl(retsts);
12667 if (fileified != NULL)
12668 PerlMem_free(fileified);
12669 if (vmsname != NULL)
12670 PerlMem_free(vmsname);
12671 return FALSE; /* Should never get here */
12675 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
12676 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12677 * subset of the applicable information.
12680 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12682 return cando_by_name_int
12683 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12684 } /* end of cando() */
12688 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12690 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12692 return cando_by_name_int(bit, effective, fname, 0);
12694 } /* end of cando_by_name() */
12698 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12700 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12702 if (!fstat(fd, &statbufp->crtl_stat)) {
12704 char *vms_filename;
12705 vms_filename = PerlMem_malloc(VMS_MAXRSS);
12706 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12708 /* Save name for cando by name in VMS format */
12709 cptr = getname(fd, vms_filename, 1);
12711 /* This should not happen, but just in case */
12712 if (cptr == NULL) {
12713 statbufp->st_devnam[0] = 0;
12716 /* Make sure that the saved name fits in 255 characters */
12717 cptr = int_rmsexpand_vms
12719 statbufp->st_devnam,
12722 statbufp->st_devnam[0] = 0;
12724 PerlMem_free(vms_filename);
12726 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12728 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12730 # ifdef RTL_USES_UTC
12731 # ifdef VMSISH_TIME
12733 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12734 statbufp->st_atime = _toloc(statbufp->st_atime);
12735 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12739 # ifdef VMSISH_TIME
12740 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12744 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12745 statbufp->st_atime = _toutc(statbufp->st_atime);
12746 statbufp->st_ctime = _toutc(statbufp->st_ctime);
12753 } /* end of flex_fstat() */
12756 #if !defined(__VAX) && __CRTL_VER >= 80200000
12764 #define lstat(_x, _y) stat(_x, _y)
12767 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
12770 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12774 const char *save_spec;
12785 if (decc_bug_devnull != 0) {
12786 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12787 memset(statbufp,0,sizeof *statbufp);
12788 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12789 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12790 statbufp->st_uid = 0x00010001;
12791 statbufp->st_gid = 0x0001;
12792 time((time_t *)&statbufp->st_mtime);
12793 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12798 /* Try for a directory name first. If fspec contains a filename without
12799 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12800 * and sea:[wine.dark]water. exist, we prefer the directory here.
12801 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12802 * not sea:[wine.dark]., if the latter exists. If the intended target is
12803 * the file with null type, specify this by calling flex_stat() with
12804 * a '.' at the end of fspec.
12806 * If we are in Posix filespec mode, accept the filename as is.
12810 fileified = PerlMem_malloc(VMS_MAXRSS);
12811 if (fileified == NULL)
12812 _ckvmssts_noperl(SS$_INSFMEM);
12814 temp_fspec = PerlMem_malloc(VMS_MAXRSS);
12815 if (temp_fspec == NULL)
12816 _ckvmssts_noperl(SS$_INSFMEM);
12818 strcpy(temp_fspec, fspec);
12822 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12823 if (decc_posix_compliant_pathnames == 0) {
12826 /* We may be able to optimize this, but in order for fileify_dirspec to
12827 * always return a usuable answer, we have to call vmspath first to
12828 * make sure that it is in VMS directory format, as stat/lstat on 8.3
12829 * can not handle directories in unix format that it does not have read
12830 * access to. Vmspath handles the case where a bare name which could be
12831 * a logical name gets passed.
12833 ret_spec = int_tovmspath(fspec, temp_fspec, NULL);
12834 if (ret_spec != NULL) {
12835 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
12836 if (ret_spec != NULL) {
12837 if (lstat_flag == 0)
12838 retval = stat(fileified, &statbufp->crtl_stat);
12840 retval = lstat(fileified, &statbufp->crtl_stat);
12841 save_spec = fileified;
12845 if (retval && vms_bug_stat_filename) {
12847 /* We should try again as a vmsified file specification */
12848 /* However Perl traditionally has not done this, which */
12849 /* causes problems with existing tests */
12851 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12852 if (ret_spec != NULL) {
12853 if (lstat_flag == 0)
12854 retval = stat(temp_fspec, &statbufp->crtl_stat);
12856 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12857 save_spec = temp_fspec;
12862 /* Last chance - allow multiple dots with out EFS CHARSET */
12863 /* The CRTL stat() falls down hard on multi-dot filenames in unix
12864 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12865 * enable it if it isn't already.
12867 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12868 if (!decc_efs_charset && (decc_efs_charset_index > 0))
12869 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12871 if (lstat_flag == 0)
12872 retval = stat(fspec, &statbufp->crtl_stat);
12874 retval = lstat(fspec, &statbufp->crtl_stat);
12876 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12877 if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12878 decc$feature_set_value(decc_efs_charset_index, 1, 0);
12884 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12886 if (lstat_flag == 0)
12887 retval = stat(temp_fspec, &statbufp->crtl_stat);
12889 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12890 save_spec = temp_fspec;
12894 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12895 /* As you were... */
12896 if (!decc_efs_charset)
12897 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12902 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12904 /* If this is an lstat, do not follow the link */
12906 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12908 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12909 /* If we used the efs_hack above, we must also use it here for */
12910 /* perl_cando to work */
12911 if (efs_hack && (decc_efs_charset_index > 0)) {
12912 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12915 cptr = int_rmsexpand_tovms(save_spec, statbufp->st_devnam, rmsex_flags);
12916 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12917 if (efs_hack && (decc_efs_charset_index > 0)) {
12918 decc$feature_set_value(decc_efs_charset, 1, 0);
12922 /* Fix me: If this is NULL then stat found a file, and we could */
12923 /* not convert the specification to VMS - Should never happen */
12925 statbufp->st_devnam[0] = 0;
12927 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12929 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12930 # ifdef RTL_USES_UTC
12931 # ifdef VMSISH_TIME
12933 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12934 statbufp->st_atime = _toloc(statbufp->st_atime);
12935 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12939 # ifdef VMSISH_TIME
12940 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12944 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12945 statbufp->st_atime = _toutc(statbufp->st_atime);
12946 statbufp->st_ctime = _toutc(statbufp->st_ctime);
12950 /* If we were successful, leave errno where we found it */
12951 if (retval == 0) RESTORE_ERRNO;
12954 } /* end of flex_stat_int() */
12957 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12959 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12961 return flex_stat_int(fspec, statbufp, 0);
12965 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12967 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12969 return flex_stat_int(fspec, statbufp, 1);
12974 /*{{{char *my_getlogin()*/
12975 /* VMS cuserid == Unix getlogin, except calling sequence */
12979 static char user[L_cuserid];
12980 return cuserid(user);
12985 /* rmscopy - copy a file using VMS RMS routines
12987 * Copies contents and attributes of spec_in to spec_out, except owner
12988 * and protection information. Name and type of spec_in are used as
12989 * defaults for spec_out. The third parameter specifies whether rmscopy()
12990 * should try to propagate timestamps from the input file to the output file.
12991 * If it is less than 0, no timestamps are preserved. If it is 0, then
12992 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12993 * propagated to the output file at creation iff the output file specification
12994 * did not contain an explicit name or type, and the revision date is always
12995 * updated at the end of the copy operation. If it is greater than 0, then
12996 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12997 * other than the revision date should be propagated, and bit 1 indicates
12998 * that the revision date should be propagated.
13000 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
13002 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
13003 * Incorporates, with permission, some code from EZCOPY by Tim Adye
13004 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
13005 * as part of the Perl standard distribution under the terms of the
13006 * GNU General Public License or the Perl Artistic License. Copies
13007 * of each may be found in the Perl standard distribution.
13009 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
13011 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
13013 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
13014 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
13015 unsigned long int i, sts, sts2;
13017 struct FAB fab_in, fab_out;
13018 struct RAB rab_in, rab_out;
13019 rms_setup_nam(nam);
13020 rms_setup_nam(nam_out);
13021 struct XABDAT xabdat;
13022 struct XABFHC xabfhc;
13023 struct XABRDT xabrdt;
13024 struct XABSUM xabsum;
13026 vmsin = PerlMem_malloc(VMS_MAXRSS);
13027 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13028 vmsout = PerlMem_malloc(VMS_MAXRSS);
13029 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13030 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
13031 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
13032 PerlMem_free(vmsin);
13033 PerlMem_free(vmsout);
13034 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13038 esa = PerlMem_malloc(VMS_MAXRSS);
13039 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13041 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13042 esal = PerlMem_malloc(VMS_MAXRSS);
13043 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13045 fab_in = cc$rms_fab;
13046 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
13047 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
13048 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
13049 fab_in.fab$l_fop = FAB$M_SQO;
13050 rms_bind_fab_nam(fab_in, nam);
13051 fab_in.fab$l_xab = (void *) &xabdat;
13053 rsa = PerlMem_malloc(VMS_MAXRSS);
13054 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13056 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13057 rsal = PerlMem_malloc(VMS_MAXRSS);
13058 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13060 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
13061 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
13062 rms_nam_esl(nam) = 0;
13063 rms_nam_rsl(nam) = 0;
13064 rms_nam_esll(nam) = 0;
13065 rms_nam_rsll(nam) = 0;
13066 #ifdef NAM$M_NO_SHORT_UPCASE
13067 if (decc_efs_case_preserve)
13068 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
13071 xabdat = cc$rms_xabdat; /* To get creation date */
13072 xabdat.xab$l_nxt = (void *) &xabfhc;
13074 xabfhc = cc$rms_xabfhc; /* To get record length */
13075 xabfhc.xab$l_nxt = (void *) &xabsum;
13077 xabsum = cc$rms_xabsum; /* To get key and area information */
13079 if (!((sts = sys$open(&fab_in)) & 1)) {
13080 PerlMem_free(vmsin);
13081 PerlMem_free(vmsout);
13084 PerlMem_free(esal);
13087 PerlMem_free(rsal);
13088 set_vaxc_errno(sts);
13090 case RMS$_FNF: case RMS$_DNF:
13091 set_errno(ENOENT); break;
13093 set_errno(ENOTDIR); break;
13095 set_errno(ENODEV); break;
13097 set_errno(EINVAL); break;
13099 set_errno(EACCES); break;
13101 set_errno(EVMSERR);
13108 fab_out.fab$w_ifi = 0;
13109 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
13110 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
13111 fab_out.fab$l_fop = FAB$M_SQO;
13112 rms_bind_fab_nam(fab_out, nam_out);
13113 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
13114 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
13115 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
13116 esa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
13117 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13118 rsa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
13119 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13122 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13123 esal_out = PerlMem_malloc(VMS_MAXRSS);
13124 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13125 rsal_out = PerlMem_malloc(VMS_MAXRSS);
13126 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13128 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
13129 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
13131 if (preserve_dates == 0) { /* Act like DCL COPY */
13132 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
13133 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
13134 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
13135 PerlMem_free(vmsin);
13136 PerlMem_free(vmsout);
13139 PerlMem_free(esal);
13142 PerlMem_free(rsal);
13143 PerlMem_free(esa_out);
13144 if (esal_out != NULL)
13145 PerlMem_free(esal_out);
13146 PerlMem_free(rsa_out);
13147 if (rsal_out != NULL)
13148 PerlMem_free(rsal_out);
13149 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
13150 set_vaxc_errno(sts);
13153 fab_out.fab$l_xab = (void *) &xabdat;
13154 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
13155 preserve_dates = 1;
13157 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
13158 preserve_dates =0; /* bitmask from this point forward */
13160 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
13161 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
13162 PerlMem_free(vmsin);
13163 PerlMem_free(vmsout);
13166 PerlMem_free(esal);
13169 PerlMem_free(rsal);
13170 PerlMem_free(esa_out);
13171 if (esal_out != NULL)
13172 PerlMem_free(esal_out);
13173 PerlMem_free(rsa_out);
13174 if (rsal_out != NULL)
13175 PerlMem_free(rsal_out);
13176 set_vaxc_errno(sts);
13179 set_errno(ENOENT); break;
13181 set_errno(ENOTDIR); break;
13183 set_errno(ENODEV); break;
13185 set_errno(EINVAL); break;
13187 set_errno(EACCES); break;
13189 set_errno(EVMSERR);
13193 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
13194 if (preserve_dates & 2) {
13195 /* sys$close() will process xabrdt, not xabdat */
13196 xabrdt = cc$rms_xabrdt;
13198 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
13200 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
13201 * is unsigned long[2], while DECC & VAXC use a struct */
13202 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
13204 fab_out.fab$l_xab = (void *) &xabrdt;
13207 ubf = PerlMem_malloc(32256);
13208 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13209 rab_in = cc$rms_rab;
13210 rab_in.rab$l_fab = &fab_in;
13211 rab_in.rab$l_rop = RAB$M_BIO;
13212 rab_in.rab$l_ubf = ubf;
13213 rab_in.rab$w_usz = 32256;
13214 if (!((sts = sys$connect(&rab_in)) & 1)) {
13215 sys$close(&fab_in); sys$close(&fab_out);
13216 PerlMem_free(vmsin);
13217 PerlMem_free(vmsout);
13221 PerlMem_free(esal);
13224 PerlMem_free(rsal);
13225 PerlMem_free(esa_out);
13226 if (esal_out != NULL)
13227 PerlMem_free(esal_out);
13228 PerlMem_free(rsa_out);
13229 if (rsal_out != NULL)
13230 PerlMem_free(rsal_out);
13231 set_errno(EVMSERR); set_vaxc_errno(sts);
13235 rab_out = cc$rms_rab;
13236 rab_out.rab$l_fab = &fab_out;
13237 rab_out.rab$l_rbf = ubf;
13238 if (!((sts = sys$connect(&rab_out)) & 1)) {
13239 sys$close(&fab_in); sys$close(&fab_out);
13240 PerlMem_free(vmsin);
13241 PerlMem_free(vmsout);
13245 PerlMem_free(esal);
13248 PerlMem_free(rsal);
13249 PerlMem_free(esa_out);
13250 if (esal_out != NULL)
13251 PerlMem_free(esal_out);
13252 PerlMem_free(rsa_out);
13253 if (rsal_out != NULL)
13254 PerlMem_free(rsal_out);
13255 set_errno(EVMSERR); set_vaxc_errno(sts);
13259 while ((sts = sys$read(&rab_in))) { /* always true */
13260 if (sts == RMS$_EOF) break;
13261 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
13262 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
13263 sys$close(&fab_in); sys$close(&fab_out);
13264 PerlMem_free(vmsin);
13265 PerlMem_free(vmsout);
13269 PerlMem_free(esal);
13272 PerlMem_free(rsal);
13273 PerlMem_free(esa_out);
13274 if (esal_out != NULL)
13275 PerlMem_free(esal_out);
13276 PerlMem_free(rsa_out);
13277 if (rsal_out != NULL)
13278 PerlMem_free(rsal_out);
13279 set_errno(EVMSERR); set_vaxc_errno(sts);
13285 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
13286 sys$close(&fab_in); sys$close(&fab_out);
13287 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
13289 PerlMem_free(vmsin);
13290 PerlMem_free(vmsout);
13294 PerlMem_free(esal);
13297 PerlMem_free(rsal);
13298 PerlMem_free(esa_out);
13299 if (esal_out != NULL)
13300 PerlMem_free(esal_out);
13301 PerlMem_free(rsa_out);
13302 if (rsal_out != NULL)
13303 PerlMem_free(rsal_out);
13306 set_errno(EVMSERR); set_vaxc_errno(sts);
13312 } /* end of rmscopy() */
13316 /*** The following glue provides 'hooks' to make some of the routines
13317 * from this file available from Perl. These routines are sufficiently
13318 * basic, and are required sufficiently early in the build process,
13319 * that's it's nice to have them available to miniperl as well as the
13320 * full Perl, so they're set up here instead of in an extension. The
13321 * Perl code which handles importation of these names into a given
13322 * package lives in [.VMS]Filespec.pm in @INC.
13326 rmsexpand_fromperl(pTHX_ CV *cv)
13329 char *fspec, *defspec = NULL, *rslt;
13331 int fs_utf8, dfs_utf8;
13335 if (!items || items > 2)
13336 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
13337 fspec = SvPV(ST(0),n_a);
13338 fs_utf8 = SvUTF8(ST(0));
13339 if (!fspec || !*fspec) XSRETURN_UNDEF;
13341 defspec = SvPV(ST(1),n_a);
13342 dfs_utf8 = SvUTF8(ST(1));
13344 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
13345 ST(0) = sv_newmortal();
13346 if (rslt != NULL) {
13347 sv_usepvn(ST(0),rslt,strlen(rslt));
13356 vmsify_fromperl(pTHX_ CV *cv)
13363 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
13364 utf8_fl = SvUTF8(ST(0));
13365 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13366 ST(0) = sv_newmortal();
13367 if (vmsified != NULL) {
13368 sv_usepvn(ST(0),vmsified,strlen(vmsified));
13377 unixify_fromperl(pTHX_ CV *cv)
13384 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
13385 utf8_fl = SvUTF8(ST(0));
13386 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13387 ST(0) = sv_newmortal();
13388 if (unixified != NULL) {
13389 sv_usepvn(ST(0),unixified,strlen(unixified));
13398 fileify_fromperl(pTHX_ CV *cv)
13405 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
13406 utf8_fl = SvUTF8(ST(0));
13407 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13408 ST(0) = sv_newmortal();
13409 if (fileified != NULL) {
13410 sv_usepvn(ST(0),fileified,strlen(fileified));
13419 pathify_fromperl(pTHX_ CV *cv)
13426 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
13427 utf8_fl = SvUTF8(ST(0));
13428 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13429 ST(0) = sv_newmortal();
13430 if (pathified != NULL) {
13431 sv_usepvn(ST(0),pathified,strlen(pathified));
13440 vmspath_fromperl(pTHX_ CV *cv)
13447 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
13448 utf8_fl = SvUTF8(ST(0));
13449 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13450 ST(0) = sv_newmortal();
13451 if (vmspath != NULL) {
13452 sv_usepvn(ST(0),vmspath,strlen(vmspath));
13461 unixpath_fromperl(pTHX_ CV *cv)
13468 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
13469 utf8_fl = SvUTF8(ST(0));
13470 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13471 ST(0) = sv_newmortal();
13472 if (unixpath != NULL) {
13473 sv_usepvn(ST(0),unixpath,strlen(unixpath));
13482 candelete_fromperl(pTHX_ CV *cv)
13490 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
13492 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13493 Newx(fspec, VMS_MAXRSS, char);
13494 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
13495 if (SvTYPE(mysv) == SVt_PVGV) {
13496 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
13497 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13505 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
13506 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13513 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
13519 rmscopy_fromperl(pTHX_ CV *cv)
13522 char *inspec, *outspec, *inp, *outp;
13524 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
13525 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13526 unsigned long int sts;
13531 if (items < 2 || items > 3)
13532 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
13534 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13535 Newx(inspec, VMS_MAXRSS, char);
13536 if (SvTYPE(mysv) == SVt_PVGV) {
13537 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
13538 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13546 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
13547 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13553 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
13554 Newx(outspec, VMS_MAXRSS, char);
13555 if (SvTYPE(mysv) == SVt_PVGV) {
13556 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
13557 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13566 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
13567 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13574 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
13576 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
13582 /* The mod2fname is limited to shorter filenames by design, so it should
13583 * not be modified to support longer EFS pathnames
13586 mod2fname(pTHX_ CV *cv)
13589 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
13590 workbuff[NAM$C_MAXRSS*1 + 1];
13591 int total_namelen = 3, counter, num_entries;
13592 /* ODS-5 ups this, but we want to be consistent, so... */
13593 int max_name_len = 39;
13594 AV *in_array = (AV *)SvRV(ST(0));
13596 num_entries = av_len(in_array);
13598 /* All the names start with PL_. */
13599 strcpy(ultimate_name, "PL_");
13601 /* Clean up our working buffer */
13602 Zero(work_name, sizeof(work_name), char);
13604 /* Run through the entries and build up a working name */
13605 for(counter = 0; counter <= num_entries; counter++) {
13606 /* If it's not the first name then tack on a __ */
13608 strcat(work_name, "__");
13610 strcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)));
13613 /* Check to see if we actually have to bother...*/
13614 if (strlen(work_name) + 3 <= max_name_len) {
13615 strcat(ultimate_name, work_name);
13617 /* It's too darned big, so we need to go strip. We use the same */
13618 /* algorithm as xsubpp does. First, strip out doubled __ */
13619 char *source, *dest, last;
13622 for (source = work_name; *source; source++) {
13623 if (last == *source && last == '_') {
13629 /* Go put it back */
13630 strcpy(work_name, workbuff);
13631 /* Is it still too big? */
13632 if (strlen(work_name) + 3 > max_name_len) {
13633 /* Strip duplicate letters */
13636 for (source = work_name; *source; source++) {
13637 if (last == toupper(*source)) {
13641 last = toupper(*source);
13643 strcpy(work_name, workbuff);
13646 /* Is it *still* too big? */
13647 if (strlen(work_name) + 3 > max_name_len) {
13648 /* Too bad, we truncate */
13649 work_name[max_name_len - 2] = 0;
13651 strcat(ultimate_name, work_name);
13654 /* Okay, return it */
13655 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13660 hushexit_fromperl(pTHX_ CV *cv)
13665 VMSISH_HUSHED = SvTRUE(ST(0));
13667 ST(0) = boolSV(VMSISH_HUSHED);
13673 Perl_vms_start_glob
13674 (pTHX_ SV *tmpglob,
13678 struct vs_str_st *rslt;
13682 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13685 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13686 struct dsc$descriptor_vs rsdsc;
13687 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13688 unsigned long hasver = 0, isunix = 0;
13689 unsigned long int lff_flags = 0;
13691 int vms_old_glob = 1;
13693 if (!SvOK(tmpglob)) {
13694 SETERRNO(ENOENT,RMS$_FNF);
13698 vms_old_glob = !decc_filename_unix_report;
13700 #ifdef VMS_LONGNAME_SUPPORT
13701 lff_flags = LIB$M_FIL_LONG_NAMES;
13703 /* The Newx macro will not allow me to assign a smaller array
13704 * to the rslt pointer, so we will assign it to the begin char pointer
13705 * and then copy the value into the rslt pointer.
13707 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13708 rslt = (struct vs_str_st *)begin;
13710 rstr = &rslt->str[0];
13711 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13712 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13713 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13714 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13716 Newx(vmsspec, VMS_MAXRSS, char);
13718 /* We could find out if there's an explicit dev/dir or version
13719 by peeking into lib$find_file's internal context at
13720 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13721 but that's unsupported, so I don't want to do it now and
13722 have it bite someone in the future. */
13723 /* Fix-me: vms_split_path() is the only way to do this, the
13724 existing method will fail with many legal EFS or UNIX specifications
13727 cp = SvPV(tmpglob,i);
13730 if (cp[i] == ';') hasver = 1;
13731 if (cp[i] == '.') {
13732 if (sts) hasver = 1;
13735 if (cp[i] == '/') {
13736 hasdir = isunix = 1;
13739 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13745 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13746 if ((hasdir == 0) && decc_filename_unix_report) {
13750 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13751 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13752 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13758 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13759 if (!stat_sts && S_ISDIR(st.st_mode)) {
13761 const char * fname;
13764 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13765 /* path delimiter of ':>]', if so, then the old behavior has */
13766 /* obviously been specificially requested */
13768 fname = SvPVX_const(tmpglob);
13769 fname_len = strlen(fname);
13770 vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13771 if (vms_old_glob || (vms_dir != NULL)) {
13772 wilddsc.dsc$a_pointer = tovmspath_utf8(
13773 SvPVX(tmpglob),vmsspec,NULL);
13774 ok = (wilddsc.dsc$a_pointer != NULL);
13775 /* maybe passed 'foo' rather than '[.foo]', thus not
13779 /* Operate just on the directory, the special stat/fstat for */
13780 /* leaves the fileified specification in the st_devnam */
13782 wilddsc.dsc$a_pointer = st.st_devnam;
13787 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13788 ok = (wilddsc.dsc$a_pointer != NULL);
13791 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13793 /* If not extended character set, replace ? with % */
13794 /* With extended character set, ? is a wildcard single character */
13795 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13798 if (!decc_efs_case_preserve)
13800 } else if (*cp == '%') {
13802 } else if (*cp == '*') {
13808 wv_sts = vms_split_path(
13809 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13810 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13811 &wvs_spec, &wvs_len);
13820 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13821 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13822 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13826 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13827 &dfltdsc,NULL,&rms_sts,&lff_flags);
13828 if (!$VMS_STATUS_SUCCESS(sts))
13831 /* with varying string, 1st word of buffer contains result length */
13832 rstr[rslt->length] = '\0';
13834 /* Find where all the components are */
13835 v_sts = vms_split_path
13850 /* If no version on input, truncate the version on output */
13851 if (!hasver && (vs_len > 0)) {
13858 /* In Unix report mode, remove the ".dir;1" from the name */
13859 /* if it is a real directory */
13860 if (decc_filename_unix_report || decc_efs_charset) {
13861 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13865 ret_sts = flex_lstat(rstr, &statbuf);
13866 if ((ret_sts == 0) &&
13867 S_ISDIR(statbuf.st_mode)) {
13874 /* No version & a null extension on UNIX handling */
13875 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13881 if (!decc_efs_case_preserve) {
13882 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13885 /* Find File treats a Null extension as return all extensions */
13886 /* This is contrary to Perl expectations */
13888 if (wildstar || wildquery || vms_old_glob) {
13889 /* really need to see if the returned file name matched */
13890 /* but for now will assume that it matches */
13893 /* Exact Match requested */
13894 /* How are directories handled? - like a file */
13895 if ((e_len == we_len) && (n_len == wn_len)) {
13899 t1 = strncmp(e_spec, we_spec, e_len);
13903 t1 = strncmp(n_spec, we_spec, n_len);
13914 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13918 /* Start with the name */
13921 strcat(begin,"\n");
13922 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13925 if (cxt) (void)lib$find_file_end(&cxt);
13928 /* Be POSIXish: return the input pattern when no matches */
13929 strcpy(rstr,SvPVX(tmpglob));
13931 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13934 if (ok && sts != RMS$_NMF &&
13935 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13938 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13940 PerlIO_close(tmpfp);
13944 PerlIO_rewind(tmpfp);
13945 IoTYPE(io) = IoTYPE_RDONLY;
13946 IoIFP(io) = fp = tmpfp;
13947 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13957 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13961 unixrealpath_fromperl(pTHX_ CV *cv)
13964 char *fspec, *rslt_spec, *rslt;
13967 if (!items || items != 1)
13968 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13970 fspec = SvPV(ST(0),n_a);
13971 if (!fspec || !*fspec) XSRETURN_UNDEF;
13973 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13974 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13976 ST(0) = sv_newmortal();
13978 sv_usepvn(ST(0),rslt,strlen(rslt));
13980 Safefree(rslt_spec);
13985 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13989 vmsrealpath_fromperl(pTHX_ CV *cv)
13992 char *fspec, *rslt_spec, *rslt;
13995 if (!items || items != 1)
13996 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13998 fspec = SvPV(ST(0),n_a);
13999 if (!fspec || !*fspec) XSRETURN_UNDEF;
14001 Newx(rslt_spec, VMS_MAXRSS + 1, char);
14002 rslt = do_vms_realname(fspec, rslt_spec, NULL);
14004 ST(0) = sv_newmortal();
14006 sv_usepvn(ST(0),rslt,strlen(rslt));
14008 Safefree(rslt_spec);
14014 * A thin wrapper around decc$symlink to make sure we follow the
14015 * standard and do not create a symlink with a zero-length name.
14017 * Also in ODS-2 mode, existing tests assume that the link target
14018 * will be converted to UNIX format.
14020 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
14021 int Perl_my_symlink(pTHX_ const char *contents, const char *link_name) {
14022 if (!link_name || !*link_name) {
14023 SETERRNO(ENOENT, SS$_NOSUCHFILE);
14027 if (decc_efs_charset) {
14028 return symlink(contents, link_name);
14033 /* Unless we are in ODS-5 mode, convert the symlink target to UNIX */
14034 /* because in order to work, the symlink target must be in UNIX format */
14036 /* As symbolic links can hold things other than files, we will only do */
14037 /* the conversion in in ODS-2 mode */
14039 Newx(utarget, VMS_MAXRSS + 1, char);
14040 if (int_tounixspec(contents, utarget, NULL) == NULL) {
14042 /* This should not fail, as an untranslatable filename */
14043 /* should be passed through */
14044 utarget = (char *)contents;
14046 sts = symlink(utarget, link_name);
14054 #endif /* HAS_SYMLINK */
14056 int do_vms_case_tolerant(void);
14059 case_tolerant_process_fromperl(pTHX_ CV *cv)
14062 ST(0) = boolSV(do_vms_case_tolerant());
14066 #ifdef USE_ITHREADS
14069 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
14070 struct interp_intern *dst)
14072 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
14074 memcpy(dst,src,sizeof(struct interp_intern));
14080 Perl_sys_intern_clear(pTHX)
14085 Perl_sys_intern_init(pTHX)
14087 unsigned int ix = RAND_MAX;
14092 MY_POSIX_EXIT = vms_posix_exit;
14095 MY_INV_RAND_MAX = 1./x;
14099 init_os_extras(void)
14102 char* file = __FILE__;
14103 if (decc_disable_to_vms_logname_translation) {
14104 no_translate_barewords = TRUE;
14106 no_translate_barewords = FALSE;
14109 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
14110 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
14111 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
14112 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
14113 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
14114 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
14115 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
14116 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
14117 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
14118 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
14119 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
14120 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
14121 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
14122 newXSproto("VMS::Filespec::case_tolerant_process",
14123 case_tolerant_process_fromperl,file,"");
14125 store_pipelocs(aTHX); /* will redo any earlier attempts */
14130 #if __CRTL_VER == 80200000
14131 /* This missed getting in to the DECC SDK for 8.2 */
14132 char *realpath(const char *file_name, char * resolved_name, ...);
14135 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
14136 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
14137 * The perl fallback routine to provide realpath() is not as efficient
14141 /* Hack, use old stat() as fastest way of getting ino_t and device */
14142 int decc$stat(const char *name, void * statbuf);
14143 #if !defined(__VAX) && __CRTL_VER >= 80200000
14144 int decc$lstat(const char *name, void * statbuf);
14146 #define decc$lstat decc$stat
14150 /* Realpath is fragile. In 8.3 it does not work if the feature
14151 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
14152 * links are implemented in RMS, not the CRTL. It also can fail if the
14153 * user does not have read/execute access to some of the directories.
14154 * So in order for Do What I Mean mode to work, if realpath() fails,
14155 * fall back to looking up the filename by the device name and FID.
14158 int vms_fid_to_name(char * outname, int outlen,
14159 const char * name, int lstat_flag, mode_t * mode)
14161 #pragma message save
14162 #pragma message disable MISALGNDSTRCT
14163 #pragma message disable MISALGNDMEM
14164 #pragma member_alignment save
14165 #pragma nomember_alignment
14168 unsigned short st_ino[3];
14169 unsigned short old_st_mode;
14170 unsigned long padl[30]; /* plenty of room */
14172 #pragma message restore
14173 #pragma member_alignment restore
14176 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14177 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14182 /* Need to follow the mostly the same rules as flex_stat_int, or we may get
14183 * unexpected answers
14186 fileified = PerlMem_malloc(VMS_MAXRSS);
14187 if (fileified == NULL)
14188 _ckvmssts_noperl(SS$_INSFMEM);
14190 temp_fspec = PerlMem_malloc(VMS_MAXRSS);
14191 if (temp_fspec == NULL)
14192 _ckvmssts_noperl(SS$_INSFMEM);
14195 /* First need to try as a directory */
14196 ret_spec = int_tovmspath(name, temp_fspec, NULL);
14197 if (ret_spec != NULL) {
14198 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
14199 if (ret_spec != NULL) {
14200 if (lstat_flag == 0)
14201 sts = decc$stat(fileified, &statbuf);
14203 sts = decc$lstat(fileified, &statbuf);
14207 /* Then as a VMS file spec */
14209 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
14210 if (ret_spec != NULL) {
14211 if (lstat_flag == 0) {
14212 sts = decc$stat(temp_fspec, &statbuf);
14214 sts = decc$lstat(temp_fspec, &statbuf);
14220 /* Next try - allow multiple dots with out EFS CHARSET */
14221 /* The CRTL stat() falls down hard on multi-dot filenames in unix
14222 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
14223 * enable it if it isn't already.
14225 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14226 if (!decc_efs_charset && (decc_efs_charset_index > 0))
14227 decc$feature_set_value(decc_efs_charset_index, 1, 1);
14229 ret_spec = int_tovmspath(name, temp_fspec, NULL);
14230 if (lstat_flag == 0) {
14231 sts = decc$stat(name, &statbuf);
14233 sts = decc$lstat(name, &statbuf);
14235 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14236 if (!decc_efs_charset && (decc_efs_charset_index > 0))
14237 decc$feature_set_value(decc_efs_charset_index, 1, 0);
14242 /* and then because the Perl Unix to VMS conversion is not perfect */
14243 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
14244 /* characters from filenames so we need to try it as-is */
14246 if (lstat_flag == 0) {
14247 sts = decc$stat(name, &statbuf);
14249 sts = decc$lstat(name, &statbuf);
14256 dvidsc.dsc$a_pointer=statbuf.st_dev;
14257 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
14259 specdsc.dsc$a_pointer = outname;
14260 specdsc.dsc$w_length = outlen-1;
14262 vms_sts = lib$fid_to_name
14263 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
14264 if ($VMS_STATUS_SUCCESS(vms_sts)) {
14265 outname[specdsc.dsc$w_length] = 0;
14267 /* Return the mode */
14269 *mode = statbuf.old_st_mode;
14280 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
14283 char * rslt = NULL;
14286 if (decc_posix_compliant_pathnames > 0 ) {
14287 /* realpath currently only works if posix compliant pathnames are
14288 * enabled. It may start working when they are not, but in that
14289 * case we still want the fallback behavior for backwards compatibility
14291 rslt = realpath(filespec, outbuf);
14295 if (rslt == NULL) {
14297 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14298 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14302 /* Fall back to fid_to_name */
14304 Newx(vms_spec, VMS_MAXRSS + 1, char);
14306 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
14310 /* Now need to trim the version off */
14311 sts = vms_split_path
14331 /* Trim off the version */
14332 int file_len = v_len + r_len + d_len + n_len + e_len;
14333 vms_spec[file_len] = 0;
14335 /* The result is expected to be in UNIX format */
14336 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
14338 /* Downcase if input had any lower case letters and
14339 * case preservation is not in effect.
14341 if (!decc_efs_case_preserve) {
14342 for (cp = filespec; *cp; cp++)
14343 if (islower(*cp)) { haslower = 1; break; }
14345 if (haslower) __mystrtolower(rslt);
14350 /* Now for some hacks to deal with backwards and forward */
14352 if (!decc_efs_charset) {
14354 /* 1. ODS-2 mode wants to do a syntax only translation */
14355 rslt = int_rmsexpand(filespec, outbuf,
14356 NULL, 0, NULL, utf8_fl);
14359 if (decc_filename_unix_report) {
14361 char * vms_dir_name;
14364 /* 2. ODS-5 / UNIX report mode should return a failure */
14365 /* if the parent directory also does not exist */
14366 /* Otherwise, get the real path for the parent */
14367 /* and add the child to it.
14369 /* basename / dirname only available for VMS 7.0+ */
14370 /* So we may need to implement them as common routines */
14372 Newx(dir_name, VMS_MAXRSS + 1, char);
14373 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
14374 dir_name[0] = '\0';
14377 /* First try a VMS parse */
14378 sts = vms_split_path
14396 int dir_len = v_len + r_len + d_len + n_len;
14398 strncpy(dir_name, filespec, dir_len);
14399 dir_name[dir_len] = '\0';
14400 file_name = (char *)&filespec[dir_len + 1];
14403 /* This must be UNIX */
14406 tchar = strrchr(filespec, '/');
14408 if (tchar != NULL) {
14409 int dir_len = tchar - filespec;
14410 strncpy(dir_name, filespec, dir_len);
14411 dir_name[dir_len] = '\0';
14412 file_name = (char *) &filespec[dir_len + 1];
14416 /* Dir name is defaulted */
14417 if (dir_name[0] == 0) {
14419 dir_name[1] = '\0';
14422 /* Need realpath for the directory */
14423 sts = vms_fid_to_name(vms_dir_name,
14425 dir_name, 0, NULL);
14428 /* Now need to pathify it.
14429 char *tdir = int_pathify_dirspec(vms_dir_name,
14432 /* And now add the original filespec to it */
14433 if (file_name != NULL) {
14434 strcat(outbuf, file_name);
14438 Safefree(vms_dir_name);
14439 Safefree(dir_name);
14443 Safefree(vms_spec);
14449 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
14452 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14453 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14456 /* Fall back to fid_to_name */
14458 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
14465 /* Now need to trim the version off */
14466 sts = vms_split_path
14486 /* Trim off the version */
14487 int file_len = v_len + r_len + d_len + n_len + e_len;
14488 outbuf[file_len] = 0;
14490 /* Downcase if input had any lower case letters and
14491 * case preservation is not in effect.
14493 if (!decc_efs_case_preserve) {
14494 for (cp = filespec; *cp; cp++)
14495 if (islower(*cp)) { haslower = 1; break; }
14497 if (haslower) __mystrtolower(outbuf);
14506 /* External entry points */
14507 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14508 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
14510 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14511 { return do_vms_realname(filespec, outbuf, utf8_fl); }
14513 /* case_tolerant */
14515 /*{{{int do_vms_case_tolerant(void)*/
14516 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
14517 * controlled by a process setting.
14519 int do_vms_case_tolerant(void)
14521 return vms_process_case_tolerant;
14524 /* External entry points */
14525 #if __CRTL_VER >= 70301000 && !defined(__VAX)
14526 int Perl_vms_case_tolerant(void)
14527 { return do_vms_case_tolerant(); }
14529 int Perl_vms_case_tolerant(void)
14530 { return vms_process_case_tolerant; }
14534 /* Start of DECC RTL Feature handling */
14536 static int sys_trnlnm
14537 (const char * logname,
14541 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
14542 const unsigned long attr = LNM$M_CASE_BLIND;
14543 struct dsc$descriptor_s name_dsc;
14545 unsigned short result;
14546 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
14549 name_dsc.dsc$w_length = strlen(logname);
14550 name_dsc.dsc$a_pointer = (char *)logname;
14551 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14552 name_dsc.dsc$b_class = DSC$K_CLASS_S;
14554 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
14556 if ($VMS_STATUS_SUCCESS(status)) {
14558 /* Null terminate and return the string */
14559 /*--------------------------------------*/
14566 static int sys_crelnm
14567 (const char * logname,
14568 const char * value)
14571 const char * proc_table = "LNM$PROCESS_TABLE";
14572 struct dsc$descriptor_s proc_table_dsc;
14573 struct dsc$descriptor_s logname_dsc;
14574 struct itmlst_3 item_list[2];
14576 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
14577 proc_table_dsc.dsc$w_length = strlen(proc_table);
14578 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14579 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
14581 logname_dsc.dsc$a_pointer = (char *) logname;
14582 logname_dsc.dsc$w_length = strlen(logname);
14583 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14584 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
14586 item_list[0].buflen = strlen(value);
14587 item_list[0].itmcode = LNM$_STRING;
14588 item_list[0].bufadr = (char *)value;
14589 item_list[0].retlen = NULL;
14591 item_list[1].buflen = 0;
14592 item_list[1].itmcode = 0;
14594 ret_val = sys$crelnm
14596 (const struct dsc$descriptor_s *)&proc_table_dsc,
14597 (const struct dsc$descriptor_s *)&logname_dsc,
14599 (const struct item_list_3 *) item_list);
14604 /* C RTL Feature settings */
14606 static int set_features
14607 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
14608 int (* cli_routine)(void), /* Not documented */
14609 void *image_info) /* Not documented */
14615 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
14616 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
14617 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
14618 unsigned long case_perm;
14619 unsigned long case_image;
14622 /* Allow an exception to bring Perl into the VMS debugger */
14623 vms_debug_on_exception = 0;
14624 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
14625 if ($VMS_STATUS_SUCCESS(status)) {
14626 val_str[0] = _toupper(val_str[0]);
14627 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14628 vms_debug_on_exception = 1;
14630 vms_debug_on_exception = 0;
14633 /* Debug unix/vms file translation routines */
14634 vms_debug_fileify = 0;
14635 status = sys_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
14636 if ($VMS_STATUS_SUCCESS(status)) {
14637 val_str[0] = _toupper(val_str[0]);
14638 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14639 vms_debug_fileify = 1;
14641 vms_debug_fileify = 0;
14645 /* Historically PERL has been doing vmsify / stat differently than */
14646 /* the CRTL. In particular, under some conditions the CRTL will */
14647 /* remove some illegal characters like spaces from filenames */
14648 /* resulting in some differences. The stat()/lstat() wrapper has */
14649 /* been reporting such file names as invalid and fails to stat them */
14650 /* fixing this bug so that stat()/lstat() accept these like the */
14651 /* CRTL does will result in several tests failing. */
14652 /* This should really be fixed, but for now, set up a feature to */
14653 /* enable it so that the impact can be studied. */
14654 vms_bug_stat_filename = 0;
14655 status = sys_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
14656 if ($VMS_STATUS_SUCCESS(status)) {
14657 val_str[0] = _toupper(val_str[0]);
14658 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14659 vms_bug_stat_filename = 1;
14661 vms_bug_stat_filename = 0;
14665 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
14666 vms_vtf7_filenames = 0;
14667 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14668 if ($VMS_STATUS_SUCCESS(status)) {
14669 val_str[0] = _toupper(val_str[0]);
14670 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14671 vms_vtf7_filenames = 1;
14673 vms_vtf7_filenames = 0;
14676 /* unlink all versions on unlink() or rename() */
14677 vms_unlink_all_versions = 0;
14678 status = sys_trnlnm
14679 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14680 if ($VMS_STATUS_SUCCESS(status)) {
14681 val_str[0] = _toupper(val_str[0]);
14682 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14683 vms_unlink_all_versions = 1;
14685 vms_unlink_all_versions = 0;
14688 /* Dectect running under GNV Bash or other UNIX like shell */
14689 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14690 gnv_unix_shell = 0;
14691 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14692 if ($VMS_STATUS_SUCCESS(status)) {
14693 gnv_unix_shell = 1;
14694 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14695 set_feature_default("DECC$EFS_CHARSET", 1);
14696 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14697 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14698 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14699 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
14700 vms_unlink_all_versions = 1;
14701 vms_posix_exit = 1;
14705 /* hacks to see if known bugs are still present for testing */
14707 /* PCP mode requires creating /dev/null special device file */
14708 decc_bug_devnull = 0;
14709 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14710 if ($VMS_STATUS_SUCCESS(status)) {
14711 val_str[0] = _toupper(val_str[0]);
14712 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14713 decc_bug_devnull = 1;
14715 decc_bug_devnull = 0;
14718 /* UNIX directory names with no paths are broken in a lot of places */
14719 decc_dir_barename = 1;
14720 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
14721 if ($VMS_STATUS_SUCCESS(status)) {
14722 val_str[0] = _toupper(val_str[0]);
14723 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14724 decc_dir_barename = 1;
14726 decc_dir_barename = 0;
14729 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14730 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14732 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14733 if (decc_disable_to_vms_logname_translation < 0)
14734 decc_disable_to_vms_logname_translation = 0;
14737 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14739 decc_efs_case_preserve = decc$feature_get_value(s, 1);
14740 if (decc_efs_case_preserve < 0)
14741 decc_efs_case_preserve = 0;
14744 s = decc$feature_get_index("DECC$EFS_CHARSET");
14745 decc_efs_charset_index = s;
14747 decc_efs_charset = decc$feature_get_value(s, 1);
14748 if (decc_efs_charset < 0)
14749 decc_efs_charset = 0;
14752 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14754 decc_filename_unix_report = decc$feature_get_value(s, 1);
14755 if (decc_filename_unix_report > 0) {
14756 decc_filename_unix_report = 1;
14757 vms_posix_exit = 1;
14760 decc_filename_unix_report = 0;
14763 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14765 decc_filename_unix_only = decc$feature_get_value(s, 1);
14766 if (decc_filename_unix_only > 0) {
14767 decc_filename_unix_only = 1;
14770 decc_filename_unix_only = 0;
14774 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14776 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14777 if (decc_filename_unix_no_version < 0)
14778 decc_filename_unix_no_version = 0;
14781 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14783 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14784 if (decc_readdir_dropdotnotype < 0)
14785 decc_readdir_dropdotnotype = 0;
14788 #if __CRTL_VER >= 80200000
14789 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14791 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14792 if (decc_posix_compliant_pathnames < 0)
14793 decc_posix_compliant_pathnames = 0;
14794 if (decc_posix_compliant_pathnames > 4)
14795 decc_posix_compliant_pathnames = 0;
14800 status = sys_trnlnm
14801 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14802 if ($VMS_STATUS_SUCCESS(status)) {
14803 val_str[0] = _toupper(val_str[0]);
14804 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14805 decc_disable_to_vms_logname_translation = 1;
14810 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14811 if ($VMS_STATUS_SUCCESS(status)) {
14812 val_str[0] = _toupper(val_str[0]);
14813 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14814 decc_efs_case_preserve = 1;
14819 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14820 if ($VMS_STATUS_SUCCESS(status)) {
14821 val_str[0] = _toupper(val_str[0]);
14822 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14823 decc_filename_unix_report = 1;
14826 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14827 if ($VMS_STATUS_SUCCESS(status)) {
14828 val_str[0] = _toupper(val_str[0]);
14829 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14830 decc_filename_unix_only = 1;
14831 decc_filename_unix_report = 1;
14834 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14835 if ($VMS_STATUS_SUCCESS(status)) {
14836 val_str[0] = _toupper(val_str[0]);
14837 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14838 decc_filename_unix_no_version = 1;
14841 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14842 if ($VMS_STATUS_SUCCESS(status)) {
14843 val_str[0] = _toupper(val_str[0]);
14844 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14845 decc_readdir_dropdotnotype = 1;
14850 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14852 /* Report true case tolerance */
14853 /*----------------------------*/
14854 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14855 if (!$VMS_STATUS_SUCCESS(status))
14856 case_perm = PPROP$K_CASE_BLIND;
14857 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14858 if (!$VMS_STATUS_SUCCESS(status))
14859 case_image = PPROP$K_CASE_BLIND;
14860 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14861 (case_image == PPROP$K_CASE_SENSITIVE))
14862 vms_process_case_tolerant = 0;
14866 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
14867 /* for strict backward compatibilty */
14868 status = sys_trnlnm
14869 ("PERL_VMS_POSIX_EXIT", val_str, sizeof(val_str));
14870 if ($VMS_STATUS_SUCCESS(status)) {
14871 val_str[0] = _toupper(val_str[0]);
14872 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14873 vms_posix_exit = 1;
14875 vms_posix_exit = 0;
14879 /* CRTL can be initialized past this point, but not before. */
14880 /* DECC$CRTL_INIT(); */
14887 #pragma extern_model save
14888 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
14889 const __align (LONGWORD) int spare[8] = {0};
14891 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
14892 #if __DECC_VER >= 60560002
14893 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
14895 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
14897 #endif /* __DECC */
14899 const long vms_cc_features = (const long)set_features;
14902 ** Force a reference to LIB$INITIALIZE to ensure it
14903 ** exists in the image.
14905 int lib$initialize(void);
14907 #pragma extern_model strict_refdef
14909 int lib_init_ref = (int) lib$initialize;
14912 #pragma extern_model restore