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() */
2067 int vms_fid_to_name(char * outname, int outlen,
2068 const char * name, int lstat_flag, mode_t * mode);
2070 /*{{{int do_rmdir(char *name)*/
2072 Perl_do_rmdir(pTHX_ const char *name)
2078 dirfile = PerlMem_malloc(VMS_MAXRSS + 1);
2079 if (dirfile == NULL)
2080 _ckvmssts(SS$_INSFMEM);
2082 /* Force to a directory specification */
2083 if (do_fileify_dirspec(name, dirfile, 0, NULL) == NULL) {
2084 PerlMem_free(dirfile);
2087 if (Perl_flex_lstat(aTHX_ dirfile, &st) || !S_ISDIR(st.st_mode)) {
2092 retval = mp_do_kill_file(aTHX_ dirfile, 1);
2094 PerlMem_free(dirfile);
2097 } /* end of do_rmdir */
2101 * Delete any file to which user has control access, regardless of whether
2102 * delete access is explicitly allowed.
2103 * Limitations: User must have write access to parent directory.
2104 * Does not block signals or ASTs; if interrupted in midstream
2105 * may leave file with an altered ACL.
2108 /*{{{int kill_file(char *name)*/
2110 Perl_kill_file(pTHX_ const char *name)
2112 char rspec[NAM$C_MAXRSS+1];
2117 /* Remove() is allowed to delete directories, according to the X/Open
2119 * This may need special handling to work with the ACL hacks.
2121 if ((flex_lstat(name, &st) == 0) && S_ISDIR(st.st_mode)) {
2122 rmsts = Perl_do_rmdir(aTHX_ name);
2126 rmsts = mp_do_kill_file(aTHX_ name, 0);
2130 } /* end of kill_file() */
2134 /*{{{int my_mkdir(char *,Mode_t)*/
2136 Perl_my_mkdir(pTHX_ const char *dir, Mode_t mode)
2138 STRLEN dirlen = strlen(dir);
2140 /* zero length string sometimes gives ACCVIO */
2141 if (dirlen == 0) return -1;
2143 /* CRTL mkdir() doesn't tolerate trailing /, since that implies
2144 * null file name/type. However, it's commonplace under Unix,
2145 * so we'll allow it for a gain in portability.
2147 if (dir[dirlen-1] == '/') {
2148 char *newdir = savepvn(dir,dirlen-1);
2149 int ret = mkdir(newdir,mode);
2153 else return mkdir(dir,mode);
2154 } /* end of my_mkdir */
2157 /*{{{int my_chdir(char *)*/
2159 Perl_my_chdir(pTHX_ const char *dir)
2161 STRLEN dirlen = strlen(dir);
2163 /* zero length string sometimes gives ACCVIO */
2164 if (dirlen == 0) return -1;
2167 /* Perl is passing the output of the DCL SHOW DEFAULT with leading spaces.
2168 * This does not work if DECC$EFS_CHARSET is active. Hack it here
2169 * so that existing scripts do not need to be changed.
2172 while ((dirlen > 0) && (*dir1 == ' ')) {
2177 /* some versions of CRTL chdir() doesn't tolerate trailing /, since
2179 * null file name/type. However, it's commonplace under Unix,
2180 * so we'll allow it for a gain in portability.
2182 * - Preview- '/' will be valid soon on VMS
2184 if ((dirlen > 1) && (dir1[dirlen-1] == '/')) {
2185 char *newdir = savepvn(dir1,dirlen-1);
2186 int ret = chdir(newdir);
2190 else return chdir(dir1);
2191 } /* end of my_chdir */
2195 /*{{{int my_chmod(char *, mode_t)*/
2197 Perl_my_chmod(pTHX_ const char *file_spec, mode_t mode)
2199 STRLEN speclen = strlen(file_spec);
2201 /* zero length string sometimes gives ACCVIO */
2202 if (speclen == 0) return -1;
2204 /* some versions of CRTL chmod() doesn't tolerate trailing /, since
2205 * that implies null file name/type. However, it's commonplace under Unix,
2206 * so we'll allow it for a gain in portability.
2208 * Tests are showing that chmod() on VMS 8.3 is only accepting directories
2209 * in VMS file.dir notation.
2211 if ((speclen > 1) && (file_spec[speclen-1] == '/')) {
2212 char *vms_src, *vms_dir, *rslt;
2216 /* First convert this to a VMS format specification */
2217 vms_src = PerlMem_malloc(VMS_MAXRSS);
2218 if (vms_src == NULL)
2219 _ckvmssts_noperl(SS$_INSFMEM);
2221 rslt = do_tovmsspec(file_spec, vms_src, 0, NULL);
2223 /* If we fail, then not a file specification */
2224 PerlMem_free(vms_src);
2229 /* Now make it a directory spec so chmod is happy */
2230 vms_dir = PerlMem_malloc(VMS_MAXRSS + 1);
2231 if (vms_dir == NULL)
2232 _ckvmssts_noperl(SS$_INSFMEM);
2233 rslt = do_fileify_dirspec(vms_src, vms_dir, 0, NULL);
2234 PerlMem_free(vms_src);
2238 ret = chmod(vms_dir, mode);
2242 PerlMem_free(vms_dir);
2245 else return chmod(file_spec, mode);
2246 } /* end of my_chmod */
2250 /*{{{FILE *my_tmpfile()*/
2257 if ((fp = tmpfile())) return fp;
2259 cp = PerlMem_malloc(L_tmpnam+24);
2260 if (cp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
2262 if (decc_filename_unix_only == 0)
2263 strcpy(cp,"Sys$Scratch:");
2266 tmpnam(cp+strlen(cp));
2267 strcat(cp,".Perltmp");
2268 fp = fopen(cp,"w+","fop=dlt");
2275 #ifndef HOMEGROWN_POSIX_SIGNALS
2277 * The C RTL's sigaction fails to check for invalid signal numbers so we
2278 * help it out a bit. The docs are correct, but the actual routine doesn't
2279 * do what the docs say it will.
2281 /*{{{int Perl_my_sigaction (pTHX_ int, const struct sigaction*, struct sigaction*);*/
2283 Perl_my_sigaction (pTHX_ int sig, const struct sigaction* act,
2284 struct sigaction* oact)
2286 if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT) {
2287 SETERRNO(EINVAL, SS$_INVARG);
2290 return sigaction(sig, act, oact);
2295 #ifdef KILL_BY_SIGPRC
2296 #include <errnodef.h>
2298 /* We implement our own kill() using the undocumented system service
2299 sys$sigprc for one of two reasons:
2301 1.) If the kill() in an older CRTL uses sys$forcex, causing the
2302 target process to do a sys$exit, which usually can't be handled
2303 gracefully...certainly not by Perl and the %SIG{} mechanism.
2305 2.) If the kill() in the CRTL can't be called from a signal
2306 handler without disappearing into the ether, i.e., the signal
2307 it purportedly sends is never trapped. Still true as of VMS 7.3.
2309 sys$sigprc has the same parameters as sys$forcex, but throws an exception
2310 in the target process rather than calling sys$exit.
2312 Note that distinguishing SIGSEGV from SIGBUS requires an extra arg
2313 on the ACCVIO condition, which sys$sigprc (and sys$forcex) don't
2314 provide. On VMS 7.0+ this is taken care of by doing sys$sigprc
2315 with condition codes C$_SIG0+nsig*8, catching the exception on the
2316 target process and resignaling with appropriate arguments.
2318 But we don't have that VMS 7.0+ exception handler, so if you
2319 Perl_my_kill(.., SIGSEGV) it will show up as a SIGBUS. Oh well.
2321 Also note that SIGTERM is listed in the docs as being "unimplemented",
2322 yet always seems to be signaled with a VMS condition code of 4 (and
2323 correctly handled for that code). So we hardwire it in.
2325 Unlike the VMS 7.0+ CRTL kill() function, we actually check the signal
2326 number to see if it's valid. So Perl_my_kill(pid,0) returns -1 rather
2327 than signalling with an unrecognized (and unhandled by CRTL) code.
2330 #define _MY_SIG_MAX 28
2333 Perl_sig_to_vmscondition_int(int sig)
2335 static unsigned int sig_code[_MY_SIG_MAX+1] =
2338 SS$_HANGUP, /* 1 SIGHUP */
2339 SS$_CONTROLC, /* 2 SIGINT */
2340 SS$_CONTROLY, /* 3 SIGQUIT */
2341 SS$_RADRMOD, /* 4 SIGILL */
2342 SS$_BREAK, /* 5 SIGTRAP */
2343 SS$_OPCCUS, /* 6 SIGABRT */
2344 SS$_COMPAT, /* 7 SIGEMT */
2346 SS$_FLTOVF, /* 8 SIGFPE VAX */
2348 SS$_HPARITH, /* 8 SIGFPE AXP */
2350 SS$_ABORT, /* 9 SIGKILL */
2351 SS$_ACCVIO, /* 10 SIGBUS */
2352 SS$_ACCVIO, /* 11 SIGSEGV */
2353 SS$_BADPARAM, /* 12 SIGSYS */
2354 SS$_NOMBX, /* 13 SIGPIPE */
2355 SS$_ASTFLT, /* 14 SIGALRM */
2372 #if __VMS_VER >= 60200000
2373 static int initted = 0;
2376 sig_code[16] = C$_SIGUSR1;
2377 sig_code[17] = C$_SIGUSR2;
2378 #if __CRTL_VER >= 70000000
2379 sig_code[20] = C$_SIGCHLD;
2381 #if __CRTL_VER >= 70300000
2382 sig_code[28] = C$_SIGWINCH;
2387 if (sig < _SIG_MIN) return 0;
2388 if (sig > _MY_SIG_MAX) return 0;
2389 return sig_code[sig];
2393 Perl_sig_to_vmscondition(int sig)
2396 if (vms_debug_on_exception != 0)
2397 lib$signal(SS$_DEBUG);
2399 return Perl_sig_to_vmscondition_int(sig);
2404 Perl_my_kill(int pid, int sig)
2409 int sys$sigprc(unsigned int *pidadr,
2410 struct dsc$descriptor_s *prcname,
2413 /* sig 0 means validate the PID */
2414 /*------------------------------*/
2416 const unsigned long int jpicode = JPI$_PID;
2419 status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL);
2420 if ($VMS_STATUS_SUCCESS(status))
2423 case SS$_NOSUCHNODE:
2424 case SS$_UNREACHABLE:
2438 code = Perl_sig_to_vmscondition_int(sig);
2441 SETERRNO(EINVAL, SS$_BADPARAM);
2445 /* Fixme: Per official UNIX specification: If pid = 0, or negative then
2446 * signals are to be sent to multiple processes.
2447 * pid = 0 - all processes in group except ones that the system exempts
2448 * pid = -1 - all processes except ones that the system exempts
2449 * pid = -n - all processes in group (abs(n)) except ...
2450 * For now, just report as not supported.
2454 SETERRNO(ENOTSUP, SS$_UNSUPPORTED);
2458 iss = sys$sigprc((unsigned int *)&pid,0,code);
2459 if (iss&1) return 0;
2463 set_errno(EPERM); break;
2465 case SS$_NOSUCHNODE:
2466 case SS$_UNREACHABLE:
2467 set_errno(ESRCH); break;
2469 set_errno(ENOMEM); break;
2471 _ckvmssts_noperl(iss);
2474 set_vaxc_errno(iss);
2480 /* Routine to convert a VMS status code to a UNIX status code.
2481 ** More tricky than it appears because of conflicting conventions with
2484 ** VMS status codes are a bit mask, with the least significant bit set for
2487 ** Special UNIX status of EVMSERR indicates that no translation is currently
2488 ** available, and programs should check the VMS status code.
2490 ** Programs compiled with _POSIX_EXIT have a special encoding that requires
2494 #ifndef C_FACILITY_NO
2495 #define C_FACILITY_NO 0x350000
2498 #define DCL_IVVERB 0x38090
2501 int Perl_vms_status_to_unix(int vms_status, int child_flag)
2509 /* Assume the best or the worst */
2510 if (vms_status & STS$M_SUCCESS)
2513 unix_status = EVMSERR;
2515 msg_status = vms_status & ~STS$M_CONTROL;
2517 facility = vms_status & STS$M_FAC_NO;
2518 fac_sp = vms_status & STS$M_FAC_SP;
2519 msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY);
2521 if (((facility == 0) || (fac_sp == 0)) && (child_flag == 0)) {
2527 unix_status = EFAULT;
2529 case SS$_DEVOFFLINE:
2530 unix_status = EBUSY;
2533 unix_status = ENOTCONN;
2541 case SS$_INVFILFOROP:
2545 unix_status = EINVAL;
2547 case SS$_UNSUPPORTED:
2548 unix_status = ENOTSUP;
2553 unix_status = EACCES;
2555 case SS$_DEVICEFULL:
2556 unix_status = ENOSPC;
2559 unix_status = ENODEV;
2561 case SS$_NOSUCHFILE:
2562 case SS$_NOSUCHOBJECT:
2563 unix_status = ENOENT;
2565 case SS$_ABORT: /* Fatal case */
2566 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_ERROR): /* Error case */
2567 case ((SS$_ABORT & STS$M_COND_ID) | STS$K_WARNING): /* Warning case */
2568 unix_status = EINTR;
2571 unix_status = E2BIG;
2574 unix_status = ENOMEM;
2577 unix_status = EPERM;
2579 case SS$_NOSUCHNODE:
2580 case SS$_UNREACHABLE:
2581 unix_status = ESRCH;
2584 unix_status = ECHILD;
2587 if ((facility == 0) && (msg_no < 8)) {
2588 /* These are not real VMS status codes so assume that they are
2589 ** already UNIX status codes
2591 unix_status = msg_no;
2597 /* Translate a POSIX exit code to a UNIX exit code */
2598 if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) {
2599 unix_status = (msg_no & 0x07F8) >> 3;
2603 /* Documented traditional behavior for handling VMS child exits */
2604 /*--------------------------------------------------------------*/
2605 if (child_flag != 0) {
2607 /* Success / Informational return 0 */
2608 /*----------------------------------*/
2609 if (msg_no & STS$K_SUCCESS)
2612 /* Warning returns 1 */
2613 /*-------------------*/
2614 if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0)
2617 /* Everything else pass through the severity bits */
2618 /*------------------------------------------------*/
2619 return (msg_no & STS$M_SEVERITY);
2622 /* Normal VMS status to ERRNO mapping attempt */
2623 /*--------------------------------------------*/
2624 switch(msg_status) {
2625 /* case RMS$_EOF: */ /* End of File */
2626 case RMS$_FNF: /* File Not Found */
2627 case RMS$_DNF: /* Dir Not Found */
2628 unix_status = ENOENT;
2630 case RMS$_RNF: /* Record Not Found */
2631 unix_status = ESRCH;
2634 unix_status = ENOTDIR;
2637 unix_status = ENODEV;
2642 unix_status = EBADF;
2645 unix_status = EEXIST;
2649 case LIB$_INVSTRDES:
2651 case LIB$_NOSUCHSYM:
2652 case LIB$_INVSYMNAM:
2654 unix_status = EINVAL;
2660 unix_status = E2BIG;
2662 case RMS$_PRV: /* No privilege */
2663 case RMS$_ACC: /* ACP file access failed */
2664 case RMS$_WLK: /* Device write locked */
2665 unix_status = EACCES;
2667 case RMS$_MKD: /* Failed to mark for delete */
2668 unix_status = EPERM;
2670 /* case RMS$_NMF: */ /* No more files */
2678 /* Try to guess at what VMS error status should go with a UNIX errno
2679 * value. This is hard to do as there could be many possible VMS
2680 * error statuses that caused the errno value to be set.
2683 int Perl_unix_status_to_vms(int unix_status)
2685 int test_unix_status;
2687 /* Trivial cases first */
2688 /*---------------------*/
2689 if (unix_status == EVMSERR)
2692 /* Is vaxc$errno sane? */
2693 /*---------------------*/
2694 test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0);
2695 if (test_unix_status == unix_status)
2698 /* If way out of range, must be VMS code already */
2699 /*-----------------------------------------------*/
2700 if (unix_status > EVMSERR)
2703 /* If out of range, punt */
2704 /*-----------------------*/
2705 if (unix_status > __ERRNO_MAX)
2709 /* Ok, now we have to do it the hard way. */
2710 /*----------------------------------------*/
2711 switch(unix_status) {
2712 case 0: return SS$_NORMAL;
2713 case EPERM: return SS$_NOPRIV;
2714 case ENOENT: return SS$_NOSUCHOBJECT;
2715 case ESRCH: return SS$_UNREACHABLE;
2716 case EINTR: return SS$_ABORT;
2719 case E2BIG: return SS$_BUFFEROVF;
2721 case EBADF: return RMS$_IFI;
2722 case ECHILD: return SS$_NONEXPR;
2724 case ENOMEM: return SS$_INSFMEM;
2725 case EACCES: return SS$_FILACCERR;
2726 case EFAULT: return SS$_ACCVIO;
2728 case EBUSY: return SS$_DEVOFFLINE;
2729 case EEXIST: return RMS$_FEX;
2731 case ENODEV: return SS$_NOSUCHDEV;
2732 case ENOTDIR: return RMS$_DIR;
2734 case EINVAL: return SS$_INVARG;
2740 case ENOSPC: return SS$_DEVICEFULL;
2741 case ESPIPE: return LIB$_INVARG;
2746 case ERANGE: return LIB$_INVARG;
2747 /* case EWOULDBLOCK */
2748 /* case EINPROGRESS */
2751 /* case EDESTADDRREQ */
2753 /* case EPROTOTYPE */
2754 /* case ENOPROTOOPT */
2755 /* case EPROTONOSUPPORT */
2756 /* case ESOCKTNOSUPPORT */
2757 /* case EOPNOTSUPP */
2758 /* case EPFNOSUPPORT */
2759 /* case EAFNOSUPPORT */
2760 /* case EADDRINUSE */
2761 /* case EADDRNOTAVAIL */
2763 /* case ENETUNREACH */
2764 /* case ENETRESET */
2765 /* case ECONNABORTED */
2766 /* case ECONNRESET */
2769 case ENOTCONN: return SS$_CLEARED;
2770 /* case ESHUTDOWN */
2771 /* case ETOOMANYREFS */
2772 /* case ETIMEDOUT */
2773 /* case ECONNREFUSED */
2775 /* case ENAMETOOLONG */
2776 /* case EHOSTDOWN */
2777 /* case EHOSTUNREACH */
2778 /* case ENOTEMPTY */
2790 /* case ECANCELED */
2794 return SS$_UNSUPPORTED;
2800 /* case EABANDONED */
2802 return SS$_ABORT; /* punt */
2805 return SS$_ABORT; /* Should not get here */
2809 /* default piping mailbox size */
2810 #define PERL_BUFSIZ 512
2814 create_mbx(unsigned short int *chan, struct dsc$descriptor_s *namdsc)
2816 unsigned long int mbxbufsiz;
2817 static unsigned long int syssize = 0;
2818 unsigned long int dviitm = DVI$_DEVNAM;
2819 char csize[LNM$C_NAMLENGTH+1];
2823 unsigned long syiitm = SYI$_MAXBUF;
2825 * Get the SYSGEN parameter MAXBUF
2827 * If the logical 'PERL_MBX_SIZE' is defined
2828 * use the value of the logical instead of PERL_BUFSIZ, but
2829 * keep the size between 128 and MAXBUF.
2832 _ckvmssts_noperl(lib$getsyi(&syiitm, &syssize, 0, 0, 0, 0));
2835 if (vmstrnenv("PERL_MBX_SIZE", csize, 0, fildev, 0)) {
2836 mbxbufsiz = atoi(csize);
2838 mbxbufsiz = PERL_BUFSIZ;
2840 if (mbxbufsiz < 128) mbxbufsiz = 128;
2841 if (mbxbufsiz > syssize) mbxbufsiz = syssize;
2843 _ckvmssts_noperl(sts = sys$crembx(0,chan,mbxbufsiz,mbxbufsiz,0,0,0));
2845 sts = lib$getdvi(&dviitm, chan, NULL, NULL, namdsc, &namdsc->dsc$w_length);
2846 _ckvmssts_noperl(sts);
2847 namdsc->dsc$a_pointer[namdsc->dsc$w_length] = '\0';
2849 } /* end of create_mbx() */
2852 /*{{{ my_popen and my_pclose*/
2854 typedef struct _iosb IOSB;
2855 typedef struct _iosb* pIOSB;
2856 typedef struct _pipe Pipe;
2857 typedef struct _pipe* pPipe;
2858 typedef struct pipe_details Info;
2859 typedef struct pipe_details* pInfo;
2860 typedef struct _srqp RQE;
2861 typedef struct _srqp* pRQE;
2862 typedef struct _tochildbuf CBuf;
2863 typedef struct _tochildbuf* pCBuf;
2866 unsigned short status;
2867 unsigned short count;
2868 unsigned long dvispec;
2871 #pragma member_alignment save
2872 #pragma nomember_alignment quadword
2873 struct _srqp { /* VMS self-relative queue entry */
2874 unsigned long qptr[2];
2876 #pragma member_alignment restore
2877 static RQE RQE_ZERO = {0,0};
2879 struct _tochildbuf {
2882 unsigned short size;
2890 unsigned short chan_in;
2891 unsigned short chan_out;
2893 unsigned int bufsize;
2905 #if defined(PERL_IMPLICIT_CONTEXT)
2906 void *thx; /* Either a thread or an interpreter */
2907 /* pointer, depending on how we're built */
2915 PerlIO *fp; /* file pointer to pipe mailbox */
2916 int useFILE; /* using stdio, not perlio */
2917 int pid; /* PID of subprocess */
2918 int mode; /* == 'r' if pipe open for reading */
2919 int done; /* subprocess has completed */
2920 int waiting; /* waiting for completion/closure */
2921 int closing; /* my_pclose is closing this pipe */
2922 unsigned long completion; /* termination status of subprocess */
2923 pPipe in; /* pipe in to sub */
2924 pPipe out; /* pipe out of sub */
2925 pPipe err; /* pipe of sub's sys$error */
2926 int in_done; /* true when in pipe finished */
2929 unsigned short xchan; /* channel to debug xterm */
2930 unsigned short xchan_valid; /* channel is assigned */
2933 struct exit_control_block
2935 struct exit_control_block *flink;
2936 unsigned long int (*exit_routine)();
2937 unsigned long int arg_count;
2938 unsigned long int *status_address;
2939 unsigned long int exit_status;
2942 typedef struct _closed_pipes Xpipe;
2943 typedef struct _closed_pipes* pXpipe;
2945 struct _closed_pipes {
2946 int pid; /* PID of subprocess */
2947 unsigned long completion; /* termination status of subprocess */
2949 #define NKEEPCLOSED 50
2950 static Xpipe closed_list[NKEEPCLOSED];
2951 static int closed_index = 0;
2952 static int closed_num = 0;
2954 #define RETRY_DELAY "0 ::0.20"
2955 #define MAX_RETRY 50
2957 static int pipe_ef = 0; /* first call to safe_popen inits these*/
2958 static unsigned long mypid;
2959 static unsigned long delaytime[2];
2961 static pInfo open_pipes = NULL;
2962 static $DESCRIPTOR(nl_desc, "NL:");
2964 #define PIPE_COMPLETION_WAIT 30 /* seconds, for EOF/FORCEX wait */
2968 static unsigned long int
2972 unsigned long int retsts = SS$_NORMAL, abort = SS$_TIMEOUT;
2973 int sts, did_stuff, need_eof, j;
2976 * Flush any pending i/o, but since we are in process run-down, be
2977 * careful about referencing PerlIO structures that may already have
2978 * been deallocated. We may not even have an interpreter anymore.
2983 #if defined(PERL_IMPLICIT_CONTEXT)
2984 /* We need to use the Perl context of the thread that created */
2988 aTHX = info->err->thx;
2990 aTHX = info->out->thx;
2992 aTHX = info->in->thx;
2995 #if defined(USE_ITHREADS)
2998 && PL_perlio_fd_refcnt)
2999 PerlIO_flush(info->fp);
3001 fflush((FILE *)info->fp);
3007 next we try sending an EOF...ignore if doesn't work, make sure we
3015 _ckvmssts_noperl(sys$setast(0));
3016 if (info->in && !info->in->shut_on_empty) {
3017 _ckvmssts_noperl(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
3022 _ckvmssts_noperl(sys$setast(1));
3026 /* wait for EOF to have effect, up to ~ 30 sec [default] */
3028 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3033 _ckvmssts_noperl(sys$setast(0));
3034 if (info->waiting && info->done)
3036 nwait += info->waiting;
3037 _ckvmssts_noperl(sys$setast(1));
3047 _ckvmssts_noperl(sys$setast(0));
3048 if (!info->done) { /* Tap them gently on the shoulder . . .*/
3049 sts = sys$forcex(&info->pid,0,&abort);
3050 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3053 _ckvmssts_noperl(sys$setast(1));
3057 /* again, wait for effect */
3059 for (j = 0; did_stuff && j < PIPE_COMPLETION_WAIT; j++) {
3064 _ckvmssts_noperl(sys$setast(0));
3065 if (info->waiting && info->done)
3067 nwait += info->waiting;
3068 _ckvmssts_noperl(sys$setast(1));
3077 _ckvmssts_noperl(sys$setast(0));
3078 if (!info->done) { /* We tried to be nice . . . */
3079 sts = sys$delprc(&info->pid,0);
3080 if (!(sts&1) && sts != SS$_NONEXPR) _ckvmssts_noperl(sts);
3081 info->done = 1; /* sys$delprc is as done as we're going to get. */
3083 _ckvmssts_noperl(sys$setast(1));
3089 #if defined(PERL_IMPLICIT_CONTEXT)
3090 /* We need to use the Perl context of the thread that created */
3093 if (open_pipes->err)
3094 aTHX = open_pipes->err->thx;
3095 else if (open_pipes->out)
3096 aTHX = open_pipes->out->thx;
3097 else if (open_pipes->in)
3098 aTHX = open_pipes->in->thx;
3100 if ((sts = my_pclose(open_pipes->fp)) == -1) retsts = vaxc$errno;
3101 else if (!(sts & 1)) retsts = sts;
3106 static struct exit_control_block pipe_exitblock =
3107 {(struct exit_control_block *) 0,
3108 pipe_exit_routine, 0, &pipe_exitblock.exit_status, 0};
3110 static void pipe_mbxtofd_ast(pPipe p);
3111 static void pipe_tochild1_ast(pPipe p);
3112 static void pipe_tochild2_ast(pPipe p);
3115 popen_completion_ast(pInfo info)
3117 pInfo i = open_pipes;
3122 info->completion &= 0x0FFFFFFF; /* strip off "control" field */
3123 closed_list[closed_index].pid = info->pid;
3124 closed_list[closed_index].completion = info->completion;
3126 if (closed_index == NKEEPCLOSED)
3131 if (i == info) break;
3134 if (!i) return; /* unlinked, probably freed too */
3139 Writing to subprocess ...
3140 if my_pclose'd: EOF already sent, should shutdown chan_in part of pipe
3142 chan_out may be waiting for "done" flag, or hung waiting
3143 for i/o completion to child...cancel the i/o. This will
3144 put it into "snarf mode" (done but no EOF yet) that discards
3147 Output from subprocess (stdout, stderr) needs to be flushed and
3148 shut down. We try sending an EOF, but if the mbx is full the pipe
3149 routine should still catch the "shut_on_empty" flag, telling it to
3150 use immediate-style reads so that "mbx empty" -> EOF.
3154 if (info->in && !info->in_done) { /* only for mode=w */
3155 if (info->in->shut_on_empty && info->in->need_wake) {
3156 info->in->need_wake = FALSE;
3157 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,info->in,0));
3159 _ckvmssts_noperl(sys$cancel(info->in->chan_out));
3163 if (info->out && !info->out_done) { /* were we also piping output? */
3164 info->out->shut_on_empty = TRUE;
3165 iss = sys$qio(0,info->out->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3166 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3167 _ckvmssts_noperl(iss);
3170 if (info->err && !info->err_done) { /* we were piping stderr */
3171 info->err->shut_on_empty = TRUE;
3172 iss = sys$qio(0,info->err->chan_in,IO$_WRITEOF|IO$M_NORSWAIT, 0, 0, 0, 0, 0, 0, 0, 0, 0);
3173 if (iss == SS$_MBFULL) iss = SS$_NORMAL;
3174 _ckvmssts_noperl(iss);
3176 _ckvmssts_noperl(sys$setef(pipe_ef));
3180 static unsigned long int setup_cmddsc(pTHX_ const char *cmd, int check_img, int *suggest_quote, struct dsc$descriptor_s **pvmscmd);
3181 static void vms_execfree(struct dsc$descriptor_s *vmscmd);
3184 we actually differ from vmstrnenv since we use this to
3185 get the RMS IFI to check if SYS$OUTPUT and SYS$ERROR *really*
3186 are pointing to the same thing
3189 static unsigned short
3190 popen_translate(pTHX_ char *logical, char *result)
3193 $DESCRIPTOR(d_table,"LNM$PROCESS_TABLE");
3194 $DESCRIPTOR(d_log,"");
3196 unsigned short length;
3197 unsigned short code;
3199 unsigned short *retlenaddr;
3201 unsigned short l, ifi;
3203 d_log.dsc$a_pointer = logical;
3204 d_log.dsc$w_length = strlen(logical);
3206 itmlst[0].code = LNM$_STRING;
3207 itmlst[0].length = 255;
3208 itmlst[0].buffer_addr = result;
3209 itmlst[0].retlenaddr = &l;
3212 itmlst[1].length = 0;
3213 itmlst[1].buffer_addr = 0;
3214 itmlst[1].retlenaddr = 0;
3216 iss = sys$trnlnm(0, &d_table, &d_log, 0, itmlst);
3217 if (iss == SS$_NOLOGNAM) {
3221 if (!(iss&1)) lib$signal(iss);
3224 logicals for PPFs have a 4 byte prefix ESC+NUL+(RMS IFI)
3225 strip it off and return the ifi, if any
3228 if (result[0] == 0x1b && result[1] == 0x00) {
3229 memmove(&ifi,result+2,2);
3230 strcpy(result,result+4);
3232 return ifi; /* this is the RMS internal file id */
3235 static void pipe_infromchild_ast(pPipe p);
3238 I'm using LIB$(GET|FREE)_VM here so that we can allocate and deallocate
3239 inside an AST routine without worrying about reentrancy and which Perl
3240 memory allocator is being used.
3242 We read data and queue up the buffers, then spit them out one at a
3243 time to the output mailbox when the output mailbox is ready for one.
3246 #define INITIAL_TOCHILDQUEUE 2
3249 pipe_tochild_setup(pTHX_ char *rmbx, char *wmbx)
3253 char mbx1[64], mbx2[64];
3254 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3255 DSC$K_CLASS_S, mbx1},
3256 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3257 DSC$K_CLASS_S, mbx2};
3258 unsigned int dviitm = DVI$_DEVBUFSIZ;
3262 _ckvmssts_noperl(lib$get_vm(&n, &p));
3264 create_mbx(&p->chan_in , &d_mbx1);
3265 create_mbx(&p->chan_out, &d_mbx2);
3266 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3269 p->shut_on_empty = FALSE;
3270 p->need_wake = FALSE;
3273 p->iosb.status = SS$_NORMAL;
3274 p->iosb2.status = SS$_NORMAL;
3280 #ifdef PERL_IMPLICIT_CONTEXT
3284 n = sizeof(CBuf) + p->bufsize;
3286 for (j = 0; j < INITIAL_TOCHILDQUEUE; j++) {
3287 _ckvmssts_noperl(lib$get_vm(&n, &b));
3288 b->buf = (char *) b + sizeof(CBuf);
3289 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3292 pipe_tochild2_ast(p);
3293 pipe_tochild1_ast(p);
3299 /* reads the MBX Perl is writing, and queues */
3302 pipe_tochild1_ast(pPipe p)
3305 int iss = p->iosb.status;
3306 int eof = (iss == SS$_ENDOFFILE);
3308 #ifdef PERL_IMPLICIT_CONTEXT
3314 p->shut_on_empty = TRUE;
3316 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3318 _ckvmssts_noperl(iss);
3322 b->size = p->iosb.count;
3323 _ckvmssts_noperl(sts = lib$insqhi(b, &p->wait));
3325 p->need_wake = FALSE;
3326 _ckvmssts_noperl(sys$dclast(pipe_tochild2_ast,p,0));
3329 p->retry = 1; /* initial call */
3332 if (eof) { /* flush the free queue, return when done */
3333 int n = sizeof(CBuf) + p->bufsize;
3335 iss = lib$remqti(&p->free, &b);
3336 if (iss == LIB$_QUEWASEMP) return;
3337 _ckvmssts_noperl(iss);
3338 _ckvmssts_noperl(lib$free_vm(&n, &b));
3342 iss = lib$remqti(&p->free, &b);
3343 if (iss == LIB$_QUEWASEMP) {
3344 int n = sizeof(CBuf) + p->bufsize;
3345 _ckvmssts_noperl(lib$get_vm(&n, &b));
3346 b->buf = (char *) b + sizeof(CBuf);
3348 _ckvmssts_noperl(iss);
3352 iss = sys$qio(0,p->chan_in,
3353 IO$_READVBLK|(p->shut_on_empty ? IO$M_NOWAIT : 0),
3355 pipe_tochild1_ast, p, b->buf, p->bufsize, 0, 0, 0, 0);
3356 if (iss == SS$_ENDOFFILE && p->shut_on_empty) iss = SS$_NORMAL;
3357 _ckvmssts_noperl(iss);
3361 /* writes queued buffers to output, waits for each to complete before
3365 pipe_tochild2_ast(pPipe p)
3368 int iss = p->iosb2.status;
3369 int n = sizeof(CBuf) + p->bufsize;
3370 int done = (p->info && p->info->done) ||
3371 iss == SS$_CANCEL || iss == SS$_ABORT;
3372 #if defined(PERL_IMPLICIT_CONTEXT)
3377 if (p->type) { /* type=1 has old buffer, dispose */
3378 if (p->shut_on_empty) {
3379 _ckvmssts_noperl(lib$free_vm(&n, &b));
3381 _ckvmssts_noperl(lib$insqhi(b, &p->free));
3386 iss = lib$remqti(&p->wait, &b);
3387 if (iss == LIB$_QUEWASEMP) {
3388 if (p->shut_on_empty) {
3390 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3391 *p->pipe_done = TRUE;
3392 _ckvmssts_noperl(sys$setef(pipe_ef));
3394 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3395 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3399 p->need_wake = TRUE;
3402 _ckvmssts_noperl(iss);
3409 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF,
3410 &p->iosb2, pipe_tochild2_ast, p, 0, 0, 0, 0, 0, 0));
3412 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,
3413 &p->iosb2, pipe_tochild2_ast, p, b->buf, b->size, 0, 0, 0, 0));
3422 pipe_infromchild_setup(pTHX_ char *rmbx, char *wmbx)
3425 char mbx1[64], mbx2[64];
3426 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
3427 DSC$K_CLASS_S, mbx1},
3428 d_mbx2 = {sizeof mbx2, DSC$K_DTYPE_T,
3429 DSC$K_CLASS_S, mbx2};
3430 unsigned int dviitm = DVI$_DEVBUFSIZ;
3432 int n = sizeof(Pipe);
3433 _ckvmssts_noperl(lib$get_vm(&n, &p));
3434 create_mbx(&p->chan_in , &d_mbx1);
3435 create_mbx(&p->chan_out, &d_mbx2);
3437 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3438 n = p->bufsize * sizeof(char);
3439 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3440 p->shut_on_empty = FALSE;
3443 p->iosb.status = SS$_NORMAL;
3444 #if defined(PERL_IMPLICIT_CONTEXT)
3447 pipe_infromchild_ast(p);
3455 pipe_infromchild_ast(pPipe p)
3457 int iss = p->iosb.status;
3458 int eof = (iss == SS$_ENDOFFILE);
3459 int myeof = (eof && (p->iosb.dvispec == mypid || p->iosb.dvispec == 0));
3460 int kideof = (eof && (p->iosb.dvispec == p->info->pid));
3461 #if defined(PERL_IMPLICIT_CONTEXT)
3465 if (p->info && p->info->closing && p->chan_out) { /* output shutdown */
3466 _ckvmssts_noperl(sys$dassgn(p->chan_out));
3471 input shutdown if EOF from self (done or shut_on_empty)
3472 output shutdown if closing flag set (my_pclose)
3473 send data/eof from child or eof from self
3474 otherwise, re-read (snarf of data from child)
3479 if (myeof && p->chan_in) { /* input shutdown */
3480 _ckvmssts_noperl(sys$dassgn(p->chan_in));
3485 if (myeof || kideof) { /* pass EOF to parent */
3486 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEOF, &p->iosb,
3487 pipe_infromchild_ast, p,
3490 } else if (eof) { /* eat EOF --- fall through to read*/
3492 } else { /* transmit data */
3493 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_WRITEVBLK,&p->iosb,
3494 pipe_infromchild_ast,p,
3495 p->buf, p->iosb.count, 0, 0, 0, 0));
3501 /* everything shut? flag as done */
3503 if (!p->chan_in && !p->chan_out) {
3504 *p->pipe_done = TRUE;
3505 _ckvmssts_noperl(sys$setef(pipe_ef));
3509 /* write completed (or read, if snarfing from child)
3510 if still have input active,
3511 queue read...immediate mode if shut_on_empty so we get EOF if empty
3513 check if Perl reading, generate EOFs as needed
3519 iss = sys$qio(0,p->chan_in,IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0),&p->iosb,
3520 pipe_infromchild_ast,p,
3521 p->buf, p->bufsize, 0, 0, 0, 0);
3522 if (p->shut_on_empty && iss == SS$_ENDOFFILE) iss = SS$_NORMAL;
3523 _ckvmssts_noperl(iss);
3524 } else { /* send EOFs for extra reads */
3525 p->iosb.status = SS$_ENDOFFILE;
3526 p->iosb.dvispec = 0;
3527 _ckvmssts_noperl(sys$qio(0,p->chan_out,IO$_SETMODE|IO$M_READATTN,
3529 pipe_infromchild_ast, p, 0, 0, 0, 0));
3535 pipe_mbxtofd_setup(pTHX_ int fd, char *out)
3539 unsigned long dviitm = DVI$_DEVBUFSIZ;
3541 struct dsc$descriptor_s d_mbx = {sizeof mbx, DSC$K_DTYPE_T,
3542 DSC$K_CLASS_S, mbx};
3543 int n = sizeof(Pipe);
3545 /* things like terminals and mbx's don't need this filter */
3546 if (fd && fstat(fd,&s) == 0) {
3547 unsigned long dviitm = DVI$_DEVCHAR, devchar;
3549 unsigned short dev_len;
3550 struct dsc$descriptor_s d_dev;
3552 struct item_list_3 items[3];
3554 unsigned short dvi_iosb[4];
3556 cptr = getname(fd, out, 1);
3557 if (cptr == NULL) _ckvmssts_noperl(SS$_NOSUCHDEV);
3558 d_dev.dsc$a_pointer = out;
3559 d_dev.dsc$w_length = strlen(out);
3560 d_dev.dsc$b_dtype = DSC$K_DTYPE_T;
3561 d_dev.dsc$b_class = DSC$K_CLASS_S;
3564 items[0].code = DVI$_DEVCHAR;
3565 items[0].bufadr = &devchar;
3566 items[0].retadr = NULL;
3568 items[1].code = DVI$_FULLDEVNAM;
3569 items[1].bufadr = device;
3570 items[1].retadr = &dev_len;
3574 status = sys$getdviw
3575 (NO_EFN, 0, &d_dev, items, dvi_iosb, NULL, NULL, NULL);
3576 _ckvmssts_noperl(status);
3577 if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
3578 device[dev_len] = 0;
3580 if (!(devchar & DEV$M_DIR)) {
3581 strcpy(out, device);
3587 _ckvmssts_noperl(lib$get_vm(&n, &p));
3588 p->fd_out = dup(fd);
3589 create_mbx(&p->chan_in, &d_mbx);
3590 _ckvmssts_noperl(lib$getdvi(&dviitm, &p->chan_in, 0, &p->bufsize));
3591 n = (p->bufsize+1) * sizeof(char);
3592 _ckvmssts_noperl(lib$get_vm(&n, &p->buf));
3593 p->shut_on_empty = FALSE;
3598 _ckvmssts_noperl(sys$qio(0, p->chan_in, IO$_READVBLK, &p->iosb,
3599 pipe_mbxtofd_ast, p,
3600 p->buf, p->bufsize, 0, 0, 0, 0));
3606 pipe_mbxtofd_ast(pPipe p)
3608 int iss = p->iosb.status;
3609 int done = p->info->done;
3611 int eof = (iss == SS$_ENDOFFILE);
3612 int myeof = eof && ((p->iosb.dvispec == mypid)||(p->iosb.dvispec == 0));
3613 int err = !(iss&1) && !eof;
3614 #if defined(PERL_IMPLICIT_CONTEXT)
3618 if (done && myeof) { /* end piping */
3620 sys$dassgn(p->chan_in);
3621 *p->pipe_done = TRUE;
3622 _ckvmssts_noperl(sys$setef(pipe_ef));
3626 if (!err && !eof) { /* good data to send to file */
3627 p->buf[p->iosb.count] = '\n';
3628 iss2 = write(p->fd_out, p->buf, p->iosb.count+1);
3631 if (p->retry < MAX_RETRY) {
3632 _ckvmssts_noperl(sys$setimr(0,delaytime,pipe_mbxtofd_ast,p));
3638 _ckvmssts_noperl(iss);
3642 iss = sys$qio(0, p->chan_in, IO$_READVBLK|(p->shut_on_empty ? IO$M_NOW : 0), &p->iosb,
3643 pipe_mbxtofd_ast, p,
3644 p->buf, p->bufsize, 0, 0, 0, 0);
3645 if (p->shut_on_empty && (iss == SS$_ENDOFFILE)) iss = SS$_NORMAL;
3646 _ckvmssts_noperl(iss);
3650 typedef struct _pipeloc PLOC;
3651 typedef struct _pipeloc* pPLOC;
3655 char dir[NAM$C_MAXRSS+1];
3657 static pPLOC head_PLOC = 0;
3660 free_pipelocs(pTHX_ void *head)
3663 pPLOC *pHead = (pPLOC *)head;
3675 store_pipelocs(pTHX)
3684 char temp[NAM$C_MAXRSS+1];
3688 free_pipelocs(aTHX_ &head_PLOC);
3690 /* the . directory from @INC comes last */
3692 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3693 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3694 p->next = head_PLOC;
3696 strcpy(p->dir,"./");
3698 /* get the directory from $^X */
3700 unixdir = PerlMem_malloc(VMS_MAXRSS);
3701 if (unixdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3703 #ifdef PERL_IMPLICIT_CONTEXT
3704 if (aTHX && PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3706 if (PL_origargv && PL_origargv[0]) { /* maybe nul if embedded Perl */
3708 strcpy(temp, PL_origargv[0]);
3709 x = strrchr(temp,']');
3711 x = strrchr(temp,'>');
3713 /* It could be a UNIX path */
3714 x = strrchr(temp,'/');
3720 /* Got a bare name, so use default directory */
3725 if ((tounixpath_utf8(temp, unixdir, NULL)) != NULL) {
3726 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3727 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3728 p->next = head_PLOC;
3730 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3731 p->dir[NAM$C_MAXRSS] = '\0';
3735 /* reverse order of @INC entries, skip "." since entered above */
3737 #ifdef PERL_IMPLICIT_CONTEXT
3740 if (PL_incgv) av = GvAVn(PL_incgv);
3742 for (i = 0; av && i <= AvFILL(av); i++) {
3743 dirsv = *av_fetch(av,i,TRUE);
3745 if (SvROK(dirsv)) continue;
3746 dir = SvPVx(dirsv,n_a);
3747 if (strcmp(dir,".") == 0) continue;
3748 if ((tounixpath_utf8(dir, unixdir, NULL)) == NULL)
3751 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3752 p->next = head_PLOC;
3754 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3755 p->dir[NAM$C_MAXRSS] = '\0';
3758 /* most likely spot (ARCHLIB) put first in the list */
3761 if ((tounixpath_utf8(ARCHLIB_EXP, unixdir, NULL)) != NULL) {
3762 p = (pPLOC) PerlMem_malloc(sizeof(PLOC));
3763 if (p == NULL) _ckvmssts_noperl(SS$_INSFMEM);
3764 p->next = head_PLOC;
3766 strncpy(p->dir,unixdir,sizeof(p->dir)-1);
3767 p->dir[NAM$C_MAXRSS] = '\0';
3770 PerlMem_free(unixdir);
3774 Perl_cando_by_name_int
3775 (pTHX_ I32 bit, bool effective, const char *fname, int opts);
3776 #if !defined(PERL_IMPLICIT_CONTEXT)
3777 #define cando_by_name_int Perl_cando_by_name_int
3779 #define cando_by_name_int(a,b,c,d) Perl_cando_by_name_int(aTHX_ a,b,c,d)
3785 static int vmspipe_file_status = 0;
3786 static char vmspipe_file[NAM$C_MAXRSS+1];
3788 /* already found? Check and use ... need read+execute permission */
3790 if (vmspipe_file_status == 1) {
3791 if (cando_by_name_int(S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3792 && cando_by_name_int
3793 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3794 return vmspipe_file;
3796 vmspipe_file_status = 0;
3799 /* scan through stored @INC, $^X */
3801 if (vmspipe_file_status == 0) {
3802 char file[NAM$C_MAXRSS+1];
3803 pPLOC p = head_PLOC;
3808 strcpy(file, p->dir);
3809 dirlen = strlen(file);
3810 strncat(file, "vmspipe.com",NAM$C_MAXRSS - dirlen);
3811 file[NAM$C_MAXRSS] = '\0';
3814 exp_res = int_rmsexpand_tovms(file, vmspipe_file, 0);
3815 if (!exp_res) continue;
3817 if (cando_by_name_int
3818 (S_IRUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)
3819 && cando_by_name_int
3820 (S_IXUSR, 0, vmspipe_file, PERL_RMSEXPAND_M_VMS_IN)) {
3821 vmspipe_file_status = 1;
3822 return vmspipe_file;
3825 vmspipe_file_status = -1; /* failed, use tempfiles */
3832 vmspipe_tempfile(pTHX)
3834 char file[NAM$C_MAXRSS+1];
3836 static int index = 0;
3840 /* create a tempfile */
3842 /* we can't go from W, shr=get to R, shr=get without
3843 an intermediate vulnerable state, so don't bother trying...
3845 and lib$spawn doesn't shr=put, so have to close the write
3847 So... match up the creation date/time and the FID to
3848 make sure we're dealing with the same file
3853 if (!decc_filename_unix_only) {
3854 sprintf(file,"sys$scratch:perlpipe_%08.8x_%d.com",mypid,index);
3855 fp = fopen(file,"w");
3857 sprintf(file,"sys$login:perlpipe_%08.8x_%d.com",mypid,index);
3858 fp = fopen(file,"w");
3860 sprintf(file,"sys$disk:[]perlpipe_%08.8x_%d.com",mypid,index);
3861 fp = fopen(file,"w");
3866 sprintf(file,"/tmp/perlpipe_%08.8x_%d.com",mypid,index);
3867 fp = fopen(file,"w");
3869 sprintf(file,"/sys$login/perlpipe_%08.8x_%d.com",mypid,index);
3870 fp = fopen(file,"w");
3872 sprintf(file,"./perlpipe_%08.8x_%d.com",mypid,index);
3873 fp = fopen(file,"w");
3877 if (!fp) return 0; /* we're hosed */
3879 fprintf(fp,"$! 'f$verify(0)'\n");
3880 fprintf(fp,"$! --- protect against nonstandard definitions ---\n");
3881 fprintf(fp,"$ perl_cfile = f$environment(\"procedure\")\n");
3882 fprintf(fp,"$ perl_define = \"define/nolog\"\n");
3883 fprintf(fp,"$ perl_on = \"set noon\"\n");
3884 fprintf(fp,"$ perl_exit = \"exit\"\n");
3885 fprintf(fp,"$ perl_del = \"delete\"\n");
3886 fprintf(fp,"$ pif = \"if\"\n");
3887 fprintf(fp,"$! --- define i/o redirection (sys$output set by lib$spawn)\n");
3888 fprintf(fp,"$ pif perl_popen_in .nes. \"\" then perl_define/user/name_attributes=confine sys$input 'perl_popen_in'\n");
3889 fprintf(fp,"$ pif perl_popen_err .nes. \"\" then perl_define/user/name_attributes=confine sys$error 'perl_popen_err'\n");
3890 fprintf(fp,"$ pif perl_popen_out .nes. \"\" then perl_define sys$output 'perl_popen_out'\n");
3891 fprintf(fp,"$! --- build command line to get max possible length\n");
3892 fprintf(fp,"$c=perl_popen_cmd0\n");
3893 fprintf(fp,"$c=c+perl_popen_cmd1\n");
3894 fprintf(fp,"$c=c+perl_popen_cmd2\n");
3895 fprintf(fp,"$x=perl_popen_cmd3\n");
3896 fprintf(fp,"$c=c+x\n");
3897 fprintf(fp,"$ perl_on\n");
3898 fprintf(fp,"$ 'c'\n");
3899 fprintf(fp,"$ perl_status = $STATUS\n");
3900 fprintf(fp,"$ perl_del 'perl_cfile'\n");
3901 fprintf(fp,"$ perl_exit 'perl_status'\n");
3904 fgetname(fp, file, 1);
3905 fstat(fileno(fp), &s0.crtl_stat);
3908 if (decc_filename_unix_only)
3909 int_tounixspec(file, file, NULL);
3910 fp = fopen(file,"r","shr=get");
3912 fstat(fileno(fp), &s1.crtl_stat);
3914 cmp_result = VMS_INO_T_COMPARE(s0.crtl_stat.st_ino, s1.crtl_stat.st_ino);
3915 if ((cmp_result != 0) && (s0.st_ctime != s1.st_ctime)) {
3924 static int vms_is_syscommand_xterm(void)
3926 const static struct dsc$descriptor_s syscommand_dsc =
3927 { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
3929 const static struct dsc$descriptor_s decwdisplay_dsc =
3930 { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
3932 struct item_list_3 items[2];
3933 unsigned short dvi_iosb[4];
3934 unsigned long devchar;
3935 unsigned long devclass;
3938 /* Very simple check to guess if sys$command is a decterm? */
3939 /* First see if the DECW$DISPLAY: device exists */
3941 items[0].code = DVI$_DEVCHAR;
3942 items[0].bufadr = &devchar;
3943 items[0].retadr = NULL;
3947 status = sys$getdviw
3948 (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
3950 if ($VMS_STATUS_SUCCESS(status)) {
3951 status = dvi_iosb[0];
3954 if (!$VMS_STATUS_SUCCESS(status)) {
3955 SETERRNO(EVMSERR, status);
3959 /* If it does, then for now assume that we are on a workstation */
3960 /* Now verify that SYS$COMMAND is a terminal */
3961 /* for creating the debugger DECTerm */
3964 items[0].code = DVI$_DEVCLASS;
3965 items[0].bufadr = &devclass;
3966 items[0].retadr = NULL;
3970 status = sys$getdviw
3971 (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
3973 if ($VMS_STATUS_SUCCESS(status)) {
3974 status = dvi_iosb[0];
3977 if (!$VMS_STATUS_SUCCESS(status)) {
3978 SETERRNO(EVMSERR, status);
3982 if (devclass == DC$_TERM) {
3989 /* If we are on a DECTerm, we can pretend to fork xterms when requested */
3990 static PerlIO * create_forked_xterm(pTHX_ const char *cmd, const char *mode)
3995 char device_name[65];
3996 unsigned short device_name_len;
3997 struct dsc$descriptor_s customization_dsc;
3998 struct dsc$descriptor_s device_name_dsc;
4001 char customization[200];
4005 unsigned short p_chan;
4007 unsigned short iosb[4];
4008 struct item_list_3 items[2];
4009 const char * cust_str =
4010 "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
4011 struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
4012 DSC$K_CLASS_S, mbx1};
4014 /* LIB$FIND_IMAGE_SIGNAL needs a handler */
4015 /*---------------------------------------*/
4016 VAXC$ESTABLISH((__vms_handler)lib$sig_to_ret);
4019 /* Make sure that this is from the Perl debugger */
4020 ret_char = strstr(cmd," xterm ");
4021 if (ret_char == NULL)
4023 cptr = ret_char + 7;
4024 ret_char = strstr(cmd,"tty");
4025 if (ret_char == NULL)
4027 ret_char = strstr(cmd,"sleep");
4028 if (ret_char == NULL)
4031 if (decw_term_port == 0) {
4032 $DESCRIPTOR(filename1_dsc, "DECW$TERMINALSHR12");
4033 $DESCRIPTOR(filename2_dsc, "DECW$TERMINALSHR");
4034 $DESCRIPTOR(decw_term_port_dsc, "DECW$TERM_PORT");
4036 status = lib$find_image_symbol
4038 &decw_term_port_dsc,
4039 (void *)&decw_term_port,
4043 /* Try again with the other image name */
4044 if (!$VMS_STATUS_SUCCESS(status)) {
4046 status = lib$find_image_symbol
4048 &decw_term_port_dsc,
4049 (void *)&decw_term_port,
4058 /* No decw$term_port, give it up */
4059 if (!$VMS_STATUS_SUCCESS(status))
4062 /* Are we on a workstation? */
4063 /* to do: capture the rows / columns and pass their properties */
4064 ret_stat = vms_is_syscommand_xterm();
4068 /* Make the title: */
4069 ret_char = strstr(cptr,"-title");
4070 if (ret_char != NULL) {
4071 while ((*cptr != 0) && (*cptr != '\"')) {
4077 while ((*cptr != 0) && (*cptr != '\"')) {
4090 strcpy(title,"Perl Debug DECTerm");
4092 sprintf(customization, cust_str, title);
4094 customization_dsc.dsc$a_pointer = customization;
4095 customization_dsc.dsc$w_length = strlen(customization);
4096 customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4097 customization_dsc.dsc$b_class = DSC$K_CLASS_S;
4099 device_name_dsc.dsc$a_pointer = device_name;
4100 device_name_dsc.dsc$w_length = sizeof device_name -1;
4101 device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
4102 device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
4104 device_name_len = 0;
4106 /* Try to create the window */
4107 status = (*decw_term_port)
4116 if (!$VMS_STATUS_SUCCESS(status)) {
4117 SETERRNO(EVMSERR, status);
4121 device_name[device_name_len] = '\0';
4123 /* Need to set this up to look like a pipe for cleanup */
4125 status = lib$get_vm(&n, &info);
4126 if (!$VMS_STATUS_SUCCESS(status)) {
4127 SETERRNO(ENOMEM, status);
4133 info->completion = 0;
4134 info->closing = FALSE;
4141 info->in_done = TRUE;
4142 info->out_done = TRUE;
4143 info->err_done = TRUE;
4145 /* Assign a channel on this so that it will persist, and not login */
4146 /* We stash this channel in the info structure for reference. */
4147 /* The created xterm self destructs when the last channel is removed */
4148 /* and it appears that perl5db.pl (perl debugger) does this routinely */
4149 /* So leave this assigned. */
4150 device_name_dsc.dsc$w_length = device_name_len;
4151 status = sys$assign(&device_name_dsc,&info->xchan,0,0);
4152 if (!$VMS_STATUS_SUCCESS(status)) {
4153 SETERRNO(EVMSERR, status);
4156 info->xchan_valid = 1;
4158 /* Now create a mailbox to be read by the application */
4160 create_mbx(&p_chan, &d_mbx1);
4162 /* write the name of the created terminal to the mailbox */
4163 status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
4164 iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
4166 if (!$VMS_STATUS_SUCCESS(status)) {
4167 SETERRNO(EVMSERR, status);
4171 info->fp = PerlIO_open(mbx1, mode);
4173 /* Done with this channel */
4176 /* If any errors, then clean up */
4179 _ckvmssts_noperl(lib$free_vm(&n, &info));
4187 static I32 my_pclose_pinfo(pTHX_ pInfo info);
4190 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
4192 static int handler_set_up = FALSE;
4194 unsigned long int sts, flags = CLI$M_NOWAIT;
4195 /* The use of a GLOBAL table (as was done previously) rendered
4196 * Perl's qx() or `` unusable from a C<$ SET SYMBOL/SCOPE=NOGLOBAL> DCL
4197 * environment. Hence we've switched to LOCAL symbol table.
4199 unsigned int table = LIB$K_CLI_LOCAL_SYM;
4201 char *p, mode[10], symbol[MAX_DCL_SYMBOL+1], *vmspipe;
4202 char *in, *out, *err, mbx[512];
4204 char tfilebuf[NAM$C_MAXRSS+1];
4206 char cmd_sym_name[20];
4207 struct dsc$descriptor_s d_symbol= {0, DSC$K_DTYPE_T,
4208 DSC$K_CLASS_S, symbol};
4209 struct dsc$descriptor_s vmspipedsc = {0, DSC$K_DTYPE_T,
4211 struct dsc$descriptor_s d_sym_cmd = {0, DSC$K_DTYPE_T,
4212 DSC$K_CLASS_S, cmd_sym_name};
4213 struct dsc$descriptor_s *vmscmd;
4214 $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
4215 $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
4216 $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
4218 /* Check here for Xterm create request. This means looking for
4219 * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
4220 * is possible to create an xterm.
4222 if (*in_mode == 'r') {
4225 xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
4226 if (xterm_fd != NULL)
4230 if (!head_PLOC) store_pipelocs(aTHX); /* at least TRY to use a static vmspipe file */
4232 /* once-per-program initialization...
4233 note that the SETAST calls and the dual test of pipe_ef
4234 makes sure that only the FIRST thread through here does
4235 the initialization...all other threads wait until it's
4238 Yeah, uglier than a pthread call, it's got all the stuff inline
4239 rather than in a separate routine.
4243 _ckvmssts_noperl(sys$setast(0));
4245 unsigned long int pidcode = JPI$_PID;
4246 $DESCRIPTOR(d_delay, RETRY_DELAY);
4247 _ckvmssts_noperl(lib$get_ef(&pipe_ef));
4248 _ckvmssts_noperl(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4249 _ckvmssts_noperl(sys$bintim(&d_delay, delaytime));
4251 if (!handler_set_up) {
4252 _ckvmssts_noperl(sys$dclexh(&pipe_exitblock));
4253 handler_set_up = TRUE;
4255 _ckvmssts_noperl(sys$setast(1));
4258 /* see if we can find a VMSPIPE.COM */
4261 vmspipe = find_vmspipe(aTHX);
4263 strcpy(tfilebuf+1,vmspipe);
4264 } else { /* uh, oh...we're in tempfile hell */
4265 tpipe = vmspipe_tempfile(aTHX);
4266 if (!tpipe) { /* a fish popular in Boston */
4267 if (ckWARN(WARN_PIPE)) {
4268 Perl_warner(aTHX_ packWARN(WARN_PIPE),"unable to find VMSPIPE.COM for i/o piping");
4272 fgetname(tpipe,tfilebuf+1,1);
4274 vmspipedsc.dsc$a_pointer = tfilebuf;
4275 vmspipedsc.dsc$w_length = strlen(tfilebuf);
4277 sts = setup_cmddsc(aTHX_ cmd,0,0,&vmscmd);
4280 case RMS$_FNF: case RMS$_DNF:
4281 set_errno(ENOENT); break;
4283 set_errno(ENOTDIR); break;
4285 set_errno(ENODEV); break;
4287 set_errno(EACCES); break;
4289 set_errno(EINVAL); break;
4290 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
4291 set_errno(E2BIG); break;
4292 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
4293 _ckvmssts_noperl(sts); /* fall through */
4294 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
4297 set_vaxc_errno(sts);
4298 if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
4299 Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", strlen(cmd), cmd, Strerror(errno));
4305 _ckvmssts_noperl(lib$get_vm(&n, &info));
4307 strcpy(mode,in_mode);
4310 info->completion = 0;
4311 info->closing = FALSE;
4318 info->in_done = TRUE;
4319 info->out_done = TRUE;
4320 info->err_done = TRUE;
4322 info->xchan_valid = 0;
4324 in = PerlMem_malloc(VMS_MAXRSS);
4325 if (in == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4326 out = PerlMem_malloc(VMS_MAXRSS);
4327 if (out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4328 err = PerlMem_malloc(VMS_MAXRSS);
4329 if (err == NULL) _ckvmssts_noperl(SS$_INSFMEM);
4331 in[0] = out[0] = err[0] = '\0';
4333 if ((p = strchr(mode,'F')) != NULL) { /* F -> use FILE* */
4337 if ((p = strchr(mode,'W')) != NULL) { /* W -> wait for completion */
4342 if (*mode == 'r') { /* piping from subroutine */
4344 info->out = pipe_infromchild_setup(aTHX_ mbx,out);
4346 info->out->pipe_done = &info->out_done;
4347 info->out_done = FALSE;
4348 info->out->info = info;
4350 if (!info->useFILE) {
4351 info->fp = PerlIO_open(mbx, mode);
4353 info->fp = (PerlIO *) freopen(mbx, mode, stdin);
4354 Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
4357 if (!info->fp && info->out) {
4358 sys$cancel(info->out->chan_out);
4360 while (!info->out_done) {
4362 _ckvmssts_noperl(sys$setast(0));
4363 done = info->out_done;
4364 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4365 _ckvmssts_noperl(sys$setast(1));
4366 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4369 if (info->out->buf) {
4370 n = info->out->bufsize * sizeof(char);
4371 _ckvmssts_noperl(lib$free_vm(&n, &info->out->buf));
4374 _ckvmssts_noperl(lib$free_vm(&n, &info->out));
4376 _ckvmssts_noperl(lib$free_vm(&n, &info));
4381 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4383 info->err->pipe_done = &info->err_done;
4384 info->err_done = FALSE;
4385 info->err->info = info;
4388 } else if (*mode == 'w') { /* piping to subroutine */
4390 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4392 info->out->pipe_done = &info->out_done;
4393 info->out_done = FALSE;
4394 info->out->info = info;
4397 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4399 info->err->pipe_done = &info->err_done;
4400 info->err_done = FALSE;
4401 info->err->info = info;
4404 info->in = pipe_tochild_setup(aTHX_ in,mbx);
4405 if (!info->useFILE) {
4406 info->fp = PerlIO_open(mbx, mode);
4408 info->fp = (PerlIO *) freopen(mbx, mode, stdout);
4409 Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",mbx);
4413 info->in->pipe_done = &info->in_done;
4414 info->in_done = FALSE;
4415 info->in->info = info;
4419 if (!info->fp && info->in) {
4421 _ckvmssts_noperl(sys$qiow(0,info->in->chan_in, IO$_WRITEOF, 0,
4422 0, 0, 0, 0, 0, 0, 0, 0));
4424 while (!info->in_done) {
4426 _ckvmssts_noperl(sys$setast(0));
4427 done = info->in_done;
4428 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4429 _ckvmssts_noperl(sys$setast(1));
4430 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4433 if (info->in->buf) {
4434 n = info->in->bufsize * sizeof(char);
4435 _ckvmssts_noperl(lib$free_vm(&n, &info->in->buf));
4438 _ckvmssts_noperl(lib$free_vm(&n, &info->in));
4440 _ckvmssts_noperl(lib$free_vm(&n, &info));
4446 } else if (*mode == 'n') { /* separate subprocess, no Perl i/o */
4447 info->out = pipe_mbxtofd_setup(aTHX_ fileno(stdout), out);
4449 info->out->pipe_done = &info->out_done;
4450 info->out_done = FALSE;
4451 info->out->info = info;
4454 info->err = pipe_mbxtofd_setup(aTHX_ fileno(stderr), err);
4456 info->err->pipe_done = &info->err_done;
4457 info->err_done = FALSE;
4458 info->err->info = info;
4462 symbol[MAX_DCL_SYMBOL] = '\0';
4464 strncpy(symbol, in, MAX_DCL_SYMBOL);
4465 d_symbol.dsc$w_length = strlen(symbol);
4466 _ckvmssts_noperl(lib$set_symbol(&d_sym_in, &d_symbol, &table));
4468 strncpy(symbol, err, MAX_DCL_SYMBOL);
4469 d_symbol.dsc$w_length = strlen(symbol);
4470 _ckvmssts_noperl(lib$set_symbol(&d_sym_err, &d_symbol, &table));
4472 strncpy(symbol, out, MAX_DCL_SYMBOL);
4473 d_symbol.dsc$w_length = strlen(symbol);
4474 _ckvmssts_noperl(lib$set_symbol(&d_sym_out, &d_symbol, &table));
4476 /* Done with the names for the pipes */
4481 p = vmscmd->dsc$a_pointer;
4482 while (*p == ' ' || *p == '\t') p++; /* remove leading whitespace */
4483 if (*p == '$') p++; /* remove leading $ */
4484 while (*p == ' ' || *p == '\t') p++;
4486 for (j = 0; j < 4; j++) {
4487 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4488 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4490 strncpy(symbol, p, MAX_DCL_SYMBOL);
4491 d_symbol.dsc$w_length = strlen(symbol);
4492 _ckvmssts_noperl(lib$set_symbol(&d_sym_cmd, &d_symbol, &table));
4494 if (strlen(p) > MAX_DCL_SYMBOL) {
4495 p += MAX_DCL_SYMBOL;
4500 _ckvmssts_noperl(sys$setast(0));
4501 info->next=open_pipes; /* prepend to list */
4503 _ckvmssts_noperl(sys$setast(1));
4504 /* Omit arg 2 (input file) so the child will get the parent's SYS$INPUT
4505 * and SYS$COMMAND. vmspipe.com will redefine SYS$INPUT, but we'll still
4506 * have SYS$COMMAND if we need it.
4508 _ckvmssts_noperl(lib$spawn(&vmspipedsc, 0, &nl_desc, &flags,
4509 0, &info->pid, &info->completion,
4510 0, popen_completion_ast,info,0,0,0));
4512 /* if we were using a tempfile, close it now */
4514 if (tpipe) fclose(tpipe);
4516 /* once the subprocess is spawned, it has copied the symbols and
4517 we can get rid of ours */
4519 for (j = 0; j < 4; j++) {
4520 sprintf(cmd_sym_name,"PERL_POPEN_CMD%d",j);
4521 d_sym_cmd.dsc$w_length = strlen(cmd_sym_name);
4522 _ckvmssts_noperl(lib$delete_symbol(&d_sym_cmd, &table));
4524 _ckvmssts_noperl(lib$delete_symbol(&d_sym_in, &table));
4525 _ckvmssts_noperl(lib$delete_symbol(&d_sym_err, &table));
4526 _ckvmssts_noperl(lib$delete_symbol(&d_sym_out, &table));
4527 vms_execfree(vmscmd);
4529 #ifdef PERL_IMPLICIT_CONTEXT
4532 PL_forkprocess = info->pid;
4539 _ckvmssts_noperl(sys$setast(0));
4541 if (!done) _ckvmssts_noperl(sys$clref(pipe_ef));
4542 _ckvmssts_noperl(sys$setast(1));
4543 if (!done) _ckvmssts_noperl(sys$waitfr(pipe_ef));
4545 *psts = info->completion;
4546 /* Caller thinks it is open and tries to close it. */
4547 /* This causes some problems, as it changes the error status */
4548 /* my_pclose(info->fp); */
4550 /* If we did not have a file pointer open, then we have to */
4551 /* clean up here or eventually we will run out of something */
4553 if (info->fp == NULL) {
4554 my_pclose_pinfo(aTHX_ info);
4562 } /* end of safe_popen */
4565 /*{{{ PerlIO *my_popen(char *cmd, char *mode)*/
4567 Perl_my_popen(pTHX_ const char *cmd, const char *mode)
4571 TAINT_PROPER("popen");
4572 PERL_FLUSHALL_FOR_CHILD;
4573 return safe_popen(aTHX_ cmd,mode,&sts);
4579 /* Routine to close and cleanup a pipe info structure */
4581 static I32 my_pclose_pinfo(pTHX_ pInfo info) {
4583 unsigned long int retsts;
4588 /* If we were writing to a subprocess, insure that someone reading from
4589 * the mailbox gets an EOF. It looks like a simple fclose() doesn't
4590 * produce an EOF record in the mailbox.
4592 * well, at least sometimes it *does*, so we have to watch out for
4593 * the first EOF closing the pipe (and DASSGN'ing the channel)...
4597 #if defined(USE_ITHREADS)
4600 && PL_perlio_fd_refcnt)
4601 PerlIO_flush(info->fp);
4603 fflush((FILE *)info->fp);
4606 _ckvmssts(sys$setast(0));
4607 info->closing = TRUE;
4608 done = info->done && info->in_done && info->out_done && info->err_done;
4609 /* hanging on write to Perl's input? cancel it */
4610 if (info->mode == 'r' && info->out && !info->out_done) {
4611 if (info->out->chan_out) {
4612 _ckvmssts(sys$cancel(info->out->chan_out));
4613 if (!info->out->chan_in) { /* EOF generation, need AST */
4614 _ckvmssts(sys$dclast(pipe_infromchild_ast,info->out,0));
4618 if (info->in && !info->in_done && !info->in->shut_on_empty) /* EOF if hasn't had one yet */
4619 _ckvmssts(sys$qio(0,info->in->chan_in,IO$_WRITEOF,0,0,0,
4621 _ckvmssts(sys$setast(1));
4624 #if defined(USE_ITHREADS)
4627 && PL_perlio_fd_refcnt)
4628 PerlIO_close(info->fp);
4630 fclose((FILE *)info->fp);
4633 we have to wait until subprocess completes, but ALSO wait until all
4634 the i/o completes...otherwise we'll be freeing the "info" structure
4635 that the i/o ASTs could still be using...
4639 _ckvmssts(sys$setast(0));
4640 done = info->done && info->in_done && info->out_done && info->err_done;
4641 if (!done) _ckvmssts(sys$clref(pipe_ef));
4642 _ckvmssts(sys$setast(1));
4643 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4645 retsts = info->completion;
4647 /* remove from list of open pipes */
4648 _ckvmssts(sys$setast(0));
4650 for (next = open_pipes; next != NULL; last = next, next = next->next) {
4656 last->next = info->next;
4658 open_pipes = info->next;
4659 _ckvmssts(sys$setast(1));
4661 /* free buffers and structures */
4664 if (info->in->buf) {
4665 n = info->in->bufsize * sizeof(char);
4666 _ckvmssts(lib$free_vm(&n, &info->in->buf));
4669 _ckvmssts(lib$free_vm(&n, &info->in));
4672 if (info->out->buf) {
4673 n = info->out->bufsize * sizeof(char);
4674 _ckvmssts(lib$free_vm(&n, &info->out->buf));
4677 _ckvmssts(lib$free_vm(&n, &info->out));
4680 if (info->err->buf) {
4681 n = info->err->bufsize * sizeof(char);
4682 _ckvmssts(lib$free_vm(&n, &info->err->buf));
4685 _ckvmssts(lib$free_vm(&n, &info->err));
4688 _ckvmssts(lib$free_vm(&n, &info));
4694 /*{{{ I32 my_pclose(PerlIO *fp)*/
4695 I32 Perl_my_pclose(pTHX_ PerlIO *fp)
4697 pInfo info, last = NULL;
4700 /* Fixme - need ast and mutex protection here */
4701 for (info = open_pipes; info != NULL; last = info, info = info->next)
4702 if (info->fp == fp) break;
4704 if (info == NULL) { /* no such pipe open */
4705 set_errno(ECHILD); /* quoth POSIX */
4706 set_vaxc_errno(SS$_NONEXPR);
4710 ret_status = my_pclose_pinfo(aTHX_ info);
4714 } /* end of my_pclose() */
4716 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4717 /* Roll our own prototype because we want this regardless of whether
4718 * _VMS_WAIT is defined.
4720 __pid_t __vms_waitpid( __pid_t __pid, int *__stat_loc, int __options );
4722 /* sort-of waitpid; special handling of pipe clean-up for subprocesses
4723 created with popen(); otherwise partially emulate waitpid() unless
4724 we have a suitable one from the CRTL that came with VMS 7.2 and later.
4725 Also check processes not considered by the CRTL waitpid().
4727 /*{{{Pid_t my_waitpid(Pid_t pid, int *statusp, int flags)*/
4729 Perl_my_waitpid(pTHX_ Pid_t pid, int *statusp, int flags)
4736 if (statusp) *statusp = 0;
4738 for (info = open_pipes; info != NULL; info = info->next)
4739 if (info->pid == pid) break;
4741 if (info != NULL) { /* we know about this child */
4742 while (!info->done) {
4743 _ckvmssts(sys$setast(0));
4745 if (!done) _ckvmssts(sys$clref(pipe_ef));
4746 _ckvmssts(sys$setast(1));
4747 if (!done) _ckvmssts(sys$waitfr(pipe_ef));
4750 if (statusp) *statusp = info->completion;
4754 /* child that already terminated? */
4756 for (j = 0; j < NKEEPCLOSED && j < closed_num; j++) {
4757 if (closed_list[j].pid == pid) {
4758 if (statusp) *statusp = closed_list[j].completion;
4763 /* fall through if this child is not one of our own pipe children */
4765 #if defined(__CRTL_VER) && __CRTL_VER >= 70200000
4767 /* waitpid() became available in the CRTL as of VMS 7.0, but only
4768 * in 7.2 did we get a version that fills in the VMS completion
4769 * status as Perl has always tried to do.
4772 sts = __vms_waitpid( pid, statusp, flags );
4774 if ( sts == 0 || !(sts == -1 && errno == ECHILD) )
4777 /* If the real waitpid tells us the child does not exist, we
4778 * fall through here to implement waiting for a child that
4779 * was created by some means other than exec() (say, spawned
4780 * from DCL) or to wait for a process that is not a subprocess
4781 * of the current process.
4784 #endif /* defined(__CRTL_VER) && __CRTL_VER >= 70200000 */
4787 $DESCRIPTOR(intdsc,"0 00:00:01");
4788 unsigned long int ownercode = JPI$_OWNER, ownerpid;
4789 unsigned long int pidcode = JPI$_PID, mypid;
4790 unsigned long int interval[2];
4791 unsigned int jpi_iosb[2];
4792 struct itmlst_3 jpilist[2] = {
4793 {sizeof(ownerpid), JPI$_OWNER, &ownerpid, 0},
4798 /* Sorry folks, we don't presently implement rooting around for
4799 the first child we can find, and we definitely don't want to
4800 pass a pid of -1 to $getjpi, where it is a wildcard operation.
4806 /* Get the owner of the child so I can warn if it's not mine. If the
4807 * process doesn't exist or I don't have the privs to look at it,
4808 * I can go home early.
4810 sts = sys$getjpiw(0,&pid,NULL,&jpilist,&jpi_iosb,NULL,NULL);
4811 if (sts & 1) sts = jpi_iosb[0];
4823 set_vaxc_errno(sts);
4827 if (ckWARN(WARN_EXEC)) {
4828 /* remind folks they are asking for non-standard waitpid behavior */
4829 _ckvmssts(lib$getjpi(&pidcode,0,0,&mypid,0,0));
4830 if (ownerpid != mypid)
4831 Perl_warner(aTHX_ packWARN(WARN_EXEC),
4832 "waitpid: process %x is not a child of process %x",
4836 /* simply check on it once a second until it's not there anymore. */
4838 _ckvmssts(sys$bintim(&intdsc,interval));
4839 while ((sts=lib$getjpi(&ownercode,&pid,0,&ownerpid,0,0)) & 1) {
4840 _ckvmssts(sys$schdwk(0,0,interval,0));
4841 _ckvmssts(sys$hiber());
4843 if (sts == SS$_NONEXPR) sts = SS$_NORMAL;
4848 } /* end of waitpid() */
4853 /*{{{ char *my_gconvert(double val, int ndig, int trail, char *buf) */
4855 my_gconvert(double val, int ndig, int trail, char *buf)
4857 static char __gcvtbuf[DBL_DIG+1];
4860 loc = buf ? buf : __gcvtbuf;
4862 #ifndef __DECC /* VAXCRTL gcvt uses E format for numbers < 1 */
4864 sprintf(loc,"%.*g",ndig,val);
4870 if (!buf && ndig > DBL_DIG) ndig = DBL_DIG;
4871 return gcvt(val,ndig,loc);
4874 loc[0] = '0'; loc[1] = '\0';
4881 #if defined(__VAX) || !defined(NAML$C_MAXRSS)
4882 static int rms_free_search_context(struct FAB * fab)
4886 nam = fab->fab$l_nam;
4887 nam->nam$b_nop |= NAM$M_SYNCHK;
4888 nam->nam$l_rlf = NULL;
4890 return sys$parse(fab, NULL, NULL);
4893 #define rms_setup_nam(nam) struct NAM nam = cc$rms_nam
4894 #define rms_clear_nam_nop(nam) nam.nam$b_nop = 0;
4895 #define rms_set_nam_nop(nam, opt) nam.nam$b_nop |= (opt)
4896 #define rms_set_nam_fnb(nam, opt) nam.nam$l_fnb |= (opt)
4897 #define rms_is_nam_fnb(nam, opt) (nam.nam$l_fnb & (opt))
4898 #define rms_nam_esll(nam) nam.nam$b_esl
4899 #define rms_nam_esl(nam) nam.nam$b_esl
4900 #define rms_nam_name(nam) nam.nam$l_name
4901 #define rms_nam_namel(nam) nam.nam$l_name
4902 #define rms_nam_type(nam) nam.nam$l_type
4903 #define rms_nam_typel(nam) nam.nam$l_type
4904 #define rms_nam_ver(nam) nam.nam$l_ver
4905 #define rms_nam_verl(nam) nam.nam$l_ver
4906 #define rms_nam_rsll(nam) nam.nam$b_rsl
4907 #define rms_nam_rsl(nam) nam.nam$b_rsl
4908 #define rms_bind_fab_nam(fab, nam) fab.fab$l_nam = &nam
4909 #define rms_set_fna(fab, nam, name, size) \
4910 { fab.fab$b_fns = size; fab.fab$l_fna = name; }
4911 #define rms_get_fna(fab, nam) fab.fab$l_fna
4912 #define rms_set_dna(fab, nam, name, size) \
4913 { fab.fab$b_dns = size; fab.fab$l_dna = name; }
4914 #define rms_nam_dns(fab, nam) fab.fab$b_dns
4915 #define rms_set_esa(nam, name, size) \
4916 { nam.nam$b_ess = size; nam.nam$l_esa = name; }
4917 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4918 { nam.nam$l_esa = s_name; nam.nam$b_ess = s_size;}
4919 #define rms_set_rsa(nam, name, size) \
4920 { nam.nam$l_rsa = name; nam.nam$b_rss = size; }
4921 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4922 { nam.nam$l_rsa = s_name; nam.nam$b_rss = s_size; }
4923 #define rms_nam_name_type_l_size(nam) \
4924 (nam.nam$b_name + nam.nam$b_type)
4926 static int rms_free_search_context(struct FAB * fab)
4930 nam = fab->fab$l_naml;
4931 nam->naml$b_nop |= NAM$M_SYNCHK;
4932 nam->naml$l_rlf = NULL;
4933 nam->naml$l_long_defname_size = 0;
4936 return sys$parse(fab, NULL, NULL);
4939 #define rms_setup_nam(nam) struct NAML nam = cc$rms_naml
4940 #define rms_clear_nam_nop(nam) nam.naml$b_nop = 0;
4941 #define rms_set_nam_nop(nam, opt) nam.naml$b_nop |= (opt)
4942 #define rms_set_nam_fnb(nam, opt) nam.naml$l_fnb |= (opt)
4943 #define rms_is_nam_fnb(nam, opt) (nam.naml$l_fnb & (opt))
4944 #define rms_nam_esll(nam) nam.naml$l_long_expand_size
4945 #define rms_nam_esl(nam) nam.naml$b_esl
4946 #define rms_nam_name(nam) nam.naml$l_name
4947 #define rms_nam_namel(nam) nam.naml$l_long_name
4948 #define rms_nam_type(nam) nam.naml$l_type
4949 #define rms_nam_typel(nam) nam.naml$l_long_type
4950 #define rms_nam_ver(nam) nam.naml$l_ver
4951 #define rms_nam_verl(nam) nam.naml$l_long_ver
4952 #define rms_nam_rsll(nam) nam.naml$l_long_result_size
4953 #define rms_nam_rsl(nam) nam.naml$b_rsl
4954 #define rms_bind_fab_nam(fab, nam) fab.fab$l_naml = &nam
4955 #define rms_set_fna(fab, nam, name, size) \
4956 { fab.fab$b_fns = 0; fab.fab$l_fna = (char *) -1; \
4957 nam.naml$l_long_filename_size = size; \
4958 nam.naml$l_long_filename = name;}
4959 #define rms_get_fna(fab, nam) nam.naml$l_long_filename
4960 #define rms_set_dna(fab, nam, name, size) \
4961 { fab.fab$b_dns = 0; fab.fab$l_dna = (char *) -1; \
4962 nam.naml$l_long_defname_size = size; \
4963 nam.naml$l_long_defname = name; }
4964 #define rms_nam_dns(fab, nam) nam.naml$l_long_defname_size
4965 #define rms_set_esa(nam, name, size) \
4966 { nam.naml$b_ess = 0; nam.naml$l_esa = (char *) -1; \
4967 nam.naml$l_long_expand_alloc = size; \
4968 nam.naml$l_long_expand = name; }
4969 #define rms_set_esal(nam, s_name, s_size, l_name, l_size) \
4970 { nam.naml$l_esa = s_name; nam.naml$b_ess = s_size; \
4971 nam.naml$l_long_expand = l_name; \
4972 nam.naml$l_long_expand_alloc = l_size; }
4973 #define rms_set_rsa(nam, name, size) \
4974 { nam.naml$l_rsa = NULL; nam.naml$b_rss = 0; \
4975 nam.naml$l_long_result = name; \
4976 nam.naml$l_long_result_alloc = size; }
4977 #define rms_set_rsal(nam, s_name, s_size, l_name, l_size) \
4978 { nam.naml$l_rsa = s_name; nam.naml$b_rss = s_size; \
4979 nam.naml$l_long_result = l_name; \
4980 nam.naml$l_long_result_alloc = l_size; }
4981 #define rms_nam_name_type_l_size(nam) \
4982 (nam.naml$l_long_name_size + nam.naml$l_long_type_size)
4987 * The CRTL for 8.3 and later can create symbolic links in any mode,
4988 * however in 8.3 the unlink/remove/delete routines will only properly handle
4989 * them if one of the PCP modes is active.
4991 static int rms_erase(const char * vmsname)
4994 struct FAB myfab = cc$rms_fab;
4995 rms_setup_nam(mynam);
4997 rms_set_fna(myfab, mynam, (char *)vmsname, strlen(vmsname)); /* cast ok */
4998 rms_bind_fab_nam(myfab, mynam);
5000 /* Are we removing all versions? */
5001 if (vms_unlink_all_versions == 1) {
5002 const char * defspec = ";*";
5003 rms_set_dna(myfab, mynam, (char *)defspec, strlen(defspec)); /* cast ok */
5006 #ifdef NAML$M_OPEN_SPECIAL
5007 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5010 status = sys$erase(&myfab, 0, 0);
5017 vms_rename_with_acl(pTHX_ const struct dsc$descriptor_s * vms_src_dsc,
5018 const struct dsc$descriptor_s * vms_dst_dsc,
5019 unsigned long flags)
5021 /* VMS and UNIX handle file permissions differently and the
5022 * the same ACL trick may be needed for renaming files,
5023 * especially if they are directories.
5026 /* todo: get kill_file and rename to share common code */
5027 /* I can not find online documentation for $change_acl
5028 * it appears to be replaced by $set_security some time ago */
5030 const unsigned int access_mode = 0;
5031 $DESCRIPTOR(obj_file_dsc,"FILE");
5034 unsigned long int jpicode = JPI$_UIC, type = ACL$C_FILE;
5035 int aclsts, fndsts, rnsts = -1;
5036 unsigned int ctx = 0;
5037 struct dsc$descriptor_s fildsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
5038 struct dsc$descriptor_s * clean_dsc;
5041 unsigned char myace$b_length;
5042 unsigned char myace$b_type;
5043 unsigned short int myace$w_flags;
5044 unsigned long int myace$l_access;
5045 unsigned long int myace$l_ident;
5046 } newace = { sizeof(struct myacedef), ACE$C_KEYID, 0,
5047 ACE$M_READ | ACE$M_WRITE | ACE$M_DELETE | ACE$M_CONTROL,
5049 oldace = { sizeof(struct myacedef), ACE$C_KEYID, 0, 0, 0};
5052 findlst[3] = {{sizeof oldace, OSS$_ACL_FIND_ENTRY, &oldace, 0},
5053 {sizeof oldace, OSS$_ACL_READ_ENTRY, &oldace, 0},
5055 addlst[2] = {{sizeof newace, OSS$_ACL_ADD_ENTRY, &newace, 0},{0,0,0,0}},
5056 dellst[2] = {{sizeof newace, OSS$_ACL_DELETE_ENTRY, &newace, 0},
5060 /* Expand the input spec using RMS, since we do not want to put
5061 * ACLs on the target of a symbolic link */
5062 vmsname = PerlMem_malloc(NAM$C_MAXRSS+1);
5063 if (vmsname == NULL)
5066 rslt = int_rmsexpand_tovms(vms_src_dsc->dsc$a_pointer,
5068 PERL_RMSEXPAND_M_SYMLINK);
5070 PerlMem_free(vmsname);
5074 /* So we get our own UIC to use as a rights identifier,
5075 * and the insert an ACE at the head of the ACL which allows us
5076 * to delete the file.
5078 _ckvmssts_noperl(lib$getjpi(&jpicode,0,0,&(oldace.myace$l_ident),0,0));
5080 fildsc.dsc$w_length = strlen(vmsname);
5081 fildsc.dsc$a_pointer = vmsname;
5083 newace.myace$l_ident = oldace.myace$l_ident;
5086 /* Grab any existing ACEs with this identifier in case we fail */
5087 clean_dsc = &fildsc;
5088 aclsts = fndsts = sys$get_security(&obj_file_dsc,
5096 if ($VMS_STATUS_SUCCESS(fndsts) || (fndsts == SS$_ACLEMPTY)) {
5097 /* Add the new ACE . . . */
5099 /* if the sys$get_security succeeded, then ctx is valid, and the
5100 * object/file descriptors will be ignored. But otherwise they
5103 aclsts = sys$set_security(&obj_file_dsc, &fildsc, NULL,
5104 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5105 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5107 set_vaxc_errno(aclsts);
5108 PerlMem_free(vmsname);
5112 rnsts = lib$rename_file(vms_src_dsc, vms_dst_dsc,
5115 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5117 if ($VMS_STATUS_SUCCESS(rnsts)) {
5118 clean_dsc = (struct dsc$descriptor_s *)vms_dst_dsc;
5121 /* Put things back the way they were. */
5123 aclsts = sys$get_security(&obj_file_dsc,
5131 if ($VMS_STATUS_SUCCESS(aclsts)) {
5135 if (!$VMS_STATUS_SUCCESS(fndsts))
5136 sec_flags = OSS$M_RELCTX;
5138 /* Get rid of the new ACE */
5139 aclsts = sys$set_security(NULL, NULL, NULL,
5140 sec_flags, dellst, &ctx, &access_mode);
5142 /* If there was an old ACE, put it back */
5143 if ($VMS_STATUS_SUCCESS(aclsts) && $VMS_STATUS_SUCCESS(fndsts)) {
5144 addlst[0].bufadr = &oldace;
5145 aclsts = sys$set_security(NULL, NULL, NULL,
5146 OSS$M_RELCTX, addlst, &ctx, &access_mode);
5147 if (!$VMS_STATUS_SUCCESS(aclsts) && (aclsts != SS$_NOCLASS)) {
5149 set_vaxc_errno(aclsts);
5155 /* Try to clear the lock on the ACL list */
5156 aclsts2 = sys$set_security(NULL, NULL, NULL,
5157 OSS$M_RELCTX, NULL, &ctx, &access_mode);
5159 /* Rename errors are most important */
5160 if (!$VMS_STATUS_SUCCESS(rnsts))
5163 set_vaxc_errno(aclsts);
5168 if (aclsts != SS$_ACLEMPTY)
5175 PerlMem_free(vmsname);
5180 /*{{{int rename(const char *, const char * */
5181 /* Not exactly what X/Open says to do, but doing it absolutely right
5182 * and efficiently would require a lot more work. This should be close
5183 * enough to pass all but the most strict X/Open compliance test.
5186 Perl_rename(pTHX_ const char *src, const char * dst)
5195 /* Validate the source file */
5196 src_sts = flex_lstat(src, &src_st);
5199 /* No source file or other problem */
5203 dst_sts = flex_lstat(dst, &dst_st);
5206 if (dst_st.st_dev != src_st.st_dev) {
5207 /* Must be on the same device */
5212 /* VMS_INO_T_COMPARE is true if the inodes are different
5213 * to match the output of memcmp
5216 if (!VMS_INO_T_COMPARE(src_st.st_ino, dst_st.st_ino)) {
5217 /* That was easy, the files are the same! */
5221 if (S_ISDIR(src_st.st_mode) && !S_ISDIR(dst_st.st_mode)) {
5222 /* If source is a directory, so must be dest */
5230 if ((dst_sts == 0) &&
5231 (vms_unlink_all_versions || S_ISDIR(dst_st.st_mode))) {
5233 /* We have issues here if vms_unlink_all_versions is set
5234 * If the destination exists, and is not a directory, then
5235 * we must delete in advance.
5237 * If the src is a directory, then we must always pre-delete
5240 * If we successfully delete the dst in advance, and the rename fails
5241 * X/Open requires that errno be EIO.
5245 if (!S_ISDIR(dst_st.st_mode) || S_ISDIR(src_st.st_mode)) {
5247 d_sts = mp_do_kill_file(aTHX_ dst, S_ISDIR(dst_st.st_mode));
5251 /* We killed the destination, so only errno now is EIO */
5256 /* Originally the idea was to call the CRTL rename() and only
5257 * try the lib$rename_file if it failed.
5258 * It turns out that there are too many variants in what the
5259 * the CRTL rename might do, so only use lib$rename_file
5264 /* Is the source and dest both in VMS format */
5265 /* if the source is a directory, then need to fileify */
5266 /* and dest must be a directory or non-existant. */
5272 unsigned long flags;
5273 struct dsc$descriptor_s old_file_dsc;
5274 struct dsc$descriptor_s new_file_dsc;
5276 /* We need to modify the src and dst depending
5277 * on if one or more of them are directories.
5280 vms_src = PerlMem_malloc(VMS_MAXRSS);
5281 if (vms_src == NULL)
5282 _ckvmssts_noperl(SS$_INSFMEM);
5284 /* Source is always a VMS format file */
5285 ret_str = do_tovmsspec(src, vms_src, 0, NULL);
5286 if (ret_str == NULL) {
5287 PerlMem_free(vms_src);
5292 vms_dst = PerlMem_malloc(VMS_MAXRSS);
5293 if (vms_dst == NULL)
5294 _ckvmssts_noperl(SS$_INSFMEM);
5296 if (S_ISDIR(src_st.st_mode)) {
5298 char * vms_dir_file;
5300 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5301 if (vms_dir_file == NULL)
5302 _ckvmssts_noperl(SS$_INSFMEM);
5304 /* The source must be a file specification */
5305 ret_str = int_fileify_dirspec(vms_src, vms_dir_file, NULL);
5306 if (ret_str == NULL) {
5307 PerlMem_free(vms_src);
5308 PerlMem_free(vms_dst);
5309 PerlMem_free(vms_dir_file);
5313 PerlMem_free(vms_src);
5314 vms_src = vms_dir_file;
5316 /* If the dest is a directory, we must remove it
5319 d_sts = mp_do_kill_file(aTHX_ dst, 1);
5321 PerlMem_free(vms_src);
5322 PerlMem_free(vms_dst);
5330 /* The dest must be a VMS file specification */
5331 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5332 if (ret_str == NULL) {
5333 PerlMem_free(vms_src);
5334 PerlMem_free(vms_dst);
5339 /* The source must be a file specification */
5340 vms_dir_file = PerlMem_malloc(VMS_MAXRSS);
5341 if (vms_dir_file == NULL)
5342 _ckvmssts_noperl(SS$_INSFMEM);
5344 ret_str = do_fileify_dirspec(vms_dst, vms_dir_file, 0, NULL);
5345 if (ret_str == NULL) {
5346 PerlMem_free(vms_src);
5347 PerlMem_free(vms_dst);
5348 PerlMem_free(vms_dir_file);
5352 PerlMem_free(vms_dst);
5353 vms_dst = vms_dir_file;
5356 /* File to file or file to new dir */
5358 if ((dst_sts == 0) && S_ISDIR(dst_st.st_mode)) {
5359 /* VMS pathify a dir target */
5360 ret_str = int_tovmspath(dst, vms_dst, NULL);
5361 if (ret_str == NULL) {
5362 PerlMem_free(vms_src);
5363 PerlMem_free(vms_dst);
5369 /* fileify a target VMS file specification */
5370 ret_str = int_tovmsspec(dst, vms_dst, 0, NULL);
5371 if (ret_str == NULL) {
5372 PerlMem_free(vms_src);
5373 PerlMem_free(vms_dst);
5380 old_file_dsc.dsc$a_pointer = vms_src;
5381 old_file_dsc.dsc$w_length = strlen(vms_src);
5382 old_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5383 old_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5385 new_file_dsc.dsc$a_pointer = vms_dst;
5386 new_file_dsc.dsc$w_length = strlen(vms_dst);
5387 new_file_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
5388 new_file_dsc.dsc$b_class = DSC$K_CLASS_S;
5391 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5392 flags |= 4; /* LIB$M_FIL_LONG_NAMES (bit 2) */
5395 sts = lib$rename_file(&old_file_dsc,
5399 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
5400 if (!$VMS_STATUS_SUCCESS(sts)) {
5402 /* We could have failed because VMS style permissions do not
5403 * permit renames that UNIX will allow. Just like the hack
5406 sts = vms_rename_with_acl(aTHX_ &old_file_dsc, &new_file_dsc, flags);
5409 PerlMem_free(vms_src);
5410 PerlMem_free(vms_dst);
5411 if (!$VMS_STATUS_SUCCESS(sts)) {
5418 if (vms_unlink_all_versions) {
5419 /* Now get rid of any previous versions of the source file that
5424 src_sts = mp_do_kill_file(aTHX_ src, S_ISDIR(src_st.st_mode));
5428 /* We deleted the destination, so must force the error to be EIO */
5429 if ((retval != 0) && (pre_delete != 0))
5437 /*{{{char *do_rmsexpand(char *fspec, char *out, int ts, char *def, unsigned opts)*/
5438 /* Shortcut for common case of simple calls to $PARSE and $SEARCH
5439 * to expand file specification. Allows for a single default file
5440 * specification and a simple mask of options. If outbuf is non-NULL,
5441 * it must point to a buffer at least NAM$C_MAXRSS bytes long, into which
5442 * the resultant file specification is placed. If outbuf is NULL, the
5443 * resultant file specification is placed into a static buffer.
5444 * The third argument, if non-NULL, is taken to be a default file
5445 * specification string. The fourth argument is unused at present.
5446 * rmesexpand() returns the address of the resultant string if
5447 * successful, and NULL on error.
5449 * New functionality for previously unused opts value:
5450 * PERL_RMSEXPAND_M_VMS - Force output file specification to VMS format.
5451 * PERL_RMSEXPAND_M_LONG - Want output in long formst
5452 * PERL_RMSEXPAND_M_VMS_IN - Input is already in VMS, so do not vmsify
5453 * PERL_RMSEXPAND_M_SYMLINK - Use symbolic link, not target
5455 static char *mp_do_tounixspec(pTHX_ const char *, char *, int, int *);
5459 (const char *filespec,
5461 const char *defspec,
5467 const char * in_spec;
5469 const char * def_spec;
5470 char * vmsfspec, *vmsdefspec;
5474 struct FAB myfab = cc$rms_fab;
5475 rms_setup_nam(mynam);
5477 unsigned long int retsts, trimver, trimtype, haslower = 0, isunix = 0;
5480 /* temp hack until UTF8 is actually implemented */
5481 if (fs_utf8 != NULL)
5484 if (!filespec || !*filespec) {
5485 set_vaxc_errno(LIB$_INVARG); set_errno(EINVAL);
5495 if ((opts & PERL_RMSEXPAND_M_VMS_IN) == 0) {
5496 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
5497 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
5499 /* If this is a UNIX file spec, convert it to VMS */
5500 sts = vms_split_path(filespec, &v_spec, &v_len, &r_spec, &r_len,
5501 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
5502 &e_len, &vs_spec, &vs_len);
5507 vmsfspec = PerlMem_malloc(VMS_MAXRSS);
5508 if (vmsfspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5509 ret_spec = int_tovmsspec(filespec, vmsfspec, 0, fs_utf8);
5510 if (ret_spec == NULL) {
5511 PerlMem_free(vmsfspec);
5514 in_spec = (const char *)vmsfspec;
5516 /* Unless we are forcing to VMS format, a UNIX input means
5517 * UNIX output, and that requires long names to be used
5519 if ((opts & PERL_RMSEXPAND_M_VMS) == 0)
5520 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5521 opts |= PERL_RMSEXPAND_M_LONG;
5529 rms_set_fna(myfab, mynam, (char *)in_spec, strlen(in_spec)); /* cast ok */
5530 rms_bind_fab_nam(myfab, mynam);
5532 /* Process the default file specification if present */
5534 if (defspec && *defspec) {
5536 t_isunix = is_unix_filespec(defspec);
5538 vmsdefspec = PerlMem_malloc(VMS_MAXRSS);
5539 if (vmsdefspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5540 ret_spec = int_tovmsspec(defspec, vmsdefspec, 0, dfs_utf8);
5542 if (ret_spec == NULL) {
5543 /* Clean up and bail */
5544 PerlMem_free(vmsdefspec);
5545 if (vmsfspec != NULL)
5546 PerlMem_free(vmsfspec);
5549 def_spec = (const char *)vmsdefspec;
5551 rms_set_dna(myfab, mynam,
5552 (char *)def_spec, strlen(def_spec)); /* cast ok */
5555 /* Now we need the expansion buffers */
5556 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
5557 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5558 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5559 esal = PerlMem_malloc(VMS_MAXRSS);
5560 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5562 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS-1);
5564 /* If a NAML block is used RMS always writes to the long and short
5565 * addresses unless you suppress the short name.
5567 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5568 outbufl = PerlMem_malloc(VMS_MAXRSS);
5569 if (outbufl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5571 rms_set_rsal(mynam, outbuf, NAM$C_MAXRSS, outbufl, (VMS_MAXRSS - 1));
5573 #ifdef NAM$M_NO_SHORT_UPCASE
5574 if (decc_efs_case_preserve)
5575 rms_set_nam_nop(mynam, NAM$M_NO_SHORT_UPCASE);
5578 /* We may not want to follow symbolic links */
5579 #ifdef NAML$M_OPEN_SPECIAL
5580 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5581 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5584 /* First attempt to parse as an existing file */
5585 retsts = sys$parse(&myfab,0,0);
5586 if (!(retsts & STS$K_SUCCESS)) {
5588 /* Could not find the file, try as syntax only if error is not fatal */
5589 rms_set_nam_nop(mynam, NAM$M_SYNCHK);
5590 if (retsts == RMS$_DNF ||
5591 retsts == RMS$_DIR ||
5592 retsts == RMS$_DEV ||
5593 retsts == RMS$_PRV) {
5594 retsts = sys$parse(&myfab,0,0);
5595 if (retsts & STS$K_SUCCESS) goto int_expanded;
5598 /* Still could not parse the file specification */
5599 /*----------------------------------------------*/
5600 sts = rms_free_search_context(&myfab); /* Free search context */
5601 if (vmsdefspec != NULL)
5602 PerlMem_free(vmsdefspec);
5603 if (vmsfspec != NULL)
5604 PerlMem_free(vmsfspec);
5605 if (outbufl != NULL)
5606 PerlMem_free(outbufl);
5610 set_vaxc_errno(retsts);
5611 if (retsts == RMS$_PRV) set_errno(EACCES);
5612 else if (retsts == RMS$_DEV) set_errno(ENODEV);
5613 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
5614 else set_errno(EVMSERR);
5617 retsts = sys$search(&myfab,0,0);
5618 if (!(retsts & STS$K_SUCCESS) && retsts != RMS$_FNF) {
5619 sts = rms_free_search_context(&myfab); /* Free search context */
5620 if (vmsdefspec != NULL)
5621 PerlMem_free(vmsdefspec);
5622 if (vmsfspec != NULL)
5623 PerlMem_free(vmsfspec);
5624 if (outbufl != NULL)
5625 PerlMem_free(outbufl);
5629 set_vaxc_errno(retsts);
5630 if (retsts == RMS$_PRV) set_errno(EACCES);
5631 else set_errno(EVMSERR);
5635 /* If the input filespec contained any lowercase characters,
5636 * downcase the result for compatibility with Unix-minded code. */
5638 if (!decc_efs_case_preserve) {
5640 for (tbuf = rms_get_fna(myfab, mynam); *tbuf; tbuf++)
5641 if (islower(*tbuf)) { haslower = 1; break; }
5644 /* Is a long or a short name expected */
5645 /*------------------------------------*/
5647 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5648 if (rms_nam_rsll(mynam)) {
5650 speclen = rms_nam_rsll(mynam);
5653 spec_buf = esal; /* Not esa */
5654 speclen = rms_nam_esll(mynam);
5658 if (rms_nam_rsl(mynam)) {
5660 speclen = rms_nam_rsl(mynam);
5663 spec_buf = esa; /* Not esal */
5664 speclen = rms_nam_esl(mynam);
5667 spec_buf[speclen] = '\0';
5669 /* Trim off null fields added by $PARSE
5670 * If type > 1 char, must have been specified in original or default spec
5671 * (not true for version; $SEARCH may have added version of existing file).
5673 trimver = !rms_is_nam_fnb(mynam, NAM$M_EXP_VER);
5674 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5675 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5676 ((rms_nam_verl(mynam) - rms_nam_typel(mynam)) == 1);
5679 trimtype = !rms_is_nam_fnb(mynam, NAM$M_EXP_TYPE) &&
5680 ((rms_nam_ver(mynam) - rms_nam_type(mynam)) == 1);
5682 if (trimver || trimtype) {
5683 if (defspec && *defspec) {
5684 char *defesal = NULL;
5685 char *defesa = NULL;
5686 defesa = PerlMem_malloc(VMS_MAXRSS + 1);
5687 if (defesa != NULL) {
5688 struct FAB deffab = cc$rms_fab;
5689 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5690 defesal = PerlMem_malloc(VMS_MAXRSS + 1);
5691 if (defesal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
5693 rms_setup_nam(defnam);
5695 rms_bind_fab_nam(deffab, defnam);
5699 (deffab, defnam, (char *)defspec, rms_nam_dns(myfab, mynam));
5701 /* RMS needs the esa/esal as a work area if wildcards are involved */
5702 rms_set_esal(defnam, defesa, NAM$C_MAXRSS, defesal, VMS_MAXRSS - 1);
5704 rms_clear_nam_nop(defnam);
5705 rms_set_nam_nop(defnam, NAM$M_SYNCHK);
5706 #ifdef NAM$M_NO_SHORT_UPCASE
5707 if (decc_efs_case_preserve)
5708 rms_set_nam_nop(defnam, NAM$M_NO_SHORT_UPCASE);
5710 #ifdef NAML$M_OPEN_SPECIAL
5711 if ((opts & PERL_RMSEXPAND_M_SYMLINK) != 0)
5712 rms_set_nam_nop(mynam, NAML$M_OPEN_SPECIAL);
5714 if (sys$parse(&deffab,0,0) & STS$K_SUCCESS) {
5716 trimver = !rms_is_nam_fnb(defnam, NAM$M_EXP_VER);
5719 trimtype = !rms_is_nam_fnb(defnam, NAM$M_EXP_TYPE);
5722 if (defesal != NULL)
5723 PerlMem_free(defesal);
5724 PerlMem_free(defesa);
5726 _ckvmssts_noperl(SS$_INSFMEM);
5730 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5731 if (*(rms_nam_verl(mynam)) != '\"')
5732 speclen = rms_nam_verl(mynam) - spec_buf;
5735 if (*(rms_nam_ver(mynam)) != '\"')
5736 speclen = rms_nam_ver(mynam) - spec_buf;
5740 /* If we didn't already trim version, copy down */
5741 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5742 if (speclen > rms_nam_verl(mynam) - spec_buf)
5744 (rms_nam_typel(mynam),
5745 rms_nam_verl(mynam),
5746 speclen - (rms_nam_verl(mynam) - spec_buf));
5747 speclen -= rms_nam_verl(mynam) - rms_nam_typel(mynam);
5750 if (speclen > rms_nam_ver(mynam) - spec_buf)
5752 (rms_nam_type(mynam),
5754 speclen - (rms_nam_ver(mynam) - spec_buf));
5755 speclen -= rms_nam_ver(mynam) - rms_nam_type(mynam);
5760 /* Done with these copies of the input files */
5761 /*-------------------------------------------*/
5762 if (vmsfspec != NULL)
5763 PerlMem_free(vmsfspec);
5764 if (vmsdefspec != NULL)
5765 PerlMem_free(vmsdefspec);
5767 /* If we just had a directory spec on input, $PARSE "helpfully"
5768 * adds an empty name and type for us */
5769 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5770 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5771 if (rms_nam_namel(mynam) == rms_nam_typel(mynam) &&
5772 rms_nam_verl(mynam) == rms_nam_typel(mynam) + 1 &&
5773 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5774 speclen = rms_nam_namel(mynam) - spec_buf;
5779 if (rms_nam_name(mynam) == rms_nam_type(mynam) &&
5780 rms_nam_ver(mynam) == rms_nam_ver(mynam) + 1 &&
5781 !(rms_is_nam_fnb(mynam, NAM$M_EXP_NAME)))
5782 speclen = rms_nam_name(mynam) - spec_buf;
5785 /* Posix format specifications must have matching quotes */
5786 if (speclen < (VMS_MAXRSS - 1)) {
5787 if (decc_posix_compliant_pathnames && (spec_buf[0] == '\"')) {
5788 if ((speclen > 1) && (spec_buf[speclen-1] != '\"')) {
5789 spec_buf[speclen] = '\"';
5794 spec_buf[speclen] = '\0';
5795 if (haslower && !decc_efs_case_preserve) __mystrtolower(spec_buf);
5797 /* Have we been working with an expanded, but not resultant, spec? */
5798 /* Also, convert back to Unix syntax if necessary. */
5802 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
5803 if ((opts & PERL_RMSEXPAND_M_LONG) != 0) {
5804 rsl = rms_nam_rsll(mynam);
5808 rsl = rms_nam_rsl(mynam);
5811 /* rsl is not present, it means that spec_buf is either */
5812 /* esa or esal, and needs to be copied to outbuf */
5813 /* convert to Unix if desired */
5815 ret_spec = int_tounixspec(spec_buf, outbuf, fs_utf8);
5817 /* VMS file specs are not in UTF-8 */
5818 if (fs_utf8 != NULL)
5820 strcpy(outbuf, spec_buf);
5825 /* Now spec_buf is either outbuf or outbufl */
5826 /* We need the result into outbuf */
5828 /* If we need this in UNIX, then we need another buffer */
5829 /* to keep things in order */
5831 char * new_src = NULL;
5832 if (spec_buf == outbuf) {
5833 new_src = PerlMem_malloc(VMS_MAXRSS);
5834 strcpy(new_src, spec_buf);
5838 ret_spec = int_tounixspec(src, outbuf, fs_utf8);
5840 PerlMem_free(new_src);
5843 /* VMS file specs are not in UTF-8 */
5844 if (fs_utf8 != NULL)
5847 /* Copy the buffer if needed */
5848 if (outbuf != spec_buf)
5849 strcpy(outbuf, spec_buf);
5855 /* Need to clean up the search context */
5856 rms_set_rsal(mynam, NULL, 0, NULL, 0);
5857 sts = rms_free_search_context(&myfab); /* Free search context */
5859 /* Clean up the extra buffers */
5863 if (outbufl != NULL)
5864 PerlMem_free(outbufl);
5866 /* Return the result */
5870 /* Common simple case - Expand an already VMS spec */
5872 int_rmsexpand_vms(const char * filespec, char * outbuf, unsigned opts) {
5873 opts |= PERL_RMSEXPAND_M_VMS_IN;
5874 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5877 /* Common simple case - Expand to a VMS spec */
5879 int_rmsexpand_tovms(const char * filespec, char * outbuf, unsigned opts) {
5880 opts |= PERL_RMSEXPAND_M_VMS;
5881 return int_rmsexpand(filespec, outbuf, NULL, opts, NULL, NULL);
5885 /* Entry point used by perl routines */
5888 (pTHX_ const char *filespec,
5891 const char *defspec,
5896 static char __rmsexpand_retbuf[VMS_MAXRSS];
5897 char * expanded, *ret_spec, *ret_buf;
5901 if (ret_buf == NULL) {
5903 Newx(expanded, VMS_MAXRSS, char);
5904 if (expanded == NULL)
5905 _ckvmssts(SS$_INSFMEM);
5908 ret_buf = __rmsexpand_retbuf;
5913 ret_spec = int_rmsexpand(filespec, ret_buf, defspec,
5914 opts, fs_utf8, dfs_utf8);
5916 if (ret_spec == NULL) {
5917 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
5925 /* External entry points */
5926 char *Perl_rmsexpand(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5927 { return do_rmsexpand(spec,buf,0,def,opt,NULL,NULL); }
5928 char *Perl_rmsexpand_ts(pTHX_ const char *spec, char *buf, const char *def, unsigned opt)
5929 { return do_rmsexpand(spec,buf,1,def,opt,NULL,NULL); }
5930 char *Perl_rmsexpand_utf8
5931 (pTHX_ const char *spec, char *buf, const char *def,
5932 unsigned opt, int * fs_utf8, int * dfs_utf8)
5933 { return do_rmsexpand(spec,buf,0,def,opt, fs_utf8, dfs_utf8); }
5934 char *Perl_rmsexpand_utf8_ts
5935 (pTHX_ const char *spec, char *buf, const char *def,
5936 unsigned opt, int * fs_utf8, int * dfs_utf8)
5937 { return do_rmsexpand(spec,buf,1,def,opt, fs_utf8, dfs_utf8); }
5941 ** The following routines are provided to make life easier when
5942 ** converting among VMS-style and Unix-style directory specifications.
5943 ** All will take input specifications in either VMS or Unix syntax. On
5944 ** failure, all return NULL. If successful, the routines listed below
5945 ** return a pointer to a buffer containing the appropriately
5946 ** reformatted spec (and, therefore, subsequent calls to that routine
5947 ** will clobber the result), while the routines of the same names with
5948 ** a _ts suffix appended will return a pointer to a mallocd string
5949 ** containing the appropriately reformatted spec.
5950 ** In all cases, only explicit syntax is altered; no check is made that
5951 ** the resulting string is valid or that the directory in question
5954 ** fileify_dirspec() - convert a directory spec into the name of the
5955 ** directory file (i.e. what you can stat() to see if it's a dir).
5956 ** The style (VMS or Unix) of the result is the same as the style
5957 ** of the parameter passed in.
5958 ** pathify_dirspec() - convert a directory spec into a path (i.e.
5959 ** what you prepend to a filename to indicate what directory it's in).
5960 ** The style (VMS or Unix) of the result is the same as the style
5961 ** of the parameter passed in.
5962 ** tounixpath() - convert a directory spec into a Unix-style path.
5963 ** tovmspath() - convert a directory spec into a VMS-style path.
5964 ** tounixspec() - convert any file spec into a Unix-style file spec.
5965 ** tovmsspec() - convert any file spec into a VMS-style spec.
5966 ** xxxxx_utf8() - Variants that support UTF8 encoding of Unix-Style file spec.
5968 ** Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>
5969 ** Permission is given to distribute this code as part of the Perl
5970 ** standard distribution under the terms of the GNU General Public
5971 ** License or the Perl Artistic License. Copies of each may be
5972 ** found in the Perl standard distribution.
5975 /*{{{ char * int_fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
5977 int_fileify_dirspec(const char *dir, char *buf, int *utf8_fl)
5979 unsigned long int dirlen, retlen, addmfd = 0, hasfilename = 0;
5980 char *cp1, *cp2, *lastdir;
5981 char *trndir, *vmsdir;
5982 unsigned short int trnlnm_iter_count;
5986 if (utf8_fl != NULL)
5989 if (!dir || !*dir) {
5990 set_errno(EINVAL); set_vaxc_errno(SS$_BADPARAM); return NULL;
5992 dirlen = strlen(dir);
5993 while (dirlen && dir[dirlen-1] == '/') --dirlen;
5994 if (!dirlen) { /* We had Unixish '/' -- substitute top of current tree */
5995 if (!decc_posix_compliant_pathnames && decc_disable_posix_root) {
6002 if (dirlen > (VMS_MAXRSS - 1)) {
6003 set_errno(ENAMETOOLONG); set_vaxc_errno(RMS$_SYN);
6006 trndir = PerlMem_malloc(VMS_MAXRSS + 1);
6007 if (trndir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6008 if (!strpbrk(dir+1,"/]>:") &&
6009 (!decc_posix_compliant_pathnames && decc_disable_posix_root)) {
6010 strcpy(trndir,*dir == '/' ? dir + 1: dir);
6011 trnlnm_iter_count = 0;
6012 while (!strpbrk(trndir,"/]>:") && simple_trnlnm(trndir,trndir,VMS_MAXRSS-1)) {
6013 trnlnm_iter_count++;
6014 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
6016 dirlen = strlen(trndir);
6019 strncpy(trndir,dir,dirlen);
6020 trndir[dirlen] = '\0';
6023 /* At this point we are done with *dir and use *trndir which is a
6024 * copy that can be modified. *dir must not be modified.
6027 /* If we were handed a rooted logical name or spec, treat it like a
6028 * simple directory, so that
6029 * $ Define myroot dev:[dir.]
6030 * ... do_fileify_dirspec("myroot",buf,1) ...
6031 * does something useful.
6033 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".]")) {
6034 trndir[--dirlen] = '\0';
6035 trndir[dirlen-1] = ']';
6037 if (dirlen >= 2 && !strcmp(trndir+dirlen-2,".>")) {
6038 trndir[--dirlen] = '\0';
6039 trndir[dirlen-1] = '>';
6042 if ((cp1 = strrchr(trndir,']')) != NULL || (cp1 = strrchr(trndir,'>')) != NULL) {
6043 /* If we've got an explicit filename, we can just shuffle the string. */
6044 if (*(cp1+1)) hasfilename = 1;
6045 /* Similarly, we can just back up a level if we've got multiple levels
6046 of explicit directories in a VMS spec which ends with directories. */
6048 for (cp2 = cp1; cp2 > trndir; cp2--) {
6050 if ((cp2 - 1 > trndir) && (*(cp2 - 1) != '^')) {
6051 /* fix-me, can not scan EFS file specs backward like this */
6052 *cp2 = *cp1; *cp1 = '\0';
6057 if (*cp2 == '[' || *cp2 == '<') break;
6062 vmsdir = PerlMem_malloc(VMS_MAXRSS + 1);
6063 if (vmsdir == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6064 cp1 = strpbrk(trndir,"]:>");
6065 if (hasfilename || !cp1) { /* filename present or not VMS */
6067 if (decc_efs_charset && !cp1) {
6069 /* EFS handling for UNIX mode */
6071 /* Just remove the trailing '/' and we should be done */
6073 trndir_len = strlen(trndir);
6075 if (trndir_len > 1) {
6077 if (trndir[trndir_len] == '/') {
6078 trndir[trndir_len] = '\0';
6081 strcpy(buf, trndir);
6082 PerlMem_free(trndir);
6083 PerlMem_free(vmsdir);
6087 /* For non-EFS mode, this is left for backwards compatibility */
6088 /* For EFS mode, this is only done for VMS format filespecs as */
6089 /* Perl programs generally have problems when a UNIX format spec */
6090 /* returns a VMS format spec */
6091 if (trndir[0] == '.') {
6092 if (trndir[1] == '\0' || (trndir[1] == '/' && trndir[2] == '\0')) {
6093 PerlMem_free(trndir);
6094 PerlMem_free(vmsdir);
6095 return int_fileify_dirspec("[]", buf, NULL);
6097 else if (trndir[1] == '.' &&
6098 (trndir[2] == '\0' || (trndir[2] == '/' && trndir[3] == '\0'))) {
6099 PerlMem_free(trndir);
6100 PerlMem_free(vmsdir);
6101 return int_fileify_dirspec("[-]", buf, NULL);
6104 if (dirlen && trndir[dirlen-1] == '/') { /* path ends with '/'; just add .dir;1 */
6105 dirlen -= 1; /* to last element */
6106 lastdir = strrchr(trndir,'/');
6108 else if ((cp1 = strstr(trndir,"/.")) != NULL) {
6109 /* If we have "/." or "/..", VMSify it and let the VMS code
6110 * below expand it, rather than repeating the code to handle
6111 * relative components of a filespec here */
6113 if (*(cp1+2) == '.') cp1++;
6114 if (*(cp1+2) == '/' || *(cp1+2) == '\0') {
6116 if (int_tovmsspec(trndir, vmsdir, 0, utf8_fl) == NULL) {
6117 PerlMem_free(trndir);
6118 PerlMem_free(vmsdir);
6121 if (strchr(vmsdir,'/') != NULL) {
6122 /* If int_tovmsspec() returned it, it must have VMS syntax
6123 * delimiters in it, so it's a mixed VMS/Unix spec. We take
6124 * the time to check this here only so we avoid a recursion
6125 * loop; otherwise, gigo.
6127 PerlMem_free(trndir);
6128 PerlMem_free(vmsdir);
6129 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN);
6132 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6133 PerlMem_free(trndir);
6134 PerlMem_free(vmsdir);
6137 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6138 PerlMem_free(trndir);
6139 PerlMem_free(vmsdir);
6143 } while ((cp1 = strstr(cp1,"/.")) != NULL);
6144 lastdir = strrchr(trndir,'/');
6146 else if (dirlen >= 7 && !strcmp(&trndir[dirlen-7],"/000000")) {
6148 /* Ditto for specs that end in an MFD -- let the VMS code
6149 * figure out whether it's a real device or a rooted logical. */
6151 /* This should not happen any more. Allowing the fake /000000
6152 * in a UNIX pathname causes all sorts of problems when trying
6153 * to run in UNIX emulation. So the VMS to UNIX conversions
6154 * now remove the fake /000000 directories.
6157 trndir[dirlen] = '/'; trndir[dirlen+1] = '\0';
6158 if (int_tovmsspec(trndir, vmsdir, 0, NULL) == NULL) {
6159 PerlMem_free(trndir);
6160 PerlMem_free(vmsdir);
6163 if (int_fileify_dirspec(vmsdir, trndir, NULL) == NULL) {
6164 PerlMem_free(trndir);
6165 PerlMem_free(vmsdir);
6168 ret_chr = int_tounixspec(trndir, buf, utf8_fl);
6169 PerlMem_free(trndir);
6170 PerlMem_free(vmsdir);
6175 if ( !(lastdir = cp1 = strrchr(trndir,'/')) &&
6176 !(lastdir = cp1 = strrchr(trndir,']')) &&
6177 !(lastdir = cp1 = strrchr(trndir,'>'))) cp1 = trndir;
6179 cp2 = strrchr(cp1,'.');
6181 int e_len, vs_len = 0;
6184 cp3 = strchr(cp2,';');
6185 e_len = strlen(cp2);
6187 vs_len = strlen(cp3);
6188 e_len = e_len - vs_len;
6190 is_dir = is_dir_ext(cp2, e_len, cp3, vs_len);
6192 if (!decc_efs_charset) {
6193 /* If this is not EFS, then not a directory */
6194 PerlMem_free(trndir);
6195 PerlMem_free(vmsdir);
6197 set_vaxc_errno(RMS$_DIR);
6201 /* Ok, here we have an issue, technically if a .dir shows */
6202 /* from inside a directory, then we should treat it as */
6203 /* xxx^.dir.dir. But we do not have that context at this */
6204 /* point unless this is totally restructured, so we remove */
6205 /* The .dir for now, and fix this better later */
6206 dirlen = cp2 - trndir;
6212 retlen = dirlen + 6;
6213 memcpy(buf, trndir, dirlen);
6216 /* We've picked up everything up to the directory file name.
6217 Now just add the type and version, and we're set. */
6219 /* We should only add type for VMS syntax, but historically Perl
6220 has added it for UNIX style also */
6222 /* Fix me - we should not be using the same routine for VMS and
6223 UNIX format files. Things are too tangled so we need to lookup
6224 what syntax the output is */
6228 lastdir = strrchr(trndir,'/');
6232 lastdir = strpbrk(trndir,"]:>");
6238 if ((is_vms == 0) && (is_unix == 0)) {
6239 /* We still do not know? */
6240 is_unix = decc_filename_unix_report;
6245 if ((is_unix && !decc_efs_charset) || is_vms) {
6247 /* It is a bug to add a .dir to a UNIX format directory spec */
6248 /* However Perl on VMS may have programs that expect this so */
6249 /* If not using EFS character specifications allow it. */
6251 if ((!decc_efs_case_preserve) && vms_process_case_tolerant) {
6252 /* Traditionally Perl expects filenames in lower case */
6253 strcat(buf, ".dir");
6255 /* VMS expects the .DIR to be in upper case */
6256 strcat(buf, ".DIR");
6259 /* It is also a bug to put a VMS format version on a UNIX file */
6260 /* specification. Perl self tests are looking for this */
6261 if (is_vms || !(decc_efs_charset || decc_filename_unix_report))
6264 PerlMem_free(trndir);
6265 PerlMem_free(vmsdir);
6268 else { /* VMS-style directory spec */
6270 char *esa, *esal, term, *cp;
6273 unsigned long int sts, cmplen, haslower = 0;
6274 unsigned int nam_fnb;
6276 struct FAB dirfab = cc$rms_fab;
6277 rms_setup_nam(savnam);
6278 rms_setup_nam(dirnam);
6280 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
6281 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6283 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
6284 esal = PerlMem_malloc(VMS_MAXRSS);
6285 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
6287 rms_set_fna(dirfab, dirnam, trndir, strlen(trndir));
6288 rms_bind_fab_nam(dirfab, dirnam);
6289 rms_set_dna(dirfab, dirnam, ".DIR;1", 6);
6290 rms_set_esal(dirnam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
6291 #ifdef NAM$M_NO_SHORT_UPCASE
6292 if (decc_efs_case_preserve)
6293 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6296 for (cp = trndir; *cp; cp++)
6297 if (islower(*cp)) { haslower = 1; break; }
6298 if (!((sts = sys$parse(&dirfab)) & STS$K_SUCCESS)) {
6299 if ((dirfab.fab$l_sts == RMS$_DIR) ||
6300 (dirfab.fab$l_sts == RMS$_DNF) ||
6301 (dirfab.fab$l_sts == RMS$_PRV)) {
6302 rms_set_nam_nop(dirnam, NAM$M_SYNCHK);
6303 sts = sys$parse(&dirfab);
6309 PerlMem_free(trndir);
6310 PerlMem_free(vmsdir);
6312 set_vaxc_errno(dirfab.fab$l_sts);
6318 /* Does the file really exist? */
6319 if (sys$search(&dirfab)& STS$K_SUCCESS) {
6320 /* Yes; fake the fnb bits so we'll check type below */
6321 rms_set_nam_fnb(dirnam, (NAM$M_EXP_TYPE | NAM$M_EXP_VER));
6323 else { /* No; just work with potential name */
6324 if (dirfab.fab$l_sts == RMS$_FNF) dirnam = savnam;
6327 fab_sts = dirfab.fab$l_sts;
6328 sts = rms_free_search_context(&dirfab);
6332 PerlMem_free(trndir);
6333 PerlMem_free(vmsdir);
6334 set_errno(EVMSERR); set_vaxc_errno(fab_sts);
6340 /* Make sure we are using the right buffer */
6343 my_esa_len = rms_nam_esll(dirnam);
6346 my_esa_len = rms_nam_esl(dirnam);
6348 my_esa[my_esa_len] = '\0';
6349 if (!rms_is_nam_fnb(dirnam, (NAM$M_EXP_DEV | NAM$M_EXP_DIR))) {
6350 cp1 = strchr(my_esa,']');
6351 if (!cp1) cp1 = strchr(my_esa,'>');
6352 if (cp1) { /* Should always be true */
6353 my_esa_len -= cp1 - my_esa - 1;
6354 memmove(my_esa, cp1 + 1, my_esa_len);
6357 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_TYPE)) { /* Was type specified? */
6358 /* Yep; check version while we're at it, if it's there. */
6359 cmplen = rms_is_nam_fnb(dirnam, NAM$M_EXP_VER) ? 6 : 4;
6360 if (strncmp(rms_nam_typel(dirnam), ".DIR;1", cmplen)) {
6361 /* Something other than .DIR[;1]. Bzzt. */
6362 sts = rms_free_search_context(&dirfab);
6366 PerlMem_free(trndir);
6367 PerlMem_free(vmsdir);
6369 set_vaxc_errno(RMS$_DIR);
6374 if (rms_is_nam_fnb(dirnam, NAM$M_EXP_NAME)) {
6375 /* They provided at least the name; we added the type, if necessary, */
6376 strcpy(buf, my_esa);
6377 sts = rms_free_search_context(&dirfab);
6378 PerlMem_free(trndir);
6382 PerlMem_free(vmsdir);
6385 if ((cp1 = strstr(esa,".][000000]")) != NULL) {
6386 for (cp2 = cp1 + 9; *cp2; cp1++,cp2++) *cp1 = *cp2;
6390 if ((cp1 = strrchr(my_esa,']')) == NULL) cp1 = strrchr(my_esa,'>');
6391 if (cp1 == NULL) { /* should never happen */
6392 sts = rms_free_search_context(&dirfab);
6393 PerlMem_free(trndir);
6397 PerlMem_free(vmsdir);
6402 retlen = strlen(my_esa);
6403 cp1 = strrchr(my_esa,'.');
6404 /* ODS-5 directory specifications can have extra "." in them. */
6405 /* Fix-me, can not scan EFS file specifications backwards */
6406 while (cp1 != NULL) {
6407 if ((cp1-1 == my_esa) || (*(cp1-1) != '^'))
6411 while ((cp1 > my_esa) && (*cp1 != '.'))
6418 if ((cp1) != NULL) {
6419 /* There's more than one directory in the path. Just roll back. */
6421 strcpy(buf, my_esa);
6424 if (rms_is_nam_fnb(dirnam, NAM$M_ROOT_DIR)) {
6425 /* Go back and expand rooted logical name */
6426 rms_set_nam_nop(dirnam, NAM$M_SYNCHK | NAM$M_NOCONCEAL);
6427 #ifdef NAM$M_NO_SHORT_UPCASE
6428 if (decc_efs_case_preserve)
6429 rms_set_nam_nop(dirnam, NAM$M_NO_SHORT_UPCASE);
6431 if (!(sys$parse(&dirfab) & STS$K_SUCCESS)) {
6432 sts = rms_free_search_context(&dirfab);
6436 PerlMem_free(trndir);
6437 PerlMem_free(vmsdir);
6439 set_vaxc_errno(dirfab.fab$l_sts);
6443 /* This changes the length of the string of course */
6445 my_esa_len = rms_nam_esll(dirnam);
6447 my_esa_len = rms_nam_esl(dirnam);
6450 retlen = my_esa_len - 9; /* esa - '][' - '].DIR;1' */
6451 cp1 = strstr(my_esa,"][");
6452 if (!cp1) cp1 = strstr(my_esa,"]<");
6453 dirlen = cp1 - my_esa;
6454 memcpy(buf, my_esa, dirlen);
6455 if (!strncmp(cp1+2,"000000]",7)) {
6456 buf[dirlen-1] = '\0';
6457 /* fix-me Not full ODS-5, just extra dots in directories for now */
6458 cp1 = buf + dirlen - 1;
6464 if (*(cp1-1) != '^')
6469 if (*cp1 == '.') *cp1 = ']';
6471 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6472 memmove(cp1+1,"000000]",7);
6476 memmove(buf+dirlen, cp1+2, retlen-dirlen);
6478 /* Convert last '.' to ']' */
6480 while (*cp != '[') {
6483 /* Do not trip on extra dots in ODS-5 directories */
6484 if ((cp1 == buf) || (*(cp1-1) != '^'))
6488 if (*cp1 == '.') *cp1 = ']';
6490 memmove(cp1+8, cp1+1, buf+dirlen-cp1);
6491 memmove(cp1+1,"000000]",7);
6495 else { /* This is a top-level dir. Add the MFD to the path. */
6498 while ((*cp1 != ':') && (*cp1 != '\0')) *(cp2++) = *(cp1++);
6499 strcpy(cp2,":[000000]");
6504 sts = rms_free_search_context(&dirfab);
6505 /* We've set up the string up through the filename. Add the
6506 type and version, and we're done. */
6507 strcat(buf,".DIR;1");
6509 /* $PARSE may have upcased filespec, so convert output to lower
6510 * case if input contained any lowercase characters. */
6511 if (haslower && !decc_efs_case_preserve) __mystrtolower(buf);
6512 PerlMem_free(trndir);
6516 PerlMem_free(vmsdir);
6519 } /* end of int_fileify_dirspec() */
6522 /*{{{ char *fileify_dirspec[_ts](char *dir, char *buf, int * utf8_fl)*/
6523 static char *mp_do_fileify_dirspec(pTHX_ const char *dir,char *buf,int ts, int *utf8_fl)
6525 static char __fileify_retbuf[VMS_MAXRSS];
6526 char * fileified, *ret_spec, *ret_buf;
6530 if (ret_buf == NULL) {
6532 Newx(fileified, VMS_MAXRSS, char);
6533 if (fileified == NULL)
6534 _ckvmssts(SS$_INSFMEM);
6535 ret_buf = fileified;
6537 ret_buf = __fileify_retbuf;
6541 ret_spec = int_fileify_dirspec(dir, ret_buf, utf8_fl);
6543 if (ret_spec == NULL) {
6544 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6546 Safefree(fileified);
6550 } /* end of do_fileify_dirspec() */
6553 /* External entry points */
6554 char *Perl_fileify_dirspec(pTHX_ const char *dir, char *buf)
6555 { return do_fileify_dirspec(dir,buf,0,NULL); }
6556 char *Perl_fileify_dirspec_ts(pTHX_ const char *dir, char *buf)
6557 { return do_fileify_dirspec(dir,buf,1,NULL); }
6558 char *Perl_fileify_dirspec_utf8(pTHX_ const char *dir, char *buf, int * utf8_fl)
6559 { return do_fileify_dirspec(dir,buf,0,utf8_fl); }
6560 char *Perl_fileify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int * utf8_fl)
6561 { return do_fileify_dirspec(dir,buf,1,utf8_fl); }
6563 static char * int_pathify_dirspec_simple(const char * dir, char * buf,
6564 char * v_spec, int v_len, char * r_spec, int r_len,
6565 char * d_spec, int d_len, char * n_spec, int n_len,
6566 char * e_spec, int e_len, char * vs_spec, int vs_len) {
6568 /* VMS specification - Try to do this the simple way */
6569 if ((v_len + r_len > 0) || (d_len > 0)) {
6572 /* No name or extension component, already a directory */
6573 if ((n_len + e_len + vs_len) == 0) {
6578 /* Special case, we may get [.foo]bar instead of [.foo]bar.dir */
6579 /* This results from catfile() being used instead of catdir() */
6580 /* So even though it should not work, we need to allow it */
6582 /* If this is .DIR;1 then do a simple conversion */
6583 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6584 if (is_dir || (e_len == 0) && (d_len > 0)) {
6586 len = v_len + r_len + d_len - 1;
6587 char dclose = d_spec[d_len - 1];
6588 strncpy(buf, dir, len);
6591 strncpy(&buf[len], n_spec, n_len);
6594 buf[len + 1] = '\0';
6599 else if (d_len > 0) {
6600 /* In the olden days, a directory needed to have a .DIR */
6601 /* extension to be a valid directory, but now it could */
6602 /* be a symbolic link */
6604 len = v_len + r_len + d_len - 1;
6605 char dclose = d_spec[d_len - 1];
6606 strncpy(buf, dir, len);
6609 strncpy(&buf[len], n_spec, n_len);
6612 if (decc_efs_charset) {
6615 strncpy(&buf[len], e_spec, e_len);
6618 set_vaxc_errno(RMS$_DIR);
6624 buf[len + 1] = '\0';
6629 set_vaxc_errno(RMS$_DIR);
6635 set_vaxc_errno(RMS$_DIR);
6641 /* Internal routine to make sure or convert a directory to be in a */
6642 /* path specification. No utf8 flag because it is not changed or used */
6643 static char *int_pathify_dirspec(const char *dir, char *buf)
6645 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
6646 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
6647 char * exp_spec, *ret_spec;
6649 unsigned short int trnlnm_iter_count;
6653 if (vms_debug_fileify) {
6655 fprintf(stderr, "int_pathify_dirspec: dir = NULL\n");
6657 fprintf(stderr, "int_pathify_dirspec: dir = %s\n", dir);
6660 /* We may need to lower case the result if we translated */
6661 /* a logical name or got the current working directory */
6664 if (!dir || !*dir) {
6666 set_vaxc_errno(SS$_BADPARAM);
6670 trndir = PerlMem_malloc(VMS_MAXRSS);
6672 _ckvmssts_noperl(SS$_INSFMEM);
6674 /* If no directory specified use the current default */
6676 strcpy(trndir, dir);
6678 getcwd(trndir, VMS_MAXRSS - 1);
6682 /* now deal with bare names that could be logical names */
6683 trnlnm_iter_count = 0;
6684 while (!strpbrk(trndir,"/]:>") && !no_translate_barewords
6685 && simple_trnlnm(trndir, trndir, VMS_MAXRSS)) {
6686 trnlnm_iter_count++;
6688 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER)
6690 trnlen = strlen(trndir);
6692 /* Trap simple rooted lnms, and return lnm:[000000] */
6693 if (!strcmp(trndir+trnlen-2,".]")) {
6695 strcat(buf, ":[000000]");
6696 PerlMem_free(trndir);
6698 if (vms_debug_fileify) {
6699 fprintf(stderr, "int_pathify_dirspec: buf = %s\n", buf);
6705 /* At this point we do not work with *dir, but the copy in *trndir */
6707 if (need_to_lower && !decc_efs_case_preserve) {
6708 /* Legacy mode, lower case the returned value */
6709 __mystrtolower(trndir);
6713 /* Some special cases, '..', '.' */
6715 if ((trndir[0] == '.') && ((trndir[1] == '.') || (trndir[1] == '\0'))) {
6716 /* Force UNIX filespec */
6720 /* Is this Unix or VMS format? */
6721 sts = vms_split_path(trndir, &v_spec, &v_len, &r_spec, &r_len,
6722 &d_spec, &d_len, &n_spec, &n_len, &e_spec,
6723 &e_len, &vs_spec, &vs_len);
6726 /* Just a filename? */
6727 if ((v_len + r_len + d_len) == 0) {
6729 /* Now we have a problem, this could be Unix or VMS */
6730 /* We have to guess. .DIR usually means VMS */
6732 /* In UNIX report mode, the .DIR extension is removed */
6733 /* if one shows up, it is for a non-directory or a directory */
6734 /* in EFS charset mode */
6736 /* So if we are in Unix report mode, assume that this */
6737 /* is a relative Unix directory specification */
6740 if (!decc_filename_unix_report && decc_efs_charset) {
6742 is_dir = is_dir_ext(e_spec, e_len, vs_spec, vs_len);
6745 /* Traditional mode, assume .DIR is directory */
6748 strncpy(&buf[2], n_spec, n_len);
6749 buf[n_len + 2] = ']';
6750 buf[n_len + 3] = '\0';
6751 PerlMem_free(trndir);
6752 if (vms_debug_fileify) {
6754 "int_pathify_dirspec: buf = %s\n",
6764 ret_spec = int_pathify_dirspec_simple(trndir, buf,
6765 v_spec, v_len, r_spec, r_len,
6766 d_spec, d_len, n_spec, n_len,
6767 e_spec, e_len, vs_spec, vs_len);
6769 if (ret_spec != NULL) {
6770 PerlMem_free(trndir);
6771 if (vms_debug_fileify) {
6773 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6778 /* Simple way did not work, which means that a logical name */
6779 /* was present for the directory specification. */
6780 /* Need to use an rmsexpand variant to decode it completely */
6781 exp_spec = PerlMem_malloc(VMS_MAXRSS);
6782 if (exp_spec == NULL)
6783 _ckvmssts_noperl(SS$_INSFMEM);
6785 ret_spec = int_rmsexpand_vms(trndir, exp_spec, PERL_RMSEXPAND_M_LONG);
6786 if (ret_spec != NULL) {
6787 sts = vms_split_path(exp_spec, &v_spec, &v_len,
6788 &r_spec, &r_len, &d_spec, &d_len,
6789 &n_spec, &n_len, &e_spec,
6790 &e_len, &vs_spec, &vs_len);
6792 ret_spec = int_pathify_dirspec_simple(
6793 exp_spec, buf, v_spec, v_len, r_spec, r_len,
6794 d_spec, d_len, n_spec, n_len,
6795 e_spec, e_len, vs_spec, vs_len);
6797 if ((ret_spec != NULL) && (!decc_efs_case_preserve)) {
6798 /* Legacy mode, lower case the returned value */
6799 __mystrtolower(ret_spec);
6802 set_vaxc_errno(RMS$_DIR);
6807 PerlMem_free(exp_spec);
6808 PerlMem_free(trndir);
6809 if (vms_debug_fileify) {
6810 if (ret_spec == NULL)
6811 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6814 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6819 /* Unix specification, Could be trivial conversion */
6821 dir_len = strlen(trndir);
6823 /* If the extended file character set is in effect */
6824 /* then pathify is simple */
6826 if (!decc_efs_charset) {
6827 /* Have to deal with traiing '.dir' or extra '.' */
6828 /* that should not be there in legacy mode, but is */
6834 lastslash = strrchr(trndir, '/');
6835 if (lastslash == NULL)
6842 /* '..' or '.' are valid directory components */
6844 if (lastslash[0] == '.') {
6845 if (lastslash[1] == '\0') {
6847 } else if (lastslash[1] == '.') {
6848 if (lastslash[2] == '\0') {
6851 /* And finally allow '...' */
6852 if ((lastslash[2] == '.') && (lastslash[3] == '\0')) {
6860 lastdot = strrchr(lastslash, '.');
6862 if (lastdot != NULL) {
6865 /* '.dir' is discarded, and any other '.' is invalid */
6866 e_len = strlen(lastdot);
6868 is_dir = is_dir_ext(lastdot, e_len, NULL, 0);
6871 dir_len = dir_len - 4;
6877 strcpy(buf, trndir);
6878 if (buf[dir_len - 1] != '/') {
6880 buf[dir_len + 1] = '\0';
6883 /* Under ODS-2 rules, '.' becomes '_', so fix it up */
6884 if (!decc_efs_charset) {
6887 if (str[0] == '.') {
6890 while ((dots[cnt] == '.') && (cnt < 3))
6893 if ((dots[cnt] == '\0') || (dots[cnt] == '/')) {
6899 for (; *str; ++str) {
6900 while (*str == '/') {
6906 /* Have to skip up to three dots which could be */
6907 /* directories, 3 dots being a VMS extension for Perl */
6910 while ((dots[cnt] == '.') && (cnt < 3)) {
6913 if (dots[cnt] == '\0')
6915 if ((cnt > 1) && (dots[cnt] != '/')) {
6921 /* too many dots? */
6922 if ((cnt == 0) || (cnt > 3)) {
6926 if (!dir_start && (*str == '.')) {
6931 PerlMem_free(trndir);
6933 if (vms_debug_fileify) {
6934 if (ret_spec == NULL)
6935 fprintf(stderr, "int_pathify_dirspec: ret_spec = NULL\n");
6938 "int_pathify_dirspec: ret_spec = %s\n", ret_spec);
6944 /*{{{ char *pathify_dirspec[_ts](char *path, char *buf)*/
6945 static char *mp_do_pathify_dirspec(pTHX_ const char *dir,char *buf, int ts, int * utf8_fl)
6947 static char __pathify_retbuf[VMS_MAXRSS];
6948 char * pathified, *ret_spec, *ret_buf;
6952 if (ret_buf == NULL) {
6954 Newx(pathified, VMS_MAXRSS, char);
6955 if (pathified == NULL)
6956 _ckvmssts(SS$_INSFMEM);
6957 ret_buf = pathified;
6959 ret_buf = __pathify_retbuf;
6963 ret_spec = int_pathify_dirspec(dir, ret_buf);
6965 if (ret_spec == NULL) {
6966 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
6968 Safefree(pathified);
6973 } /* end of do_pathify_dirspec() */
6976 /* External entry points */
6977 char *Perl_pathify_dirspec(pTHX_ const char *dir, char *buf)
6978 { return do_pathify_dirspec(dir,buf,0,NULL); }
6979 char *Perl_pathify_dirspec_ts(pTHX_ const char *dir, char *buf)
6980 { return do_pathify_dirspec(dir,buf,1,NULL); }
6981 char *Perl_pathify_dirspec_utf8(pTHX_ const char *dir, char *buf, int *utf8_fl)
6982 { return do_pathify_dirspec(dir,buf,0,utf8_fl); }
6983 char *Perl_pathify_dirspec_utf8_ts(pTHX_ const char *dir, char *buf, int *utf8_fl)
6984 { return do_pathify_dirspec(dir,buf,1,utf8_fl); }
6986 /* Internal tounixspec routine that does not use a thread context */
6987 /*{{{ char *int_tounixspec[_ts](char *spec, char *buf, int *)*/
6988 static char *int_tounixspec(const char *spec, char *rslt, int * utf8_fl)
6990 char *dirend, *cp1, *cp3, *tmp;
6992 int devlen, dirlen, retlen = VMS_MAXRSS;
6993 int expand = 1; /* guarantee room for leading and trailing slashes */
6994 unsigned short int trnlnm_iter_count;
6996 if (utf8_fl != NULL)
6999 if (vms_debug_fileify) {
7001 fprintf(stderr, "int_tounixspec: spec = NULL\n");
7003 fprintf(stderr, "int_tounixspec: spec = %s\n", spec);
7009 set_vaxc_errno(SS$_BADPARAM);
7012 if (strlen(spec) > (VMS_MAXRSS-1)) {
7014 set_vaxc_errno(SS$_BUFFEROVF);
7018 /* New VMS specific format needs translation
7019 * glob passes filenames with trailing '\n' and expects this preserved.
7021 if (decc_posix_compliant_pathnames) {
7022 if (strncmp(spec, "\"^UP^", 5) == 0) {
7028 tunix = PerlMem_malloc(VMS_MAXRSS);
7029 if (tunix == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7030 strcpy(tunix, spec);
7031 tunix_len = strlen(tunix);
7033 if (tunix[tunix_len - 1] == '\n') {
7034 tunix[tunix_len - 1] = '\"';
7035 tunix[tunix_len] = '\0';
7039 uspec = decc$translate_vms(tunix);
7040 PerlMem_free(tunix);
7041 if ((int)uspec > 0) {
7047 /* If we can not translate it, makemaker wants as-is */
7055 cmp_rslt = 0; /* Presume VMS */
7056 cp1 = strchr(spec, '/');
7060 /* Look for EFS ^/ */
7061 if (decc_efs_charset) {
7062 while (cp1 != NULL) {
7065 /* Found illegal VMS, assume UNIX */
7070 cp1 = strchr(cp1, '/');
7074 /* Look for "." and ".." */
7075 if (decc_filename_unix_report) {
7076 if (spec[0] == '.') {
7077 if ((spec[1] == '\0') || (spec[1] == '\n')) {
7081 if ((spec[1] == '.') && ((spec[2] == '\0') || (spec[2] == '\n'))) {
7087 /* This is already UNIX or at least nothing VMS understands */
7090 if (vms_debug_fileify) {
7091 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7098 dirend = strrchr(spec,']');
7099 if (dirend == NULL) dirend = strrchr(spec,'>');
7100 if (dirend == NULL) dirend = strchr(spec,':');
7101 if (dirend == NULL) {
7103 if (vms_debug_fileify) {
7104 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7109 /* Special case 1 - sys$posix_root = / */
7110 #if __CRTL_VER >= 70000000
7111 if (!decc_disable_posix_root) {
7112 if (strncasecmp(spec, "SYS$POSIX_ROOT:", 15) == 0) {
7120 /* Special case 2 - Convert NLA0: to /dev/null */
7121 #if __CRTL_VER < 70000000
7122 cmp_rslt = strncmp(spec,"NLA0:", 5);
7124 cmp_rslt = strncmp(spec,"nla0:", 5);
7126 cmp_rslt = strncasecmp(spec,"NLA0:", 5);
7128 if (cmp_rslt == 0) {
7129 strcpy(rslt, "/dev/null");
7132 if (spec[6] != '\0') {
7139 /* Also handle special case "SYS$SCRATCH:" */
7140 #if __CRTL_VER < 70000000
7141 cmp_rslt = strncmp(spec,"SYS$SCRATCH:", 12);
7143 cmp_rslt = strncmp(spec,"sys$scratch:", 12);
7145 cmp_rslt = strncasecmp(spec,"SYS$SCRATCH:", 12);
7147 tmp = PerlMem_malloc(VMS_MAXRSS);
7148 if (tmp == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7149 if (cmp_rslt == 0) {
7152 islnm = simple_trnlnm("TMP", tmp, VMS_MAXRSS-1);
7154 strcpy(rslt, "/tmp");
7157 if (spec[12] != '\0') {
7165 if (*cp2 != '[' && *cp2 != '<') {
7168 else { /* the VMS spec begins with directories */
7170 if (*cp2 == ']' || *cp2 == '>') {
7171 *(cp1++) = '.'; *(cp1++) = '/'; *(cp1++) = '\0';
7175 else if ( *cp2 != '^' && *cp2 != '.' && *cp2 != '-') { /* add the implied device */
7176 if (getcwd(tmp, VMS_MAXRSS-1 ,1) == NULL) {
7178 if (vms_debug_fileify) {
7179 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7183 trnlnm_iter_count = 0;
7186 while (*cp3 != ':' && *cp3) cp3++;
7188 if (strchr(cp3,']') != NULL) break;
7189 trnlnm_iter_count++;
7190 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER+1) break;
7191 } while (vmstrnenv(tmp,tmp,0,fildev,0));
7196 *(cp1++) = *(cp3++);
7197 if (cp1 - rslt > (VMS_MAXRSS - 1)) {
7199 set_errno(ENAMETOOLONG);
7200 set_vaxc_errno(SS$_BUFFEROVF);
7201 if (vms_debug_fileify) {
7202 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7204 return NULL; /* No room */
7209 if ((*cp2 == '^')) {
7210 /* EFS file escape, pass the next character as is */
7211 /* Fix me: HEX encoding for Unicode not implemented */
7214 else if ( *cp2 == '.') {
7215 if (*(cp2+1) == '.' && *(cp2+2) == '.') {
7216 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7223 for (; cp2 <= dirend; cp2++) {
7224 if ((*cp2 == '^')) {
7225 /* EFS file escape, pass the next character as is */
7226 /* Fix me: HEX encoding for Unicode not implemented */
7227 *(cp1++) = *(++cp2);
7228 /* An escaped dot stays as is -- don't convert to slash */
7229 if (*cp2 == '.') cp2++;
7233 if (*(cp2+1) == '[') cp2++;
7235 else if (*cp2 == ']' || *cp2 == '>') {
7236 if (*(cp1-1) != '/') *(cp1++) = '/'; /* Don't double after ellipsis */
7238 else if ((*cp2 == '.') && (*cp2-1 != '^')) {
7240 if (*(cp2+1) == ']' || *(cp2+1) == '>') {
7241 while (*(cp2+1) == ']' || *(cp2+1) == '>' ||
7242 *(cp2+1) == '[' || *(cp2+1) == '<') cp2++;
7243 if (!strncmp(cp2,"[000000",7) && (*(cp2+7) == ']' ||
7244 *(cp2+7) == '>' || *(cp2+7) == '.')) cp2 += 7;
7246 else if ( *(cp2+1) == '.' && *(cp2+2) == '.') {
7247 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) ='/';
7251 else if (*cp2 == '-') {
7252 if (*(cp2-1) == '[' || *(cp2-1) == '<' || *(cp2-1) == '.') {
7253 while (*cp2 == '-') {
7255 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '/';
7257 if (*cp2 != '.' && *cp2 != ']' && *cp2 != '>') { /* we don't allow */
7258 /* filespecs like */
7259 set_errno(EINVAL); set_vaxc_errno(RMS$_SYN); /* [fred.--foo.bar] */
7260 if (vms_debug_fileify) {
7261 fprintf(stderr, "int_tounixspec: rslt = NULL\n");
7266 else *(cp1++) = *cp2;
7268 else *(cp1++) = *cp2;
7270 /* Translate the rest of the filename. */
7275 /* Fixme - for compatibility with the CRTL we should be removing */
7276 /* spaces from the file specifications, but this may show that */
7277 /* some tests that were appearing to pass are not really passing */
7283 /* Fix me hex expansions not implemented */
7284 cp2++; /* '^.' --> '.' and other. */
7290 *(cp1++) = *(cp2++);
7295 if (decc_filename_unix_no_version) {
7296 /* Easy, drop the version */
7301 /* Punt - passing the version as a dot will probably */
7302 /* break perl in weird ways, but so did passing */
7303 /* through the ; as a version. Follow the CRTL and */
7304 /* hope for the best. */
7311 /* We will need to fix this properly later */
7312 /* As Perl may be installed on an ODS-5 volume, but not */
7313 /* have the EFS_CHARSET enabled, it still may encounter */
7314 /* filenames with extra dots in them, and a precedent got */
7315 /* set which allowed them to work, that we will uphold here */
7316 /* If extra dots are present in a name and no ^ is on them */
7317 /* VMS assumes that the first one is the extension delimiter */
7318 /* the rest have an implied ^. */
7320 /* this is also a conflict as the . is also a version */
7321 /* delimiter in VMS, */
7323 *(cp1++) = *(cp2++);
7327 /* This is an extension */
7328 if (decc_readdir_dropdotnotype) {
7330 if ((!*cp2) || (*cp2 == ';') || (*cp2 == '.')) {
7331 /* Drop the dot for the extension */
7339 *(cp1++) = *(cp2++);
7344 /* This still leaves /000000/ when working with a
7345 * VMS device root or concealed root.
7351 ulen = strlen(rslt);
7353 /* Get rid of "000000/ in rooted filespecs */
7355 zeros = strstr(rslt, "/000000/");
7356 if (zeros != NULL) {
7358 mlen = ulen - (zeros - rslt) - 7;
7359 memmove(zeros, &zeros[7], mlen);
7366 if (vms_debug_fileify) {
7367 fprintf(stderr, "int_tounixspec: rslt = %s\n", rslt);
7371 } /* end of int_tounixspec() */
7374 /*{{{ char *tounixspec[_ts](char *spec, char *buf, int *)*/
7375 static char *mp_do_tounixspec(pTHX_ const char *spec, char *buf, int ts, int * utf8_fl)
7377 static char __tounixspec_retbuf[VMS_MAXRSS];
7378 char * unixspec, *ret_spec, *ret_buf;
7382 if (ret_buf == NULL) {
7384 Newx(unixspec, VMS_MAXRSS, char);
7385 if (unixspec == NULL)
7386 _ckvmssts(SS$_INSFMEM);
7389 ret_buf = __tounixspec_retbuf;
7393 ret_spec = int_tounixspec(spec, ret_buf, utf8_fl);
7395 if (ret_spec == NULL) {
7396 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
7403 } /* end of do_tounixspec() */
7405 /* External entry points */
7406 char *Perl_tounixspec(pTHX_ const char *spec, char *buf)
7407 { return do_tounixspec(spec,buf,0, NULL); }
7408 char *Perl_tounixspec_ts(pTHX_ const char *spec, char *buf)
7409 { return do_tounixspec(spec,buf,1, NULL); }
7410 char *Perl_tounixspec_utf8(pTHX_ const char *spec, char *buf, int * utf8_fl)
7411 { return do_tounixspec(spec,buf,0, utf8_fl); }
7412 char *Perl_tounixspec_utf8_ts(pTHX_ const char *spec, char *buf, int * utf8_fl)
7413 { return do_tounixspec(spec,buf,1, utf8_fl); }
7415 #if __CRTL_VER >= 70200000 && !defined(__VAX)
7418 This procedure is used to identify if a path is based in either
7419 the old SYS$POSIX_ROOT: or the new 8.3 RMS based POSIX root, and
7420 it returns the OpenVMS format directory for it.
7422 It is expecting specifications of only '/' or '/xxxx/'
7424 If a posix root does not exist, or 'xxxx' is not a directory
7425 in the posix root, it returns a failure.
7427 FIX-ME: xxxx could be in UTF-8 and needs to be returned in VTF-7.
7429 It is used only internally by posix_to_vmsspec_hardway().
7432 static int posix_root_to_vms
7433 (char *vmspath, int vmspath_len,
7434 const char *unixpath,
7435 const int * utf8_fl)
7438 struct FAB myfab = cc$rms_fab;
7439 rms_setup_nam(mynam);
7440 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7441 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
7442 char * esa, * esal, * rsa, * rsal;
7449 unixlen = strlen(unixpath);
7454 #if __CRTL_VER >= 80200000
7455 /* If not a posix spec already, convert it */
7456 if (decc_posix_compliant_pathnames) {
7457 if (strncmp(unixpath,"\"^UP^",5) != 0) {
7458 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7461 /* This is already a VMS specification, no conversion */
7463 strncpy(vmspath,unixpath, vmspath_len);
7472 /* Check to see if this is under the POSIX root */
7473 if (decc_disable_posix_root) {
7477 /* Skip leading / */
7478 if (unixpath[0] == '/') {
7484 strcpy(vmspath,"SYS$POSIX_ROOT:");
7486 /* If this is only the / , or blank, then... */
7487 if (unixpath[0] == '\0') {
7488 /* by definition, this is the answer */
7492 /* Need to look up a directory */
7496 /* Copy and add '^' escape characters as needed */
7499 while (unixpath[i] != 0) {
7502 j += copy_expand_unix_filename_escape
7503 (&vmspath[j], &unixpath[i], &k, utf8_fl);
7507 path_len = strlen(vmspath);
7508 if (vmspath[path_len - 1] == '/')
7510 vmspath[path_len] = ']';
7512 vmspath[path_len] = '\0';
7515 vmspath[vmspath_len] = 0;
7516 if (unixpath[unixlen - 1] == '/')
7518 esal = PerlMem_malloc(VMS_MAXRSS);
7519 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7520 esa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7521 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7522 rsal = PerlMem_malloc(VMS_MAXRSS);
7523 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7524 rsa = PerlMem_malloc(NAM$C_MAXRSS + 1);
7525 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7526 rms_set_fna(myfab, mynam, (char *) vmspath, strlen(vmspath)); /* cast ok */
7527 rms_bind_fab_nam(myfab, mynam);
7528 rms_set_esal(mynam, esa, NAM$C_MAXRSS, esal, VMS_MAXRSS - 1);
7529 rms_set_rsal(mynam, rsa, NAM$C_MAXRSS, rsal, VMS_MAXRSS - 1);
7530 if (decc_efs_case_preserve)
7531 mynam.naml$b_nop |= NAM$M_NO_SHORT_UPCASE;
7532 #ifdef NAML$M_OPEN_SPECIAL
7533 mynam.naml$l_input_flags |= NAML$M_OPEN_SPECIAL;
7536 /* Set up the remaining naml fields */
7537 sts = sys$parse(&myfab);
7539 /* It failed! Try again as a UNIX filespec */
7548 /* get the Device ID and the FID */
7549 sts = sys$search(&myfab);
7551 /* These are no longer needed */
7556 /* on any failure, returned the POSIX ^UP^ filespec */
7561 specdsc.dsc$a_pointer = vmspath;
7562 specdsc.dsc$w_length = vmspath_len;
7564 dvidsc.dsc$a_pointer = &mynam.naml$t_dvi[1];
7565 dvidsc.dsc$w_length = mynam.naml$t_dvi[0];
7566 sts = lib$fid_to_name
7567 (&dvidsc, mynam.naml$w_fid, &specdsc, &specdsc.dsc$w_length);
7569 /* on any failure, returned the POSIX ^UP^ filespec */
7571 /* This can happen if user does not have permission to read directories */
7572 if (strncmp(unixpath,"\"^UP^",5) != 0)
7573 sprintf(vmspath,"\"^UP^%s\"",unixpath);
7575 strcpy(vmspath, unixpath);
7578 vmspath[specdsc.dsc$w_length] = 0;
7580 /* Are we expecting a directory? */
7581 if (dir_flag != 0) {
7587 i = specdsc.dsc$w_length - 1;
7591 /* Version must be '1' */
7592 if (vmspath[i--] != '1')
7594 /* Version delimiter is one of ".;" */
7595 if ((vmspath[i] != '.') && (vmspath[i] != ';'))
7598 if (vmspath[i--] != 'R')
7600 if (vmspath[i--] != 'I')
7602 if (vmspath[i--] != 'D')
7604 if (vmspath[i--] != '.')
7606 eptr = &vmspath[i+1];
7608 if ((vmspath[i] == ']') || (vmspath[i] == '>')) {
7609 if (vmspath[i-1] != '^') {
7617 /* Get rid of 6 imaginary zero directory filename */
7618 vmspath[i+1] = '\0';
7622 if (vmspath[i] == '0')
7636 /* /dev/mumble needs to be handled special.
7637 /dev/null becomes NLA0:, And there is the potential for other stuff
7638 like /dev/tty which may need to be mapped to something.
7642 slash_dev_special_to_vms
7643 (const char * unixptr,
7653 nextslash = strchr(unixptr, '/');
7654 len = strlen(unixptr);
7655 if (nextslash != NULL)
7656 len = nextslash - unixptr;
7657 cmp = strncmp("null", unixptr, 5);
7659 if (vmspath_len >= 6) {
7660 strcpy(vmspath, "_NLA0:");
7667 /* The built in routines do not understand perl's special needs, so
7668 doing a manual conversion from UNIX to VMS
7670 If the utf8_fl is not null and points to a non-zero value, then
7671 treat 8 bit characters as UTF-8.
7673 The sequence starting with '$(' and ending with ')' will be passed
7674 through with out interpretation instead of being escaped.
7677 static int posix_to_vmsspec_hardway
7678 (char *vmspath, int vmspath_len,
7679 const char *unixpath,
7684 const char *unixptr;
7685 const char *unixend;
7687 const char *lastslash;
7688 const char *lastdot;
7694 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
7695 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
7697 if (utf8_fl != NULL)
7703 /* Ignore leading "/" characters */
7704 while((unixptr[0] == '/') && (unixptr[1] == '/')) {
7707 unixlen = strlen(unixptr);
7709 /* Do nothing with blank paths */
7716 /* This could have a "^UP^ on the front */
7717 if (strncmp(unixptr,"\"^UP^",5) == 0) {
7723 lastslash = strrchr(unixptr,'/');
7724 lastdot = strrchr(unixptr,'.');
7725 unixend = strrchr(unixptr,'\"');
7726 if (!quoted || !((unixend != NULL) && (unixend[1] == '\0'))) {
7727 unixend = unixptr + unixlen;
7730 /* last dot is last dot or past end of string */
7731 if (lastdot == NULL)
7732 lastdot = unixptr + unixlen;
7734 /* if no directories, set last slash to beginning of string */
7735 if (lastslash == NULL) {
7736 lastslash = unixptr;
7739 /* Watch out for trailing "." after last slash, still a directory */
7740 if ((lastslash[1] == '.') && (lastslash[2] == '\0')) {
7741 lastslash = unixptr + unixlen;
7744 /* Watch out for traiing ".." after last slash, still a directory */
7745 if ((lastslash[1] == '.')&&(lastslash[2] == '.')&&(lastslash[3] == '\0')) {
7746 lastslash = unixptr + unixlen;
7749 /* dots in directories are aways escaped */
7750 if (lastdot < lastslash)
7751 lastdot = unixptr + unixlen;
7754 /* if (unixptr < lastslash) then we are in a directory */
7761 /* Start with the UNIX path */
7762 if (*unixptr != '/') {
7763 /* relative paths */
7765 /* If allowing logical names on relative pathnames, then handle here */
7766 if ((unixptr[0] != '.') && !decc_disable_to_vms_logname_translation &&
7767 !decc_posix_compliant_pathnames) {
7773 /* Find the next slash */
7774 nextslash = strchr(unixptr,'/');
7776 esa = PerlMem_malloc(vmspath_len);
7777 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7779 trn = PerlMem_malloc(VMS_MAXRSS);
7780 if (trn == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7782 if (nextslash != NULL) {
7784 seg_len = nextslash - unixptr;
7785 strncpy(esa, unixptr, seg_len);
7789 strcpy(esa, unixptr);
7790 seg_len = strlen(unixptr);
7792 /* trnlnm(section) */
7793 islnm = vmstrnenv(esa, trn, 0, fildev, 0);
7796 /* Now fix up the directory */
7798 /* Split up the path to find the components */
7799 sts = vms_split_path
7818 /* A logical name must be a directory or the full
7819 specification. It is only a full specification if
7820 it is the only component */
7821 if ((unixptr[seg_len] == '\0') ||
7822 (unixptr[seg_len+1] == '\0')) {
7824 /* Is a directory being required? */
7825 if (((n_len + e_len) != 0) && (dir_flag !=0)) {
7826 /* Not a logical name */
7831 if ((unixptr[seg_len] == '/') || (dir_flag != 0)) {
7832 /* This must be a directory */
7833 if (((n_len + e_len) == 0)&&(seg_len <= vmspath_len)) {
7834 strcpy(vmsptr, esa);
7835 vmslen=strlen(vmsptr);
7836 vmsptr[vmslen] = ':';
7838 vmsptr[vmslen] = '\0';
7846 /* must be dev/directory - ignore version */
7847 if ((n_len + e_len) != 0)
7850 /* transfer the volume */
7851 if (v_len > 0 && ((v_len + vmslen) < vmspath_len)) {
7852 strncpy(vmsptr, v_spec, v_len);
7858 /* unroot the rooted directory */
7859 if ((r_len > 0) && ((r_len + d_len + vmslen) < vmspath_len)) {
7861 r_spec[r_len - 1] = ']';
7863 /* This should not be there, but nothing is perfect */
7865 cmp = strcmp(&r_spec[1], "000000.");
7875 strncpy(vmsptr, r_spec, r_len);
7881 /* Bring over the directory. */
7883 ((d_len + vmslen) < vmspath_len)) {
7885 d_spec[d_len - 1] = ']';
7887 cmp = strcmp(&d_spec[1], "000000.");
7898 /* Remove the redundant root */
7906 strncpy(vmsptr, d_spec, d_len);
7920 if (lastslash > unixptr) {
7923 /* skip leading ./ */
7925 while ((unixptr[0] == '.') && (unixptr[1] == '/')) {
7931 /* Are we still in a directory? */
7932 if (unixptr <= lastslash) {
7937 /* if not backing up, then it is relative forward. */
7938 if (!((*unixptr == '.') && (unixptr[1] == '.') &&
7939 ((unixptr[2] == '/') || (&unixptr[2] == unixend)))) {
7947 /* Perl wants an empty directory here to tell the difference
7948 * between a DCL commmand and a filename
7957 /* Handle two special files . and .. */
7958 if (unixptr[0] == '.') {
7959 if (&unixptr[1] == unixend) {
7966 if ((unixptr[1] == '.') && (&unixptr[2] == unixend)) {
7977 else { /* Absolute PATH handling */
7981 /* Need to find out where root is */
7983 /* In theory, this procedure should never get an absolute POSIX pathname
7984 * that can not be found on the POSIX root.
7985 * In practice, that can not be relied on, and things will show up
7986 * here that are a VMS device name or concealed logical name instead.
7987 * So to make things work, this procedure must be tolerant.
7989 esa = PerlMem_malloc(vmspath_len);
7990 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
7993 nextslash = strchr(&unixptr[1],'/');
7995 if (nextslash != NULL) {
7997 seg_len = nextslash - &unixptr[1];
7998 strncpy(vmspath, unixptr, seg_len + 1);
7999 vmspath[seg_len+1] = 0;
8002 cmp = strncmp(vmspath, "dev", 4);
8004 sts = slash_dev_special_to_vms(unixptr, vmspath, vmspath_len);
8005 if (sts = SS$_NORMAL)
8009 sts = posix_root_to_vms(esa, vmspath_len, vmspath, utf8_fl);
8012 if ($VMS_STATUS_SUCCESS(sts)) {
8013 /* This is verified to be a real path */
8015 sts = posix_root_to_vms(esa, vmspath_len, "/", NULL);
8016 if ($VMS_STATUS_SUCCESS(sts)) {
8017 strcpy(vmspath, esa);
8018 vmslen = strlen(vmspath);
8019 vmsptr = vmspath + vmslen;
8021 if (unixptr < lastslash) {
8030 cmp = strcmp(rptr,"000000.");
8035 } /* removing 6 zeros */
8036 } /* vmslen < 7, no 6 zeros possible */
8037 } /* Not in a directory */
8038 } /* Posix root found */
8040 /* No posix root, fall back to default directory */
8041 strcpy(vmspath, "SYS$DISK:[");
8042 vmsptr = &vmspath[10];
8044 if (unixptr > lastslash) {
8053 } /* end of verified real path handling */
8058 /* Ok, we have a device or a concealed root that is not in POSIX
8059 * or we have garbage. Make the best of it.
8062 /* Posix to VMS destroyed this, so copy it again */
8063 strncpy(vmspath, &unixptr[1], seg_len);
8064 vmspath[seg_len] = 0;
8066 vmsptr = &vmsptr[vmslen];
8069 /* Now do we need to add the fake 6 zero directory to it? */
8071 if ((*lastslash == '/') && (nextslash < lastslash)) {
8072 /* No there is another directory */
8079 /* now we have foo:bar or foo:[000000]bar to decide from */
8080 islnm = vmstrnenv(vmspath, esa, 0, fildev, 0);
8082 if (!islnm && !decc_posix_compliant_pathnames) {
8084 cmp = strncmp("bin", vmspath, 4);
8086 /* bin => SYS$SYSTEM: */
8087 islnm = vmstrnenv("SYS$SYSTEM:", esa, 0, fildev, 0);
8090 /* tmp => SYS$SCRATCH: */
8091 cmp = strncmp("tmp", vmspath, 4);
8093 islnm = vmstrnenv("SYS$SCRATCH:", esa, 0, fildev, 0);
8098 trnend = islnm ? islnm - 1 : 0;
8100 /* if this was a logical name, ']' or '>' must be present */
8101 /* if not a logical name, then assume a device and hope. */
8102 islnm = trnend ? (esa[trnend] == ']' || esa[trnend] == '>') : 0;
8104 /* if log name and trailing '.' then rooted - treat as device */
8105 add_6zero = islnm ? (esa[trnend-1] == '.') : 0;
8107 /* Fix me, if not a logical name, a device lookup should be
8108 * done to see if the device is file structured. If the device
8109 * is not file structured, the 6 zeros should not be put on.
8111 * As it is, perl is occasionally looking for dev:[000000]tty.
8112 * which looks a little strange.
8114 * Not that easy to detect as "/dev" may be file structured with
8115 * special device files.
8118 if ((add_6zero == 0) && (*nextslash == '/') &&
8119 (&nextslash[1] == unixend)) {
8120 /* No real directory present */
8125 /* Put the device delimiter on */
8128 unixptr = nextslash;
8131 /* Start directory if needed */
8132 if (!islnm || add_6zero) {
8138 /* add fake 000000] if needed */
8151 } /* non-POSIX translation */
8153 } /* End of relative/absolute path handling */
8155 while ((unixptr <= unixend) && (vmslen < vmspath_len)){
8162 if (dir_start != 0) {
8164 /* First characters in a directory are handled special */
8165 while ((*unixptr == '/') ||
8166 ((*unixptr == '.') &&
8167 ((unixptr[1]=='.') || (unixptr[1]=='/') ||
8168 (&unixptr[1]==unixend)))) {
8173 /* Skip redundant / in specification */
8174 while ((*unixptr == '/') && (dir_start != 0)) {
8177 if (unixptr == lastslash)
8180 if (unixptr == lastslash)
8183 /* Skip redundant ./ characters */
8184 while ((*unixptr == '.') &&
8185 ((unixptr[1] == '/')||(&unixptr[1] == unixend))) {
8188 if (unixptr == lastslash)
8190 if (*unixptr == '/')
8193 if (unixptr == lastslash)
8196 /* Skip redundant ../ characters */
8197 while ((*unixptr == '.') && (unixptr[1] == '.') &&
8198 ((unixptr[2] == '/') || (&unixptr[2] == unixend))) {
8199 /* Set the backing up flag */
8205 unixptr++; /* first . */
8206 unixptr++; /* second . */
8207 if (unixptr == lastslash)
8209 if (*unixptr == '/') /* The slash */
8212 if (unixptr == lastslash)
8215 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8216 /* Not needed when VMS is pretending to be UNIX. */
8218 /* Is this loop stuck because of too many dots? */
8219 if (loop_flag == 0) {
8220 /* Exit the loop and pass the rest through */
8225 /* Are we done with directories yet? */
8226 if (unixptr >= lastslash) {
8228 /* Watch out for trailing dots */
8237 if (*unixptr == '/')
8241 /* Have we stopped backing up? */
8246 /* dir_start continues to be = 1 */
8248 if (*unixptr == '-') {
8250 *vmsptr++ = *unixptr++;
8254 /* Now are we done with directories yet? */
8255 if (unixptr >= lastslash) {
8257 /* Watch out for trailing dots */
8273 if (unixptr >= unixend)
8276 /* Normal characters - More EFS work probably needed */
8282 /* remove multiple / */
8283 while (unixptr[1] == '/') {
8286 if (unixptr == lastslash) {
8287 /* Watch out for trailing dots */
8299 /* To do: Perl expects /.../ to be translated to [...] on VMS */
8300 /* Not needed when VMS is pretending to be UNIX. */
8304 if (unixptr != unixend)
8309 if ((unixptr < lastdot) || (unixptr < lastslash) ||
8310 (&unixptr[1] == unixend)) {
8316 /* trailing dot ==> '^..' on VMS */
8317 if (unixptr == unixend) {
8325 *vmsptr++ = *unixptr++;
8329 if (quoted && (&unixptr[1] == unixend)) {
8333 in_cnt = copy_expand_unix_filename_escape
8334 (vmsptr, unixptr, &out_cnt, utf8_fl);
8344 in_cnt = copy_expand_unix_filename_escape
8345 (vmsptr, unixptr, &out_cnt, utf8_fl);
8352 /* Make sure directory is closed */
8353 if (unixptr == lastslash) {
8355 vmsptr2 = vmsptr - 1;
8357 if (*vmsptr2 != ']') {
8360 /* directories do not end in a dot bracket */
8361 if (*vmsptr2 == '.') {
8365 if (*vmsptr2 != '^') {
8366 vmsptr--; /* back up over the dot */
8374 /* Add a trailing dot if a file with no extension */
8375 vmsptr2 = vmsptr - 1;
8377 (*vmsptr2 != ']') && (*vmsptr2 != '*') && (*vmsptr2 != '%') &&
8378 (*vmsptr2 != ')') && (*lastdot != '.')) {
8389 /* Eventual routine to convert a UTF-8 specification to VTF-7. */
8390 static char * utf8_to_vtf7(char * rslt, const char * path, int *utf8_fl)
8395 /* If a UTF8 flag is being passed, honor it */
8397 if (utf8_fl != NULL) {
8398 utf8_flag = *utf8_fl;
8403 /* If there is a possibility of UTF8, then if any UTF8 characters
8404 are present, then they must be converted to VTF-7
8406 result = strcpy(rslt, path); /* FIX-ME */
8409 result = strcpy(rslt, path);
8416 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8417 static char *int_tovmsspec
8418 (const char *path, char *rslt, int dir_flag, int * utf8_flag) {
8424 unsigned long int infront = 0, hasdir = 1;
8427 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
8428 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
8430 if (vms_debug_fileify) {
8432 fprintf(stderr, "int_tovmsspec: path = NULL\n");
8434 fprintf(stderr, "int_tovmsspec: path = %s\n", path);
8438 /* If we fail, we should be setting errno */
8440 set_vaxc_errno(SS$_BADPARAM);
8443 rslt_len = VMS_MAXRSS-1;
8445 /* '.' and '..' are "[]" and "[-]" for a quick check */
8446 if (path[0] == '.') {
8447 if (path[1] == '\0') {
8449 if (utf8_flag != NULL)
8454 if (path[1] == '.' && path[2] == '\0') {
8456 if (utf8_flag != NULL)
8463 /* Posix specifications are now a native VMS format */
8464 /*--------------------------------------------------*/
8465 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8466 if (decc_posix_compliant_pathnames) {
8467 if (strncmp(path,"\"^UP^",5) == 0) {
8468 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8474 /* This is really the only way to see if this is already in VMS format */
8475 sts = vms_split_path
8490 /* FIX-ME - If dir_flag is non-zero, then this is a mp_do_vmspath()
8491 replacement, because the above parse just took care of most of
8492 what is needed to do vmspath when the specification is already
8495 And if it is not already, it is easier to do the conversion as
8496 part of this routine than to call this routine and then work on
8500 /* If VMS punctuation was found, it is already VMS format */
8501 if ((v_len != 0) || (r_len != 0) || (d_len != 0) || (vs_len != 0)) {
8502 if (utf8_flag != NULL)
8505 if (vms_debug_fileify) {
8506 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8510 /* Now, what to do with trailing "." cases where there is no
8511 extension? If this is a UNIX specification, and EFS characters
8512 are enabled, then the trailing "." should be converted to a "^.".
8513 But if this was already a VMS specification, then it should be
8516 So in the case of ambiguity, leave the specification alone.
8520 /* If there is a possibility of UTF8, then if any UTF8 characters
8521 are present, then they must be converted to VTF-7
8523 if (utf8_flag != NULL)
8526 if (vms_debug_fileify) {
8527 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8532 dirend = strrchr(path,'/');
8534 if (dirend == NULL) {
8538 /* If we get here with no UNIX directory delimiters, then this is
8539 not a complete file specification, either garbage a UNIX glob
8540 specification that can not be converted to a VMS wildcard, or
8541 it a UNIX shell macro. MakeMaker wants shell macros passed
8544 utf8 flag setting needs to be preserved.
8549 macro_start = strchr(path,'$');
8550 if (macro_start != NULL) {
8551 if (macro_start[1] == '(') {
8555 if ((decc_efs_charset == 0) || (has_macro)) {
8557 if (vms_debug_fileify) {
8558 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8564 /* If POSIX mode active, handle the conversion */
8565 #if __CRTL_VER >= 80200000 && !defined(__VAX)
8566 if (decc_efs_charset) {
8567 posix_to_vmsspec_hardway(rslt, rslt_len, path, dir_flag, utf8_flag);
8568 if (vms_debug_fileify) {
8569 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8575 if (*(dirend+1) == '.') { /* do we have trailing "/." or "/.." or "/..."? */
8576 if (!*(dirend+2)) dirend +=2;
8577 if (*(dirend+2) == '.' && !*(dirend+3)) dirend += 3;
8578 if (decc_efs_charset == 0) {
8579 if (*(dirend+2) == '.' && *(dirend+3) == '.' && !*(dirend+4)) dirend += 4;
8585 lastdot = strrchr(cp2,'.');
8591 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8593 if (decc_disable_posix_root) {
8594 strcpy(rslt,"sys$disk:[000000]");
8597 strcpy(rslt,"sys$posix_root:[000000]");
8599 if (utf8_flag != NULL)
8601 if (vms_debug_fileify) {
8602 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8606 while (*(++cp2) != '/' && *cp2) *(cp1++) = *cp2;
8608 trndev = PerlMem_malloc(VMS_MAXRSS);
8609 if (trndev == NULL) _ckvmssts_noperl(SS$_INSFMEM);
8610 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8612 /* DECC special handling */
8614 if (strcmp(rslt,"bin") == 0) {
8615 strcpy(rslt,"sys$system");
8618 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8620 else if (strcmp(rslt,"tmp") == 0) {
8621 strcpy(rslt,"sys$scratch");
8624 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8626 else if (!decc_disable_posix_root) {
8627 strcpy(rslt, "sys$posix_root");
8631 while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
8632 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8634 else if (strcmp(rslt,"dev") == 0) {
8635 if (strncmp(cp2,"/null", 5) == 0) {
8636 if ((cp2[5] == 0) || (cp2[5] == '/')) {
8637 strcpy(rslt,"NLA0");
8641 islnm = simple_trnlnm(rslt,trndev,VMS_MAXRSS-1);
8647 trnend = islnm ? strlen(trndev) - 1 : 0;
8648 islnm = trnend ? (trndev[trnend] == ']' || trndev[trnend] == '>') : 0;
8649 rooted = islnm ? (trndev[trnend-1] == '.') : 0;
8650 /* If the first element of the path is a logical name, determine
8651 * whether it has to be translated so we can add more directories. */
8652 if (!islnm || rooted) {
8655 if (cp2 == dirend) while (infront++ < 6) *(cp1++) = '0';
8659 if (cp2 != dirend) {
8660 strcpy(rslt,trndev);
8661 cp1 = rslt + trnend;
8668 if (decc_disable_posix_root) {
8674 PerlMem_free(trndev);
8679 if (*(cp2+1) == '/' || *(cp2+1) == '\0') {
8680 cp2 += 2; /* skip over "./" - it's redundant */
8681 *(cp1++) = '.'; /* but it does indicate a relative dirspec */
8683 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8684 *(cp1++) = '-'; /* "../" --> "-" */
8687 else if (*(cp2+1) == '.' && *(cp2+2) == '.' &&
8688 (*(cp2+3) == '/' || *(cp2+3) == '\0')) {
8689 *(cp1++) = '.'; *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8690 if (!*(cp2+4)) *(cp1++) = '.'; /* Simulate trailing '/' for later */
8693 else if ((cp2 != lastdot) || (lastdot < dirend)) {
8694 /* Escape the extra dots in EFS file specifications */
8697 if (cp2 > dirend) cp2 = dirend;
8699 else *(cp1++) = '.';
8701 for (; cp2 < dirend; cp2++) {
8703 if (*(cp2-1) == '/') continue;
8704 if (*(cp1-1) != '.') *(cp1++) = '.';
8707 else if (!infront && *cp2 == '.') {
8708 if (cp2+1 == dirend || *(cp2+1) == '\0') { cp2++; break; }
8709 else if (*(cp2+1) == '/') cp2++; /* skip over "./" - it's redundant */
8710 else if (*(cp2+1) == '.' && (*(cp2+2) == '/' || *(cp2+2) == '\0')) {
8711 if (*(cp1-1) == '-' || *(cp1-1) == '[') *(cp1++) = '-'; /* handle "../" */
8712 else if (*(cp1-2) == '[') *(cp1-1) = '-';
8713 else { /* back up over previous directory name */
8715 while (*(cp1-1) != '.' && *(cp1-1) != '[') cp1--;
8716 if (*(cp1-1) == '[') {
8717 memcpy(cp1,"000000.",7);
8722 if (cp2 == dirend) break;
8724 else if ( *(cp2+1) == '.' && *(cp2+2) == '.' &&
8725 (*(cp2+3) == '/' || *(cp2+3) == '\0') ) {
8726 if (*(cp1-1) != '.') *(cp1++) = '.'; /* May already have 1 from '/' */
8727 *(cp1++) = '.'; *(cp1++) = '.'; /* ".../" --> "..." */
8729 *(cp1++) = '.'; /* Simulate trailing '/' */
8730 cp2 += 2; /* for loop will incr this to == dirend */
8732 else cp2 += 3; /* Trailing '/' was there, so skip it, too */
8735 if (decc_efs_charset == 0)
8736 *(cp1++) = '_'; /* fix up syntax - '.' in name not allowed */
8738 *(cp1++) = '^'; /* fix up syntax - '.' in name is allowed */
8744 if (!infront && *(cp1-1) == '-') *(cp1++) = '.';
8746 if (decc_efs_charset == 0)
8753 else *(cp1++) = *cp2;
8757 if (*(cp1-1) == '.') cp1--; /* Unix spec ending in '/' ==> trailing '.' */
8758 if (hasdir) *(cp1++) = ']';
8759 if (*cp2) cp2++; /* check in case we ended with trailing '..' */
8760 /* fixme for ODS5 */
8767 if (decc_efs_charset == 0)
8778 if (((cp2 < lastdot) || (cp2[1] == '\0')) &&
8779 decc_readdir_dropdotnotype) {
8784 /* trailing dot ==> '^..' on VMS */
8791 *(cp1++) = *(cp2++);
8796 /* This could be a macro to be passed through */
8797 *(cp1++) = *(cp2++);
8799 const char * save_cp2;
8803 /* paranoid check */
8809 *(cp1++) = *(cp2++);
8810 if (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8811 *(cp1++) = *(cp2++);
8812 while (isalnum(*cp2) || (*cp2 == '.') || (*cp2 == '_')) {
8813 *(cp1++) = *(cp2++);
8816 *(cp1++) = *(cp2++);
8820 if (is_macro == 0) {
8821 /* Not really a macro - never mind */
8834 /* Don't escape again if following character is
8835 * already something we escape.
8837 if (strchr("\"~`!#%^&()=+\'@[]{}:\\|<>_.", *(cp2+1))) {
8838 *(cp1++) = *(cp2++);
8841 /* But otherwise fall through and escape it. */
8859 *(cp1++) = *(cp2++);
8862 /* FIXME: This needs fixing as Perl is putting ".dir;" on UNIX filespecs
8863 * which is wrong. UNIX notation should be ".dir." unless
8864 * the DECC$FILENAME_UNIX_NO_VERSION is enabled.
8865 * changing this behavior could break more things at this time.
8866 * efs character set effectively does not allow "." to be a version
8867 * delimiter as a further complication about changing this.
8869 if (decc_filename_unix_report != 0) {
8872 *(cp1++) = *(cp2++);
8875 *(cp1++) = *(cp2++);
8878 if ((no_type_seen == 1) && decc_readdir_dropdotnotype) {
8882 /* Fix me for "^]", but that requires making sure that you do
8883 * not back up past the start of the filename
8885 if ((*lcp1 != ']') && (*lcp1 != '*') && (*lcp1 != '%'))
8890 if (utf8_flag != NULL)
8892 if (vms_debug_fileify) {
8893 fprintf(stderr, "int_tovmsspec: rslt = %s\n", rslt);
8897 } /* end of int_tovmsspec() */
8900 /*{{{ char *tovmsspec[_ts](char *path, char *buf, int * utf8_flag)*/
8901 static char *mp_do_tovmsspec
8902 (pTHX_ const char *path, char *buf, int ts, int dir_flag, int * utf8_flag) {
8903 static char __tovmsspec_retbuf[VMS_MAXRSS];
8904 char * vmsspec, *ret_spec, *ret_buf;
8908 if (ret_buf == NULL) {
8910 Newx(vmsspec, VMS_MAXRSS, char);
8911 if (vmsspec == NULL)
8912 _ckvmssts(SS$_INSFMEM);
8915 ret_buf = __tovmsspec_retbuf;
8919 ret_spec = int_tovmsspec(path, ret_buf, 0, utf8_flag);
8921 if (ret_spec == NULL) {
8922 /* Cleanup on isle 5, if this is thread specific we need to deallocate */
8929 } /* end of mp_do_tovmsspec() */
8931 /* External entry points */
8932 char *Perl_tovmsspec(pTHX_ const char *path, char *buf)
8933 { return do_tovmsspec(path,buf,0,NULL); }
8934 char *Perl_tovmsspec_ts(pTHX_ const char *path, char *buf)
8935 { return do_tovmsspec(path,buf,1,NULL); }
8936 char *Perl_tovmsspec_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
8937 { return do_tovmsspec(path,buf,0,utf8_fl); }
8938 char *Perl_tovmsspec_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
8939 { return do_tovmsspec(path,buf,1,utf8_fl); }
8941 /*{{{ char *int_tovmspath(char *path, char *buf, const int *)*/
8942 /* Internal routine for use with out an explict context present */
8943 static char * int_tovmspath(const char *path, char *buf, int * utf8_fl) {
8945 char * ret_spec, *pathified;
8950 pathified = PerlMem_malloc(VMS_MAXRSS);
8951 if (pathified == NULL)
8952 _ckvmssts_noperl(SS$_INSFMEM);
8954 ret_spec = int_pathify_dirspec(path, pathified);
8956 if (ret_spec == NULL) {
8957 PerlMem_free(pathified);
8961 ret_spec = int_tovmsspec(pathified, buf, 0, utf8_fl);
8963 PerlMem_free(pathified);
8968 /*{{{ char *tovmspath[_ts](char *path, char *buf, const int *)*/
8969 static char *mp_do_tovmspath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
8970 static char __tovmspath_retbuf[VMS_MAXRSS];
8972 char *pathified, *vmsified, *cp;
8974 if (path == NULL) return NULL;
8975 pathified = PerlMem_malloc(VMS_MAXRSS);
8976 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
8977 if (int_pathify_dirspec(path, pathified) == NULL) {
8978 PerlMem_free(pathified);
8984 Newx(vmsified, VMS_MAXRSS, char);
8985 if (do_tovmsspec(pathified, buf ? buf : vmsified, 0, NULL) == NULL) {
8986 PerlMem_free(pathified);
8987 if (vmsified) Safefree(vmsified);
8990 PerlMem_free(pathified);
8995 vmslen = strlen(vmsified);
8996 Newx(cp,vmslen+1,char);
8997 memcpy(cp,vmsified,vmslen);
9003 strcpy(__tovmspath_retbuf,vmsified);
9005 return __tovmspath_retbuf;
9008 } /* end of do_tovmspath() */
9010 /* External entry points */
9011 char *Perl_tovmspath(pTHX_ const char *path, char *buf)
9012 { return do_tovmspath(path,buf,0, NULL); }
9013 char *Perl_tovmspath_ts(pTHX_ const char *path, char *buf)
9014 { return do_tovmspath(path,buf,1, NULL); }
9015 char *Perl_tovmspath_utf8(pTHX_ const char *path, char *buf, int *utf8_fl)
9016 { return do_tovmspath(path,buf,0,utf8_fl); }
9017 char *Perl_tovmspath_utf8_ts(pTHX_ const char *path, char *buf, int *utf8_fl)
9018 { return do_tovmspath(path,buf,1,utf8_fl); }
9021 /*{{{ char *tounixpath[_ts](char *path, char *buf, int * utf8_fl)*/
9022 static char *mp_do_tounixpath(pTHX_ const char *path, char *buf, int ts, int * utf8_fl) {
9023 static char __tounixpath_retbuf[VMS_MAXRSS];
9025 char *pathified, *unixified, *cp;
9027 if (path == NULL) return NULL;
9028 pathified = PerlMem_malloc(VMS_MAXRSS);
9029 if (pathified == NULL) _ckvmssts(SS$_INSFMEM);
9030 if (int_pathify_dirspec(path, pathified) == NULL) {
9031 PerlMem_free(pathified);
9037 Newx(unixified, VMS_MAXRSS, char);
9039 if (do_tounixspec(pathified,buf ? buf : unixified,0,NULL) == NULL) {
9040 PerlMem_free(pathified);
9041 if (unixified) Safefree(unixified);
9044 PerlMem_free(pathified);
9049 unixlen = strlen(unixified);
9050 Newx(cp,unixlen+1,char);
9051 memcpy(cp,unixified,unixlen);
9053 Safefree(unixified);
9057 strcpy(__tounixpath_retbuf,unixified);
9058 Safefree(unixified);
9059 return __tounixpath_retbuf;
9062 } /* end of do_tounixpath() */
9064 /* External entry points */
9065 char *Perl_tounixpath(pTHX_ const char *path, char *buf)
9066 { return do_tounixpath(path,buf,0,NULL); }
9067 char *Perl_tounixpath_ts(pTHX_ const char *path, char *buf)
9068 { return do_tounixpath(path,buf,1,NULL); }
9069 char *Perl_tounixpath_utf8(pTHX_ const char *path, char *buf, int * utf8_fl)
9070 { return do_tounixpath(path,buf,0,utf8_fl); }
9071 char *Perl_tounixpath_utf8_ts(pTHX_ const char *path, char *buf, int * utf8_fl)
9072 { return do_tounixpath(path,buf,1,utf8_fl); }
9075 * @(#)argproc.c 2.2 94/08/16 Mark Pizzolato (mark AT infocomm DOT com)
9077 *****************************************************************************
9079 * Copyright (C) 1989-1994, 2007 by *
9080 * Mark Pizzolato - INFO COMM, Danville, California (510) 837-5600 *
9082 * Permission is hereby granted for the reproduction of this software *
9083 * on condition that this copyright notice is included in source *
9084 * distributions of the software. The code may be modified and *
9085 * distributed under the same terms as Perl itself. *
9087 * 27-Aug-1994 Modified for inclusion in perl5 *
9088 * by Charles Bailey (bailey AT newman DOT upenn DOT edu) *
9089 *****************************************************************************
9093 * getredirection() is intended to aid in porting C programs
9094 * to VMS (Vax-11 C). The native VMS environment does not support
9095 * '>' and '<' I/O redirection, or command line wild card expansion,
9096 * or a command line pipe mechanism using the '|' AND background
9097 * command execution '&'. All of these capabilities are provided to any
9098 * C program which calls this procedure as the first thing in the
9100 * The piping mechanism will probably work with almost any 'filter' type
9101 * of program. With suitable modification, it may useful for other
9102 * portability problems as well.
9104 * Author: Mark Pizzolato (mark AT infocomm DOT com)
9108 struct list_item *next;
9112 static void add_item(struct list_item **head,
9113 struct list_item **tail,
9117 static void mp_expand_wild_cards(pTHX_ char *item,
9118 struct list_item **head,
9119 struct list_item **tail,
9122 static int background_process(pTHX_ int argc, char **argv);
9124 static void pipe_and_fork(pTHX_ char **cmargv);
9126 /*{{{ void getredirection(int *ac, char ***av)*/
9128 mp_getredirection(pTHX_ int *ac, char ***av)
9130 * Process vms redirection arg's. Exit if any error is seen.
9131 * If getredirection() processes an argument, it is erased
9132 * from the vector. getredirection() returns a new argc and argv value.
9133 * In the event that a background command is requested (by a trailing "&"),
9134 * this routine creates a background subprocess, and simply exits the program.
9136 * Warning: do not try to simplify the code for vms. The code
9137 * presupposes that getredirection() is called before any data is
9138 * read from stdin or written to stdout.
9140 * Normal usage is as follows:
9146 * getredirection(&argc, &argv);
9150 int argc = *ac; /* Argument Count */
9151 char **argv = *av; /* Argument Vector */
9152 char *ap; /* Argument pointer */
9153 int j; /* argv[] index */
9154 int item_count = 0; /* Count of Items in List */
9155 struct list_item *list_head = 0; /* First Item in List */
9156 struct list_item *list_tail; /* Last Item in List */
9157 char *in = NULL; /* Input File Name */
9158 char *out = NULL; /* Output File Name */
9159 char *outmode = "w"; /* Mode to Open Output File */
9160 char *err = NULL; /* Error File Name */
9161 char *errmode = "w"; /* Mode to Open Error File */
9162 int cmargc = 0; /* Piped Command Arg Count */
9163 char **cmargv = NULL;/* Piped Command Arg Vector */
9166 * First handle the case where the last thing on the line ends with
9167 * a '&'. This indicates the desire for the command to be run in a
9168 * subprocess, so we satisfy that desire.
9171 if (0 == strcmp("&", ap))
9172 exit(background_process(aTHX_ --argc, argv));
9173 if (*ap && '&' == ap[strlen(ap)-1])
9175 ap[strlen(ap)-1] = '\0';
9176 exit(background_process(aTHX_ argc, argv));
9179 * Now we handle the general redirection cases that involve '>', '>>',
9180 * '<', and pipes '|'.
9182 for (j = 0; j < argc; ++j)
9184 if (0 == strcmp("<", argv[j]))
9188 fprintf(stderr,"No input file after < on command line");
9189 exit(LIB$_WRONUMARG);
9194 if ('<' == *(ap = argv[j]))
9199 if (0 == strcmp(">", ap))
9203 fprintf(stderr,"No output file after > on command line");
9204 exit(LIB$_WRONUMARG);
9223 fprintf(stderr,"No output file after > or >> on command line");
9224 exit(LIB$_WRONUMARG);
9228 if (('2' == *ap) && ('>' == ap[1]))
9245 fprintf(stderr,"No output file after 2> or 2>> on command line");
9246 exit(LIB$_WRONUMARG);
9250 if (0 == strcmp("|", argv[j]))
9254 fprintf(stderr,"No command into which to pipe on command line");
9255 exit(LIB$_WRONUMARG);
9257 cmargc = argc-(j+1);
9258 cmargv = &argv[j+1];
9262 if ('|' == *(ap = argv[j]))
9270 expand_wild_cards(ap, &list_head, &list_tail, &item_count);
9273 * Allocate and fill in the new argument vector, Some Unix's terminate
9274 * the list with an extra null pointer.
9276 argv = (char **) PerlMem_malloc((item_count+1) * sizeof(char *));
9277 if (argv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9279 for (j = 0; j < item_count; ++j, list_head = list_head->next)
9280 argv[j] = list_head->value;
9286 fprintf(stderr,"'|' and '>' may not both be specified on command line");
9287 exit(LIB$_INVARGORD);
9289 pipe_and_fork(aTHX_ cmargv);
9292 /* Check for input from a pipe (mailbox) */
9294 if (in == NULL && 1 == isapipe(0))
9296 char mbxname[L_tmpnam];
9298 long int dvi_item = DVI$_DEVBUFSIZ;
9299 $DESCRIPTOR(mbxnam, "");
9300 $DESCRIPTOR(mbxdevnam, "");
9302 /* Input from a pipe, reopen it in binary mode to disable */
9303 /* carriage control processing. */
9305 fgetname(stdin, mbxname);
9306 mbxnam.dsc$a_pointer = mbxname;
9307 mbxnam.dsc$w_length = strlen(mbxnam.dsc$a_pointer);
9308 lib$getdvi(&dvi_item, 0, &mbxnam, &bufsize, 0, 0);
9309 mbxdevnam.dsc$a_pointer = mbxname;
9310 mbxdevnam.dsc$w_length = sizeof(mbxname);
9311 dvi_item = DVI$_DEVNAM;
9312 lib$getdvi(&dvi_item, 0, &mbxnam, 0, &mbxdevnam, &mbxdevnam.dsc$w_length);
9313 mbxdevnam.dsc$a_pointer[mbxdevnam.dsc$w_length] = '\0';
9316 freopen(mbxname, "rb", stdin);
9319 fprintf(stderr,"Can't reopen input pipe (name: %s) in binary mode",mbxname);
9323 if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
9325 fprintf(stderr,"Can't open input file %s as stdin",in);
9328 if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
9330 fprintf(stderr,"Can't open output file %s as stdout",out);
9333 if (out != NULL) Perl_vmssetuserlnm(aTHX_ "SYS$OUTPUT",out);
9336 if (strcmp(err,"&1") == 0) {
9337 dup2(fileno(stdout), fileno(stderr));
9338 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR","SYS$OUTPUT");
9341 if (NULL == (tmperr = fopen(err, errmode, "mbc=32", "mbf=2")))
9343 fprintf(stderr,"Can't open error file %s as stderr",err);
9347 if (NULL == freopen(err, "a", stderr, "mbc=32", "mbf=2"))
9351 Perl_vmssetuserlnm(aTHX_ "SYS$ERROR",err);
9354 #ifdef ARGPROC_DEBUG
9355 PerlIO_printf(Perl_debug_log, "Arglist:\n");
9356 for (j = 0; j < *ac; ++j)
9357 PerlIO_printf(Perl_debug_log, "argv[%d] = '%s'\n", j, argv[j]);
9359 /* Clear errors we may have hit expanding wildcards, so they don't
9360 show up in Perl's $! later */
9361 set_errno(0); set_vaxc_errno(1);
9362 } /* end of getredirection() */
9365 static void add_item(struct list_item **head,
9366 struct list_item **tail,
9372 *head = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9373 if (head == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9377 (*tail)->next = (struct list_item *) PerlMem_malloc(sizeof(struct list_item));
9378 if ((*tail)->next == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9379 *tail = (*tail)->next;
9381 (*tail)->value = value;
9385 static void mp_expand_wild_cards(pTHX_ char *item,
9386 struct list_item **head,
9387 struct list_item **tail,
9391 unsigned long int context = 0;
9399 $DESCRIPTOR(filespec, "");
9400 $DESCRIPTOR(defaultspec, "SYS$DISK:[]");
9401 $DESCRIPTOR(resultspec, "");
9402 unsigned long int lff_flags = 0;
9406 #ifdef VMS_LONGNAME_SUPPORT
9407 lff_flags = LIB$M_FIL_LONG_NAMES;
9410 for (cp = item; *cp; cp++) {
9411 if (*cp == '*' || *cp == '%' || isspace(*cp)) break;
9412 if (*cp == '.' && *(cp-1) == '.' && *(cp-2) =='.') break;
9414 if (!*cp || isspace(*cp))
9416 add_item(head, tail, item, count);
9421 /* "double quoted" wild card expressions pass as is */
9422 /* From DCL that means using e.g.: */
9423 /* perl program """perl.*""" */
9424 item_len = strlen(item);
9425 if ( '"' == *item && '"' == item[item_len-1] )
9428 item[item_len-2] = '\0';
9429 add_item(head, tail, item, count);
9433 resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
9434 resultspec.dsc$b_class = DSC$K_CLASS_D;
9435 resultspec.dsc$a_pointer = NULL;
9436 vmsspec = PerlMem_malloc(VMS_MAXRSS);
9437 if (vmsspec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9438 if ((isunix = (int) strchr(item,'/')) != (int) NULL)
9439 filespec.dsc$a_pointer = int_tovmsspec(item, vmsspec, 0, NULL);
9440 if (!isunix || !filespec.dsc$a_pointer)
9441 filespec.dsc$a_pointer = item;
9442 filespec.dsc$w_length = strlen(filespec.dsc$a_pointer);
9444 * Only return version specs, if the caller specified a version
9446 had_version = strchr(item, ';');
9448 * Only return device and directory specs, if the caller specifed either.
9450 had_device = strchr(item, ':');
9451 had_directory = (isunix || NULL != strchr(item, '[')) || (NULL != strchr(item, '<'));
9453 while ($VMS_STATUS_SUCCESS(sts = lib$find_file
9454 (&filespec, &resultspec, &context,
9455 &defaultspec, 0, &rms_sts, &lff_flags)))
9460 string = PerlMem_malloc(resultspec.dsc$w_length+1);
9461 if (string == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9462 strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
9463 string[resultspec.dsc$w_length] = '\0';
9464 if (NULL == had_version)
9465 *(strrchr(string, ';')) = '\0';
9466 if ((!had_directory) && (had_device == NULL))
9468 if (NULL == (devdir = strrchr(string, ']')))
9469 devdir = strrchr(string, '>');
9470 strcpy(string, devdir + 1);
9473 * Be consistent with what the C RTL has already done to the rest of
9474 * the argv items and lowercase all of these names.
9476 if (!decc_efs_case_preserve) {
9477 for (c = string; *c; ++c)
9481 if (isunix) trim_unixpath(string,item,1);
9482 add_item(head, tail, string, count);
9485 PerlMem_free(vmsspec);
9486 if (sts != RMS$_NMF)
9488 set_vaxc_errno(sts);
9491 case RMS$_FNF: case RMS$_DNF:
9492 set_errno(ENOENT); break;
9494 set_errno(ENOTDIR); break;
9496 set_errno(ENODEV); break;
9497 case RMS$_FNM: case RMS$_SYN:
9498 set_errno(EINVAL); break;
9500 set_errno(EACCES); break;
9502 _ckvmssts_noperl(sts);
9506 add_item(head, tail, item, count);
9507 _ckvmssts_noperl(lib$sfree1_dd(&resultspec));
9508 _ckvmssts_noperl(lib$find_file_end(&context));
9511 static int child_st[2];/* Event Flag set when child process completes */
9513 static unsigned short child_chan;/* I/O Channel for Pipe Mailbox */
9515 static unsigned long int exit_handler(int *status)
9519 if (0 == child_st[0])
9521 #ifdef ARGPROC_DEBUG
9522 PerlIO_printf(Perl_debug_log, "Waiting for Child Process to Finish . . .\n");
9524 fflush(stdout); /* Have to flush pipe for binary data to */
9525 /* terminate properly -- <tp@mccall.com> */
9526 sys$qiow(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
9527 sys$dassgn(child_chan);
9529 sys$synch(0, child_st);
9534 static void sig_child(int chan)
9536 #ifdef ARGPROC_DEBUG
9537 PerlIO_printf(Perl_debug_log, "Child Completion AST\n");
9539 if (child_st[0] == 0)
9543 static struct exit_control_block exit_block =
9548 &exit_block.exit_status,
9553 pipe_and_fork(pTHX_ char **cmargv)
9556 struct dsc$descriptor_s *vmscmd;
9557 char subcmd[2*MAX_DCL_LINE_LENGTH], *p, *q;
9558 int sts, j, l, ismcr, quote, tquote = 0;
9560 sts = setup_cmddsc(aTHX_ cmargv[0],0,"e,&vmscmd);
9561 vms_execfree(vmscmd);
9566 ismcr = q && toupper(*q) == 'M' && toupper(*(q+1)) == 'C'
9567 && toupper(*(q+2)) == 'R' && !*(q+3);
9569 while (q && l < MAX_DCL_LINE_LENGTH) {
9571 if (j > 0 && quote) {
9577 if (ismcr && j > 1) quote = 1;
9578 tquote = (strchr(q,' ')) != NULL || *q == '\0';
9581 if (quote || tquote) {
9587 if ((quote||tquote) && *q == '"') {
9597 fp = safe_popen(aTHX_ subcmd,"wbF",&sts);
9599 PerlIO_printf(Perl_debug_log,"Can't open output pipe (status %d)",sts);
9603 static int background_process(pTHX_ int argc, char **argv)
9605 char command[MAX_DCL_SYMBOL + 1] = "$";
9606 $DESCRIPTOR(value, "");
9607 static $DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
9608 static $DESCRIPTOR(null, "NLA0:");
9609 static $DESCRIPTOR(pidsymbol, "SHELL_BACKGROUND_PID");
9611 $DESCRIPTOR(pidstr, "");
9613 unsigned long int flags = 17, one = 1, retsts;
9616 strcat(command, argv[0]);
9617 len = strlen(command);
9618 while (--argc && (len < MAX_DCL_SYMBOL))
9620 strcat(command, " \"");
9621 strcat(command, *(++argv));
9622 strcat(command, "\"");
9623 len = strlen(command);
9625 value.dsc$a_pointer = command;
9626 value.dsc$w_length = strlen(value.dsc$a_pointer);
9627 _ckvmssts_noperl(lib$set_symbol(&cmd, &value));
9628 retsts = lib$spawn(&cmd, &null, 0, &flags, 0, &pid);
9629 if (retsts == 0x38250) { /* DCL-W-NOTIFY - We must be BATCH, so retry */
9630 _ckvmssts_noperl(lib$spawn(&cmd, &null, 0, &one, 0, &pid));
9633 _ckvmssts_noperl(retsts);
9635 #ifdef ARGPROC_DEBUG
9636 PerlIO_printf(Perl_debug_log, "%s\n", command);
9638 sprintf(pidstring, "%08X", pid);
9639 PerlIO_printf(Perl_debug_log, "%s\n", pidstring);
9640 pidstr.dsc$a_pointer = pidstring;
9641 pidstr.dsc$w_length = strlen(pidstr.dsc$a_pointer);
9642 lib$set_symbol(&pidsymbol, &pidstr);
9646 /***** End of code taken from Mark Pizzolato's argproc.c package *****/
9649 /* OS-specific initialization at image activation (not thread startup) */
9650 /* Older VAXC header files lack these constants */
9651 #ifndef JPI$_RIGHTS_SIZE
9652 # define JPI$_RIGHTS_SIZE 817
9654 #ifndef KGB$M_SUBSYSTEM
9655 # define KGB$M_SUBSYSTEM 0x8
9658 /* Avoid Newx() in vms_image_init as thread context has not been initialized. */
9660 /*{{{void vms_image_init(int *, char ***)*/
9662 vms_image_init(int *argcp, char ***argvp)
9665 char eqv[LNM$C_NAMLENGTH+1] = "";
9666 unsigned int len, tabct = 8, tabidx = 0;
9667 unsigned long int *mask, iosb[2], i, rlst[128], rsz;
9668 unsigned long int iprv[(sizeof(union prvdef) + sizeof(unsigned long int) - 1) / sizeof(unsigned long int)];
9669 unsigned short int dummy, rlen;
9670 struct dsc$descriptor_s **tabvec;
9671 #if defined(PERL_IMPLICIT_CONTEXT)
9674 struct itmlst_3 jpilist[4] = { {sizeof iprv, JPI$_IMAGPRIV, iprv, &dummy},
9675 {sizeof rlst, JPI$_RIGHTSLIST, rlst, &rlen},
9676 { sizeof rsz, JPI$_RIGHTS_SIZE, &rsz, &dummy},
9679 #ifdef KILL_BY_SIGPRC
9680 Perl_csighandler_init();
9683 /* This was moved from the pre-image init handler because on threaded */
9684 /* Perl it was always returning 0 for the default value. */
9685 status = simple_trnlnm("SYS$POSIX_ROOT", eqv, LNM$C_NAMLENGTH);
9688 s = decc$feature_get_index("DECC$DISABLE_POSIX_ROOT");
9691 initial = decc$feature_get_value(s, 4);
9693 /* initial is: 0 if nothing has set the feature */
9694 /* -1 if initialized to default */
9695 /* 1 if set by logical name */
9696 /* 2 if set by decc$feature_set_value */
9697 decc_disable_posix_root = decc$feature_get_value(s, 1);
9699 /* If the value is not valid, force the feature off */
9700 if (decc_disable_posix_root < 0) {
9701 decc$feature_set_value(s, 1, 1);
9702 decc_disable_posix_root = 1;
9706 /* Nothing has asked for it explicitly, so use our own default. */
9707 decc_disable_posix_root = 1;
9708 decc$feature_set_value(s, 1, 1);
9714 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,jpilist,iosb,NULL,NULL));
9715 _ckvmssts_noperl(iosb[0]);
9716 for (i = 0; i < sizeof iprv / sizeof(unsigned long int); i++) {
9717 if (iprv[i]) { /* Running image installed with privs? */
9718 _ckvmssts_noperl(sys$setprv(0,iprv,0,NULL)); /* Turn 'em off. */
9723 /* Rights identifiers might trigger tainting as well. */
9724 if (!will_taint && (rlen || rsz)) {
9725 while (rlen < rsz) {
9726 /* We didn't get all the identifiers on the first pass. Allocate a
9727 * buffer much larger than $GETJPI wants (rsz is size in bytes that
9728 * were needed to hold all identifiers at time of last call; we'll
9729 * allocate that many unsigned long ints), and go back and get 'em.
9730 * If it gave us less than it wanted to despite ample buffer space,
9731 * something's broken. Is your system missing a system identifier?
9733 if (rsz <= jpilist[1].buflen) {
9734 /* Perl_croak accvios when used this early in startup. */
9735 fprintf(stderr, "vms_image_init: $getjpiw refuses to store RIGHTSLIST of %u bytes in buffer of %u bytes.\n%s",
9736 rsz, (unsigned long) jpilist[1].buflen,
9737 "Check your rights database for corruption.\n");
9740 if (jpilist[1].bufadr != rlst) PerlMem_free(jpilist[1].bufadr);
9741 jpilist[1].bufadr = mask = (unsigned long int *) PerlMem_malloc(rsz * sizeof(unsigned long int));
9742 if (mask == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9743 jpilist[1].buflen = rsz * sizeof(unsigned long int);
9744 _ckvmssts_noperl(sys$getjpiw(0,NULL,NULL,&jpilist[1],iosb,NULL,NULL));
9745 _ckvmssts_noperl(iosb[0]);
9747 mask = jpilist[1].bufadr;
9748 /* Check attribute flags for each identifier (2nd longword); protected
9749 * subsystem identifiers trigger tainting.
9751 for (i = 1; i < (rlen + sizeof(unsigned long int) - 1) / sizeof(unsigned long int); i += 2) {
9752 if (mask[i] & KGB$M_SUBSYSTEM) {
9757 if (mask != rlst) PerlMem_free(mask);
9760 /* When Perl is in decc_filename_unix_report mode and is run from a concealed
9761 * logical, some versions of the CRTL will add a phanthom /000000/
9762 * directory. This needs to be removed.
9764 if (decc_filename_unix_report) {
9767 ulen = strlen(argvp[0][0]);
9769 zeros = strstr(argvp[0][0], "/000000/");
9770 if (zeros != NULL) {
9772 mlen = ulen - (zeros - argvp[0][0]) - 7;
9773 memmove(zeros, &zeros[7], mlen);
9775 argvp[0][0][ulen] = '\0';
9778 /* It also may have a trailing dot that needs to be removed otherwise
9779 * it will be converted to VMS mode incorrectly.
9782 if ((argvp[0][0][ulen] == '.') && (decc_readdir_dropdotnotype))
9783 argvp[0][0][ulen] = '\0';
9786 /* We need to use this hack to tell Perl it should run with tainting,
9787 * since its tainting flag may be part of the PL_curinterp struct, which
9788 * hasn't been allocated when vms_image_init() is called.
9791 char **newargv, **oldargv;
9793 newargv = (char **) PerlMem_malloc(((*argcp)+2) * sizeof(char *));
9794 if (newargv == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9795 newargv[0] = oldargv[0];
9796 newargv[1] = PerlMem_malloc(3 * sizeof(char));
9797 if (newargv[1] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9798 strcpy(newargv[1], "-T");
9799 Copy(&oldargv[1],&newargv[2],(*argcp)-1,char **);
9801 newargv[*argcp] = NULL;
9802 /* We orphan the old argv, since we don't know where it's come from,
9803 * so we don't know how to free it.
9807 else { /* Did user explicitly request tainting? */
9809 char *cp, **av = *argvp;
9810 for (i = 1; i < *argcp; i++) {
9811 if (*av[i] != '-') break;
9812 for (cp = av[i]+1; *cp; cp++) {
9813 if (*cp == 'T') { will_taint = 1; break; }
9814 else if ( (*cp == 'd' || *cp == 'V') && *(cp+1) == ':' ||
9815 strchr("DFIiMmx",*cp)) break;
9817 if (will_taint) break;
9822 len = my_trnlnm("PERL_ENV_TABLES",eqv,tabidx);
9825 tabvec = (struct dsc$descriptor_s **)
9826 PerlMem_malloc(tabct * sizeof(struct dsc$descriptor_s *));
9827 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9829 else if (tabidx >= tabct) {
9831 tabvec = (struct dsc$descriptor_s **) PerlMem_realloc(tabvec, tabct * sizeof(struct dsc$descriptor_s *));
9832 if (tabvec == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9834 tabvec[tabidx] = (struct dsc$descriptor_s *) PerlMem_malloc(sizeof(struct dsc$descriptor_s));
9835 if (tabvec[tabidx] == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9836 tabvec[tabidx]->dsc$w_length = 0;
9837 tabvec[tabidx]->dsc$b_dtype = DSC$K_DTYPE_T;
9838 tabvec[tabidx]->dsc$b_class = DSC$K_CLASS_D;
9839 tabvec[tabidx]->dsc$a_pointer = NULL;
9840 _ckvmssts_noperl(lib$scopy_r_dx(&len,eqv,tabvec[tabidx]));
9842 if (tabidx) { tabvec[tabidx] = NULL; env_tables = tabvec; }
9844 getredirection(argcp,argvp);
9845 #if defined(USE_ITHREADS) && ( defined(__DECC) || defined(__DECCXX) )
9847 # include <reentrancy.h>
9848 decc$set_reentrancy(C$C_MULTITHREAD);
9857 * Trim Unix-style prefix off filespec, so it looks like what a shell
9858 * glob expansion would return (i.e. from specified prefix on, not
9859 * full path). Note that returned filespec is Unix-style, regardless
9860 * of whether input filespec was VMS-style or Unix-style.
9862 * fspec is filespec to be trimmed, and wildspec is wildcard spec used to
9863 * determine prefix (both may be in VMS or Unix syntax). opts is a bit
9864 * vector of options; at present, only bit 0 is used, and if set tells
9865 * trim unixpath to try the current default directory as a prefix when
9866 * presented with a possibly ambiguous ... wildcard.
9868 * Returns !=0 on success, with trimmed filespec replacing contents of
9869 * fspec, and 0 on failure, with contents of fpsec unchanged.
9871 /*{{{int trim_unixpath(char *fspec, char *wildspec, int opts)*/
9873 Perl_trim_unixpath(pTHX_ char *fspec, const char *wildspec, int opts)
9875 char *unixified, *unixwild,
9876 *template, *base, *end, *cp1, *cp2;
9877 register int tmplen, reslen = 0, dirs = 0;
9879 if (!wildspec || !fspec) return 0;
9881 unixwild = PerlMem_malloc(VMS_MAXRSS);
9882 if (unixwild == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9883 template = unixwild;
9884 if (strpbrk(wildspec,"]>:") != NULL) {
9885 if (int_tounixspec(wildspec, unixwild, NULL) == NULL) {
9886 PerlMem_free(unixwild);
9891 strncpy(unixwild, wildspec, VMS_MAXRSS-1);
9892 unixwild[VMS_MAXRSS-1] = 0;
9894 unixified = PerlMem_malloc(VMS_MAXRSS);
9895 if (unixified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9896 if (strpbrk(fspec,"]>:") != NULL) {
9897 if (int_tounixspec(fspec, unixified, NULL) == NULL) {
9898 PerlMem_free(unixwild);
9899 PerlMem_free(unixified);
9902 else base = unixified;
9903 /* reslen != 0 ==> we had to unixify resultant filespec, so we must
9904 * check to see that final result fits into (isn't longer than) fspec */
9905 reslen = strlen(fspec);
9909 /* No prefix or absolute path on wildcard, so nothing to remove */
9910 if (!*template || *template == '/') {
9911 PerlMem_free(unixwild);
9912 if (base == fspec) {
9913 PerlMem_free(unixified);
9916 tmplen = strlen(unixified);
9917 if (tmplen > reslen) {
9918 PerlMem_free(unixified);
9919 return 0; /* not enough space */
9921 /* Copy unixified resultant, including trailing NUL */
9922 memmove(fspec,unixified,tmplen+1);
9923 PerlMem_free(unixified);
9927 for (end = base; *end; end++) ; /* Find end of resultant filespec */
9928 if ((cp1 = strstr(template,".../")) == NULL) { /* No ...; just count elts */
9929 for (cp1 = template; *cp1; cp1++) if (*cp1 == '/') dirs++;
9930 for (cp1 = end ;cp1 >= base; cp1--)
9931 if ((*cp1 == '/') && !dirs--) /* postdec so we get front of rel path */
9933 if (cp1 != fspec) memmove(fspec,cp1, end - cp1 + 1);
9934 PerlMem_free(unixified);
9935 PerlMem_free(unixwild);
9940 char *front, *nextell, *lcend, *lcfront, *ellipsis = cp1;
9941 int ells = 1, totells, segdirs, match;
9942 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL},
9943 resdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
9945 while ((cp1 = strstr(ellipsis+4,".../")) != NULL) {ellipsis = cp1; ells++;}
9947 for (cp1 = ellipsis+4; *cp1; cp1++) if (*cp1 == '/') dirs++;
9948 tpl = PerlMem_malloc(VMS_MAXRSS);
9949 if (tpl == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9950 if (ellipsis == template && opts & 1) {
9951 /* Template begins with an ellipsis. Since we can't tell how many
9952 * directory names at the front of the resultant to keep for an
9953 * arbitrary starting point, we arbitrarily choose the current
9954 * default directory as a starting point. If it's there as a prefix,
9955 * clip it off. If not, fall through and act as if the leading
9956 * ellipsis weren't there (i.e. return shortest possible path that
9957 * could match template).
9959 if (getcwd(tpl, (VMS_MAXRSS - 1),0) == NULL) {
9961 PerlMem_free(unixified);
9962 PerlMem_free(unixwild);
9965 if (!decc_efs_case_preserve) {
9966 for (cp1 = tpl, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
9967 if (_tolower(*cp1) != _tolower(*cp2)) break;
9969 segdirs = dirs - totells; /* Min # of dirs we must have left */
9970 for (front = cp2+1; *front; front++) if (*front == '/') segdirs--;
9971 if (*cp1 == '\0' && *cp2 == '/' && segdirs < 1) {
9972 memmove(fspec,cp2+1,end - cp2);
9974 PerlMem_free(unixified);
9975 PerlMem_free(unixwild);
9979 /* First off, back up over constant elements at end of path */
9981 for (front = end ; front >= base; front--)
9982 if (*front == '/' && !dirs--) { front++; break; }
9984 lcres = PerlMem_malloc(VMS_MAXRSS);
9985 if (lcres == NULL) _ckvmssts_noperl(SS$_INSFMEM);
9986 for (cp1=template,cp2=lcres; *cp1 && cp2 <= lcres + (VMS_MAXRSS - 1);
9988 if (!decc_efs_case_preserve) {
9989 *cp2 = _tolower(*cp1); /* Make lc copy for match */
9997 PerlMem_free(unixified);
9998 PerlMem_free(unixwild);
9999 PerlMem_free(lcres);
10000 return 0; /* Path too long. */
10003 *cp2 = '\0'; /* Pick up with memcpy later */
10004 lcfront = lcres + (front - base);
10005 /* Now skip over each ellipsis and try to match the path in front of it. */
10007 for (cp1 = ellipsis - 2; cp1 >= template; cp1--)
10008 if (*(cp1) == '.' && *(cp1+1) == '.' &&
10009 *(cp1+2) == '.' && *(cp1+3) == '/' ) break;
10010 if (cp1 < template) break; /* template started with an ellipsis */
10011 if (cp1 + 4 == ellipsis) { /* Consecutive ellipses */
10012 ellipsis = cp1; continue;
10014 wilddsc.dsc$a_pointer = tpl;
10015 wilddsc.dsc$w_length = resdsc.dsc$w_length = ellipsis - 1 - cp1;
10017 for (segdirs = 0, cp2 = tpl;
10018 cp1 <= ellipsis - 1 && cp2 <= tpl + (VMS_MAXRSS-1);
10020 if (*cp1 == '?') *cp2 = '%'; /* Substitute VMS' wildcard for Unix' */
10022 if (!decc_efs_case_preserve) {
10023 *cp2 = _tolower(*cp1); /* else lowercase for match */
10026 *cp2 = *cp1; /* else preserve case for match */
10029 if (*cp2 == '/') segdirs++;
10031 if (cp1 != ellipsis - 1) {
10033 PerlMem_free(unixified);
10034 PerlMem_free(unixwild);
10035 PerlMem_free(lcres);
10036 return 0; /* Path too long */
10038 /* Back up at least as many dirs as in template before matching */
10039 for (cp1 = lcfront - 1; segdirs && cp1 >= lcres; cp1--)
10040 if (*cp1 == '/' && !segdirs--) { cp1++; break; }
10041 for (match = 0; cp1 > lcres;) {
10042 resdsc.dsc$a_pointer = cp1;
10043 if (str$match_wild(&wilddsc,&resdsc) == STR$_MATCH) {
10045 if (match == 1) lcfront = cp1;
10047 for ( ; cp1 >= lcres; cp1--) if (*cp1 == '/') { cp1++; break; }
10051 PerlMem_free(unixified);
10052 PerlMem_free(unixwild);
10053 PerlMem_free(lcres);
10054 return 0; /* Can't find prefix ??? */
10056 if (match > 1 && opts & 1) {
10057 /* This ... wildcard could cover more than one set of dirs (i.e.
10058 * a set of similar dir names is repeated). If the template
10059 * contains more than 1 ..., upstream elements could resolve the
10060 * ambiguity, but it's not worth a full backtracking setup here.
10061 * As a quick heuristic, clip off the current default directory
10062 * if it's present to find the trimmed spec, else use the
10063 * shortest string that this ... could cover.
10065 char def[NAM$C_MAXRSS+1], *st;
10067 if (getcwd(def, sizeof def,0) == NULL) {
10068 PerlMem_free(unixified);
10069 PerlMem_free(unixwild);
10070 PerlMem_free(lcres);
10074 if (!decc_efs_case_preserve) {
10075 for (cp1 = def, cp2 = base; *cp1 && *cp2; cp1++,cp2++)
10076 if (_tolower(*cp1) != _tolower(*cp2)) break;
10078 segdirs = dirs - totells; /* Min # of dirs we must have left */
10079 for (st = cp2+1; *st; st++) if (*st == '/') segdirs--;
10080 if (*cp1 == '\0' && *cp2 == '/') {
10081 memmove(fspec,cp2+1,end - cp2);
10083 PerlMem_free(unixified);
10084 PerlMem_free(unixwild);
10085 PerlMem_free(lcres);
10088 /* Nope -- stick with lcfront from above and keep going. */
10091 memmove(fspec,base + (lcfront - lcres), lcend - lcfront + 1);
10093 PerlMem_free(unixified);
10094 PerlMem_free(unixwild);
10095 PerlMem_free(lcres);
10097 ellipsis = nextell;
10100 } /* end of trim_unixpath() */
10105 * VMS readdir() routines.
10106 * Written by Rich $alz, <rsalz@bbn.com> in August, 1990.
10108 * 21-Jul-1994 Charles Bailey bailey@newman.upenn.edu
10109 * Minor modifications to original routines.
10112 /* readdir may have been redefined by reentr.h, so make sure we get
10113 * the local version for what we do here.
10118 #if !defined(PERL_IMPLICIT_CONTEXT)
10119 # define readdir Perl_readdir
10121 # define readdir(a) Perl_readdir(aTHX_ a)
10124 /* Number of elements in vms_versions array */
10125 #define VERSIZE(e) (sizeof e->vms_versions / sizeof e->vms_versions[0])
10128 * Open a directory, return a handle for later use.
10130 /*{{{ DIR *opendir(char*name) */
10132 Perl_opendir(pTHX_ const char *name)
10138 Newx(dir, VMS_MAXRSS, char);
10139 if (int_tovmspath(name, dir, NULL) == NULL) {
10143 /* Check access before stat; otherwise stat does not
10144 * accurately report whether it's a directory.
10146 if (!cando_by_name_int(S_IRUSR,0,dir,PERL_RMSEXPAND_M_VMS_IN)) {
10147 /* cando_by_name has already set errno */
10151 if (flex_stat(dir,&sb) == -1) return NULL;
10152 if (!S_ISDIR(sb.st_mode)) {
10154 set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
10157 /* Get memory for the handle, and the pattern. */
10159 Newx(dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
10161 /* Fill in the fields; mainly playing with the descriptor. */
10162 sprintf(dd->pattern, "%s*.*",dir);
10167 /* By saying we always want the result of readdir() in unix format, we
10168 * are really saying we want all the escapes removed. Otherwise the caller,
10169 * having no way to know whether it's already in VMS format, might send it
10170 * through tovmsspec again, thus double escaping.
10172 dd->flags = PERL_VMSDIR_M_UNIXSPECS;
10173 dd->pat.dsc$a_pointer = dd->pattern;
10174 dd->pat.dsc$w_length = strlen(dd->pattern);
10175 dd->pat.dsc$b_dtype = DSC$K_DTYPE_T;
10176 dd->pat.dsc$b_class = DSC$K_CLASS_S;
10177 #if defined(USE_ITHREADS)
10178 Newx(dd->mutex,1,perl_mutex);
10179 MUTEX_INIT( (perl_mutex *) dd->mutex );
10185 } /* end of opendir() */
10189 * Set the flag to indicate we want versions or not.
10191 /*{{{ void vmsreaddirversions(DIR *dd, int flag)*/
10193 vmsreaddirversions(DIR *dd, int flag)
10196 dd->flags |= PERL_VMSDIR_M_VERSIONS;
10198 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10203 * Free up an opened directory.
10205 /*{{{ void closedir(DIR *dd)*/
10207 Perl_closedir(DIR *dd)
10211 sts = lib$find_file_end(&dd->context);
10212 Safefree(dd->pattern);
10213 #if defined(USE_ITHREADS)
10214 MUTEX_DESTROY( (perl_mutex *) dd->mutex );
10215 Safefree(dd->mutex);
10222 * Collect all the version numbers for the current file.
10225 collectversions(pTHX_ DIR *dd)
10227 struct dsc$descriptor_s pat;
10228 struct dsc$descriptor_s res;
10230 char *p, *text, *buff;
10232 unsigned long context, tmpsts;
10234 /* Convenient shorthand. */
10237 /* Add the version wildcard, ignoring the "*.*" put on before */
10238 i = strlen(dd->pattern);
10239 Newx(text,i + e->d_namlen + 3,char);
10240 strcpy(text, dd->pattern);
10241 sprintf(&text[i - 3], "%s;*", e->d_name);
10243 /* Set up the pattern descriptor. */
10244 pat.dsc$a_pointer = text;
10245 pat.dsc$w_length = i + e->d_namlen - 1;
10246 pat.dsc$b_dtype = DSC$K_DTYPE_T;
10247 pat.dsc$b_class = DSC$K_CLASS_S;
10249 /* Set up result descriptor. */
10250 Newx(buff, VMS_MAXRSS, char);
10251 res.dsc$a_pointer = buff;
10252 res.dsc$w_length = VMS_MAXRSS - 1;
10253 res.dsc$b_dtype = DSC$K_DTYPE_T;
10254 res.dsc$b_class = DSC$K_CLASS_S;
10256 /* Read files, collecting versions. */
10257 for (context = 0, e->vms_verscount = 0;
10258 e->vms_verscount < VERSIZE(e);
10259 e->vms_verscount++) {
10260 unsigned long rsts;
10261 unsigned long flags = 0;
10263 #ifdef VMS_LONGNAME_SUPPORT
10264 flags = LIB$M_FIL_LONG_NAMES;
10266 tmpsts = lib$find_file(&pat, &res, &context, NULL, NULL, &rsts, &flags);
10267 if (tmpsts == RMS$_NMF || context == 0) break;
10269 buff[VMS_MAXRSS - 1] = '\0';
10270 if ((p = strchr(buff, ';')))
10271 e->vms_versions[e->vms_verscount] = atoi(p + 1);
10273 e->vms_versions[e->vms_verscount] = -1;
10276 _ckvmssts(lib$find_file_end(&context));
10280 } /* end of collectversions() */
10283 * Read the next entry from the directory.
10285 /*{{{ struct dirent *readdir(DIR *dd)*/
10287 Perl_readdir(pTHX_ DIR *dd)
10289 struct dsc$descriptor_s res;
10291 unsigned long int tmpsts;
10292 unsigned long rsts;
10293 unsigned long flags = 0;
10294 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
10295 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
10297 /* Set up result descriptor, and get next file. */
10298 Newx(buff, VMS_MAXRSS, char);
10299 res.dsc$a_pointer = buff;
10300 res.dsc$w_length = VMS_MAXRSS - 1;
10301 res.dsc$b_dtype = DSC$K_DTYPE_T;
10302 res.dsc$b_class = DSC$K_CLASS_S;
10304 #ifdef VMS_LONGNAME_SUPPORT
10305 flags = LIB$M_FIL_LONG_NAMES;
10308 tmpsts = lib$find_file
10309 (&dd->pat, &res, &dd->context, NULL, NULL, &rsts, &flags);
10310 if ( tmpsts == RMS$_NMF || dd->context == 0) return NULL; /* None left. */
10311 if (!(tmpsts & 1)) {
10312 set_vaxc_errno(tmpsts);
10315 set_errno(EACCES); break;
10317 set_errno(ENODEV); break;
10319 set_errno(ENOTDIR); break;
10320 case RMS$_FNF: case RMS$_DNF:
10321 set_errno(ENOENT); break;
10323 set_errno(EVMSERR);
10329 /* Force the buffer to end with a NUL, and downcase name to match C convention. */
10330 buff[res.dsc$w_length] = '\0';
10331 p = buff + res.dsc$w_length;
10332 while (--p >= buff) if (!isspace(*p)) break;
10334 if (!decc_efs_case_preserve) {
10335 for (p = buff; *p; p++) *p = _tolower(*p);
10338 /* Skip any directory component and just copy the name. */
10339 sts = vms_split_path
10354 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10356 /* In Unix report mode, remove the ".dir;1" from the name */
10357 /* if it is a real directory. */
10358 if (decc_filename_unix_report || decc_efs_charset) {
10359 if ((e_len == 4) && (vs_len == 2) && (vs_spec[1] == '1')) {
10360 if ((toupper(e_spec[1]) == 'D') &&
10361 (toupper(e_spec[2]) == 'I') &&
10362 (toupper(e_spec[3]) == 'R')) {
10366 ret_sts = stat(buff, &statbuf.crtl_stat);
10367 if ((ret_sts == 0) && S_ISDIR(statbuf.st_mode)) {
10375 /* Drop NULL extensions on UNIX file specification */
10376 if ((e_len == 1) && decc_readdir_dropdotnotype) {
10382 strncpy(dd->entry.d_name, n_spec, n_len + e_len);
10383 dd->entry.d_name[n_len + e_len] = '\0';
10384 dd->entry.d_namlen = strlen(dd->entry.d_name);
10386 /* Convert the filename to UNIX format if needed */
10387 if (dd->flags & PERL_VMSDIR_M_UNIXSPECS) {
10389 /* Translate the encoded characters. */
10390 /* Fixme: Unicode handling could result in embedded 0 characters */
10391 if (strchr(dd->entry.d_name, '^') != NULL) {
10392 char new_name[256];
10394 p = dd->entry.d_name;
10397 int inchars_read, outchars_added;
10398 inchars_read = copy_expand_vms_filename_escape(q, p, &outchars_added);
10400 q += outchars_added;
10402 /* if outchars_added > 1, then this is a wide file specification */
10403 /* Wide file specifications need to be passed in Perl */
10404 /* counted strings apparently with a Unicode flag */
10407 strcpy(dd->entry.d_name, new_name);
10408 dd->entry.d_namlen = strlen(dd->entry.d_name);
10412 dd->entry.vms_verscount = 0;
10413 if (dd->flags & PERL_VMSDIR_M_VERSIONS) collectversions(aTHX_ dd);
10417 } /* end of readdir() */
10421 * Read the next entry from the directory -- thread-safe version.
10423 /*{{{ int readdir_r(DIR *dd, struct dirent *entry, struct dirent **result)*/
10425 Perl_readdir_r(pTHX_ DIR *dd, struct dirent *entry, struct dirent **result)
10429 MUTEX_LOCK( (perl_mutex *) dd->mutex );
10431 entry = readdir(dd);
10433 retval = ( *result == NULL ? errno : 0 );
10435 MUTEX_UNLOCK( (perl_mutex *) dd->mutex );
10439 } /* end of readdir_r() */
10443 * Return something that can be used in a seekdir later.
10445 /*{{{ long telldir(DIR *dd)*/
10447 Perl_telldir(DIR *dd)
10454 * Return to a spot where we used to be. Brute force.
10456 /*{{{ void seekdir(DIR *dd,long count)*/
10458 Perl_seekdir(pTHX_ DIR *dd, long count)
10462 /* If we haven't done anything yet... */
10463 if (dd->count == 0)
10466 /* Remember some state, and clear it. */
10467 old_flags = dd->flags;
10468 dd->flags &= ~PERL_VMSDIR_M_VERSIONS;
10469 _ckvmssts(lib$find_file_end(&dd->context));
10472 /* The increment is in readdir(). */
10473 for (dd->count = 0; dd->count < count; )
10476 dd->flags = old_flags;
10478 } /* end of seekdir() */
10481 /* VMS subprocess management
10483 * my_vfork() - just a vfork(), after setting a flag to record that
10484 * the current script is trying a Unix-style fork/exec.
10486 * vms_do_aexec() and vms_do_exec() are called in response to the
10487 * perl 'exec' function. If this follows a vfork call, then they
10488 * call out the regular perl routines in doio.c which do an
10489 * execvp (for those who really want to try this under VMS).
10490 * Otherwise, they do exactly what the perl docs say exec should
10491 * do - terminate the current script and invoke a new command
10492 * (See below for notes on command syntax.)
10494 * do_aspawn() and do_spawn() implement the VMS side of the perl
10495 * 'system' function.
10497 * Note on command arguments to perl 'exec' and 'system': When handled
10498 * in 'VMSish fashion' (i.e. not after a call to vfork) The args
10499 * are concatenated to form a DCL command string. If the first non-numeric
10500 * arg begins with '$' (i.e. the perl script had "\$ Type" or some such),
10501 * the command string is handed off to DCL directly. Otherwise,
10502 * the first token of the command is taken as the filespec of an image
10503 * to run. The filespec is expanded using a default type of '.EXE' and
10504 * the process defaults for device, directory, etc., and if found, the resultant
10505 * filespec is invoked using the DCL verb 'MCR', and passed the rest of
10506 * the command string as parameters. This is perhaps a bit complicated,
10507 * but I hope it will form a happy medium between what VMS folks expect
10508 * from lib$spawn and what Unix folks expect from exec.
10511 static int vfork_called;
10513 /*{{{int my_vfork()*/
10524 vms_execfree(struct dsc$descriptor_s *vmscmd)
10527 if (vmscmd->dsc$a_pointer) {
10528 PerlMem_free(vmscmd->dsc$a_pointer);
10530 PerlMem_free(vmscmd);
10535 setup_argstr(pTHX_ SV *really, SV **mark, SV **sp)
10537 char *junk, *tmps = NULL;
10538 register size_t cmdlen = 0;
10545 tmps = SvPV(really,rlen);
10547 cmdlen += rlen + 1;
10552 for (idx++; idx <= sp; idx++) {
10554 junk = SvPVx(*idx,rlen);
10555 cmdlen += rlen ? rlen + 1 : 0;
10558 Newx(PL_Cmd, cmdlen+1, char);
10560 if (tmps && *tmps) {
10561 strcpy(PL_Cmd,tmps);
10564 else *PL_Cmd = '\0';
10565 while (++mark <= sp) {
10567 char *s = SvPVx(*mark,n_a);
10569 if (*PL_Cmd) strcat(PL_Cmd," ");
10575 } /* end of setup_argstr() */
10578 static unsigned long int
10579 setup_cmddsc(pTHX_ const char *incmd, int check_img, int *suggest_quote,
10580 struct dsc$descriptor_s **pvmscmd)
10584 char image_name[NAM$C_MAXRSS+1];
10585 char image_argv[NAM$C_MAXRSS+1];
10586 $DESCRIPTOR(defdsc,".EXE");
10587 $DESCRIPTOR(defdsc2,".");
10588 struct dsc$descriptor_s resdsc;
10589 struct dsc$descriptor_s *vmscmd;
10590 struct dsc$descriptor_s imgdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10591 unsigned long int cxt = 0, flags = 1, retsts = SS$_NORMAL;
10592 register char *s, *rest, *cp, *wordbreak;
10595 register int isdcl;
10597 vmscmd = PerlMem_malloc(sizeof(struct dsc$descriptor_s));
10598 if (vmscmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10600 /* vmsspec is a DCL command buffer, not just a filename */
10601 vmsspec = PerlMem_malloc(MAX_DCL_LINE_LENGTH + 1);
10602 if (vmsspec == NULL)
10603 _ckvmssts_noperl(SS$_INSFMEM);
10605 resspec = PerlMem_malloc(VMS_MAXRSS);
10606 if (resspec == NULL)
10607 _ckvmssts_noperl(SS$_INSFMEM);
10609 /* Make a copy for modification */
10610 cmdlen = strlen(incmd);
10611 cmd = PerlMem_malloc(cmdlen+1);
10612 if (cmd == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10613 strncpy(cmd, incmd, cmdlen);
10618 resdsc.dsc$a_pointer = resspec;
10619 resdsc.dsc$b_dtype = DSC$K_DTYPE_T;
10620 resdsc.dsc$b_class = DSC$K_CLASS_S;
10621 resdsc.dsc$w_length = VMS_MAXRSS - 1;
10623 vmscmd->dsc$a_pointer = NULL;
10624 vmscmd->dsc$b_dtype = DSC$K_DTYPE_T;
10625 vmscmd->dsc$b_class = DSC$K_CLASS_S;
10626 vmscmd->dsc$w_length = 0;
10627 if (pvmscmd) *pvmscmd = vmscmd;
10629 if (suggest_quote) *suggest_quote = 0;
10631 if (strlen(cmd) > MAX_DCL_LINE_LENGTH) {
10633 PerlMem_free(vmsspec);
10634 PerlMem_free(resspec);
10635 return CLI$_BUFOVF; /* continuation lines currently unsupported */
10640 while (*s && isspace(*s)) s++;
10642 if (*s == '@' || *s == '$') {
10643 vmsspec[0] = *s; rest = s + 1;
10644 for (cp = &vmsspec[1]; *rest && isspace(*rest); rest++,cp++) *cp = *rest;
10646 else { cp = vmsspec; rest = s; }
10647 if (*rest == '.' || *rest == '/') {
10649 for (cp2 = resspec;
10650 *rest && !isspace(*rest) && cp2 - resspec < (VMS_MAXRSS - 1);
10651 rest++, cp2++) *cp2 = *rest;
10653 if (int_tovmsspec(resspec, cp, 0, NULL)) {
10656 /* When a UNIX spec with no file type is translated to VMS, */
10657 /* A trailing '.' is appended under ODS-5 rules. */
10658 /* Here we do not want that trailing "." as it prevents */
10659 /* Looking for a implied ".exe" type. */
10660 if (decc_efs_charset) {
10662 i = strlen(vmsspec);
10663 if (vmsspec[i-1] == '.') {
10664 vmsspec[i-1] = '\0';
10669 for (cp2 = vmsspec + strlen(vmsspec);
10670 *rest && cp2 - vmsspec < MAX_DCL_LINE_LENGTH;
10671 rest++, cp2++) *cp2 = *rest;
10676 /* Intuit whether verb (first word of cmd) is a DCL command:
10677 * - if first nonspace char is '@', it's a DCL indirection
10679 * - if verb contains a filespec separator, it's not a DCL command
10680 * - if it doesn't, caller tells us whether to default to a DCL
10681 * command, or to a local image unless told it's DCL (by leading '$')
10685 if (suggest_quote) *suggest_quote = 1;
10687 register char *filespec = strpbrk(s,":<[.;");
10688 rest = wordbreak = strpbrk(s," \"\t/");
10689 if (!wordbreak) wordbreak = s + strlen(s);
10690 if (*s == '$') check_img = 0;
10691 if (filespec && (filespec < wordbreak)) isdcl = 0;
10692 else isdcl = !check_img;
10697 imgdsc.dsc$a_pointer = s;
10698 imgdsc.dsc$w_length = wordbreak - s;
10699 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10701 _ckvmssts_noperl(lib$find_file_end(&cxt));
10702 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10703 if (!(retsts & 1) && *s == '$') {
10704 _ckvmssts_noperl(lib$find_file_end(&cxt));
10705 imgdsc.dsc$a_pointer++; imgdsc.dsc$w_length--;
10706 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc,0,&rsts,&flags);
10708 _ckvmssts_noperl(lib$find_file_end(&cxt));
10709 retsts = lib$find_file(&imgdsc,&resdsc,&cxt,&defdsc2,0,&rsts,&flags);
10713 _ckvmssts_noperl(lib$find_file_end(&cxt));
10718 while (*s && !isspace(*s)) s++;
10721 /* check that it's really not DCL with no file extension */
10722 fp = fopen(resspec,"r","ctx=bin","ctx=rec","shr=get");
10724 char b[256] = {0,0,0,0};
10725 read(fileno(fp), b, 256);
10726 isdcl = isprint(b[0]) && isprint(b[1]) && isprint(b[2]) && isprint(b[3]);
10730 /* Check for script */
10732 if ((b[0] == '#') && (b[1] == '!'))
10734 #ifdef ALTERNATE_SHEBANG
10736 shebang_len = strlen(ALTERNATE_SHEBANG);
10737 if (strncmp(b, ALTERNATE_SHEBANG, shebang_len) == 0) {
10739 perlstr = strstr("perl",b);
10740 if (perlstr == NULL)
10748 if (shebang_len > 0) {
10751 char tmpspec[NAM$C_MAXRSS + 1];
10754 /* Image is following after white space */
10755 /*--------------------------------------*/
10756 while (isprint(b[i]) && isspace(b[i]))
10760 while (isprint(b[i]) && !isspace(b[i])) {
10761 tmpspec[j++] = b[i++];
10762 if (j >= NAM$C_MAXRSS)
10767 /* There may be some default parameters to the image */
10768 /*---------------------------------------------------*/
10770 while (isprint(b[i])) {
10771 image_argv[j++] = b[i++];
10772 if (j >= NAM$C_MAXRSS)
10775 while ((j > 0) && !isprint(image_argv[j-1]))
10779 /* It will need to be converted to VMS format and validated */
10780 if (tmpspec[0] != '\0') {
10783 /* Try to find the exact program requested to be run */
10784 /*---------------------------------------------------*/
10785 iname = int_rmsexpand
10786 (tmpspec, image_name, ".exe",
10787 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10788 if (iname != NULL) {
10789 if (cando_by_name_int
10790 (S_IXUSR,0,image_name,PERL_RMSEXPAND_M_VMS_IN)) {
10791 /* MCR prefix needed */
10795 /* Try again with a null type */
10796 /*----------------------------*/
10797 iname = int_rmsexpand
10798 (tmpspec, image_name, ".",
10799 PERL_RMSEXPAND_M_VMS, NULL, NULL);
10800 if (iname != NULL) {
10801 if (cando_by_name_int
10802 (S_IXUSR,0,image_name, PERL_RMSEXPAND_M_VMS_IN)) {
10803 /* MCR prefix needed */
10809 /* Did we find the image to run the script? */
10810 /*------------------------------------------*/
10814 /* Assume DCL or foreign command exists */
10815 /*--------------------------------------*/
10816 tchr = strrchr(tmpspec, '/');
10817 if (tchr != NULL) {
10823 strcpy(image_name, tchr);
10831 if (check_img && isdcl) {
10833 PerlMem_free(resspec);
10834 PerlMem_free(vmsspec);
10838 if (cando_by_name(S_IXUSR,0,resspec)) {
10839 vmscmd->dsc$a_pointer = PerlMem_malloc(MAX_DCL_LINE_LENGTH);
10840 if (vmscmd->dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
10842 strcpy(vmscmd->dsc$a_pointer,"$ MCR ");
10843 if (image_name[0] != 0) {
10844 strcat(vmscmd->dsc$a_pointer, image_name);
10845 strcat(vmscmd->dsc$a_pointer, " ");
10847 } else if (image_name[0] != 0) {
10848 strcpy(vmscmd->dsc$a_pointer, image_name);
10849 strcat(vmscmd->dsc$a_pointer, " ");
10851 strcpy(vmscmd->dsc$a_pointer,"@");
10853 if (suggest_quote) *suggest_quote = 1;
10855 /* If there is an image name, use original command */
10856 if (image_name[0] == 0)
10857 strcat(vmscmd->dsc$a_pointer,resspec);
10860 while (*rest && isspace(*rest)) rest++;
10863 if (image_argv[0] != 0) {
10864 strcat(vmscmd->dsc$a_pointer,image_argv);
10865 strcat(vmscmd->dsc$a_pointer, " ");
10871 rest_len = strlen(rest);
10872 vmscmd_len = strlen(vmscmd->dsc$a_pointer);
10873 if ((rest_len + vmscmd_len) < MAX_DCL_LINE_LENGTH)
10874 strcat(vmscmd->dsc$a_pointer,rest);
10876 retsts = CLI$_BUFOVF;
10878 vmscmd->dsc$w_length = strlen(vmscmd->dsc$a_pointer);
10880 PerlMem_free(vmsspec);
10881 PerlMem_free(resspec);
10882 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10888 /* It's either a DCL command or we couldn't find a suitable image */
10889 vmscmd->dsc$w_length = strlen(cmd);
10891 vmscmd->dsc$a_pointer = PerlMem_malloc(vmscmd->dsc$w_length + 1);
10892 strncpy(vmscmd->dsc$a_pointer,cmd,vmscmd->dsc$w_length);
10893 vmscmd->dsc$a_pointer[vmscmd->dsc$w_length] = 0;
10896 PerlMem_free(resspec);
10897 PerlMem_free(vmsspec);
10899 /* check if it's a symbol (for quoting purposes) */
10900 if (suggest_quote && !*suggest_quote) {
10902 char equiv[LNM$C_NAMLENGTH];
10903 struct dsc$descriptor_s eqvdsc = {sizeof(equiv), DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
10904 eqvdsc.dsc$a_pointer = equiv;
10906 iss = lib$get_symbol(vmscmd,&eqvdsc);
10907 if (iss&1 && (*equiv == '$' || *equiv == '@')) *suggest_quote = 1;
10909 if (!(retsts & 1)) {
10910 /* just hand off status values likely to be due to user error */
10911 if (retsts == RMS$_FNF || retsts == RMS$_DNF || retsts == RMS$_PRV ||
10912 retsts == RMS$_DEV || retsts == RMS$_DIR || retsts == RMS$_SYN ||
10913 (retsts & STS$M_CODE) == (SHR$_NOWILD & STS$M_CODE)) return retsts;
10914 else { _ckvmssts_noperl(retsts); }
10917 return (vmscmd->dsc$w_length > MAX_DCL_LINE_LENGTH ? CLI$_BUFOVF : retsts);
10919 } /* end of setup_cmddsc() */
10922 /* {{{ bool vms_do_aexec(SV *really,SV **mark,SV **sp) */
10924 Perl_vms_do_aexec(pTHX_ SV *really,SV **mark,SV **sp)
10930 if (vfork_called) { /* this follows a vfork - act Unixish */
10932 if (vfork_called < 0) {
10933 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10936 else return do_aexec(really,mark,sp);
10938 /* no vfork - act VMSish */
10939 cmd = setup_argstr(aTHX_ really,mark,sp);
10940 exec_sts = vms_do_exec(cmd);
10941 Safefree(cmd); /* Clean up from setup_argstr() */
10946 } /* end of vms_do_aexec() */
10949 /* {{{bool vms_do_exec(char *cmd) */
10951 Perl_vms_do_exec(pTHX_ const char *cmd)
10953 struct dsc$descriptor_s *vmscmd;
10955 if (vfork_called) { /* this follows a vfork - act Unixish */
10957 if (vfork_called < 0) {
10958 Perl_warn(aTHX_ "Internal inconsistency in tracking vforks");
10961 else return do_exec(cmd);
10964 { /* no vfork - act VMSish */
10965 unsigned long int retsts;
10968 TAINT_PROPER("exec");
10969 if ((retsts = setup_cmddsc(aTHX_ cmd,1,0,&vmscmd)) & 1)
10970 retsts = lib$do_command(vmscmd);
10973 case RMS$_FNF: case RMS$_DNF:
10974 set_errno(ENOENT); break;
10976 set_errno(ENOTDIR); break;
10978 set_errno(ENODEV); break;
10980 set_errno(EACCES); break;
10982 set_errno(EINVAL); break;
10983 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
10984 set_errno(E2BIG); break;
10985 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
10986 _ckvmssts_noperl(retsts); /* fall through */
10987 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
10988 set_errno(EVMSERR);
10990 set_vaxc_errno(retsts);
10991 if (ckWARN(WARN_EXEC)) {
10992 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't exec \"%*s\": %s",
10993 vmscmd->dsc$w_length, vmscmd->dsc$a_pointer, Strerror(errno));
10995 vms_execfree(vmscmd);
11000 } /* end of vms_do_exec() */
11003 int do_spawn2(pTHX_ const char *, int);
11006 Perl_do_aspawn(pTHX_ SV* really, SV** mark, SV** sp)
11008 unsigned long int sts;
11014 /* We'll copy the (undocumented?) Win32 behavior and allow a
11015 * numeric first argument. But the only value we'll support
11016 * through do_aspawn is a value of 1, which means spawn without
11017 * waiting for completion -- other values are ignored.
11019 if (SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
11021 flags = SvIVx(*mark);
11024 if (flags && flags == 1) /* the Win32 P_NOWAIT value */
11025 flags = CLI$M_NOWAIT;
11029 cmd = setup_argstr(aTHX_ really, mark, sp);
11030 sts = do_spawn2(aTHX_ cmd, flags);
11031 /* pp_sys will clean up cmd */
11035 } /* end of do_aspawn() */
11039 /* {{{int do_spawn(char* cmd) */
11041 Perl_do_spawn(pTHX_ char* cmd)
11043 PERL_ARGS_ASSERT_DO_SPAWN;
11045 return do_spawn2(aTHX_ cmd, 0);
11049 /* {{{int do_spawn_nowait(char* cmd) */
11051 Perl_do_spawn_nowait(pTHX_ char* cmd)
11053 PERL_ARGS_ASSERT_DO_SPAWN_NOWAIT;
11055 return do_spawn2(aTHX_ cmd, CLI$M_NOWAIT);
11059 /* {{{int do_spawn2(char *cmd) */
11061 do_spawn2(pTHX_ const char *cmd, int flags)
11063 unsigned long int sts, substs;
11065 /* The caller of this routine expects to Safefree(PL_Cmd) */
11066 Newx(PL_Cmd,10,char);
11069 TAINT_PROPER("spawn");
11070 if (!cmd || !*cmd) {
11071 sts = lib$spawn(0,0,0,&flags,0,0,&substs,0,0,0,0,0,0);
11074 case RMS$_FNF: case RMS$_DNF:
11075 set_errno(ENOENT); break;
11077 set_errno(ENOTDIR); break;
11079 set_errno(ENODEV); break;
11081 set_errno(EACCES); break;
11083 set_errno(EINVAL); break;
11084 case CLI$_BUFOVF: case RMS$_RTB: case CLI$_TKNOVF: case CLI$_RSLOVF:
11085 set_errno(E2BIG); break;
11086 case LIB$_INVARG: case LIB$_INVSTRDES: case SS$_ACCVIO: /* shouldn't happen */
11087 _ckvmssts_noperl(sts); /* fall through */
11088 default: /* SS$_DUPLNAM, SS$_CLI, resource exhaustion, etc. */
11089 set_errno(EVMSERR);
11091 set_vaxc_errno(sts);
11092 if (ckWARN(WARN_EXEC)) {
11093 Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn: %s",
11102 if (flags & CLI$M_NOWAIT)
11105 strcpy(mode, "nW");
11107 fp = safe_popen(aTHX_ cmd, mode, (int *)&sts);
11110 /* sts will be the pid in the nowait case */
11113 } /* end of do_spawn2() */
11117 static unsigned int *sockflags, sockflagsize;
11120 * Shim fdopen to identify sockets for my_fwrite later, since the stdio
11121 * routines found in some versions of the CRTL can't deal with sockets.
11122 * We don't shim the other file open routines since a socket isn't
11123 * likely to be opened by a name.
11125 /*{{{ FILE *my_fdopen(int fd, const char *mode)*/
11126 FILE *my_fdopen(int fd, const char *mode)
11128 FILE *fp = fdopen(fd, mode);
11131 unsigned int fdoff = fd / sizeof(unsigned int);
11132 Stat_t sbuf; /* native stat; we don't need flex_stat */
11133 if (!sockflagsize || fdoff > sockflagsize) {
11134 if (sockflags) Renew( sockflags,fdoff+2,unsigned int);
11135 else Newx (sockflags,fdoff+2,unsigned int);
11136 memset(sockflags+sockflagsize,0,fdoff + 2 - sockflagsize);
11137 sockflagsize = fdoff + 2;
11139 if (fstat(fd, &sbuf.crtl_stat) == 0 && S_ISSOCK(sbuf.st_mode))
11140 sockflags[fdoff] |= 1 << (fd % sizeof(unsigned int));
11149 * Clear the corresponding bit when the (possibly) socket stream is closed.
11150 * There still a small hole: we miss an implicit close which might occur
11151 * via freopen(). >> Todo
11153 /*{{{ int my_fclose(FILE *fp)*/
11154 int my_fclose(FILE *fp) {
11156 unsigned int fd = fileno(fp);
11157 unsigned int fdoff = fd / sizeof(unsigned int);
11159 if (sockflagsize && fdoff < sockflagsize)
11160 sockflags[fdoff] &= ~(1 << fd % sizeof(unsigned int));
11168 * A simple fwrite replacement which outputs itmsz*nitm chars without
11169 * introducing record boundaries every itmsz chars.
11170 * We are using fputs, which depends on a terminating null. We may
11171 * well be writing binary data, so we need to accommodate not only
11172 * data with nulls sprinkled in the middle but also data with no null
11175 /*{{{ int my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)*/
11177 my_fwrite(const void *src, size_t itmsz, size_t nitm, FILE *dest)
11179 register char *cp, *end, *cpd, *data;
11180 register unsigned int fd = fileno(dest);
11181 register unsigned int fdoff = fd / sizeof(unsigned int);
11183 int bufsize = itmsz * nitm + 1;
11185 if (fdoff < sockflagsize &&
11186 (sockflags[fdoff] | 1 << (fd % sizeof(unsigned int)))) {
11187 if (write(fd, src, itmsz * nitm) == EOF) return EOF;
11191 _ckvmssts_noperl(lib$get_vm(&bufsize, &data));
11192 memcpy( data, src, itmsz*nitm );
11193 data[itmsz*nitm] = '\0';
11195 end = data + itmsz * nitm;
11196 retval = (int) nitm; /* on success return # items written */
11199 while (cpd <= end) {
11200 for (cp = cpd; cp <= end; cp++) if (!*cp) break;
11201 if (fputs(cpd,dest) == EOF) { retval = EOF; break; }
11203 if (fputc('\0',dest) == EOF) { retval = EOF; break; }
11207 if (data) _ckvmssts_noperl(lib$free_vm(&bufsize, &data));
11210 } /* end of my_fwrite() */
11213 /*{{{ int my_flush(FILE *fp)*/
11215 Perl_my_flush(pTHX_ FILE *fp)
11218 if ((res = fflush(fp)) == 0 && fp) {
11219 #ifdef VMS_DO_SOCKETS
11221 if (fstat(fileno(fp), &s.crtl_stat) == 0 && !S_ISSOCK(s.st_mode))
11223 res = fsync(fileno(fp));
11226 * If the flush succeeded but set end-of-file, we need to clear
11227 * the error because our caller may check ferror(). BTW, this
11228 * probably means we just flushed an empty file.
11230 if (res == 0 && vaxc$errno == RMS$_EOF) clearerr(fp);
11237 * Here are replacements for the following Unix routines in the VMS environment:
11238 * getpwuid Get information for a particular UIC or UID
11239 * getpwnam Get information for a named user
11240 * getpwent Get information for each user in the rights database
11241 * setpwent Reset search to the start of the rights database
11242 * endpwent Finish searching for users in the rights database
11244 * getpwuid, getpwnam, and getpwent return a pointer to the passwd structure
11245 * (defined in pwd.h), which contains the following fields:-
11247 * char *pw_name; Username (in lower case)
11248 * char *pw_passwd; Hashed password
11249 * unsigned int pw_uid; UIC
11250 * unsigned int pw_gid; UIC group number
11251 * char *pw_unixdir; Default device/directory (VMS-style)
11252 * char *pw_gecos; Owner name
11253 * char *pw_dir; Default device/directory (Unix-style)
11254 * char *pw_shell; Default CLI name (eg. DCL)
11256 * If the specified user does not exist, getpwuid and getpwnam return NULL.
11258 * pw_uid is the full UIC (eg. what's returned by stat() in st_uid).
11259 * not the UIC member number (eg. what's returned by getuid()),
11260 * getpwuid() can accept either as input (if uid is specified, the caller's
11261 * UIC group is used), though it won't recognise gid=0.
11263 * Note that in VMS it is necessary to have GRPPRV or SYSPRV to return
11264 * information about other users in your group or in other groups, respectively.
11265 * If the required privilege is not available, then these routines fill only
11266 * the pw_name, pw_uid, and pw_gid fields (the others point to an empty
11269 * By Tim Adye (T.J.Adye@rl.ac.uk), 10th February 1995.
11272 /* sizes of various UAF record fields */
11273 #define UAI$S_USERNAME 12
11274 #define UAI$S_IDENT 31
11275 #define UAI$S_OWNER 31
11276 #define UAI$S_DEFDEV 31
11277 #define UAI$S_DEFDIR 63
11278 #define UAI$S_DEFCLI 31
11279 #define UAI$S_PWD 8
11281 #define valid_uic(uic) ((uic).uic$v_format == UIC$K_UIC_FORMAT && \
11282 (uic).uic$v_member != UIC$K_WILD_MEMBER && \
11283 (uic).uic$v_group != UIC$K_WILD_GROUP)
11285 static char __empty[]= "";
11286 static struct passwd __passwd_empty=
11287 {(char *) __empty, (char *) __empty, 0, 0,
11288 (char *) __empty, (char *) __empty, (char *) __empty, (char *) __empty};
11289 static int contxt= 0;
11290 static struct passwd __pwdcache;
11291 static char __pw_namecache[UAI$S_IDENT+1];
11294 * This routine does most of the work extracting the user information.
11296 static int fillpasswd (pTHX_ const char *name, struct passwd *pwd)
11299 unsigned char length;
11300 char pw_gecos[UAI$S_OWNER+1];
11302 static union uicdef uic;
11304 unsigned char length;
11305 char pw_dir[UAI$S_DEFDEV+UAI$S_DEFDIR+1];
11308 unsigned char length;
11309 char unixdir[UAI$_DEFDEV+UAI$S_DEFDIR+1];
11312 unsigned char length;
11313 char pw_shell[UAI$S_DEFCLI+1];
11315 static char pw_passwd[UAI$S_PWD+1];
11317 static unsigned short lowner, luic, ldefdev, ldefdir, ldefcli, lpwd;
11318 struct dsc$descriptor_s name_desc;
11319 unsigned long int sts;
11321 static struct itmlst_3 itmlst[]= {
11322 {UAI$S_OWNER+1, UAI$_OWNER, &owner, &lowner},
11323 {sizeof(uic), UAI$_UIC, &uic, &luic},
11324 {UAI$S_DEFDEV+1, UAI$_DEFDEV, &defdev, &ldefdev},
11325 {UAI$S_DEFDIR+1, UAI$_DEFDIR, &defdir, &ldefdir},
11326 {UAI$S_DEFCLI+1, UAI$_DEFCLI, &defcli, &ldefcli},
11327 {UAI$S_PWD, UAI$_PWD, pw_passwd, &lpwd},
11328 {0, 0, NULL, NULL}};
11330 name_desc.dsc$w_length= strlen(name);
11331 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11332 name_desc.dsc$b_class= DSC$K_CLASS_S;
11333 name_desc.dsc$a_pointer= (char *) name; /* read only pointer */
11335 /* Note that sys$getuai returns many fields as counted strings. */
11336 sts= sys$getuai(0, 0, &name_desc, &itmlst, 0, 0, 0);
11337 if (sts == SS$_NOSYSPRV || sts == SS$_NOGRPPRV || sts == RMS$_RNF) {
11338 set_vaxc_errno(sts); set_errno(sts == RMS$_RNF ? EINVAL : EACCES);
11340 else { _ckvmssts(sts); }
11341 if (!(sts & 1)) return 0; /* out here in case _ckvmssts() doesn't abort */
11343 if ((int) owner.length < lowner) lowner= (int) owner.length;
11344 if ((int) defdev.length < ldefdev) ldefdev= (int) defdev.length;
11345 if ((int) defdir.length < ldefdir) ldefdir= (int) defdir.length;
11346 if ((int) defcli.length < ldefcli) ldefcli= (int) defcli.length;
11347 memcpy(&defdev.pw_dir[ldefdev], &defdir.unixdir[0], ldefdir);
11348 owner.pw_gecos[lowner]= '\0';
11349 defdev.pw_dir[ldefdev+ldefdir]= '\0';
11350 defcli.pw_shell[ldefcli]= '\0';
11351 if (valid_uic(uic)) {
11352 pwd->pw_uid= uic.uic$l_uic;
11353 pwd->pw_gid= uic.uic$v_group;
11356 Perl_warn(aTHX_ "getpwnam returned invalid UIC %#o for user \"%s\"");
11357 pwd->pw_passwd= pw_passwd;
11358 pwd->pw_gecos= owner.pw_gecos;
11359 pwd->pw_dir= defdev.pw_dir;
11360 pwd->pw_unixdir= do_tounixpath(defdev.pw_dir, defdir.unixdir,1,NULL);
11361 pwd->pw_shell= defcli.pw_shell;
11362 if (pwd->pw_unixdir && pwd->pw_unixdir[0]) {
11364 ldir= strlen(pwd->pw_unixdir) - 1;
11365 if (pwd->pw_unixdir[ldir]=='/') pwd->pw_unixdir[ldir]= '\0';
11368 strcpy(pwd->pw_unixdir, pwd->pw_dir);
11369 if (!decc_efs_case_preserve)
11370 __mystrtolower(pwd->pw_unixdir);
11375 * Get information for a named user.
11377 /*{{{struct passwd *getpwnam(char *name)*/
11378 struct passwd *Perl_my_getpwnam(pTHX_ const char *name)
11380 struct dsc$descriptor_s name_desc;
11382 unsigned long int status, sts;
11384 __pwdcache = __passwd_empty;
11385 if (!fillpasswd(aTHX_ name, &__pwdcache)) {
11386 /* We still may be able to determine pw_uid and pw_gid */
11387 name_desc.dsc$w_length= strlen(name);
11388 name_desc.dsc$b_dtype= DSC$K_DTYPE_T;
11389 name_desc.dsc$b_class= DSC$K_CLASS_S;
11390 name_desc.dsc$a_pointer= (char *) name;
11391 if ((sts = sys$asctoid(&name_desc, &uic, 0)) == SS$_NORMAL) {
11392 __pwdcache.pw_uid= uic.uic$l_uic;
11393 __pwdcache.pw_gid= uic.uic$v_group;
11396 if (sts == SS$_NOSUCHID || sts == SS$_IVIDENT || sts == RMS$_PRV) {
11397 set_vaxc_errno(sts);
11398 set_errno(sts == RMS$_PRV ? EACCES : EINVAL);
11401 else { _ckvmssts(sts); }
11404 strncpy(__pw_namecache, name, sizeof(__pw_namecache));
11405 __pw_namecache[sizeof __pw_namecache - 1] = '\0';
11406 __pwdcache.pw_name= __pw_namecache;
11407 return &__pwdcache;
11408 } /* end of my_getpwnam() */
11412 * Get information for a particular UIC or UID.
11413 * Called by my_getpwent with uid=-1 to list all users.
11415 /*{{{struct passwd *my_getpwuid(Uid_t uid)*/
11416 struct passwd *Perl_my_getpwuid(pTHX_ Uid_t uid)
11418 const $DESCRIPTOR(name_desc,__pw_namecache);
11419 unsigned short lname;
11421 unsigned long int status;
11423 if (uid == (unsigned int) -1) {
11425 status = sys$idtoasc(-1, &lname, &name_desc, &uic, 0, &contxt);
11426 if (status == SS$_NOSUCHID || status == RMS$_PRV) {
11427 set_vaxc_errno(status);
11428 set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11432 else { _ckvmssts(status); }
11433 } while (!valid_uic (uic));
11436 uic.uic$l_uic= uid;
11437 if (!uic.uic$v_group)
11438 uic.uic$v_group= PerlProc_getgid();
11439 if (valid_uic(uic))
11440 status = sys$idtoasc(uic.uic$l_uic, &lname, &name_desc, 0, 0, 0);
11441 else status = SS$_IVIDENT;
11442 if (status == SS$_IVIDENT || status == SS$_NOSUCHID ||
11443 status == RMS$_PRV) {
11444 set_vaxc_errno(status); set_errno(status == RMS$_PRV ? EACCES : EINVAL);
11447 else { _ckvmssts(status); }
11449 __pw_namecache[lname]= '\0';
11450 __mystrtolower(__pw_namecache);
11452 __pwdcache = __passwd_empty;
11453 __pwdcache.pw_name = __pw_namecache;
11455 /* Fill in the uid and gid in case fillpasswd can't (eg. no privilege).
11456 The identifier's value is usually the UIC, but it doesn't have to be,
11457 so if we can, we let fillpasswd update this. */
11458 __pwdcache.pw_uid = uic.uic$l_uic;
11459 __pwdcache.pw_gid = uic.uic$v_group;
11461 fillpasswd(aTHX_ __pw_namecache, &__pwdcache);
11462 return &__pwdcache;
11464 } /* end of my_getpwuid() */
11468 * Get information for next user.
11470 /*{{{struct passwd *my_getpwent()*/
11471 struct passwd *Perl_my_getpwent(pTHX)
11473 return (my_getpwuid((unsigned int) -1));
11478 * Finish searching rights database for users.
11480 /*{{{void my_endpwent()*/
11481 void Perl_my_endpwent(pTHX)
11484 _ckvmssts(sys$finish_rdb(&contxt));
11490 #ifdef HOMEGROWN_POSIX_SIGNALS
11491 /* Signal handling routines, pulled into the core from POSIX.xs.
11493 * We need these for threads, so they've been rolled into the core,
11494 * rather than left in POSIX.xs.
11496 * (DRS, Oct 23, 1997)
11499 /* sigset_t is atomic under VMS, so these routines are easy */
11500 /*{{{int my_sigemptyset(sigset_t *) */
11501 int my_sigemptyset(sigset_t *set) {
11502 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11503 *set = 0; return 0;
11508 /*{{{int my_sigfillset(sigset_t *)*/
11509 int my_sigfillset(sigset_t *set) {
11511 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11512 for (i = 0; i < NSIG; i++) *set |= (1 << i);
11518 /*{{{int my_sigaddset(sigset_t *set, int sig)*/
11519 int my_sigaddset(sigset_t *set, int sig) {
11520 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11521 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11522 *set |= (1 << (sig - 1));
11528 /*{{{int my_sigdelset(sigset_t *set, int sig)*/
11529 int my_sigdelset(sigset_t *set, int sig) {
11530 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11531 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11532 *set &= ~(1 << (sig - 1));
11538 /*{{{int my_sigismember(sigset_t *set, int sig)*/
11539 int my_sigismember(sigset_t *set, int sig) {
11540 if (!set) { SETERRNO(EFAULT,SS$_ACCVIO); return -1; }
11541 if (sig > NSIG) { SETERRNO(EINVAL,LIB$_INVARG); return -1; }
11542 return *set & (1 << (sig - 1));
11547 /*{{{int my_sigprocmask(int how, sigset_t *set, sigset_t *oset)*/
11548 int my_sigprocmask(int how, sigset_t *set, sigset_t *oset) {
11551 /* If set and oset are both null, then things are badly wrong. Bail out. */
11552 if ((oset == NULL) && (set == NULL)) {
11553 set_errno(EFAULT); set_vaxc_errno(SS$_ACCVIO);
11557 /* If set's null, then we're just handling a fetch. */
11559 tempmask = sigblock(0);
11564 tempmask = sigsetmask(*set);
11567 tempmask = sigblock(*set);
11570 tempmask = sigblock(0);
11571 sigsetmask(*oset & ~tempmask);
11574 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
11579 /* Did they pass us an oset? If so, stick our holding mask into it */
11586 #endif /* HOMEGROWN_POSIX_SIGNALS */
11589 /* Used for UTC calculation in my_gmtime(), my_localtime(), my_time(),
11590 * my_utime(), and flex_stat(), all of which operate on UTC unless
11591 * VMSISH_TIMES is true.
11593 /* method used to handle UTC conversions:
11594 * 1 == CRTL gmtime(); 2 == SYS$TIMEZONE_DIFFERENTIAL; 3 == no correction
11596 static int gmtime_emulation_type;
11597 /* number of secs to add to UTC POSIX-style time to get local time */
11598 static long int utc_offset_secs;
11600 /* We #defined 'gmtime', 'localtime', and 'time' as 'my_gmtime' etc.
11601 * in vmsish.h. #undef them here so we can call the CRTL routines
11610 * DEC C previous to 6.0 corrupts the behavior of the /prefix
11611 * qualifier with the extern prefix pragma. This provisional
11612 * hack circumvents this prefix pragma problem in previous
11615 #if defined(__VMS_VER) && __VMS_VER >= 70000000
11616 # if defined(VMS_WE_ARE_CASE_SENSITIVE) && (__DECC_VER < 60000000)
11617 # pragma __extern_prefix save
11618 # pragma __extern_prefix "" /* set to empty to prevent prefixing */
11619 # define gmtime decc$__utctz_gmtime
11620 # define localtime decc$__utctz_localtime
11621 # define time decc$__utc_time
11622 # pragma __extern_prefix restore
11624 struct tm *gmtime(), *localtime();
11630 static time_t toutc_dst(time_t loc) {
11633 if ((rsltmp = localtime(&loc)) == NULL) return -1;
11634 loc -= utc_offset_secs;
11635 if (rsltmp->tm_isdst) loc -= 3600;
11638 #define _toutc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11639 ((gmtime_emulation_type || my_time(NULL)), \
11640 (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
11641 ((secs) - utc_offset_secs))))
11643 static time_t toloc_dst(time_t utc) {
11646 utc += utc_offset_secs;
11647 if ((rsltmp = localtime(&utc)) == NULL) return -1;
11648 if (rsltmp->tm_isdst) utc += 3600;
11651 #define _toloc(secs) ((secs) == (time_t) -1 ? (time_t) -1 : \
11652 ((gmtime_emulation_type || my_time(NULL)), \
11653 (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
11654 ((secs) + utc_offset_secs))))
11656 #ifndef RTL_USES_UTC
11659 ucx$tz = "EST5EDT4,M4.1.0,M10.5.0" typical
11660 DST starts on 1st sun of april at 02:00 std time
11661 ends on last sun of october at 02:00 dst time
11662 see the UCX management command reference, SET CONFIG TIMEZONE
11663 for formatting info.
11665 No, it's not as general as it should be, but then again, NOTHING
11666 will handle UK times in a sensible way.
11671 parse the DST start/end info:
11672 (Jddd|ddd|Mmon.nth.dow)[/hh:mm:ss]
11676 tz_parse_startend(char *s, struct tm *w, int *past)
11678 int dinm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
11679 int ly, dozjd, d, m, n, hour, min, sec, j, k;
11684 if (!past) return 0;
11687 if (w->tm_year % 4 == 0) ly = 1;
11688 if (w->tm_year % 100 == 0) ly = 0;
11689 if (w->tm_year+1900 % 400 == 0) ly = 1;
11692 dozjd = isdigit(*s);
11693 if (*s == 'J' || *s == 'j' || dozjd) {
11694 if (!dozjd && !isdigit(*++s)) return 0;
11697 d = d*10 + *s++ - '0';
11699 d = d*10 + *s++ - '0';
11702 if (d == 0) return 0;
11703 if (d > 366) return 0;
11705 if (!dozjd && d > 58 && ly) d++; /* after 28 feb */
11708 } else if (*s == 'M' || *s == 'm') {
11709 if (!isdigit(*++s)) return 0;
11711 if (isdigit(*s)) m = 10*m + *s++ - '0';
11712 if (*s != '.') return 0;
11713 if (!isdigit(*++s)) return 0;
11715 if (n < 1 || n > 5) return 0;
11716 if (*s != '.') return 0;
11717 if (!isdigit(*++s)) return 0;
11719 if (d > 6) return 0;
11723 if (!isdigit(*++s)) return 0;
11725 if (isdigit(*s)) hour = 10*hour + *s++ - '0';
11727 if (!isdigit(*++s)) return 0;
11729 if (isdigit(*s)) min = 10*min + *s++ - '0';
11731 if (!isdigit(*++s)) return 0;
11733 if (isdigit(*s)) sec = 10*sec + *s++ - '0';
11743 if (w->tm_yday < d) goto before;
11744 if (w->tm_yday > d) goto after;
11746 if (w->tm_mon+1 < m) goto before;
11747 if (w->tm_mon+1 > m) goto after;
11749 j = (42 + w->tm_wday - w->tm_mday)%7; /*dow of mday 0 */
11750 k = d - j; /* mday of first d */
11751 if (k <= 0) k += 7;
11752 k += 7 * ((n>4?4:n)-1); /* mday of n'th d */
11753 if (n == 5 && k+7 <= dinm[w->tm_mon]) k += 7;
11754 if (w->tm_mday < k) goto before;
11755 if (w->tm_mday > k) goto after;
11758 if (w->tm_hour < hour) goto before;
11759 if (w->tm_hour > hour) goto after;
11760 if (w->tm_min < min) goto before;
11761 if (w->tm_min > min) goto after;
11762 if (w->tm_sec < sec) goto before;
11776 /* parse the offset: (+|-)hh[:mm[:ss]] */
11779 tz_parse_offset(char *s, int *offset)
11781 int hour = 0, min = 0, sec = 0;
11784 if (!offset) return 0;
11786 if (*s == '-') {neg++; s++;}
11787 if (*s == '+') s++;
11788 if (!isdigit(*s)) return 0;
11790 if (isdigit(*s)) hour = hour*10+(*s++ - '0');
11791 if (hour > 24) return 0;
11793 if (!isdigit(*++s)) return 0;
11795 if (isdigit(*s)) min = min*10 + (*s++ - '0');
11796 if (min > 59) return 0;
11798 if (!isdigit(*++s)) return 0;
11800 if (isdigit(*s)) sec = sec*10 + (*s++ - '0');
11801 if (sec > 59) return 0;
11805 *offset = (hour*60+min)*60 + sec;
11806 if (neg) *offset = -*offset;
11811 input time is w, whatever type of time the CRTL localtime() uses.
11812 sets dst, the zone, and the gmtoff (seconds)
11814 caches the value of TZ and UCX$TZ env variables; note that
11815 my_setenv looks for these and sets a flag if they're changed
11818 We have to watch out for the "australian" case (dst starts in
11819 october, ends in april)...flagged by "reverse" and checked by
11820 scanning through the months of the previous year.
11825 tz_parse(pTHX_ time_t *w, int *dst, char *zone, int *gmtoff)
11830 char *dstzone, *tz, *s_start, *s_end;
11831 int std_off, dst_off, isdst;
11832 int y, dststart, dstend;
11833 static char envtz[1025]; /* longer than any logical, symbol, ... */
11834 static char ucxtz[1025];
11835 static char reversed = 0;
11841 reversed = -1; /* flag need to check */
11842 envtz[0] = ucxtz[0] = '\0';
11843 tz = my_getenv("TZ",0);
11844 if (tz) strcpy(envtz, tz);
11845 tz = my_getenv("UCX$TZ",0);
11846 if (tz) strcpy(ucxtz, tz);
11847 if (!envtz[0] && !ucxtz[0]) return 0; /* we give up */
11850 if (!*tz) tz = ucxtz;
11853 while (isalpha(*s)) s++;
11854 s = tz_parse_offset(s, &std_off);
11856 if (!*s) { /* no DST, hurray we're done! */
11862 while (isalpha(*s)) s++;
11863 s2 = tz_parse_offset(s, &dst_off);
11867 dst_off = std_off - 3600;
11870 if (!*s) { /* default dst start/end?? */
11871 if (tz != ucxtz) { /* if TZ tells zone only, UCX$TZ tells rule */
11872 s = strchr(ucxtz,',');
11874 if (!s || !*s) s = ",M4.1.0,M10.5.0"; /* we know we do dst, default rule */
11876 if (*s != ',') return 0;
11879 when = _toutc(when); /* convert to utc */
11880 when = when - std_off; /* convert to pseudolocal time*/
11882 w2 = localtime(&when);
11885 s = tz_parse_startend(s_start,w2,&dststart);
11887 if (*s != ',') return 0;
11890 when = _toutc(when); /* convert to utc */
11891 when = when - dst_off; /* convert to pseudolocal time*/
11892 w2 = localtime(&when);
11893 if (w2->tm_year != y) { /* spans a year, just check one time */
11894 when += dst_off - std_off;
11895 w2 = localtime(&when);
11898 s = tz_parse_startend(s_end,w2,&dstend);
11901 if (reversed == -1) { /* need to check if start later than end */
11905 if (when < 2*365*86400) {
11906 when += 2*365*86400;
11910 w2 =localtime(&when);
11911 when = when + (15 - w2->tm_yday) * 86400; /* jan 15 */
11913 for (j = 0; j < 12; j++) {
11914 w2 =localtime(&when);
11915 tz_parse_startend(s_start,w2,&ds);
11916 tz_parse_startend(s_end,w2,&de);
11917 if (ds != de) break;
11921 if (de && !ds) reversed = 1;
11924 isdst = dststart && !dstend;
11925 if (reversed) isdst = dststart || !dstend;
11928 if (dst) *dst = isdst;
11929 if (gmtoff) *gmtoff = isdst ? dst_off : std_off;
11930 if (isdst) tz = dstzone;
11932 while(isalpha(*tz)) *zone++ = *tz++;
11938 #endif /* !RTL_USES_UTC */
11940 /* my_time(), my_localtime(), my_gmtime()
11941 * By default traffic in UTC time values, using CRTL gmtime() or
11942 * SYS$TIMEZONE_DIFFERENTIAL to determine offset from local time zone.
11943 * Note: We need to use these functions even when the CRTL has working
11944 * UTC support, since they also handle C<use vmsish qw(times);>
11946 * Contributed by Chuck Lane <lane@duphy4.physics.drexel.edu>
11947 * Modified by Charles Bailey <bailey@newman.upenn.edu>
11950 /*{{{time_t my_time(time_t *timep)*/
11951 time_t Perl_my_time(pTHX_ time_t *timep)
11956 if (gmtime_emulation_type == 0) {
11958 time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between */
11959 /* results of calls to gmtime() and localtime() */
11960 /* for same &base */
11962 gmtime_emulation_type++;
11963 if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
11964 char off[LNM$C_NAMLENGTH+1];;
11966 gmtime_emulation_type++;
11967 if (!vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
11968 gmtime_emulation_type++;
11969 utc_offset_secs = 0;
11970 Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
11972 else { utc_offset_secs = atol(off); }
11974 else { /* We've got a working gmtime() */
11975 struct tm gmt, local;
11978 tm_p = localtime(&base);
11980 utc_offset_secs = (local.tm_mday - gmt.tm_mday) * 86400;
11981 utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
11982 utc_offset_secs += (local.tm_min - gmt.tm_min) * 60;
11983 utc_offset_secs += (local.tm_sec - gmt.tm_sec);
11988 # ifdef VMSISH_TIME
11989 # ifdef RTL_USES_UTC
11990 if (VMSISH_TIME) when = _toloc(when);
11992 if (!VMSISH_TIME) when = _toutc(when);
11995 if (timep != NULL) *timep = when;
11998 } /* end of my_time() */
12002 /*{{{struct tm *my_gmtime(const time_t *timep)*/
12004 Perl_my_gmtime(pTHX_ const time_t *timep)
12010 if (timep == NULL) {
12011 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12014 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
12017 # ifdef VMSISH_TIME
12018 if (VMSISH_TIME) when = _toutc(when); /* Input was local time */
12020 # ifdef RTL_USES_UTC /* this implies that the CRTL has a working gmtime() */
12021 return gmtime(&when);
12023 /* CRTL localtime() wants local time as input, so does no tz correction */
12024 rsltmp = localtime(&when);
12025 if (rsltmp) rsltmp->tm_isdst = 0; /* We already took DST into account */
12028 } /* end of my_gmtime() */
12032 /*{{{struct tm *my_localtime(const time_t *timep)*/
12034 Perl_my_localtime(pTHX_ const time_t *timep)
12036 time_t when, whenutc;
12040 if (timep == NULL) {
12041 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12044 if (*timep == 0) gmtime_emulation_type = 0; /* possibly reset TZ */
12045 if (gmtime_emulation_type == 0) my_time(NULL); /* Init UTC */
12048 # ifdef RTL_USES_UTC
12049 # ifdef VMSISH_TIME
12050 if (VMSISH_TIME) when = _toutc(when);
12052 /* CRTL localtime() wants UTC as input, does tz correction itself */
12053 return localtime(&when);
12055 # else /* !RTL_USES_UTC */
12057 # ifdef VMSISH_TIME
12058 if (!VMSISH_TIME) when = _toloc(whenutc); /* input was UTC */
12059 if (VMSISH_TIME) whenutc = _toutc(when); /* input was truelocal */
12062 #ifndef RTL_USES_UTC
12063 if (tz_parse(aTHX_ &when, &dst, 0, &offset)) { /* truelocal determines DST*/
12064 when = whenutc - offset; /* pseudolocal time*/
12067 /* CRTL localtime() wants local time as input, so does no tz correction */
12068 rsltmp = localtime(&when);
12069 if (rsltmp && gmtime_emulation_type != 1) rsltmp->tm_isdst = dst;
12073 } /* end of my_localtime() */
12076 /* Reset definitions for later calls */
12077 #define gmtime(t) my_gmtime(t)
12078 #define localtime(t) my_localtime(t)
12079 #define time(t) my_time(t)
12082 /* my_utime - update modification/access time of a file
12084 * VMS 7.3 and later implementation
12085 * Only the UTC translation is home-grown. The rest is handled by the
12086 * CRTL utime(), which will take into account the relevant feature
12087 * logicals and ODS-5 volume characteristics for true access times.
12089 * pre VMS 7.3 implementation:
12090 * The calling sequence is identical to POSIX utime(), but under
12091 * VMS with ODS-2, only the modification time is changed; ODS-2 does
12092 * not maintain access times. Restrictions differ from the POSIX
12093 * definition in that the time can be changed as long as the
12094 * caller has permission to execute the necessary IO$_MODIFY $QIO;
12095 * no separate checks are made to insure that the caller is the
12096 * owner of the file or has special privs enabled.
12097 * Code here is based on Joe Meadows' FILE utility.
12101 /* Adjustment from Unix epoch (01-JAN-1970 00:00:00.00)
12102 * to VMS epoch (01-JAN-1858 00:00:00.00)
12103 * in 100 ns intervals.
12105 static const long int utime_baseadjust[2] = { 0x4beb4000, 0x7c9567 };
12107 /*{{{int my_utime(const char *path, const struct utimbuf *utimes)*/
12108 int Perl_my_utime(pTHX_ const char *file, const struct utimbuf *utimes)
12110 #if __CRTL_VER >= 70300000
12111 struct utimbuf utc_utimes, *utc_utimesp;
12113 if (utimes != NULL) {
12114 utc_utimes.actime = utimes->actime;
12115 utc_utimes.modtime = utimes->modtime;
12116 # ifdef VMSISH_TIME
12117 /* If input was local; convert to UTC for sys svc */
12119 utc_utimes.actime = _toutc(utimes->actime);
12120 utc_utimes.modtime = _toutc(utimes->modtime);
12123 utc_utimesp = &utc_utimes;
12126 utc_utimesp = NULL;
12129 return utime(file, utc_utimesp);
12131 #else /* __CRTL_VER < 70300000 */
12135 long int bintime[2], len = 2, lowbit, unixtime,
12136 secscale = 10000000; /* seconds --> 100 ns intervals */
12137 unsigned long int chan, iosb[2], retsts;
12138 char vmsspec[NAM$C_MAXRSS+1], rsa[NAM$C_MAXRSS], esa[NAM$C_MAXRSS];
12139 struct FAB myfab = cc$rms_fab;
12140 struct NAM mynam = cc$rms_nam;
12141 #if defined (__DECC) && defined (__VAX)
12142 /* VAX DEC C atrdef.h has unsigned type for pointer member atr$l_addr,
12143 * at least through VMS V6.1, which causes a type-conversion warning.
12145 # pragma message save
12146 # pragma message disable cvtdiftypes
12148 struct atrdef myatr[2] = {{sizeof bintime, ATR$C_REVDATE, bintime}, {0,0,0}};
12149 struct fibdef myfib;
12150 #if defined (__DECC) && defined (__VAX)
12151 /* This should be right after the declaration of myatr, but due
12152 * to a bug in VAX DEC C, this takes effect a statement early.
12154 # pragma message restore
12156 /* cast ok for read only parameter */
12157 struct dsc$descriptor fibdsc = {sizeof(myfib), DSC$K_DTYPE_Z, DSC$K_CLASS_S,(char *) &myfib},
12158 devdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0},
12159 fnmdsc = {0,DSC$K_DTYPE_T, DSC$K_CLASS_S,0};
12161 if (file == NULL || *file == '\0') {
12162 SETERRNO(ENOENT, LIB$_INVARG);
12166 /* Convert to VMS format ensuring that it will fit in 255 characters */
12167 if (int_rmsexpand_tovms(file, vmsspec, 0) == NULL) {
12168 SETERRNO(ENOENT, LIB$_INVARG);
12171 if (utimes != NULL) {
12172 /* Convert Unix time (seconds since 01-JAN-1970 00:00:00.00)
12173 * to VMS quadword time (100 nsec intervals since 01-JAN-1858 00:00:00.00).
12174 * Since time_t is unsigned long int, and lib$emul takes a signed long int
12175 * as input, we force the sign bit to be clear by shifting unixtime right
12176 * one bit, then multiplying by an extra factor of 2 in lib$emul().
12178 lowbit = (utimes->modtime & 1) ? secscale : 0;
12179 unixtime = (long int) utimes->modtime;
12180 # ifdef VMSISH_TIME
12181 /* If input was UTC; convert to local for sys svc */
12182 if (!VMSISH_TIME) unixtime = _toloc(unixtime);
12184 unixtime >>= 1; secscale <<= 1;
12185 retsts = lib$emul(&secscale, &unixtime, &lowbit, bintime);
12186 if (!(retsts & 1)) {
12187 SETERRNO(EVMSERR, retsts);
12190 retsts = lib$addx(bintime,utime_baseadjust,bintime,&len);
12191 if (!(retsts & 1)) {
12192 SETERRNO(EVMSERR, retsts);
12197 /* Just get the current time in VMS format directly */
12198 retsts = sys$gettim(bintime);
12199 if (!(retsts & 1)) {
12200 SETERRNO(EVMSERR, retsts);
12205 myfab.fab$l_fna = vmsspec;
12206 myfab.fab$b_fns = (unsigned char) strlen(vmsspec);
12207 myfab.fab$l_nam = &mynam;
12208 mynam.nam$l_esa = esa;
12209 mynam.nam$b_ess = (unsigned char) sizeof esa;
12210 mynam.nam$l_rsa = rsa;
12211 mynam.nam$b_rss = (unsigned char) sizeof rsa;
12212 if (decc_efs_case_preserve)
12213 mynam.nam$b_nop |= NAM$M_NO_SHORT_UPCASE;
12215 /* Look for the file to be affected, letting RMS parse the file
12216 * specification for us as well. I have set errno using only
12217 * values documented in the utime() man page for VMS POSIX.
12219 retsts = sys$parse(&myfab,0,0);
12220 if (!(retsts & 1)) {
12221 set_vaxc_errno(retsts);
12222 if (retsts == RMS$_PRV) set_errno(EACCES);
12223 else if (retsts == RMS$_DIR) set_errno(ENOTDIR);
12224 else set_errno(EVMSERR);
12227 retsts = sys$search(&myfab,0,0);
12228 if (!(retsts & 1)) {
12229 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
12230 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
12231 set_vaxc_errno(retsts);
12232 if (retsts == RMS$_PRV) set_errno(EACCES);
12233 else if (retsts == RMS$_FNF) set_errno(ENOENT);
12234 else set_errno(EVMSERR);
12238 devdsc.dsc$w_length = mynam.nam$b_dev;
12239 /* cast ok for read only parameter */
12240 devdsc.dsc$a_pointer = (char *) mynam.nam$l_dev;
12242 retsts = sys$assign(&devdsc,&chan,0,0);
12243 if (!(retsts & 1)) {
12244 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
12245 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
12246 set_vaxc_errno(retsts);
12247 if (retsts == SS$_IVDEVNAM) set_errno(ENOTDIR);
12248 else if (retsts == SS$_NOPRIV) set_errno(EACCES);
12249 else if (retsts == SS$_NOSUCHDEV) set_errno(ENOTDIR);
12250 else set_errno(EVMSERR);
12254 fnmdsc.dsc$a_pointer = mynam.nam$l_name;
12255 fnmdsc.dsc$w_length = mynam.nam$b_name + mynam.nam$b_type + mynam.nam$b_ver;
12257 memset((void *) &myfib, 0, sizeof myfib);
12258 #if defined(__DECC) || defined(__DECCXX)
12259 for (i=0;i<3;i++) myfib.fib$w_fid[i] = mynam.nam$w_fid[i];
12260 for (i=0;i<3;i++) myfib.fib$w_did[i] = mynam.nam$w_did[i];
12261 /* This prevents the revision time of the file being reset to the current
12262 * time as a result of our IO$_MODIFY $QIO. */
12263 myfib.fib$l_acctl = FIB$M_NORECORD;
12265 for (i=0;i<3;i++) myfib.fib$r_fid_overlay.fib$w_fid[i] = mynam.nam$w_fid[i];
12266 for (i=0;i<3;i++) myfib.fib$r_did_overlay.fib$w_did[i] = mynam.nam$w_did[i];
12267 myfib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NORECORD;
12269 retsts = sys$qiow(0,chan,IO$_MODIFY,iosb,0,0,&fibdsc,&fnmdsc,0,0,myatr,0);
12270 mynam.nam$b_nop |= NAM$M_SYNCHK; mynam.nam$l_rlf = NULL;
12271 myfab.fab$b_dns = 0; sts = sys$parse(&myfab,0,0);
12272 _ckvmssts(sys$dassgn(chan));
12273 if (retsts & 1) retsts = iosb[0];
12274 if (!(retsts & 1)) {
12275 set_vaxc_errno(retsts);
12276 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12277 else set_errno(EVMSERR);
12283 #endif /* #if __CRTL_VER >= 70300000 */
12285 } /* end of my_utime() */
12289 * flex_stat, flex_lstat, flex_fstat
12290 * basic stat, but gets it right when asked to stat
12291 * a Unix-style path ending in a directory name (e.g. dir1/dir2/dir3)
12294 #ifndef _USE_STD_STAT
12295 /* encode_dev packs a VMS device name string into an integer to allow
12296 * simple comparisons. This can be used, for example, to check whether two
12297 * files are located on the same device, by comparing their encoded device
12298 * names. Even a string comparison would not do, because stat() reuses the
12299 * device name buffer for each call; so without encode_dev, it would be
12300 * necessary to save the buffer and use strcmp (this would mean a number of
12301 * changes to the standard Perl code, to say nothing of what a Perl script
12302 * would have to do.
12304 * The device lock id, if it exists, should be unique (unless perhaps compared
12305 * with lock ids transferred from other nodes). We have a lock id if the disk is
12306 * mounted cluster-wide, which is when we tend to get long (host-qualified)
12307 * device names. Thus we use the lock id in preference, and only if that isn't
12308 * available, do we try to pack the device name into an integer (flagged by
12309 * the sign bit (LOCKID_MASK) being set).
12311 * Note that encode_dev cannot guarantee an 1-to-1 correspondence twixt device
12312 * name and its encoded form, but it seems very unlikely that we will find
12313 * two files on different disks that share the same encoded device names,
12314 * and even more remote that they will share the same file id (if the test
12315 * is to check for the same file).
12317 * A better method might be to use sys$device_scan on the first call, and to
12318 * search for the device, returning an index into the cached array.
12319 * The number returned would be more intelligible.
12320 * This is probably not worth it, and anyway would take quite a bit longer
12321 * on the first call.
12323 #define LOCKID_MASK 0x80000000 /* Use 0 to force device name use only */
12324 static mydev_t encode_dev (pTHX_ const char *dev)
12327 unsigned long int f;
12332 if (!dev || !dev[0]) return 0;
12336 struct dsc$descriptor_s dev_desc;
12337 unsigned long int status, lockid = 0, item = DVI$_LOCKID;
12339 /* For cluster-mounted disks, the disk lock identifier is unique, so we
12340 can try that first. */
12341 dev_desc.dsc$w_length = strlen (dev);
12342 dev_desc.dsc$b_dtype = DSC$K_DTYPE_T;
12343 dev_desc.dsc$b_class = DSC$K_CLASS_S;
12344 dev_desc.dsc$a_pointer = (char *) dev; /* Read only parameter */
12345 status = lib$getdvi(&item, 0, &dev_desc, &lockid, 0, 0);
12346 if (!$VMS_STATUS_SUCCESS(status)) {
12348 case SS$_NOSUCHDEV:
12349 SETERRNO(ENODEV, status);
12355 if (lockid) return (lockid & ~LOCKID_MASK);
12359 /* Otherwise we try to encode the device name */
12363 for (q = dev + strlen(dev); q--; q >= dev) {
12368 else if (isalpha (toupper (*q)))
12369 c= toupper (*q) - 'A' + (char)10;
12371 continue; /* Skip '$'s */
12373 if (i>6) break; /* 36^7 is too large to fit in an unsigned long int */
12375 enc += f * (unsigned long int) c;
12377 return (enc | LOCKID_MASK); /* May have already overflowed into bit 31 */
12379 } /* end of encode_dev() */
12380 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12381 device_no = encode_dev(aTHX_ devname)
12383 #define VMS_DEVICE_ENCODE(device_no, devname, new_dev_no) \
12384 device_no = new_dev_no
12388 is_null_device(name)
12391 if (decc_bug_devnull != 0) {
12392 if (strncmp("/dev/null", name, 9) == 0)
12395 /* The VMS null device is named "_NLA0:", usually abbreviated as "NL:".
12396 The underscore prefix, controller letter, and unit number are
12397 independently optional; for our purposes, the colon punctuation
12398 is not. The colon can be trailed by optional directory and/or
12399 filename, but two consecutive colons indicates a nodename rather
12400 than a device. [pr] */
12401 if (*name == '_') ++name;
12402 if (tolower(*name++) != 'n') return 0;
12403 if (tolower(*name++) != 'l') return 0;
12404 if (tolower(*name) == 'a') ++name;
12405 if (*name == '0') ++name;
12406 return (*name++ == ':') && (*name != ':');
12410 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag);
12413 Perl_cando_by_name_int
12414 (pTHX_ I32 bit, bool effective, const char *fname, int opts)
12416 char usrname[L_cuserid];
12417 struct dsc$descriptor_s usrdsc =
12418 {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, usrname};
12419 char *vmsname = NULL, *fileified = NULL;
12420 unsigned long int objtyp = ACL$C_FILE, access, retsts, privused, iosb[2], flags;
12421 unsigned short int retlen, trnlnm_iter_count;
12422 struct dsc$descriptor_s namdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12423 union prvdef curprv;
12424 struct itmlst_3 armlst[4] = {{sizeof access, CHP$_ACCESS, &access, &retlen},
12425 {sizeof privused, CHP$_PRIVUSED, &privused, &retlen},
12426 {sizeof flags, CHP$_FLAGS, &flags, &retlen},{0,0,0,0}};
12427 struct itmlst_3 jpilst[3] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
12428 {sizeof usrname, JPI$_USERNAME, &usrname, &usrdsc.dsc$w_length},
12430 struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
12432 struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
12434 static int profile_context = -1;
12436 if (!fname || !*fname) return FALSE;
12438 /* Make sure we expand logical names, since sys$check_access doesn't */
12439 fileified = PerlMem_malloc(VMS_MAXRSS);
12440 if (fileified == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12441 if (!strpbrk(fname,"/]>:")) {
12442 strcpy(fileified,fname);
12443 trnlnm_iter_count = 0;
12444 while (!strpbrk(fileified,"/]>:") && my_trnlnm(fileified,fileified,0)) {
12445 trnlnm_iter_count++;
12446 if (trnlnm_iter_count >= PERL_LNM_MAX_ITER) break;
12451 vmsname = PerlMem_malloc(VMS_MAXRSS);
12452 if (vmsname == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12453 if ( !(opts & PERL_RMSEXPAND_M_VMS_IN) ) {
12454 /* Don't know if already in VMS format, so make sure */
12455 if (!do_rmsexpand(fname, vmsname, 0, NULL, PERL_RMSEXPAND_M_VMS, NULL, NULL)) {
12456 PerlMem_free(fileified);
12457 PerlMem_free(vmsname);
12462 strcpy(vmsname,fname);
12465 /* sys$check_access needs a file spec, not a directory spec.
12466 * flex_stat now will handle a null thread context during startup.
12469 retlen = namdsc.dsc$w_length = strlen(vmsname);
12470 if (vmsname[retlen-1] == ']'
12471 || vmsname[retlen-1] == '>'
12472 || vmsname[retlen-1] == ':'
12473 || (!Perl_flex_stat_int(NULL, vmsname, &st, 1) &&
12474 S_ISDIR(st.st_mode))) {
12476 if (!int_fileify_dirspec(vmsname, fileified, NULL)) {
12477 PerlMem_free(fileified);
12478 PerlMem_free(vmsname);
12487 retlen = namdsc.dsc$w_length = strlen(fname);
12488 namdsc.dsc$a_pointer = (char *)fname;
12491 case S_IXUSR: case S_IXGRP: case S_IXOTH:
12492 access = ARM$M_EXECUTE;
12493 flags = CHP$M_READ;
12495 case S_IRUSR: case S_IRGRP: case S_IROTH:
12496 access = ARM$M_READ;
12497 flags = CHP$M_READ | CHP$M_USEREADALL;
12499 case S_IWUSR: case S_IWGRP: case S_IWOTH:
12500 access = ARM$M_WRITE;
12501 flags = CHP$M_READ | CHP$M_WRITE;
12503 case S_IDUSR: case S_IDGRP: case S_IDOTH:
12504 access = ARM$M_DELETE;
12505 flags = CHP$M_READ | CHP$M_WRITE;
12508 if (fileified != NULL)
12509 PerlMem_free(fileified);
12510 if (vmsname != NULL)
12511 PerlMem_free(vmsname);
12515 /* Before we call $check_access, create a user profile with the current
12516 * process privs since otherwise it just uses the default privs from the
12517 * UAF and might give false positives or negatives. This only works on
12518 * VMS versions v6.0 and later since that's when sys$create_user_profile
12519 * became available.
12522 /* get current process privs and username */
12523 _ckvmssts_noperl(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
12524 _ckvmssts_noperl(iosb[0]);
12526 #if defined(__VMS_VER) && __VMS_VER >= 60000000
12528 /* find out the space required for the profile */
12529 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
12530 &usrprodsc.dsc$w_length,&profile_context));
12532 /* allocate space for the profile and get it filled in */
12533 usrprodsc.dsc$a_pointer = PerlMem_malloc(usrprodsc.dsc$w_length);
12534 if (usrprodsc.dsc$a_pointer == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12535 _ckvmssts_noperl(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
12536 &usrprodsc.dsc$w_length,&profile_context));
12538 /* use the profile to check access to the file; free profile & analyze results */
12539 retsts = sys$check_access(&objtyp,&namdsc,0,armlst,&profile_context,0,0,&usrprodsc);
12540 PerlMem_free(usrprodsc.dsc$a_pointer);
12541 if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
12545 retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
12549 if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
12550 retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
12551 retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
12552 set_vaxc_errno(retsts);
12553 if (retsts == SS$_NOPRIV) set_errno(EACCES);
12554 else if (retsts == SS$_INVFILFOROP) set_errno(EINVAL);
12555 else set_errno(ENOENT);
12556 if (fileified != NULL)
12557 PerlMem_free(fileified);
12558 if (vmsname != NULL)
12559 PerlMem_free(vmsname);
12562 if (retsts == SS$_NORMAL || retsts == SS$_ACCONFLICT) {
12563 if (fileified != NULL)
12564 PerlMem_free(fileified);
12565 if (vmsname != NULL)
12566 PerlMem_free(vmsname);
12569 _ckvmssts_noperl(retsts);
12571 if (fileified != NULL)
12572 PerlMem_free(fileified);
12573 if (vmsname != NULL)
12574 PerlMem_free(vmsname);
12575 return FALSE; /* Should never get here */
12579 /* Do the permissions allow some operation? Assumes PL_statcache already set. */
12580 /* Do this via $Check_Access on VMS, since the CRTL stat() returns only a
12581 * subset of the applicable information.
12584 Perl_cando(pTHX_ Mode_t bit, bool effective, const Stat_t *statbufp)
12586 return cando_by_name_int
12587 (bit, effective, statbufp->st_devnam, PERL_RMSEXPAND_M_VMS_IN);
12588 } /* end of cando() */
12592 /*{{{I32 cando_by_name(I32 bit, bool effective, char *fname)*/
12594 Perl_cando_by_name(pTHX_ I32 bit, bool effective, const char *fname)
12596 return cando_by_name_int(bit, effective, fname, 0);
12598 } /* end of cando_by_name() */
12602 /*{{{ int flex_fstat(int fd, Stat_t *statbuf)*/
12604 Perl_flex_fstat(pTHX_ int fd, Stat_t *statbufp)
12606 if (!fstat(fd, &statbufp->crtl_stat)) {
12608 char *vms_filename;
12609 vms_filename = PerlMem_malloc(VMS_MAXRSS);
12610 if (vms_filename == NULL) _ckvmssts(SS$_INSFMEM);
12612 /* Save name for cando by name in VMS format */
12613 cptr = getname(fd, vms_filename, 1);
12615 /* This should not happen, but just in case */
12616 if (cptr == NULL) {
12617 statbufp->st_devnam[0] = 0;
12620 /* Make sure that the saved name fits in 255 characters */
12621 cptr = int_rmsexpand_vms
12623 statbufp->st_devnam,
12626 statbufp->st_devnam[0] = 0;
12628 PerlMem_free(vms_filename);
12630 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12632 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12634 # ifdef RTL_USES_UTC
12635 # ifdef VMSISH_TIME
12637 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12638 statbufp->st_atime = _toloc(statbufp->st_atime);
12639 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12643 # ifdef VMSISH_TIME
12644 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12648 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12649 statbufp->st_atime = _toutc(statbufp->st_atime);
12650 statbufp->st_ctime = _toutc(statbufp->st_ctime);
12657 } /* end of flex_fstat() */
12660 #if !defined(__VAX) && __CRTL_VER >= 80200000
12668 #define lstat(_x, _y) stat(_x, _y)
12671 #define flex_stat_int(a,b,c) Perl_flex_stat_int(aTHX_ a,b,c)
12674 Perl_flex_stat_int(pTHX_ const char *fspec, Stat_t *statbufp, int lstat_flag)
12678 const char *save_spec;
12689 if (decc_bug_devnull != 0) {
12690 if (is_null_device(fspec)) { /* Fake a stat() for the null device */
12691 memset(statbufp,0,sizeof *statbufp);
12692 VMS_DEVICE_ENCODE(statbufp->st_dev, "_NLA0:", 0);
12693 statbufp->st_mode = S_IFBLK | S_IREAD | S_IWRITE | S_IEXEC;
12694 statbufp->st_uid = 0x00010001;
12695 statbufp->st_gid = 0x0001;
12696 time((time_t *)&statbufp->st_mtime);
12697 statbufp->st_atime = statbufp->st_ctime = statbufp->st_mtime;
12702 /* Try for a directory name first. If fspec contains a filename without
12703 * a type (e.g. sea:[wine.dark]water), and both sea:[wine.dark]water.dir
12704 * and sea:[wine.dark]water. exist, we prefer the directory here.
12705 * Similarly, sea:[wine.dark] returns the result for sea:[wine]dark.dir,
12706 * not sea:[wine.dark]., if the latter exists. If the intended target is
12707 * the file with null type, specify this by calling flex_stat() with
12708 * a '.' at the end of fspec.
12710 * If we are in Posix filespec mode, accept the filename as is.
12714 fileified = PerlMem_malloc(VMS_MAXRSS);
12715 if (fileified == NULL)
12716 _ckvmssts_noperl(SS$_INSFMEM);
12718 temp_fspec = PerlMem_malloc(VMS_MAXRSS);
12719 if (temp_fspec == NULL)
12720 _ckvmssts_noperl(SS$_INSFMEM);
12722 strcpy(temp_fspec, fspec);
12726 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12727 if (decc_posix_compliant_pathnames == 0) {
12730 /* We may be able to optimize this, but in order for fileify_dirspec to
12731 * always return a usuable answer, we have to call vmspath first to
12732 * make sure that it is in VMS directory format, as stat/lstat on 8.3
12733 * can not handle directories in unix format that it does not have read
12734 * access to. Vmspath handles the case where a bare name which could be
12735 * a logical name gets passed.
12737 ret_spec = int_tovmspath(fspec, temp_fspec, NULL);
12738 if (ret_spec != NULL) {
12739 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
12740 if (ret_spec != NULL) {
12741 if (lstat_flag == 0)
12742 retval = stat(fileified, &statbufp->crtl_stat);
12744 retval = lstat(fileified, &statbufp->crtl_stat);
12745 save_spec = fileified;
12749 if (retval && vms_bug_stat_filename) {
12751 /* We should try again as a vmsified file specification */
12752 /* However Perl traditionally has not done this, which */
12753 /* causes problems with existing tests */
12755 ret_spec = int_tovmsspec(fspec, temp_fspec, 0, NULL);
12756 if (ret_spec != NULL) {
12757 if (lstat_flag == 0)
12758 retval = stat(temp_fspec, &statbufp->crtl_stat);
12760 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12761 save_spec = temp_fspec;
12766 /* Last chance - allow multiple dots with out EFS CHARSET */
12767 /* The CRTL stat() falls down hard on multi-dot filenames in unix
12768 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
12769 * enable it if it isn't already.
12771 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12772 if (!decc_efs_charset && (decc_efs_charset_index > 0))
12773 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12775 if (lstat_flag == 0)
12776 retval = stat(fspec, &statbufp->crtl_stat);
12778 retval = lstat(fspec, &statbufp->crtl_stat);
12780 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12781 if (!decc_efs_charset && (decc_efs_charset_index > 0)) {
12782 decc$feature_set_value(decc_efs_charset_index, 1, 0);
12788 #if __CRTL_VER >= 80200000 && !defined(__VAX)
12790 if (lstat_flag == 0)
12791 retval = stat(temp_fspec, &statbufp->crtl_stat);
12793 retval = lstat(temp_fspec, &statbufp->crtl_stat);
12794 save_spec = temp_fspec;
12798 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12799 /* As you were... */
12800 if (!decc_efs_charset)
12801 decc$feature_set_value(decc$feature_get_index("DECC$EFS_CHARSET"),1,0);
12806 int rmsex_flags = PERL_RMSEXPAND_M_VMS;
12808 /* If this is an lstat, do not follow the link */
12810 rmsex_flags |= PERL_RMSEXPAND_M_SYMLINK;
12812 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12813 /* If we used the efs_hack above, we must also use it here for */
12814 /* perl_cando to work */
12815 if (efs_hack && (decc_efs_charset_index > 0)) {
12816 decc$feature_set_value(decc_efs_charset_index, 1, 1);
12819 cptr = int_rmsexpand_tovms(save_spec, statbufp->st_devnam, rmsex_flags);
12820 #if __CRTL_VER >= 70300000 && !defined(__VAX)
12821 if (efs_hack && (decc_efs_charset_index > 0)) {
12822 decc$feature_set_value(decc_efs_charset, 1, 0);
12826 /* Fix me: If this is NULL then stat found a file, and we could */
12827 /* not convert the specification to VMS - Should never happen */
12829 statbufp->st_devnam[0] = 0;
12831 VMS_INO_T_COPY(statbufp->st_ino, statbufp->crtl_stat.st_ino);
12833 (statbufp->st_dev, statbufp->st_devnam, statbufp->crtl_stat.st_dev);
12834 # ifdef RTL_USES_UTC
12835 # ifdef VMSISH_TIME
12837 statbufp->st_mtime = _toloc(statbufp->st_mtime);
12838 statbufp->st_atime = _toloc(statbufp->st_atime);
12839 statbufp->st_ctime = _toloc(statbufp->st_ctime);
12843 # ifdef VMSISH_TIME
12844 if (!VMSISH_TIME) { /* Return UTC instead of local time */
12848 statbufp->st_mtime = _toutc(statbufp->st_mtime);
12849 statbufp->st_atime = _toutc(statbufp->st_atime);
12850 statbufp->st_ctime = _toutc(statbufp->st_ctime);
12854 /* If we were successful, leave errno where we found it */
12855 if (retval == 0) RESTORE_ERRNO;
12858 } /* end of flex_stat_int() */
12861 /*{{{ int flex_stat(const char *fspec, Stat_t *statbufp)*/
12863 Perl_flex_stat(pTHX_ const char *fspec, Stat_t *statbufp)
12865 return flex_stat_int(fspec, statbufp, 0);
12869 /*{{{ int flex_lstat(const char *fspec, Stat_t *statbufp)*/
12871 Perl_flex_lstat(pTHX_ const char *fspec, Stat_t *statbufp)
12873 return flex_stat_int(fspec, statbufp, 1);
12878 /*{{{char *my_getlogin()*/
12879 /* VMS cuserid == Unix getlogin, except calling sequence */
12883 static char user[L_cuserid];
12884 return cuserid(user);
12889 /* rmscopy - copy a file using VMS RMS routines
12891 * Copies contents and attributes of spec_in to spec_out, except owner
12892 * and protection information. Name and type of spec_in are used as
12893 * defaults for spec_out. The third parameter specifies whether rmscopy()
12894 * should try to propagate timestamps from the input file to the output file.
12895 * If it is less than 0, no timestamps are preserved. If it is 0, then
12896 * rmscopy() will behave similarly to the DCL COPY command: timestamps are
12897 * propagated to the output file at creation iff the output file specification
12898 * did not contain an explicit name or type, and the revision date is always
12899 * updated at the end of the copy operation. If it is greater than 0, then
12900 * it is interpreted as a bitmask, in which bit 0 indicates that timestamps
12901 * other than the revision date should be propagated, and bit 1 indicates
12902 * that the revision date should be propagated.
12904 * Returns 1 on success; returns 0 and sets errno and vaxc$errno on failure.
12906 * Copyright 1996 by Charles Bailey <bailey@newman.upenn.edu>.
12907 * Incorporates, with permission, some code from EZCOPY by Tim Adye
12908 * <T.J.Adye@rl.ac.uk>. Permission is given to distribute this code
12909 * as part of the Perl standard distribution under the terms of the
12910 * GNU General Public License or the Perl Artistic License. Copies
12911 * of each may be found in the Perl standard distribution.
12913 /*{{{int rmscopy(char *src, char *dst, int preserve_dates)*/
12915 Perl_rmscopy(pTHX_ const char *spec_in, const char *spec_out, int preserve_dates)
12917 char *vmsin, * vmsout, *esa, *esal, *esa_out, *esal_out,
12918 *rsa, *rsal, *rsa_out, *rsal_out, *ubf;
12919 unsigned long int i, sts, sts2;
12921 struct FAB fab_in, fab_out;
12922 struct RAB rab_in, rab_out;
12923 rms_setup_nam(nam);
12924 rms_setup_nam(nam_out);
12925 struct XABDAT xabdat;
12926 struct XABFHC xabfhc;
12927 struct XABRDT xabrdt;
12928 struct XABSUM xabsum;
12930 vmsin = PerlMem_malloc(VMS_MAXRSS);
12931 if (vmsin == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12932 vmsout = PerlMem_malloc(VMS_MAXRSS);
12933 if (vmsout == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12934 if (!spec_in || !*spec_in || !int_tovmsspec(spec_in, vmsin, 1, NULL) ||
12935 !spec_out || !*spec_out || !int_tovmsspec(spec_out, vmsout, 1, NULL)) {
12936 PerlMem_free(vmsin);
12937 PerlMem_free(vmsout);
12938 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
12942 esa = PerlMem_malloc(VMS_MAXRSS);
12943 if (esa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12945 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12946 esal = PerlMem_malloc(VMS_MAXRSS);
12947 if (esal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12949 fab_in = cc$rms_fab;
12950 rms_set_fna(fab_in, nam, vmsin, strlen(vmsin));
12951 fab_in.fab$b_shr = FAB$M_SHRPUT | FAB$M_UPI;
12952 fab_in.fab$b_fac = FAB$M_BIO | FAB$M_GET;
12953 fab_in.fab$l_fop = FAB$M_SQO;
12954 rms_bind_fab_nam(fab_in, nam);
12955 fab_in.fab$l_xab = (void *) &xabdat;
12957 rsa = PerlMem_malloc(VMS_MAXRSS);
12958 if (rsa == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12960 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
12961 rsal = PerlMem_malloc(VMS_MAXRSS);
12962 if (rsal == NULL) _ckvmssts_noperl(SS$_INSFMEM);
12964 rms_set_rsal(nam, rsa, NAM$C_MAXRSS, rsal, (VMS_MAXRSS - 1));
12965 rms_set_esal(nam, esa, NAM$C_MAXRSS, esal, (VMS_MAXRSS - 1));
12966 rms_nam_esl(nam) = 0;
12967 rms_nam_rsl(nam) = 0;
12968 rms_nam_esll(nam) = 0;
12969 rms_nam_rsll(nam) = 0;
12970 #ifdef NAM$M_NO_SHORT_UPCASE
12971 if (decc_efs_case_preserve)
12972 rms_set_nam_nop(nam, NAM$M_NO_SHORT_UPCASE);
12975 xabdat = cc$rms_xabdat; /* To get creation date */
12976 xabdat.xab$l_nxt = (void *) &xabfhc;
12978 xabfhc = cc$rms_xabfhc; /* To get record length */
12979 xabfhc.xab$l_nxt = (void *) &xabsum;
12981 xabsum = cc$rms_xabsum; /* To get key and area information */
12983 if (!((sts = sys$open(&fab_in)) & 1)) {
12984 PerlMem_free(vmsin);
12985 PerlMem_free(vmsout);
12988 PerlMem_free(esal);
12991 PerlMem_free(rsal);
12992 set_vaxc_errno(sts);
12994 case RMS$_FNF: case RMS$_DNF:
12995 set_errno(ENOENT); break;
12997 set_errno(ENOTDIR); break;
12999 set_errno(ENODEV); break;
13001 set_errno(EINVAL); break;
13003 set_errno(EACCES); break;
13005 set_errno(EVMSERR);
13012 fab_out.fab$w_ifi = 0;
13013 fab_out.fab$b_fac = FAB$M_BIO | FAB$M_PUT;
13014 fab_out.fab$b_shr = FAB$M_SHRGET | FAB$M_UPI;
13015 fab_out.fab$l_fop = FAB$M_SQO;
13016 rms_bind_fab_nam(fab_out, nam_out);
13017 rms_set_fna(fab_out, nam_out, vmsout, strlen(vmsout));
13018 dna_len = rms_nam_namel(nam) ? rms_nam_name_type_l_size(nam) : 0;
13019 rms_set_dna(fab_out, nam_out, rms_nam_namel(nam), dna_len);
13020 esa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
13021 if (esa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13022 rsa_out = PerlMem_malloc(NAM$C_MAXRSS + 1);
13023 if (rsa_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13026 #if !defined(__VAX) && defined(NAML$C_MAXRSS)
13027 esal_out = PerlMem_malloc(VMS_MAXRSS);
13028 if (esal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13029 rsal_out = PerlMem_malloc(VMS_MAXRSS);
13030 if (rsal_out == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13032 rms_set_rsal(nam_out, rsa_out, NAM$C_MAXRSS, rsal_out, (VMS_MAXRSS - 1));
13033 rms_set_esal(nam_out, esa_out, NAM$C_MAXRSS, esal_out, (VMS_MAXRSS - 1));
13035 if (preserve_dates == 0) { /* Act like DCL COPY */
13036 rms_set_nam_nop(nam_out, NAM$M_SYNCHK);
13037 fab_out.fab$l_xab = NULL; /* Don't disturb data from input file */
13038 if (!((sts = sys$parse(&fab_out)) & STS$K_SUCCESS)) {
13039 PerlMem_free(vmsin);
13040 PerlMem_free(vmsout);
13043 PerlMem_free(esal);
13046 PerlMem_free(rsal);
13047 PerlMem_free(esa_out);
13048 if (esal_out != NULL)
13049 PerlMem_free(esal_out);
13050 PerlMem_free(rsa_out);
13051 if (rsal_out != NULL)
13052 PerlMem_free(rsal_out);
13053 set_errno(sts == RMS$_SYN ? EINVAL : EVMSERR);
13054 set_vaxc_errno(sts);
13057 fab_out.fab$l_xab = (void *) &xabdat;
13058 if (rms_is_nam_fnb(nam, NAM$M_EXP_NAME | NAM$M_EXP_TYPE))
13059 preserve_dates = 1;
13061 if (preserve_dates < 0) /* Clear all bits; we'll use it as a */
13062 preserve_dates =0; /* bitmask from this point forward */
13064 if (!(preserve_dates & 1)) fab_out.fab$l_xab = (void *) &xabfhc;
13065 if (!((sts = sys$create(&fab_out)) & STS$K_SUCCESS)) {
13066 PerlMem_free(vmsin);
13067 PerlMem_free(vmsout);
13070 PerlMem_free(esal);
13073 PerlMem_free(rsal);
13074 PerlMem_free(esa_out);
13075 if (esal_out != NULL)
13076 PerlMem_free(esal_out);
13077 PerlMem_free(rsa_out);
13078 if (rsal_out != NULL)
13079 PerlMem_free(rsal_out);
13080 set_vaxc_errno(sts);
13083 set_errno(ENOENT); break;
13085 set_errno(ENOTDIR); break;
13087 set_errno(ENODEV); break;
13089 set_errno(EINVAL); break;
13091 set_errno(EACCES); break;
13093 set_errno(EVMSERR);
13097 fab_out.fab$l_fop |= FAB$M_DLT; /* in case we have to bail out */
13098 if (preserve_dates & 2) {
13099 /* sys$close() will process xabrdt, not xabdat */
13100 xabrdt = cc$rms_xabrdt;
13102 xabrdt.xab$q_rdt = xabdat.xab$q_rdt;
13104 /* gcc doesn't like the assignment, since its prototype for xab$q_rdt
13105 * is unsigned long[2], while DECC & VAXC use a struct */
13106 memcpy(xabrdt.xab$q_rdt,xabdat.xab$q_rdt,sizeof xabrdt.xab$q_rdt);
13108 fab_out.fab$l_xab = (void *) &xabrdt;
13111 ubf = PerlMem_malloc(32256);
13112 if (ubf == NULL) _ckvmssts_noperl(SS$_INSFMEM);
13113 rab_in = cc$rms_rab;
13114 rab_in.rab$l_fab = &fab_in;
13115 rab_in.rab$l_rop = RAB$M_BIO;
13116 rab_in.rab$l_ubf = ubf;
13117 rab_in.rab$w_usz = 32256;
13118 if (!((sts = sys$connect(&rab_in)) & 1)) {
13119 sys$close(&fab_in); sys$close(&fab_out);
13120 PerlMem_free(vmsin);
13121 PerlMem_free(vmsout);
13125 PerlMem_free(esal);
13128 PerlMem_free(rsal);
13129 PerlMem_free(esa_out);
13130 if (esal_out != NULL)
13131 PerlMem_free(esal_out);
13132 PerlMem_free(rsa_out);
13133 if (rsal_out != NULL)
13134 PerlMem_free(rsal_out);
13135 set_errno(EVMSERR); set_vaxc_errno(sts);
13139 rab_out = cc$rms_rab;
13140 rab_out.rab$l_fab = &fab_out;
13141 rab_out.rab$l_rbf = ubf;
13142 if (!((sts = sys$connect(&rab_out)) & 1)) {
13143 sys$close(&fab_in); sys$close(&fab_out);
13144 PerlMem_free(vmsin);
13145 PerlMem_free(vmsout);
13149 PerlMem_free(esal);
13152 PerlMem_free(rsal);
13153 PerlMem_free(esa_out);
13154 if (esal_out != NULL)
13155 PerlMem_free(esal_out);
13156 PerlMem_free(rsa_out);
13157 if (rsal_out != NULL)
13158 PerlMem_free(rsal_out);
13159 set_errno(EVMSERR); set_vaxc_errno(sts);
13163 while ((sts = sys$read(&rab_in))) { /* always true */
13164 if (sts == RMS$_EOF) break;
13165 rab_out.rab$w_rsz = rab_in.rab$w_rsz;
13166 if (!(sts & 1) || !((sts = sys$write(&rab_out)) & 1)) {
13167 sys$close(&fab_in); sys$close(&fab_out);
13168 PerlMem_free(vmsin);
13169 PerlMem_free(vmsout);
13173 PerlMem_free(esal);
13176 PerlMem_free(rsal);
13177 PerlMem_free(esa_out);
13178 if (esal_out != NULL)
13179 PerlMem_free(esal_out);
13180 PerlMem_free(rsa_out);
13181 if (rsal_out != NULL)
13182 PerlMem_free(rsal_out);
13183 set_errno(EVMSERR); set_vaxc_errno(sts);
13189 fab_out.fab$l_fop &= ~FAB$M_DLT; /* We got this far; keep the output */
13190 sys$close(&fab_in); sys$close(&fab_out);
13191 sts = (fab_in.fab$l_sts & 1) ? fab_out.fab$l_sts : fab_in.fab$l_sts;
13193 PerlMem_free(vmsin);
13194 PerlMem_free(vmsout);
13198 PerlMem_free(esal);
13201 PerlMem_free(rsal);
13202 PerlMem_free(esa_out);
13203 if (esal_out != NULL)
13204 PerlMem_free(esal_out);
13205 PerlMem_free(rsa_out);
13206 if (rsal_out != NULL)
13207 PerlMem_free(rsal_out);
13210 set_errno(EVMSERR); set_vaxc_errno(sts);
13216 } /* end of rmscopy() */
13220 /*** The following glue provides 'hooks' to make some of the routines
13221 * from this file available from Perl. These routines are sufficiently
13222 * basic, and are required sufficiently early in the build process,
13223 * that's it's nice to have them available to miniperl as well as the
13224 * full Perl, so they're set up here instead of in an extension. The
13225 * Perl code which handles importation of these names into a given
13226 * package lives in [.VMS]Filespec.pm in @INC.
13230 rmsexpand_fromperl(pTHX_ CV *cv)
13233 char *fspec, *defspec = NULL, *rslt;
13235 int fs_utf8, dfs_utf8;
13239 if (!items || items > 2)
13240 Perl_croak(aTHX_ "Usage: VMS::Filespec::rmsexpand(spec[,defspec])");
13241 fspec = SvPV(ST(0),n_a);
13242 fs_utf8 = SvUTF8(ST(0));
13243 if (!fspec || !*fspec) XSRETURN_UNDEF;
13245 defspec = SvPV(ST(1),n_a);
13246 dfs_utf8 = SvUTF8(ST(1));
13248 rslt = do_rmsexpand(fspec,NULL,1,defspec,0,&fs_utf8,&dfs_utf8);
13249 ST(0) = sv_newmortal();
13250 if (rslt != NULL) {
13251 sv_usepvn(ST(0),rslt,strlen(rslt));
13260 vmsify_fromperl(pTHX_ CV *cv)
13267 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsify(spec)");
13268 utf8_fl = SvUTF8(ST(0));
13269 vmsified = do_tovmsspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13270 ST(0) = sv_newmortal();
13271 if (vmsified != NULL) {
13272 sv_usepvn(ST(0),vmsified,strlen(vmsified));
13281 unixify_fromperl(pTHX_ CV *cv)
13288 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixify(spec)");
13289 utf8_fl = SvUTF8(ST(0));
13290 unixified = do_tounixspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13291 ST(0) = sv_newmortal();
13292 if (unixified != NULL) {
13293 sv_usepvn(ST(0),unixified,strlen(unixified));
13302 fileify_fromperl(pTHX_ CV *cv)
13309 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::fileify(spec)");
13310 utf8_fl = SvUTF8(ST(0));
13311 fileified = do_fileify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13312 ST(0) = sv_newmortal();
13313 if (fileified != NULL) {
13314 sv_usepvn(ST(0),fileified,strlen(fileified));
13323 pathify_fromperl(pTHX_ CV *cv)
13330 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::pathify(spec)");
13331 utf8_fl = SvUTF8(ST(0));
13332 pathified = do_pathify_dirspec(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13333 ST(0) = sv_newmortal();
13334 if (pathified != NULL) {
13335 sv_usepvn(ST(0),pathified,strlen(pathified));
13344 vmspath_fromperl(pTHX_ CV *cv)
13351 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::vmspath(spec)");
13352 utf8_fl = SvUTF8(ST(0));
13353 vmspath = do_tovmspath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13354 ST(0) = sv_newmortal();
13355 if (vmspath != NULL) {
13356 sv_usepvn(ST(0),vmspath,strlen(vmspath));
13365 unixpath_fromperl(pTHX_ CV *cv)
13372 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::unixpath(spec)");
13373 utf8_fl = SvUTF8(ST(0));
13374 unixpath = do_tounixpath(SvPV(ST(0),n_a),NULL,1,&utf8_fl);
13375 ST(0) = sv_newmortal();
13376 if (unixpath != NULL) {
13377 sv_usepvn(ST(0),unixpath,strlen(unixpath));
13386 candelete_fromperl(pTHX_ CV *cv)
13394 if (items != 1) Perl_croak(aTHX_ "Usage: VMS::Filespec::candelete(spec)");
13396 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13397 Newx(fspec, VMS_MAXRSS, char);
13398 if (fspec == NULL) _ckvmssts(SS$_INSFMEM);
13399 if (SvTYPE(mysv) == SVt_PVGV) {
13400 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),fspec)) {
13401 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13409 if (mysv != ST(0) || !(fsp = SvPV(mysv,n_a)) || !*fsp) {
13410 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13417 ST(0) = boolSV(cando_by_name(S_IDUSR,0,fsp));
13423 rmscopy_fromperl(pTHX_ CV *cv)
13426 char *inspec, *outspec, *inp, *outp;
13428 struct dsc$descriptor indsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0},
13429 outdsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13430 unsigned long int sts;
13435 if (items < 2 || items > 3)
13436 Perl_croak(aTHX_ "Usage: File::Copy::rmscopy(from,to[,date_flag])");
13438 mysv = SvROK(ST(0)) ? SvRV(ST(0)) : ST(0);
13439 Newx(inspec, VMS_MAXRSS, char);
13440 if (SvTYPE(mysv) == SVt_PVGV) {
13441 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),inspec)) {
13442 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13450 if (mysv != ST(0) || !(inp = SvPV(mysv,n_a)) || !*inp) {
13451 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13457 mysv = SvROK(ST(1)) ? SvRV(ST(1)) : ST(1);
13458 Newx(outspec, VMS_MAXRSS, char);
13459 if (SvTYPE(mysv) == SVt_PVGV) {
13460 if (!(io = GvIOp(mysv)) || !PerlIO_getname(IoIFP(io),outspec)) {
13461 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13470 if (mysv != ST(1) || !(outp = SvPV(mysv,n_a)) || !*outp) {
13471 set_errno(EINVAL); set_vaxc_errno(LIB$_INVARG);
13478 date_flag = (items == 3) ? SvIV(ST(2)) : 0;
13480 ST(0) = boolSV(rmscopy(inp,outp,date_flag));
13486 /* The mod2fname is limited to shorter filenames by design, so it should
13487 * not be modified to support longer EFS pathnames
13490 mod2fname(pTHX_ CV *cv)
13493 char ultimate_name[NAM$C_MAXRSS+1], work_name[NAM$C_MAXRSS*8 + 1],
13494 workbuff[NAM$C_MAXRSS*1 + 1];
13495 int total_namelen = 3, counter, num_entries;
13496 /* ODS-5 ups this, but we want to be consistent, so... */
13497 int max_name_len = 39;
13498 AV *in_array = (AV *)SvRV(ST(0));
13500 num_entries = av_len(in_array);
13502 /* All the names start with PL_. */
13503 strcpy(ultimate_name, "PL_");
13505 /* Clean up our working buffer */
13506 Zero(work_name, sizeof(work_name), char);
13508 /* Run through the entries and build up a working name */
13509 for(counter = 0; counter <= num_entries; counter++) {
13510 /* If it's not the first name then tack on a __ */
13512 strcat(work_name, "__");
13514 strcat(work_name, SvPV_nolen(*av_fetch(in_array, counter, FALSE)));
13517 /* Check to see if we actually have to bother...*/
13518 if (strlen(work_name) + 3 <= max_name_len) {
13519 strcat(ultimate_name, work_name);
13521 /* It's too darned big, so we need to go strip. We use the same */
13522 /* algorithm as xsubpp does. First, strip out doubled __ */
13523 char *source, *dest, last;
13526 for (source = work_name; *source; source++) {
13527 if (last == *source && last == '_') {
13533 /* Go put it back */
13534 strcpy(work_name, workbuff);
13535 /* Is it still too big? */
13536 if (strlen(work_name) + 3 > max_name_len) {
13537 /* Strip duplicate letters */
13540 for (source = work_name; *source; source++) {
13541 if (last == toupper(*source)) {
13545 last = toupper(*source);
13547 strcpy(work_name, workbuff);
13550 /* Is it *still* too big? */
13551 if (strlen(work_name) + 3 > max_name_len) {
13552 /* Too bad, we truncate */
13553 work_name[max_name_len - 2] = 0;
13555 strcat(ultimate_name, work_name);
13558 /* Okay, return it */
13559 ST(0) = sv_2mortal(newSVpv(ultimate_name, 0));
13564 hushexit_fromperl(pTHX_ CV *cv)
13569 VMSISH_HUSHED = SvTRUE(ST(0));
13571 ST(0) = boolSV(VMSISH_HUSHED);
13577 Perl_vms_start_glob
13578 (pTHX_ SV *tmpglob,
13582 struct vs_str_st *rslt;
13586 $DESCRIPTOR(dfltdsc,"SYS$DISK:[]*.*;");
13589 struct dsc$descriptor_s wilddsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
13590 struct dsc$descriptor_vs rsdsc;
13591 unsigned long int cxt = 0, sts = 0, ok = 1, hasdir = 0;
13592 unsigned long hasver = 0, isunix = 0;
13593 unsigned long int lff_flags = 0;
13595 int vms_old_glob = 1;
13597 if (!SvOK(tmpglob)) {
13598 SETERRNO(ENOENT,RMS$_FNF);
13602 vms_old_glob = !decc_filename_unix_report;
13604 #ifdef VMS_LONGNAME_SUPPORT
13605 lff_flags = LIB$M_FIL_LONG_NAMES;
13607 /* The Newx macro will not allow me to assign a smaller array
13608 * to the rslt pointer, so we will assign it to the begin char pointer
13609 * and then copy the value into the rslt pointer.
13611 Newx(begin, VMS_MAXRSS + sizeof(unsigned short int), char);
13612 rslt = (struct vs_str_st *)begin;
13614 rstr = &rslt->str[0];
13615 rsdsc.dsc$a_pointer = (char *) rslt; /* cast required */
13616 rsdsc.dsc$w_maxstrlen = VMS_MAXRSS + sizeof(unsigned short int);
13617 rsdsc.dsc$b_dtype = DSC$K_DTYPE_VT;
13618 rsdsc.dsc$b_class = DSC$K_CLASS_VS;
13620 Newx(vmsspec, VMS_MAXRSS, char);
13622 /* We could find out if there's an explicit dev/dir or version
13623 by peeking into lib$find_file's internal context at
13624 ((struct NAM *)((struct FAB *)cxt)->fab$l_nam)->nam$l_fnb
13625 but that's unsupported, so I don't want to do it now and
13626 have it bite someone in the future. */
13627 /* Fix-me: vms_split_path() is the only way to do this, the
13628 existing method will fail with many legal EFS or UNIX specifications
13631 cp = SvPV(tmpglob,i);
13634 if (cp[i] == ';') hasver = 1;
13635 if (cp[i] == '.') {
13636 if (sts) hasver = 1;
13639 if (cp[i] == '/') {
13640 hasdir = isunix = 1;
13643 if (cp[i] == ']' || cp[i] == '>' || cp[i] == ':') {
13649 /* In UNIX report mode, assume UNIX unless VMS directory delimiters seen */
13650 if ((hasdir == 0) && decc_filename_unix_report) {
13654 if ((tmpfp = PerlIO_tmpfile()) != NULL) {
13655 char * wv_spec, * wr_spec, * wd_spec, * wn_spec, * we_spec, * wvs_spec;
13656 int wv_sts, wv_len, wr_len, wd_len, wn_len, we_len, wvs_len;
13662 stat_sts = PerlLIO_stat(SvPVX_const(tmpglob),&st);
13663 if (!stat_sts && S_ISDIR(st.st_mode)) {
13665 const char * fname;
13668 /* Test to see if SvPVX_const(tmpglob) ends with a VMS */
13669 /* path delimiter of ':>]', if so, then the old behavior has */
13670 /* obviously been specificially requested */
13672 fname = SvPVX_const(tmpglob);
13673 fname_len = strlen(fname);
13674 vms_dir = strpbrk(&fname[fname_len - 1], ":>]");
13675 if (vms_old_glob || (vms_dir != NULL)) {
13676 wilddsc.dsc$a_pointer = tovmspath_utf8(
13677 SvPVX(tmpglob),vmsspec,NULL);
13678 ok = (wilddsc.dsc$a_pointer != NULL);
13679 /* maybe passed 'foo' rather than '[.foo]', thus not
13683 /* Operate just on the directory, the special stat/fstat for */
13684 /* leaves the fileified specification in the st_devnam */
13686 wilddsc.dsc$a_pointer = st.st_devnam;
13691 wilddsc.dsc$a_pointer = tovmsspec_utf8(SvPVX(tmpglob),vmsspec,NULL);
13692 ok = (wilddsc.dsc$a_pointer != NULL);
13695 wilddsc.dsc$w_length = strlen(wilddsc.dsc$a_pointer);
13697 /* If not extended character set, replace ? with % */
13698 /* With extended character set, ? is a wildcard single character */
13699 for (cp=wilddsc.dsc$a_pointer; ok && cp && *cp; cp++) {
13702 if (!decc_efs_case_preserve)
13704 } else if (*cp == '%') {
13706 } else if (*cp == '*') {
13712 wv_sts = vms_split_path(
13713 wilddsc.dsc$a_pointer, &wv_spec, &wv_len, &wr_spec, &wr_len,
13714 &wd_spec, &wd_len, &wn_spec, &wn_len, &we_spec, &we_len,
13715 &wvs_spec, &wvs_len);
13724 while (ok && $VMS_STATUS_SUCCESS(sts)) {
13725 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
13726 int v_sts, v_len, r_len, d_len, n_len, e_len, vs_len;
13730 sts = lib$find_file(&wilddsc,&rsdsc,&cxt,
13731 &dfltdsc,NULL,&rms_sts,&lff_flags);
13732 if (!$VMS_STATUS_SUCCESS(sts))
13735 /* with varying string, 1st word of buffer contains result length */
13736 rstr[rslt->length] = '\0';
13738 /* Find where all the components are */
13739 v_sts = vms_split_path
13754 /* If no version on input, truncate the version on output */
13755 if (!hasver && (vs_len > 0)) {
13762 /* In Unix report mode, remove the ".dir;1" from the name */
13763 /* if it is a real directory */
13764 if (decc_filename_unix_report || decc_efs_charset) {
13765 if (is_dir_ext(e_spec, e_len, vs_spec, vs_len)) {
13769 ret_sts = flex_lstat(rstr, &statbuf);
13770 if ((ret_sts == 0) &&
13771 S_ISDIR(statbuf.st_mode)) {
13778 /* No version & a null extension on UNIX handling */
13779 if ((e_len == 1) && decc_readdir_dropdotnotype) {
13785 if (!decc_efs_case_preserve) {
13786 for (cp = rstr; *cp; cp++) *cp = _tolower(*cp);
13789 /* Find File treats a Null extension as return all extensions */
13790 /* This is contrary to Perl expectations */
13792 if (wildstar || wildquery || vms_old_glob) {
13793 /* really need to see if the returned file name matched */
13794 /* but for now will assume that it matches */
13797 /* Exact Match requested */
13798 /* How are directories handled? - like a file */
13799 if ((e_len == we_len) && (n_len == wn_len)) {
13803 t1 = strncmp(e_spec, we_spec, e_len);
13807 t1 = strncmp(n_spec, we_spec, n_len);
13818 if (isunix) trim_unixpath(rstr,SvPVX(tmpglob),1);
13822 /* Start with the name */
13825 strcat(begin,"\n");
13826 ok = (PerlIO_puts(tmpfp,begin) != EOF);
13829 if (cxt) (void)lib$find_file_end(&cxt);
13832 /* Be POSIXish: return the input pattern when no matches */
13833 strcpy(rstr,SvPVX(tmpglob));
13835 ok = (PerlIO_puts(tmpfp,rstr) != EOF);
13838 if (ok && sts != RMS$_NMF &&
13839 sts != RMS$_DNF && sts != RMS_FNF) ok = 0;
13842 SETERRNO((sts == RMS$_SYN ? EINVAL : EVMSERR),sts);
13844 PerlIO_close(tmpfp);
13848 PerlIO_rewind(tmpfp);
13849 IoTYPE(io) = IoTYPE_RDONLY;
13850 IoIFP(io) = fp = tmpfp;
13851 IoFLAGS(io) &= ~IOf_UNTAINT; /* maybe redundant */
13861 mp_do_vms_realpath(pTHX_ const char *filespec, char * rslt_spec,
13865 unixrealpath_fromperl(pTHX_ CV *cv)
13868 char *fspec, *rslt_spec, *rslt;
13871 if (!items || items != 1)
13872 Perl_croak(aTHX_ "Usage: VMS::Filespec::unixrealpath(spec)");
13874 fspec = SvPV(ST(0),n_a);
13875 if (!fspec || !*fspec) XSRETURN_UNDEF;
13877 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13878 rslt = do_vms_realpath(fspec, rslt_spec, NULL);
13880 ST(0) = sv_newmortal();
13882 sv_usepvn(ST(0),rslt,strlen(rslt));
13884 Safefree(rslt_spec);
13889 mp_do_vms_realname(pTHX_ const char *filespec, char * rslt_spec,
13893 vmsrealpath_fromperl(pTHX_ CV *cv)
13896 char *fspec, *rslt_spec, *rslt;
13899 if (!items || items != 1)
13900 Perl_croak(aTHX_ "Usage: VMS::Filespec::vmsrealpath(spec)");
13902 fspec = SvPV(ST(0),n_a);
13903 if (!fspec || !*fspec) XSRETURN_UNDEF;
13905 Newx(rslt_spec, VMS_MAXRSS + 1, char);
13906 rslt = do_vms_realname(fspec, rslt_spec, NULL);
13908 ST(0) = sv_newmortal();
13910 sv_usepvn(ST(0),rslt,strlen(rslt));
13912 Safefree(rslt_spec);
13918 * A thin wrapper around decc$symlink to make sure we follow the
13919 * standard and do not create a symlink with a zero-length name.
13921 * Also in ODS-2 mode, existing tests assume that the link target
13922 * will be converted to UNIX format.
13924 /*{{{ int my_symlink(pTHX_ const char *contents, const char *link_name)*/
13925 int Perl_my_symlink(pTHX_ const char *contents, const char *link_name) {
13926 if (!link_name || !*link_name) {
13927 SETERRNO(ENOENT, SS$_NOSUCHFILE);
13931 if (decc_efs_charset) {
13932 return symlink(contents, link_name);
13937 /* Unless we are in ODS-5 mode, convert the symlink target to UNIX */
13938 /* because in order to work, the symlink target must be in UNIX format */
13940 /* As symbolic links can hold things other than files, we will only do */
13941 /* the conversion in in ODS-2 mode */
13943 Newx(utarget, VMS_MAXRSS + 1, char);
13944 if (int_tounixspec(contents, utarget, NULL) == NULL) {
13946 /* This should not fail, as an untranslatable filename */
13947 /* should be passed through */
13948 utarget = (char *)contents;
13950 sts = symlink(utarget, link_name);
13958 #endif /* HAS_SYMLINK */
13960 int do_vms_case_tolerant(void);
13963 case_tolerant_process_fromperl(pTHX_ CV *cv)
13966 ST(0) = boolSV(do_vms_case_tolerant());
13970 #ifdef USE_ITHREADS
13973 Perl_sys_intern_dup(pTHX_ struct interp_intern *src,
13974 struct interp_intern *dst)
13976 PERL_ARGS_ASSERT_SYS_INTERN_DUP;
13978 memcpy(dst,src,sizeof(struct interp_intern));
13984 Perl_sys_intern_clear(pTHX)
13989 Perl_sys_intern_init(pTHX)
13991 unsigned int ix = RAND_MAX;
13996 MY_POSIX_EXIT = vms_posix_exit;
13999 MY_INV_RAND_MAX = 1./x;
14003 init_os_extras(void)
14006 char* file = __FILE__;
14007 if (decc_disable_to_vms_logname_translation) {
14008 no_translate_barewords = TRUE;
14010 no_translate_barewords = FALSE;
14013 newXSproto("VMS::Filespec::rmsexpand",rmsexpand_fromperl,file,"$;$");
14014 newXSproto("VMS::Filespec::vmsify",vmsify_fromperl,file,"$");
14015 newXSproto("VMS::Filespec::unixify",unixify_fromperl,file,"$");
14016 newXSproto("VMS::Filespec::pathify",pathify_fromperl,file,"$");
14017 newXSproto("VMS::Filespec::fileify",fileify_fromperl,file,"$");
14018 newXSproto("VMS::Filespec::vmspath",vmspath_fromperl,file,"$");
14019 newXSproto("VMS::Filespec::unixpath",unixpath_fromperl,file,"$");
14020 newXSproto("VMS::Filespec::candelete",candelete_fromperl,file,"$");
14021 newXSproto("DynaLoader::mod2fname", mod2fname, file, "$");
14022 newXS("File::Copy::rmscopy",rmscopy_fromperl,file);
14023 newXSproto("vmsish::hushed",hushexit_fromperl,file,";$");
14024 newXSproto("VMS::Filespec::unixrealpath",unixrealpath_fromperl,file,"$;$");
14025 newXSproto("VMS::Filespec::vmsrealpath",vmsrealpath_fromperl,file,"$;$");
14026 newXSproto("VMS::Filespec::case_tolerant_process",
14027 case_tolerant_process_fromperl,file,"");
14029 store_pipelocs(aTHX); /* will redo any earlier attempts */
14034 #if __CRTL_VER == 80200000
14035 /* This missed getting in to the DECC SDK for 8.2 */
14036 char *realpath(const char *file_name, char * resolved_name, ...);
14039 /*{{{char *do_vms_realpath(const char *file_name, char *resolved_name)*/
14040 /* wrapper for the realpath() function added with 8.2 RMS SYMLINK SDK.
14041 * The perl fallback routine to provide realpath() is not as efficient
14045 /* Hack, use old stat() as fastest way of getting ino_t and device */
14046 int decc$stat(const char *name, void * statbuf);
14047 #if !defined(__VAX) && __CRTL_VER >= 80200000
14048 int decc$lstat(const char *name, void * statbuf);
14050 #define decc$lstat decc$stat
14054 /* Realpath is fragile. In 8.3 it does not work if the feature
14055 * DECC$POSIX_COMPLIANT_PATHNAMES is not enabled, even though symbolic
14056 * links are implemented in RMS, not the CRTL. It also can fail if the
14057 * user does not have read/execute access to some of the directories.
14058 * So in order for Do What I Mean mode to work, if realpath() fails,
14059 * fall back to looking up the filename by the device name and FID.
14062 int vms_fid_to_name(char * outname, int outlen,
14063 const char * name, int lstat_flag, mode_t * mode)
14065 #pragma message save
14066 #pragma message disable MISALGNDSTRCT
14067 #pragma message disable MISALGNDMEM
14068 #pragma member_alignment save
14069 #pragma nomember_alignment
14072 unsigned short st_ino[3];
14073 unsigned short old_st_mode;
14074 unsigned long padl[30]; /* plenty of room */
14076 #pragma message restore
14077 #pragma member_alignment restore
14080 struct dsc$descriptor_s dvidsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14081 struct dsc$descriptor_s specdsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
14086 /* Need to follow the mostly the same rules as flex_stat_int, or we may get
14087 * unexpected answers
14090 fileified = PerlMem_malloc(VMS_MAXRSS);
14091 if (fileified == NULL)
14092 _ckvmssts_noperl(SS$_INSFMEM);
14094 temp_fspec = PerlMem_malloc(VMS_MAXRSS);
14095 if (temp_fspec == NULL)
14096 _ckvmssts_noperl(SS$_INSFMEM);
14099 /* First need to try as a directory */
14100 ret_spec = int_tovmspath(name, temp_fspec, NULL);
14101 if (ret_spec != NULL) {
14102 ret_spec = int_fileify_dirspec(temp_fspec, fileified, NULL);
14103 if (ret_spec != NULL) {
14104 if (lstat_flag == 0)
14105 sts = decc$stat(fileified, &statbuf);
14107 sts = decc$lstat(fileified, &statbuf);
14111 /* Then as a VMS file spec */
14113 ret_spec = int_tovmsspec(name, temp_fspec, 0, NULL);
14114 if (ret_spec != NULL) {
14115 if (lstat_flag == 0) {
14116 sts = decc$stat(temp_fspec, &statbuf);
14118 sts = decc$lstat(temp_fspec, &statbuf);
14124 /* Next try - allow multiple dots with out EFS CHARSET */
14125 /* The CRTL stat() falls down hard on multi-dot filenames in unix
14126 * format unless * DECC$EFS_CHARSET is in effect, so temporarily
14127 * enable it if it isn't already.
14129 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14130 if (!decc_efs_charset && (decc_efs_charset_index > 0))
14131 decc$feature_set_value(decc_efs_charset_index, 1, 1);
14133 ret_spec = int_tovmspath(name, temp_fspec, NULL);
14134 if (lstat_flag == 0) {
14135 sts = decc$stat(name, &statbuf);
14137 sts = decc$lstat(name, &statbuf);
14139 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14140 if (!decc_efs_charset && (decc_efs_charset_index > 0))
14141 decc$feature_set_value(decc_efs_charset_index, 1, 0);
14146 /* and then because the Perl Unix to VMS conversion is not perfect */
14147 /* Specifically the CRTL removes spaces and possibly other illegal ODS-2 */
14148 /* characters from filenames so we need to try it as-is */
14150 if (lstat_flag == 0) {
14151 sts = decc$stat(name, &statbuf);
14153 sts = decc$lstat(name, &statbuf);
14160 dvidsc.dsc$a_pointer=statbuf.st_dev;
14161 dvidsc.dsc$w_length=strlen(statbuf.st_dev);
14163 specdsc.dsc$a_pointer = outname;
14164 specdsc.dsc$w_length = outlen-1;
14166 vms_sts = lib$fid_to_name
14167 (&dvidsc, statbuf.st_ino, &specdsc, &specdsc.dsc$w_length);
14168 if ($VMS_STATUS_SUCCESS(vms_sts)) {
14169 outname[specdsc.dsc$w_length] = 0;
14171 /* Return the mode */
14173 *mode = statbuf.old_st_mode;
14184 mp_do_vms_realpath(pTHX_ const char *filespec, char *outbuf,
14187 char * rslt = NULL;
14190 if (decc_posix_compliant_pathnames > 0 ) {
14191 /* realpath currently only works if posix compliant pathnames are
14192 * enabled. It may start working when they are not, but in that
14193 * case we still want the fallback behavior for backwards compatibility
14195 rslt = realpath(filespec, outbuf);
14199 if (rslt == NULL) {
14201 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14202 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14206 /* Fall back to fid_to_name */
14208 Newx(vms_spec, VMS_MAXRSS + 1, char);
14210 sts = vms_fid_to_name(vms_spec, VMS_MAXRSS + 1, filespec, 0, &my_mode);
14214 /* Now need to trim the version off */
14215 sts = vms_split_path
14235 /* Trim off the version */
14236 int file_len = v_len + r_len + d_len + n_len + e_len;
14237 vms_spec[file_len] = 0;
14239 /* The result is expected to be in UNIX format */
14240 rslt = int_tounixspec(vms_spec, outbuf, utf8_fl);
14242 /* Downcase if input had any lower case letters and
14243 * case preservation is not in effect.
14245 if (!decc_efs_case_preserve) {
14246 for (cp = filespec; *cp; cp++)
14247 if (islower(*cp)) { haslower = 1; break; }
14249 if (haslower) __mystrtolower(rslt);
14254 /* Now for some hacks to deal with backwards and forward */
14256 if (!decc_efs_charset) {
14258 /* 1. ODS-2 mode wants to do a syntax only translation */
14259 rslt = int_rmsexpand(filespec, outbuf,
14260 NULL, 0, NULL, utf8_fl);
14263 if (decc_filename_unix_report) {
14265 char * vms_dir_name;
14268 /* 2. ODS-5 / UNIX report mode should return a failure */
14269 /* if the parent directory also does not exist */
14270 /* Otherwise, get the real path for the parent */
14271 /* and add the child to it.
14273 /* basename / dirname only available for VMS 7.0+ */
14274 /* So we may need to implement them as common routines */
14276 Newx(dir_name, VMS_MAXRSS + 1, char);
14277 Newx(vms_dir_name, VMS_MAXRSS + 1, char);
14278 dir_name[0] = '\0';
14281 /* First try a VMS parse */
14282 sts = vms_split_path
14300 int dir_len = v_len + r_len + d_len + n_len;
14302 strncpy(dir_name, filespec, dir_len);
14303 dir_name[dir_len] = '\0';
14304 file_name = (char *)&filespec[dir_len + 1];
14307 /* This must be UNIX */
14310 tchar = strrchr(filespec, '/');
14312 if (tchar != NULL) {
14313 int dir_len = tchar - filespec;
14314 strncpy(dir_name, filespec, dir_len);
14315 dir_name[dir_len] = '\0';
14316 file_name = (char *) &filespec[dir_len + 1];
14320 /* Dir name is defaulted */
14321 if (dir_name[0] == 0) {
14323 dir_name[1] = '\0';
14326 /* Need realpath for the directory */
14327 sts = vms_fid_to_name(vms_dir_name,
14329 dir_name, 0, NULL);
14332 /* Now need to pathify it.
14333 char *tdir = int_pathify_dirspec(vms_dir_name,
14336 /* And now add the original filespec to it */
14337 if (file_name != NULL) {
14338 strcat(outbuf, file_name);
14342 Safefree(vms_dir_name);
14343 Safefree(dir_name);
14347 Safefree(vms_spec);
14353 mp_do_vms_realname(pTHX_ const char *filespec, char *outbuf,
14356 char * v_spec, * r_spec, * d_spec, * n_spec, * e_spec, * vs_spec;
14357 int sts, v_len, r_len, d_len, n_len, e_len, vs_len;
14360 /* Fall back to fid_to_name */
14362 sts = vms_fid_to_name(outbuf, VMS_MAXRSS + 1, filespec, 0, NULL);
14369 /* Now need to trim the version off */
14370 sts = vms_split_path
14390 /* Trim off the version */
14391 int file_len = v_len + r_len + d_len + n_len + e_len;
14392 outbuf[file_len] = 0;
14394 /* Downcase if input had any lower case letters and
14395 * case preservation is not in effect.
14397 if (!decc_efs_case_preserve) {
14398 for (cp = filespec; *cp; cp++)
14399 if (islower(*cp)) { haslower = 1; break; }
14401 if (haslower) __mystrtolower(outbuf);
14410 /* External entry points */
14411 char *Perl_vms_realpath(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14412 { return do_vms_realpath(filespec, outbuf, utf8_fl); }
14414 char *Perl_vms_realname(pTHX_ const char *filespec, char *outbuf, int *utf8_fl)
14415 { return do_vms_realname(filespec, outbuf, utf8_fl); }
14417 /* case_tolerant */
14419 /*{{{int do_vms_case_tolerant(void)*/
14420 /* OpenVMS provides a case sensitive implementation of ODS-5 and this is
14421 * controlled by a process setting.
14423 int do_vms_case_tolerant(void)
14425 return vms_process_case_tolerant;
14428 /* External entry points */
14429 #if __CRTL_VER >= 70301000 && !defined(__VAX)
14430 int Perl_vms_case_tolerant(void)
14431 { return do_vms_case_tolerant(); }
14433 int Perl_vms_case_tolerant(void)
14434 { return vms_process_case_tolerant; }
14438 /* Start of DECC RTL Feature handling */
14440 static int sys_trnlnm
14441 (const char * logname,
14445 const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV");
14446 const unsigned long attr = LNM$M_CASE_BLIND;
14447 struct dsc$descriptor_s name_dsc;
14449 unsigned short result;
14450 struct itmlst_3 itlst[2] = {{value_len, LNM$_STRING, value, &result},
14453 name_dsc.dsc$w_length = strlen(logname);
14454 name_dsc.dsc$a_pointer = (char *)logname;
14455 name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14456 name_dsc.dsc$b_class = DSC$K_CLASS_S;
14458 status = sys$trnlnm(&attr, &table_dsc, &name_dsc, 0, itlst);
14460 if ($VMS_STATUS_SUCCESS(status)) {
14462 /* Null terminate and return the string */
14463 /*--------------------------------------*/
14470 static int sys_crelnm
14471 (const char * logname,
14472 const char * value)
14475 const char * proc_table = "LNM$PROCESS_TABLE";
14476 struct dsc$descriptor_s proc_table_dsc;
14477 struct dsc$descriptor_s logname_dsc;
14478 struct itmlst_3 item_list[2];
14480 proc_table_dsc.dsc$a_pointer = (char *) proc_table;
14481 proc_table_dsc.dsc$w_length = strlen(proc_table);
14482 proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14483 proc_table_dsc.dsc$b_class = DSC$K_CLASS_S;
14485 logname_dsc.dsc$a_pointer = (char *) logname;
14486 logname_dsc.dsc$w_length = strlen(logname);
14487 logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
14488 logname_dsc.dsc$b_class = DSC$K_CLASS_S;
14490 item_list[0].buflen = strlen(value);
14491 item_list[0].itmcode = LNM$_STRING;
14492 item_list[0].bufadr = (char *)value;
14493 item_list[0].retlen = NULL;
14495 item_list[1].buflen = 0;
14496 item_list[1].itmcode = 0;
14498 ret_val = sys$crelnm
14500 (const struct dsc$descriptor_s *)&proc_table_dsc,
14501 (const struct dsc$descriptor_s *)&logname_dsc,
14503 (const struct item_list_3 *) item_list);
14508 /* C RTL Feature settings */
14510 static int set_features
14511 (int (* init_coroutine)(int *, int *, void *), /* Needs casts if used */
14512 int (* cli_routine)(void), /* Not documented */
14513 void *image_info) /* Not documented */
14519 #if defined(JPI$_CASE_LOOKUP_PERM) && !defined(__VAX)
14520 const unsigned long int jpicode1 = JPI$_CASE_LOOKUP_PERM;
14521 const unsigned long int jpicode2 = JPI$_CASE_LOOKUP_IMAGE;
14522 unsigned long case_perm;
14523 unsigned long case_image;
14526 /* Allow an exception to bring Perl into the VMS debugger */
14527 vms_debug_on_exception = 0;
14528 status = sys_trnlnm("PERL_VMS_EXCEPTION_DEBUG", val_str, sizeof(val_str));
14529 if ($VMS_STATUS_SUCCESS(status)) {
14530 val_str[0] = _toupper(val_str[0]);
14531 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14532 vms_debug_on_exception = 1;
14534 vms_debug_on_exception = 0;
14537 /* Debug unix/vms file translation routines */
14538 vms_debug_fileify = 0;
14539 status = sys_trnlnm("PERL_VMS_FILEIFY_DEBUG", val_str, sizeof(val_str));
14540 if ($VMS_STATUS_SUCCESS(status)) {
14541 val_str[0] = _toupper(val_str[0]);
14542 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14543 vms_debug_fileify = 1;
14545 vms_debug_fileify = 0;
14549 /* Historically PERL has been doing vmsify / stat differently than */
14550 /* the CRTL. In particular, under some conditions the CRTL will */
14551 /* remove some illegal characters like spaces from filenames */
14552 /* resulting in some differences. The stat()/lstat() wrapper has */
14553 /* been reporting such file names as invalid and fails to stat them */
14554 /* fixing this bug so that stat()/lstat() accept these like the */
14555 /* CRTL does will result in several tests failing. */
14556 /* This should really be fixed, but for now, set up a feature to */
14557 /* enable it so that the impact can be studied. */
14558 vms_bug_stat_filename = 0;
14559 status = sys_trnlnm("PERL_VMS_BUG_STAT_FILENAME", val_str, sizeof(val_str));
14560 if ($VMS_STATUS_SUCCESS(status)) {
14561 val_str[0] = _toupper(val_str[0]);
14562 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14563 vms_bug_stat_filename = 1;
14565 vms_bug_stat_filename = 0;
14569 /* Create VTF-7 filenames from Unicode instead of UTF-8 */
14570 vms_vtf7_filenames = 0;
14571 status = sys_trnlnm("PERL_VMS_VTF7_FILENAMES", val_str, sizeof(val_str));
14572 if ($VMS_STATUS_SUCCESS(status)) {
14573 val_str[0] = _toupper(val_str[0]);
14574 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14575 vms_vtf7_filenames = 1;
14577 vms_vtf7_filenames = 0;
14580 /* unlink all versions on unlink() or rename() */
14581 vms_unlink_all_versions = 0;
14582 status = sys_trnlnm
14583 ("PERL_VMS_UNLINK_ALL_VERSIONS", val_str, sizeof(val_str));
14584 if ($VMS_STATUS_SUCCESS(status)) {
14585 val_str[0] = _toupper(val_str[0]);
14586 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14587 vms_unlink_all_versions = 1;
14589 vms_unlink_all_versions = 0;
14592 /* Dectect running under GNV Bash or other UNIX like shell */
14593 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14594 gnv_unix_shell = 0;
14595 status = sys_trnlnm("GNV$UNIX_SHELL", val_str, sizeof(val_str));
14596 if ($VMS_STATUS_SUCCESS(status)) {
14597 gnv_unix_shell = 1;
14598 set_feature_default("DECC$EFS_CASE_PRESERVE", 1);
14599 set_feature_default("DECC$EFS_CHARSET", 1);
14600 set_feature_default("DECC$FILENAME_UNIX_NO_VERSION", 1);
14601 set_feature_default("DECC$FILENAME_UNIX_REPORT", 1);
14602 set_feature_default("DECC$READDIR_DROPDOTNOTYPE", 1);
14603 set_feature_default("DECC$DISABLE_POSIX_ROOT", 0);
14604 vms_unlink_all_versions = 1;
14605 vms_posix_exit = 1;
14609 /* hacks to see if known bugs are still present for testing */
14611 /* PCP mode requires creating /dev/null special device file */
14612 decc_bug_devnull = 0;
14613 status = sys_trnlnm("DECC_BUG_DEVNULL", val_str, sizeof(val_str));
14614 if ($VMS_STATUS_SUCCESS(status)) {
14615 val_str[0] = _toupper(val_str[0]);
14616 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14617 decc_bug_devnull = 1;
14619 decc_bug_devnull = 0;
14622 /* UNIX directory names with no paths are broken in a lot of places */
14623 decc_dir_barename = 1;
14624 status = sys_trnlnm("DECC_DIR_BARENAME", val_str, sizeof(val_str));
14625 if ($VMS_STATUS_SUCCESS(status)) {
14626 val_str[0] = _toupper(val_str[0]);
14627 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T'))
14628 decc_dir_barename = 1;
14630 decc_dir_barename = 0;
14633 #if __CRTL_VER >= 70300000 && !defined(__VAX)
14634 s = decc$feature_get_index("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION");
14636 decc_disable_to_vms_logname_translation = decc$feature_get_value(s, 1);
14637 if (decc_disable_to_vms_logname_translation < 0)
14638 decc_disable_to_vms_logname_translation = 0;
14641 s = decc$feature_get_index("DECC$EFS_CASE_PRESERVE");
14643 decc_efs_case_preserve = decc$feature_get_value(s, 1);
14644 if (decc_efs_case_preserve < 0)
14645 decc_efs_case_preserve = 0;
14648 s = decc$feature_get_index("DECC$EFS_CHARSET");
14649 decc_efs_charset_index = s;
14651 decc_efs_charset = decc$feature_get_value(s, 1);
14652 if (decc_efs_charset < 0)
14653 decc_efs_charset = 0;
14656 s = decc$feature_get_index("DECC$FILENAME_UNIX_REPORT");
14658 decc_filename_unix_report = decc$feature_get_value(s, 1);
14659 if (decc_filename_unix_report > 0) {
14660 decc_filename_unix_report = 1;
14661 vms_posix_exit = 1;
14664 decc_filename_unix_report = 0;
14667 s = decc$feature_get_index("DECC$FILENAME_UNIX_ONLY");
14669 decc_filename_unix_only = decc$feature_get_value(s, 1);
14670 if (decc_filename_unix_only > 0) {
14671 decc_filename_unix_only = 1;
14674 decc_filename_unix_only = 0;
14678 s = decc$feature_get_index("DECC$FILENAME_UNIX_NO_VERSION");
14680 decc_filename_unix_no_version = decc$feature_get_value(s, 1);
14681 if (decc_filename_unix_no_version < 0)
14682 decc_filename_unix_no_version = 0;
14685 s = decc$feature_get_index("DECC$READDIR_DROPDOTNOTYPE");
14687 decc_readdir_dropdotnotype = decc$feature_get_value(s, 1);
14688 if (decc_readdir_dropdotnotype < 0)
14689 decc_readdir_dropdotnotype = 0;
14692 #if __CRTL_VER >= 80200000
14693 s = decc$feature_get_index("DECC$POSIX_COMPLIANT_PATHNAMES");
14695 decc_posix_compliant_pathnames = decc$feature_get_value(s, 1);
14696 if (decc_posix_compliant_pathnames < 0)
14697 decc_posix_compliant_pathnames = 0;
14698 if (decc_posix_compliant_pathnames > 4)
14699 decc_posix_compliant_pathnames = 0;
14704 status = sys_trnlnm
14705 ("DECC$DISABLE_TO_VMS_LOGNAME_TRANSLATION", val_str, sizeof(val_str));
14706 if ($VMS_STATUS_SUCCESS(status)) {
14707 val_str[0] = _toupper(val_str[0]);
14708 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14709 decc_disable_to_vms_logname_translation = 1;
14714 status = sys_trnlnm("DECC$EFS_CASE_PRESERVE", val_str, sizeof(val_str));
14715 if ($VMS_STATUS_SUCCESS(status)) {
14716 val_str[0] = _toupper(val_str[0]);
14717 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14718 decc_efs_case_preserve = 1;
14723 status = sys_trnlnm("DECC$FILENAME_UNIX_REPORT", val_str, sizeof(val_str));
14724 if ($VMS_STATUS_SUCCESS(status)) {
14725 val_str[0] = _toupper(val_str[0]);
14726 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14727 decc_filename_unix_report = 1;
14730 status = sys_trnlnm("DECC$FILENAME_UNIX_ONLY", val_str, sizeof(val_str));
14731 if ($VMS_STATUS_SUCCESS(status)) {
14732 val_str[0] = _toupper(val_str[0]);
14733 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14734 decc_filename_unix_only = 1;
14735 decc_filename_unix_report = 1;
14738 status = sys_trnlnm("DECC$FILENAME_UNIX_NO_VERSION", val_str, sizeof(val_str));
14739 if ($VMS_STATUS_SUCCESS(status)) {
14740 val_str[0] = _toupper(val_str[0]);
14741 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14742 decc_filename_unix_no_version = 1;
14745 status = sys_trnlnm("DECC$READDIR_DROPDOTNOTYPE", val_str, sizeof(val_str));
14746 if ($VMS_STATUS_SUCCESS(status)) {
14747 val_str[0] = _toupper(val_str[0]);
14748 if ((val_str[0] == 'E') || (val_str[0] == '1') || (val_str[0] == 'T')) {
14749 decc_readdir_dropdotnotype = 1;
14754 #if defined(JPI$_CASE_LOOKUP_PERM) && defined(PPROP$K_CASE_BLIND) && !defined(__VAX)
14756 /* Report true case tolerance */
14757 /*----------------------------*/
14758 status = lib$getjpi(&jpicode1, 0, 0, &case_perm, 0, 0);
14759 if (!$VMS_STATUS_SUCCESS(status))
14760 case_perm = PPROP$K_CASE_BLIND;
14761 status = lib$getjpi(&jpicode2, 0, 0, &case_image, 0, 0);
14762 if (!$VMS_STATUS_SUCCESS(status))
14763 case_image = PPROP$K_CASE_BLIND;
14764 if ((case_perm == PPROP$K_CASE_SENSITIVE) ||
14765 (case_image == PPROP$K_CASE_SENSITIVE))
14766 vms_process_case_tolerant = 0;
14770 /* USE POSIX/DCL Exit codes - Recommended, but needs to default to */
14771 /* for strict backward compatibilty */
14772 status = sys_trnlnm
14773 ("PERL_VMS_POSIX_EXIT", 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 vms_posix_exit = 1;
14779 vms_posix_exit = 0;
14783 /* CRTL can be initialized past this point, but not before. */
14784 /* DECC$CRTL_INIT(); */
14791 #pragma extern_model save
14792 #pragma extern_model strict_refdef "LIB$INITIALIZ" nowrt
14793 const __align (LONGWORD) int spare[8] = {0};
14795 /* .psect LIB$INITIALIZE, NOPIC, USR, CON, REL, GBL, NOSHR, NOEXE, RD, NOWRT, LONG */
14796 #if __DECC_VER >= 60560002
14797 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, rel, gbl, noshr, noexe, nowrt, long
14799 #pragma extern_model strict_refdef "LIB$INITIALIZE" nopic, con, gbl, noshr, nowrt, long
14801 #endif /* __DECC */
14803 const long vms_cc_features = (const long)set_features;
14806 ** Force a reference to LIB$INITIALIZE to ensure it
14807 ** exists in the image.
14809 int lib$initialize(void);
14811 #pragma extern_model strict_refdef
14813 int lib_init_ref = (int) lib$initialize;
14816 #pragma extern_model restore