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 = flex_lstat(src, &src_st);
5267 /* No source file or other problem */
5271 dst_sts = flex_lstat(dst, &dst_st);
5274 if (dst_st.st_dev != src_st.st_dev) {
5275 /* Must be on the same device */
5280 /* VMS_INO_T_COMPARE is true if the inodes are different
5281 * to match the output of memcmp
5284 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5285 /* That was easy, the files are the same! */
5289 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5290 /* If source is a directory, so must be dest */
5298 if ((dst_sts == 0) &&
5299 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5301 /* We have issues here if vms_unlink_all_versions is set
5302 * If the destination exists, and is not a directory, then
5303 * we must delete in advance.
5305 * If the src is a directory, then we must always pre-delete
5308 * If we successfully delete the dst in advance, and the rename fails
5309 * X/Open requires that errno be EIO.
5313 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5315 d_sts = mp_do_kill_file(aTHX_ dst, S_ISDIR(dst_st.st_mode));
5319 /* We killed the destination, so only errno now is EIO */
5324 /* Originally the idea was to call the CRTL rename() and only
5325 * try the lib$rename_file if it failed.
5326 * It turns out that there are too many variants in what the
5327 * the CRTL rename might do, so only use lib$rename_file
5332 /* Is the source and dest both in VMS format */
5333 /* if the source is a directory, then need to fileify */
5334 /* and dest must be a directory or non-existant. */
5340 unsigned long flags;
5341 struct dsc$descriptor_s old_file_dsc;
5342 struct dsc$descriptor_s new_file_dsc;
5344 /* We need to modify the src and dst depending
5345 * on if one or more of them are directories.
5348 vms_src = PerlMem_malloc(VMS_MAXRSS);
5349 if (vms_src == NULL)
5350 _ckvmssts_noperl(SS$_INSFMEM);
5352 /* Source is always a VMS format file */
5353 ret_str = do_tovmsspec(src, vms_src, 0, NULL);
5354 if (ret_str == NULL) {
5355 PerlMem_free(vms_src);
5360 vms_dst = PerlMem_malloc(VMS_MAXRSS);
5361 if (vms_dst == NULL)
5362 _ckvmssts_noperl(SS$_INSFMEM);
5364 if (S_ISDIR(src_st.st_mode)) {
5366 char * vms_dir_file;
5368 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5369 if (vms_dir_file == NULL)
5370 _ckvmssts_noperl(SS$_INSFMEM);
5372 /* The source must be a file specification */
5373 ret_str = int_fileify_dirspec(vms_src, vms_dir_file, NULL);
5374 if (ret_str == NULL) {
5375 PerlMem_free(vms_src);
5376 PerlMem_free(vms_dst);
5377 PerlMem_free(vms_dir_file);
5381 PerlMem_free(vms_src);
5382 vms_src = vms_dir_file;
5384 /* If the dest is a directory, we must remove it
5387 d_sts = mp_do_kill_file(aTHX_ dst, 1);
5389 PerlMem_free(vms_src);
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_src);
5402 PerlMem_free(vms_dst);
5407 /* The source must be a file specification */
5408 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5409 if (vms_dir_file == NULL)
5410 _ckvmssts_noperl(SS$_INSFMEM);
5412 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5413 if (ret_str == NULL) {
5414 PerlMem_free(vms_src);
5415 PerlMem_free(vms_dst);
5416 PerlMem_free(vms_dir_file);
5420 PerlMem_free(vms_dst);
5421 vms_dst = vms_dir_file;
5424 /* File to file or file to new dir */
5426 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5427 /* VMS pathify a dir target */
5428 ret_str = int_tovmspath(dst, vms_dst, NULL);
5429 if (ret_str == NULL) {
5430 PerlMem_free(vms_src);
5431 PerlMem_free(vms_dst);
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_src);
5441 PerlMem_free(vms_dst);
5448 old_file_dsc.dsc$a_pointer = vms_src;
5449 old_file_dsc.dsc$w_length = strlen(vms_src);
5450 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5451 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5453 new_file_dsc.dsc$a_pointer = vms_dst;
5454 new_file_dsc.dsc$w_length = strlen(vms_dst);
5455 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5456 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5459 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5460 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5463 sts = lib$rename_file(&old_file_dsc,
5467 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5468 if (!$VMS_STATUS_SUCCESS(sts)) {
5470 /* We could have failed because VMS style permissions do not
5471 * permit renames that UNIX will allow. Just like the hack
5474 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5477 PerlMem_free(vms_src);
5478 PerlMem_free(vms_dst);
5479 if (!$VMS_STATUS_SUCCESS(sts)) {
5486 if (vms_unlink_all_versions) {
5487 /* Now get rid of any previous versions of the source file that
5492 src_sts = mp_do_kill_file(aTHX_ src, S_ISDIR(src_st.st_mode));
5496 /* We deleted the destination, so must force the error to be EIO */
5497 if ((retval != 0) && (pre_delete != 0))
5505 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5506 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5507 * to expand file specification. Allows for a single default file
5508 * specification and a simple mask of options. If outbuf is non-NULL,
5509 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5510 * the resultant file specification is placed. If outbuf is NULL, the
5511 * resultant file specification is placed into a static buffer.
5512 * The third argument, if non-NULL, is taken to be a default file
5513 * specification string. The fourth argument is unused at present.
5514 * rmesexpand() returns the address of the resultant string if
5515 * successful, and NULL on error.
5517 * New functionality for previously unused opts value:
5518 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5519 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5520 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5521 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5523 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5527 (const char *filespec,
5529 const char *defspec,
5535 const char * in_spec;
5537 const char * def_spec;
5538 char * vmsfspec, *vmsdefspec;
5542 struct FAB myfab = cc$rms_fab;
5543 rms_setup_nam(mynam);
5545 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5548 /* temp hack until UTF8 is actually implemented */
5549 if (fs_utf8 != NULL)
5552 if (!filespec || !*filespec) {
5553 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5563 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5564 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5565 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5567 /* If this is a UNIX file spec, convert it to VMS */
5568 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5569 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5570 &e_len, &vs_spec, &vs_len);
5575 vmsfspec = PerlMem_malloc(VMS_MAXRSS);
5576 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5577 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5578 if (ret_spec == NULL) {
5579 PerlMem_free(vmsfspec);
5582 in_spec = (const char *)vmsfspec;
5584 /* Unless we are forcing to VMS format, a UNIX input means
5585 * UNIX output, and that requires long names to be used
5587 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5588 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5589 opts |= PERL_RMSEXPAND_M_LONG;
5597 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5598 rms_bind_fab_nam(myfab, mynam);
5600 /* Process the default file specification if present */
5602 if (defspec && *defspec) {
5604 t_isunix = is_unix_filespec(defspec);
5606 vmsdefspec = PerlMem_malloc(VMS_MAXRSS);
5607 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5608 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5610 if (ret_spec == NULL) {
5611 /* Clean up and bail */
5612 PerlMem_free(vmsdefspec);
5613 if (vmsfspec != NULL)
5614 PerlMem_free(vmsfspec);
5617 def_spec = (const char *)vmsdefspec;
5619 rms_set_dna(myfab, mynam,
5620 (char *)def_spec, strlen(def_spec)); /* cast ok */
5623 /* Now we need the expansion buffers */
5624 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
5625 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5626 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5627 esal = PerlMem_malloc(VMS_MAXRSS);
5628 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5630 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5632 /* If a NAML block is used RMS always writes to the long and short
5633 * addresses unless you suppress the short name.
5635 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5636 outbufl = PerlMem_malloc(VMS_MAXRSS);
5637 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5639 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5641 #ifdef NAM$M_NO_SHORT_UPCASE
5642 if (decc_efs_case_preserve)
5643 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5646 /* We may not want to follow symbolic links */
5647 #ifdef NAML$M_OPEN_SPECIAL
5648 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5649 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5652 /* First attempt to parse as an existing file */
5653 retsts = sys$parse(&myfab,0,0);
5654 if (!(retsts & STS$K_SUCCESS)) {
5656 /* Could not find the file, try as syntax only if error is not fatal */
5657 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5658 if (retsts == RMS$_DNF ||
5659 retsts == RMS$_DIR ||
5660 retsts == RMS$_DEV ||
5661 retsts == RMS$_PRV) {
5662 retsts = sys$parse(&myfab,0,0);
5663 if (retsts & STS$K_SUCCESS) goto int_expanded;
5666 /* Still could not parse the file specification */
5667 /*----------------------------------------------*/
5668 sts = rms_free_search_context(&myfab); /* Free search context */
5669 if (vmsdefspec != NULL)
5670 PerlMem_free(vmsdefspec);
5671 if (vmsfspec != NULL)
5672 PerlMem_free(vmsfspec);
5673 if (outbufl != NULL)
5674 PerlMem_free(outbufl);
5678 set_vaxc_errno(retsts);
5679 if (retsts == RMS$_PRV) set_errno(EACCES);
5680 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5681 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5682 else set_errno(EVMSERR);
5685 retsts = sys$search(&myfab,0,0);
5686 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5687 sts = rms_free_search_context(&myfab); /* Free search context */
5688 if (vmsdefspec != NULL)
5689 PerlMem_free(vmsdefspec);
5690 if (vmsfspec != NULL)
5691 PerlMem_free(vmsfspec);
5692 if (outbufl != NULL)
5693 PerlMem_free(outbufl);
5697 set_vaxc_errno(retsts);
5698 if (retsts == RMS$_PRV) set_errno(EACCES);
5699 else set_errno(EVMSERR);
5703 /* If the input filespec contained any lowercase characters,
5704 * downcase the result for compatibility with Unix-minded code. */
5706 if (!decc_efs_case_preserve) {
5708 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5709 if (islower(*tbuf)) { haslower = 1; break; }
5712 /* Is a long or a short name expected */
5713 /*------------------------------------*/
5715 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5716 if (rms_nam_rsll(mynam)) {
5718 speclen = rms_nam_rsll(mynam);
5721 spec_buf = esal; /* Not esa */
5722 speclen = rms_nam_esll(mynam);
5726 if (rms_nam_rsl(mynam)) {
5728 speclen = rms_nam_rsl(mynam);
5731 spec_buf = esa; /* Not esal */
5732 speclen = rms_nam_esl(mynam);
5735 spec_buf[speclen] = '\0';
5737 /* Trim off null fields added by $PARSE
5738 * If type > 1 char, must have been specified in original or default spec
5739 * (not true for version; $SEARCH may have added version of existing file).
5741 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5742 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5743 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5744 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5747 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5748 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5750 if (trimver || trimtype) {
5751 if (defspec && *defspec) {
5752 char *defesal = NULL;
5753 char *defesa = NULL;
5754 defesa = PerlMem_malloc(VMS_MAXRSS + 1);
5755 if (defesa != NULL) {
5756 struct FAB deffab = cc$rms_fab;
5757 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5758 defesal = PerlMem_malloc(VMS_MAXRSS + 1);
5759 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5761 rms_setup_nam(defnam);
5763 rms_bind_fab_nam(deffab, defnam);
5767 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5769 /* RMS needs the esa/esal as a work area if wildcards are involved */
5770 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5772 rms_clear_nam_nop(defnam);
5773 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5774 #ifdef NAM$M_NO_SHORT_UPCASE
5775 if (decc_efs_case_preserve)
5776 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5778 #ifdef NAML$M_OPEN_SPECIAL
5779 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5780 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5782 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5784 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5787 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5790 if (defesal != NULL)
5791 PerlMem_free(defesal);
5792 PerlMem_free(defesa);
5794 _ckvmssts_noperl(SS$_INSFMEM);
5798 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5799 if (*(rms_nam_verl(mynam)) != '\"')
5800 speclen = rms_nam_verl(mynam) - spec_buf;
5803 if (*(rms_nam_ver(mynam)) != '\"')
5804 speclen = rms_nam_ver(mynam) - spec_buf;
5808 /* If we didn't already trim version, copy down */
5809 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5810 if (speclen > rms_nam_verl(mynam) - spec_buf)
5812 (rms_nam_typel(mynam),
5813 rms_nam_verl(mynam),
5814 speclen - (rms_nam_verl(mynam) - spec_buf));
5815 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5818 if (speclen > rms_nam_ver(mynam) - spec_buf)
5820 (rms_nam_type(mynam),
5822 speclen - (rms_nam_ver(mynam) - spec_buf));
5823 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5828 /* Done with these copies of the input files */
5829 /*-------------------------------------------*/
5830 if (vmsfspec != NULL)
5831 PerlMem_free(vmsfspec);
5832 if (vmsdefspec != NULL)
5833 PerlMem_free(vmsdefspec);
5835 /* If we just had a directory spec on input, $PARSE "helpfully"
5836 * adds an empty name and type for us */
5837 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5838 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5839 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5840 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5841 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5842 speclen = rms_nam_namel(mynam) - spec_buf;
5847 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5848 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5849 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5850 speclen = rms_nam_name(mynam) - spec_buf;
5853 /* Posix format specifications must have matching quotes */
5854 if (speclen < (VMS_MAXRSS - 1)) {
5855 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5856 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5857 spec_buf[speclen] = '\"';
5862 spec_buf[speclen] = '\0';
5863 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5865 /* Have we been working with an expanded, but not resultant, spec? */
5866 /* Also, convert back to Unix syntax if necessary. */
5870 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5871 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5872 rsl = rms_nam_rsll(mynam);
5876 rsl = rms_nam_rsl(mynam);
5879 /* rsl is not present, it means that spec_buf is either */
5880 /* esa or esal, and needs to be copied to outbuf */
5881 /* convert to Unix if desired */
5883 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5885 /* VMS file specs are not in UTF-8 */
5886 if (fs_utf8 != NULL)
5888 strcpy(outbuf, spec_buf);
5893 /* Now spec_buf is either outbuf or outbufl */
5894 /* We need the result into outbuf */
5896 /* If we need this in UNIX, then we need another buffer */
5897 /* to keep things in order */
5899 char * new_src = NULL;
5900 if (spec_buf == outbuf) {
5901 new_src = PerlMem_malloc(VMS_MAXRSS);
5902 strcpy(new_src, spec_buf);
5906 ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5908 PerlMem_free(new_src);
5911 /* VMS file specs are not in UTF-8 */
5912 if (fs_utf8 != NULL)
5915 /* Copy the buffer if needed */
5916 if (outbuf != spec_buf)
5917 strcpy(outbuf, spec_buf);
5923 /* Need to clean up the search context */
5924 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5925 sts = rms_free_search_context(&myfab); /* Free search context */
5927 /* Clean up the extra buffers */
5931 if (outbufl != NULL)
5932 PerlMem_free(outbufl);
5934 /* Return the result */
5938 /* Common simple case - Expand an already VMS spec */
5940 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5941 opts |= PERL_RMSEXPAND_M_VMS_IN;
5942 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5945 /* Common simple case - Expand to a VMS spec */
5947 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5948 opts |= PERL_RMSEXPAND_M_VMS;
5949 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5953 /* Entry point used by perl routines */
5956 (pTHX_ const char *filespec,
5959 const char *defspec,
5964 static char __rmsexpand_retbuf[VMS_MAXRSS];
5965 char * expanded, *ret_spec, *ret_buf;
5969 if (ret_buf == NULL) {
5971 Newx(expanded, VMS_MAXRSS, char);
5972 if (expanded == NULL)
5973 _ckvmssts(SS$_INSFMEM);
5976 ret_buf = __rmsexpand_retbuf;
5981 ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5982 opts, fs_utf8, dfs_utf8);
5984 if (ret_spec == NULL) {
5985 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5993 /* External entry points */
5994 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5995 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5996 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5997 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5998 char *Perl_rmsexpand_utf8
5999 (pTHX_ const char *spec, char *buf, const char *def,
6000 unsigned opt, int * fs_utf8, int * dfs_utf8)
6001 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
6002 char *Perl_rmsexpand_utf8_ts
6003 (pTHX_ const char *spec, char *buf, const char *def,
6004 unsigned opt, int * fs_utf8, int * dfs_utf8)
6005 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
6009 ** The following routines are provided to make life easier when
6010 ** converting among VMS-style and Unix-style directory specifications.
6011 ** All will take input specifications in either VMS or Unix syntax. On
6012 ** failure, all return NULL. If successful, the routines listed below
6013 ** return a pointer to a buffer containing the appropriately
6014 ** reformatted spec (and, therefore, subsequent calls to that routine
6015 ** will clobber the result), while the routines of the same names with
6016 ** a _ts suffix appended will return a pointer to a mallocd string
6017 ** containing the appropriately reformatted spec.
6018 ** In all cases, only explicit syntax is altered; no check is made that
6019 ** the resulting string is valid or that the directory in question
6022 ** fileify_dirspec() - convert a directory spec into the name of the
6023 ** directory file (i.e. what you can stat() to see if it's a dir).
6024 ** The style (VMS or Unix) of the result is the same as the style
6025 ** of the parameter passed in.
6026 ** pathify_dirspec() - convert a directory spec into a path (i.e.
6027 ** what you prepend to a filename to indicate what directory it's in).
6028 ** The style (VMS or Unix) of the result is the same as the style
6029 ** of the parameter passed in.
6030 ** tounixpath() - convert a directory spec into a Unix-style path.
6031 ** tovmspath() - convert a directory spec into a VMS-style path.
6032 ** tounixspec() - convert any file spec into a Unix-style file spec.
6033 ** tovmsspec() - convert any file spec into a VMS-style spec.
6034 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
6036 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
6037 ** Permission is given to distribute this code as part of the Perl
6038 ** standard distribution under the terms of the GNU General Public
6039 ** License or the Perl Artistic License. Copies of each may be
6040 ** found in the Perl standard distribution.
6043 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6045 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
6047 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
6048 char *cp1, *cp2, *lastdir;
6049 char *trndir, *vmsdir;
6050 unsigned short int trnlnm_iter_count;
6054 if (utf8_fl != NULL)
6057 if (!dir || !*dir) {
6058 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
6060 dirlen = strlen(dir);
6061 while (dirlen && dir[dirlen-1] == '/') --dirlen;
6062 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
6063 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
6070 if (dirlen > (VMS_MAXRSS - 1)) {
6071 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
6074 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
6075 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6076 if (!strpbrk(dir+1,"/]>:") &&
6077 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
6078 strcpy(trndir,*dir == '/' ? dir + 1: dir);
6079 trnlnm_iter_count = 0;
6080 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
6081 trnlnm_iter_count++;
6082 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6084 dirlen = strlen(trndir);
6087 strncpy(trndir,dir,dirlen);
6088 trndir[dirlen] = '\0';
6091 /* At this point we are done with *dir and use *trndir which is a
6092 * copy that can be modified. *dir must not be modified.
6095 /* If we were handed a rooted logical name or spec, treat it like a
6096 * simple directory, so that
6097 * $ Define myroot dev:[dir.]
6098 * ... do_fileify_dirspec("myroot",buf,1) ...
6099 * does something useful.
6101 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
6102 trndir[--dirlen] = '\0';
6103 trndir[dirlen-1] = ']';
6105 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
6106 trndir[--dirlen] = '\0';
6107 trndir[dirlen-1] = '>';
6110 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
6111 /* If we've got an explicit filename, we can just shuffle the string. */
6112 if (*(cp1+1)) hasfilename = 1;
6113 /* Similarly, we can just back up a level if we've got multiple levels
6114 of explicit directories in a VMS spec which ends with directories. */
6116 for (cp2 = cp1; cp2 > trndir; cp2--) {
6118 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
6119 /* fix-me, can not scan EFS file specs backward like this */
6120 *cp2 = *cp1; *cp1 = '\0';
6125 if (*cp2 == '[' || *cp2 == '<') break;
6130 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
6131 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6132 cp1 = strpbrk(trndir,"]:>");
6133 if (hasfilename || !cp1) { /* filename present or not VMS */
6135 if (decc_efs_charset && !cp1) {
6137 /* EFS handling for UNIX mode */
6139 /* Just remove the trailing '/' and we should be done */
6141 trndir_len = strlen(trndir);
6143 if (trndir_len > 1) {
6145 if (trndir[trndir_len] == '/') {
6146 trndir[trndir_len] = '\0';
6149 strcpy(buf, trndir);
6150 PerlMem_free(trndir);
6151 PerlMem_free(vmsdir);
6155 /* For non-EFS mode, this is left for backwards compatibility */
6156 /* For EFS mode, this is only done for VMS format filespecs as */
6157 /* Perl programs generally have problems when a UNIX format spec */
6158 /* returns a VMS format spec */
6159 if (trndir[0] == '.') {
6160 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
6161 PerlMem_free(trndir);
6162 PerlMem_free(vmsdir);
6163 return int_fileify_dirspec("[]", buf, NULL);
6165 else if (trndir[1] == '.' &&
6166 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
6167 PerlMem_free(trndir);
6168 PerlMem_free(vmsdir);
6169 return int_fileify_dirspec("[-]", buf, NULL);
6172 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
6173 dirlen -= 1; /* to last element */
6174 lastdir = strrchr(trndir,'/');
6176 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
6177 /* If we have "/." or "/..", VMSify it and let the VMS code
6178 * below expand it, rather than repeating the code to handle
6179 * relative components of a filespec here */
6181 if (*(cp1+2) == '.') cp1++;
6182 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6184 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6185 PerlMem_free(trndir);
6186 PerlMem_free(vmsdir);
6189 if (strchr(vmsdir,'/') != NULL) {
6190 /* If int_tovmsspec() returned it, it must have VMS syntax
6191 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6192 * the time to check this here only so we avoid a recursion
6193 * loop; otherwise, gigo.
6195 PerlMem_free(trndir);
6196 PerlMem_free(vmsdir);
6197 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6200 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6201 PerlMem_free(trndir);
6202 PerlMem_free(vmsdir);
6205 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6206 PerlMem_free(trndir);
6207 PerlMem_free(vmsdir);
6211 } while ((cp1 = strstr(cp1,"/.")) != NULL);
6212 lastdir = strrchr(trndir,'/');
6214 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6216 /* Ditto for specs that end in an MFD -- let the VMS code
6217 * figure out whether it's a real device or a rooted logical. */
6219 /* This should not happen any more. Allowing the fake /000000
6220 * in a UNIX pathname causes all sorts of problems when trying
6221 * to run in UNIX emulation. So the VMS to UNIX conversions
6222 * now remove the fake /000000 directories.
6225 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6226 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6227 PerlMem_free(trndir);
6228 PerlMem_free(vmsdir);
6231 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6232 PerlMem_free(trndir);
6233 PerlMem_free(vmsdir);
6236 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6237 PerlMem_free(trndir);
6238 PerlMem_free(vmsdir);
6243 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6244 !(lastdir = cp1 = strrchr(trndir,']')) &&
6245 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6247 cp2 = strrchr(cp1,'.');
6249 int e_len, vs_len = 0;
6252 cp3 = strchr(cp2,';');
6253 e_len = strlen(cp2);
6255 vs_len = strlen(cp3);
6256 e_len = e_len - vs_len;
6258 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6260 if (!decc_efs_charset) {
6261 /* If this is not EFS, then not a directory */
6262 PerlMem_free(trndir);
6263 PerlMem_free(vmsdir);
6265 set_vaxc_errno(RMS$_DIR);
6269 /* Ok, here we have an issue, technically if a .dir shows */
6270 /* from inside a directory, then we should treat it as */
6271 /* xxx^.dir.dir. But we do not have that context at this */
6272 /* point unless this is totally restructured, so we remove */
6273 /* The .dir for now, and fix this better later */
6274 dirlen = cp2 - trndir;
6280 retlen = dirlen + 6;
6281 memcpy(buf, trndir, dirlen);
6284 /* We've picked up everything up to the directory file name.
6285 Now just add the type and version, and we're set. */
6287 /* We should only add type for VMS syntax, but historically Perl
6288 has added it for UNIX style also */
6290 /* Fix me - we should not be using the same routine for VMS and
6291 UNIX format files. Things are too tangled so we need to lookup
6292 what syntax the output is */
6296 lastdir = strrchr(trndir,'/');
6300 lastdir = strpbrk(trndir,"]:>");
6306 if ((is_vms == 0) && (is_unix == 0)) {
6307 /* We still do not know? */
6308 is_unix = decc_filename_unix_report;
6313 if ((is_unix && !decc_efs_charset) || is_vms) {
6315 /* It is a bug to add a .dir to a UNIX format directory spec */
6316 /* However Perl on VMS may have programs that expect this so */
6317 /* If not using EFS character specifications allow it. */
6319 if ((!decc_efs_case_preserve) && vms_process_case_tolerant) {
6320 /* Traditionally Perl expects filenames in lower case */
6321 strcat(buf, ".dir");
6323 /* VMS expects the .DIR to be in upper case */
6324 strcat(buf, ".DIR");
6327 /* It is also a bug to put a VMS format version on a UNIX file */
6328 /* specification. Perl self tests are looking for this */
6329 if (is_vms || !(decc_efs_charset || decc_filename_unix_report))
6332 PerlMem_free(trndir);
6333 PerlMem_free(vmsdir);
6336 else { /* VMS-style directory spec */
6338 char *esa, *esal, term, *cp;
6341 unsigned long int sts, cmplen, haslower = 0;
6342 unsigned int nam_fnb;
6344 struct FAB dirfab = cc$rms_fab;
6345 rms_setup_nam(savnam);
6346 rms_setup_nam(dirnam);
6348 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
6349 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6351 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6352 esal = PerlMem_malloc(VMS_MAXRSS);
6353 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6355 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6356 rms_bind_fab_nam(dirfab, dirnam);
6357 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6358 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6359 #ifdef NAM$M_NO_SHORT_UPCASE
6360 if (decc_efs_case_preserve)
6361 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6364 for (cp = trndir; *cp; cp++)
6365 if (islower(*cp)) { haslower = 1; break; }
6366 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6367 if ((dirfab.fab$l_sts == RMS$_DIR) ||
6368 (dirfab.fab$l_sts == RMS$_DNF) ||
6369 (dirfab.fab$l_sts == RMS$_PRV)) {
6370 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6371 sts = sys$parse(&dirfab);
6377 PerlMem_free(trndir);
6378 PerlMem_free(vmsdir);
6380 set_vaxc_errno(dirfab.fab$l_sts);
6386 /* Does the file really exist? */
6387 if (sys$search(&dirfab)& STS$K_SUCCESS) {
6388 /* Yes; fake the fnb bits so we'll check type below */
6389 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6391 else { /* No; just work with potential name */
6392 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6395 fab_sts = dirfab.fab$l_sts;
6396 sts = rms_free_search_context(&dirfab);
6400 PerlMem_free(trndir);
6401 PerlMem_free(vmsdir);
6402 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
6408 /* Make sure we are using the right buffer */
6411 my_esa_len = rms_nam_esll(dirnam);
6414 my_esa_len = rms_nam_esl(dirnam);
6416 my_esa[my_esa_len] = '\0';
6417 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6418 cp1 = strchr(my_esa,']');
6419 if (!cp1) cp1 = strchr(my_esa,'>');
6420 if (cp1) { /* Should always be true */
6421 my_esa_len -= cp1 - my_esa - 1;
6422 memmove(my_esa, cp1 + 1, my_esa_len);
6425 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6426 /* Yep; check version while we're at it, if it's there. */
6427 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6428 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6429 /* Something other than .DIR[;1]. Bzzt. */
6430 sts = rms_free_search_context(&dirfab);
6434 PerlMem_free(trndir);
6435 PerlMem_free(vmsdir);
6437 set_vaxc_errno(RMS$_DIR);
6442 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6443 /* They provided at least the name; we added the type, if necessary, */
6444 strcpy(buf, my_esa);
6445 sts = rms_free_search_context(&dirfab);
6446 PerlMem_free(trndir);
6450 PerlMem_free(vmsdir);
6453 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6454 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6458 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6459 if (cp1 == NULL) { /* should never happen */
6460 sts = rms_free_search_context(&dirfab);
6461 PerlMem_free(trndir);
6465 PerlMem_free(vmsdir);
6470 retlen = strlen(my_esa);
6471 cp1 = strrchr(my_esa,'.');
6472 /* ODS-5 directory specifications can have extra "." in them. */
6473 /* Fix-me, can not scan EFS file specifications backwards */
6474 while (cp1 != NULL) {
6475 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6479 while ((cp1 > my_esa) && (*cp1 != '.'))
6486 if ((cp1) != NULL) {
6487 /* There's more than one directory in the path. Just roll back. */
6489 strcpy(buf, my_esa);
6492 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6493 /* Go back and expand rooted logical name */
6494 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6495 #ifdef NAM$M_NO_SHORT_UPCASE
6496 if (decc_efs_case_preserve)
6497 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6499 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6500 sts = rms_free_search_context(&dirfab);
6504 PerlMem_free(trndir);
6505 PerlMem_free(vmsdir);
6507 set_vaxc_errno(dirfab.fab$l_sts);
6511 /* This changes the length of the string of course */
6513 my_esa_len = rms_nam_esll(dirnam);
6515 my_esa_len = rms_nam_esl(dirnam);
6518 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6519 cp1 = strstr(my_esa,"][");
6520 if (!cp1) cp1 = strstr(my_esa,"]<");
6521 dirlen = cp1 - my_esa;
6522 memcpy(buf, my_esa, dirlen);
6523 if (!strncmp(cp1+2,"000000]",7)) {
6524 buf[dirlen-1] = '\0';
6525 /* fix-me Not full ODS-5, just extra dots in directories for now */
6526 cp1 = buf + dirlen - 1;
6532 if (*(cp1-1) != '^')
6537 if (*cp1 == '.') *cp1 = ']';
6539 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6540 memmove(cp1+1,"000000]",7);
6544 memmove(buf+dirlen, cp1+2, retlen-dirlen);
6546 /* Convert last '.' to ']' */
6548 while (*cp != '[') {
6551 /* Do not trip on extra dots in ODS-5 directories */
6552 if ((cp1 == buf) || (*(cp1-1) != '^'))
6556 if (*cp1 == '.') *cp1 = ']';
6558 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6559 memmove(cp1+1,"000000]",7);
6563 else { /* This is a top-level dir. Add the MFD to the path. */
6566 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
6567 strcpy(cp2,":[000000]");
6572 sts = rms_free_search_context(&dirfab);
6573 /* We've set up the string up through the filename. Add the
6574 type and version, and we're done. */
6575 strcat(buf,".DIR;1");
6577 /* $PARSE may have upcased filespec, so convert output to lower
6578 * case if input contained any lowercase characters. */
6579 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
6580 PerlMem_free(trndir);
6584 PerlMem_free(vmsdir);
6587 } /* end of int_fileify_dirspec() */
6590 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6591 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6593 static char __fileify_retbuf[VMS_MAXRSS];
6594 char * fileified, *ret_spec, *ret_buf;
6598 if (ret_buf == NULL) {
6600 Newx(fileified, VMS_MAXRSS, char);
6601 if (fileified == NULL)
6602 _ckvmssts(SS$_INSFMEM);
6603 ret_buf = fileified;
6605 ret_buf = __fileify_retbuf;
6609 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6611 if (ret_spec == NULL) {
6612 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6614 Safefree(fileified);
6618 } /* end of do_fileify_dirspec() */
6621 /* External entry points */
6622 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6623 { return do_fileify_dirspec(dir,buf,0,NULL); }
6624 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6625 { return do_fileify_dirspec(dir,buf,1,NULL); }
6626 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6627 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6628 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6629 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6631 static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6632 char * v_spec, int v_len, char * r_spec, int r_len,
6633 char * d_spec, int d_len, char * n_spec, int n_len,
6634 char * e_spec, int e_len, char * vs_spec, int vs_len) {
6636 /* VMS specification - Try to do this the simple way */
6637 if ((v_len + r_len > 0) || (d_len > 0)) {
6640 /* No name or extension component, already a directory */
6641 if ((n_len + e_len + vs_len) == 0) {
6646 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6647 /* This results from catfile() being used instead of catdir() */
6648 /* So even though it should not work, we need to allow it */
6650 /* If this is .DIR;1 then do a simple conversion */
6651 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6652 if (is_dir || (e_len == 0) && (d_len > 0)) {
6654 len = v_len + r_len + d_len - 1;
6655 char dclose = d_spec[d_len - 1];
6656 strncpy(buf, dir, len);
6659 strncpy(&buf[len], n_spec, n_len);
6662 buf[len + 1] = '\0';
6667 else if (d_len > 0) {
6668 /* In the olden days, a directory needed to have a .DIR */
6669 /* extension to be a valid directory, but now it could */
6670 /* be a symbolic link */
6672 len = v_len + r_len + d_len - 1;
6673 char dclose = d_spec[d_len - 1];
6674 strncpy(buf, dir, len);
6677 strncpy(&buf[len], n_spec, n_len);
6680 if (decc_efs_charset) {
6683 strncpy(&buf[len], e_spec, e_len);
6686 set_vaxc_errno(RMS$_DIR);
6692 buf[len + 1] = '\0';
6697 set_vaxc_errno(RMS$_DIR);
6703 set_vaxc_errno(RMS$_DIR);
6709 /* Internal routine to make sure or convert a directory to be in a */
6710 /* path specification. No utf8 flag because it is not changed or used */
6711 static char *int_pathify_dirspec(const char *dir, char *buf)
6713 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6714 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6715 char * exp_spec, *ret_spec;
6717 unsigned short int trnlnm_iter_count;
6721 if (vms_debug_fileify) {
6723 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6725 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6728 /* We may need to lower case the result if we translated */
6729 /* a logical name or got the current working directory */
6732 if (!dir || !*dir) {
6734 set_vaxc_errno(SS$_BADPARAM);
6738 trndir = PerlMem_malloc(VMS_MAXRSS);
6740 _ckvmssts_noperl(SS$_INSFMEM);
6742 /* If no directory specified use the current default */
6744 strcpy(trndir, dir);
6746 getcwd(trndir, VMS_MAXRSS - 1);
6750 /* now deal with bare names that could be logical names */
6751 trnlnm_iter_count = 0;
6752 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6753 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6754 trnlnm_iter_count++;
6756 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6758 trnlen = strlen(trndir);
6760 /* Trap simple rooted lnms, and return lnm:[000000] */
6761 if (!strcmp(trndir+trnlen-2,".]")) {
6763 strcat(buf, ":[000000]");
6764 PerlMem_free(trndir);
6766 if (vms_debug_fileify) {
6767 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6773 /* At this point we do not work with *dir, but the copy in *trndir */
6775 if (need_to_lower && !decc_efs_case_preserve) {
6776 /* Legacy mode, lower case the returned value */
6777 __mystrtolower(trndir);
6781 /* Some special cases, '..', '.' */
6783 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6784 /* Force UNIX filespec */
6788 /* Is this Unix or VMS format? */
6789 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6790 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6791 &e_len, &vs_spec, &vs_len);
6794 /* Just a filename? */
6795 if ((v_len + r_len + d_len) == 0) {
6797 /* Now we have a problem, this could be Unix or VMS */
6798 /* We have to guess. .DIR usually means VMS */
6800 /* In UNIX report mode, the .DIR extension is removed */
6801 /* if one shows up, it is for a non-directory or a directory */
6802 /* in EFS charset mode */
6804 /* So if we are in Unix report mode, assume that this */
6805 /* is a relative Unix directory specification */
6808 if (!decc_filename_unix_report && decc_efs_charset) {
6810 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6813 /* Traditional mode, assume .DIR is directory */
6816 strncpy(&buf[2], n_spec, n_len);
6817 buf[n_len + 2] = ']';
6818 buf[n_len + 3] = '\0';
6819 PerlMem_free(trndir);
6820 if (vms_debug_fileify) {
6822 "int_pathify_dirspec: buf = %s\n",
6832 ret_spec = int_pathify_dirspec_simple(trndir, buf,
6833 v_spec, v_len, r_spec, r_len,
6834 d_spec, d_len, n_spec, n_len,
6835 e_spec, e_len, vs_spec, vs_len);
6837 if (ret_spec != NULL) {
6838 PerlMem_free(trndir);
6839 if (vms_debug_fileify) {
6841 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6846 /* Simple way did not work, which means that a logical name */
6847 /* was present for the directory specification. */
6848 /* Need to use an rmsexpand variant to decode it completely */
6849 exp_spec = PerlMem_malloc(VMS_MAXRSS);
6850 if (exp_spec == NULL)
6851 _ckvmssts_noperl(SS$_INSFMEM);
6853 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6854 if (ret_spec != NULL) {
6855 sts = vms_split_path(exp_spec, &v_spec, &v_len,
6856 &r_spec, &r_len, &d_spec, &d_len,
6857 &n_spec, &n_len, &e_spec,
6858 &e_len, &vs_spec, &vs_len);
6860 ret_spec = int_pathify_dirspec_simple(
6861 exp_spec, buf, 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) && (!decc_efs_case_preserve)) {
6866 /* Legacy mode, lower case the returned value */
6867 __mystrtolower(ret_spec);
6870 set_vaxc_errno(RMS$_DIR);
6875 PerlMem_free(exp_spec);
6876 PerlMem_free(trndir);
6877 if (vms_debug_fileify) {
6878 if (ret_spec == NULL)
6879 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6882 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6887 /* Unix specification, Could be trivial conversion */
6889 dir_len = strlen(trndir);
6891 /* If the extended file character set is in effect */
6892 /* then pathify is simple */
6894 if (!decc_efs_charset) {
6895 /* Have to deal with traiing '.dir' or extra '.' */
6896 /* that should not be there in legacy mode, but is */
6902 lastslash = strrchr(trndir, '/');
6903 if (lastslash == NULL)
6910 /* '..' or '.' are valid directory components */
6912 if (lastslash[0] == '.') {
6913 if (lastslash[1] == '\0') {
6915 } else if (lastslash[1] == '.') {
6916 if (lastslash[2] == '\0') {
6919 /* And finally allow '...' */
6920 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6928 lastdot = strrchr(lastslash, '.');
6930 if (lastdot != NULL) {
6933 /* '.dir' is discarded, and any other '.' is invalid */
6934 e_len = strlen(lastdot);
6936 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6939 dir_len = dir_len - 4;
6945 strcpy(buf, trndir);
6946 if (buf[dir_len - 1] != '/') {
6948 buf[dir_len + 1] = '\0';
6951 /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6952 if (!decc_efs_charset) {
6955 if (str[0] == '.') {
6958 while ((dots[cnt] == '.') && (cnt < 3))
6961 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6967 for (; *str; ++str) {
6968 while (*str == '/') {
6974 /* Have to skip up to three dots which could be */
6975 /* directories, 3 dots being a VMS extension for Perl */
6978 while ((dots[cnt] == '.') && (cnt < 3)) {
6981 if (dots[cnt] == '\0')
6983 if ((cnt > 1) && (dots[cnt] != '/')) {
6989 /* too many dots? */
6990 if ((cnt == 0) || (cnt > 3)) {
6994 if (!dir_start && (*str == '.')) {
6999 PerlMem_free(trndir);
7001 if (vms_debug_fileify) {
7002 if (ret_spec == NULL)
7003 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
7006 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
7012 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
7013 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
7015 static char __pathify_retbuf[VMS_MAXRSS];
7016 char * pathified, *ret_spec, *ret_buf;
7020 if (ret_buf == NULL) {
7022 Newx(pathified, VMS_MAXRSS, char);
7023 if (pathified == NULL)
7024 _ckvmssts(SS$_INSFMEM);
7025 ret_buf = pathified;
7027 ret_buf = __pathify_retbuf;
7031 ret_spec = int_pathify_dirspec(dir, ret_buf);
7033 if (ret_spec == NULL) {
7034 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7036 Safefree(pathified);
7041 } /* end of do_pathify_dirspec() */
7044 /* External entry points */
7045 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
7046 { return do_pathify_dirspec(dir,buf,0,NULL); }
7047 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
7048 { return do_pathify_dirspec(dir,buf,1,NULL); }
7049 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
7050 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
7051 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
7052 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
7054 /* Internal tounixspec routine that does not use a thread context */
7055 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
7056 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
7058 char *dirend, *cp1, *cp3, *tmp;
7060 int devlen, dirlen, retlen = VMS_MAXRSS;
7061 int expand = 1; /* guarantee room for leading and trailing slashes */
7062 unsigned short int trnlnm_iter_count;
7064 if (utf8_fl != NULL)
7067 if (vms_debug_fileify) {
7069 fprintf(stderr, "int_tounixspec: spec = NULL\n");
7071 fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
7077 set_vaxc_errno(SS$_BADPARAM);
7080 if (strlen(spec) > (VMS_MAXRSS-1)) {
7082 set_vaxc_errno(SS$_BUFFEROVF);
7086 /* New VMS specific format needs translation
7087 * glob passes filenames with trailing '\n' and expects this preserved.
7089 if (decc_posix_compliant_pathnames) {
7090 if (strncmp(spec, "\"^UP^", 5) == 0) {
7096 tunix = PerlMem_malloc(VMS_MAXRSS);
7097 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7098 strcpy(tunix, spec);
7099 tunix_len = strlen(tunix);
7101 if (tunix[tunix_len - 1] == '\n') {
7102 tunix[tunix_len - 1] = '\"';
7103 tunix[tunix_len] = '\0';
7107 uspec = decc$translate_vms(tunix);
7108 PerlMem_free(tunix);
7109 if ((int)uspec > 0) {
7115 /* If we can not translate it, makemaker wants as-is */
7123 cmp_rslt = 0; /* Presume VMS */
7124 cp1 = strchr(spec, '/');
7128 /* Look for EFS ^/ */
7129 if (decc_efs_charset) {
7130 while (cp1 != NULL) {
7133 /* Found illegal VMS, assume UNIX */
7138 cp1 = strchr(cp1, '/');
7142 /* Look for "." and ".." */
7143 if (decc_filename_unix_report) {
7144 if (spec[0] == '.') {
7145 if ((spec[1] == '\0') || (spec[1] == '\n')) {
7149 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
7155 /* This is already UNIX or at least nothing VMS understands */
7158 if (vms_debug_fileify) {
7159 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7166 dirend = strrchr(spec,']');
7167 if (dirend == NULL) dirend = strrchr(spec,'>');
7168 if (dirend == NULL) dirend = strchr(spec,':');
7169 if (dirend == NULL) {
7171 if (vms_debug_fileify) {
7172 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7177 /* Special case 1 - sys$posix_root = / */
7178 #if __CRTL_VER >= 70000000
7179 if (!decc_disable_posix_root) {
7180 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7188 /* Special case 2 - Convert NLA0: to /dev/null */
7189 #if __CRTL_VER < 70000000
7190 cmp_rslt = strncmp(spec,"NLA0:", 5);
7192 cmp_rslt = strncmp(spec,"nla0:", 5);
7194 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7196 if (cmp_rslt == 0) {
7197 strcpy(rslt, "/dev/null");
7200 if (spec[6] != '\0') {
7207 /* Also handle special case "SYS$SCRATCH:" */
7208 #if __CRTL_VER < 70000000
7209 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
7211 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
7213 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7215 tmp = PerlMem_malloc(VMS_MAXRSS);
7216 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7217 if (cmp_rslt == 0) {
7220 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7222 strcpy(rslt, "/tmp");
7225 if (spec[12] != '\0') {
7233 if (*cp2 != '[' && *cp2 != '<') {
7236 else { /* the VMS spec begins with directories */
7238 if (*cp2 == ']' || *cp2 == '>') {
7239 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
7243 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7244 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7246 if (vms_debug_fileify) {
7247 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7251 trnlnm_iter_count = 0;
7254 while (*cp3 != ':' && *cp3) cp3++;
7256 if (strchr(cp3,']') != NULL) break;
7257 trnlnm_iter_count++;
7258 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7259 } while (vmstrnenv(tmp,tmp,0,fildev,0));
7264 *(cp1++) = *(cp3++);
7265 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7267 set_errno(ENAMETOOLONG);
7268 set_vaxc_errno(SS$_BUFFEROVF);
7269 if (vms_debug_fileify) {
7270 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7272 return NULL; /* No room */
7277 if ((*cp2 == '^')) {
7278 /* EFS file escape, pass the next character as is */
7279 /* Fix me: HEX encoding for Unicode not implemented */
7282 else if ( *cp2 == '.') {
7283 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7284 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7291 for (; cp2 <= dirend; cp2++) {
7292 if ((*cp2 == '^')) {
7293 /* EFS file escape, pass the next character as is */
7294 /* Fix me: HEX encoding for Unicode not implemented */
7295 *(cp1++) = *(++cp2);
7296 /* An escaped dot stays as is -- don't convert to slash */
7297 if (*cp2 == '.') cp2++;
7301 if (*(cp2+1) == '[') cp2++;
7303 else if (*cp2 == ']' || *cp2 == '>') {
7304 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7306 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7308 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7309 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7310 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7311 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7312 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7314 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7315 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7319 else if (*cp2 == '-') {
7320 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7321 while (*cp2 == '-') {
7323 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7325 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7326 /* filespecs like */
7327 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
7328 if (vms_debug_fileify) {
7329 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7334 else *(cp1++) = *cp2;
7336 else *(cp1++) = *cp2;
7338 /* Translate the rest of the filename. */
7343 /* Fixme - for compatibility with the CRTL we should be removing */
7344 /* spaces from the file specifications, but this may show that */
7345 /* some tests that were appearing to pass are not really passing */
7351 /* Fix me hex expansions not implemented */
7352 cp2++; /* '^.' --> '.' and other. */
7358 *(cp1++) = *(cp2++);
7363 if (decc_filename_unix_no_version) {
7364 /* Easy, drop the version */
7369 /* Punt - passing the version as a dot will probably */
7370 /* break perl in weird ways, but so did passing */
7371 /* through the ; as a version. Follow the CRTL and */
7372 /* hope for the best. */
7379 /* We will need to fix this properly later */
7380 /* As Perl may be installed on an ODS-5 volume, but not */
7381 /* have the EFS_CHARSET enabled, it still may encounter */
7382 /* filenames with extra dots in them, and a precedent got */
7383 /* set which allowed them to work, that we will uphold here */
7384 /* If extra dots are present in a name and no ^ is on them */
7385 /* VMS assumes that the first one is the extension delimiter */
7386 /* the rest have an implied ^. */
7388 /* this is also a conflict as the . is also a version */
7389 /* delimiter in VMS, */
7391 *(cp1++) = *(cp2++);
7395 /* This is an extension */
7396 if (decc_readdir_dropdotnotype) {
7398 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7399 /* Drop the dot for the extension */
7407 *(cp1++) = *(cp2++);
7412 /* This still leaves /000000/ when working with a
7413 * VMS device root or concealed root.
7419 ulen = strlen(rslt);
7421 /* Get rid of "000000/ in rooted filespecs */
7423 zeros = strstr(rslt, "/000000/");
7424 if (zeros != NULL) {
7426 mlen = ulen - (zeros - rslt) - 7;
7427 memmove(zeros, &zeros[7], mlen);
7434 if (vms_debug_fileify) {
7435 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7439 } /* end of int_tounixspec() */
7442 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7443 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7445 static char __tounixspec_retbuf[VMS_MAXRSS];
7446 char * unixspec, *ret_spec, *ret_buf;
7450 if (ret_buf == NULL) {
7452 Newx(unixspec, VMS_MAXRSS, char);
7453 if (unixspec == NULL)
7454 _ckvmssts(SS$_INSFMEM);
7457 ret_buf = __tounixspec_retbuf;
7461 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7463 if (ret_spec == NULL) {
7464 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7471 } /* end of do_tounixspec() */
7473 /* External entry points */
7474 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7475 { return do_tounixspec(spec,buf,0, NULL); }
7476 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7477 { return do_tounixspec(spec,buf,1, NULL); }
7478 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7479 { return do_tounixspec(spec,buf,0, utf8_fl); }
7480 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7481 { return do_tounixspec(spec,buf,1, utf8_fl); }
7483 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7486 This procedure is used to identify if a path is based in either
7487 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7488 it returns the OpenVMS format directory for it.
7490 It is expecting specifications of only '/' or '/xxxx/'
7492 If a posix root does not exist, or 'xxxx' is not a directory
7493 in the posix root, it returns a failure.
7495 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7497 It is used only internally by posix_to_vmsspec_hardway().
7500 static int posix_root_to_vms
7501 (char *vmspath, int vmspath_len,
7502 const char *unixpath,
7503 const int * utf8_fl)
7506 struct FAB myfab = cc$rms_fab;
7507 rms_setup_nam(mynam);
7508 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7509 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7510 char * esa, * esal, * rsa, * rsal;
7517 unixlen = strlen(unixpath);
7522 #if __CRTL_VER >= 80200000
7523 /* If not a posix spec already, convert it */
7524 if (decc_posix_compliant_pathnames) {
7525 if (strncmp(unixpath,"\"^UP^",5) != 0) {
7526 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7529 /* This is already a VMS specification, no conversion */
7531 strncpy(vmspath,unixpath, vmspath_len);
7540 /* Check to see if this is under the POSIX root */
7541 if (decc_disable_posix_root) {
7545 /* Skip leading / */
7546 if (unixpath[0] == '/') {
7552 strcpy(vmspath,"SYS$POSIX_ROOT:");
7554 /* If this is only the / , or blank, then... */
7555 if (unixpath[0] == '\0') {
7556 /* by definition, this is the answer */
7560 /* Need to look up a directory */
7564 /* Copy and add '^' escape characters as needed */
7567 while (unixpath[i] != 0) {
7570 j += copy_expand_unix_filename_escape
7571 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7575 path_len = strlen(vmspath);
7576 if (vmspath[path_len - 1] == '/')
7578 vmspath[path_len] = ']';
7580 vmspath[path_len] = '\0';
7583 vmspath[vmspath_len] = 0;
7584 if (unixpath[unixlen - 1] == '/')
7586 esal = PerlMem_malloc(VMS_MAXRSS);
7587 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7588 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7589 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7590 rsal = PerlMem_malloc(VMS_MAXRSS);
7591 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7592 rsa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7593 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7594 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7595 rms_bind_fab_nam(myfab, mynam);
7596 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7597 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7598 if (decc_efs_case_preserve)
7599 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7600 #ifdef NAML$M_OPEN_SPECIAL
7601 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7604 /* Set up the remaining naml fields */
7605 sts = sys$parse(&myfab);
7607 /* It failed! Try again as a UNIX filespec */
7616 /* get the Device ID and the FID */
7617 sts = sys$search(&myfab);
7619 /* These are no longer needed */
7624 /* on any failure, returned the POSIX ^UP^ filespec */
7629 specdsc.dsc$a_pointer = vmspath;
7630 specdsc.dsc$w_length = vmspath_len;
7632 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7633 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7634 sts = lib$fid_to_name
7635 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7637 /* on any failure, returned the POSIX ^UP^ filespec */
7639 /* This can happen if user does not have permission to read directories */
7640 if (strncmp(unixpath,"\"^UP^",5) != 0)
7641 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7643 strcpy(vmspath, unixpath);
7646 vmspath[specdsc.dsc$w_length] = 0;
7648 /* Are we expecting a directory? */
7649 if (dir_flag != 0) {
7655 i = specdsc.dsc$w_length - 1;
7659 /* Version must be '1' */
7660 if (vmspath[i--] != '1')
7662 /* Version delimiter is one of ".;" */
7663 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7666 if (vmspath[i--] != 'R')
7668 if (vmspath[i--] != 'I')
7670 if (vmspath[i--] != 'D')
7672 if (vmspath[i--] != '.')
7674 eptr = &vmspath[i+1];
7676 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7677 if (vmspath[i-1] != '^') {
7685 /* Get rid of 6 imaginary zero directory filename */
7686 vmspath[i+1] = '\0';
7690 if (vmspath[i] == '0')
7704 /* /dev/mumble needs to be handled special.
7705 /dev/null becomes NLA0:, And there is the potential for other stuff
7706 like /dev/tty which may need to be mapped to something.
7710 slash_dev_special_to_vms
7711 (const char * unixptr,
7721 nextslash = strchr(unixptr, '/');
7722 len = strlen(unixptr);
7723 if (nextslash != NULL)
7724 len = nextslash - unixptr;
7725 cmp = strncmp("null", unixptr, 5);
7727 if (vmspath_len >= 6) {
7728 strcpy(vmspath, "_NLA0:");
7735 /* The built in routines do not understand perl's special needs, so
7736 doing a manual conversion from UNIX to VMS
7738 If the utf8_fl is not null and points to a non-zero value, then
7739 treat 8 bit characters as UTF-8.
7741 The sequence starting with '$(' and ending with ')' will be passed
7742 through with out interpretation instead of being escaped.
7745 static int posix_to_vmsspec_hardway
7746 (char *vmspath, int vmspath_len,
7747 const char *unixpath,
7752 const char *unixptr;
7753 const char *unixend;
7755 const char *lastslash;
7756 const char *lastdot;
7762 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7763 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7765 if (utf8_fl != NULL)
7771 /* Ignore leading "/" characters */
7772 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7775 unixlen = strlen(unixptr);
7777 /* Do nothing with blank paths */
7784 /* This could have a "^UP^ on the front */
7785 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7791 lastslash = strrchr(unixptr,'/');
7792 lastdot = strrchr(unixptr,'.');
7793 unixend = strrchr(unixptr,'\"');
7794 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7795 unixend = unixptr + unixlen;
7798 /* last dot is last dot or past end of string */
7799 if (lastdot == NULL)
7800 lastdot = unixptr + unixlen;
7802 /* if no directories, set last slash to beginning of string */
7803 if (lastslash == NULL) {
7804 lastslash = unixptr;
7807 /* Watch out for trailing "." after last slash, still a directory */
7808 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7809 lastslash = unixptr + unixlen;
7812 /* Watch out for traiing ".." after last slash, still a directory */
7813 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7814 lastslash = unixptr + unixlen;
7817 /* dots in directories are aways escaped */
7818 if (lastdot < lastslash)
7819 lastdot = unixptr + unixlen;
7822 /* if (unixptr < lastslash) then we are in a directory */
7829 /* Start with the UNIX path */
7830 if (*unixptr != '/') {
7831 /* relative paths */
7833 /* If allowing logical names on relative pathnames, then handle here */
7834 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7835 !decc_posix_compliant_pathnames) {
7841 /* Find the next slash */
7842 nextslash = strchr(unixptr,'/');
7844 esa = PerlMem_malloc(vmspath_len);
7845 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7847 trn = PerlMem_malloc(VMS_MAXRSS);
7848 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7850 if (nextslash != NULL) {
7852 seg_len = nextslash - unixptr;
7853 strncpy(esa, unixptr, seg_len);
7857 strcpy(esa, unixptr);
7858 seg_len = strlen(unixptr);
7860 /* trnlnm(section) */
7861 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7864 /* Now fix up the directory */
7866 /* Split up the path to find the components */
7867 sts = vms_split_path
7886 /* A logical name must be a directory or the full
7887 specification. It is only a full specification if
7888 it is the only component */
7889 if ((unixptr[seg_len] == '\0') ||
7890 (unixptr[seg_len+1] == '\0')) {
7892 /* Is a directory being required? */
7893 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7894 /* Not a logical name */
7899 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7900 /* This must be a directory */
7901 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7902 strcpy(vmsptr, esa);
7903 vmslen=strlen(vmsptr);
7904 vmsptr[vmslen] = ':';
7906 vmsptr[vmslen] = '\0';
7914 /* must be dev/directory - ignore version */
7915 if ((n_len + e_len) != 0)
7918 /* transfer the volume */
7919 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7920 strncpy(vmsptr, v_spec, v_len);
7926 /* unroot the rooted directory */
7927 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7929 r_spec[r_len - 1] = ']';
7931 /* This should not be there, but nothing is perfect */
7933 cmp = strcmp(&r_spec[1], "000000.");
7943 strncpy(vmsptr, r_spec, r_len);
7949 /* Bring over the directory. */
7951 ((d_len + vmslen) < vmspath_len)) {
7953 d_spec[d_len - 1] = ']';
7955 cmp = strcmp(&d_spec[1], "000000.");
7966 /* Remove the redundant root */
7974 strncpy(vmsptr, d_spec, d_len);
7988 if (lastslash > unixptr) {
7991 /* skip leading ./ */
7993 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7999 /* Are we still in a directory? */
8000 if (unixptr <= lastslash) {
8005 /* if not backing up, then it is relative forward. */
8006 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
8007 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
8015 /* Perl wants an empty directory here to tell the difference
8016 * between a DCL commmand and a filename
8025 /* Handle two special files . and .. */
8026 if (unixptr[0] == '.') {
8027 if (&unixptr[1] == unixend) {
8034 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
8045 else { /* Absolute PATH handling */
8049 /* Need to find out where root is */
8051 /* In theory, this procedure should never get an absolute POSIX pathname
8052 * that can not be found on the POSIX root.
8053 * In practice, that can not be relied on, and things will show up
8054 * here that are a VMS device name or concealed logical name instead.
8055 * So to make things work, this procedure must be tolerant.
8057 esa = PerlMem_malloc(vmspath_len);
8058 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8061 nextslash = strchr(&unixptr[1],'/');
8063 if (nextslash != NULL) {
8065 seg_len = nextslash - &unixptr[1];
8066 strncpy(vmspath, unixptr, seg_len + 1);
8067 vmspath[seg_len+1] = 0;
8070 cmp = strncmp(vmspath, "dev", 4);
8072 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
8073 if (sts = SS$_NORMAL)
8077 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
8080 if ($VMS_STATUS_SUCCESS(sts)) {
8081 /* This is verified to be a real path */
8083 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
8084 if ($VMS_STATUS_SUCCESS(sts)) {
8085 strcpy(vmspath, esa);
8086 vmslen = strlen(vmspath);
8087 vmsptr = vmspath + vmslen;
8089 if (unixptr < lastslash) {
8098 cmp = strcmp(rptr,"000000.");
8103 } /* removing 6 zeros */
8104 } /* vmslen < 7, no 6 zeros possible */
8105 } /* Not in a directory */
8106 } /* Posix root found */
8108 /* No posix root, fall back to default directory */
8109 strcpy(vmspath, "SYS$DISK:[");
8110 vmsptr = &vmspath[10];
8112 if (unixptr > lastslash) {
8121 } /* end of verified real path handling */
8126 /* Ok, we have a device or a concealed root that is not in POSIX
8127 * or we have garbage. Make the best of it.
8130 /* Posix to VMS destroyed this, so copy it again */
8131 strncpy(vmspath, &unixptr[1], seg_len);
8132 vmspath[seg_len] = 0;
8134 vmsptr = &vmsptr[vmslen];
8137 /* Now do we need to add the fake 6 zero directory to it? */
8139 if ((*lastslash == '/') && (nextslash < lastslash)) {
8140 /* No there is another directory */
8147 /* now we have foo:bar or foo:[000000]bar to decide from */
8148 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
8150 if (!islnm && !decc_posix_compliant_pathnames) {
8152 cmp = strncmp("bin", vmspath, 4);
8154 /* bin => SYS$SYSTEM: */
8155 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
8158 /* tmp => SYS$SCRATCH: */
8159 cmp = strncmp("tmp", vmspath, 4);
8161 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
8166 trnend = islnm ? islnm - 1 : 0;
8168 /* if this was a logical name, ']' or '>' must be present */
8169 /* if not a logical name, then assume a device and hope. */
8170 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
8172 /* if log name and trailing '.' then rooted - treat as device */
8173 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
8175 /* Fix me, if not a logical name, a device lookup should be
8176 * done to see if the device is file structured. If the device
8177 * is not file structured, the 6 zeros should not be put on.
8179 * As it is, perl is occasionally looking for dev:[000000]tty.
8180 * which looks a little strange.
8182 * Not that easy to detect as "/dev" may be file structured with
8183 * special device files.
8186 if ((add_6zero == 0) && (*nextslash == '/') &&
8187 (&nextslash[1] == unixend)) {
8188 /* No real directory present */
8193 /* Put the device delimiter on */
8196 unixptr = nextslash;
8199 /* Start directory if needed */
8200 if (!islnm || add_6zero) {
8206 /* add fake 000000] if needed */
8219 } /* non-POSIX translation */
8221 } /* End of relative/absolute path handling */
8223 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
8230 if (dir_start != 0) {
8232 /* First characters in a directory are handled special */
8233 while ((*unixptr == '/') ||
8234 ((*unixptr == '.') &&
8235 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8236 (&unixptr[1]==unixend)))) {
8241 /* Skip redundant / in specification */
8242 while ((*unixptr == '/') && (dir_start != 0)) {
8245 if (unixptr == lastslash)
8248 if (unixptr == lastslash)
8251 /* Skip redundant ./ characters */
8252 while ((*unixptr == '.') &&
8253 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8256 if (unixptr == lastslash)
8258 if (*unixptr == '/')
8261 if (unixptr == lastslash)
8264 /* Skip redundant ../ characters */
8265 while ((*unixptr == '.') && (unixptr[1] == '.') &&
8266 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8267 /* Set the backing up flag */
8273 unixptr++; /* first . */
8274 unixptr++; /* second . */
8275 if (unixptr == lastslash)
8277 if (*unixptr == '/') /* The slash */
8280 if (unixptr == lastslash)
8283 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8284 /* Not needed when VMS is pretending to be UNIX. */
8286 /* Is this loop stuck because of too many dots? */
8287 if (loop_flag == 0) {
8288 /* Exit the loop and pass the rest through */
8293 /* Are we done with directories yet? */
8294 if (unixptr >= lastslash) {
8296 /* Watch out for trailing dots */
8305 if (*unixptr == '/')
8309 /* Have we stopped backing up? */
8314 /* dir_start continues to be = 1 */
8316 if (*unixptr == '-') {
8318 *vmsptr++ = *unixptr++;
8322 /* Now are we done with directories yet? */
8323 if (unixptr >= lastslash) {
8325 /* Watch out for trailing dots */
8341 if (unixptr >= unixend)
8344 /* Normal characters - More EFS work probably needed */
8350 /* remove multiple / */
8351 while (unixptr[1] == '/') {
8354 if (unixptr == lastslash) {
8355 /* Watch out for trailing dots */
8367 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8368 /* Not needed when VMS is pretending to be UNIX. */
8372 if (unixptr != unixend)
8377 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8378 (&unixptr[1] == unixend)) {
8384 /* trailing dot ==> '^..' on VMS */
8385 if (unixptr == unixend) {
8393 *vmsptr++ = *unixptr++;
8397 if (quoted && (&unixptr[1] == unixend)) {
8401 in_cnt = copy_expand_unix_filename_escape
8402 (vmsptr, unixptr, &out_cnt, utf8_fl);
8412 in_cnt = copy_expand_unix_filename_escape
8413 (vmsptr, unixptr, &out_cnt, utf8_fl);
8420 /* Make sure directory is closed */
8421 if (unixptr == lastslash) {
8423 vmsptr2 = vmsptr - 1;
8425 if (*vmsptr2 != ']') {
8428 /* directories do not end in a dot bracket */
8429 if (*vmsptr2 == '.') {
8433 if (*vmsptr2 != '^') {
8434 vmsptr--; /* back up over the dot */
8442 /* Add a trailing dot if a file with no extension */
8443 vmsptr2 = vmsptr - 1;
8445 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8446 (*vmsptr2 != ')') && (*lastdot != '.')) {
8457 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8458 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8463 /* If a UTF8 flag is being passed, honor it */
8465 if (utf8_fl != NULL) {
8466 utf8_flag = *utf8_fl;
8471 /* If there is a possibility of UTF8, then if any UTF8 characters
8472 are present, then they must be converted to VTF-7
8474 result = strcpy(rslt, path); /* FIX-ME */
8477 result = strcpy(rslt, path);
8484 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8485 static char *int_tovmsspec
8486 (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8492 unsigned long int infront = 0, hasdir = 1;
8495 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8496 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8498 if (vms_debug_fileify) {
8500 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8502 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8506 /* If we fail, we should be setting errno */
8508 set_vaxc_errno(SS$_BADPARAM);
8511 rslt_len = VMS_MAXRSS-1;
8513 /* '.' and '..' are "[]" and "[-]" for a quick check */
8514 if (path[0] == '.') {
8515 if (path[1] == '\0') {
8517 if (utf8_flag != NULL)
8522 if (path[1] == '.' && path[2] == '\0') {
8524 if (utf8_flag != NULL)
8531 /* Posix specifications are now a native VMS format */
8532 /*--------------------------------------------------*/
8533 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8534 if (decc_posix_compliant_pathnames) {
8535 if (strncmp(path,"\"^UP^",5) == 0) {
8536 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8542 /* This is really the only way to see if this is already in VMS format */
8543 sts = vms_split_path
8558 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8559 replacement, because the above parse just took care of most of
8560 what is needed to do vmspath when the specification is already
8563 And if it is not already, it is easier to do the conversion as
8564 part of this routine than to call this routine and then work on
8568 /* If VMS punctuation was found, it is already VMS format */
8569 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8570 if (utf8_flag != NULL)
8573 if (vms_debug_fileify) {
8574 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8578 /* Now, what to do with trailing "." cases where there is no
8579 extension? If this is a UNIX specification, and EFS characters
8580 are enabled, then the trailing "." should be converted to a "^.".
8581 But if this was already a VMS specification, then it should be
8584 So in the case of ambiguity, leave the specification alone.
8588 /* If there is a possibility of UTF8, then if any UTF8 characters
8589 are present, then they must be converted to VTF-7
8591 if (utf8_flag != NULL)
8594 if (vms_debug_fileify) {
8595 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8600 dirend = strrchr(path,'/');
8602 if (dirend == NULL) {
8606 /* If we get here with no UNIX directory delimiters, then this is
8607 not a complete file specification, either garbage a UNIX glob
8608 specification that can not be converted to a VMS wildcard, or
8609 it a UNIX shell macro. MakeMaker wants shell macros passed
8612 utf8 flag setting needs to be preserved.
8617 macro_start = strchr(path,'$');
8618 if (macro_start != NULL) {
8619 if (macro_start[1] == '(') {
8623 if ((decc_efs_charset == 0) || (has_macro)) {
8625 if (vms_debug_fileify) {
8626 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8632 /* If POSIX mode active, handle the conversion */
8633 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8634 if (decc_efs_charset) {
8635 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8636 if (vms_debug_fileify) {
8637 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8643 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
8644 if (!*(dirend+2)) dirend +=2;
8645 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8646 if (decc_efs_charset == 0) {
8647 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8653 lastdot = strrchr(cp2,'.');
8659 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8661 if (decc_disable_posix_root) {
8662 strcpy(rslt,"sys$disk:[000000]");
8665 strcpy(rslt,"sys$posix_root:[000000]");
8667 if (utf8_flag != NULL)
8669 if (vms_debug_fileify) {
8670 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8674 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8676 trndev = PerlMem_malloc(VMS_MAXRSS);
8677 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8678 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8680 /* DECC special handling */
8682 if (strcmp(rslt,"bin") == 0) {
8683 strcpy(rslt,"sys$system");
8686 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8688 else if (strcmp(rslt,"tmp") == 0) {
8689 strcpy(rslt,"sys$scratch");
8692 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8694 else if (!decc_disable_posix_root) {
8695 strcpy(rslt, "sys$posix_root");
8699 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8700 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8702 else if (strcmp(rslt,"dev") == 0) {
8703 if (strncmp(cp2,"/null", 5) == 0) {
8704 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8705 strcpy(rslt,"NLA0");
8709 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8715 trnend = islnm ? strlen(trndev) - 1 : 0;
8716 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8717 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8718 /* If the first element of the path is a logical name, determine
8719 * whether it has to be translated so we can add more directories. */
8720 if (!islnm || rooted) {
8723 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8727 if (cp2 != dirend) {
8728 strcpy(rslt,trndev);
8729 cp1 = rslt + trnend;
8736 if (decc_disable_posix_root) {
8742 PerlMem_free(trndev);
8747 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8748 cp2 += 2; /* skip over "./" - it's redundant */
8749 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8751 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8752 *(cp1++) = '-'; /* "../" --> "-" */
8755 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8756 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8757 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8758 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8761 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8762 /* Escape the extra dots in EFS file specifications */
8765 if (cp2 > dirend) cp2 = dirend;
8767 else *(cp1++) = '.';
8769 for (; cp2 < dirend; cp2++) {
8771 if (*(cp2-1) == '/') continue;
8772 if (*(cp1-1) != '.') *(cp1++) = '.';
8775 else if (!infront && *cp2 == '.') {
8776 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8777 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
8778 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8779 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
8780 else if (*(cp1-2) == '[') *(cp1-1) = '-';
8781 else { /* back up over previous directory name */
8783 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
8784 if (*(cp1-1) == '[') {
8785 memcpy(cp1,"000000.",7);
8790 if (cp2 == dirend) break;
8792 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8793 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8794 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8795 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8797 *(cp1++) = '.'; /* Simulate trailing '/' */
8798 cp2 += 2; /* for loop will incr this to == dirend */
8800 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8803 if (decc_efs_charset == 0)
8804 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8806 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
8812 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
8814 if (decc_efs_charset == 0)
8821 else *(cp1++) = *cp2;
8825 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8826 if (hasdir) *(cp1++) = ']';
8827 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
8828 /* fixme for ODS5 */
8835 if (decc_efs_charset == 0)
8846 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8847 decc_readdir_dropdotnotype) {
8852 /* trailing dot ==> '^..' on VMS */
8859 *(cp1++) = *(cp2++);
8864 /* This could be a macro to be passed through */
8865 *(cp1++) = *(cp2++);
8867 const char * save_cp2;
8871 /* paranoid check */
8877 *(cp1++) = *(cp2++);
8878 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8879 *(cp1++) = *(cp2++);
8880 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8881 *(cp1++) = *(cp2++);
8884 *(cp1++) = *(cp2++);
8888 if (is_macro == 0) {
8889 /* Not really a macro - never mind */
8902 /* Don't escape again if following character is
8903 * already something we escape.
8905 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8906 *(cp1++) = *(cp2++);
8909 /* But otherwise fall through and escape it. */
8927 *(cp1++) = *(cp2++);
8930 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
8931 * which is wrong. UNIX notation should be ".dir." unless
8932 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8933 * changing this behavior could break more things at this time.
8934 * efs character set effectively does not allow "." to be a version
8935 * delimiter as a further complication about changing this.
8937 if (decc_filename_unix_report != 0) {
8940 *(cp1++) = *(cp2++);
8943 *(cp1++) = *(cp2++);
8946 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8950 /* Fix me for "^]", but that requires making sure that you do
8951 * not back up past the start of the filename
8953 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8958 if (utf8_flag != NULL)
8960 if (vms_debug_fileify) {
8961 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8965 } /* end of int_tovmsspec() */
8968 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8969 static char *mp_do_tovmsspec
8970 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
8971 static char __tovmsspec_retbuf[VMS_MAXRSS];
8972 char * vmsspec, *ret_spec, *ret_buf;
8976 if (ret_buf == NULL) {
8978 Newx(vmsspec, VMS_MAXRSS, char);
8979 if (vmsspec == NULL)
8980 _ckvmssts(SS$_INSFMEM);
8983 ret_buf = __tovmsspec_retbuf;
8987 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8989 if (ret_spec == NULL) {
8990 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8997 } /* end of mp_do_tovmsspec() */
8999 /* External entry points */
9000 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
9001 { return do_tovmsspec(path,buf,0,NULL); }
9002 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
9003 { return do_tovmsspec(path,buf,1,NULL); }
9004 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9005 { return do_tovmsspec(path,buf,0,utf8_fl); }
9006 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9007 { return do_tovmsspec(path,buf,1,utf8_fl); }
9009 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
9010 /* Internal routine for use with out an explict context present */
9011 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
9013 char * ret_spec, *pathified;
9018 pathified = PerlMem_malloc(VMS_MAXRSS);
9019 if (pathified == NULL)
9020 _ckvmssts_noperl(SS$_INSFMEM);
9022 ret_spec = int_pathify_dirspec(path, pathified);
9024 if (ret_spec == NULL) {
9025 PerlMem_free(pathified);
9029 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
9031 PerlMem_free(pathified);
9036 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
9037 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
9038 static char __tovmspath_retbuf[VMS_MAXRSS];
9040 char *pathified, *vmsified, *cp;
9042 if (path == NULL) return NULL;
9043 pathified = PerlMem_malloc(VMS_MAXRSS);
9044 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
9045 if (int_pathify_dirspec(path, pathified) == NULL) {
9046 PerlMem_free(pathified);
9052 Newx(vmsified, VMS_MAXRSS, char);
9053 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
9054 PerlMem_free(pathified);
9055 if (vmsified) Safefree(vmsified);
9058 PerlMem_free(pathified);
9063 vmslen = strlen(vmsified);
9064 Newx(cp,vmslen+1,char);
9065 memcpy(cp,vmsified,vmslen);
9071 strcpy(__tovmspath_retbuf,vmsified);
9073 return __tovmspath_retbuf;
9076 } /* end of do_tovmspath() */
9078 /* External entry points */
9079 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
9080 { return do_tovmspath(path,buf,0, NULL); }
9081 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
9082 { return do_tovmspath(path,buf,1, NULL); }
9083 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
9084 { return do_tovmspath(path,buf,0,utf8_fl); }
9085 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
9086 { return do_tovmspath(path,buf,1,utf8_fl); }
9089 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
9090 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
9091 static char __tounixpath_retbuf[VMS_MAXRSS];
9093 char *pathified, *unixified, *cp;
9095 if (path == NULL) return NULL;
9096 pathified = PerlMem_malloc(VMS_MAXRSS);
9097 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
9098 if (int_pathify_dirspec(path, pathified) == NULL) {
9099 PerlMem_free(pathified);
9105 Newx(unixified, VMS_MAXRSS, char);
9107 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
9108 PerlMem_free(pathified);
9109 if (unixified) Safefree(unixified);
9112 PerlMem_free(pathified);
9117 unixlen = strlen(unixified);
9118 Newx(cp,unixlen+1,char);
9119 memcpy(cp,unixified,unixlen);
9121 Safefree(unixified);
9125 strcpy(__tounixpath_retbuf,unixified);
9126 Safefree(unixified);
9127 return __tounixpath_retbuf;
9130 } /* end of do_tounixpath() */
9132 /* External entry points */
9133 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
9134 { return do_tounixpath(path,buf,0,NULL); }
9135 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
9136 { return do_tounixpath(path,buf,1,NULL); }
9137 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9138 { return do_tounixpath(path,buf,0,utf8_fl); }
9139 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9140 { return do_tounixpath(path,buf,1,utf8_fl); }
9143 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
9145 *****************************************************************************
9147 * Copyright (C) 1989-1994, 2007 by *
9148 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
9150 * Permission is hereby granted for the reproduction of this software *
9151 * on condition that this copyright notice is included in source *
9152 * distributions of the software. The code may be modified and *
9153 * distributed under the same terms as Perl itself. *
9155 * 27-Aug-1994 Modified for inclusion in perl5 *
9156 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
9157 *****************************************************************************
9161 * getredirection() is intended to aid in porting C programs
9162 * to VMS (Vax-11 C). The native VMS environment does not support
9163 * '>' and '<' I/O redirection, or command line wild card expansion,
9164 * or a command line pipe mechanism using the '|' AND background
9165 * command execution '&'. All of these capabilities are provided to any
9166 * C program which calls this procedure as the first thing in the
9168 * The piping mechanism will probably work with almost any 'filter' type
9169 * of program. With suitable modification, it may useful for other
9170 * portability problems as well.
9172 * Author: Mark Pizzolato (mark AT infocomm DOT com)
9176 struct list_item *next;
9180 static void add_item(struct list_item **head,
9181 struct list_item **tail,
9185 static void mp_expand_wild_cards(pTHX_ char *item,
9186 struct list_item **head,
9187 struct list_item **tail,
9190 static int background_process(pTHX_ int argc, char **argv);
9192 static void pipe_and_fork(pTHX_ char **cmargv);
9194 /*{{{ void getredirection(int *ac, char ***av)*/
9196 mp_getredirection(pTHX_ int *ac, char ***av)
9198 * Process vms redirection arg's. Exit if any error is seen.
9199 * If getredirection() processes an argument, it is erased
9200 * from the vector. getredirection() returns a new argc and argv value.
9201 * In the event that a background command is requested (by a trailing "&"),
9202 * this routine creates a background subprocess, and simply exits the program.
9204 * Warning: do not try to simplify the code for vms. The code
9205 * presupposes that getredirection() is called before any data is
9206 * read from stdin or written to stdout.
9208 * Normal usage is as follows:
9214 * getredirection(&argc, &argv);
9218 int argc = *ac; /* Argument Count */
9219 char **argv = *av; /* Argument Vector */
9220 char *ap; /* Argument pointer */
9221 int j; /* argv[] index */
9222 int item_count = 0; /* Count of Items in List */
9223 struct list_item *list_head = 0; /* First Item in List */
9224 struct list_item *list_tail; /* Last Item in List */
9225 char *in = NULL; /* Input File Name */
9226 char *out = NULL; /* Output File Name */
9227 char *outmode = "w"; /* Mode to Open Output File */
9228 char *err = NULL; /* Error File Name */
9229 char *errmode = "w"; /* Mode to Open Error File */
9230 int cmargc = 0; /* Piped Command Arg Count */
9231 char **cmargv = NULL;/* Piped Command Arg Vector */
9234 * First handle the case where the last thing on the line ends with
9235 * a '&'. This indicates the desire for the command to be run in a
9236 * subprocess, so we satisfy that desire.
9239 if (0 == strcmp("&", ap))
9240 exit(background_process(aTHX_ --argc, argv));
9241 if (*ap && '&' == ap[strlen(ap)-1])
9243 ap[strlen(ap)-1] = '\0';
9244 exit(background_process(aTHX_ argc, argv));
9247 * Now we handle the general redirection cases that involve '>', '>>',
9248 * '<', and pipes '|'.
9250 for (j = 0; j < argc; ++j)
9252 if (0 == strcmp("<", argv[j]))
9256 fprintf(stderr,"No input file after < on command line");
9257 exit(LIB$_WRONUMARG);
9262 if ('<' == *(ap = argv[j]))
9267 if (0 == strcmp(">", ap))
9271 fprintf(stderr,"No output file after > on command line");
9272 exit(LIB$_WRONUMARG);
9291 fprintf(stderr,"No output file after > or >> on command line");
9292 exit(LIB$_WRONUMARG);
9296 if (('2' == *ap) && ('>' == ap[1]))
9313 fprintf(stderr,"No output file after 2> or 2>> on command line");
9314 exit(LIB$_WRONUMARG);
9318 if (0 == strcmp("|", argv[j]))
9322 fprintf(stderr,"No command into which to pipe on command line");
9323 exit(LIB$_WRONUMARG);
9325 cmargc = argc-(j+1);
9326 cmargv = &argv[j+1];
9330 if ('|' == *(ap = argv[j]))
9338 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9341 * Allocate and fill in the new argument vector, Some Unix's terminate
9342 * the list with an extra null pointer.
9344 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9345 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9347 for (j = 0; j < item_count; ++j, list_head = list_head->next)
9348 argv[j] = list_head->value;
9354 fprintf(stderr,"'|' and '>' may not both be specified on command line");
9355 exit(LIB$_INVARGORD);
9357 pipe_and_fork(aTHX_ cmargv);
9360 /* Check for input from a pipe (mailbox) */
9362 if (in == NULL && 1 == isapipe(0))
9364 char mbxname[L_tmpnam];
9366 long int dvi_item = DVI$_DEVBUFSIZ;
9367 $DESCRIPTOR(mbxnam, "");
9368 $DESCRIPTOR(mbxdevnam, "");
9370 /* Input from a pipe, reopen it in binary mode to disable */
9371 /* carriage control processing. */
9373 fgetname(stdin, mbxname);
9374 mbxnam.dsc$a_pointer = mbxname;
9375 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9376 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9377 mbxdevnam.dsc$a_pointer = mbxname;
9378 mbxdevnam.dsc$w_length = sizeof(mbxname);
9379 dvi_item = DVI$_DEVNAM;
9380 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9381 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9384 freopen(mbxname, "rb", stdin);
9387 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9391 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9393 fprintf(stderr,"Can't open input file %s as stdin",in);
9396 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9398 fprintf(stderr,"Can't open output file %s as stdout",out);
9401 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
9404 if (strcmp(err,"&1") == 0) {
9405 dup2(fileno(stdout), fileno(stderr));
9406 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
9409 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9411 fprintf(stderr,"Can't open error file %s as stderr",err);
9415 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9419 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
9422 #ifdef ARGPROC_DEBUG
9423 PerlIO_printf(Perl_debug_log, "Arglist:\n");
9424 for (j = 0; j < *ac; ++j)
9425 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9427 /* Clear errors we may have hit expanding wildcards, so they don't
9428 show up in Perl's $! later */
9429 set_errno(0); set_vaxc_errno(1);
9430 } /* end of getredirection() */
9433 static void add_item(struct list_item **head,
9434 struct list_item **tail,
9440 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9441 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9445 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9446 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9447 *tail = (*tail)->next;
9449 (*tail)->value = value;
9453 static void mp_expand_wild_cards(pTHX_ char *item,
9454 struct list_item **head,
9455 struct list_item **tail,
9459 unsigned long int context = 0;
9467 $DESCRIPTOR(filespec, "");
9468 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9469 $DESCRIPTOR(resultspec, "");
9470 unsigned long int lff_flags = 0;
9474 #ifdef VMS_LONGNAME_SUPPORT
9475 lff_flags = LIB$M_FIL_LONG_NAMES;
9478 for (cp = item; *cp; cp++) {
9479 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9480 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9482 if (!*cp || isspace(*cp))
9484 add_item(head, tail, item, count);
9489 /* "double quoted" wild card expressions pass as is */
9490 /* From DCL that means using e.g.: */
9491 /* perl program """perl.*""" */
9492 item_len = strlen(item);
9493 if ( '"' == *item && '"' == item[item_len-1] )
9496 item[item_len-2] = '\0';
9497 add_item(head, tail, item, count);
9501 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9502 resultspec.dsc$b_class = DSC$K_CLASS_D;
9503 resultspec.dsc$a_pointer = NULL;
9504 vmsspec = PerlMem_malloc(VMS_MAXRSS);
9505 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9506 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9507 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9508 if (!isunix || !filespec.dsc$a_pointer)
9509 filespec.dsc$a_pointer = item;
9510 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9512 * Only return version specs, if the caller specified a version
9514 had_version = strchr(item, ';');
9516 * Only return device and directory specs, if the caller specifed either.
9518 had_device = strchr(item, ':');
9519 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9521 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9522 (&filespec, &resultspec, &context,
9523 &defaultspec, 0, &rms_sts, &lff_flags)))
9528 string = PerlMem_malloc(resultspec.dsc$w_length+1);
9529 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9530 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
9531 string[resultspec.dsc$w_length] = '\0';
9532 if (NULL == had_version)
9533 *(strrchr(string, ';')) = '\0';
9534 if ((!had_directory) && (had_device == NULL))
9536 if (NULL == (devdir = strrchr(string, ']')))
9537 devdir = strrchr(string, '>');
9538 strcpy(string, devdir + 1);
9541 * Be consistent with what the C RTL has already done to the rest of
9542 * the argv items and lowercase all of these names.
9544 if (!decc_efs_case_preserve) {
9545 for (c = string; *c; ++c)
9549 if (isunix) trim_unixpath(string,item,1);
9550 add_item(head, tail, string, count);
9553 PerlMem_free(vmsspec);
9554 if (sts != RMS$_NMF)
9556 set_vaxc_errno(sts);
9559 case RMS$_FNF: case RMS$_DNF:
9560 set_errno(ENOENT); break;
9562 set_errno(ENOTDIR); break;
9564 set_errno(ENODEV); break;
9565 case RMS$_FNM: case RMS$_SYN:
9566 set_errno(EINVAL); break;
9568 set_errno(EACCES); break;
9570 _ckvmssts_noperl(sts);
9574 add_item(head, tail, item, count);
9575 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9576 _ckvmssts_noperl(lib$find_file_end(&context));
9579 static int child_st[2];/* Event Flag set when child process completes */
9581 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
9583 static unsigned long int exit_handler(int *status)
9587 if (0 == child_st[0])
9589 #ifdef ARGPROC_DEBUG
9590 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
9592 fflush(stdout); /* Have to flush pipe for binary data to */
9593 /* terminate properly -- <tp@mccall.com> */
9594 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9595 sys$dassgn(child_chan);
9597 sys$synch(0, child_st);
9602 static void sig_child(int chan)
9604 #ifdef ARGPROC_DEBUG
9605 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
9607 if (child_st[0] == 0)
9611 static struct exit_control_block exit_block =
9616 &exit_block.exit_status,
9621 pipe_and_fork(pTHX_ char **cmargv)
9624 struct dsc$descriptor_s *vmscmd;
9625 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9626 int sts, j, l, ismcr, quote, tquote = 0;
9628 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
9629 vms_execfree(vmscmd);
9634 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
9635 && toupper(*(q+2)) == 'R' && !*(q+3);
9637 while (q && l < MAX_DCL_LINE_LENGTH) {
9639 if (j > 0 && quote) {
9645 if (ismcr && j > 1) quote = 1;
9646 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9649 if (quote || tquote) {
9655 if ((quote||tquote) && *q == '"') {
9665 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9667 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9671 static int background_process(pTHX_ int argc, char **argv)
9673 char command[MAX_DCL_SYMBOL + 1] = "$";
9674 $DESCRIPTOR(value, "");
9675 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9676 static $DESCRIPTOR(null, "NLA0:");
9677 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9679 $DESCRIPTOR(pidstr, "");
9681 unsigned long int flags = 17, one = 1, retsts;
9684 strcat(command, argv[0]);
9685 len = strlen(command);
9686 while (--argc && (len < MAX_DCL_SYMBOL))
9688 strcat(command, " \"");
9689 strcat(command, *(++argv));
9690 strcat(command, "\"");
9691 len = strlen(command);
9693 value.dsc$a_pointer = command;
9694 value.dsc$w_length = strlen(value.dsc$a_pointer);
9695 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9696 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9697 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9698 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9701 _ckvmssts_noperl(retsts);
9703 #ifdef ARGPROC_DEBUG
9704 PerlIO_printf(Perl_debug_log, "%s\n", command);
9706 sprintf(pidstring, "%08X", pid);
9707 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9708 pidstr.dsc$a_pointer = pidstring;
9709 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9710 lib$set_symbol(&pidsymbol, &pidstr);
9714 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9717 /* OS-specific initialization at image activation (not thread startup) */
9718 /* Older VAXC header files lack these constants */
9719 #ifndef JPI$_RIGHTS_SIZE
9720 # define JPI$_RIGHTS_SIZE 817
9722 #ifndef KGB$M_SUBSYSTEM
9723 # define KGB$M_SUBSYSTEM 0x8
9726 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9728 /*{{{void vms_image_init(int *, char ***)*/
9730 vms_image_init(int *argcp, char ***argvp)
9733 char eqv[LNM$C_NAMLENGTH+1] = "";
9734 unsigned int len, tabct = 8, tabidx = 0;
9735 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9736 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9737 unsigned short int dummy, rlen;
9738 struct dsc$descriptor_s **tabvec;
9739 #if defined(PERL_IMPLICIT_CONTEXT)
9742 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9743 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9744 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9747 #ifdef KILL_BY_SIGPRC
9748 Perl_csighandler_init();
9751 /* This was moved from the pre-image init handler because on threaded */
9752 /* Perl it was always returning 0 for the default value. */
9753 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9756 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9759 initial = decc$feature_get_value(s, 4);
9761 /* initial is: 0 if nothing has set the feature */
9762 /* -1 if initialized to default */
9763 /* 1 if set by logical name */
9764 /* 2 if set by decc$feature_set_value */
9765 decc_disable_posix_root = decc$feature_get_value(s, 1);
9767 /* If the value is not valid, force the feature off */
9768 if (decc_disable_posix_root < 0) {
9769 decc$feature_set_value(s, 1, 1);
9770 decc_disable_posix_root = 1;
9774 /* Nothing has asked for it explicitly, so use our own default. */
9775 decc_disable_posix_root = 1;
9776 decc$feature_set_value(s, 1, 1);
9782 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9783 _ckvmssts_noperl(iosb[0]);
9784 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9785 if (iprv[i]) { /* Running image installed with privs? */
9786 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
9791 /* Rights identifiers might trigger tainting as well. */
9792 if (!will_taint && (rlen || rsz)) {
9793 while (rlen < rsz) {
9794 /* We didn't get all the identifiers on the first pass. Allocate a
9795 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9796 * were needed to hold all identifiers at time of last call; we'll
9797 * allocate that many unsigned long ints), and go back and get 'em.
9798 * If it gave us less than it wanted to despite ample buffer space,
9799 * something's broken. Is your system missing a system identifier?
9801 if (rsz <= jpilist[1].buflen) {
9802 /* Perl_croak accvios when used this early in startup. */
9803 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9804 rsz, (unsigned long) jpilist[1].buflen,
9805 "Check your rights database for corruption.\n");
9808 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9809 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9810 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9811 jpilist[1].buflen = rsz * sizeof(unsigned long int);
9812 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9813 _ckvmssts_noperl(iosb[0]);
9815 mask = jpilist[1].bufadr;
9816 /* Check attribute flags for each identifier (2nd longword); protected
9817 * subsystem identifiers trigger tainting.
9819 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9820 if (mask[i] & KGB$M_SUBSYSTEM) {
9825 if (mask != rlst) PerlMem_free(mask);
9828 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9829 * logical, some versions of the CRTL will add a phanthom /000000/
9830 * directory. This needs to be removed.
9832 if (decc_filename_unix_report) {
9835 ulen = strlen(argvp[0][0]);
9837 zeros = strstr(argvp[0][0], "/000000/");
9838 if (zeros != NULL) {
9840 mlen = ulen - (zeros - argvp[0][0]) - 7;
9841 memmove(zeros, &zeros[7], mlen);
9843 argvp[0][0][ulen] = '\0';
9846 /* It also may have a trailing dot that needs to be removed otherwise
9847 * it will be converted to VMS mode incorrectly.
9850 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9851 argvp[0][0][ulen] = '\0';
9854 /* We need to use this hack to tell Perl it should run with tainting,
9855 * since its tainting flag may be part of the PL_curinterp struct, which
9856 * hasn't been allocated when vms_image_init() is called.
9859 char **newargv, **oldargv;
9861 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9862 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9863 newargv[0] = oldargv[0];
9864 newargv[1] = PerlMem_malloc(3 * sizeof(char));
9865 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9866 strcpy(newargv[1], "-T");
9867 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9869 newargv[*argcp] = NULL;
9870 /* We orphan the old argv, since we don't know where it's come from,
9871 * so we don't know how to free it.
9875 else { /* Did user explicitly request tainting? */
9877 char *cp, **av = *argvp;
9878 for (i = 1; i < *argcp; i++) {
9879 if (*av[i] != '-') break;
9880 for (cp = av[i]+1; *cp; cp++) {
9881 if (*cp == 'T') { will_taint = 1; break; }
9882 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9883 strchr("DFIiMmx",*cp)) break;
9885 if (will_taint) break;
9890 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9893 tabvec = (struct dsc$descriptor_s **)
9894 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9895 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9897 else if (tabidx >= tabct) {
9899 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9900 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9902 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9903 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9904 tabvec[tabidx]->dsc$w_length = 0;
9905 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9906 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
9907 tabvec[tabidx]->dsc$a_pointer = NULL;
9908 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
9910 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9912 getredirection(argcp,argvp);
9913 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9915 # include <reentrancy.h>
9916 decc$set_reentrancy(C$C_MULTITHREAD);
9925 * Trim Unix-style prefix off filespec, so it looks like what a shell
9926 * glob expansion would return (i.e. from specified prefix on, not
9927 * full path). Note that returned filespec is Unix-style, regardless
9928 * of whether input filespec was VMS-style or Unix-style.
9930 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9931 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9932 * vector of options; at present, only bit 0 is used, and if set tells
9933 * trim unixpath to try the current default directory as a prefix when
9934 * presented with a possibly ambiguous ... wildcard.
9936 * Returns !=0 on success, with trimmed filespec replacing contents of
9937 * fspec, and 0 on failure, with contents of fpsec unchanged.
9939 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9941 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9943 char *unixified, *unixwild,
9944 *template, *base, *end, *cp1, *cp2;
9945 register int tmplen, reslen = 0, dirs = 0;
9947 if (!wildspec || !fspec) return 0;
9949 unixwild = PerlMem_malloc(VMS_MAXRSS);
9950 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9951 template = unixwild;
9952 if (strpbrk(wildspec,"]>:") != NULL) {
9953 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
9954 PerlMem_free(unixwild);
9959 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
9960 unixwild[VMS_MAXRSS-1] = 0;
9962 unixified = PerlMem_malloc(VMS_MAXRSS);
9963 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9964 if (strpbrk(fspec,"]>:") != NULL) {
9965 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
9966 PerlMem_free(unixwild);
9967 PerlMem_free(unixified);
9970 else base = unixified;
9971 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9972 * check to see that final result fits into (isn't longer than) fspec */
9973 reslen = strlen(fspec);
9977 /* No prefix or absolute path on wildcard, so nothing to remove */
9978 if (!*template || *template == '/') {
9979 PerlMem_free(unixwild);
9980 if (base == fspec) {
9981 PerlMem_free(unixified);
9984 tmplen = strlen(unixified);
9985 if (tmplen > reslen) {
9986 PerlMem_free(unixified);
9987 return 0; /* not enough space */
9989 /* Copy unixified resultant, including trailing NUL */
9990 memmove(fspec,unixified,tmplen+1);
9991 PerlMem_free(unixified);
9995 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9996 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
9997 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
9998 for (cp1 = end ;cp1 >= base; cp1--)
9999 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
10001 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
10002 PerlMem_free(unixified);
10003 PerlMem_free(unixwild);
10008 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
10009 int ells = 1, totells, segdirs, match;
10010 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
10011 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10013 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
10015 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
10016 tpl = PerlMem_malloc(VMS_MAXRSS);
10017 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10018 if (ellipsis == template && opts & 1) {
10019 /* Template begins with an ellipsis. Since we can't tell how many
10020 * directory names at the front of the resultant to keep for an
10021 * arbitrary starting point, we arbitrarily choose the current
10022 * default directory as a starting point. If it's there as a prefix,
10023 * clip it off. If not, fall through and act as if the leading
10024 * ellipsis weren't there (i.e. return shortest possible path that
10025 * could match template).
10027 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
10029 PerlMem_free(unixified);
10030 PerlMem_free(unixwild);
10033 if (!decc_efs_case_preserve) {
10034 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10035 if (_tolower(*cp1) != _tolower(*cp2)) break;
10037 segdirs = dirs - totells; /* Min # of dirs we must have left */
10038 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
10039 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
10040 memmove(fspec,cp2+1,end - cp2);
10042 PerlMem_free(unixified);
10043 PerlMem_free(unixwild);
10047 /* First off, back up over constant elements at end of path */
10049 for (front = end ; front >= base; front--)
10050 if (*front == '/' && !dirs--) { front++; break; }
10052 lcres = PerlMem_malloc(VMS_MAXRSS);
10053 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10054 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
10056 if (!decc_efs_case_preserve) {
10057 *cp2 = _tolower(*cp1); /* Make lc copy for match */
10065 PerlMem_free(unixified);
10066 PerlMem_free(unixwild);
10067 PerlMem_free(lcres);
10068 return 0; /* Path too long. */
10071 *cp2 = '\0'; /* Pick up with memcpy later */
10072 lcfront = lcres + (front - base);
10073 /* Now skip over each ellipsis and try to match the path in front of it. */
10075 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
10076 if (*(cp1) == '.' && *(cp1+1) == '.' &&
10077 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
10078 if (cp1 < template) break; /* template started with an ellipsis */
10079 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
10080 ellipsis = cp1; continue;
10082 wilddsc.dsc$a_pointer = tpl;
10083 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
10085 for (segdirs = 0, cp2 = tpl;
10086 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
10088 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
10090 if (!decc_efs_case_preserve) {
10091 *cp2 = _tolower(*cp1); /* else lowercase for match */
10094 *cp2 = *cp1; /* else preserve case for match */
10097 if (*cp2 == '/') segdirs++;
10099 if (cp1 != ellipsis - 1) {
10101 PerlMem_free(unixified);
10102 PerlMem_free(unixwild);
10103 PerlMem_free(lcres);
10104 return 0; /* Path too long */
10106 /* Back up at least as many dirs as in template before matching */
10107 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
10108 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
10109 for (match = 0; cp1 > lcres;) {
10110 resdsc.dsc$a_pointer = cp1;
10111 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
10113 if (match == 1) lcfront = cp1;
10115 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
10119 PerlMem_free(unixified);
10120 PerlMem_free(unixwild);
10121 PerlMem_free(lcres);
10122 return 0; /* Can't find prefix ??? */
10124 if (match > 1 && opts & 1) {
10125 /* This ... wildcard could cover more than one set of dirs (i.e.
10126 * a set of similar dir names is repeated). If the template
10127 * contains more than 1 ..., upstream elements could resolve the
10128 * ambiguity, but it's not worth a full backtracking setup here.
10129 * As a quick heuristic, clip off the current default directory
10130 * if it's present to find the trimmed spec, else use the
10131 * shortest string that this ... could cover.
10133 char def[NAM$C_MAXRSS+1], *st;
10135 if (getcwd(def, sizeof def,0) == NULL) {
10136 PerlMem_free(unixified);
10137 PerlMem_free(unixwild);
10138 PerlMem_free(lcres);
10142 if (!decc_efs_case_preserve) {
10143 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10144 if (_tolower(*cp1) != _tolower(*cp2)) break;
10146 segdirs = dirs - totells; /* Min # of dirs we must have left */
10147 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
10148 if (*cp1 == '\0' && *cp2 == '/') {
10149 memmove(fspec,cp2+1,end - cp2);
10151 PerlMem_free(unixified);
10152 PerlMem_free(unixwild);
10153 PerlMem_free(lcres);
10156 /* Nope -- stick with lcfront from above and keep going. */
10159 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
10161 PerlMem_free(unixified);
10162 PerlMem_free(unixwild);
10163 PerlMem_free(lcres);
10165 ellipsis = nextell;
10168 } /* end of trim_unixpath() */
10173 * VMS readdir() routines.
10174 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
10176 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
10177 * Minor modifications to original routines.
10180 /* readdir may have been redefined by reentr.h, so make sure we get
10181 * the local version for what we do here.
10186 #if !defined(PERL_IMPLICIT_CONTEXT)
10187 # define readdir Perl_readdir
10189 # define readdir(a) Perl_readdir(aTHX_ a)
10192 /* Number of elements in vms_versions array */
10193 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
10196 * Open a directory, return a handle for later use.
10198 /*{{{ DIR *opendir(char*name) */
10200 Perl_opendir(pTHX_ const char *name)
10206 Newx(dir, VMS_MAXRSS, char);
10207 if (int_tovmspath(name, dir, NULL) == NULL) {
10211 /* Check access before stat; otherwise stat does not
10212 * accurately report whether it's a directory.
10214 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
10215 /* cando_by_name has already set errno */
10219 if (flex_stat(dir,&sb) == -1) return NULL;
10220 if (!S_ISDIR(sb.st_mode)) {
10222 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
10225 /* Get memory for the handle, and the pattern. */
10227 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
10229 /* Fill in the fields; mainly playing with the descriptor. */
10230 sprintf(dd->pattern, "%s*.*",dir);
10235 /* By saying we always want the result of readdir() in unix format, we
10236 * are really saying we want all the escapes removed. Otherwise the caller,
10237 * having no way to know whether it's already in VMS format, might send it
10238 * through tovmsspec again, thus double escaping.
10240 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
10241 dd->pat.dsc$a_pointer = dd->pattern;
10242 dd->pat.dsc$w_length = strlen(dd->pattern);
10243 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10244 dd->pat.dsc$b_class = DSC$K_CLASS_S;
10245 #if defined(USE_ITHREADS)
10246 Newx(dd->mutex,1,perl_mutex);
10247 MUTEX_INIT( (perl_mutex *) dd->mutex );
10253 } /* end of opendir() */
10257 * Set the flag to indicate we want versions or not.
10259 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10261 vmsreaddirversions(DIR *dd, int flag)
10264 dd->flags |= PERL_VMSDIR_M_VERSIONS;
10266 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10271 * Free up an opened directory.
10273 /*{{{ void closedir(DIR *dd)*/
10275 Perl_closedir(DIR *dd)
10279 sts = lib$find_file_end(&dd->context);
10280 Safefree(dd->pattern);
10281 #if defined(USE_ITHREADS)
10282 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10283 Safefree(dd->mutex);
10290 * Collect all the version numbers for the current file.
10293 collectversions(pTHX_ DIR *dd)
10295 struct dsc$descriptor_s pat;
10296 struct dsc$descriptor_s res;
10298 char *p, *text, *buff;
10300 unsigned long context, tmpsts;
10302 /* Convenient shorthand. */
10305 /* Add the version wildcard, ignoring the "*.*" put on before */
10306 i = strlen(dd->pattern);
10307 Newx(text,i + e->d_namlen + 3,char);
10308 strcpy(text, dd->pattern);
10309 sprintf(&text[i - 3], "%s;*", e->d_name);
10311 /* Set up the pattern descriptor. */
10312 pat.dsc$a_pointer = text;
10313 pat.dsc$w_length = i + e->d_namlen - 1;
10314 pat.dsc$b_dtype = DSC$K_DTYPE_T;
10315 pat.dsc$b_class = DSC$K_CLASS_S;
10317 /* Set up result descriptor. */
10318 Newx(buff, VMS_MAXRSS, char);
10319 res.dsc$a_pointer = buff;
10320 res.dsc$w_length = VMS_MAXRSS - 1;
10321 res.dsc$b_dtype = DSC$K_DTYPE_T;
10322 res.dsc$b_class = DSC$K_CLASS_S;
10324 /* Read files, collecting versions. */
10325 for (context = 0, e->vms_verscount = 0;
10326 e->vms_verscount < VERSIZE(e);
10327 e->vms_verscount++) {
10328 unsigned long rsts;
10329 unsigned long flags = 0;
10331 #ifdef VMS_LONGNAME_SUPPORT
10332 flags = LIB$M_FIL_LONG_NAMES;
10334 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10335 if (tmpsts == RMS$_NMF || context == 0) break;
10337 buff[VMS_MAXRSS - 1] = '\0';
10338 if ((p = strchr(buff, ';')))
10339 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10341 e->vms_versions[e->vms_verscount] = -1;
10344 _ckvmssts(lib$find_file_end(&context));
10348 } /* end of collectversions() */
10351 * Read the next entry from the directory.
10353 /*{{{ struct dirent *readdir(DIR *dd)*/
10355 Perl_readdir(pTHX_ DIR *dd)
10357 struct dsc$descriptor_s res;
10359 unsigned long int tmpsts;
10360 unsigned long rsts;
10361 unsigned long flags = 0;
10362 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10363 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10365 /* Set up result descriptor, and get next file. */
10366 Newx(buff, VMS_MAXRSS, char);
10367 res.dsc$a_pointer = buff;
10368 res.dsc$w_length = VMS_MAXRSS - 1;
10369 res.dsc$b_dtype = DSC$K_DTYPE_T;
10370 res.dsc$b_class = DSC$K_CLASS_S;
10372 #ifdef VMS_LONGNAME_SUPPORT
10373 flags = LIB$M_FIL_LONG_NAMES;
10376 tmpsts = lib$find_file
10377 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10378 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
10379 if (!(tmpsts & 1)) {
10380 set_vaxc_errno(tmpsts);
10383 set_errno(EACCES); break;
10385 set_errno(ENODEV); break;
10387 set_errno(ENOTDIR); break;
10388 case RMS$_FNF: case RMS$_DNF:
10389 set_errno(ENOENT); break;
10391 set_errno(EVMSERR);
10397 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10398 buff[res.dsc$w_length] = '\0';
10399 p = buff + res.dsc$w_length;
10400 while (--p >= buff) if (!isspace(*p)) break;
10402 if (!decc_efs_case_preserve) {
10403 for (p = buff; *p; p++) *p = _tolower(*p);
10406 /* Skip any directory component and just copy the name. */
10407 sts = vms_split_path
10422 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10424 /* In Unix report mode, remove the ".dir;1" from the name */
10425 /* if it is a real directory. */
10426 if (decc_filename_unix_report || decc_efs_charset) {
10427 if ((e_len == 4) && (vs_len == 2) && (vs_spec[1] == '1')) {
10428 if ((toupper(e_spec[1]) == 'D') &&
10429 (toupper(e_spec[2]) == 'I') &&
10430 (toupper(e_spec[3]) == 'R')) {
10434 ret_sts = stat(buff, &statbuf.crtl_stat);
10435 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10443 /* Drop NULL extensions on UNIX file specification */
10444 if ((e_len == 1) && decc_readdir_dropdotnotype) {
10450 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
10451 dd->entry.d_name[n_len + e_len] = '\0';
10452 dd->entry.d_namlen = strlen(dd->entry.d_name);
10454 /* Convert the filename to UNIX format if needed */
10455 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10457 /* Translate the encoded characters. */
10458 /* Fixme: Unicode handling could result in embedded 0 characters */
10459 if (strchr(dd->entry.d_name, '^') != NULL) {
10460 char new_name[256];
10462 p = dd->entry.d_name;
10465 int inchars_read, outchars_added;
10466 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10468 q += outchars_added;
10470 /* if outchars_added > 1, then this is a wide file specification */
10471 /* Wide file specifications need to be passed in Perl */
10472 /* counted strings apparently with a Unicode flag */
10475 strcpy(dd->entry.d_name, new_name);
10476 dd->entry.d_namlen = strlen(dd->entry.d_name);
10480 dd->entry.vms_verscount = 0;
10481 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10485 } /* end of readdir() */
10489 * Read the next entry from the directory -- thread-safe version.
10491 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10493 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10497 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10499 entry = readdir(dd);
10501 retval = ( *result == NULL ? errno : 0 );
10503 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10507 } /* end of readdir_r() */
10511 * Return something that can be used in a seekdir later.
10513 /*{{{ long telldir(DIR *dd)*/
10515 Perl_telldir(DIR *dd)
10522 * Return to a spot where we used to be. Brute force.
10524 /*{{{ void seekdir(DIR *dd,long count)*/
10526 Perl_seekdir(pTHX_ DIR *dd, long count)
10530 /* If we haven't done anything yet... */
10531 if (dd->count == 0)
10534 /* Remember some state, and clear it. */
10535 old_flags = dd->flags;
10536 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10537 _ckvmssts(lib$find_file_end(&dd->context));
10540 /* The increment is in readdir(). */
10541 for (dd->count = 0; dd->count < count; )
10544 dd->flags = old_flags;
10546 } /* end of seekdir() */
10549 /* VMS subprocess management
10551 * my_vfork() - just a vfork(), after setting a flag to record that
10552 * the current script is trying a Unix-style fork/exec.
10554 * vms_do_aexec() and vms_do_exec() are called in response to the
10555 * perl 'exec' function. If this follows a vfork call, then they
10556 * call out the regular perl routines in doio.c which do an
10557 * execvp (for those who really want to try this under VMS).
10558 * Otherwise, they do exactly what the perl docs say exec should
10559 * do - terminate the current script and invoke a new command
10560 * (See below for notes on command syntax.)
10562 * do_aspawn() and do_spawn() implement the VMS side of the perl
10563 * 'system' function.
10565 * Note on command arguments to perl 'exec' and 'system': When handled
10566 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10567 * are concatenated to form a DCL command string. If the first non-numeric
10568 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10569 * the command string is handed off to DCL directly. Otherwise,
10570 * the first token of the command is taken as the filespec of an image
10571 * to run. The filespec is expanded using a default type of '.EXE' and
10572 * the process defaults for device, directory, etc., and if found, the resultant
10573 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10574 * the command string as parameters. This is perhaps a bit complicated,
10575 * but I hope it will form a happy medium between what VMS folks expect
10576 * from lib$spawn and what Unix folks expect from exec.
10579 static int vfork_called;
10581 /*{{{int my_vfork()*/
10592 vms_execfree(struct dsc$descriptor_s *vmscmd)
10595 if (vmscmd->dsc$a_pointer) {
10596 PerlMem_free(vmscmd->dsc$a_pointer);
10598 PerlMem_free(vmscmd);
10603 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10605 char *junk, *tmps = NULL;
10606 register size_t cmdlen = 0;
10613 tmps = SvPV(really,rlen);
10615 cmdlen += rlen + 1;
10620 for (idx++; idx <= sp; idx++) {
10622 junk = SvPVx(*idx,rlen);
10623 cmdlen += rlen ? rlen + 1 : 0;
10626 Newx(PL_Cmd, cmdlen+1, char);
10628 if (tmps && *tmps) {
10629 strcpy(PL_Cmd,tmps);
10632 else *PL_Cmd = '\0';
10633 while (++mark <= sp) {
10635 char *s = SvPVx(*mark,n_a);
10637 if (*PL_Cmd) strcat(PL_Cmd," ");
10643 } /* end of setup_argstr() */
10646 static unsigned long int
10647 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10648 struct dsc$descriptor_s **pvmscmd)
10652 char image_name[NAM$C_MAXRSS+1];
10653 char image_argv[NAM$C_MAXRSS+1];
10654 $DESCRIPTOR(defdsc,".EXE");
10655 $DESCRIPTOR(defdsc2,".");
10656 struct dsc$descriptor_s resdsc;
10657 struct dsc$descriptor_s *vmscmd;
10658 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10659 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10660 register char *s, *rest, *cp, *wordbreak;
10663 register int isdcl;
10665 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10666 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10668 /* vmsspec is a DCL command buffer, not just a filename */
10669 vmsspec = PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10670 if (vmsspec == NULL)
10671 _ckvmssts_noperl(SS$_INSFMEM);
10673 resspec = PerlMem_malloc(VMS_MAXRSS);
10674 if (resspec == NULL)
10675 _ckvmssts_noperl(SS$_INSFMEM);
10677 /* Make a copy for modification */
10678 cmdlen = strlen(incmd);
10679 cmd = PerlMem_malloc(cmdlen+1);
10680 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10681 strncpy(cmd, incmd, cmdlen);
10686 resdsc.dsc$a_pointer = resspec;
10687 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10688 resdsc.dsc$b_class = DSC$K_CLASS_S;
10689 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10691 vmscmd->dsc$a_pointer = NULL;
10692 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10693 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10694 vmscmd->dsc$w_length = 0;
10695 if (pvmscmd) *pvmscmd = vmscmd;
10697 if (suggest_quote) *suggest_quote = 0;
10699 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10701 PerlMem_free(vmsspec);
10702 PerlMem_free(resspec);
10703 return CLI$_BUFOVF; /* continuation lines currently unsupported */
10708 while (*s && isspace(*s)) s++;
10710 if (*s == '@' || *s == '$') {
10711 vmsspec[0] = *s; rest = s + 1;
10712 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10714 else { cp = vmsspec; rest = s; }
10715 if (*rest == '.' || *rest == '/') {
10717 for (cp2 = resspec;
10718 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10719 rest++, cp2++) *cp2 = *rest;
10721 if (int_tovmsspec(resspec, cp, 0, NULL)) {
10724 /* When a UNIX spec with no file type is translated to VMS, */
10725 /* A trailing '.' is appended under ODS-5 rules. */
10726 /* Here we do not want that trailing "." as it prevents */
10727 /* Looking for a implied ".exe" type. */
10728 if (decc_efs_charset) {
10730 i = strlen(vmsspec);
10731 if (vmsspec[i-1] == '.') {
10732 vmsspec[i-1] = '\0';
10737 for (cp2 = vmsspec + strlen(vmsspec);
10738 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10739 rest++, cp2++) *cp2 = *rest;
10744 /* Intuit whether verb (first word of cmd) is a DCL command:
10745 * - if first nonspace char is '@', it's a DCL indirection
10747 * - if verb contains a filespec separator, it's not a DCL command
10748 * - if it doesn't, caller tells us whether to default to a DCL
10749 * command, or to a local image unless told it's DCL (by leading '$')
10753 if (suggest_quote) *suggest_quote = 1;
10755 register char *filespec = strpbrk(s,":<[.;");
10756 rest = wordbreak = strpbrk(s," \"\t/");
10757 if (!wordbreak) wordbreak = s + strlen(s);
10758 if (*s == '$') check_img = 0;
10759 if (filespec && (filespec < wordbreak)) isdcl = 0;
10760 else isdcl = !check_img;
10765 imgdsc.dsc$a_pointer = s;
10766 imgdsc.dsc$w_length = wordbreak - s;
10767 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10769 _ckvmssts_noperl(lib$find_file_end(&cxt));
10770 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10771 if (!(retsts & 1) && *s == '$') {
10772 _ckvmssts_noperl(lib$find_file_end(&cxt));
10773 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10774 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10776 _ckvmssts_noperl(lib$find_file_end(&cxt));
10777 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10781 _ckvmssts_noperl(lib$find_file_end(&cxt));
10786 while (*s && !isspace(*s)) s++;
10789 /* check that it's really not DCL with no file extension */
10790 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10792 char b[256] = {0,0,0,0};
10793 read(fileno(fp), b, 256);
10794 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10798 /* Check for script */
10800 if ((b[0] == '#') && (b[1] == '!'))
10802 #ifdef ALTERNATE_SHEBANG
10804 shebang_len = strlen(ALTERNATE_SHEBANG);
10805 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10807 perlstr = strstr("perl",b);
10808 if (perlstr == NULL)
10816 if (shebang_len > 0) {
10819 char tmpspec[NAM$C_MAXRSS + 1];
10822 /* Image is following after white space */
10823 /*--------------------------------------*/
10824 while (isprint(b[i]) && isspace(b[i]))
10828 while (isprint(b[i]) && !isspace(b[i])) {
10829 tmpspec[j++] = b[i++];
10830 if (j >= NAM$C_MAXRSS)
10835 /* There may be some default parameters to the image */
10836 /*---------------------------------------------------*/
10838 while (isprint(b[i])) {
10839 image_argv[j++] = b[i++];
10840 if (j >= NAM$C_MAXRSS)
10843 while ((j > 0) && !isprint(image_argv[j-1]))
10847 /* It will need to be converted to VMS format and validated */
10848 if (tmpspec[0] != '\0') {
10851 /* Try to find the exact program requested to be run */
10852 /*---------------------------------------------------*/
10853 iname = int_rmsexpand
10854 (tmpspec, image_name, ".exe",
10855 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10856 if (iname != NULL) {
10857 if (cando_by_name_int
10858 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10859 /* MCR prefix needed */
10863 /* Try again with a null type */
10864 /*----------------------------*/
10865 iname = int_rmsexpand
10866 (tmpspec, image_name, ".",
10867 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10868 if (iname != NULL) {
10869 if (cando_by_name_int
10870 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10871 /* MCR prefix needed */
10877 /* Did we find the image to run the script? */
10878 /*------------------------------------------*/
10882 /* Assume DCL or foreign command exists */
10883 /*--------------------------------------*/
10884 tchr = strrchr(tmpspec, '/');
10885 if (tchr != NULL) {
10891 strcpy(image_name, tchr);
10899 if (check_img && isdcl) {
10901 PerlMem_free(resspec);
10902 PerlMem_free(vmsspec);
10906 if (cando_by_name(S_IXUSR,0,resspec)) {
10907 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10908 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10910 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
10911 if (image_name[0] != 0) {
10912 strcat(vmscmd->dsc$a_pointer, image_name);
10913 strcat(vmscmd->dsc$a_pointer, " ");
10915 } else if (image_name[0] != 0) {
10916 strcpy(vmscmd->dsc$a_pointer, image_name);
10917 strcat(vmscmd->dsc$a_pointer, " ");
10919 strcpy(vmscmd->dsc$a_pointer,"@");
10921 if (suggest_quote) *suggest_quote = 1;
10923 /* If there is an image name, use original command */
10924 if (image_name[0] == 0)
10925 strcat(vmscmd->dsc$a_pointer,resspec);
10928 while (*rest && isspace(*rest)) rest++;
10931 if (image_argv[0] != 0) {
10932 strcat(vmscmd->dsc$a_pointer,image_argv);
10933 strcat(vmscmd->dsc$a_pointer, " ");
10939 rest_len = strlen(rest);
10940 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10941 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10942 strcat(vmscmd->dsc$a_pointer,rest);
10944 retsts = CLI$_BUFOVF;
10946 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10948 PerlMem_free(vmsspec);
10949 PerlMem_free(resspec);
10950 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10956 /* It's either a DCL command or we couldn't find a suitable image */
10957 vmscmd->dsc$w_length = strlen(cmd);
10959 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
10960 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
10961 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
10964 PerlMem_free(resspec);
10965 PerlMem_free(vmsspec);
10967 /* check if it's a symbol (for quoting purposes) */
10968 if (suggest_quote && !*suggest_quote) {
10970 char equiv[LNM$C_NAMLENGTH];
10971 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10972 eqvdsc.dsc$a_pointer = equiv;
10974 iss = lib$get_symbol(vmscmd,&eqvdsc);
10975 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10977 if (!(retsts & 1)) {
10978 /* just hand off status values likely to be due to user error */
10979 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10980 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10981 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10982 else { _ckvmssts_noperl(retsts); }
10985 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10987 } /* end of setup_cmddsc() */
10990 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10992 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10998 if (vfork_called) { /* this follows a vfork - act Unixish */
11000 if (vfork_called < 0) {
11001 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
11004 else return do_aexec(really,mark,sp);
11006 /* no vfork - act VMSish */
11007 cmd = setup_argstr(aTHX_ really,mark,sp);
11008 exec_sts = vms_do_exec(cmd);
11009 Safefree(cmd); /* Clean up from setup_argstr() */
11014 } /* end of vms_do_aexec() */
11017 /* {{{bool vms_do_exec(char *cmd) */
11019 Perl_vms_do_exec(pTHX_ const char *cmd)
11021 struct dsc$descriptor_s *vmscmd;
11023 if (vfork_called) { /* this follows a vfork - act Unixish */
11025 if (vfork_called < 0) {
11026 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
11029 else return do_exec(cmd);
11032 { /* no vfork - act VMSish */
11033 unsigned long int retsts;
11036 TAINT_PROPER("exec");
11037 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
11038 retsts = lib$do_command(vmscmd);
11041 case RMS$_FNF: case RMS$_DNF:
11042 set_errno(ENOENT); break;
11044 set_errno(ENOTDIR); break;
11046 set_errno(ENODEV); break;
11048 set_errno(EACCES); break;
11050 set_errno(EINVAL); break;
11051 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11052 set_errno(E2BIG); break;
11053 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11054 _ckvmssts_noperl(retsts); /* fall through */
11055 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11056 set_errno(EVMSERR);
11058 set_vaxc_errno(retsts);
11059 if (ckWARN(WARN_EXEC)) {
11060 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
11061 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
11063 vms_execfree(vmscmd);
11068 } /* end of vms_do_exec() */
11071 int do_spawn2(pTHX_ const char *, int);
11074 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
11076 unsigned long int sts;
11082 /* We'll copy the (undocumented?) Win32 behavior and allow a
11083 * numeric first argument. But the only value we'll support
11084 * through do_aspawn is a value of 1, which means spawn without
11085 * waiting for completion -- other values are ignored.
11087 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
11089 flags = SvIVx(*mark);
11092 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
11093 flags = CLI$M_NOWAIT;
11097 cmd = setup_argstr(aTHX_ really, mark, sp);
11098 sts = do_spawn2(aTHX_ cmd, flags);
11099 /* pp_sys will clean up cmd */
11103 } /* end of do_aspawn() */
11107 /* {{{int do_spawn(char* cmd) */
11109 Perl_do_spawn(pTHX_ char* cmd)
11111 PERL_ARGS_ASSERT_DO_SPAWN;
11113 return do_spawn2(aTHX_ cmd, 0);
11117 /* {{{int do_spawn_nowait(char* cmd) */
11119 Perl_do_spawn_nowait(pTHX_ char* cmd)
11121 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
11123 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
11127 /* {{{int do_spawn2(char *cmd) */
11129 do_spawn2(pTHX_ const char *cmd, int flags)
11131 unsigned long int sts, substs;
11133 /* The caller of this routine expects to Safefree(PL_Cmd) */
11134 Newx(PL_Cmd,10,char);
11137 TAINT_PROPER("spawn");
11138 if (!cmd || !*cmd) {
11139 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
11142 case RMS$_FNF: case RMS$_DNF:
11143 set_errno(ENOENT); break;
11145 set_errno(ENOTDIR); break;
11147 set_errno(ENODEV); break;
11149 set_errno(EACCES); break;
11151 set_errno(EINVAL); break;
11152 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11153 set_errno(E2BIG); break;
11154 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11155 _ckvmssts_noperl(sts); /* fall through */
11156 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11157 set_errno(EVMSERR);
11159 set_vaxc_errno(sts);
11160 if (ckWARN(WARN_EXEC)) {
11161 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
11170 if (flags & CLI$M_NOWAIT)
11173 strcpy(mode, "nW");
11175 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
11178 /* sts will be the pid in the nowait case */
11181 } /* end of do_spawn2() */
11185 static unsigned int *sockflags, sockflagsize;
11188 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11189 * routines found in some versions of the CRTL can't deal with sockets.
11190 * We don't shim the other file open routines since a socket isn't
11191 * likely to be opened by a name.
11193 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11194 FILE *my_fdopen(int fd, const char *mode)
11196 FILE *fp = fdopen(fd, mode);
11199 unsigned int fdoff = fd / sizeof(unsigned int);
11200 Stat_t sbuf; /* native stat; we don't need flex_stat */
11201 if (!sockflagsize || fdoff > sockflagsize) {
11202 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
11203 else Newx (sockflags,fdoff+2,unsigned int);
11204 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11205 sockflagsize = fdoff + 2;
11207 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11208 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11217 * Clear the corresponding bit when the (possibly) socket stream is closed.
11218 * There still a small hole: we miss an implicit close which might occur
11219 * via freopen(). >> Todo
11221 /*{{{ int my_fclose(FILE *fp)*/
11222 int my_fclose(FILE *fp) {
11224 unsigned int fd = fileno(fp);
11225 unsigned int fdoff = fd / sizeof(unsigned int);
11227 if (sockflagsize && fdoff < sockflagsize)
11228 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11236 * A simple fwrite replacement which outputs itmsz*nitm chars without
11237 * introducing record boundaries every itmsz chars.
11238 * We are using fputs, which depends on a terminating null. We may
11239 * well be writing binary data, so we need to accommodate not only
11240 * data with nulls sprinkled in the middle but also data with no null
11243 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11245 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11247 register char *cp, *end, *cpd, *data;
11248 register unsigned int fd = fileno(dest);
11249 register unsigned int fdoff = fd / sizeof(unsigned int);
11251 int bufsize = itmsz * nitm + 1;
11253 if (fdoff < sockflagsize &&
11254 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11255 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11259 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11260 memcpy( data, src, itmsz*nitm );
11261 data[itmsz*nitm] = '\0';
11263 end = data + itmsz * nitm;
11264 retval = (int) nitm; /* on success return # items written */
11267 while (cpd <= end) {
11268 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11269 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11271 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11275 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11278 } /* end of my_fwrite() */
11281 /*{{{ int my_flush(FILE *fp)*/
11283 Perl_my_flush(pTHX_ FILE *fp)
11286 if ((res = fflush(fp)) == 0 && fp) {
11287 #ifdef VMS_DO_SOCKETS
11289 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11291 res = fsync(fileno(fp));
11294 * If the flush succeeded but set end-of-file, we need to clear
11295 * the error because our caller may check ferror(). BTW, this
11296 * probably means we just flushed an empty file.
11298 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11305 * Here are replacements for the following Unix routines in the VMS environment:
11306 * getpwuid Get information for a particular UIC or UID
11307 * getpwnam Get information for a named user
11308 * getpwent Get information for each user in the rights database
11309 * setpwent Reset search to the start of the rights database
11310 * endpwent Finish searching for users in the rights database
11312 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11313 * (defined in pwd.h), which contains the following fields:-
11315 * char *pw_name; Username (in lower case)
11316 * char *pw_passwd; Hashed password
11317 * unsigned int pw_uid; UIC
11318 * unsigned int pw_gid; UIC group number
11319 * char *pw_unixdir; Default device/directory (VMS-style)
11320 * char *pw_gecos; Owner name
11321 * char *pw_dir; Default device/directory (Unix-style)
11322 * char *pw_shell; Default CLI name (eg. DCL)
11324 * If the specified user does not exist, getpwuid and getpwnam return NULL.
11326 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11327 * not the UIC member number (eg. what's returned by getuid()),
11328 * getpwuid() can accept either as input (if uid is specified, the caller's
11329 * UIC group is used), though it won't recognise gid=0.
11331 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11332 * information about other users in your group or in other groups, respectively.
11333 * If the required privilege is not available, then these routines fill only
11334 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11337 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11340 /* sizes of various UAF record fields */
11341 #define UAI$S_USERNAME 12
11342 #define UAI$S_IDENT 31
11343 #define UAI$S_OWNER 31
11344 #define UAI$S_DEFDEV 31
11345 #define UAI$S_DEFDIR 63
11346 #define UAI$S_DEFCLI 31
11347 #define UAI$S_PWD 8
11349 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
11350 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11351 (uic).uic$v_group != UIC$K_WILD_GROUP)
11353 static char __empty[]= "";
11354 static struct passwd __passwd_empty=
11355 {(char *) __empty, (char *) __empty, 0, 0,
11356 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11357 static int contxt= 0;
11358 static struct passwd __pwdcache;
11359 static char __pw_namecache[UAI$S_IDENT+1];
11362 * This routine does most of the work extracting the user information.
11364 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11367 unsigned char length;
11368 char pw_gecos[UAI$S_OWNER+1];
11370 static union uicdef uic;
11372 unsigned char length;
11373 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11376 unsigned char length;
11377 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11380 unsigned char length;
11381 char pw_shell[UAI$S_DEFCLI+1];
11383 static char pw_passwd[UAI$S_PWD+1];
11385 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11386 struct dsc$descriptor_s name_desc;
11387 unsigned long int sts;
11389 static struct itmlst_3 itmlst[]= {
11390 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
11391 {sizeof(uic), UAI$_UIC, &uic, &luic},
11392 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
11393 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
11394 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
11395 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
11396 {0, 0, NULL, NULL}};
11398 name_desc.dsc$w_length= strlen(name);
11399 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11400 name_desc.dsc$b_class= DSC$K_CLASS_S;
11401 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11403 /* Note that sys$getuai returns many fields as counted strings. */
11404 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11405 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11406 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11408 else { _ckvmssts(sts); }
11409 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
11411 if ((int) owner.length < lowner) lowner= (int) owner.length;
11412 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11413 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11414 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11415 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11416 owner.pw_gecos[lowner]= '\0';
11417 defdev.pw_dir[ldefdev+ldefdir]= '\0';
11418 defcli.pw_shell[ldefcli]= '\0';
11419 if (valid_uic(uic)) {
11420 pwd->pw_uid= uic.uic$l_uic;
11421 pwd->pw_gid= uic.uic$v_group;
11424 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11425 pwd->pw_passwd= pw_passwd;
11426 pwd->pw_gecos= owner.pw_gecos;
11427 pwd->pw_dir= defdev.pw_dir;
11428 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11429 pwd->pw_shell= defcli.pw_shell;
11430 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11432 ldir= strlen(pwd->pw_unixdir) - 1;
11433 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11436 strcpy(pwd->pw_unixdir, pwd->pw_dir);
11437 if (!decc_efs_case_preserve)
11438 __mystrtolower(pwd->pw_unixdir);
11443 * Get information for a named user.
11445 /*{{{struct passwd *getpwnam(char *name)*/
11446 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
11448 struct dsc$descriptor_s name_desc;
11450 unsigned long int status, sts;
11452 __pwdcache = __passwd_empty;
11453 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11454 /* We still may be able to determine pw_uid and pw_gid */
11455 name_desc.dsc$w_length= strlen(name);
11456 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11457 name_desc.dsc$b_class= DSC$K_CLASS_S;
11458 name_desc.dsc$a_pointer= (char *) name;
11459 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11460 __pwdcache.pw_uid= uic.uic$l_uic;
11461 __pwdcache.pw_gid= uic.uic$v_group;
11464 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11465 set_vaxc_errno(sts);
11466 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11469 else { _ckvmssts(sts); }
11472 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
11473 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
11474 __pwdcache.pw_name= __pw_namecache;
11475 return &__pwdcache;
11476 } /* end of my_getpwnam() */
11480 * Get information for a particular UIC or UID.
11481 * Called by my_getpwent with uid=-1 to list all users.
11483 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11484 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
11486 const $DESCRIPTOR(name_desc,__pw_namecache);
11487 unsigned short lname;
11489 unsigned long int status;
11491 if (uid == (unsigned int) -1) {
11493 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11494 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11495 set_vaxc_errno(status);
11496 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11500 else { _ckvmssts(status); }
11501 } while (!valid_uic (uic));
11504 uic.uic$l_uic= uid;
11505 if (!uic.uic$v_group)
11506 uic.uic$v_group= PerlProc_getgid();
11507 if (valid_uic(uic))
11508 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11509 else status = SS$_IVIDENT;
11510 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11511 status == RMS$_PRV) {
11512 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11515 else { _ckvmssts(status); }
11517 __pw_namecache[lname]= '\0';
11518 __mystrtolower(__pw_namecache);
11520 __pwdcache = __passwd_empty;
11521 __pwdcache.pw_name = __pw_namecache;
11523 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11524 The identifier's value is usually the UIC, but it doesn't have to be,
11525 so if we can, we let fillpasswd update this. */
11526 __pwdcache.pw_uid = uic.uic$l_uic;
11527 __pwdcache.pw_gid = uic.uic$v_group;
11529 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11530 return &__pwdcache;
11532 } /* end of my_getpwuid() */
11536 * Get information for next user.
11538 /*{{{struct passwd *my_getpwent()*/
11539 struct passwd *Perl_my_getpwent(pTHX)
11541 return (my_getpwuid((unsigned int) -1));
11546 * Finish searching rights database for users.
11548 /*{{{void my_endpwent()*/
11549 void Perl_my_endpwent(pTHX)
11552 _ckvmssts(sys$finish_rdb(&contxt));
11558 #ifdef HOMEGROWN_POSIX_SIGNALS
11559 /* Signal handling routines, pulled into the core from POSIX.xs.
11561 * We need these for threads, so they've been rolled into the core,
11562 * rather than left in POSIX.xs.
11564 * (DRS, Oct 23, 1997)
11567 /* sigset_t is atomic under VMS, so these routines are easy */
11568 /*{{{int my_sigemptyset(sigset_t *) */
11569 int my_sigemptyset(sigset_t *set) {
11570 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11571 *set = 0; return 0;
11576 /*{{{int my_sigfillset(sigset_t *)*/
11577 int my_sigfillset(sigset_t *set) {
11579 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11580 for (i = 0; i < NSIG; i++) *set |= (1 << i);
11586 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
11587 int my_sigaddset(sigset_t *set, int sig) {
11588 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11589 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11590 *set |= (1 << (sig - 1));
11596 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
11597 int my_sigdelset(sigset_t *set, int sig) {
11598 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11599 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11600 *set &= ~(1 << (sig - 1));
11606 /*{{{int my_sigismember(sigset_t *set, int sig)*/
11607 int my_sigismember(sigset_t *set, int sig) {
11608 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11609 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11610 return *set & (1 << (sig - 1));
11615 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
11616 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
11619 /* If set and oset are both null, then things are badly wrong. Bail out. */
11620 if ((oset == NULL) && (set == NULL)) {
11621 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
11625 /* If set's null, then we're just handling a fetch. */
11627 tempmask = sigblock(0);
11632 tempmask = sigsetmask(*set);
11635 tempmask = sigblock(*set);
11638 tempmask = sigblock(0);
11639 sigsetmask(*oset & ~tempmask);
11642 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11647 /* Did they pass us an oset? If so, stick our holding mask into it */
11654 #endif /* HOMEGROWN_POSIX_SIGNALS */
11657 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11658 * my_utime(), and flex_stat(), all of which operate on UTC unless
11659 * VMSISH_TIMES is true.
11661 /* method used to handle UTC conversions:
11662 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
11664 static int gmtime_emulation_type;
11665 /* number of secs to add to UTC POSIX-style time to get local time */
11666 static long int utc_offset_secs;
11668 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11669 * in vmsish.h. #undef them here so we can call the CRTL routines
11678 * DEC C previous to 6.0 corrupts the behavior of the /prefix
11679 * qualifier with the extern prefix pragma. This provisional
11680 * hack circumvents this prefix pragma problem in previous
11683 #if defined(__VMS_VER) && __VMS_VER >= 70000000
11684 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
11685 # pragma __extern_prefix save
11686 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
11687 # define gmtime decc$__utctz_gmtime
11688 # define localtime decc$__utctz_localtime
11689 # define time decc$__utc_time
11690 # pragma __extern_prefix restore
11692 struct tm *gmtime(), *localtime();
11698 static time_t toutc_dst(time_t loc) {
11701 if ((rsltmp = localtime(&loc)) == NULL) return -1;
11702 loc -= utc_offset_secs;
11703 if (rsltmp->tm_isdst) loc -= 3600;
11706 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11707 ((gmtime_emulation_type || my_time(NULL)), \
11708 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11709 ((secs) - utc_offset_secs))))
11711 static time_t toloc_dst(time_t utc) {
11714 utc += utc_offset_secs;
11715 if ((rsltmp = localtime(&utc)) == NULL) return -1;
11716 if (rsltmp->tm_isdst) utc += 3600;
11719 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11720 ((gmtime_emulation_type || my_time(NULL)), \
11721 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11722 ((secs) + utc_offset_secs))))
11724 #ifndef RTL_USES_UTC
11727 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
11728 DST starts on 1st sun of april at 02:00 std time
11729 ends on last sun of october at 02:00 dst time
11730 see the UCX management command reference, SET CONFIG TIMEZONE
11731 for formatting info.
11733 No, it's not as general as it should be, but then again, NOTHING
11734 will handle UK times in a sensible way.
11739 parse the DST start/end info:
11740 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
11744 tz_parse_startend(char *s, struct tm *w, int *past)
11746 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
11747 int ly, dozjd, d, m, n, hour, min, sec, j, k;
11752 if (!past) return 0;
11755 if (w->tm_year % 4 == 0) ly = 1;
11756 if (w->tm_year % 100 == 0) ly = 0;
11757 if (w->tm_year+1900 % 400 == 0) ly = 1;
11760 dozjd = isdigit(*s);
11761 if (*s == 'J' || *s == 'j' || dozjd) {
11762 if (!dozjd && !isdigit(*++s)) return 0;
11765 d = d*10 + *s++ - '0';
11767 d = d*10 + *s++ - '0';
11770 if (d == 0) return 0;
11771 if (d > 366) return 0;
11773 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
11776 } else if (*s == 'M' || *s == 'm') {
11777 if (!isdigit(*++s)) return 0;
11779 if (isdigit(*s)) m = 10*m + *s++ - '0';
11780 if (*s != '.') return 0;
11781 if (!isdigit(*++s)) return 0;
11783 if (n < 1 || n > 5) return 0;
11784 if (*s != '.') return 0;
11785 if (!isdigit(*++s)) return 0;
11787 if (d > 6) return 0;
11791 if (!isdigit(*++s)) return 0;
11793 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
11795 if (!isdigit(*++s)) return 0;
11797 if (isdigit(*s)) min = 10*min + *s++ - '0';
11799 if (!isdigit(*++s)) return 0;
11801 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
11811 if (w->tm_yday < d) goto before;
11812 if (w->tm_yday > d) goto after;
11814 if (w->tm_mon+1 < m) goto before;
11815 if (w->tm_mon+1 > m) goto after;
11817 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
11818 k = d - j; /* mday of first d */
11819 if (k <= 0) k += 7;
11820 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
11821 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
11822 if (w->tm_mday < k) goto before;
11823 if (w->tm_mday > k) goto after;
11826 if (w->tm_hour < hour) goto before;
11827 if (w->tm_hour > hour) goto after;
11828 if (w->tm_min < min) goto before;
11829 if (w->tm_min > min) goto after;
11830 if (w->tm_sec < sec) goto before;
11844 /* parse the offset: (+|-)hh[:mm[:ss]] */
11847 tz_parse_offset(char *s, int *offset)
11849 int hour = 0, min = 0, sec = 0;
11852 if (!offset) return 0;
11854 if (*s == '-') {neg++; s++;}
11855 if (*s == '+') s++;
11856 if (!isdigit(*s)) return 0;
11858 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
11859 if (hour > 24) return 0;
11861 if (!isdigit(*++s)) return 0;
11863 if (isdigit(*s)) min = min*10 + (*s++ - '0');
11864 if (min > 59) return 0;
11866 if (!isdigit(*++s)) return 0;
11868 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
11869 if (sec > 59) return 0;
11873 *offset = (hour*60+min)*60 + sec;
11874 if (neg) *offset = -*offset;
11879 input time is w, whatever type of time the CRTL localtime() uses.
11880 sets dst, the zone, and the gmtoff (seconds)
11882 caches the value of TZ and UCX$TZ env variables; note that
11883 my_setenv looks for these and sets a flag if they're changed
11886 We have to watch out for the "australian" case (dst starts in
11887 october, ends in april)...flagged by "reverse" and checked by
11888 scanning through the months of the previous year.
11893 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
11898 char *dstzone, *tz, *s_start, *s_end;
11899 int std_off, dst_off, isdst;
11900 int y, dststart, dstend;
11901 static char envtz[1025]; /* longer than any logical, symbol, ... */
11902 static char ucxtz[1025];
11903 static char reversed = 0;
11909 reversed = -1; /* flag need to check */
11910 envtz[0] = ucxtz[0] = '\0';
11911 tz = my_getenv("TZ",0);
11912 if (tz) strcpy(envtz, tz);
11913 tz = my_getenv("UCX$TZ",0);
11914 if (tz) strcpy(ucxtz, tz);
11915 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
11918 if (!*tz) tz = ucxtz;
11921 while (isalpha(*s)) s++;
11922 s = tz_parse_offset(s, &std_off);
11924 if (!*s) { /* no DST, hurray we're done! */
11930 while (isalpha(*s)) s++;
11931 s2 = tz_parse_offset(s, &dst_off);
11935 dst_off = std_off - 3600;
11938 if (!*s) { /* default dst start/end?? */
11939 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
11940 s = strchr(ucxtz,',');
11942 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
11944 if (*s != ',') return 0;
11947 when = _toutc(when); /* convert to utc */
11948 when = when - std_off; /* convert to pseudolocal time*/
11950 w2 = localtime(&when);
11953 s = tz_parse_startend(s_start,w2,&dststart);
11955 if (*s != ',') return 0;
11958 when = _toutc(when); /* convert to utc */
11959 when = when - dst_off; /* convert to pseudolocal time*/
11960 w2 = localtime(&when);
11961 if (w2->tm_year != y) { /* spans a year, just check one time */
11962 when += dst_off - std_off;
11963 w2 = localtime(&when);
11966 s = tz_parse_startend(s_end,w2,&dstend);
11969 if (reversed == -1) { /* need to check if start later than end */
11973 if (when < 2*365*86400) {
11974 when += 2*365*86400;
11978 w2 =localtime(&when);
11979 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
11981 for (j = 0; j < 12; j++) {
11982 w2 =localtime(&when);
11983 tz_parse_startend(s_start,w2,&ds);
11984 tz_parse_startend(s_end,w2,&de);
11985 if (ds != de) break;
11989 if (de && !ds) reversed = 1;
11992 isdst = dststart && !dstend;
11993 if (reversed) isdst = dststart || !dstend;
11996 if (dst) *dst = isdst;
11997 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
11998 if (isdst) tz = dstzone;
12000 while(isalpha(*tz)) *zone++ = *tz++;
12006 #endif /* !RTL_USES_UTC */
12008 /* my_time(), my_localtime(), my_gmtime()
12009 * By default traffic in UTC time values, using CRTL gmtime() or
12010 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
12011 * Note: We need to use these functions even when the CRTL has working
12012 * UTC support, since they also handle C<use vmsish qw(times);>
12014 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
12015 * Modified by Charles Bailey <bailey@newman.upenn.edu>
12018 /*{{{time_t my_time(time_t *timep)*/
12019 time_t Perl_my_time(pTHX_ time_t *timep)
12024 if (gmtime_emulation_type == 0) {
12026 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
12027 /* results of calls to gmtime() and localtime() */
12028 /* for same &base */
12030 gmtime_emulation_type++;
12031 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
12032 char off[LNM$C_NAMLENGTH+1];;
12034 gmtime_emulation_type++;
12035 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
12036 gmtime_emulation_type++;
12037 utc_offset_secs = 0;
12038 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
12040 else { utc_offset_secs = atol(off); }
12042 else { /* We've got a working gmtime() */
12043 struct tm gmt, local;
12046 tm_p = localtime(&base);
12048 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
12049 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
12050 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
12051 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
12056 # ifdef VMSISH_TIME
12057 # ifdef RTL_USES_UTC
12058 if (VMSISH_TIME) when = _toloc(when);
12060 if (!VMSISH_TIME) when = _toutc(when);
12063 if (timep != NULL) *timep = when;
12066 } /* end of my_time() */
12070 /*{{{struct tm *my_gmtime(const time_t *timep)*/
12072 Perl_my_gmtime(pTHX_ const time_t *timep)
12078 if (timep == NULL) {
12079 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12082 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
12085 # ifdef VMSISH_TIME
12086 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
12088 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
12089 return gmtime(&when);
12091 /* CRTL localtime() wants local time as input, so does no tz correction */
12092 rsltmp = localtime(&when);
12093 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
12096 } /* end of my_gmtime() */
12100 /*{{{struct tm *my_localtime(const time_t *timep)*/
12102 Perl_my_localtime(pTHX_ const time_t *timep)
12104 time_t when, whenutc;
12108 if (timep == NULL) {
12109 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12112 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
12113 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
12116 # ifdef RTL_USES_UTC
12117 # ifdef VMSISH_TIME
12118 if (VMSISH_TIME) when = _toutc(when);
12120 /* CRTL localtime() wants UTC as input, does tz correction itself */
12121 return localtime(&when);
12123 # else /* !RTL_USES_UTC */
12125 # ifdef VMSISH_TIME
12126 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
12127 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
12130 #ifndef RTL_USES_UTC
12131 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
12132 when = whenutc - offset; /* pseudolocal time*/
12135 /* CRTL localtime() wants local time as input, so does no tz correction */
12136 rsltmp = localtime(&when);
12137 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
12141 } /* end of my_localtime() */
12144 /* Reset definitions for later calls */
12145 #define gmtime(t) my_gmtime(t)
12146 #define localtime(t) my_localtime(t)
12147 #define time(t) my_time(t)
12150 /* my_utime - update modification/access time of a file
12152 * VMS 7.3 and later implementation
12153 * Only the UTC translation is home-grown. The rest is handled by the
12154 * CRTL utime(), which will take into account the relevant feature
12155 * logicals and ODS-5 volume characteristics for true access times.
12157 * pre VMS 7.3 implementation:
12158 * The calling sequence is identical to POSIX utime(), but under
12159 * VMS with ODS-2, only the modification time is changed; ODS-2 does
12160 * not maintain access times. Restrictions differ from the POSIX
12161 * definition in that the time can be changed as long as the
12162 * caller has permission to execute the necessary IO$_MODIFY $QIO;
12163 * no separate checks are made to insure that the caller is the
12164 * owner of the file or has special privs enabled.
12165 * Code here is based on Joe Meadows' FILE utility.
12169 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
12170 * to VMS epoch (01-JAN-1858 00:00:00.00)
12171 * in 100 ns intervals.
12173 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
12175 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
12176 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
12178 #if __CRTL_VER >= 70300000
12179 struct utimbuf utc_utimes, *utc_utimesp;
12181 if (utimes != NULL) {
12182 utc_utimes.actime = utimes->actime;
12183 utc_utimes.modtime = utimes->modtime;
12184 # ifdef VMSISH_TIME
12185 /* If input was local; convert to UTC for sys svc */
12187 utc_utimes.actime = _toutc(utimes->actime);
12188 utc_utimes.modtime = _toutc(utimes->modtime);
12191 utc_utimesp = &utc_utimes;
12194 utc_utimesp = NULL;
12197 return utime(file, utc_utimesp);
12199 #else /* __CRTL_VER < 70300000 */
12203 long int bintime[2], len = 2, lowbit, unixtime,
12204 secscale = 10000000; /* seconds --> 100 ns intervals */
12205 unsigned long int chan, iosb[2], retsts;
12206 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
12207 struct FAB myfab = cc$rms_fab;
12208 struct NAM mynam = cc$rms_nam;
12209 #if defined (__DECC) && defined (__VAX)
12210 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
12211 * at least through VMS V6.1, which causes a type-conversion warning.
12213 # pragma message save
12214 # pragma message disable cvtdiftypes
12216 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
12217 struct fibdef myfib;
12218 #if defined (__DECC) && defined (__VAX)
12219 /* This should be right after the declaration of myatr, but due
12220 * to a bug in VAX DEC C, this takes effect a statement early.
12222 # pragma message restore
12224 /* cast ok for read only parameter */
12225 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
12226 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
12227 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
12229 if (file == NULL || *file == '\0') {
12230 SETERRNO(ENOENT, LIB$_INVARG);
12234 /* Convert to VMS format ensuring that it will fit in 255 characters */
12235 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
12236 SETERRNO(ENOENT, LIB$_INVARG);
12239 if (utimes != NULL) {
12240 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
12241 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
12242 * Since time_t is unsigned long int, and lib$emul takes a signed long int
12243 * as input, we force the sign bit to be clear by shifting unixtime right
12244 * one bit, then multiplying by an extra factor of 2 in lib$emul().
12246 lowbit = (utimes->modtime & 1) ? secscale : 0;
12247 unixtime = (long int) utimes->modtime;
12248 # ifdef VMSISH_TIME
12249 /* If input was UTC; convert to local for sys svc */
12250 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
12252 unixtime >>= 1; secscale <<= 1;
12253 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
12254 if (!(retsts & 1)) {
12255 SETERRNO(EVMSERR, retsts);
12258 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
12259 if (!(retsts & 1)) {
12260 SETERRNO(EVMSERR, retsts);
12265 /* Just get the current time in VMS format directly */
12266 retsts = sys$gettim(bintime);
12267 if (!(retsts & 1)) {
12268 SETERRNO(EVMSERR, retsts);
12273 myfab.fab$l_fna = vmsspec;
12274 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
12275 myfab.fab$l_nam = &mynam;
12276 mynam.nam$l_esa = esa;
12277 mynam.nam$b_ess = (unsigned char) sizeof esa;
12278 mynam.nam$l_rsa = rsa;
12279 mynam.nam$b_rss = (unsigned char) sizeof rsa;
12280 if (decc_efs_case_preserve)
12281 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
12283 /* Look for the file to be affected, letting RMS parse the file
12284 * specification for us as well. I have set errno using only
12285 * values documented in the utime() man page for VMS POSIX.
12287 retsts = sys$parse(&myfab,0,0);
12288 if (!(retsts & 1)) {
12289 set_vaxc_errno(retsts);
12290 if (retsts == RMS$_PRV) set_errno(EACCES);
12291 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
12292 else set_errno(EVMSERR);
12295 retsts = sys$search(&myfab,0,0);
12296 if (!(retsts & 1)) {
12297 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
12298 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
12299 set_vaxc_errno(retsts);
12300 if (retsts == RMS$_PRV) set_errno(EACCES);
12301 else if (retsts == RMS$_FNF) set_errno(ENOENT);
12302 else set_errno(EVMSERR);
12306 devdsc.dsc$w_length = mynam.nam$b_dev;
12307 /* cast ok for read only parameter */
12308 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
12310 retsts = sys$assign(&devdsc,&chan,0,0);
12311 if (!(retsts & 1)) {
12312 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
12313 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
12314 set_vaxc_errno(retsts);
12315 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
12316 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
12317 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
12318 else set_errno(EVMSERR);
12322 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
12323 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
12325 memset((void *) &myfib, 0, sizeof myfib);
12326 #if defined(__DECC) || defined(__DECCXX)
12327 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
12328 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
12329 /* This prevents the revision time of the file being reset to the current
12330 * time as a result of our IO$_MODIFY $QIO. */
12331 myfib.fib$l_acctl = FIB$M_NORECORD;
12333 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
12334 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
12335 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
12337 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
12338 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
12339 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
12340 _ckvmssts(sys$dassgn(chan));
12341 if (retsts & 1) retsts = iosb[0];
12342 if (!(retsts & 1)) {
12343 set_vaxc_errno(retsts);
12344 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12345 else set_errno(EVMSERR);
12351 #endif /* #if __CRTL_VER >= 70300000 */
12353 } /* end of my_utime() */
12357 * flex_stat, flex_lstat, flex_fstat
12358 * basic stat, but gets it right when asked to stat
12359 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
12362 #ifndef _USE_STD_STAT
12363 /* encode_dev packs a VMS device name string into an integer to allow
12364 * simple comparisons. This can be used, for example, to check whether two
12365 * files are located on the same device, by comparing their encoded device
12366 * names. Even a string comparison would not do, because stat() reuses the
12367 * device name buffer for each call; so without encode_dev, it would be
12368 * necessary to save the buffer and use strcmp (this would mean a number of
12369 * changes to the standard Perl code, to say nothing of what a Perl script
12370 * would have to do.
12372 * The device lock id, if it exists, should be unique (unless perhaps compared
12373 * with lock ids transferred from other nodes). We have a lock id if the disk is
12374 * mounted cluster-wide, which is when we tend to get long (host-qualified)
12375 * device names. Thus we use the lock id in preference, and only if that isn't
12376 * available, do we try to pack the device name into an integer (flagged by
12377 * the sign bit (LOCKID_MASK) being set).
12379 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
12380 * name and its encoded form, but it seems very unlikely that we will find
12381 * two files on different disks that share the same encoded device names,
12382 * and even more remote that they will share the same file id (if the test
12383 * is to check for the same file).
12385 * A better method might be to use sys$device_scan on the first call, and to
12386 * search for the device, returning an index into the cached array.
12387 * The number returned would be more intelligible.
12388 * This is probably not worth it, and anyway would take quite a bit longer
12389 * on the first call.
12391 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
12392 static mydev_t encode_dev (pTHX_ const char *dev)
12395 unsigned long int f;
12400 if (!dev || !dev[0]) return 0;
12404 struct dsc$descriptor_s dev_desc;
12405 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
12407 /* For cluster-mounted disks, the disk lock identifier is unique, so we
12408 can try that first. */
12409 dev_desc.dsc$w_length = strlen (dev);
12410 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
12411 dev_desc.dsc$b_class = DSC$K_CLASS_S;
12412 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
12413 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
12414 if (!$VMS_STATUS_SUCCESS(status)) {
12416 case SS$_NOSUCHDEV:
12417 SETERRNO(ENODEV, status);
12423 if (lockid) return (lockid & ~LOCKID_MASK);
12427 /* Otherwise we try to encode the device name */
12431 for (q = dev + strlen(dev); q--; q >= dev) {
12436 else if (isalpha (toupper (*q)))
12437 c= toupper (*q) - 'A' + (char)10;
12439 continue; /* Skip '$'s */
12441 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
12443 enc += f * (unsigned long int) c;
12445 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
12447 } /* end of encode_dev() */
12448 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12449 device_no = encode_dev(aTHX_ devname)
12451 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12452 device_no = new_dev_no
12456 is_null_device(name)
12459 if (decc_bug_devnull != 0) {
12460 if (strncmp("/dev/null", name, 9) == 0)
12463 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
12464 The underscore prefix, controller letter, and unit number are
12465 independently optional; for our purposes, the colon punctuation
12466 is not. The colon can be trailed by optional directory and/or
12467 filename, but two consecutive colons indicates a nodename rather
12468 than a device. [pr] */
12469 if (*name == '_') ++name;
12470 if (tolower(*name++) != 'n') return 0;
12471 if (tolower(*name++) != 'l') return 0;
12472 if (tolower(*name) == 'a') ++name;
12473 if (*name == '0') ++name;
12474 return (*name++ == ':') && (*name != ':');
12478 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
12481 Perl_cando_by_name_int
12482 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
12484 char usrname[L_cuserid];
12485 struct dsc$descriptor_s usrdsc =
12486 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
12487 char *vmsname = NULL, *fileified = NULL;
12488 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
12489 unsigned short int retlen, trnlnm_iter_count;
12490 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12491 union prvdef curprv;
12492 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
12493 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
12494 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
12495 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
12496 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
12498 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
12500 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12502 static int profile_context = -1;
12504 if (!fname || !*fname) return FALSE;
12506 /* Make sure we expand logical names, since sys$check_access doesn't */
12507 fileified = PerlMem_malloc(VMS_MAXRSS);
12508 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12509 if (!strpbrk(fname,"/]>:")) {
12510 strcpy(fileified,fname);
12511 trnlnm_iter_count = 0;
12512 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
12513 trnlnm_iter_count++;
12514 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
12519 vmsname = PerlMem_malloc(VMS_MAXRSS);
12520 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12521 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
12522 /* Don't know if already in VMS format, so make sure */
12523 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
12524 PerlMem_free(fileified);
12525 PerlMem_free(vmsname);
12530 strcpy(vmsname,fname);
12533 /* sys$check_access needs a file spec, not a directory spec.
12534 * flex_stat now will handle a null thread context during startup.
12537 retlen = namdsc.dsc$w_length = strlen(vmsname);
12538 if (vmsname[retlen-1] == ']'
12539 || vmsname[retlen-1] == '>'
12540 || vmsname[retlen-1] == ':'
12541 || (!Perl_flex_stat_int(NULL, vmsname, &st, 1) &&
12542 S_ISDIR(st.st_mode))) {
12544 if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
12545 PerlMem_free(fileified);
12546 PerlMem_free(vmsname);
12555 retlen = namdsc.dsc$w_length = strlen(fname);
12556 namdsc.dsc$a_pointer = (char *)fname;
12559 case S_IXUSR: case S_IXGRP: case S_IXOTH:
12560 access = ARM$M_EXECUTE;
12561 flags = CHP$M_READ;
12563 case S_IRUSR: case S_IRGRP: case S_IROTH:
12564 access = ARM$M_READ;
12565 flags = CHP$M_READ | CHP$M_USEREADALL;
12567 case S_IWUSR: case S_IWGRP: case S_IWOTH:
12568 access = ARM$M_WRITE;
12569 flags = CHP$M_READ | CHP$M_WRITE;
12571 case S_IDUSR: case S_IDGRP: case S_IDOTH:
12572 access = ARM$M_DELETE;
12573 flags = CHP$M_READ | CHP$M_WRITE;
12576 if (fileified != NULL)
12577 PerlMem_free(fileified);
12578 if (vmsname != NULL)
12579 PerlMem_free(vmsname);
12583 /* Before we call $check_access, create a user profile with the current
12584 * process privs since otherwise it just uses the default privs from the
12585 * UAF and might give false positives or negatives. This only works on
12586 * VMS versions v6.0 and later since that's when sys$create_user_profile
12587 * became available.
12590 /* get current process privs and username */
12591 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
12592 _ckvmssts_noperl(iosb[0]);
12594 #if defined(__VMS_VER) && __VMS_VER >= 60000000
12596 /* find out the space required for the profile */
12597 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
12598 &usrprodsc.dsc$w_length,&profile_context));
12600 /* allocate space for the profile and get it filled in */
12601 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
12602 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12603 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
12604 &usrprodsc.dsc$w_length,&profile_context));
12606 /* use the profile to check access to the file; free profile & analyze results */
12607 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
12608 PerlMem_free(usrprodsc.dsc$a_pointer);
12609 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
12613 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
12617 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
12618 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
12619 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
12620 set_vaxc_errno(retsts);
12621 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12622 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12623 else set_errno(ENOENT);
12624 if (fileified != NULL)
12625 PerlMem_free(fileified);
12626 if (vmsname != NULL)
12627 PerlMem_free(vmsname);
12630 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12631 if (fileified != NULL)
12632 PerlMem_free(fileified);
12633 if (vmsname != NULL)
12634 PerlMem_free(vmsname);
12637 _ckvmssts_noperl(retsts);
12639 if (fileified != NULL)
12640 PerlMem_free(fileified);
12641 if (vmsname != NULL)
12642 PerlMem_free(vmsname);
12643 return FALSE; /* Should never get here */
12647 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
12648 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12649 * subset of the applicable information.
12652 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12654 return cando_by_name_int
12655 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12656 } /* end of cando() */
12660 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12662 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12664 return cando_by_name_int(bit, effective, fname, 0);
12666 } /* end of cando_by_name() */
12670 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12672 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12674 if (!fstat(fd, &statbufp->crtl_stat)) {
12676 char *vms_filename;
12677 vms_filename = PerlMem_malloc(VMS_MAXRSS);
12678 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12680 /* Save name for cando by name in VMS format */
12681 cptr = getname(fd, vms_filename, 1);
12683 /* This should not happen, but just in case */
12684 if (cptr == NULL) {
12685 statbufp->st_devnam[0] = 0;
12688 /* Make sure that the saved name fits in 255 characters */
12689 cptr = int_rmsexpand_vms
12691 statbufp->st_devnam,
12694 statbufp->st_devnam[0] = 0;
12696 PerlMem_free(vms_filename);
12698 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12700 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12702 # ifdef RTL_USES_UTC
12703 # ifdef VMSISH_TIME
12705 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12706 statbufp->st_atime = _toloc(statbufp->st_atime);
12707 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12711 # ifdef VMSISH_TIME
12712 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12716 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12717 statbufp->st_atime = _toutc(statbufp->st_atime);
12718 statbufp->st_ctime = _toutc(statbufp->st_ctime);
12725 } /* end of flex_fstat() */
12728 #if !defined(__VAX) && __CRTL_VER >= 80200000
12736 #define lstat(_x, _y) stat(_x, _y)
12739 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
12742 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12746 const char *save_spec;
12757 if (decc_bug_devnull != 0) {
12758 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12759 memset(statbufp,0,sizeof *statbufp);
12760 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12761 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12762 statbufp->st_uid = 0x00010001;
12763 statbufp->st_gid = 0x0001;
12764 time((time_t *)&statbufp->st_mtime);
12765 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12770 /* Try for a directory name first. If fspec contains a filename without
12771 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12772 * and sea:[wine.dark]water. exist, we prefer the directory here.
12773 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12774 * not sea:[wine.dark]., if the latter exists. If the intended target is
12775 * the file with null type, specify this by calling flex_stat() with
12776 * a '.' at the end of fspec.
12778 * If we are in Posix filespec mode, accept the filename as is.
12782 fileified = PerlMem_malloc(VMS_MAXRSS);
12783 if (fileified == NULL)
12784 _ckvmssts_noperl(SS$_INSFMEM);
12786 temp_fspec = PerlMem_malloc(VMS_MAXRSS);
12787 if (temp_fspec == NULL)
12788 _ckvmssts_noperl(SS$_INSFMEM);
12790 strcpy(temp_fspec, fspec);
12794 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12795 if (decc_posix_compliant_pathnames == 0) {
12798 /* We may be able to optimize this, but in order for fileify_dirspec to
12799 * always return a usuable answer, we have to call vmspath first to
12800 * make sure that it is in VMS directory format, as stat/lstat on 8.3
12801 * can not handle directories in unix format that it does not have read
12802 * access to. Vmspath handles the case where a bare name which could be
12803 * a logical name gets passed.
12805 ret_spec = int_tovmspath(fspec, temp_fspec, NULL);
12806 if (ret_spec != NULL) {
12807 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
12808 if (ret_spec != NULL) {
12809 if (lstat_flag == 0)
12810 retval = stat(fileified, &statbufp->crtl_stat);
12812 retval = lstat(fileified, &statbufp->crtl_stat);
12813 save_spec = fileified;
12817 if (retval && vms_bug_stat_filename) {
12819 /* We should try again as a vmsified file specification */
12820 /* However Perl traditionally has not done this, which */
12821 /* causes problems with existing tests */
12823 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12824 if (ret_spec != NULL) {
12825 if (lstat_flag == 0)
12826 retval = stat(temp_fspec, &statbufp->crtl_stat);
12828 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12829 save_spec = temp_fspec;
12834 /* Last chance - allow multiple dots with out EFS CHARSET */
12835 /* The CRTL stat() falls down hard on multi-dot filenames in unix
12836 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12837 * enable it if it isn't already.
12839 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12840 if (!decc_efs_charset && (decc_efs_charset_index > 0))
12841 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12843 if (lstat_flag == 0)
12844 retval = stat(fspec, &statbufp->crtl_stat);
12846 retval = lstat(fspec, &statbufp->crtl_stat);
12848 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12849 if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12850 decc$feature_set_value(decc_efs_charset_index, 1, 0);
12856 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12858 if (lstat_flag == 0)
12859 retval = stat(temp_fspec, &statbufp->crtl_stat);
12861 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12862 save_spec = temp_fspec;
12866 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12867 /* As you were... */
12868 if (!decc_efs_charset)
12869 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12874 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12876 /* If this is an lstat, do not follow the link */
12878 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12880 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12881 /* If we used the efs_hack above, we must also use it here for */
12882 /* perl_cando to work */
12883 if (efs_hack && (decc_efs_charset_index > 0)) {
12884 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12887 cptr = int_rmsexpand_tovms(save_spec, statbufp->st_devnam, rmsex_flags);
12888 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12889 if (efs_hack && (decc_efs_charset_index > 0)) {
12890 decc$feature_set_value(decc_efs_charset, 1, 0);
12894 /* Fix me: If this is NULL then stat found a file, and we could */
12895 /* not convert the specification to VMS - Should never happen */
12897 statbufp->st_devnam[0] = 0;
12899 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12901 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12902 # ifdef RTL_USES_UTC
12903 # ifdef VMSISH_TIME
12905 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12906 statbufp->st_atime = _toloc(statbufp->st_atime);
12907 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12911 # ifdef VMSISH_TIME
12912 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12916 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12917 statbufp->st_atime = _toutc(statbufp->st_atime);
12918 statbufp->st_ctime = _toutc(statbufp->st_ctime);
12922 /* If we were successful, leave errno where we found it */
12923 if (retval == 0) RESTORE_ERRNO;
12926 } /* end of flex_stat_int() */
12929 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12931 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12933 return flex_stat_int(fspec, statbufp, 0);
12937 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12939 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12941 return flex_stat_int(fspec, statbufp, 1);
12946 /*{{{char *my_getlogin()*/
12947 /* VMS cuserid == Unix getlogin, except calling sequence */
12951 static char user[L_cuserid];
12952 return cuserid(user);
12957 /* rmscopy - copy a file using VMS RMS routines
12959 * Copies contents and attributes of spec_in to spec_out, except owner
12960 * and protection information. Name and type of spec_in are used as
12961 * defaults for spec_out. The third parameter specifies whether rmscopy()
12962 * should try to propagate timestamps from the input file to the output file.
12963 * If it is less than 0, no timestamps are preserved. If it is 0, then
12964 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12965 * propagated to the output file at creation iff the output file specification
12966 * did not contain an explicit name or type, and the revision date is always
12967 * updated at the end of the copy operation. If it is greater than 0, then
12968 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12969 * other than the revision date should be propagated, and bit 1 indicates
12970 * that the revision date should be propagated.
12972 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12974 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12975 * Incorporates, with permission, some code from EZCOPY by Tim Adye
12976 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12977 * as part of the Perl standard distribution under the terms of the
12978 * GNU General Public License or the Perl Artistic License. Copies
12979 * of each may be found in the Perl standard distribution.
12981 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12983 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12985 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12986 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12987 unsigned long int i, sts, sts2;
12989 struct FAB fab_in, fab_out;
12990 struct RAB rab_in, rab_out;
12991 rms_setup_nam(nam);
12992 rms_setup_nam(nam_out);
12993 struct XABDAT xabdat;
12994 struct XABFHC xabfhc;
12995 struct XABRDT xabrdt;
12996 struct XABSUM xabsum;
12998 vmsin = PerlMem_malloc(VMS_MAXRSS);
12999 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13000 vmsout = PerlMem_malloc(VMS_MAXRSS);
13001 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13002 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
13003 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
13004 PerlMem_free(vmsin);
13005 PerlMem_free(vmsout);
13006 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13010 esa = PerlMem_malloc(VMS_MAXRSS);
13011 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13013 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13014 esal = PerlMem_malloc(VMS_MAXRSS);
13015 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13017 fab_in = cc$rms_fab;
13018 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
13019 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
13020 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
13021 fab_in.fab$l_fop = FAB$M_SQO;
13022 rms_bind_fab_nam(fab_in, nam);
13023 fab_in.fab$l_xab = (void *) &xabdat;
13025 rsa = PerlMem_malloc(VMS_MAXRSS);
13026 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13028 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13029 rsal = PerlMem_malloc(VMS_MAXRSS);
13030 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13032 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
13033 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
13034 rms_nam_esl(nam) = 0;
13035 rms_nam_rsl(nam) = 0;
13036 rms_nam_esll(nam) = 0;
13037 rms_nam_rsll(nam) = 0;
13038 #ifdef NAM$M_NO_SHORT_UPCASE
13039 if (decc_efs_case_preserve)
13040 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
13043 xabdat = cc$rms_xabdat; /* To get creation date */
13044 xabdat.xab$l_nxt = (void *) &xabfhc;
13046 xabfhc = cc$rms_xabfhc; /* To get record length */
13047 xabfhc.xab$l_nxt = (void *) &xabsum;
13049 xabsum = cc$rms_xabsum; /* To get key and area information */
13051 if (!((sts = sys$open(&fab_in)) & 1)) {
13052 PerlMem_free(vmsin);
13053 PerlMem_free(vmsout);
13056 PerlMem_free(esal);
13059 PerlMem_free(rsal);
13060 set_vaxc_errno(sts);
13062 case RMS$_FNF: case RMS$_DNF:
13063 set_errno(ENOENT); break;
13065 set_errno(ENOTDIR); break;
13067 set_errno(ENODEV); break;
13069 set_errno(EINVAL); break;
13071 set_errno(EACCES); break;
13073 set_errno(EVMSERR);
13080 fab_out.fab$w_ifi = 0;
13081 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
13082 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
13083 fab_out.fab$l_fop = FAB$M_SQO;
13084 rms_bind_fab_nam(fab_out, nam_out);
13085 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
13086 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
13087 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
13088 esa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
13089 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13090 rsa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
13091 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13094 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13095 esal_out = PerlMem_malloc(VMS_MAXRSS);
13096 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13097 rsal_out = PerlMem_malloc(VMS_MAXRSS);
13098 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13100 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
13101 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
13103 if (preserve_dates == 0) { /* Act like DCL COPY */
13104 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
13105 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
13106 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
13107 PerlMem_free(vmsin);
13108 PerlMem_free(vmsout);
13111 PerlMem_free(esal);
13114 PerlMem_free(rsal);
13115 PerlMem_free(esa_out);
13116 if (esal_out != NULL)
13117 PerlMem_free(esal_out);
13118 PerlMem_free(rsa_out);
13119 if (rsal_out != NULL)
13120 PerlMem_free(rsal_out);
13121 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
13122 set_vaxc_errno(sts);
13125 fab_out.fab$l_xab = (void *) &xabdat;
13126 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
13127 preserve_dates = 1;
13129 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
13130 preserve_dates =0; /* bitmask from this point forward */
13132 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
13133 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
13134 PerlMem_free(vmsin);
13135 PerlMem_free(vmsout);
13138 PerlMem_free(esal);
13141 PerlMem_free(rsal);
13142 PerlMem_free(esa_out);
13143 if (esal_out != NULL)
13144 PerlMem_free(esal_out);
13145 PerlMem_free(rsa_out);
13146 if (rsal_out != NULL)
13147 PerlMem_free(rsal_out);
13148 set_vaxc_errno(sts);
13151 set_errno(ENOENT); break;
13153 set_errno(ENOTDIR); break;
13155 set_errno(ENODEV); break;
13157 set_errno(EINVAL); break;
13159 set_errno(EACCES); break;
13161 set_errno(EVMSERR);
13165 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
13166 if (preserve_dates & 2) {
13167 /* sys$close() will process xabrdt, not xabdat */
13168 xabrdt = cc$rms_xabrdt;
13170 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
13172 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
13173 * is unsigned long[2], while DECC & VAXC use a struct */
13174 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
13176 fab_out.fab$l_xab = (void *) &xabrdt;
13179 ubf = PerlMem_malloc(32256);
13180 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13181 rab_in = cc$rms_rab;
13182 rab_in.rab$l_fab = &fab_in;
13183 rab_in.rab$l_rop = RAB$M_BIO;
13184 rab_in.rab$l_ubf = ubf;
13185 rab_in.rab$w_usz = 32256;
13186 if (!((sts = sys$connect(&rab_in)) & 1)) {
13187 sys$close(&fab_in); sys$close(&fab_out);
13188 PerlMem_free(vmsin);
13189 PerlMem_free(vmsout);
13193 PerlMem_free(esal);
13196 PerlMem_free(rsal);
13197 PerlMem_free(esa_out);
13198 if (esal_out != NULL)
13199 PerlMem_free(esal_out);
13200 PerlMem_free(rsa_out);
13201 if (rsal_out != NULL)
13202 PerlMem_free(rsal_out);
13203 set_errno(EVMSERR); set_vaxc_errno(sts);
13207 rab_out = cc$rms_rab;
13208 rab_out.rab$l_fab = &fab_out;
13209 rab_out.rab$l_rbf = ubf;
13210 if (!((sts = sys$connect(&rab_out)) & 1)) {
13211 sys$close(&fab_in); sys$close(&fab_out);
13212 PerlMem_free(vmsin);
13213 PerlMem_free(vmsout);
13217 PerlMem_free(esal);
13220 PerlMem_free(rsal);
13221 PerlMem_free(esa_out);
13222 if (esal_out != NULL)
13223 PerlMem_free(esal_out);
13224 PerlMem_free(rsa_out);
13225 if (rsal_out != NULL)
13226 PerlMem_free(rsal_out);
13227 set_errno(EVMSERR); set_vaxc_errno(sts);
13231 while ((sts = sys$read(&rab_in))) { /* always true */
13232 if (sts == RMS$_EOF) break;
13233 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
13234 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
13235 sys$close(&fab_in); sys$close(&fab_out);
13236 PerlMem_free(vmsin);
13237 PerlMem_free(vmsout);
13241 PerlMem_free(esal);
13244 PerlMem_free(rsal);
13245 PerlMem_free(esa_out);
13246 if (esal_out != NULL)
13247 PerlMem_free(esal_out);
13248 PerlMem_free(rsa_out);
13249 if (rsal_out != NULL)
13250 PerlMem_free(rsal_out);
13251 set_errno(EVMSERR); set_vaxc_errno(sts);
13257 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
13258 sys$close(&fab_in); sys$close(&fab_out);
13259 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
13261 PerlMem_free(vmsin);
13262 PerlMem_free(vmsout);
13266 PerlMem_free(esal);
13269 PerlMem_free(rsal);
13270 PerlMem_free(esa_out);
13271 if (esal_out != NULL)
13272 PerlMem_free(esal_out);
13273 PerlMem_free(rsa_out);
13274 if (rsal_out != NULL)
13275 PerlMem_free(rsal_out);
13278 set_errno(EVMSERR); set_vaxc_errno(sts);
13284 } /* end of rmscopy() */
13288 /*** The following glue provides 'hooks' to make some of the routines
13289 * from this file available from Perl. These routines are sufficiently
13290 * basic, and are required sufficiently early in the build process,
13291 * that's it's nice to have them available to miniperl as well as the
13292 * full Perl, so they're set up here instead of in an extension. The
13293 * Perl code which handles importation of these names into a given
13294 * package lives in [.VMS]Filespec.pm in @INC.
13298 rmsexpand_fromperl(pTHX_ CV *cv)
13301 char *fspec, *defspec = NULL, *rslt;
13303 int fs_utf8, dfs_utf8;
13307 if (!items || items > 2)
13308 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
13309 fspec = SvPV(ST(0),n_a);
13310 fs_utf8 = SvUTF8(ST(0));
13311 if (!fspec || !*fspec) XSRETURN_UNDEF;
13313 defspec = SvPV(ST(1),n_a);
13314 dfs_utf8 = SvUTF8(ST(1));
13316 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
13317 ST(0) = sv_newmortal();
13318 if (rslt != NULL) {
13319 sv_usepvn(ST(0),rslt,strlen(rslt));
13328 vmsify_fromperl(pTHX_ CV *cv)
13335 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
13336 utf8_fl = SvUTF8(ST(0));
13337 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13338 ST(0) = sv_newmortal();
13339 if (vmsified != NULL) {
13340 sv_usepvn(ST(0),vmsified,strlen(vmsified));
13349 unixify_fromperl(pTHX_ CV *cv)
13356 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
13357 utf8_fl = SvUTF8(ST(0));
13358 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13359 ST(0) = sv_newmortal();
13360 if (unixified != NULL) {
13361 sv_usepvn(ST(0),unixified,strlen(unixified));
13370 fileify_fromperl(pTHX_ CV *cv)
13377 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
13378 utf8_fl = SvUTF8(ST(0));
13379 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13380 ST(0) = sv_newmortal();
13381 if (fileified != NULL) {
13382 sv_usepvn(ST(0),fileified,strlen(fileified));
13391 pathify_fromperl(pTHX_ CV *cv)
13398 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
13399 utf8_fl = SvUTF8(ST(0));
13400 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13401 ST(0) = sv_newmortal();
13402 if (pathified != NULL) {
13403 sv_usepvn(ST(0),pathified,strlen(pathified));
13412 vmspath_fromperl(pTHX_ CV *cv)
13419 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
13420 utf8_fl = SvUTF8(ST(0));
13421 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13422 ST(0) = sv_newmortal();
13423 if (vmspath != NULL) {
13424 sv_usepvn(ST(0),vmspath,strlen(vmspath));
13433 unixpath_fromperl(pTHX_ CV *cv)
13440 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
13441 utf8_fl = SvUTF8(ST(0));
13442 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13443 ST(0) = sv_newmortal();
13444 if (unixpath != NULL) {
13445 sv_usepvn(ST(0),unixpath,strlen(unixpath));
13454 candelete_fromperl(pTHX_ CV *cv)
13462 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
13464 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13465 Newx(fspec, VMS_MAXRSS, char);
13466 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
13467 if (SvTYPE(mysv) == SVt_PVGV) {
13468 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
13469 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13477 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
13478 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13485 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
13491 rmscopy_fromperl(pTHX_ CV *cv)
13494 char *inspec, *outspec, *inp, *outp;
13496 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
13497 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13498 unsigned long int sts;
13503 if (items < 2 || items > 3)
13504 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
13506 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13507 Newx(inspec, VMS_MAXRSS, char);
13508 if (SvTYPE(mysv) == SVt_PVGV) {
13509 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
13510 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13518 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
13519 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13525 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
13526 Newx(outspec, VMS_MAXRSS, char);
13527 if (SvTYPE(mysv) == SVt_PVGV) {
13528 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
13529 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13538 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
13539 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13546 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
13548 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
13554 /* The mod2fname is limited to shorter filenames by design, so it should
13555 * not be modified to support longer EFS pathnames
13558 mod2fname(pTHX_ CV *cv)
13561 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
13562 workbuff[NAM$C_MAXRSS*1 + 1];
13563 int total_namelen = 3, counter, num_entries;
13564 /* ODS-5 ups this, but we want to be consistent, so... */
13565 int max_name_len = 39;
13566 AV *in_array = (AV *)SvRV(ST(0));
13568 num_entries = av_len(in_array);
13570 /* All the names start with PL_. */
13571 strcpy(ultimate_name, "PL_");
13573 /* Clean up our working buffer */
13574 Zero(work_name, sizeof(work_name), char);
13576 /* Run through the entries and build up a working name */
13577 for(counter = 0; counter <= num_entries; counter++) {
13578 /* If it's not the first name then tack on a __ */
13580 strcat(work_name, "__");
13582 strcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)));
13585 /* Check to see if we actually have to bother...*/
13586 if (strlen(work_name) + 3 <= max_name_len) {
13587 strcat(ultimate_name, work_name);
13589 /* It's too darned big, so we need to go strip. We use the same */
13590 /* algorithm as xsubpp does. First, strip out doubled __ */
13591 char *source, *dest, last;
13594 for (source = work_name; *source; source++) {
13595 if (last == *source && last == '_') {
13601 /* Go put it back */
13602 strcpy(work_name, workbuff);
13603 /* Is it still too big? */
13604 if (strlen(work_name) + 3 > max_name_len) {
13605 /* Strip duplicate letters */
13608 for (source = work_name; *source; source++) {
13609 if (last == toupper(*source)) {
13613 last = toupper(*source);
13615 strcpy(work_name, workbuff);
13618 /* Is it *still* too big? */
13619 if (strlen(work_name) + 3 > max_name_len) {
13620 /* Too bad, we truncate */
13621 work_name[max_name_len - 2] = 0;
13623 strcat(ultimate_name, work_name);
13626 /* Okay, return it */
13627 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13632 hushexit_fromperl(pTHX_ CV *cv)
13637 VMSISH_HUSHED = SvTRUE(ST(0));
13639 ST(0) = boolSV(VMSISH_HUSHED);
13645 Perl_vms_start_glob
13646 (pTHX_ SV *tmpglob,
13650 struct vs_str_st *rslt;
13654 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13657 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13658 struct dsc$descriptor_vs rsdsc;
13659 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13660 unsigned long hasver = 0, isunix = 0;
13661 unsigned long int lff_flags = 0;
13663 int vms_old_glob = 1;
13665 if (!SvOK(tmpglob)) {
13666 SETERRNO(ENOENT,RMS$_FNF);
13670 vms_old_glob = !decc_filename_unix_report;
13672 #ifdef VMS_LONGNAME_SUPPORT
13673 lff_flags = LIB$M_FIL_LONG_NAMES;
13675 /* The Newx macro will not allow me to assign a smaller array
13676 * to the rslt pointer, so we will assign it to the begin char pointer
13677 * and then copy the value into the rslt pointer.
13679 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13680 rslt = (struct vs_str_st *)begin;
13682 rstr = &rslt->str[0];
13683 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13684 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13685 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13686 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13688 Newx(vmsspec, VMS_MAXRSS, char);
13690 /* We could find out if there's an explicit dev/dir or version
13691 by peeking into lib$find_file's internal context at
13692 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13693 but that's unsupported, so I don't want to do it now and
13694 have it bite someone in the future. */
13695 /* Fix-me: vms_split_path() is the only way to do this, the
13696 existing method will fail with many legal EFS or UNIX specifications
13699 cp = SvPV(tmpglob,i);
13702 if (cp[i] == ';') hasver = 1;
13703 if (cp[i] == '.') {
13704 if (sts) hasver = 1;
13707 if (cp[i] == '/') {
13708 hasdir = isunix = 1;
13711 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13717 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13718 if ((hasdir == 0) && decc_filename_unix_report) {
13722 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13723 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13724 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13730 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13731 if (!stat_sts && S_ISDIR(st.st_mode)) {
13733 const char * fname;
13736 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13737 /* path delimiter of ':>]', if so, then the old behavior has */
13738 /* obviously been specificially requested */
13740 fname = SvPVX_const(tmpglob);
13741 fname_len = strlen(fname);
13742 vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13743 if (vms_old_glob || (vms_dir != NULL)) {
13744 wilddsc.dsc$a_pointer = tovmspath_utf8(
13745 SvPVX(tmpglob),vmsspec,NULL);
13746 ok = (wilddsc.dsc$a_pointer != NULL);
13747 /* maybe passed 'foo' rather than '[.foo]', thus not
13751 /* Operate just on the directory, the special stat/fstat for */
13752 /* leaves the fileified specification in the st_devnam */
13754 wilddsc.dsc$a_pointer = st.st_devnam;
13759 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13760 ok = (wilddsc.dsc$a_pointer != NULL);
13763 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13765 /* If not extended character set, replace ? with % */
13766 /* With extended character set, ? is a wildcard single character */
13767 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13770 if (!decc_efs_case_preserve)
13772 } else if (*cp == '%') {
13774 } else if (*cp == '*') {
13780 wv_sts = vms_split_path(
13781 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13782 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13783 &wvs_spec, &wvs_len);
13792 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13793 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13794 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13798 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13799 &dfltdsc,NULL,&rms_sts,&lff_flags);
13800 if (!$VMS_STATUS_SUCCESS(sts))
13803 /* with varying string, 1st word of buffer contains result length */
13804 rstr[rslt->length] = '\0';
13806 /* Find where all the components are */
13807 v_sts = vms_split_path
13822 /* If no version on input, truncate the version on output */
13823 if (!hasver && (vs_len > 0)) {
13830 /* In Unix report mode, remove the ".dir;1" from the name */
13831 /* if it is a real directory */
13832 if (decc_filename_unix_report || decc_efs_charset) {
13833 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13837 ret_sts = flex_lstat(rstr, &statbuf);
13838 if ((ret_sts == 0) &&
13839 S_ISDIR(statbuf.st_mode)) {
13846 /* No version & a null extension on UNIX handling */
13847 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13853 if (!decc_efs_case_preserve) {
13854 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13857 /* Find File treats a Null extension as return all extensions */
13858 /* This is contrary to Perl expectations */
13860 if (wildstar || wildquery || vms_old_glob) {
13861 /* really need to see if the returned file name matched */
13862 /* but for now will assume that it matches */
13865 /* Exact Match requested */
13866 /* How are directories handled? - like a file */
13867 if ((e_len == we_len) && (n_len == wn_len)) {
13871 t1 = strncmp(e_spec, we_spec, e_len);
13875 t1 = strncmp(n_spec, we_spec, n_len);
13886 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13890 /* Start with the name */
13893 strcat(begin,"\n");
13894 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13897 if (cxt) (void)lib$find_file_end(&cxt);
13900 /* Be POSIXish: return the input pattern when no matches */
13901 strcpy(rstr,SvPVX(tmpglob));
13903 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13906 if (ok && sts != RMS$_NMF &&
13907 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13910 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13912 PerlIO_close(tmpfp);
13916 PerlIO_rewind(tmpfp);
13917 IoTYPE(io) = IoTYPE_RDONLY;
13918 IoIFP(io) = fp = tmpfp;
13919 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13929 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13933 unixrealpath_fromperl(pTHX_ CV *cv)
13936 char *fspec, *rslt_spec, *rslt;
13939 if (!items || items != 1)
13940 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13942 fspec = SvPV(ST(0),n_a);
13943 if (!fspec || !*fspec) XSRETURN_UNDEF;
13945 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13946 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13948 ST(0) = sv_newmortal();
13950 sv_usepvn(ST(0),rslt,strlen(rslt));
13952 Safefree(rslt_spec);
13957 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13961 vmsrealpath_fromperl(pTHX_ CV *cv)
13964 char *fspec, *rslt_spec, *rslt;
13967 if (!items || items != 1)
13968 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(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_realname(fspec, rslt_spec, NULL);
13976 ST(0) = sv_newmortal();
13978 sv_usepvn(ST(0),rslt,strlen(rslt));
13980 Safefree(rslt_spec);
13986 * A thin wrapper around decc$symlink to make sure we follow the
13987 * standard and do not create a symlink with a zero-length name.
13989 * Also in ODS-2 mode, existing tests assume that the link target
13990 * will be converted to UNIX format.
13992 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13993 int Perl_my_symlink(pTHX_ const char *contents, const char *link_name) {
13994 if (!link_name || !*link_name) {
13995 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13999 if (decc_efs_charset) {
14000 return symlink(contents, link_name);
14005 /* Unless we are in ODS-5 mode, convert the symlink target to UNIX */
14006 /* because in order to work, the symlink target must be in UNIX format */
14008 /* As symbolic links can hold things other than files, we will only do */
14009 /* the conversion in in ODS-2 mode */
14011 Newx(utarget, VMS_MAXRSS + 1, char);
14012 if (int_tounixspec(contents, utarget, NULL) == NULL) {
14014 /* This should not fail, as an untranslatable filename */
14015 /* should be passed through */
14016 utarget = (char *)contents;
14018 sts = symlink(utarget, link_name);
14026 #endif /* HAS_SYMLINK */
14028 int do_vms_case_tolerant(void);
14031 case_tolerant_process_fromperl(pTHX_ CV *cv)
14034 ST(0) = boolSV(do_vms_case_tolerant());
14038 #ifdef USE_ITHREADS
14041 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
14042 struct interp_intern *dst)
14044 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
14046 memcpy(dst,src,sizeof(struct interp_intern));
14052 Perl_sys_intern_clear(pTHX)
14057 Perl_sys_intern_init(pTHX)
14059 unsigned int ix = RAND_MAX;
14064 MY_POSIX_EXIT = vms_posix_exit;
14067 MY_INV_RAND_MAX = 1./x;
14071 init_os_extras(void)
14074 char* file = __FILE__;
14075 if (decc_disable_to_vms_logname_translation) {
14076 no_translate_barewords = TRUE;
14078 no_translate_barewords = FALSE;
14081 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
14082 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
14083 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
14084 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
14085 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
14086 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
14087 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
14088 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
14089 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
14090 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
14091 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
14092 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
14093 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
14094 newXSproto("VMS::Filespec::case_tolerant_process",
14095 case_tolerant_process_fromperl,file,"");
14097 store_pipelocs(aTHX); /* will redo any earlier attempts */
14102 #if __CRTL_VER == 80200000
14103 /* This missed getting in to the DECC SDK for 8.2 */
14104 char *realpath(const char *file_name, char * resolved_name, ...);
14107 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
14108 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
14109 * The perl fallback routine to provide realpath() is not as efficient
14113 /* Hack, use old stat() as fastest way of getting ino_t and device */
14114 int decc$stat(const char *name, void * statbuf);
14115 #if !defined(__VAX) && __CRTL_VER >= 80200000
14116 int decc$lstat(const char *name, void * statbuf);
14118 #define decc$lstat decc$stat
14122 /* Realpath is fragile. In 8.3 it does not work if the feature
14123 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
14124 * links are implemented in RMS, not the CRTL. It also can fail if the
14125 * user does not have read/execute access to some of the directories.
14126 * So in order for Do What I Mean mode to work, if realpath() fails,
14127 * fall back to looking up the filename by the device name and FID.
14130 int vms_fid_to_name(char * outname, int outlen,
14131 const char * name, int lstat_flag, mode_t * mode)
14133 #pragma message save
14134 #pragma message disable MISALGNDSTRCT
14135 #pragma message disable MISALGNDMEM
14136 #pragma member_alignment save
14137 #pragma nomember_alignment
14140 unsigned short st_ino[3];
14141 unsigned short old_st_mode;
14142 unsigned long padl[30]; /* plenty of room */
14144 #pragma message restore
14145 #pragma member_alignment restore
14148 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14149 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14154 /* Need to follow the mostly the same rules as flex_stat_int, or we may get
14155 * unexpected answers
14158 fileified = PerlMem_malloc(VMS_MAXRSS);
14159 if (fileified == NULL)
14160 _ckvmssts_noperl(SS$_INSFMEM);
14162 temp_fspec = PerlMem_malloc(VMS_MAXRSS);
14163 if (temp_fspec == NULL)
14164 _ckvmssts_noperl(SS$_INSFMEM);
14167 /* First need to try as a directory */
14168 ret_spec = int_tovmspath(name, temp_fspec, NULL);
14169 if (ret_spec != NULL) {
14170 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
14171 if (ret_spec != NULL) {
14172 if (lstat_flag == 0)
14173 sts = decc$stat(fileified, &statbuf);
14175 sts = decc$lstat(fileified, &statbuf);
14179 /* Then as a VMS file spec */
14181 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
14182 if (ret_spec != NULL) {
14183 if (lstat_flag == 0) {
14184 sts = decc$stat(temp_fspec, &statbuf);
14186 sts = decc$lstat(temp_fspec, &statbuf);
14192 /* Next try - allow multiple dots with out EFS CHARSET */
14193 /* The CRTL stat() falls down hard on multi-dot filenames in unix
14194 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
14195 * enable it if it isn't already.
14197 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14198 if (!decc_efs_charset && (decc_efs_charset_index > 0))
14199 decc$feature_set_value(decc_efs_charset_index, 1, 1);
14201 ret_spec = int_tovmspath(name, temp_fspec, NULL);
14202 if (lstat_flag == 0) {
14203 sts = decc$stat(name, &statbuf);
14205 sts = decc$lstat(name, &statbuf);
14207 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14208 if (!decc_efs_charset && (decc_efs_charset_index > 0))
14209 decc$feature_set_value(decc_efs_charset_index, 1, 0);
14214 /* and then because the Perl Unix to VMS conversion is not perfect */
14215 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
14216 /* characters from filenames so we need to try it as-is */
14218 if (lstat_flag == 0) {
14219 sts = decc$stat(name, &statbuf);
14221 sts = decc$lstat(name, &statbuf);
14228 dvidsc.dsc$a_pointer=statbuf.st_dev;
14229 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
14231 specdsc.dsc$a_pointer = outname;
14232 specdsc.dsc$w_length = outlen-1;
14234 vms_sts = lib$fid_to_name
14235 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
14236 if ($VMS_STATUS_SUCCESS(vms_sts)) {
14237 outname[specdsc.dsc$w_length] = 0;
14239 /* Return the mode */
14241 *mode = statbuf.old_st_mode;
14252 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
14255 char * rslt = NULL;
14258 if (decc_posix_compliant_pathnames > 0 ) {
14259 /* realpath currently only works if posix compliant pathnames are
14260 * enabled. It may start working when they are not, but in that
14261 * case we still want the fallback behavior for backwards compatibility
14263 rslt = realpath(filespec, outbuf);
14267 if (rslt == NULL) {
14269 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14270 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14274 /* Fall back to fid_to_name */
14276 Newx(vms_spec, VMS_MAXRSS + 1, char);
14278 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
14282 /* Now need to trim the version off */
14283 sts = vms_split_path
14303 /* Trim off the version */
14304 int file_len = v_len + r_len + d_len + n_len + e_len;
14305 vms_spec[file_len] = 0;
14307 /* The result is expected to be in UNIX format */
14308 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
14310 /* Downcase if input had any lower case letters and
14311 * case preservation is not in effect.
14313 if (!decc_efs_case_preserve) {
14314 for (cp = filespec; *cp; cp++)
14315 if (islower(*cp)) { haslower = 1; break; }
14317 if (haslower) __mystrtolower(rslt);
14322 /* Now for some hacks to deal with backwards and forward */
14324 if (!decc_efs_charset) {
14326 /* 1. ODS-2 mode wants to do a syntax only translation */
14327 rslt = int_rmsexpand(filespec, outbuf,
14328 NULL, 0, NULL, utf8_fl);
14331 if (decc_filename_unix_report) {
14333 char * vms_dir_name;
14336 /* 2. ODS-5 / UNIX report mode should return a failure */
14337 /* if the parent directory also does not exist */
14338 /* Otherwise, get the real path for the parent */
14339 /* and add the child to it.
14341 /* basename / dirname only available for VMS 7.0+ */
14342 /* So we may need to implement them as common routines */
14344 Newx(dir_name, VMS_MAXRSS + 1, char);
14345 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
14346 dir_name[0] = '\0';
14349 /* First try a VMS parse */
14350 sts = vms_split_path
14368 int dir_len = v_len + r_len + d_len + n_len;
14370 strncpy(dir_name, filespec, dir_len);
14371 dir_name[dir_len] = '\0';
14372 file_name = (char *)&filespec[dir_len + 1];
14375 /* This must be UNIX */
14378 tchar = strrchr(filespec, '/');
14380 if (tchar != NULL) {
14381 int dir_len = tchar - filespec;
14382 strncpy(dir_name, filespec, dir_len);
14383 dir_name[dir_len] = '\0';
14384 file_name = (char *) &filespec[dir_len + 1];
14388 /* Dir name is defaulted */
14389 if (dir_name[0] == 0) {
14391 dir_name[1] = '\0';
14394 /* Need realpath for the directory */
14395 sts = vms_fid_to_name(vms_dir_name,
14397 dir_name, 0, NULL);
14400 /* Now need to pathify it.
14401 char *tdir = int_pathify_dirspec(vms_dir_name,
14404 /* And now add the original filespec to it */
14405 if (file_name != NULL) {
14406 strcat(outbuf, file_name);
14410 Safefree(vms_dir_name);
14411 Safefree(dir_name);
14415 Safefree(vms_spec);
14421 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
14424 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14425 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14428 /* Fall back to fid_to_name */
14430 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
14437 /* Now need to trim the version off */
14438 sts = vms_split_path
14458 /* Trim off the version */
14459 int file_len = v_len + r_len + d_len + n_len + e_len;
14460 outbuf[file_len] = 0;
14462 /* Downcase if input had any lower case letters and
14463 * case preservation is not in effect.
14465 if (!decc_efs_case_preserve) {
14466 for (cp = filespec; *cp; cp++)
14467 if (islower(*cp)) { haslower = 1; break; }
14469 if (haslower) __mystrtolower(outbuf);
14478 /* External entry points */
14479 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14480 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
14482 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14483 { return do_vms_realname(filespec, outbuf, utf8_fl); }
14485 /* case_tolerant */
14487 /*{{{int do_vms_case_tolerant(void)*/
14488 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
14489 * controlled by a process setting.
14491 int do_vms_case_tolerant(void)
14493 return vms_process_case_tolerant;
14496 /* External entry points */
14497 #if __CRTL_VER >= 70301000 && !defined(__VAX)
14498 int Perl_vms_case_tolerant(void)
14499 { return do_vms_case_tolerant(); }
14501 int Perl_vms_case_tolerant(void)
14502 { return vms_process_case_tolerant; }
14506 /* Start of DECC RTL Feature handling */
14508 static int sys_trnlnm
14509 (const char * logname,
14513 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
14514 const unsigned long attr = LNM$M_CASE_BLIND;
14515 struct dsc$descriptor_s name_dsc;
14517 unsigned short result;
14518 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
14521 name_dsc.dsc$w_length = strlen(logname);
14522 name_dsc.dsc$a_pointer = (char *)logname;
14523 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14524 name_dsc.dsc$b_class = DSC$K_CLASS_S;
14526 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
14528 if ($VMS_STATUS_SUCCESS(status)) {
14530 /* Null terminate and return the string */
14531 /*--------------------------------------*/
14538 static int sys_crelnm
14539 (const char * logname,
14540 const char * value)
14543 const char * proc_table = "LNM$PROCESS_TABLE";
14544 struct dsc$descriptor_s proc_table_dsc;
14545 struct dsc$descriptor_s logname_dsc;
14546 struct itmlst_3 item_list[2];
14548 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
14549 proc_table_dsc.dsc$w_length = strlen(proc_table);
14550 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14551 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
14553 logname_dsc.dsc$a_pointer = (char *) logname;
14554 logname_dsc.dsc$w_length = strlen(logname);
14555 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14556 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
14558 item_list[0].buflen = strlen(value);
14559 item_list[0].itmcode = LNM$_STRING;
14560 item_list[0].bufadr = (char *)value;
14561 item_list[0].retlen = NULL;
14563 item_list[1].buflen = 0;
14564 item_list[1].itmcode = 0;
14566 ret_val = sys$crelnm
14568 (const struct dsc$descriptor_s *)&proc_table_dsc,
14569 (const struct dsc$descriptor_s *)&logname_dsc,
14571 (const struct item_list_3 *) item_list);
14576 /* C RTL Feature settings */
14578 static int set_features
14579 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
14580 int (* cli_routine)(void), /* Not documented */
14581 void *image_info) /* Not documented */
14587 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
14588 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
14589 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
14590 unsigned long case_perm;
14591 unsigned long case_image;
14594 /* Allow an exception to bring Perl into the VMS debugger */
14595 vms_debug_on_exception = 0;
14596 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
14597 if ($VMS_STATUS_SUCCESS(status)) {
14598 val_str[0] = _toupper(val_str[0]);
14599 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14600 vms_debug_on_exception = 1;
14602 vms_debug_on_exception = 0;
14605 /* Debug unix/vms file translation routines */
14606 vms_debug_fileify = 0;
14607 status = sys_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
14608 if ($VMS_STATUS_SUCCESS(status)) {
14609 val_str[0] = _toupper(val_str[0]);
14610 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14611 vms_debug_fileify = 1;
14613 vms_debug_fileify = 0;
14617 /* Historically PERL has been doing vmsify / stat differently than */
14618 /* the CRTL. In particular, under some conditions the CRTL will */
14619 /* remove some illegal characters like spaces from filenames */
14620 /* resulting in some differences. The stat()/lstat() wrapper has */
14621 /* been reporting such file names as invalid and fails to stat them */
14622 /* fixing this bug so that stat()/lstat() accept these like the */
14623 /* CRTL does will result in several tests failing. */
14624 /* This should really be fixed, but for now, set up a feature to */
14625 /* enable it so that the impact can be studied. */
14626 vms_bug_stat_filename = 0;
14627 status = sys_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
14628 if ($VMS_STATUS_SUCCESS(status)) {
14629 val_str[0] = _toupper(val_str[0]);
14630 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14631 vms_bug_stat_filename = 1;
14633 vms_bug_stat_filename = 0;
14637 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
14638 vms_vtf7_filenames = 0;
14639 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14640 if ($VMS_STATUS_SUCCESS(status)) {
14641 val_str[0] = _toupper(val_str[0]);
14642 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14643 vms_vtf7_filenames = 1;
14645 vms_vtf7_filenames = 0;
14648 /* unlink all versions on unlink() or rename() */
14649 vms_unlink_all_versions = 0;
14650 status = sys_trnlnm
14651 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14652 if ($VMS_STATUS_SUCCESS(status)) {
14653 val_str[0] = _toupper(val_str[0]);
14654 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14655 vms_unlink_all_versions = 1;
14657 vms_unlink_all_versions = 0;
14660 /* Dectect running under GNV Bash or other UNIX like shell */
14661 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14662 gnv_unix_shell = 0;
14663 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14664 if ($VMS_STATUS_SUCCESS(status)) {
14665 gnv_unix_shell = 1;
14666 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14667 set_feature_default("DECC$EFS_CHARSET", 1);
14668 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14669 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14670 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14671 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
14672 vms_unlink_all_versions = 1;
14673 vms_posix_exit = 1;
14677 /* hacks to see if known bugs are still present for testing */
14679 /* PCP mode requires creating /dev/null special device file */
14680 decc_bug_devnull = 0;
14681 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14682 if ($VMS_STATUS_SUCCESS(status)) {
14683 val_str[0] = _toupper(val_str[0]);
14684 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14685 decc_bug_devnull = 1;
14687 decc_bug_devnull = 0;
14690 /* UNIX directory names with no paths are broken in a lot of places */
14691 decc_dir_barename = 1;
14692 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
14693 if ($VMS_STATUS_SUCCESS(status)) {
14694 val_str[0] = _toupper(val_str[0]);
14695 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14696 decc_dir_barename = 1;
14698 decc_dir_barename = 0;
14701 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14702 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14704 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14705 if (decc_disable_to_vms_logname_translation < 0)
14706 decc_disable_to_vms_logname_translation = 0;
14709 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14711 decc_efs_case_preserve = decc$feature_get_value(s, 1);
14712 if (decc_efs_case_preserve < 0)
14713 decc_efs_case_preserve = 0;
14716 s = decc$feature_get_index("DECC$EFS_CHARSET");
14717 decc_efs_charset_index = s;
14719 decc_efs_charset = decc$feature_get_value(s, 1);
14720 if (decc_efs_charset < 0)
14721 decc_efs_charset = 0;
14724 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14726 decc_filename_unix_report = decc$feature_get_value(s, 1);
14727 if (decc_filename_unix_report > 0) {
14728 decc_filename_unix_report = 1;
14729 vms_posix_exit = 1;
14732 decc_filename_unix_report = 0;
14735 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14737 decc_filename_unix_only = decc$feature_get_value(s, 1);
14738 if (decc_filename_unix_only > 0) {
14739 decc_filename_unix_only = 1;
14742 decc_filename_unix_only = 0;
14746 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14748 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14749 if (decc_filename_unix_no_version < 0)
14750 decc_filename_unix_no_version = 0;
14753 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14755 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14756 if (decc_readdir_dropdotnotype < 0)
14757 decc_readdir_dropdotnotype = 0;
14760 #if __CRTL_VER >= 80200000
14761 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14763 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14764 if (decc_posix_compliant_pathnames < 0)
14765 decc_posix_compliant_pathnames = 0;
14766 if (decc_posix_compliant_pathnames > 4)
14767 decc_posix_compliant_pathnames = 0;
14772 status = sys_trnlnm
14773 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14774 if ($VMS_STATUS_SUCCESS(status)) {
14775 val_str[0] = _toupper(val_str[0]);
14776 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14777 decc_disable_to_vms_logname_translation = 1;
14782 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14783 if ($VMS_STATUS_SUCCESS(status)) {
14784 val_str[0] = _toupper(val_str[0]);
14785 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14786 decc_efs_case_preserve = 1;
14791 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14792 if ($VMS_STATUS_SUCCESS(status)) {
14793 val_str[0] = _toupper(val_str[0]);
14794 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14795 decc_filename_unix_report = 1;
14798 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14799 if ($VMS_STATUS_SUCCESS(status)) {
14800 val_str[0] = _toupper(val_str[0]);
14801 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14802 decc_filename_unix_only = 1;
14803 decc_filename_unix_report = 1;
14806 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14807 if ($VMS_STATUS_SUCCESS(status)) {
14808 val_str[0] = _toupper(val_str[0]);
14809 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14810 decc_filename_unix_no_version = 1;
14813 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14814 if ($VMS_STATUS_SUCCESS(status)) {
14815 val_str[0] = _toupper(val_str[0]);
14816 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14817 decc_readdir_dropdotnotype = 1;
14822 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14824 /* Report true case tolerance */
14825 /*----------------------------*/
14826 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14827 if (!$VMS_STATUS_SUCCESS(status))
14828 case_perm = PPROP$K_CASE_BLIND;
14829 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14830 if (!$VMS_STATUS_SUCCESS(status))
14831 case_image = PPROP$K_CASE_BLIND;
14832 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14833 (case_image == PPROP$K_CASE_SENSITIVE))
14834 vms_process_case_tolerant = 0;
14838 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
14839 /* for strict backward compatibilty */
14840 status = sys_trnlnm
14841 ("PERL_VMS_POSIX_EXIT", 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 vms_posix_exit = 1;
14847 vms_posix_exit = 0;
14851 /* CRTL can be initialized past this point, but not before. */
14852 /* DECC$CRTL_INIT(); */
14859 #pragma extern_model save
14860 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
14861 const __align (LONGWORD) int spare[8] = {0};
14863 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
14864 #if __DECC_VER >= 60560002
14865 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
14867 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
14869 #endif /* __DECC */
14871 const long vms_cc_features = (const long)set_features;
14874 ** Force a reference to LIB$INITIALIZE to ensure it
14875 ** exists in the image.
14877 int lib$initialize(void);
14879 #pragma extern_model strict_refdef
14881 int lib_init_ref = (int) lib$initialize;
14884 #pragma extern_model restore